KcsCommon.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. /** @file
  2. KCS instance of Manageability Transport Library
  3. Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.<BR>
  4. SPDX-License-Identifier: BSD-2-Clause-Patent
  5. **/
  6. #include <Uefi.h>
  7. #include <IndustryStandard/IpmiKcs.h>
  8. #include <Library/BaseMemoryLib.h>
  9. #include <Library/IoLib.h>
  10. #include <Library/DebugLib.h>
  11. #include <Library/MemoryAllocationLib.h>
  12. #include <Library/TimerLib.h>
  13. #include "ManageabilityTransportKcs.h"
  14. extern MANAGEABILITY_TRANSPORT_KCS_HARDWARE_INFO mKcsHardwareInfo;
  15. /**
  16. This function waits for parameter Flag to set.
  17. Checks status flag in every 1ms internal till 5 seconds elapses.
  18. @param[in] Flag KCS Flag to test.
  19. @retval EFI_SUCCESS The KCS flag under test is set.
  20. @retval EFI_TIMEOUT The KCS flag didn't set in 5 second windows.
  21. **/
  22. EFI_STATUS
  23. WaitStatusSet (
  24. IN UINT8 Flag
  25. )
  26. {
  27. UINT64 Timeout = 0;
  28. while (!(KcsRegisterRead8 (KCS_REG_STATUS) & Flag)) {
  29. MicroSecondDelay (IPMI_KCS_TIMEOUT_1MS);
  30. Timeout = Timeout + IPMI_KCS_TIMEOUT_1MS;
  31. if (Timeout >= IPMI_KCS_TIMEOUT_5_SEC) {
  32. return EFI_TIMEOUT;
  33. }
  34. }
  35. return EFI_SUCCESS;
  36. }
  37. /**
  38. This function waits for parameter Flag to get cleared.
  39. Checks status flag in every 1ms internal till 5 seconds elapses.
  40. @param[in] Flag KCS Flag to test.
  41. @retval EFI_SUCCESS The KCS flag under test is clear.
  42. @retval EFI_TIMEOUT The KCS flag didn't cleared in 5 second windows.
  43. **/
  44. EFI_STATUS
  45. WaitStatusClear (
  46. IN UINT8 Flag
  47. )
  48. {
  49. UINT64 Timeout = 0;
  50. while (KcsRegisterRead8 (KCS_REG_STATUS) & Flag) {
  51. MicroSecondDelay (IPMI_KCS_TIMEOUT_1MS);
  52. Timeout = Timeout + IPMI_KCS_TIMEOUT_1MS;
  53. if (Timeout >= IPMI_KCS_TIMEOUT_5_SEC) {
  54. return EFI_TIMEOUT;
  55. }
  56. }
  57. return EFI_SUCCESS;
  58. }
  59. /**
  60. This function validates KCS OBF bit.
  61. Checks whether OBF bit is set or not.
  62. @retval EFI_SUCCESS OBF bit is set.
  63. @retval EFI_NOT_READY OBF bit is not set.
  64. **/
  65. EFI_STATUS
  66. ClearOBF (
  67. VOID
  68. )
  69. {
  70. if (KcsRegisterRead8 (KCS_REG_STATUS) & IPMI_KCS_OBF) {
  71. KcsRegisterRead8 (KCS_REG_DATA_IN); // read the data to clear the OBF
  72. if (KcsRegisterRead8 (KCS_REG_STATUS) & IPMI_KCS_OBF) {
  73. return EFI_NOT_READY;
  74. }
  75. }
  76. return EFI_SUCCESS;
  77. }
  78. /**
  79. This function writes/sends data to the KCS port.
  80. Algorithm is based on flow chart provided in IPMI spec 2.0
  81. Figure 9-6, KCS Interface BMC to SMS Write Transfer Flow Chart
  82. @param[in] NetFunction Net function of the command.
  83. @param[in] Command IPMI Command.
  84. @param[in] RequestData Command Request Data, could be NULL.
  85. RequestDataSize must be zero, if RequestData
  86. is NULL.
  87. @param[in] RequestDataSize Size of Command Request Data.
  88. @retval EFI_SUCCESS The command byte stream was successfully
  89. submit to the device and a response was
  90. successfully received.
  91. @retval EFI_NOT_FOUND The command was not successfully sent to the
  92. device or a response was not successfully
  93. received from the device.
  94. @retval EFI_NOT_READY Ipmi Device is not ready for Ipmi command
  95. access.
  96. @retval EFI_DEVICE_ERROR Ipmi Device hardware error.
  97. @retval EFI_TIMEOUT The command time out.
  98. @retval EFI_UNSUPPORTED The command was not successfully sent to
  99. the device.
  100. @retval EFI_OUT_OF_RESOURCES The resource allocation is out of resource or
  101. data size error.
  102. **/
  103. EFI_STATUS
  104. KcsTransportWrite (
  105. IN UINT8 NetFunction,
  106. IN UINT8 Command,
  107. IN UINT8 *RequestData OPTIONAL,
  108. IN UINT32 RequestDataSize
  109. )
  110. {
  111. EFI_STATUS Status;
  112. UINT32 Length;
  113. UINT8 *Buffer;
  114. UINT8 *BufferPtr;
  115. // Validation on RequestData and RequestDataSize.
  116. if ((RequestData == NULL && RequestDataSize != 0) ||
  117. (RequestData != NULL && RequestDataSize == 0)
  118. ) {
  119. DEBUG ((DEBUG_ERROR, "%a: Mismatched values of RequestData or RequestDataSize.\n", __FUNCTION__));
  120. return EFI_INVALID_PARAMETER;
  121. }
  122. Length = sizeof (NetFunction) + sizeof (Command);
  123. if (RequestData != NULL) {
  124. Length = Length + RequestDataSize;
  125. }
  126. Buffer = AllocateZeroPool (Length);
  127. if (Buffer == NULL) {
  128. return EFI_OUT_OF_RESOURCES;
  129. }
  130. //
  131. // Buffer[0] = NetFunction
  132. // Buffer[1] = Command
  133. // Buffer [2..RequestDataSize] = RequestData
  134. //
  135. BufferPtr = Buffer;
  136. CopyMem (BufferPtr, &NetFunction, sizeof (NetFunction));
  137. BufferPtr += sizeof (NetFunction);
  138. CopyMem (BufferPtr, &Command, sizeof (Command));
  139. BufferPtr += sizeof (Command);
  140. if (Length > (sizeof (NetFunction) + sizeof (Command))) {
  141. CopyMem (BufferPtr, RequestData, RequestDataSize);
  142. }
  143. BufferPtr = Buffer;
  144. // Step 1. wait for IBF to get clear
  145. Status = WaitStatusClear (IPMI_KCS_IBF);
  146. if (EFI_ERROR (Status)) {
  147. FreePool (Buffer);
  148. return Status;
  149. }
  150. // Step 2. clear OBF
  151. if (EFI_ERROR (ClearOBF ())) {
  152. FreePool (Buffer);
  153. return EFI_NOT_READY;
  154. }
  155. // Step 3. WR_START to CMD, phase=wr_start
  156. KcsRegisterWrite8 (KCS_REG_COMMAND, IPMI_KCS_CONTROL_CODE_WRITE_START);
  157. // Step 4. wait for IBF to get clear
  158. Status = WaitStatusClear (IPMI_KCS_IBF);
  159. if (EFI_ERROR (Status)) {
  160. FreePool (Buffer);
  161. return Status;
  162. }
  163. // Step 5. check state it should be WRITE_STATE, else exit with error
  164. if (IPMI_KCS_GET_STATE (KcsRegisterRead8 (KCS_REG_STATUS)) != IpmiKcsWriteState) {
  165. FreePool (Buffer);
  166. return EFI_NOT_READY;
  167. }
  168. // Step 6, Clear OBF
  169. if (EFI_ERROR (ClearOBF ())) {
  170. FreePool (Buffer);
  171. return EFI_NOT_READY;
  172. }
  173. while (Length > 1) {
  174. // Step 7, phase wr_data, write one byte of Data
  175. KcsRegisterWrite8 (KCS_REG_DATA_OUT, *BufferPtr);
  176. Length--;
  177. BufferPtr++;
  178. // Step 8. wait for IBF clear
  179. Status = WaitStatusClear (IPMI_KCS_IBF);
  180. if (EFI_ERROR (Status)) {
  181. FreePool (Buffer);
  182. return Status;
  183. }
  184. // Step 9. check state it should be WRITE_STATE, else exit with error
  185. if (IPMI_KCS_GET_STATE (KcsRegisterRead8 (KCS_REG_STATUS)) != IpmiKcsWriteState) {
  186. FreePool (Buffer);
  187. return EFI_NOT_READY;
  188. }
  189. // Step 10
  190. if (EFI_ERROR (ClearOBF ())) {
  191. FreePool (Buffer);
  192. return EFI_NOT_READY;
  193. }
  194. //
  195. // Step 11, check for DATA completion if more than one byte;
  196. // if still need to be transferred then go to step 7 and repeat
  197. //
  198. }
  199. // Step 12, WR_END to CMD
  200. KcsRegisterWrite8 (KCS_REG_COMMAND, IPMI_KCS_CONTROL_CODE_WRITE_END);
  201. // Step 13. wait for IBF to get clear
  202. Status = WaitStatusClear (IPMI_KCS_IBF);
  203. if (EFI_ERROR (Status)) {
  204. FreePool (Buffer);
  205. return Status;
  206. }
  207. // Step 14. check state it should be WRITE_STATE, else exit with error
  208. if (IPMI_KCS_GET_STATE (KcsRegisterRead8 (KCS_REG_STATUS)) != IpmiKcsWriteState) {
  209. FreePool (Buffer);
  210. return EFI_NOT_READY;
  211. }
  212. // Step 15
  213. if (EFI_ERROR (ClearOBF ())) {
  214. FreePool (Buffer);
  215. return EFI_NOT_READY;
  216. }
  217. // Step 16, write the last byte
  218. KcsRegisterWrite8 (KCS_REG_DATA_OUT, *BufferPtr);
  219. FreePool (Buffer);
  220. return EFI_SUCCESS;
  221. }
  222. /**
  223. This function sends/receives data from KCS port.
  224. Algorithm is based on flow chart provided in IPMI spec 2.0
  225. Figure 9-7, KCS Interface BMC to SMS Read Transfer Flow Chart
  226. @param [in] DataBytes Buffer to hold the read Data.
  227. @param [in, out] Length Number of Bytes read from KCS port.
  228. @retval EFI_SUCCESS The command byte stream was
  229. successfully submit to the device and
  230. a response was successfully received.
  231. @retval EFI_NOT_FOUND The command was not successfully sent
  232. to the device or a response was not
  233. successfully received from the
  234. device.
  235. @retval EFI_NOT_READY Ipmi Device is not ready for Ipmi
  236. command access.
  237. @retval EFI_DEVICE_ERROR Ipmi Device hardware error.
  238. @retval EFI_TIMEOUT The command time out.
  239. @retval EFI_UNSUPPORTED The command was not successfully set
  240. to the device.
  241. @retval EFI_OUT_OF_RESOURCES The resource allocation is out of
  242. resource or data size error.
  243. **/
  244. EFI_STATUS
  245. KcsTransportRead (
  246. OUT UINT8 *DataByte,
  247. IN OUT UINT32 *Length
  248. )
  249. {
  250. EFI_STATUS Status;
  251. UINT32 ReadLength;
  252. if (DataByte == NULL || *Length == 0) {
  253. DEBUG ((DEBUG_ERROR, "%a: Either DataByte is NULL or Length is 0.\n", __FUNCTION__));
  254. return EFI_INVALID_PARAMETER;
  255. }
  256. ReadLength = 0;
  257. while (ReadLength < *Length) {
  258. // Step 1. wait for IBF to get clear
  259. Status = WaitStatusClear (IPMI_KCS_IBF);
  260. if (EFI_ERROR (Status)) {
  261. *Length = ReadLength;
  262. return Status;
  263. }
  264. // Step 2. check state it should be READ_STATE, else exit with error
  265. if (IPMI_KCS_GET_STATE (KcsRegisterRead8 (KCS_REG_STATUS)) == IpmiKcsReadState) {
  266. // Step 2.1.1 check of OBF to get clear
  267. Status = WaitStatusSet (IPMI_KCS_OBF);
  268. if (EFI_ERROR (Status)) {
  269. *Length = ReadLength;
  270. return Status;
  271. }
  272. // Step 2.1.2 read data from data out
  273. DataByte[ReadLength++] = KcsRegisterRead8 (KCS_REG_DATA_IN);
  274. Status = WaitStatusClear (IPMI_KCS_IBF);
  275. if (EFI_ERROR (Status)) {
  276. *Length = ReadLength;
  277. return Status;
  278. }
  279. // Step 2.1.3 Write READ byte to data in register.
  280. KcsRegisterWrite8 (KCS_REG_DATA_OUT, IPMI_KCS_CONTROL_CODE_READ);
  281. } else if (IPMI_KCS_GET_STATE (KcsRegisterRead8 (KCS_REG_STATUS)) == IpmiKcsIdleState) {
  282. // Step 2.2.1
  283. Status = WaitStatusSet (IPMI_KCS_OBF);
  284. if (EFI_ERROR (Status)) {
  285. *Length = ReadLength;
  286. return Status;
  287. }
  288. // Step 2.2.2 read dummy data
  289. KcsRegisterRead8 (KCS_REG_DATA_IN); // Dummy read as per IPMI spec
  290. *Length = ReadLength;
  291. return EFI_SUCCESS;
  292. } else {
  293. *Length = ReadLength;
  294. return EFI_DEVICE_ERROR;
  295. }
  296. }
  297. *Length = ReadLength;
  298. return EFI_SUCCESS;
  299. }
  300. /**
  301. This service communicates with BMC using KCS protocol.
  302. @param[in] NetFunction Net function of the command.
  303. @param[in] Command IPMI Command.
  304. @param[in] RequestData Command Request Data.
  305. @param[in] RequestDataSize Size of Command Request Data.
  306. @param[out] ResponseData Command Response Data. The completion
  307. code is the first byte of response
  308. data.
  309. @param[in, out] ResponseDataSize Size of Command Response Data.
  310. When IN, it is the expected data size
  311. of response data.
  312. When OUT, it is the data size of response
  313. exactly returned.
  314. @retval EFI_SUCCESS The command byte stream was
  315. successfully submit to the device and a
  316. response was successfully received.
  317. @retval EFI_NOT_FOUND The command was not successfully sent
  318. to the device or a response was not
  319. successfully received from the device.
  320. @retval EFI_NOT_READY Ipmi Device is not ready for Ipmi
  321. command access.
  322. @retval EFI_DEVICE_ERROR Ipmi Device hardware error.
  323. @retval EFI_TIMEOUT The command time out.
  324. @retval EFI_UNSUPPORTED The command was not successfully sent to
  325. the device.
  326. @retval EFI_OUT_OF_RESOURCES The resource allocation is out of
  327. resource or data size error.
  328. **/
  329. EFI_STATUS
  330. EFIAPI
  331. KcsTransportSendCommand (
  332. IN UINT8 NetFunction,
  333. IN UINT8 Command,
  334. IN UINT8 *RequestData OPTIONAL,
  335. IN UINT32 RequestDataSize,
  336. OUT UINT8 *ResponseData OPTIONAL,
  337. IN OUT UINT32 *ResponseDataSize OPTIONAL
  338. )
  339. {
  340. EFI_STATUS Status;
  341. UINT32 RspHeaderSize;
  342. IPMI_KCS_RESPONSE_HEADER RspHeader;
  343. if ((RequestData != NULL) && (RequestDataSize == 0)) {
  344. DEBUG((DEBUG_ERROR, "%a: Mismatched values of RequestData and RequestDataSize\n", __FUNCTION__));
  345. return EFI_INVALID_PARAMETER;
  346. }
  347. if ((ResponseData != NULL) && ((ResponseDataSize != NULL) && (*ResponseDataSize == 0))) {
  348. DEBUG((DEBUG_ERROR, "%a: Mismatched values of ResponseData and ResponseDataSize\n", __FUNCTION__));
  349. return EFI_INVALID_PARAMETER;
  350. }
  351. Status = KcsTransportWrite (
  352. (NetFunction << 2),
  353. Command,
  354. RequestData,
  355. RequestDataSize
  356. );
  357. if (EFI_ERROR (Status)) {
  358. DEBUG ((
  359. DEBUG_ERROR,
  360. "IPMI KCS Write Failed with Status(%r) for NetFunction(0x%x)," \
  361. " Command(0x%x).\n",
  362. Status,
  363. NetFunction,
  364. Command
  365. ));
  366. return Status;
  367. }
  368. //
  369. // Read the response header
  370. RspHeaderSize = sizeof (IPMI_KCS_RESPONSE_HEADER);
  371. Status = KcsTransportRead ((UINT8 *)&RspHeader, &RspHeaderSize);
  372. if (EFI_ERROR (Status)) {
  373. DEBUG ((
  374. DEBUG_ERROR,
  375. "IPMI KCS read response header failed Status(%r), " \
  376. "RspNetFunctionLun = 0x%x, " \
  377. "Comamnd = 0x%x \n",
  378. Status,
  379. RspHeader.NetFunc,
  380. RspHeader.Command
  381. ));
  382. return (Status);
  383. }
  384. Status = KcsTransportRead ((UINT8 *)ResponseData, ResponseDataSize);
  385. if (EFI_ERROR (Status)) {
  386. DEBUG ((
  387. DEBUG_ERROR,
  388. "IPMI KCS response read Failed with Status(%r) for NetFunction(0x%x)," \
  389. " Command(0x%x).\n",
  390. Status,
  391. NetFunction,
  392. Command
  393. ));
  394. }
  395. return Status;
  396. }
  397. /**
  398. This function reads 8-bit value from register address.
  399. @param[in] Address This represents either 16-bit IO address
  400. or 32-bit memory mapped address.
  401. @retval UINT8 8-bit value.
  402. **/
  403. UINT8
  404. KcsRegisterRead8 (
  405. MANAGEABILITY_TRANSPORT_HARDWARE_IO Address
  406. )
  407. {
  408. UINT8 Value;
  409. if (mKcsHardwareInfo.MemoryMap == MANAGEABILITY_TRANSPORT_KCS_MEMORY_MAP_IO) {
  410. // Read 8-bit value from 32-bit Memory mapped address.
  411. Value = MmioRead8 ((UINTN)Address.IoAddress32);
  412. } else {
  413. // Read 8-bit value from 16-bit I/O address
  414. Value = IoRead8 ((UINTN)Address.IoAddress16);
  415. }
  416. return Value;
  417. }
  418. /**
  419. This function writes 8-bit value to register address.
  420. @param[in] Address This represents either 16-bit IO address
  421. or 32-bit memory mapped address.
  422. @param[in] Value 8-bit value write to register address
  423. **/
  424. VOID
  425. KcsRegisterWrite8 (
  426. MANAGEABILITY_TRANSPORT_HARDWARE_IO Address,
  427. UINT8 Value
  428. )
  429. {
  430. if (mKcsHardwareInfo.MemoryMap == MANAGEABILITY_TRANSPORT_KCS_MEMORY_MAP_IO) {
  431. // Write 8-bit value to 32-bit Memory mapped address.
  432. MmioWrite8 ((UINTN)Address.IoAddress32, Value);
  433. } else {
  434. // Write 8-bit value to 16-bit I/O address
  435. IoWrite8 ((UINTN)Address.IoAddress16, Value);
  436. }
  437. }