IpmiLibKcs.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. /** @file
  2. IPMI library - KCS.
  3. Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
  4. SPDX-License-Identifier: BSD-2-Clause-Patent
  5. **/
  6. #include <PiPei.h>
  7. #include <Library/BaseMemoryLib.h>
  8. #include <Library/DebugLib.h>
  9. #include <Library/HobLib.h>
  10. #include <Library/PcdLib.h>
  11. #include <Library/TimerLib.h>
  12. #include <Library/IpmiPlatformHookLib.h>
  13. #include <IndustryStandard/Ipmi.h>
  14. #include "KcsBmc.h"
  15. #define MAX_TEMP_DATA 160
  16. //
  17. // Structure of IPMI Command buffer
  18. //
  19. #define EFI_IPMI_COMMAND_HEADER_SIZE 2
  20. typedef struct {
  21. UINT8 Lun : 2;
  22. UINT8 NetFunction : 6;
  23. UINT8 Command;
  24. UINT8 CommandData[MAX_TEMP_DATA - EFI_IPMI_COMMAND_HEADER_SIZE];
  25. } EFI_IPMI_COMMAND;
  26. //
  27. // Structure of IPMI Command response buffer
  28. //
  29. #define EFI_IPMI_RESPONSE_HEADER_SIZE 2
  30. typedef struct {
  31. UINT8 Lun : 2;
  32. UINT8 NetFunction : 6;
  33. UINT8 Command;
  34. UINT8 ResponseData[MAX_TEMP_DATA - EFI_IPMI_RESPONSE_HEADER_SIZE];
  35. } EFI_IPMI_RESPONSE;
  36. #define IPMI_INSTANCE_INFO_HOB_GUID { \
  37. 0x38ee71f, 0x1c78, 0x4874, { 0xba, 0xe3, 0xf8, 0xa2, 0x57, 0x75, 0x28, 0x52 } \
  38. }
  39. EFI_GUID mIpmiInstanceGuid = IPMI_INSTANCE_INFO_HOB_GUID;
  40. #define SM_IPMI_BMC_SIGNATURE SIGNATURE_32 ('i', 'p', 'm', 'i')
  41. typedef UINT32 EFI_BMC_STATUS;
  42. typedef struct {
  43. UINTN Signature;
  44. UINT64 KcsTimeoutPeriod;
  45. UINT16 IpmiIoBase;
  46. UINT8 SlaveAddress;
  47. EFI_BMC_STATUS BmcStatus;
  48. UINT64 ErrorStatus;
  49. UINT8 SoftErrorCount;
  50. UINT8 TempData[MAX_TEMP_DATA];
  51. } IPMI_INSTANCE;
  52. #define EFI_BMC_OK 0
  53. #define EFI_BMC_SOFTFAIL 1
  54. #define EFI_BMC_HARDFAIL 2
  55. #define EFI_BMC_UPDATE_IN_PROGRESS 3
  56. #define EFI_BMC_NOTREADY 4
  57. EFI_STATUS
  58. UpdateErrorStatus (
  59. IN UINT8 BmcError,
  60. IPMI_INSTANCE *IpmiInstance
  61. )
  62. /*++
  63. Routine Description:
  64. Check if the completion code is a Soft Error and increment the count. The count
  65. is not updated if the BMC is in Force Update Mode.
  66. Arguments:
  67. BmcError - Completion code to check
  68. IpmiInstance - BMC instance data
  69. Returns:
  70. EFI_SUCCESS - Status
  71. --*/
  72. {
  73. UINT8 Errors[] = {
  74. IPMI_COMP_CODE_NODE_BUSY, IPMI_COMP_CODE_TIMEOUT, IPMI_COMP_CODE_OUT_OF_SPACE, IPMI_COMP_CODE_OUT_OF_RANGE,
  75. IPMI_COMP_CODE_CMD_RESP_NOT_PROVIDED, IPMI_COMP_CODE_FAIL_DUP_REQUEST, IPMI_COMP_CODE_SDR_REP_IN_UPDATE_MODE,
  76. IPMI_COMP_CODE_DEV_IN_FW_UPDATE_MODE, IPMI_COMP_CODE_BMC_INIT_IN_PROGRESS, IPMI_COMP_CODE_UNSPECIFIED
  77. };
  78. UINT16 CodeCount;
  79. UINT8 i;
  80. CodeCount = sizeof (Errors) / sizeof (Errors[0]);
  81. for (i = 0; i < CodeCount; i++) {
  82. if (BmcError == Errors[i]) {
  83. //
  84. // Don't change Bmc Status flag if the BMC is in Force Update Mode.
  85. //
  86. if (IpmiInstance->BmcStatus != EFI_BMC_UPDATE_IN_PROGRESS) {
  87. IpmiInstance->BmcStatus = EFI_BMC_SOFTFAIL;
  88. }
  89. IpmiInstance->SoftErrorCount++;
  90. break;
  91. }
  92. }
  93. return EFI_SUCCESS;
  94. }
  95. VOID
  96. UpdateBmcStatusOnResponse (
  97. IN IPMI_INSTANCE *IpmiInstance,
  98. IN EFI_IPMI_COMMAND *IpmiCommand,
  99. IN EFI_STATUS EfiStatus,
  100. IN EFI_IPMI_RESPONSE *IpmiResponse
  101. )
  102. {
  103. IPMI_GET_DEVICE_ID_RESPONSE *BmcInfo;
  104. IPMI_SELF_TEST_RESULT_RESPONSE *TestResult;
  105. if ((IpmiCommand->NetFunction == IPMI_NETFN_APP) && (IpmiCommand->Command == IPMI_APP_GET_DEVICE_ID)) {
  106. if (EFI_ERROR(EfiStatus)) {
  107. IpmiInstance->BmcStatus = EFI_BMC_HARDFAIL;
  108. } else {
  109. BmcInfo = (VOID *)IpmiResponse->ResponseData;
  110. if (BmcInfo->FirmwareRev1.Bits.UpdateMode) {
  111. IpmiInstance->BmcStatus = EFI_BMC_UPDATE_IN_PROGRESS;
  112. }
  113. }
  114. } else if ((IpmiCommand->NetFunction == IPMI_NETFN_APP) && (IpmiCommand->Command == IPMI_APP_GET_DEVICE_ID)) {
  115. if (EFI_ERROR(EfiStatus)) {
  116. IpmiInstance->BmcStatus = EFI_BMC_HARDFAIL;
  117. } else {
  118. TestResult = (VOID *)IpmiResponse->ResponseData;
  119. switch (TestResult->Result) {
  120. case IPMI_APP_SELFTEST_NO_ERROR:
  121. case IPMI_APP_SELFTEST_NOT_IMPLEMENTED:
  122. IpmiInstance->BmcStatus = EFI_BMC_OK;
  123. break;
  124. case IPMI_APP_SELFTEST_ERROR:
  125. //
  126. // Three of the possible errors result in BMC hard failure; FRU Corruption,
  127. // BootBlock Firmware corruption, and Operational Firmware Corruption. All
  128. // other errors are BMC soft failures.
  129. //
  130. if ((TestResult->Param & (IPMI_APP_SELFTEST_FRU_CORRUPT | IPMI_APP_SELFTEST_FW_BOOTBLOCK_CORRUPT | IPMI_APP_SELFTEST_FW_CORRUPT)) != 0) {
  131. IpmiInstance->BmcStatus = EFI_BMC_HARDFAIL;
  132. } else {
  133. IpmiInstance->BmcStatus = EFI_BMC_SOFTFAIL;
  134. }
  135. break;
  136. case IPMI_APP_SELFTEST_FATAL_HW_ERROR:
  137. IpmiInstance->BmcStatus = EFI_BMC_HARDFAIL;
  138. break;
  139. default:
  140. break;
  141. }
  142. }
  143. }
  144. }
  145. /**
  146. This service enables submitting commands via Ipmi.
  147. @param[in] NetFunction Net function of the command.
  148. @param[in] Command IPMI Command.
  149. @param[in] RequestData Command Request Data.
  150. @param[in] RequestDataSize Size of Command Request Data.
  151. @param[out] ResponseData Command Response Data. The completion code is the first byte of response data.
  152. @param[in, out] ResponseDataSize Size of Command Response Data.
  153. @retval EFI_SUCCESS The command byte stream was successfully submit to the device and a response was successfully received.
  154. @retval EFI_NOT_FOUND The command was not successfully sent to the device or a response was not successfully received from the device.
  155. @retval EFI_NOT_READY Ipmi Device is not ready for Ipmi command access.
  156. @retval EFI_DEVICE_ERROR Ipmi Device hardware error.
  157. @retval EFI_TIMEOUT The command time out.
  158. @retval EFI_UNSUPPORTED The command was not successfully sent to the device.
  159. @retval EFI_OUT_OF_RESOURCES The resource allcation is out of resource or data size error.
  160. **/
  161. EFI_STATUS
  162. EFIAPI
  163. IpmiSubmitCommand (
  164. IN UINT8 NetFunction,
  165. IN UINT8 Command,
  166. IN UINT8 *RequestData,
  167. IN UINT32 RequestDataSize,
  168. OUT UINT8 *ResponseData,
  169. IN OUT UINT32 *ResponseDataSize
  170. )
  171. {
  172. UINT8 DataSize;
  173. EFI_STATUS Status;
  174. EFI_IPMI_COMMAND *IpmiCommand;
  175. EFI_IPMI_RESPONSE *IpmiResponse;
  176. VOID *Hob;
  177. IPMI_INSTANCE *IpmiInstance;
  178. DEBUG ((DEBUG_INFO, "IpmiSubmitCommand\n"));
  179. Hob = GetFirstGuidHob (&mIpmiInstanceGuid);
  180. if (Hob != NULL) {
  181. IpmiInstance = GET_GUID_HOB_DATA(Hob);
  182. } else {
  183. IpmiInstance = BuildGuidHob (&mIpmiInstanceGuid, sizeof(IPMI_INSTANCE));
  184. ASSERT(IpmiInstance != NULL);
  185. if (IpmiInstance == NULL) {
  186. return EFI_OUT_OF_RESOURCES;
  187. }
  188. IpmiInstance->Signature = SM_IPMI_BMC_SIGNATURE;
  189. IpmiInstance->KcsTimeoutPeriod = PcdGet64(PcdIpmiKcsTimeoutPeriod);
  190. IpmiInstance->SlaveAddress = PcdGet8(PcdIpmiBmcSlaveAddress);
  191. IpmiInstance->IpmiIoBase = PcdGet16(PcdIpmiIoBaseAddress);
  192. DEBUG((DEBUG_INFO,"IPMI KcsTimeoutPeriod=0x%x\n", IpmiInstance->KcsTimeoutPeriod));
  193. DEBUG((DEBUG_INFO,"IPMI SlaveAddress=0x%x\n", IpmiInstance->SlaveAddress));
  194. DEBUG((DEBUG_INFO,"IPMI IpmiIoBase=0x%x\n", IpmiInstance->IpmiIoBase));
  195. IpmiInstance->BmcStatus = EFI_BMC_NOTREADY;
  196. IpmiInstance->ErrorStatus = 0x00;
  197. IpmiInstance->SoftErrorCount = 0x00;
  198. MicroSecondDelay(10*1000);
  199. Status = PlatformIpmiIoRangeSet (IpmiInstance->IpmiIoBase);
  200. DEBUG ((DEBUG_INFO, "IPMI PlatformIpmiIoRangeSet - %r!\n", Status));
  201. if (EFI_ERROR(Status)) {
  202. return Status;
  203. }
  204. }
  205. IpmiCommand = (VOID *)IpmiInstance->TempData;
  206. IpmiResponse = (VOID *)IpmiInstance->TempData;
  207. //
  208. // Send IPMI command to BMC
  209. //
  210. IpmiCommand->Lun = 0;
  211. IpmiCommand->NetFunction = NetFunction;
  212. IpmiCommand->Command = Command;
  213. //
  214. // Ensure that the buffer is valid before attempting to copy the command data
  215. // buffer into the IpmiCommand structure.
  216. //
  217. if (RequestDataSize > 0) {
  218. if (RequestData == NULL) {
  219. return EFI_INVALID_PARAMETER;
  220. }
  221. CopyMem (
  222. IpmiCommand->CommandData,
  223. RequestData,
  224. RequestDataSize
  225. );
  226. }
  227. Status = SendDataToBmcPort (
  228. IpmiInstance->KcsTimeoutPeriod,
  229. IpmiInstance->IpmiIoBase,
  230. (UINT8 *)IpmiCommand,
  231. (UINT8)(RequestDataSize + EFI_IPMI_COMMAND_HEADER_SIZE)
  232. );
  233. if (Status != EFI_SUCCESS) {
  234. IpmiInstance->BmcStatus = EFI_BMC_SOFTFAIL;
  235. IpmiInstance->SoftErrorCount++;
  236. UpdateBmcStatusOnResponse (IpmiInstance, IpmiCommand, Status, NULL);
  237. return Status;
  238. }
  239. //
  240. // Get Response to IPMI Command from BMC.
  241. //
  242. DataSize = MAX_TEMP_DATA;
  243. Status = ReceiveBmcDataFromPort (
  244. IpmiInstance->KcsTimeoutPeriod,
  245. IpmiInstance->IpmiIoBase,
  246. (UINT8 *)IpmiResponse,
  247. &DataSize
  248. );
  249. if (Status != EFI_SUCCESS) {
  250. IpmiInstance->BmcStatus = EFI_BMC_SOFTFAIL;
  251. IpmiInstance->SoftErrorCount++;
  252. UpdateBmcStatusOnResponse (IpmiInstance, IpmiCommand, Status, NULL);
  253. return Status;
  254. }
  255. //
  256. // If we got this far without any error codes, but the DataSize is 0 then the
  257. // command response failed, so do not continue.
  258. //
  259. if (DataSize < 3) {
  260. Status = EFI_DEVICE_ERROR;
  261. IpmiInstance->BmcStatus = EFI_BMC_SOFTFAIL;
  262. IpmiInstance->SoftErrorCount++;
  263. UpdateBmcStatusOnResponse (IpmiInstance, IpmiCommand, Status, NULL);
  264. return Status;
  265. }
  266. if ((IpmiResponse->ResponseData[0] != IPMI_COMP_CODE_NORMAL) &&
  267. (IpmiInstance->BmcStatus == EFI_BMC_UPDATE_IN_PROGRESS)) {
  268. //
  269. // If the completion code is not normal and the BMC is in Force Update
  270. // mode, then update the error status and return EFI_UNSUPPORTED.
  271. //
  272. UpdateErrorStatus (
  273. IpmiResponse->ResponseData[0],
  274. IpmiInstance
  275. );
  276. UpdateBmcStatusOnResponse (IpmiInstance, IpmiCommand, Status, IpmiResponse);
  277. return EFI_UNSUPPORTED;
  278. } else if (IpmiResponse->ResponseData[0] != IPMI_COMP_CODE_NORMAL) {
  279. //
  280. // Otherwise if the BMC is in normal mode, but the completion code
  281. // is not normal, then update the error status and return device error.
  282. //
  283. UpdateErrorStatus (
  284. IpmiResponse->ResponseData[0],
  285. IpmiInstance
  286. );
  287. UpdateBmcStatusOnResponse (IpmiInstance, IpmiCommand, Status, IpmiResponse);
  288. return EFI_DEVICE_ERROR;
  289. }
  290. //
  291. // Verify the response data buffer passed in is big enough.
  292. //
  293. if ((UINTN)(DataSize - EFI_IPMI_RESPONSE_HEADER_SIZE) > *ResponseDataSize) {
  294. return EFI_BUFFER_TOO_SMALL;
  295. }
  296. //
  297. // Copy data over to the response data buffer.
  298. //
  299. if ((ResponseData != NULL) && (ResponseDataSize != NULL) && (*ResponseDataSize != 0)) {
  300. *ResponseDataSize = DataSize - EFI_IPMI_RESPONSE_HEADER_SIZE;
  301. CopyMem (
  302. ResponseData,
  303. IpmiResponse->ResponseData,
  304. *ResponseDataSize
  305. );
  306. }
  307. IpmiInstance->BmcStatus = EFI_BMC_OK;
  308. return EFI_SUCCESS;
  309. }