123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461 |
- /** @file
- Generic IPMI stack driver
- @copyright
- Copyright 1999 - 2021 Intel Corporation. <BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #include <IndustryStandard/Ipmi.h>
- #include <SmStatusCodes.h>
- #include "IpmiHooks.h"
- #include "IpmiBmcCommon.h"
- #include "IpmiBmc.h"
- #include "IpmiPhysicalLayer.h"
- #include <Library/TimerLib.h>
- #ifdef FAST_VIDEO_SUPPORT
- #include <Protocol/VideoPrint.h>
- #endif
- #include <Library/UefiRuntimeServicesTableLib.h>
- /******************************************************************************
- * Local variables
- */
- IPMI_BMC_INSTANCE_DATA *mIpmiInstance = NULL;
- EFI_HANDLE mImageHandle;
- //
- // Specific test interface
- //
- VOID
- GetDeviceSpecificTestResults (
- IN IPMI_BMC_INSTANCE_DATA *IpmiInstance
- )
- /*++
- Routine Description:
- This is a BMC specific routine to check the device specific self test results as defined
- in the Bensley BMC core specification.
- Arguments:
- IpmiInstance - Data structure describing BMC variables and used for sending commands
- Returns:
- VOID
- --*/
- {
- return;
- }
- EFI_STATUS
- GetSelfTest (
- IN IPMI_BMC_INSTANCE_DATA *IpmiInstance,
- IN EFI_STATUS_CODE_VALUE StatusCodeValue[],
- IN OUT UINT8 *ErrorCount
- )
- /*++
- Routine Description:
- Execute the Get Self Test results command to determine whether or not the BMC self tests
- have passed
- Arguments:
- IpmiInstance - Data structure describing BMC variables and used for sending commands
- StatusCodeValue - An array used to accumulate error codes for later reporting.
- ErrorCount - Counter used to keep track of error codes in StatusCodeValue
- Returns:
- EFI_SUCCESS - BMC Self test results are retrieved and saved into BmcStatus
- EFI_DEVICE_ERROR - BMC failed to return self test results.
- --*/
- {
- EFI_STATUS Status;
- UINT32 DataSize;
- UINT8 Index;
- UINT8 *TempPtr;
- UINT32 Retries;
- BOOLEAN bResultFlag = FALSE;
- UINT8 TempData[MAX_TEMP_DATA];
- IPMI_SELF_TEST_RESULT_RESPONSE *SelfTestResult;
- //
- // Get the SELF TEST Results.
- //
- //
- //Note: If BMC PcdIpmiBmcReadyDelayTimer < BMC_KCS_TIMEOUT, it need set Retries as 1. Otherwise it will make SELT failure, caused by below condition (EFI_ERROR(Status) || Retries == 0)
- //
- if (PcdGet8 (PcdIpmiBmcReadyDelayTimer) < BMC_KCS_TIMEOUT) {
- Retries = 1;
- } else {
- Retries = PcdGet8 (PcdIpmiBmcReadyDelayTimer);
- }
- DataSize = sizeof (TempData);
- SelfTestResult = (IPMI_SELF_TEST_RESULT_RESPONSE *) &TempData[0];
- SelfTestResult->Result = 0;
- do {
- Status = IpmiSendCommand (
- &IpmiInstance->IpmiTransport,
- IPMI_NETFN_APP,
- 0,
- IPMI_APP_GET_SELFTEST_RESULTS,
- NULL,
- 0,
- TempData,
- &DataSize
- );
- if (Status == EFI_SUCCESS) {
- switch (SelfTestResult->Result) {
- case IPMI_APP_SELFTEST_NO_ERROR:
- case IPMI_APP_SELFTEST_NOT_IMPLEMENTED:
- case IPMI_APP_SELFTEST_ERROR:
- case IPMI_APP_SELFTEST_FATAL_HW_ERROR:
- bResultFlag = TRUE;
- break;
- default:
- break;
- } //switch
- if (bResultFlag) {
- break;
- }
- }
- MicroSecondDelay (500 * 1000);
- } while (--Retries > 0);
- //
- // If Status indicates a Device error, then the BMC is not responding, so send an error.
- //
- if (EFI_ERROR (Status) || Retries == 0) {
- DEBUG ((EFI_D_ERROR, "\n[IPMI] BMC self-test does not respond (status: %r)!\n\n", Status));
- if (*ErrorCount < MAX_SOFT_COUNT) {
- StatusCodeValue[*ErrorCount] = EFI_COMPUTING_UNIT_FIRMWARE_PROCESSOR | EFI_CU_FP_EC_COMM_ERROR;
- (*ErrorCount)++;
- }
- IpmiInstance->BmcStatus = BMC_HARDFAIL;
- return Status;
- } else {
- DEBUG ((DEBUG_INFO, "[IPMI] BMC self-test result: %02X-%02X\n", SelfTestResult->Result, SelfTestResult->Param));
- //
- // Copy the Self test results to Error Status. Data will be copied as long as it
- // does not exceed the size of the ErrorStatus variable.
- //
- for (Index = 0, TempPtr = (UINT8 *) &IpmiInstance->ErrorStatus;
- (Index < DataSize) && (Index < sizeof (IpmiInstance->ErrorStatus));
- Index++, TempPtr++
- ) {
- *TempPtr = TempData[Index];
- }
- //
- // Check the IPMI defined self test results.
- // Additional Cases are device specific test results.
- //
- switch (SelfTestResult->Result) {
- case IPMI_APP_SELFTEST_NO_ERROR:
- case IPMI_APP_SELFTEST_NOT_IMPLEMENTED:
- IpmiInstance->BmcStatus = BMC_OK;
- break;
- case IPMI_APP_SELFTEST_ERROR:
- //
- // Three of the possible errors result in BMC hard failure; FRU Corruption,
- // BootBlock Firmware corruption, and Operational Firmware Corruption. All
- // other errors are BMC soft failures.
- //
- if ((SelfTestResult->Param & (IPMI_APP_SELFTEST_FRU_CORRUPT | IPMI_APP_SELFTEST_FW_BOOTBLOCK_CORRUPT | IPMI_APP_SELFTEST_FW_CORRUPT)) != 0) {
- IpmiInstance->BmcStatus = BMC_HARDFAIL;
- } else {
- IpmiInstance->BmcStatus = BMC_SOFTFAIL;
- }
- //
- // Check if SDR repository is empty and report it if it is.
- //
- if ((SelfTestResult->Param & IPMI_APP_SELFTEST_SDR_REPOSITORY_EMPTY) != 0) {
- if (*ErrorCount < MAX_SOFT_COUNT) {
- StatusCodeValue[*ErrorCount] = EFI_COMPUTING_UNIT_FIRMWARE_PROCESSOR | CU_FP_EC_SDR_EMPTY;
- (*ErrorCount)++;
- }
- }
- break;
- case IPMI_APP_SELFTEST_FATAL_HW_ERROR:
- IpmiInstance->BmcStatus = BMC_HARDFAIL;
- break;
- default:
- IpmiInstance->BmcStatus = BMC_HARDFAIL;
- //
- // Call routine to check device specific failures.
- //
- GetDeviceSpecificTestResults (IpmiInstance);
- }
- if (IpmiInstance->BmcStatus == BMC_HARDFAIL) {
- if (*ErrorCount < MAX_SOFT_COUNT) {
- StatusCodeValue[*ErrorCount] = EFI_COMPUTING_UNIT_FIRMWARE_PROCESSOR | EFI_CU_FP_EC_HARD_FAIL;
- (*ErrorCount)++;
- }
- } else if (IpmiInstance->BmcStatus == BMC_SOFTFAIL) {
- if (*ErrorCount < MAX_SOFT_COUNT) {
- StatusCodeValue[*ErrorCount] = EFI_COMPUTING_UNIT_FIRMWARE_PROCESSOR | EFI_CU_FP_EC_SOFT_FAIL;
- (*ErrorCount)++;
- }
- }
- }
- return EFI_SUCCESS;
- } // GetSelfTest()
- EFI_STATUS
- GetDeviceId (
- IN IPMI_BMC_INSTANCE_DATA *IpmiInstance,
- IN EFI_STATUS_CODE_VALUE StatusCodeValue[ ],
- IN OUT UINT8 *ErrorCount
- )
- /*++
- Routine Description:
- Execute the Get Device ID command to determine whether or not the BMC is in Force Update
- Mode. If it is, then report it to the error manager.
- Arguments:
- IpmiInstance - Data structure describing BMC variables and used for sending commands
- StatusCodeValue - An array used to accumulate error codes for later reporting.
- ErrorCount - Counter used to keep track of error codes in StatusCodeValue
- Returns:
- Status
- --*/
- {
- EFI_STATUS Status;
- UINT32 DataSize;
- SM_CTRL_INFO *pBmcInfo;
- IPMI_MSG_GET_BMC_EXEC_RSP *pBmcExecContext;
- UINT32 Retries;
- UINT8 TempData[MAX_TEMP_DATA];
- #ifdef FAST_VIDEO_SUPPORT
- EFI_VIDEOPRINT_PROTOCOL *VideoPrintProtocol;
- EFI_STATUS VideoPrintStatus;
- #endif
- #ifdef FAST_VIDEO_SUPPORT
- VideoPrintStatus = gBS->LocateProtocol (
- &gEfiVideoPrintProtocolGuid,
- NULL,
- &VideoPrintProtocol
- );
- #endif
- //
- // Set up a loop to retry for up to PcdIpmiBmcReadyDelayTimer seconds. Calculate retries not timeout
- // so that in case KCS is not enabled and IpmiSendCommand() returns
- // immediately we will not wait all the PcdIpmiBmcReadyDelayTimer seconds.
- //
- Retries = PcdGet8 (PcdIpmiBmcReadyDelayTimer);
- //
- // Get the device ID information for the BMC.
- //
- DataSize = sizeof (TempData);
- while (EFI_ERROR (Status = IpmiSendCommand (
- &IpmiInstance->IpmiTransport,
- IPMI_NETFN_APP, 0,
- IPMI_APP_GET_DEVICE_ID,
- NULL, 0,
- TempData, &DataSize))
- ) {
- DEBUG ((DEBUG_ERROR, "[IPMI] BMC does not respond by Get BMC DID (status: %r), %d retries left, ResponseData: 0x%lx\n",
- Status, Retries, TempData));
- if (Retries-- == 0) {
- IpmiInstance->BmcStatus = BMC_HARDFAIL;
- return Status;
- }
- //
- //Handle the case that BMC FW still not enable KCS channel after AC cycle. just stall 1 second
- //
- MicroSecondDelay (1*1000*1000);
- }
- pBmcInfo = (SM_CTRL_INFO*)&TempData[0];
- DEBUG ((EFI_D_ERROR, "[IPMI] BMC Device ID: 0x%02X, firmware version: %d.%02X UpdateMode:%x\n", pBmcInfo->DeviceId, pBmcInfo->MajorFirmwareRev, pBmcInfo->MinorFirmwareRev,pBmcInfo->UpdateMode));
- //
- // In OpenBMC, UpdateMode: the bit 7 of byte 4 in get device id command is used for the BMC status:
- // 0 means BMC is ready, 1 means BMC is not ready.
- // At the very beginning of BMC power on, the status is 1 means BMC is in booting process and not ready. It is not the flag for force update mode.
- //
- if (pBmcInfo->UpdateMode == BMC_READY) {
- mIpmiInstance->BmcStatus = BMC_OK;
- return EFI_SUCCESS;
- } else {
- DataSize = sizeof (TempData);
- Status = IpmiSendCommand (
- &IpmiInstance->IpmiTransport,
- IPMI_NETFN_FIRMWARE, 0,
- IPMI_GET_BMC_EXECUTION_CONTEXT,
- NULL, 0,
- TempData, &DataSize
- );
- pBmcExecContext = (IPMI_MSG_GET_BMC_EXEC_RSP*)&TempData[0];
- DEBUG ((DEBUG_INFO, "[IPMI] Operational status of BMC: 0x%x\n", pBmcExecContext->CurrentExecutionContext));
- if ((pBmcExecContext->CurrentExecutionContext == IPMI_BMC_IN_FORCED_UPDATE_MODE) &&
- !EFI_ERROR (Status)) {
- DEBUG ((DEBUG_ERROR, "[IPMI] BMC in Forced Update mode, skip waiting for BMC_READY.\n"));
- IpmiInstance->BmcStatus = BMC_UPDATE_IN_PROGRESS;
- } else {
- //
- // Updatemode = 1 mean BMC is not ready, continue waiting.
- //
- while (Retries-- != 0) {
- MicroSecondDelay(1*1000*1000); //delay 1 seconds
- DEBUG ((EFI_D_ERROR, "[IPMI] UpdateMode Retries: %d \n",Retries));
- DataSize = sizeof (TempData);
- Status = IpmiSendCommand (
- &IpmiInstance->IpmiTransport,
- IPMI_NETFN_APP, 0,
- IPMI_APP_GET_DEVICE_ID,
- NULL, 0,
- TempData, &DataSize
- );
- if (!EFI_ERROR (Status)) {
- pBmcInfo = (SM_CTRL_INFO*)&TempData[0];
- DEBUG ((DEBUG_ERROR, "[IPMI] UpdateMode Retries: %d pBmcInfo->UpdateMode:%x, Status: %r, Response Data: 0x%lx\n",Retries, pBmcInfo->UpdateMode, Status, TempData));
- if (pBmcInfo->UpdateMode == BMC_READY) {
- mIpmiInstance->BmcStatus = BMC_OK;
- return EFI_SUCCESS;
- }
- }
- }
- mIpmiInstance->BmcStatus = BMC_HARDFAIL;
- }
- }
- return Status;
- } // GetDeviceId()
- /**
- This function initializes KCS interface to BMC.
- Setup and initialize the BMC for the DXE phase. In order to verify the BMC is functioning
- as expected, the BMC Selftest is performed. The results are then checked and any errors are
- reported to the error manager. Errors are collected throughout this routine and reported
- just prior to installing the driver. If there are more errors than MAX_SOFT_COUNT, then they
- will be ignored.
- @param[in] ImageHandle - Handle of this driver image
- @param[in] SystemTable - Table containing standard EFI services
- @retval EFI_SUCCESS - Always success is returned even if KCS does not function
- **/
- EFI_STATUS
- InitializeIpmiKcsPhysicalLayer (
- IN EFI_HANDLE ImageHandle,
- IN EFI_SYSTEM_TABLE *SystemTable
- )
- {
- EFI_STATUS Status;
- UINT8 ErrorCount;
- EFI_HANDLE Handle;
- UINT8 Index;
- EFI_STATUS_CODE_VALUE StatusCodeValue[MAX_SOFT_COUNT];
- ErrorCount = 0;
- mImageHandle = ImageHandle;
- mIpmiInstance = AllocateZeroPool (sizeof (*mIpmiInstance));
- if (mIpmiInstance == NULL) {
- DEBUG ((EFI_D_ERROR, "ERROR!! Null Pointer returned by AllocateZeroPool ()\n"));
- ASSERT_EFI_ERROR (EFI_OUT_OF_RESOURCES);
- return EFI_OUT_OF_RESOURCES;
- } else {
- //
- // Calibrate TSC Counter. Stall for 10ms, then multiply the resulting number of
- // ticks in that period by 100 to get the number of ticks in a 1 second timeout
- //
- //
- // Initialize the KCS transaction timeout.
- //
- mIpmiInstance->KcsTimeoutPeriod = (BMC_KCS_TIMEOUT * 1000*1000) / KCS_DELAY_UNIT;
- DEBUG ((EFI_D_ERROR, "[IPMI] mIpmiInstance->KcsTimeoutPeriod: 0x%lx\n",mIpmiInstance->KcsTimeoutPeriod));
- //
- // Initialize IPMI IO Base.
- //
- mIpmiInstance->IpmiIoBase = PcdGet16 (PcdIpmiIoBaseAddress);
- mIpmiInstance->Signature = SM_IPMI_BMC_SIGNATURE;
- mIpmiInstance->SlaveAddress = BMC_SLAVE_ADDRESS;
- mIpmiInstance->BmcStatus = BMC_NOTREADY;
- mIpmiInstance->IpmiTransport.IpmiSubmitCommand = IpmiSendCommand;
- mIpmiInstance->IpmiTransport.GetBmcStatus = IpmiGetBmcStatus;
- //
- // Get the Device ID and check if the system is in Force Update mode.
- //
- Status = GetDeviceId (
- mIpmiInstance,
- StatusCodeValue,
- &ErrorCount
- );
- //
- // Do not continue initialization if the BMC is in Force Update Mode.
- //
- if ((mIpmiInstance->BmcStatus != BMC_UPDATE_IN_PROGRESS) &&
- (mIpmiInstance->BmcStatus != BMC_HARDFAIL)) {
- //
- // Get the SELF TEST Results.
- //
- Status = GetSelfTest (
- mIpmiInstance,
- StatusCodeValue,
- &ErrorCount
- );
- }
- //
- // iterate through the errors reporting them to the error manager.
- //
- for (Index = 0; Index < ErrorCount; Index++) {
- ReportStatusCode (
- EFI_ERROR_CODE | EFI_ERROR_MAJOR,
- StatusCodeValue[Index]
- );
- }
- //
- // Now install the Protocol if the BMC is not in a HardFail State and not in Force Update mode
- //
- if ((mIpmiInstance->BmcStatus != BMC_HARDFAIL) && (mIpmiInstance->BmcStatus != BMC_UPDATE_IN_PROGRESS)) {
- Handle = NULL;
- Status = gBS->InstallProtocolInterface (
- &Handle,
- &gIpmiTransportProtocolGuid,
- EFI_NATIVE_INTERFACE,
- &mIpmiInstance->IpmiTransport
- );
- ASSERT_EFI_ERROR (Status);
- }
- return EFI_SUCCESS;
- }
- } // InitializeIpmiKcsPhysicalLayer()
|