diff options
Diffstat (limited to 'ReferenceCode/ME/Heci/Smm/Hecicore.c')
-rw-r--r-- | ReferenceCode/ME/Heci/Smm/Hecicore.c | 1585 |
1 files changed, 1585 insertions, 0 deletions
diff --git a/ReferenceCode/ME/Heci/Smm/Hecicore.c b/ReferenceCode/ME/Heci/Smm/Hecicore.c new file mode 100644 index 0000000..612f2f6 --- /dev/null +++ b/ReferenceCode/ME/Heci/Smm/Hecicore.c @@ -0,0 +1,1585 @@ +/*++ + This file contains an 'Intel Peripheral Driver' and uniquely + identified as "Intel Reference Module" and is + licensed for Intel CPUs and chipsets under the terms of your + license agreement with Intel or your vendor. This file may + be modified by the user, subject to additional terms of the + license agreement +--*/ +/*++ + +Copyright (c) 2008 - 2010 Intel Corporation. All rights reserved +This software and associated documentation (if any) is furnished +under a license and may only be used or copied in accordance +with the terms of the license. Except as permitted by such +license, no part of this software or documentation may be +reproduced, stored in a retrieval system, or transmitted in any +form or by any means without the express written consent of +Intel Corporation. + +Module Name: + + Hecicore.c + +Abstract: + + Heci driver core. For Dxe Phase, determines the HECI device and initializes it. + +--*/ +#include "HeciHpet.h" +#include "HeciCore.h" +#include "HeciRegs.h" +#include "MeState.h" + +// +// ////////////////////////////////////////////////////////////////////////////////// +// Globals used in Heci driver +//////////////////////////////////////////////////////////////////////////////////// +// +UINT16 HECICtlrBDF; +static UINT32 HeciMBAR = 0; + +// +// ////////////////////////////////////////////////////////////////////////////////// +// Macro definition for function used in Heci driver +//////////////////////////////////////////////////////////////////////////////////// +// +#define R_PCH_RCRB_FUNC_DIS2 0x3428 + +UINT32 +MmIoReadDword ( + UINTN a + ) +/*++ + +Routine Description: + + The routing of MmIo Read Dword + +Arguments: + + a - The address of Mmio + +Returns: + + Return the valut of MmIo Read + +--*/ +{ + volatile HECI_HOST_CONTROL_REGISTER *HeciRegHCsrPtr; + + HeciRegHCsrPtr = (HECI_HOST_CONTROL_REGISTER *) a; + return HeciRegHCsrPtr->ul; +} + +VOID +MmIoWriteDword ( + UINTN a, + UINT32 b + ) +/*++ + +Routine Description: + + The routing of MmIo Write Dword + +Arguments: + + a - The address of Mmio + b - Value revised + +Returns: + + None + +--*/ +{ + volatile HECI_HOST_CONTROL_REGISTER *HeciRegHCsrPtr; + + HeciRegHCsrPtr = (HECI_HOST_CONTROL_REGISTER *) a; + + HeciRegHCsrPtr->ul = b; +} + +#define MMIOREADDWORD(a) MmIoReadDword (a) +#define MMIOWRITEDWORD(a, b) MmIoWriteDword (a, b) + +// +// Extern for shared HECI data and protocols +// +extern HECI_INSTANCE_SMM *mHeciContext; + +// +// ////////////////////////////////////////////////////////////////////////////////// +// Forward declaration +//////////////////////////////////////////////////////////////////////////////////// +// +UINT8 +FilledSlots ( + IN UINT32 ReadPointer, + IN UINT32 WritePointer + ); + +EFI_STATUS +OverflowCB ( + IN UINT32 ReadPointer, + IN UINT32 WritePointer, + IN UINT32 BufferDepth + ); + +EFI_STATUS +WaitForMEReady ( + VOID + ); + +// +// Heci driver function definitions +// +EFI_STATUS +InitializeHeciPrivate ( + VOID + ) +/*++ + + Routine Description: + Determines if the HECI device is present and, if present, initializes it for + use by the BIOS. + + Arguments: + None. + + Returns: + EFI_STATUS + +--*/ +{ + HECI_HOST_CONTROL_REGISTER HeciRegHCsr; + VOLATILE HECI_HOST_CONTROL_REGISTER *HeciRegHCsrPtr; + UINT32 MeMode; + EFI_STATUS Status; + UINTN HeciPciAddressBase; + + Status = EFI_SUCCESS; + + SaveHpet (); + + do { + // + // Check for ME FPT Bad + // + if ((HeciPciRead32 (R_FWSTATE) & 0x0020) != 0) { + Status = EFI_DEVICE_ERROR; + break; + } + // + // Check for ME error status + // + if ((HeciPciRead32 (R_FWSTATE) & 0xF000) != 0) { + // + // ME failed to start so no HECI + // + Status = EFI_DEVICE_ERROR; + break; + } + // + // Check ME Operation Mode to decice which HECI to use in SMM mode + // + Status = HeciGetMeMode (&MeMode); + ASSERT_EFI_ERROR (Status); + + // + // HECI MSG is unsupported if ME MODE is in Security Override + // + mHeciContext->MeMode = MeMode; + if (mHeciContext->MeMode == ME_MODE_DEBUG) { + Status = EFI_UNSUPPORTED; + break; + } + + HeciPciAddressBase = PCI_LIB_ADDRESS ( + ME_BUS, + ME_DEVICE_NUMBER, + HECI_FUNCTION_NUMBER, + 0 + ); + mHeciContext->HeciDevSaveEnable = Heci2DevSaveEnable; + mHeciContext->PciAddressBase = HeciPciAddressBase; + + // + // Store HECI vendor and device information away + // + mHeciContext->DeviceInfo = PciRead16 (HeciPciAddressBase + PCI_DEVICE_ID_OFFSET); + + // + // Check for HECI-2 PCI device availability + // + if (mHeciContext->DeviceInfo == 0xFFFF) { + Status = EFI_DEVICE_ERROR; + break; + } + // + // Store HECI revision ID + // + mHeciContext->RevisionInfo = PciRead8 (HeciPciAddressBase + PCI_REVISION_ID_OFFSET); + + // + // Get HECI_MBAR and see if it is programmed + // to a useable value + // + mHeciContext->HeciMBAR = PciRead32 (HeciPciAddressBase + R_HECIMBAR) & 0xFFFFFFF0; + HeciMBAR = mHeciContext->HeciMBAR; + + // + // Load temporary address for HECI_MBAR if one is not assigned + // + if (mHeciContext->HeciMBAR == 0) { + // + // mHeciContext->HeciMBAR = mHeciContext->DefaultHeciBar; + // PciWrite32 (HeciPciAddressBase + R_HECIMBAR, mHeciContext->HeciMBAR); + // HeciMBAR = mHeciContext->HeciMBAR; + // + DEBUG ((EFI_D_ERROR, "Heci MMIO Bar not programmed in SMM phase\n")); + } + // + // Enable HECI BME and MSE + // + PciOr8 ( + HeciPciAddressBase + PCI_COMMAND_OFFSET, + EFI_PCI_COMMAND_MEMORY_SPACE | EFI_PCI_COMMAND_BUS_MASTER + ); + + // + // Set HECI interrupt delivery mode. + // HECI-2 using legacy/MSI interrupt + // + PciAnd8 (HeciPciAddressBase + R_HIDM, 0xFC); + + // + // Need to do following on ME init: + // + // 1) wait for ME_CSR_HA reg ME_RDY bit set + // + if (WaitForMEReady () != EFI_SUCCESS) { + Status = EFI_TIMEOUT; + break; + } + // + // 2) setup H_CSR reg as follows: + // a) Make sure H_RST is clear + // b) Set H_RDY + // c) Set H_IG + // + HeciRegHCsrPtr = (VOID *) (UINTN) (mHeciContext->HeciMBAR + H_CSR); + HeciRegHCsr.ul = HeciRegHCsrPtr->ul; + if (HeciRegHCsrPtr->r.H_RDY == 0) { + HeciRegHCsr.r.H_RST = 0; + HeciRegHCsr.r.H_RDY = 1; + HeciRegHCsr.r.H_IG = 1; + HeciRegHCsrPtr->ul = HeciRegHCsr.ul; + } + + } while (EFI_ERROR (Status)); + + RestoreHpet (); + + return Status; +} + +EFI_STATUS +WaitForMEReady ( + VOID + ) +/*++ + + Routine Description: + Waits for the ME to report that it is ready for communication over the HECI + interface. + + Arguments: + None. + + Returns: + EFI_STATUS + +--*/ +{ + UINT32 TimerStart; + UINT32 TimerEnd; + HECI_ME_CONTROL_REGISTER HeciRegMeCsrHa; + + // + // Wait for ME ready + // + // + // Check for ME ready status + // + StartTimer (&TimerStart, &TimerEnd, HECI_INIT_TIMEOUT); + HeciRegMeCsrHa.ul = MMIOREADDWORD (HeciMBAR + ME_CSR_HA); + while (HeciRegMeCsrHa.r.ME_RDY_HRA == 0) { + // + // If 5 second timeout has expired, return fail + // + if (Timeout (TimerStart, TimerEnd) != EFI_SUCCESS) { + return EFI_TIMEOUT; + } + // + // Perform IO delay + // + IoDelay (HECI_WAIT_DELAY); + + HeciRegMeCsrHa.ul = MMIOREADDWORD (HeciMBAR + ME_CSR_HA); + } + // + // ME ready!!! + // + return EFI_SUCCESS; +} + +BOOLEAN +CheckForHeciReset ( + VOID + ) +/*++ + + Routine Description: + Checks if HECI reset has occured. + + Arguments: + None. + + Returns: + TRUE - HECI reset occurred + FALSE - No HECI reset occurred + +--*/ +{ + HECI_HOST_CONTROL_REGISTER HeciRegHCsr; + HECI_ME_CONTROL_REGISTER HeciRegMeCsrHa; + + // + // Init Host & ME CSR + // + HeciRegHCsr.ul = MMIOREADDWORD (HeciMBAR + H_CSR); + HeciRegMeCsrHa.ul = MMIOREADDWORD (HeciMBAR + ME_CSR_HA); + + if ((HeciRegMeCsrHa.r.ME_RDY_HRA == 0) || (HeciRegHCsr.r.H_RDY == 0)) { + return TRUE; + } + + return FALSE; +} + +EFI_STATUS +HeciInitialize ( + VOID + ) +/*++ + + Routine Description: + Determines if the HECI device is present and, if present, initializes it for + use by the BIOS. + + Arguments: + None. + + Returns: + EFI_STATUS + +--*/ +{ + HECI_HOST_CONTROL_REGISTER HeciRegHCsr; + + // + // Make sure that HECI device BAR is correct and device is enabled. + // + HeciMBAR = CheckAndFixHeciForAccess (); + + // + // Need to do following on ME init: + // + // 1) wait for ME_CSR_HA reg ME_RDY bit set + // + if (WaitForMEReady () != EFI_SUCCESS) { + return EFI_TIMEOUT; + } + // + // 2) setup H_CSR reg as follows: + // a) Make sure H_RST is clear + // b) Set H_RDY + // c) Set H_IG + // + HeciRegHCsr.ul = MMIOREADDWORD (HeciMBAR + H_CSR); + if (HeciRegHCsr.r.H_RDY == 0) { + HeciRegHCsr.r.H_RST = 0; + HeciRegHCsr.r.H_RDY = 1; + HeciRegHCsr.r.H_IG = 1; + MMIOWRITEDWORD (HeciMBAR + H_CSR, HeciRegHCsr.ul); + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +HeciReInitialize ( + VOID + ) +/*++ + + Routine Description: + Heci Re-initializes it for Host + + Arguments: + None. + + Returns: + EFI_STATUS + +--*/ +{ + HECI_HOST_CONTROL_REGISTER HeciRegHCsr; + EFI_STATUS Status; + + Status = EFI_SUCCESS; + // + // Need to do following on ME init: + // + // 1) wait for HOST_CSR_HA reg H_RDY bit set + // + // if (WaitForHostReady() != EFI_SUCCESS) { + // + if (MeResetWait (HECI_INIT_TIMEOUT) != EFI_SUCCESS) { + return EFI_TIMEOUT; + } + + HeciRegHCsr.ul = MMIOREADDWORD (HeciMBAR + H_CSR); + if (HeciRegHCsr.r.H_RDY == 0) { + Status = ResetHeciInterface (); + + } + + return Status; +} + +EFI_STATUS +EFIAPI +HeciReInitialize2 ( + VOID + ) +/*++ + + Routine Description: + Heci Re-initializes it for Me + + Arguments: + None. + + Returns: + EFI_STATUS + +--*/ +{ + HECI_ME_CONTROL_REGISTER HeciRegMeCsrHa; + EFI_STATUS Status; + UINT32 TimerStart; + UINT32 TimerEnd; + Status = EFI_SUCCESS; + // + // Need to do following on ME init: + // + // 1) wait for HOST_CSR_HA reg H_RDY bit set + // + // if (WaitForHostReady() != EFI_SUCCESS) { + // + StartTimer (&TimerStart, &TimerEnd, HECI_INIT_TIMEOUT); + HeciRegMeCsrHa.ul = MMIOREADDWORD (HeciMBAR + ME_CSR_HA); + while (HeciRegMeCsrHa.r.ME_RDY_HRA == 1) { + // + // If 5 second timeout has expired, return fail + // + if (Timeout (TimerStart, TimerEnd) != EFI_SUCCESS) { + return EFI_TIMEOUT; + } + + IoDelay (HECI_WAIT_DELAY); + + HeciRegMeCsrHa.ul = MMIOREADDWORD (HeciMBAR + ME_CSR_HA); + } + + if (WaitForMEReady () != EFI_SUCCESS) { + return EFI_TIMEOUT; + } + + return Status; +} + +EFI_STATUS +HECIPacketRead ( + IN UINT32 Blocking, + OUT HECI_MESSAGE_HEADER *MessageHeader, + OUT UINT32 *MessageData, + IN OUT UINT32 *Length + ) +/*++ + + Routine Description: + Function to pull one messsage packet off the HECI circular buffer. + Corresponds to HECI HPS (part of) section 4.2.4 + + + Arguments: + Blocking - Used to determine if the read is BLOCKING or NON_BLOCKING. + MessageHeader - Pointer to a buffer for the message header. + MessageData - Pointer to a buffer to recieve the message in. + Length - On input is the size of the callers buffer in bytes. On + output this is the size of the packet in bytes. + + Returns: + EFI_STATUS + +--*/ +{ + BOOLEAN GotMessage; + UINT32 TimerStart; + UINT32 TimerEnd; + UINT32 TimerStart1; + UINT32 TimerEnd1; + UINT32 i; + UINT32 LengthInDwords; + HECI_ME_CONTROL_REGISTER HeciRegMeCsrHa; + HECI_HOST_CONTROL_REGISTER HeciRegHCsr; + + GotMessage = FALSE; + // + // Initialize memory mapped register pointers + // + // VOLATILE HECI_HOST_CONTROL_REGISTER *HeciRegHCsrPtr = (VOID*)(mHeciContext->HeciMBAR + H_CSR); + // VOLATILE HECI_ME_CONTROL_REGISTER *HeciRegMeCsrHaPtr = (VOID*)(mHeciContext->HeciMBAR + ME_CSR_HA); + // VOLATILE UINT32 *HeciRegMeCbrwPtr = (VOID*)(mHeciContext->HeciMBAR + ME_CB_RW); + // + // clear Interrupt Status bit + // + HeciRegHCsr.ul = MMIOREADDWORD (HeciMBAR + H_CSR); + HeciRegHCsr.r.H_IS = 1; + + // + // test for circular buffer overflow + // + HeciRegMeCsrHa.ul = MMIOREADDWORD (HeciMBAR + ME_CSR_HA); + if (OverflowCB ( + HeciRegMeCsrHa.r.ME_CBRP_HRA, + HeciRegMeCsrHa.r.ME_CBWP_HRA, + HeciRegMeCsrHa.r.ME_CBD_HRA + ) != EFI_SUCCESS) { + // + // if we get here, the circular buffer is overflowed + // + *Length = 0; + return EFI_DEVICE_ERROR; + } + // + // If NON_BLOCKING, exit if the circular buffer is empty + // + HeciRegMeCsrHa.ul = MMIOREADDWORD (HeciMBAR + ME_CSR_HA);; + if ((FilledSlots (HeciRegMeCsrHa.r.ME_CBRP_HRA, HeciRegMeCsrHa.r.ME_CBWP_HRA) == 0) && (Blocking == NON_BLOCKING)) { + *Length = 0; + return EFI_NO_RESPONSE; + } + // + // Start timeout counter + // + StartTimer (&TimerStart, &TimerEnd, HECI_READ_TIMEOUT); + + // + // loop until we get a message packet + // + while (!GotMessage) { + // + // If 1 second timeout has expired, return fail as we have not yet received a full message. + // + if (Timeout (TimerStart, TimerEnd) != EFI_SUCCESS) { + *Length = 0; + return EFI_TIMEOUT; + } + // + // Read one message from HECI buffer and advance read pointer. Make sure + // that we do not pass the write pointer. + // + HeciRegMeCsrHa.ul = MMIOREADDWORD (HeciMBAR + ME_CSR_HA);; + if (FilledSlots (HeciRegMeCsrHa.r.ME_CBRP_HRA, HeciRegMeCsrHa.r.ME_CBWP_HRA) > 0) { + // + // Eat the HECI Message header + // + MessageHeader->Data = MMIOREADDWORD (HeciMBAR + ME_CB_RW); + + // + // Compute required message length in DWORDS + // + LengthInDwords = ((MessageHeader->Fields.Length + 3) / 4); + + // + // Just return success if Length is 0 + // + if (MessageHeader->Fields.Length == 0) { + // + // Set Interrupt Generate bit and return + // + MMIOREADDWORD (HeciMBAR + H_CSR); + HeciRegHCsr.r.H_IG = 1; + MMIOWRITEDWORD (HeciMBAR + H_CSR, HeciRegHCsr.ul); + *Length = 0; + return EFI_SUCCESS; + } + // + // Make sure that the message does not overflow the circular buffer. + // + HeciRegMeCsrHa.ul = MMIOREADDWORD (HeciMBAR + ME_CSR_HA); + if ((MessageHeader->Fields.Length + sizeof (HECI_MESSAGE_HEADER)) > (HeciRegMeCsrHa.r.ME_CBD_HRA * 4)) { + *Length = 0; + return EFI_DEVICE_ERROR; + } + // + // Make sure that the callers buffer can hold the correct number of DWORDS + // + if ((MessageHeader->Fields.Length) <= *Length) { + // + // Start timeout counter for inner loop + // + StartTimer (&TimerStart1, &TimerEnd1, HECI_READ_TIMEOUT); + + // + // Wait here until entire message is present in circular buffer + // + HeciRegMeCsrHa.ul = MMIOREADDWORD (HeciMBAR + ME_CSR_HA); + while (LengthInDwords > FilledSlots (HeciRegMeCsrHa.r.ME_CBRP_HRA, HeciRegMeCsrHa.r.ME_CBWP_HRA)) { + // + // If 1 second timeout has expired, return fail as we have not yet received a full message + // + if (Timeout (TimerStart1, TimerEnd1) != EFI_SUCCESS) { + *Length = 0; + return EFI_TIMEOUT; + } + // + // Wait before we read the register again + // + IoDelay (HECI_WAIT_DELAY); + + // + // Read the register again + // + HeciRegMeCsrHa.ul = MMIOREADDWORD (HeciMBAR + ME_CSR_HA); + } + // + // copy rest of message + // + for (i = 0; i < LengthInDwords; i++) { + MessageData[i] = MMIOREADDWORD (HeciMBAR + ME_CB_RW); + } + // + // Update status and length + // + GotMessage = TRUE; + *Length = MessageHeader->Fields.Length; + + } else { + // + // Message packet is larger than caller's buffer + // + *Length = 0; + return EFI_BUFFER_TOO_SMALL; + } + } + // + // Wait before we try to get a message again + // + IoDelay (HECI_WAIT_DELAY); + } + // + // Read ME_CSR_HA. If the ME_RDY bit is 0, then an ME reset occurred during the + // transaction and the message should be discarded as bad data may have been retrieved + // from the host's circular buffer + // + HeciRegMeCsrHa.ul = MMIOREADDWORD (HeciMBAR + ME_CSR_HA); + if (HeciRegMeCsrHa.r.ME_RDY_HRA == 0) { + *Length = 0; + return EFI_DEVICE_ERROR; + } + // + // Set Interrupt Generate bit + // + HeciRegHCsr.ul = MMIOREADDWORD (HeciMBAR + H_CSR); + HeciRegHCsr.r.H_IG = 1; + MMIOWRITEDWORD (HeciMBAR + H_CSR, HeciRegHCsr.ul); + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +HeciReceive ( + IN UINT32 Blocking, + IN OUT UINT32 *MessageBody, + IN OUT UINT32 *Length + ) +/*++ + + Routine Description: + Reads a message from the ME across HECI. + + Arguments: + Blocking - Used to determine if the read is BLOCKING or NON_BLOCKING. + MessageBody - Pointer to a buffer used to receive a message. + Length - Pointer to the length of the buffer on input and the length + of the message on return. (in bytes) + + Returns: + EFI_STATUS + +--*/ +{ + HECI_MESSAGE_HEADER PacketHeader; + UINT32 CurrentLength; + UINT32 MessageComplete; + EFI_STATUS Status; + UINT32 PacketBuffer; + UINT32 timer_start; + UINT32 timer_end; + UINT32 MeDeviceState; + BOOLEAN QuitFlag; + + Status = EFI_SUCCESS; + CurrentLength = 0; + MessageComplete = 0; + QuitFlag = FALSE; + + SaveHpet (); + + do { + if (mHeciContext->MeMode == ME_MODE_SECOVER) { + Status = EFI_UNSUPPORTED; + break; + } + // + // Enable HECI and Save the Device State + // + mHeciContext->HeciDevSaveEnable (&MeDeviceState); + + // + // Make sure that HECI device BAR is correct and device is enabled. + // + HeciMBAR = CheckAndFixHeciForAccess (); + + // + // Make sure we do not have a HECI reset + // + if (CheckForHeciReset ()) { + // + // if HECI reset than try to re-init HECI + // + Status = HeciInitialize (); + + if (EFI_ERROR (Status)) { + HeciDevRestore (MeDeviceState); + Status = EFI_DEVICE_ERROR; + break; + } + } + // + // Make sure that HECI is ready for communication. + // + if (WaitForMEReady () != EFI_SUCCESS) { + HeciDevRestore (MeDeviceState); + Status = EFI_TIMEOUT; + break; + } + // + // Set up timer for BIOS timeout. + // + StartTimer (&timer_start, &timer_end, HECI_READ_TIMEOUT); + while ((CurrentLength < *Length) && (MessageComplete == 0)) { + // + // If 1 second timeout has expired, return fail as we have not yet received a full message + // + if (Timeout (timer_start, timer_end) != EFI_SUCCESS) { + Status = EFI_TIMEOUT; + QuitFlag = TRUE; + break; + } + + PacketBuffer = *Length - CurrentLength; + Status = HECIPacketRead ( + Blocking, + &PacketHeader, + (UINT32 *) &MessageBody[CurrentLength / 4], + &PacketBuffer + ); + + // + // Check for error condition on read + // + if (EFI_ERROR (Status)) { + *Length = 0; + QuitFlag = TRUE; + break; + } + // + // Get completion status from the packet header + // + MessageComplete = PacketHeader.Fields.MessageComplete; + + // + // Check for zero length messages + // + if (PacketBuffer == 0) { + // + // If we are not in the middle of a message, and we see Message Complete, + // this is a valid zero-length message. + // + if ((CurrentLength == 0) && (MessageComplete == 1)) { + *Length = 0; + QuitFlag = TRUE; + break; + } else { + // + // We should not expect a zero-length message packet except as described above. + // + *Length = 0; + Status = EFI_DEVICE_ERROR; + QuitFlag = TRUE; + break; + } + } + // + // Track the length of what we have read so far + // + CurrentLength += PacketBuffer; + + } + + if (QuitFlag == TRUE) { + break; + } + // + // If we get here the message should be complete, if it is not + // the caller's buffer was not large enough. + // + if (MessageComplete == 0) { + *Length = 0; + Status = EFI_BUFFER_TOO_SMALL; + } + + if (*Length != 0) { + *Length = CurrentLength; + } + // + // Restore HECI Device State + // + HeciDevRestore (MeDeviceState); + + } while (EFI_ERROR (Status) && (Status != EFI_BUFFER_TOO_SMALL)); + RestoreHpet (); + + return Status; +} + +EFI_STATUS +EFIAPI +HeciSend ( + IN UINT32 *Message, + IN UINT32 Length, + IN UINT8 HostAddress, + IN UINT8 MeAddress + ) +/*++ + + Routine Description: + Function sends one messsage (of any length) through the HECI circular buffer. + + Arguments: + Message - Pointer to the message data to be sent. + Length - Length of the message in bytes. + HostAddress - The address of the host processor. + MeAddress - Address of the ME subsystem the message is being sent to. + + Returns: + EFI_STATUS + +--*/ +{ + UINT32 CBLength; + UINT32 SendLength; + UINT32 CurrentLength; + HECI_MESSAGE_HEADER MessageHeader; + EFI_STATUS Status; + HECI_HOST_CONTROL_REGISTER HeciRegHCsr; + UINT32 MeDeviceState; + + Status = EFI_SUCCESS; + CurrentLength = 0; + + SaveHpet (); + + do { + if (mHeciContext->MeMode == ME_MODE_SECOVER) { + Status = EFI_UNSUPPORTED; + break; + } + // + // Enable HECI and Save the Device State + // + mHeciContext->HeciDevSaveEnable (&MeDeviceState); + + // + // Make sure that HECI device BAR is correct and device is enabled. + // + HeciMBAR = CheckAndFixHeciForAccess (); + + // + // Make sure we do not have a HECI reset + // + if (CheckForHeciReset ()) { + // + // if HECI reset than try to re-init HECI + // + Status = HeciInitialize (); + + if (EFI_ERROR (Status)) { + HeciDevRestore (MeDeviceState); + Status = EFI_DEVICE_ERROR; + break; + } + } + // + // Make sure that HECI is ready for communication. + // + if (WaitForMEReady () != EFI_SUCCESS) { + HeciDevRestore (MeDeviceState); + Status = EFI_TIMEOUT; + break; + } + // + // Set up memory mapped registers + // + HeciRegHCsr.ul = MMIOREADDWORD (HeciMBAR + H_CSR); + + // + // Grab Circular Buffer length + // + CBLength = HeciRegHCsr.r.H_CBD; + + // + // Prepare message header + // + MessageHeader.Data = 0; + MessageHeader.Fields.MeAddress = MeAddress; + MessageHeader.Fields.HostAddress = HostAddress; + + // + // Break message up into CB-sized packets and loop until completely sent + // + while (Length > CurrentLength) { + // + // Set the Message Complete bit if this is our last packet in the message. + // Needs to be 'less than' to account for the header. + // + if ((((Length - CurrentLength) + 3) / 4) < CBLength) { + MessageHeader.Fields.MessageComplete = 1; + } + // + // Calculate length for Message Header + // header length == smaller of circular buffer or remaining message (both account for the size of the header) + // + SendLength = ((CBLength < (((Length - CurrentLength) + 3) / 4)) ? ((CBLength - 1) * 4) : (Length - CurrentLength)); + MessageHeader.Fields.Length = SendLength; + + // + // send the current packet (CurrentLength can be treated as the index into the message buffer) + // + Status = HeciPacketWrite (&MessageHeader, (UINT32 *) ((UINTN) Message + CurrentLength)); + if (EFI_ERROR (Status)) { + break; + } + // + // Update the length information + // + CurrentLength += SendLength; + } + + if (EFI_ERROR (Status)) { + break; + } + // + // Restore HECI Device State + // + HeciDevRestore (MeDeviceState); + + } while (EFI_ERROR (Status)); + + RestoreHpet (); + + return Status; +} + +EFI_STATUS +HeciPacketWrite ( + IN HECI_MESSAGE_HEADER *MessageHeader, + IN UINT32 *MessageData + ) +/*++ + + Routine Description: + Function sends one messsage packet through the HECI circular buffer + Corresponds to HECI HPS (part of) section 4.2.3 + + Arguments: + MessageHeader - Pointer to the message header. + MessageData - Pointer to the actual message data. + + Returns: + EFI_STATUS + +--*/ +{ + UINT32 timer_start; + UINT32 timer_end; + UINT32 i; + UINT32 LengthInDwords; + HECI_HOST_CONTROL_REGISTER HeciRegHCsr; + HECI_ME_CONTROL_REGISTER HeciRegMeCsrHa; + + // + // VOLATILE HECI_HOST_CONTROL_REGISTER *HeciRegHCsrPtr = (VOID*)(mHeciContext->HeciMBAR + H_CSR); + // VOLATILE HECI_ME_CONTROL_REGISTER *HeciRegMeCsrHaPtr = (VOID*)(mHeciContext->HeciMBAR + ME_CSR_HA); + // VOLATILE UINT32 *HeciRegHCbwwPtr = (VOID*)(mHeciContext->HeciMBAR + H_CB_WW); + // + // Make sure that HECI is ready for communication. + // + if (WaitForMEReady () != EFI_SUCCESS) { + return EFI_TIMEOUT; + } + // + // Start timeout counter + // + StartTimer (&timer_start, &timer_end, HECI_SEND_TIMEOUT); + + // + // Compute message length in DWORDS + // + LengthInDwords = ((MessageHeader->Fields.Length + 3) / 4); + + // + // Wait until there is sufficient room in the circular buffer + // Must have room for message and message header + // + HeciRegHCsr.ul = MMIOREADDWORD (HeciMBAR + H_CSR); + while ((LengthInDwords + 1) > (HeciRegHCsr.r.H_CBD - FilledSlots (HeciRegHCsr.r.H_CBRP, HeciRegHCsr.r.H_CBWP))) { + // + // If 1 second timeout has expired, return fail as the circular buffer never emptied + // + if (Timeout (timer_start, timer_end) != EFI_SUCCESS) { + return EFI_TIMEOUT; + } + // + // Wait before we read the register again + // + IoDelay (HECI_WAIT_DELAY); + + // + // Read Host CSR for next iteration + // + HeciRegHCsr.ul = MMIOREADDWORD (HeciMBAR + H_CSR); + } + // + // Write Message Header + // + MMIOWRITEDWORD (HeciMBAR + H_CB_WW, MessageHeader->Data); + + // + // Write Message Body + // + for (i = 0; i < LengthInDwords; i++) { + MMIOWRITEDWORD (HeciMBAR + H_CB_WW, MessageData[i]); + } + // + // Set Interrupt Generate bit + // + HeciRegHCsr.ul = MMIOREADDWORD (HeciMBAR + H_CSR); + HeciRegHCsr.r.H_IG = 1; + MMIOWRITEDWORD (HeciMBAR + H_CSR, HeciRegHCsr.ul); + + // + // Test if ME Ready bit is set to 1, if set to 0 a fatal error occured during + // the transmission of this message. + // + HeciRegMeCsrHa.ul = MMIOREADDWORD (HeciMBAR + ME_CSR_HA); + if (HeciRegMeCsrHa.r.ME_RDY_HRA == 0) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +HeciSendwACK ( + IN OUT UINT32 *Message, + IN UINT32 Length, + IN OUT UINT32 *RecLength, + IN UINT8 HostAddress, + IN UINT8 MeAddress + ) +/*++ + + Routine Description: + Function sends one messsage through the HECI circular buffer and waits + for the corresponding ACK message. + + Arguments: + Message - Pointer to the message buffer. + SendLength - Length of the message in bytes. + RecLength - Length of the message response in bytes. + HostAddress - Address of the sending entity. + MeAddress - Address of the ME entity that should receive the message. + + Returns: + EFI_STATUS + +--*/ +{ + EFI_STATUS Status; + UINT16 RetryCount; + UINT32 TempRecLength; + + if (mHeciContext->MeMode == ME_MODE_SECOVER) { + return EFI_UNSUPPORTED; + } + // + // Send the message + // + Status = HeciSend (Message, Length, HostAddress, MeAddress); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Wait for ACK message + // + TempRecLength = *RecLength; + for (RetryCount = 0; RetryCount < HECI_MAX_RETRY; RetryCount++) { + // + // Read Message + // + Status = HeciReceive (BLOCKING, Message, &TempRecLength); + if (!EFI_ERROR (Status)) { + break; + } + // + // Reload receive length as it has been modified by the read function + // + TempRecLength = *RecLength; + } + // + // Return read length and status + // + *RecLength = TempRecLength; + return Status; +} + +EFI_STATUS +EFIAPI +MeResetWait ( + IN UINT32 Delay + ) +/*++ + +Routine Description: + + Me reset and waiting for ready + +Arguments: + + Delay - The biggest waiting time + +Returns: + + EFI_TIMEOUT - Time out + EFI_SUCCESS - Me ready + +--*/ +{ + HECI_HOST_CONTROL_REGISTER HeciRegHCsr; + UINT32 TimerStart; + UINT32 TimerEnd; + + // + // Make sure that HECI device BAR is correct and device is enabled. + // + HeciMBAR = CheckAndFixHeciForAccess (); + + // + // Wait for the HOST Ready bit to be cleared to signal a reset + // + StartTimer (&TimerStart, &TimerEnd, Delay); + HeciRegHCsr.ul = MMIOREADDWORD (HeciMBAR + H_CSR); + while (HeciRegHCsr.r.H_RDY == 1) { + // + // If timeout has expired, return fail + // + if (Timeout (TimerStart, TimerEnd) != EFI_SUCCESS) { + return EFI_TIMEOUT; + } + + HeciRegHCsr.ul = MMIOREADDWORD (HeciMBAR + H_CSR); + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +ResetHeciInterface ( + VOID + ) +/*++ + + Routine Description: + Function forces a reinit of the heci interface by following the reset heci interface via host algorithm + in HPS 0.90 doc 4-17-06 njy + + Arguments: + none + + Returns: + EFI_STATUS + +--*/ +{ + HECI_HOST_CONTROL_REGISTER HeciRegHCsr; + HECI_ME_CONTROL_REGISTER HeciRegMeCsrHa; + UINT32 TimerStart; + UINT32 TimerEnd; + + // + // Make sure that HECI device BAR is correct and device is enabled. + // + HeciMBAR = CheckAndFixHeciForAccess (); + + // + // Enable Reset + // + HeciRegHCsr.ul = MMIOREADDWORD (HeciMBAR + H_CSR); + HeciRegHCsr.r.H_RST = 1; + HeciRegHCsr.r.H_IG = 1; + MMIOWRITEDWORD (HeciMBAR + H_CSR, HeciRegHCsr.ul); + + // + // Make sure that the reset started + // + // HeciRegHCsr.ul = MMIOREADDWORD(HeciMBAR + H_CSR); + // + StartTimer (&TimerStart, &TimerEnd, HECI_INIT_TIMEOUT); + do { + // + // If 5 second timeout has expired, return fail + // + if (Timeout (TimerStart, TimerEnd) != EFI_SUCCESS) { + return EFI_TIMEOUT; + } + // + // Read the ME CSR + // + HeciRegHCsr.ul = MMIOREADDWORD (HeciMBAR + H_CSR); + } while (HeciRegHCsr.r.H_RDY == 1); + + // + // Wait for ME to perform reset + // + // HeciRegMeCsrHa.ul = MMIOREADDWORD(HeciMBAR + ME_CSR_HA); + // + StartTimer (&TimerStart, &TimerEnd, HECI_INIT_TIMEOUT); + do { + // + // If 5 second timeout has expired, return fail + // + if (Timeout (TimerStart, TimerEnd) != EFI_SUCCESS) { + return EFI_TIMEOUT; + } + // + // Read the ME CSR + // + HeciRegMeCsrHa.ul = MMIOREADDWORD (HeciMBAR + ME_CSR_HA); + } while (HeciRegMeCsrHa.r.ME_RDY_HRA == 0); + + // + // Make sure IS has been signaled on the HOST side + // + // HeciRegHCsr.ul = MMIOREADDWORD(HeciMBAR + H_CSR); + // + StartTimer (&TimerStart, &TimerEnd, HECI_INIT_TIMEOUT); + do { + // + // If 5 second timeout has expired, return fail + // + if (Timeout (TimerStart, TimerEnd) != EFI_SUCCESS) { + return EFI_TIMEOUT; + } + // + // Read the ME CSR + // + HeciRegHCsr.ul = MMIOREADDWORD (HeciMBAR + H_CSR); + } while (HeciRegHCsr.r.H_IS == 0); + + // + // Enable host side interface + // + HeciRegHCsr.ul = MMIOREADDWORD (HeciMBAR + H_CSR);; + HeciRegHCsr.r.H_RST = 0; + HeciRegHCsr.r.H_IG = 1; + HeciRegHCsr.r.H_RDY = 1; + MMIOWRITEDWORD (HeciMBAR + H_CSR, HeciRegHCsr.ul); + + return EFI_SUCCESS; +} + +UINT8 +FilledSlots ( + IN UINT32 ReadPointer, + IN UINT32 WritePointer + ) +/*++ + + Routine Description: + Calculate if the circular buffer has overflowed. + Corresponds to HECI HPS (part of) section 4.2.1 + + Arguments: + ReadPointer - Location of the read pointer. + WritePointer - Location of the write pointer. + + Returns: + Number of filled slots. + +--*/ +{ + UINT8 FilledSlots; + + // + // Calculation documented in HECI HPS 0.68 section 4.2.1 + // + FilledSlots = (((INT8) WritePointer) - ((INT8) ReadPointer)); + + return FilledSlots; +} + +EFI_STATUS +OverflowCB ( + IN UINT32 ReadPointer, + IN UINT32 WritePointer, + IN UINT32 BufferDepth + ) +/*++ + + Routine Description: + Calculate if the circular buffer has overflowed + Corresponds to HECI HPS (part of) section 4.2.1 + + Arguments: + ReadPointer - Value read from host/me read pointer + WritePointer - Value read from host/me write pointer + BufferDepth - Value read from buffer depth register + + Returns: + EFI_STATUS + +--*/ +{ + UINT8 FilledSlots; + + // + // Calculation documented in HECI HPS 0.68 section 4.2.1 + // + FilledSlots = (((INT8) WritePointer) - ((INT8) ReadPointer)); + + // + // test for overflow + // + if (FilledSlots > ((UINT8) BufferDepth)) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +HeciGetMeStatus ( + IN UINT32 *MeStatus + ) +/*++ + + Routine Description: + Return ME Status + + Arguments: + MeStatus pointer for status report + + Returns: + EFI_STATUS + +--*/ +{ + HECI_FWS_REGISTER MeFirmwareStatus; + UINT32 MeDeviceState; + + if (MeStatus == NULL) { + return EFI_INVALID_PARAMETER; + } + // + // Save HECI1 Device State and Enable it + // + Heci1DevSaveEnable (&MeDeviceState); + + MeFirmwareStatus.ul = HeciPciRead32 (R_FWSTATE); + + if (MeFirmwareStatus.r.CurrentState == ME_STATE_NORMAL && MeFirmwareStatus.r.ErrorCode == ME_ERROR_CODE_NO_ERROR) { + *MeStatus = ME_READY; + } else if (MeFirmwareStatus.r.CurrentState == ME_STATE_RECOVERY) { + *MeStatus = ME_IN_RECOVERY_MODE; + } else { + *MeStatus = ME_NOT_READY; + } + + if (MeFirmwareStatus.r.FwInitComplete == ME_FIRMWARE_COMPLETED) { + *MeStatus |= ME_FW_INIT_COMPLETE; + } + // + // Save HECI Device State and Enable it + // + HeciDevRestore (MeDeviceState); + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +HeciGetMeMode ( + IN UINT32 *MeMode + ) +/*++ + + Routine Description: + Return ME Mode + + Arguments: + MeMode pointer for ME Mode report + + Returns: + EFI_STATUS + +--*/ +{ + HECI_FWS_REGISTER MeFirmwareStatus; + UINT32 MeDeviceState; + + if (MeMode == NULL) { + return EFI_INVALID_PARAMETER; + } + // + // Save HECI1 Device State and Enable it + // + Heci1DevSaveEnable (&MeDeviceState); + + MeFirmwareStatus.ul = HeciPciRead32 (R_FWSTATE); + switch (MeFirmwareStatus.r.MeOperationMode) { + case ME_OPERATION_MODE_NORMAL: + *MeMode = ME_MODE_NORMAL; + break; + + case ME_OPERATION_MODE_DEBUG: + *MeMode = ME_MODE_DEBUG; + break; + + case ME_OPERATION_MODE_SOFT_TEMP_DISABLE: + *MeMode = ME_MODE_TEMP_DISABLED; + break; + + case ME_OPERATION_MODE_SECOVR_JMPR: + case ME_OPERATION_MODE_SECOVR_HECI_MSG: + *MeMode = ME_MODE_SECOVER; + break; + + default: + *MeMode = ME_MODE_FAILED; + } + // + // Save HECI Device State and Enable it + // + HeciDevRestore (MeDeviceState); + + return EFI_SUCCESS; +} + +EFI_STATUS +Heci1DevSaveEnable ( + IN OUT UINT32 *DevState + ) +/*++ + + Routine Description: + Save HECI1 State and Enable it + + Arguments: + DevState - Device State Save Buffer + + Returns: + EFI_STATUS + +--*/ +{ + *DevState = MMIOREADDWORD (PCH_RCRB_BASE + R_PCH_RCRB_FUNC_DIS2); + HeciEnable (); + return EFI_SUCCESS; +} + +EFI_STATUS +Heci2DevSaveEnable ( + IN OUT UINT32 *DevState + ) +/*++ + + Routine Description: + Save HECI2 State and Enable it + + Arguments: + DevState - Device State Save Buffer + + Returns: + EFI_STATUS + +--*/ +{ + *DevState = MMIOREADDWORD (PCH_RCRB_BASE + R_PCH_RCRB_FUNC_DIS2); + Heci2Enable (); + return EFI_SUCCESS; +} + +EFI_STATUS +HeciDevRestore ( + IN UINT32 DevState + ) +/*++ + + Routine Description: + Restore HECI1&HECI2 State + + Arguments: + DevState - Device State Save Buffer + + Returns: + EFI_STATUS + +--*/ +{ + MMIOWRITEDWORD (PCH_RCRB_BASE + R_PCH_RCRB_FUNC_DIS2, DevState); + MMIOREADDWORD (PCH_RCRB_BASE + R_PCH_RCRB_FUNC_DIS2); + return EFI_SUCCESS; +} |