PeiGenericIpmi.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. /** @file
  2. Generic IPMI stack during PEI phase
  3. @copyright
  4. Copyright 2017 - 2021 Intel Corporation. <BR>
  5. SPDX-License-Identifier: BSD-2-Clause-Patent
  6. **/
  7. #include <IndustryStandard/Ipmi.h>
  8. #include "PeiGenericIpmi.h"
  9. #include <Library/ReportStatusCodeLib.h>
  10. #include <Library/IpmiPlatformHookLib.h>
  11. ///////////////////////////////////////////////////////////////////////////////
  12. // Function Implementations
  13. //
  14. /*****************************************************************************
  15. @brief
  16. Internal function
  17. @param[in] PeiServices General purpose services available to every PEIM.
  18. @retval EFI_SUCCESS Always return EFI_SUCCESS
  19. **/
  20. EFI_STATUS
  21. EFIAPI
  22. PeiInitializeIpmiKcsPhysicalLayer (
  23. IN CONST EFI_PEI_SERVICES **PeiServices
  24. )
  25. {
  26. EFI_STATUS Status;
  27. PEI_IPMI_BMC_INSTANCE_DATA *mIpmiInstance;
  28. mIpmiInstance = NULL;
  29. //
  30. // Send Pre-Boot signal to BMC
  31. //
  32. if (PcdGetBool (PcdSignalPreBootToBmc)) {
  33. Status = SendPreBootSignaltoBmc (PeiServices);
  34. if (EFI_ERROR (Status)) {
  35. return Status;
  36. }
  37. }
  38. //
  39. // Enable OEM specific southbridge SIO KCS I/O address range 0xCA0 to 0xCAF at here
  40. // if the the I/O address range has not been enabled.
  41. //
  42. Status = PlatformIpmiIoRangeSet (PcdGet16 (PcdIpmiIoBaseAddress));
  43. DEBUG ((DEBUG_INFO, "IPMI Peim:PlatformIpmiIoRangeSet - %r!\n", Status));
  44. if (EFI_ERROR(Status)) {
  45. return Status;
  46. }
  47. mIpmiInstance = AllocateZeroPool (sizeof (PEI_IPMI_BMC_INSTANCE_DATA));
  48. if (mIpmiInstance == NULL) {
  49. DEBUG ((EFI_D_ERROR,"IPMI Peim:EFI_OUT_OF_RESOURCES of memory allocation\n"));
  50. return EFI_OUT_OF_RESOURCES;
  51. }
  52. //
  53. // Calibrate TSC Counter. Stall for 10ms, then multiply the resulting number of
  54. // ticks in that period by 100 to get the number of ticks in a 1 second timeout
  55. //
  56. DEBUG ((DEBUG_INFO,"IPMI Peim:IPMI STACK Initialization\n"));
  57. mIpmiInstance->KcsTimeoutPeriod = (BMC_KCS_TIMEOUT_PEI *1000*1000) / KCS_DELAY_UNIT_PEI;
  58. DEBUG ((EFI_D_INFO,"IPMI Peim:KcsTimeoutPeriod = 0x%x\n", mIpmiInstance->KcsTimeoutPeriod));
  59. //
  60. // Initialize IPMI IO Base.
  61. //
  62. mIpmiInstance->IpmiIoBase = PcdGet16 (PcdIpmiIoBaseAddress);
  63. DEBUG ((EFI_D_INFO,"IPMI Peim:IpmiIoBase=0x%x\n",mIpmiInstance->IpmiIoBase));
  64. mIpmiInstance->Signature = SM_IPMI_BMC_SIGNATURE;
  65. mIpmiInstance->SlaveAddress = BMC_SLAVE_ADDRESS;
  66. mIpmiInstance->BmcStatus = BMC_NOTREADY;
  67. mIpmiInstance->IpmiTransportPpi.IpmiSubmitCommand = PeiIpmiSendCommand;
  68. mIpmiInstance->IpmiTransportPpi.GetBmcStatus = PeiGetIpmiBmcStatus;
  69. mIpmiInstance->PeiIpmiBmcDataDesc.Flags = EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST;
  70. mIpmiInstance->PeiIpmiBmcDataDesc.Guid = &gPeiIpmiTransportPpiGuid;
  71. mIpmiInstance->PeiIpmiBmcDataDesc.Ppi = &mIpmiInstance->IpmiTransportPpi;
  72. //
  73. // Get the Device ID and check if the system is in Force Update mode.
  74. //
  75. Status = GetDeviceId (mIpmiInstance);
  76. if (EFI_ERROR (Status)) {
  77. DEBUG ((EFI_D_ERROR,"IPMI Peim:Get BMC Device Id Failed. Status=%r\n",Status));
  78. }
  79. //
  80. // Do not continue initialization if the BMC is in Force Update Mode.
  81. //
  82. if (mIpmiInstance->BmcStatus == BMC_UPDATE_IN_PROGRESS || mIpmiInstance->BmcStatus == BMC_HARDFAIL) {
  83. return EFI_UNSUPPORTED;
  84. }
  85. //
  86. // Just produce PPI
  87. //
  88. Status = PeiServicesInstallPpi (&mIpmiInstance->PeiIpmiBmcDataDesc);
  89. if (EFI_ERROR (Status)) {
  90. return Status;
  91. }
  92. return EFI_SUCCESS;
  93. }
  94. /*****************************************************************************
  95. @bref
  96. PRE-BOOT signal will be sent in very early PEI phase, to enable necessary KCS access for host boot.
  97. @param[in] PeiServices General purpose services available to every PEIM.
  98. @retval EFI_SUCCESS Indicates that the signal is sent successfully.
  99. **/
  100. EFI_STATUS
  101. SendPreBootSignaltoBmc (
  102. IN CONST EFI_PEI_SERVICES **PeiServices
  103. )
  104. {
  105. EFI_STATUS Status;
  106. EFI_PEI_CPU_IO_PPI *CpuIoPpi;
  107. UINT32 ProvisionPort = 0;
  108. UINT8 PreBoot = 0;
  109. //
  110. // Locate CpuIo service
  111. //
  112. CpuIoPpi = (**PeiServices).CpuIo;
  113. ProvisionPort = PcdGet32 (PcdSioMailboxBaseAddress) + MBXDAT_B;
  114. PreBoot = 0x01;// PRE-BOOT
  115. Status = CpuIoPpi->Io.Write (
  116. PeiServices,
  117. CpuIoPpi,
  118. EfiPeiCpuIoWidthUint8,
  119. ProvisionPort,
  120. 1,
  121. &PreBoot
  122. );
  123. if (EFI_ERROR (Status)) {
  124. DEBUG ((EFI_D_ERROR, "SendPreBootSignaltoBmc () Write PRE-BOOT Status=%r\n", Status));
  125. return Status;
  126. }
  127. return EFI_SUCCESS;
  128. }
  129. /*****************************************************************************
  130. @bref
  131. The entry point of the Ipmi PEIM. Instals Ipmi PPI interface.
  132. @param FileHandle Handle of the file being invoked.
  133. @param PeiServices Describes the list of possible PEI Services.
  134. @retval EFI_SUCCESS Indicates that Ipmi initialization completed successfully.
  135. **/
  136. EFI_STATUS
  137. PeimIpmiInterfaceInit (
  138. IN EFI_PEI_FILE_HANDLE FileHandle,
  139. IN CONST EFI_PEI_SERVICES **PeiServices
  140. )
  141. {
  142. EFI_STATUS Status;
  143. //
  144. // Performing Ipmi KCS physical layer initialization
  145. //
  146. Status = PeiInitializeIpmiKcsPhysicalLayer (PeiServices);
  147. return Status;
  148. } // PeimIpmiInterfaceInit()
  149. EFI_STATUS
  150. PeiIpmiSendCommand (
  151. IN PEI_IPMI_TRANSPORT_PPI *This,
  152. IN UINT8 NetFunction,
  153. IN UINT8 Lun,
  154. IN UINT8 Command,
  155. IN UINT8 *CommandData,
  156. IN UINT32 CommandDataSize,
  157. IN OUT UINT8 *ResponseData,
  158. IN OUT UINT32 *ResponseDataSize
  159. )
  160. /*++
  161. Routine Description:
  162. Send Ipmi Command in the right mode: HECI or KCS, to the
  163. appropiate device, ME or BMC.
  164. Arguments:
  165. This - Pointer to IPMI protocol instance
  166. NetFunction - Net Function of command to send
  167. Lun - LUN of command to send
  168. Command - IPMI command to send
  169. CommandData - Pointer to command data buffer, if needed
  170. CommandDataSize - Size of command data buffer
  171. ResponseData - Pointer to response data buffer
  172. ResponseDataSize - Pointer to response data buffer size
  173. Returns:
  174. EFI_INVALID_PARAMETER - One of the input values is bad
  175. EFI_DEVICE_ERROR - IPMI command failed
  176. EFI_BUFFER_TOO_SMALL - Response buffer is too small
  177. EFI_UNSUPPORTED - Command is not supported by BMC
  178. EFI_SUCCESS - Command completed successfully
  179. --*/
  180. {
  181. //
  182. // This Will be unchanged ( BMC/KCS style )
  183. //
  184. return PeiIpmiSendCommandToBmc (
  185. This,
  186. NetFunction,
  187. Lun,
  188. Command,
  189. CommandData,
  190. (UINT8) CommandDataSize,
  191. ResponseData,
  192. (UINT8 *) ResponseDataSize,
  193. NULL
  194. );
  195. } // IpmiSendCommand()
  196. EFI_STATUS
  197. PeiGetIpmiBmcStatus (
  198. IN PEI_IPMI_TRANSPORT_PPI *This,
  199. OUT BMC_STATUS *BmcStatus,
  200. OUT SM_COM_ADDRESS *ComAddress
  201. )
  202. /*++
  203. Routine Description:
  204. Updates the BMC status and returns the Com Address
  205. Arguments:
  206. This - Pointer to IPMI protocol instance
  207. BmcStatus - BMC status
  208. ComAddress - Com Address
  209. Returns:
  210. EFI_SUCCESS - Success
  211. --*/
  212. {
  213. return PeiIpmiBmcStatus (
  214. This,
  215. BmcStatus,
  216. ComAddress,
  217. NULL
  218. );
  219. }
  220. EFI_STATUS
  221. GetDeviceId (
  222. IN PEI_IPMI_BMC_INSTANCE_DATA *mIpmiInstance
  223. )
  224. /*++
  225. Routine Description:
  226. Execute the Get Device ID command to determine whether or not the BMC is in Force Update
  227. Mode. If it is, then report it to the error manager.
  228. Arguments:
  229. mIpmiInstance - Data structure describing BMC variables and used for sending commands
  230. StatusCodeValue - An array used to accumulate error codes for later reporting.
  231. ErrorCount - Counter used to keep track of error codes in StatusCodeValue
  232. Returns:
  233. Status
  234. --*/
  235. {
  236. EFI_STATUS Status;
  237. UINT32 DataSize;
  238. SM_CTRL_INFO *pBmcInfo;
  239. UINTN Retries;
  240. UINT8 TempData[MAX_TEMP_DATA];
  241. //
  242. // Set up a loop to retry for up to PcdIpmiBmcReadyDelayTimer seconds. Calculate retries not timeout
  243. // so that in case KCS is not enabled and IpmiSendCommand() returns
  244. // immediately we will not wait all the PcdIpmiBmcReadyDelayTimer seconds.
  245. //
  246. Retries = PcdGet8 (PcdIpmiBmcReadyDelayTimer);
  247. //
  248. // Get the device ID information for the BMC.
  249. //
  250. DataSize = sizeof (TempData);
  251. while (EFI_ERROR (Status = PeiIpmiSendCommand (
  252. &mIpmiInstance->IpmiTransportPpi,
  253. IPMI_NETFN_APP,
  254. 0,
  255. IPMI_APP_GET_DEVICE_ID,
  256. NULL,
  257. 0,
  258. TempData,
  259. &DataSize
  260. ))) {
  261. DEBUG ((EFI_D_ERROR, "[IPMI] BMC does not respond (status: %r), %d retries left\n",
  262. Status, Retries));
  263. if (Retries-- == 0) {
  264. ReportStatusCode (EFI_ERROR_CODE | EFI_ERROR_MAJOR, EFI_COMPUTING_UNIT_FIRMWARE_PROCESSOR | EFI_CU_FP_EC_COMM_ERROR);
  265. mIpmiInstance->BmcStatus = BMC_HARDFAIL;
  266. return Status;
  267. }
  268. //
  269. // Handle the case that BMC FW still not enable KCS channel after AC cycle. just stall 1 second
  270. //
  271. MicroSecondDelay (1*1000*1000);
  272. }
  273. pBmcInfo = (SM_CTRL_INFO*) &TempData[0];
  274. DEBUG ((DEBUG_INFO, "[IPMI PEI] BMC Device ID: 0x%02X, firmware version: %d.%02X UpdateMode:%x\n",
  275. pBmcInfo->DeviceId, pBmcInfo->MajorFirmwareRev, pBmcInfo->MinorFirmwareRev, pBmcInfo->UpdateMode));
  276. //
  277. // In OpenBMC, UpdateMode: the bit 7 of byte 4 in get device id command is used for the BMC status:
  278. // 0 means BMC is ready, 1 means BMC is not ready.
  279. // 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.
  280. //
  281. if (pBmcInfo->UpdateMode == BMC_READY) {
  282. mIpmiInstance->BmcStatus = BMC_OK;
  283. return EFI_SUCCESS;
  284. } else {
  285. //
  286. // Updatemode = 1 mean BMC is not ready, continue waiting.
  287. //
  288. while (Retries-- != 0) {
  289. MicroSecondDelay(1*1000*1000); //delay 1 seconds
  290. DEBUG ((DEBUG_INFO, "[IPMI PEI] UpdateMode Retries:%x \n",Retries));
  291. Status = PeiIpmiSendCommand (
  292. &mIpmiInstance->IpmiTransportPpi,
  293. IPMI_NETFN_APP,
  294. 0,
  295. IPMI_APP_GET_DEVICE_ID,
  296. NULL,
  297. 0,
  298. TempData,
  299. &DataSize
  300. );
  301. if (!EFI_ERROR (Status)) {
  302. pBmcInfo = (SM_CTRL_INFO*) &TempData[0];
  303. DEBUG ((DEBUG_INFO, "[IPMI PEI] UpdateMode Retries:%x pBmcInfo->UpdateMode:%x\n", Retries, pBmcInfo->UpdateMode));
  304. if (pBmcInfo->UpdateMode == BMC_READY) {
  305. mIpmiInstance->BmcStatus = BMC_OK;
  306. return EFI_SUCCESS;
  307. }
  308. }
  309. }
  310. }
  311. mIpmiInstance->BmcStatus = BMC_HARDFAIL;
  312. return Status;
  313. } // GetDeviceId()