diff options
Diffstat (limited to 'ReferenceCode/ME/Heci/Dxe/Hecicore.c')
-rw-r--r-- | ReferenceCode/ME/Heci/Dxe/Hecicore.c | 1461 |
1 files changed, 1461 insertions, 0 deletions
diff --git a/ReferenceCode/ME/Heci/Dxe/Hecicore.c b/ReferenceCode/ME/Heci/Dxe/Hecicore.c new file mode 100644 index 0000000..176fbed --- /dev/null +++ b/ReferenceCode/ME/Heci/Dxe/Hecicore.c @@ -0,0 +1,1461 @@ +/** @file + Heci driver core. For Dxe Phase, determines the HECI device and initializes it. + +@copyright + Copyright (c) 2007 - 2012 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. + + 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 + +**/ +#include "HeciDrv.h" +#include "HeciHpet.h" +#include "HeciCore.h" +#include "HeciRegs.h" +#include "MeState.h" + +// +// Globals used in Heci driver +// +UINT16 HECICtlrBDF; +static UINT64 HeciMBAR = 0; + +// +// Extern for shared HECI data and protocols +// +extern HECI_INSTANCE *mHeciContext; + +/** + The routing of MmIo Read Dword + + @param[in] a The address of Mmio + + @retval Return the valut of MmIo Read +**/ +UINT32 +MmIoReadDword ( + IN UINT64 a + ) +{ + volatile HECI_HOST_CONTROL_REGISTER *HeciRegHCsrPtr; + + HeciRegHCsrPtr = (HECI_HOST_CONTROL_REGISTER *) a; + return HeciRegHCsrPtr->ul; +} + +/** + The routing of MmIo Write Dword + + @param[in] a The address of Mmio + @param[in] b Value revised + + @retval None +**/ +VOID +MmIoWriteDword ( + IN UINT64 a, + IN UINT32 b + ) +{ + volatile HECI_HOST_CONTROL_REGISTER *HeciRegHCsrPtr; + + HeciRegHCsrPtr = (HECI_HOST_CONTROL_REGISTER *) a; + + HeciRegHCsrPtr->ul = b; +} + +// +// Macro definition for function used in Heci driver +// +#define MMIOREADDWORD(a) MmIoReadDword (a) +#define MMIOWRITEDWORD(a, b) MmIoWriteDword (a, b) + +#ifdef EFI_DEBUG + +/** + For serial debugger used, it will show the buffer message line by line to serial console. + + @param[in] Message the address point of buffer message + @param[in] Length message length + + @retval None +**/ +VOID +ShowBuffer ( + IN UINT8 *Message, + IN UINT32 Length + ) +{ + UINT32 Index; + UINT32 Offset; + CHAR16 Buffer[51]; // To construct a line needs 51 chars. + + Index = 0; + Offset = 0; + ZeroMem (Buffer, sizeof (Buffer)); + + while (Length-- > 0) { + // + // Get the corresponding offset value from the index of buffer message. + // + Offset = ((Index & 0x0F) > 7) ? (((Index & 0x0F) * 3) + 2) : ((Index & 0x0F) * 3); + + // + // Print "- " at the half of a line increases the readability of debug message. + // + if ((Index & 0x0F) == 0x08) { + UnicodeSPrint (&Buffer[24], 3 * sizeof (CHAR16), L"- "); + } + + // + // Collect the data of buffer message. + // + UnicodeSPrint (&Buffer[Offset], 4 * sizeof (CHAR16), L"%02x ", Message[Index]); + + // + // A line contains 16 bytes of buffer message. If a line is complete, it will be shown through DEBUG macro. + // + if (Offset == 47) { + DEBUG ((EFI_D_ERROR, "%02x: %s\n", (Index & 0xF0), Buffer)); + } + + Index++; + } + + // + // If a line isn't complete, show the remaining data. + // + if (Offset != 47) { + DEBUG ((EFI_D_ERROR, "%02x: %s\n", (Index & 0xF0), Buffer)); + } + return ; +} + +#endif // End Of EFI_DEBUG + +// +// Heci driver function definitions +// + +/** + Determines if the HECI device is present and, if present, initializes it for + use by the BIOS. + + @param[in] None. + + @retval EFI_SUCCESS HECI device is present and initialized. + @retval EFI_DEVICE_ERROR No HECI controller. + @exception EFI_UNSUPPORTED HECI MSG is unsupported because ME MODE is in ME ALT Disabled & + SECOVR JMPR + @retval EFI_TIMEOUT ME is not ready +**/ +EFI_STATUS +InitializeHeciPrivate ( + VOID + ) +{ + HECI_HOST_CONTROL_REGISTER HeciRegHCsr; + VOLATILE HECI_HOST_CONTROL_REGISTER *HeciRegHCsrPtr; + EFI_STATUS Status; + HECI_FWS_REGISTER MeFirmwareStatus; + + Status = EFI_SUCCESS; + + SaveHpet (); + do { + /// + /// Store HECI vendor and device information away + /// + mHeciContext->DeviceInfo = HeciPciRead16 (PCI_DEVICE_ID_OFFSET); + + /// + /// Check for HECI-1 PCI device availability + /// + if (mHeciContext->DeviceInfo == 0xFFFF) { + Status = EFI_DEVICE_ERROR; + break; + } + + MeFirmwareStatus.ul = HeciPciRead32 (R_FWSTATE); + + /// + /// Check for ME FPT Bad + /// + if (MeFirmwareStatus.r.FptBad) { + Status = EFI_DEVICE_ERROR; + break; + } + /// + /// Check for ME error status + /// + if (MeFirmwareStatus.r.ErrorCode) { + Status = EFI_NOT_READY; + if (MeFirmwareStatus.r.ErrorCode == ME_ERROR_CODE_UNKNOWN || MeFirmwareStatus.r.ErrorCode == ME_ERROR_CODE_IMAGE_FAILURE) { + /// + /// ME failed to start so no HECI + /// + Status = EFI_DEVICE_ERROR; + break; + } + } + /// + /// HECI MSG is unsupported if ME MODE is in ME ALT Disabled & SECOVR JMPR + /// + if ((MeFirmwareStatus.r.MeOperationMode == ME_OPERATION_MODE_SECOVR_JMPR) || + (MeFirmwareStatus.r.MeOperationMode == ME_OPERATION_MODE_DEBUG) + ) { + Status = EFI_UNSUPPORTED; + break; + } + /// + /// Store HECI revision ID + /// + mHeciContext->RevisionInfo = HeciPciRead8 (PCI_REVISION_ID_OFFSET); + + /// + /// Check if Base register is 64 bits wide. + /// + if (HeciPciRead32 (R_HECIMBAR) & 0x4) { + mHeciContext->HeciMBAR = (((UINT64) HeciPciRead32 (R_HECIMBAR + 4) << 32) | + (UINT64) HeciPciRead32 (R_HECIMBAR)) & 0xFFFFFFF0; + } else { + mHeciContext->HeciMBAR = (UINT64) HeciPciRead32 (R_HECIMBAR) & 0xFFFFFFF0; + } + /// + /// Get HECI_MBAR and see if it is programmed + /// to a useable value + /// + HeciMBAR = mHeciContext->HeciMBAR; + + /// + /// Load temporary address for HECI_MBAR if one is not assigned + /// + if (mHeciContext->HeciMBAR == 0) { + DEBUG ((EFI_D_ERROR, "Heci MMIO Bar not programmed in DXE phase\n")); + } + /// + /// Enable HECI BME and MSE + /// + HeciPciOr8 ( + PCI_COMMAND_OFFSET, + EFI_PCI_COMMAND_MEMORY_SPACE | EFI_PCI_COMMAND_BUS_MASTER + ); + + /// + /// Set HECI interrupt delivery mode. + /// HECI-1 using legacy/MSI interrupt + /// + HeciPciAnd8 (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 (Status != EFI_SUCCESS && Status != EFI_NOT_READY); + + RestoreHpet (); + + return Status; +} + +/** + Waits for the ME to report that it is ready for communication over the HECI + interface. + + @param[in] None. + + @retval EFI_SUCCESS ME is ready + @retval EFI_TIMEOUT ME is not ready +**/ +EFI_STATUS +WaitForMEReady ( + VOID + ) +{ + 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; +} + +/** + Checks if HECI reset has occured. + + @param[in] None. + + @retval TRUE HECI reset occurred + @retval FALSE No HECI reset occurred +**/ +BOOLEAN +CheckForHeciReset ( + VOID + ) +{ + 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; +} + +/** + Determines if the HECI device is present and, if present, initializes it for + use by the BIOS. + + @param[in] None. + + @retval EFI_SUCCESS HECI device is present and initialized + @retval EFI_TIMEOUT ME is not ready +**/ +EFI_STATUS +EFIAPI +HeciInitialize ( + VOID + ) +{ + 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; +} + +/** + Heci Re-initializes it for Host + + @param[in] None. + + @retval EFI_TIMEOUT ME is not ready + @retval EFI_STATUS Status code returned by ResetHeciInterface +**/ +EFI_STATUS +EFIAPI +HeciReInitialize ( + VOID + ) +{ + 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; +} + +/** + Heci Re-initializes it for Me + + @param[in] None. + + @retval EFI_TIMEOUT ME is not ready + @retval EFI_SUCCESS Re-initialization done +**/ +EFI_STATUS +EFIAPI +HeciReInitialize2 ( + VOID + ) +{ + 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; +} + +/** + Function to pull one messsage packet off the HECI circular buffer. + Corresponds to HECI HPS (part of) section 4.2.4 + + @param[in] Blocking Used to determine if the read is BLOCKING or NON_BLOCKING. + @param[out] MessageHeader Pointer to a buffer for the message header. + @param[in] MessageData Pointer to a buffer to recieve the message in. + @param[in][out] Length On input is the size of the callers buffer in bytes. On + output this is the size of the packet in bytes. + + @retval EFI_SUCCESS One message packet read. + @retval EFI_DEVICE_ERROR The circular buffer is overflowed. + @retval EFI_NO_RESPONSE The circular buffer is empty + @retval EFI_TIMEOUT Failed to receive a full message + @retval EFI_BUFFER_TOO_SMALL Message packet is larger than caller's buffer +**/ +EFI_STATUS +HECIPacketRead ( + IN UINT32 Blocking, + OUT HECI_MESSAGE_HEADER *MessageHeader, + OUT UINT32 *MessageData, + IN OUT UINT32 *Length + ) +{ + 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; +} + +/** + Reads a message from the ME across HECI. + + @param[in] Blocking Used to determine if the read is BLOCKING or NON_BLOCKING. + @param[in][out] MessageBody Pointer to a buffer used to receive a message. + @param[in][out] Length Pointer to the length of the buffer on input and the length + of the message on return. (in bytes) + + @retval EFI_SUCCESS One message packet read. + @retval EFI_DEVICE_ERROR Failed to initialize HECI or zero-length message packet read + @retval EFI_TIMEOUT HECI is not ready for communication + @retval EFI_BUFFER_TOO_SMALL The caller's buffer was not large enough +**/ +EFI_STATUS +EFIAPI +HeciReceive ( + IN UINT32 Blocking, + IN OUT UINT32 *MessageBody, + IN OUT UINT32 *Length + ) +{ + HECI_MESSAGE_HEADER PacketHeader; + UINT32 CurrentLength; + UINT32 MessageComplete; + EFI_STATUS ReadError; + EFI_STATUS Status; + UINT32 PacketBuffer; + UINT32 timer_start; + UINT32 timer_end; + BOOLEAN QuitFlag; + + CurrentLength = 0; + MessageComplete = 0; + Status = EFI_SUCCESS; + QuitFlag = FALSE; + + SaveHpet (); + + do { + /// + /// 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)) { + Status = EFI_DEVICE_ERROR; + break; + } + } + /// + /// Make sure that HECI is ready for communication. + /// + if (WaitForMEReady () != EFI_SUCCESS) { + 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; + ReadError = HECIPacketRead ( + Blocking, + &PacketHeader, + (UINT32 *) &MessageBody[CurrentLength / 4], + &PacketBuffer + ); + + /// + /// Check for error condition on read + /// + if (EFI_ERROR (ReadError)) { + *Length = 0; + Status = ReadError; + 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; + Status = EFI_SUCCESS; + 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; + break; + } + + *Length = CurrentLength; + + DEBUG ((EFI_D_ERROR, "HECI ReadMsg:\n")); +#ifdef EFI_DEBUG + DEBUG_CODE ( + ShowBuffer ((UINT8 *) MessageBody, *Length); + ); +#endif + } while (EFI_ERROR (Status)); + + RestoreHpet (); + + return Status; +} + +/** + Function sends one messsage (of any length) through the HECI circular buffer. + + @param[in] Message Pointer to the message data to be sent. + @param[in] Length Length of the message in bytes. + @param[in] HostAddress The address of the host processor. + @param[in] MeAddress Address of the ME subsystem the message is being sent to. + + @retval EFI_SUCCESS One message packet sent. + @retval EFI_DEVICE_ERROR Failed to initialize HECI + @retval EFI_TIMEOUT HECI is not ready for communication + @exception EFI_UNSUPPORTED Current ME mode doesn't support send message through HECI +**/ +EFI_STATUS +EFIAPI +HeciSend ( + IN UINT32 *Message, + IN UINT32 Length, + IN UINT8 HostAddress, + IN UINT8 MeAddress + ) +{ + UINT32 CBLength; + UINT32 SendLength; + UINT32 CurrentLength; + HECI_MESSAGE_HEADER MessageHeader; + EFI_STATUS WriteStatus; + EFI_STATUS Status; + HECI_HOST_CONTROL_REGISTER HeciRegHCsr; + UINT32 MeMode; + + CurrentLength = 0; + Status = EFI_SUCCESS; + + SaveHpet (); + + HeciGetMeMode (&MeMode); + do { + if (MeMode == ME_MODE_SECOVER) { + Status = EFI_UNSUPPORTED; + break; + } + /// + /// 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)) { + Status = EFI_DEVICE_ERROR; + break; + } + } + + DEBUG ((EFI_D_ERROR, "HECI SendMsg:\n")); +#ifdef EFI_DEBUG + DEBUG_CODE ( + ShowBuffer ((UINT8 *) Message, Length); + ); +#endif + /// + /// Make sure that HECI is ready for communication. + /// + if (WaitForMEReady () != EFI_SUCCESS) { + 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 OR Needs to be exactly equal to CB depth. + /// + if (((((Length - CurrentLength) + 3) / 4) < CBLength) || + ((((((Length - CurrentLength) + 3) / 4) == CBLength) && ((((Length - CurrentLength) + 3) % 4) == 0))) + ) { + 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; + + DEBUG ((EFI_D_ERROR, "HECI Header: %08x\n", MessageHeader.Data)); + + /// + /// send the current packet (CurrentLength can be treated as the index into the message buffer) + /// + WriteStatus = HeciPacketWrite (&MessageHeader, (UINT32 *) ((UINTN) Message + CurrentLength)); + if (EFI_ERROR (WriteStatus)) { + Status = WriteStatus; + break; + } + /// + /// Update the length information + /// + CurrentLength += SendLength; + } + + if (EFI_ERROR (Status)) { + break; + } + } while (EFI_ERROR (Status)); + + RestoreHpet (); + + return Status; + +} + +/** + Function sends one messsage packet through the HECI circular buffer + Corresponds to HECI HPS (part of) section 4.2.3 + + @param[in] MessageHeader Pointer to the message header. + @param[in] MessageData Pointer to the actual message data. + + @retval EFI_SUCCESS One message packet sent + @retval EFI_DEVICE_ERROR ME is not ready + @retval EFI_TIMEOUT HECI is not ready for communication +**/ +EFI_STATUS +HeciPacketWrite ( + IN HECI_MESSAGE_HEADER *MessageHeader, + IN UINT32 *MessageData + ) +{ + UINT32 timer_start; + UINT32 timer_end; + UINT32 i; + UINT32 LengthInDwords; + HECI_HOST_CONTROL_REGISTER HeciRegHCsr; + HECI_ME_CONTROL_REGISTER HeciRegMeCsrHa; + + /// + /// 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; +} + +/** + Function sends one messsage through the HECI circular buffer and waits + for the corresponding ACK message. + + @param[in][out] Message Pointer to the message buffer. + @param[in] Length Length of the message in bytes. + @param[in][out] RecLength Length of the message response in bytes. + @param[in] HostAddress Address of the sending entity. + @param[in] MeAddress Address of the ME entity that should receive the message. + + @retval EFI_SUCCESS Command succeeded + @retval EFI_DEVICE_ERROR HECI Device error, command aborts abnormally + @retval EFI_TIMEOUT HECI does not return the bufferbefore timeout + @retval EFI_BUFFER_TOO_SMALL Message Buffer is too small for the Acknowledge + @exception EFI_UNSUPPORTED Current ME mode doesn't support send message through HECI +**/ +EFI_STATUS +EFIAPI +HeciSendwACK ( + IN OUT UINT32 *Message, + IN UINT32 Length, + IN OUT UINT32 *RecLength, + IN UINT8 HostAddress, + IN UINT8 MeAddress + ) +{ + EFI_STATUS Status; + UINT16 RetryCount; + UINT32 TempRecLength; + UINT32 MeMode; + + HeciGetMeMode (&MeMode); + if (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; +} + +/** + Me reset and waiting for ready + + @param[in] Delay The biggest waiting time + + @retval EFI_TIMEOUT ME is not ready + @retval EFI_SUCCESS Me is ready +**/ +EFI_STATUS +EFIAPI +MeResetWait ( + IN UINT32 Delay + ) +{ + 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; +} + +/** + 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 + + @param[in] none + + @retval EFI_TIMEOUT ME is not ready + @retval EFI_SUCCESS Interface reset +**/ +EFI_STATUS +EFIAPI +ResetHeciInterface ( + VOID + ) +{ + 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; +} + +/** + Calculate if the circular buffer has overflowed. + Corresponds to HECI HPS (part of) section 4.2.1 + + @param[in] ReadPointer Location of the read pointer. + @param[in] WritePointer Location of the write pointer. + + @retval Number of filled slots. +**/ +UINT8 +FilledSlots ( + IN UINT32 ReadPointer, + IN UINT32 WritePointer + ) +{ + UINT8 FilledSlots; + + /// + /// Calculation documented in HECI HPS 0.68 section 4.2.1 + /// + FilledSlots = (((INT8) WritePointer) - ((INT8) ReadPointer)); + + return FilledSlots; +} + +/** + Calculate if the circular buffer has overflowed + Corresponds to HECI HPS (part of) section 4.2.1 + + @param[in] ReadPointer Value read from host/me read pointer + @param[in] WritePointer Value read from host/me write pointer + @param[in] BufferDepth Value read from buffer depth register + + @retval EFI_DEVICE_ERROR The circular buffer has overflowed + @retval EFI_SUCCESS The circular buffer does not overflowed +**/ +EFI_STATUS +OverflowCB ( + IN UINT32 ReadPointer, + IN UINT32 WritePointer, + IN UINT32 BufferDepth + ) +{ + 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; +} + +/** + Get an abstract Intel ME State from Firmware Status Register. + This is used to control BIOS flow for different Intel ME + functions + + @param[out] MeStatus Pointer for status report + see MeState.h - Abstract ME status definitions. + + @retval EFI_SUCCESS MeStatus copied + @retval EFI_INVALID_PARAMETER Pointer of MeStatus is invalid +**/ +EFI_STATUS +EFIAPI +HeciGetMeStatus ( + OUT UINT32 *MeStatus + ) +{ + HECI_FWS_REGISTER MeFirmwareStatus; + + if (MeStatus == NULL) { + return EFI_INVALID_PARAMETER; + } + + 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 if (MeFirmwareStatus.r.CurrentState == ME_STATE_INIT) { + *MeStatus = ME_INITIALIZING; + } else if (MeFirmwareStatus.r.CurrentState == ME_STATE_DISABLE_WAIT) { + *MeStatus = ME_DISABLE_WAIT; + } else if (MeFirmwareStatus.r.CurrentState == ME_STATE_TRANSITION) { + *MeStatus = ME_TRANSITION; + } else { + *MeStatus = ME_NOT_READY; + } + + if (MeFirmwareStatus.r.FwUpdateInprogress) { + *MeStatus |= ME_FW_UPDATES_IN_PROGRESS; + } + + if (MeFirmwareStatus.r.FwInitComplete == ME_FIRMWARE_COMPLETED) { + *MeStatus |= ME_FW_INIT_COMPLETE; + } + + if (MeFirmwareStatus.r.MeBootOptionsPresent == ME_BOOT_OPTIONS_PRESENT) { + *MeStatus |= ME_FW_BOOT_OPTIONS_PRESENT; + } + + DEBUG ((EFI_D_ERROR, "HECI MeStatus %X\n", MeFirmwareStatus.ul)); + + return EFI_SUCCESS; +} + +/** + Return ME Mode + + @param[out] MeMode Pointer for ME Mode report + + @retval EFI_SUCCESS MeMode copied + @retval EFI_INVALID_PARAMETER Pointer of MeMode is invalid +**/ +EFI_STATUS +EFIAPI +HeciGetMeMode ( + OUT UINT32 *MeMode + ) +{ + HECI_FWS_REGISTER MeFirmwareStatus; + + if (MeMode == NULL) { + return EFI_INVALID_PARAMETER; + } + + 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; + } + DEBUG ((EFI_D_ERROR, "HECI MeMode %X\n", MeFirmwareStatus.r.MeOperationMode)); + + return EFI_SUCCESS; +} |