| /** @file | |
| KCS instance of Manageability Transport Library | |
| Copyright (C) 2023-2026 Advanced Micro Devices, Inc. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <Uefi.h> | |
| #include <IndustryStandard/IpmiKcs.h> | |
| #include <IndustryStandard/Mctp.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/IoLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/ManageabilityTransportHelperLib.h> | |
| #include <Library/ManageabilityTransportMctpLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Library/TimerLib.h> | |
| #include "ManageabilityTransportKcs.h" | |
| extern MANAGEABILITY_TRANSPORT_KCS_HARDWARE_INFO mKcsHardwareInfo; | |
| extern MANAGEABILITY_TRANSPORT_KCS *mSingleSessionToken; | |
| /** | |
| This function waits for parameter Flag to set. | |
| Checks status flag in every 1ms internal till 5 seconds elapses. | |
| @param[in] Flag KCS Flag to test. | |
| @retval EFI_SUCCESS The KCS flag under test is set. | |
| @retval EFI_TIMEOUT The KCS flag didn't set in 5 second windows. | |
| **/ | |
| EFI_STATUS | |
| WaitStatusSet ( | |
| IN UINT8 Flag | |
| ) | |
| { | |
| UINT64 Timeout; | |
| Timeout = 0; | |
| while ((KcsRegisterRead8 (KCS_REG_STATUS) & Flag) != TRUE) { | |
| MicroSecondDelay (IPMI_KCS_TIMEOUT_1MS); | |
| Timeout = Timeout + IPMI_KCS_TIMEOUT_1MS; | |
| if (Timeout >= IPMI_KCS_TIMEOUT_5_SEC) { | |
| return EFI_TIMEOUT; | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| This function waits for parameter Flag to get cleared. | |
| Checks status flag in every 1ms internal till 5 seconds elapses. | |
| @param[in] Flag KCS Flag to test. | |
| @retval EFI_SUCCESS The KCS flag under test is clear. | |
| @retval EFI_TIMEOUT The KCS flag didn't cleared in 5 second windows. | |
| **/ | |
| EFI_STATUS | |
| WaitStatusClear ( | |
| IN UINT8 Flag | |
| ) | |
| { | |
| UINT64 Timeout; | |
| Timeout = 0; | |
| while (KcsRegisterRead8 (KCS_REG_STATUS) & Flag) { | |
| MicroSecondDelay (IPMI_KCS_TIMEOUT_1MS); | |
| Timeout = Timeout + IPMI_KCS_TIMEOUT_1MS; | |
| if (Timeout >= IPMI_KCS_TIMEOUT_5_SEC) { | |
| return EFI_TIMEOUT; | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| This function validates KCS OBF bit. | |
| Checks whether OBF bit is set or not. | |
| @retval EFI_SUCCESS OBF bit is set. | |
| @retval EFI_NOT_READY OBF bit is not set. | |
| **/ | |
| EFI_STATUS | |
| ClearOBF ( | |
| VOID | |
| ) | |
| { | |
| if (KcsRegisterRead8 (KCS_REG_STATUS) & IPMI_KCS_OBF) { | |
| KcsRegisterRead8 (KCS_REG_DATA_IN); // read the data to clear the OBF | |
| if (KcsRegisterRead8 (KCS_REG_STATUS) & IPMI_KCS_OBF) { | |
| return EFI_NOT_READY; | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| This function writes/sends data to the KCS port. | |
| Algorithm is based on flow chart provided in IPMI spec 2.0 | |
| Figure 9-6, KCS Interface BMC to SMS Write Transfer Flow Chart | |
| @param[in] TransmitHeader KCS packet header. | |
| @param[in] TransmitHeaderSize KCS packet header size in byte. | |
| @param[in] TransmitTrailer KCS packet trailer. | |
| @param[in] TransmitTrailerSize KCS packet trailer size in byte. | |
| @param[in] RequestData Command Request Data, could be NULL. | |
| RequestDataSize must be zero, if RequestData | |
| is NULL. | |
| @param[in] RequestDataSize Size of Command Request Data. | |
| @retval EFI_SUCCESS The command byte stream was successfully | |
| submit to the device and a response was | |
| successfully received. | |
| @retval EFI_NOT_FOUND The command was not successfully sent to the | |
| device or a response was not successfully | |
| received from the device. | |
| @retval EFI_NOT_READY Ipmi Device is not ready for Ipmi command | |
| access. | |
| @retval EFI_DEVICE_ERROR Ipmi Device hardware error. | |
| @retval EFI_TIMEOUT The command time out. | |
| @retval EFI_UNSUPPORTED The command was not successfully sent to | |
| the device. | |
| @retval EFI_OUT_OF_RESOURCES The resource allocation is out of resource or | |
| data size error. | |
| **/ | |
| EFI_STATUS | |
| KcsTransportWrite ( | |
| IN MANAGEABILITY_TRANSPORT_HEADER TransmitHeader, | |
| IN UINT16 TransmitHeaderSize, | |
| IN MANAGEABILITY_TRANSPORT_TRAILER TransmitTrailer OPTIONAL, | |
| IN UINT16 TransmitTrailerSize, | |
| IN UINT8 *RequestData OPTIONAL, | |
| IN UINT32 RequestDataSize | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT32 Length; | |
| UINT8 *Buffer; | |
| UINT8 *BufferPtr; | |
| // Validation on RequestData and RequestDataSize. | |
| if (((RequestData == NULL) && (RequestDataSize != 0)) || | |
| ((RequestData != NULL) && (RequestDataSize == 0)) | |
| ) | |
| { | |
| DEBUG ((DEBUG_ERROR, "%a: Mismatched values of RequestData or RequestDataSize.\n", __func__)); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // Validation on TransmitHeader and TransmitHeaderSize. | |
| if (((TransmitHeader == NULL) && (TransmitHeaderSize != 0)) || | |
| ((TransmitHeader != NULL) && (TransmitHeaderSize == 0)) | |
| ) | |
| { | |
| DEBUG ((DEBUG_ERROR, "%a: Mismatched values of TransmitHeader or TransmitHeaderSize.\n", __func__)); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // Validation on TransmitHeader and TransmitHeaderSize. | |
| if (((TransmitTrailer == NULL) && (TransmitTrailerSize != 0)) || | |
| ((TransmitTrailer != NULL) && (TransmitTrailerSize == 0)) | |
| ) | |
| { | |
| DEBUG ((DEBUG_ERROR, "%a: Mismatched values of TransmitTrailer or TransmitTrailerSize.\n", __func__)); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Length = TransmitHeaderSize; | |
| if (RequestData != NULL) { | |
| Length = Length + RequestDataSize; | |
| } | |
| if ((TransmitTrailer != NULL) && (TransmitTrailerSize != 0)) { | |
| Length += TransmitTrailerSize; | |
| } | |
| Buffer = AllocateZeroPool (Length); | |
| if (Buffer == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| // | |
| // Buffer[0..(TransmitHeaderSize - 1)] = TransmitHeader | |
| // Buffer [TransmitHeader..(TransmitHeader + RequestDataSize - 1)] = RequestData | |
| // Buffer [(TransmitHeader + RequestDataSize)..(TransmitHeader + RequestDataSize + TransmitTrailerSize - 1)] = TransmitTrailer | |
| // | |
| BufferPtr = Buffer; | |
| CopyMem ((VOID *)BufferPtr, (VOID *)TransmitHeader, TransmitHeaderSize); | |
| BufferPtr += TransmitHeaderSize; | |
| if (RequestData != NULL) { | |
| CopyMem (BufferPtr, RequestData, RequestDataSize); | |
| } | |
| BufferPtr += RequestDataSize; | |
| if (TransmitTrailer != NULL) { | |
| CopyMem (BufferPtr, (VOID *)TransmitTrailer, TransmitTrailerSize); | |
| } | |
| BufferPtr = Buffer; | |
| // Step 1. wait for IBF to get clear | |
| Status = WaitStatusClear (IPMI_KCS_IBF); | |
| if (EFI_ERROR (Status)) { | |
| FreePool (Buffer); | |
| return Status; | |
| } | |
| // Step 2. clear OBF | |
| if (EFI_ERROR (ClearOBF ())) { | |
| FreePool (Buffer); | |
| return EFI_NOT_READY; | |
| } | |
| // Step 3. WR_START to CMD, phase=wr_start | |
| KcsRegisterWrite8 (KCS_REG_COMMAND, IPMI_KCS_CONTROL_CODE_WRITE_START); | |
| // Step 4. wait for IBF to get clear | |
| Status = WaitStatusClear (IPMI_KCS_IBF); | |
| if (EFI_ERROR (Status)) { | |
| FreePool (Buffer); | |
| return Status; | |
| } | |
| // Step 5. check state it should be WRITE_STATE, else exit with error | |
| if (IPMI_KCS_GET_STATE (KcsRegisterRead8 (KCS_REG_STATUS)) != IpmiKcsWriteState) { | |
| FreePool (Buffer); | |
| return EFI_NOT_READY; | |
| } | |
| // Step 6, Clear OBF | |
| if (EFI_ERROR (ClearOBF ())) { | |
| FreePool (Buffer); | |
| return EFI_NOT_READY; | |
| } | |
| while (Length > 1) { | |
| // Step 7, phase wr_data, write one byte of Data | |
| KcsRegisterWrite8 (KCS_REG_DATA_OUT, *BufferPtr); | |
| Length--; | |
| BufferPtr++; | |
| // Step 8. wait for IBF clear | |
| Status = WaitStatusClear (IPMI_KCS_IBF); | |
| if (EFI_ERROR (Status)) { | |
| FreePool (Buffer); | |
| return Status; | |
| } | |
| // Step 9. check state it should be WRITE_STATE, else exit with error | |
| if (IPMI_KCS_GET_STATE (KcsRegisterRead8 (KCS_REG_STATUS)) != IpmiKcsWriteState) { | |
| FreePool (Buffer); | |
| return EFI_NOT_READY; | |
| } | |
| // Step 10 | |
| if (EFI_ERROR (ClearOBF ())) { | |
| FreePool (Buffer); | |
| return EFI_NOT_READY; | |
| } | |
| // | |
| // Step 11, check for DATA completion if more than one byte; | |
| // if still need to be transferred then go to step 7 and repeat | |
| // | |
| } | |
| // Step 12, WR_END to CMD | |
| KcsRegisterWrite8 (KCS_REG_COMMAND, IPMI_KCS_CONTROL_CODE_WRITE_END); | |
| // Step 13. wait for IBF to get clear | |
| Status = WaitStatusClear (IPMI_KCS_IBF); | |
| if (EFI_ERROR (Status)) { | |
| FreePool (Buffer); | |
| return Status; | |
| } | |
| // Step 14. check state it should be WRITE_STATE, else exit with error | |
| if (IPMI_KCS_GET_STATE (KcsRegisterRead8 (KCS_REG_STATUS)) != IpmiKcsWriteState) { | |
| FreePool (Buffer); | |
| return EFI_NOT_READY; | |
| } | |
| // Step 15 | |
| if (EFI_ERROR (ClearOBF ())) { | |
| FreePool (Buffer); | |
| return EFI_NOT_READY; | |
| } | |
| // Step 16, write the last byte | |
| KcsRegisterWrite8 (KCS_REG_DATA_OUT, *BufferPtr); | |
| FreePool (Buffer); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| This function sends/receives data from KCS port. | |
| Algorithm is based on flow chart provided in IPMI spec 2.0 | |
| Figure 9-7, KCS Interface BMC to SMS Read Transfer Flow Chart | |
| @param [out] DataBytes Buffer to hold the read Data. | |
| @param [in, out] Length Number of Bytes read from KCS port. | |
| @retval EFI_SUCCESS The command byte stream was | |
| successfully submit to the device and | |
| a response was successfully received. | |
| @retval EFI_NOT_FOUND The command was not successfully sent | |
| to the device or a response was not | |
| successfully received from the | |
| device. | |
| @retval EFI_NOT_READY Ipmi Device is not ready for Ipmi | |
| command access. | |
| @retval EFI_DEVICE_ERROR Ipmi Device hardware error. | |
| @retval EFI_TIMEOUT The command time out. | |
| @retval EFI_UNSUPPORTED The command was not successfully set | |
| to the device. | |
| @retval EFI_OUT_OF_RESOURCES The resource allocation is out of | |
| resource or data size error. | |
| **/ | |
| EFI_STATUS | |
| KcsTransportRead ( | |
| OUT UINT8 *DataByte, | |
| IN OUT UINT32 *Length | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT32 ReadLength; | |
| if ((DataByte == NULL) || (*Length == 0)) { | |
| DEBUG ((DEBUG_ERROR, "%a: Either DataByte is NULL or Length is 0.\n", __func__)); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| ReadLength = 0; | |
| while (ReadLength < *Length) { | |
| // Step 1. wait for IBF to get clear | |
| Status = WaitStatusClear (IPMI_KCS_IBF); | |
| if (EFI_ERROR (Status)) { | |
| *Length = ReadLength; | |
| return Status; | |
| } | |
| // Step 2. check state it should be READ_STATE, else exit with error | |
| if (IPMI_KCS_GET_STATE (KcsRegisterRead8 (KCS_REG_STATUS)) == IpmiKcsReadState) { | |
| // Step 2.1.1 check of OBF to get clear | |
| Status = WaitStatusSet (IPMI_KCS_OBF); | |
| if (EFI_ERROR (Status)) { | |
| *Length = ReadLength; | |
| return Status; | |
| } | |
| // Step 2.1.2 read data from data out | |
| DataByte[ReadLength++] = KcsRegisterRead8 (KCS_REG_DATA_IN); | |
| Status = WaitStatusClear (IPMI_KCS_IBF); | |
| if (EFI_ERROR (Status)) { | |
| *Length = ReadLength; | |
| return Status; | |
| } | |
| // Step 2.1.3 Write READ byte to data in register. | |
| KcsRegisterWrite8 (KCS_REG_DATA_OUT, IPMI_KCS_CONTROL_CODE_READ); | |
| } else if (IPMI_KCS_GET_STATE (KcsRegisterRead8 (KCS_REG_STATUS)) == IpmiKcsIdleState) { | |
| // Step 2.2.1 | |
| Status = WaitStatusSet (IPMI_KCS_OBF); | |
| if (EFI_ERROR (Status)) { | |
| *Length = ReadLength; | |
| return Status; | |
| } | |
| // Step 2.2.2 read dummy data | |
| KcsRegisterRead8 (KCS_REG_DATA_IN); // Dummy read as per IPMI spec | |
| *Length = ReadLength; | |
| return EFI_SUCCESS; | |
| } else { | |
| *Length = ReadLength; | |
| return EFI_DEVICE_ERROR; | |
| } | |
| } | |
| *Length = ReadLength; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| This function checks the KCS response data according to | |
| manageability protocol. | |
| @param[in] ResponseData Pointer to response data. | |
| @param[in] ResponseDataSize Size of response data. | |
| @param[out] AdditionalStatus Pointer to receive the additional status. | |
| @retval EFI_SUCCESS KCS response header is checked without error | |
| @retval EFI_DEVICE_ERROR KCS response header has problem. | |
| **/ | |
| EFI_STATUS | |
| KcsCheckResponseData ( | |
| IN UINT8 *ResponseData, | |
| IN UINT32 ResponseDataSize, | |
| OUT MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS *AdditionalStatus | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| MANAGEABILITY_MCTP_KCS_TRAILER MctpKcsPec; | |
| UINT32 PecSize; | |
| UINT8 CalculatedPec; | |
| CHAR16 *CompletionCodeStr; | |
| Status = EFI_SUCCESS; | |
| *AdditionalStatus = MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS_NO_ERRORS; | |
| if (CompareGuid (&gManageabilityProtocolMctpGuid, mSingleSessionToken->Token.ManageabilityProtocolSpecification)) { | |
| // | |
| // For MCTP over KCS, check PEC | |
| // | |
| PecSize = sizeof (MANAGEABILITY_MCTP_KCS_TRAILER) + 1; // +1 to read last dummy byte that finishes KCS transfer | |
| Status = KcsTransportRead (&MctpKcsPec.Pec, &PecSize); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: Error! Failed to read PEC with Status(%r)\n", | |
| __func__, | |
| Status | |
| )); | |
| *AdditionalStatus = MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS_ERROR; | |
| return Status; | |
| } | |
| if (PecSize != sizeof (MctpKcsPec)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: Error! Received PEC size is %d instead of %d\n", | |
| __func__, | |
| PecSize, | |
| sizeof (MctpKcsPec) | |
| )); | |
| *AdditionalStatus = MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS_ERROR; | |
| return EFI_DEVICE_ERROR; | |
| } | |
| HelperManageabilityDebugPrint ((VOID *)&MctpKcsPec.Pec, PecSize - 1, "MCTP over KCS Response PEC:\n"); | |
| CalculatedPec = HelperManageabilityGenerateCrc8 (MCTP_KCS_PACKET_ERROR_CODE_POLY, 0, ResponseData, ResponseDataSize); | |
| if (CalculatedPec != MctpKcsPec.Pec) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: Error! Received PEC is 0x%02x instead of 0x%02x\n", | |
| __func__, | |
| MctpKcsPec.Pec, | |
| CalculatedPec | |
| )); | |
| Status = EFI_DEVICE_ERROR; | |
| } | |
| } else if (CompareGuid (&gManageabilityProtocolIpmiGuid, mSingleSessionToken->Token.ManageabilityProtocolSpecification)) { | |
| // | |
| // For IPMI over KCS | |
| // Check and print Completion Code | |
| // | |
| Status = IpmiHelperCheckCompletionCode (*ResponseData, &CompletionCodeStr, AdditionalStatus); | |
| if (!EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_MANAGEABILITY_INFO, "Cc: %02x %s.\n", *((UINT8 *)ResponseData), CompletionCodeStr)); | |
| } else if (Status == EFI_NOT_FOUND) { | |
| DEBUG ((DEBUG_ERROR, "Cc: %02x not defined in IpmiCompletionCodeMapping or invalid.\n", *((UINT8 *)ResponseData))); | |
| } | |
| } | |
| return Status; | |
| } | |
| /** | |
| This function reads the KCS response header according to | |
| manageability protocol. Caller has to free the memory | |
| allocated for response header. | |
| @param[in] ResponseHeader Pointer to receive the response header. | |
| @param[out] AdditionalStatus Pointer to receive the additional status. | |
| @retval EFI_SUCCESS KCS response header is checked and returned | |
| to caller. | |
| @retval EFI_INVALID_PARAMETER One of the given parameter is incorrect. | |
| @retval EFI_OUT_OF_RESOURCE Memory allocation is failed for ResponseHeader. | |
| @retval EFI_DEVICE_ERROR Incorrect response header. | |
| **/ | |
| EFI_STATUS | |
| KcsReadResponseHeader ( | |
| IN UINT8 **ResponseHeader, | |
| OUT MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS *AdditionalStatus | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT32 RspHeaderSize; | |
| UINT32 ExpectedHeaderSize; | |
| UINT8 *RspHeader; | |
| if ((ResponseHeader == NULL) || (AdditionalStatus == NULL)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| *ResponseHeader = NULL; | |
| if (CompareGuid (&gManageabilityProtocolMctpGuid, mSingleSessionToken->Token.ManageabilityProtocolSpecification)) { | |
| // For MCTP over KCS | |
| ExpectedHeaderSize = sizeof (MANAGEABILITY_MCTP_KCS_HEADER); | |
| DEBUG (( | |
| DEBUG_MANAGEABILITY_INFO, | |
| "%a: Reading MCTP over KCS response header.\n", | |
| __func__ | |
| )); | |
| } else if (CompareGuid (&gManageabilityProtocolIpmiGuid, mSingleSessionToken->Token.ManageabilityProtocolSpecification)) { | |
| // For IPMI over KCS | |
| ExpectedHeaderSize = sizeof (IPMI_KCS_RESPONSE_HEADER); | |
| DEBUG (( | |
| DEBUG_MANAGEABILITY_INFO, | |
| "%a: Reading IPMI over KCS response header.\n", | |
| __func__ | |
| )); | |
| } else { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: Error! Unsupported manageability protocol over KCS: %g.\n", | |
| __func__, | |
| mSingleSessionToken->Token.ManageabilityProtocolSpecification | |
| )); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| RspHeader = (UINT8 *)AllocateZeroPool (ExpectedHeaderSize); | |
| if (RspHeader == NULL) { | |
| DEBUG ((DEBUG_ERROR, "Memory allocation failed for KCS response header!\n")); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| RspHeaderSize = ExpectedHeaderSize; | |
| Status = KcsTransportRead (RspHeader, &RspHeaderSize); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: Error! Failed to read KCS response header Status(%r)\n", | |
| __func__, | |
| Status | |
| )); | |
| FreePool (RspHeader); | |
| *AdditionalStatus = MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS_ERROR; | |
| return Status; | |
| } | |
| if (RspHeaderSize != 0) { | |
| HelperManageabilityDebugPrint ((VOID *)RspHeader, RspHeaderSize, "KCS Response Header:\n"); | |
| } | |
| if (ExpectedHeaderSize != RspHeaderSize) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "The size (%d bytes) of returned response header is not the same as expection (%d bytes)!\n", | |
| RspHeaderSize, | |
| ExpectedHeaderSize | |
| )); | |
| FreePool (RspHeader); | |
| return EFI_DEVICE_ERROR; | |
| } | |
| if (CompareGuid (&gManageabilityProtocolMctpGuid, mSingleSessionToken->Token.ManageabilityProtocolSpecification)) { | |
| // | |
| // MCTP over KCS | |
| // | |
| if (((MANAGEABILITY_MCTP_KCS_HEADER *)RspHeader)->NetFunc != MCTP_KCS_NETFN_LUN) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: Error! MANAGEABILITY_MCTP_KCS_HEADER.NetFunc is equal 0x%02x instead of 0x%02x\n", | |
| __func__, | |
| ((MANAGEABILITY_MCTP_KCS_HEADER *)RspHeader)->NetFunc, | |
| MCTP_KCS_NETFN_LUN | |
| )); | |
| FreePool (RspHeader); | |
| *AdditionalStatus = MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS_ERROR; | |
| return EFI_DEVICE_ERROR; | |
| } | |
| if (((MANAGEABILITY_MCTP_KCS_HEADER *)RspHeader)->DefiningBody != DEFINING_BODY_DMTF_PRE_OS_WORKING_GROUP) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: Error! MANAGEABILITY_MCTP_KCS_HEADER.DefiningBody is equal 0x%02x instead of 0x%02x\n", | |
| __func__, | |
| ((MANAGEABILITY_MCTP_KCS_HEADER *)RspHeader)->DefiningBody, | |
| DEFINING_BODY_DMTF_PRE_OS_WORKING_GROUP | |
| )); | |
| FreePool (RspHeader); | |
| *AdditionalStatus = MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS_ERROR; | |
| return EFI_DEVICE_ERROR; | |
| } | |
| } | |
| *ResponseHeader = RspHeader; | |
| *AdditionalStatus = MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS_NO_ERRORS; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| This service communicates with BMC using KCS protocol. | |
| @param[in] TransmitHeader KCS packet header. | |
| @param[in] TransmitHeaderSize KCS packet header size in byte. | |
| @param[in] TransmitTrailer KCS packet trailer. | |
| @param[in] TransmitTrailerSize KCS packet trailer size in byte. | |
| @param[in] RequestData Command Request Data. | |
| @param[in] RequestDataSize Size of Command Request Data. | |
| @param[out] ResponseData Command Response Data. The completion | |
| code is the first byte of response | |
| data. | |
| @param[in, out] ResponseDataSize Size of Command Response Data. | |
| @param[out] AdditionalStatus Additional status of this transaction. | |
| @retval EFI_SUCCESS The command byte stream was | |
| successfully submit to the device and a | |
| response was successfully received. | |
| @retval EFI_NOT_FOUND The command was not successfully sent | |
| to the device or a response was not | |
| successfully received from the device. | |
| @retval EFI_NOT_READY Ipmi Device is not ready for Ipmi | |
| command access. | |
| @retval EFI_DEVICE_ERROR Ipmi Device hardware error. | |
| @retval EFI_TIMEOUT The command time out. | |
| @retval EFI_UNSUPPORTED The command was not successfully sent to | |
| the device. | |
| @retval EFI_OUT_OF_RESOURCES The resource allocation is out of | |
| resource or data size error. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| KcsTransportSendCommand ( | |
| IN MANAGEABILITY_TRANSPORT_HEADER TransmitHeader OPTIONAL, | |
| IN UINT16 TransmitHeaderSize, | |
| IN MANAGEABILITY_TRANSPORT_TRAILER TransmitTrailer OPTIONAL, | |
| IN UINT16 TransmitTrailerSize, | |
| IN UINT8 *RequestData OPTIONAL, | |
| IN UINT32 RequestDataSize, | |
| OUT UINT8 *ResponseData OPTIONAL, | |
| IN OUT UINT32 *ResponseDataSize OPTIONAL, | |
| OUT MANAGEABILITY_TRANSPORT_ADDITIONAL_STATUS *AdditionalStatus | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT8 *RspHeader; | |
| UINT32 ExpectedResponseDataSize; | |
| if ((RequestData != NULL) && (RequestDataSize == 0)) { | |
| DEBUG ((DEBUG_ERROR, "%a: Mismatched values of RequestData and RequestDataSize\n", __func__)); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if ((ResponseData != NULL) && ((ResponseDataSize != NULL) && (*ResponseDataSize == 0))) { | |
| DEBUG ((DEBUG_ERROR, "%a: Mismatched values of ResponseData and ResponseDataSize\n", __func__)); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (AdditionalStatus == NULL) { | |
| DEBUG ((DEBUG_ERROR, "%a: AdditionalStatus is NULL.\n", __func__)); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // Print out the request payloads. | |
| if ((TransmitHeader != NULL) && (TransmitHeaderSize != 0)) { | |
| HelperManageabilityDebugPrint ((VOID *)TransmitHeader, (UINT32)TransmitHeaderSize, "KCS Transmit Header:\n"); | |
| } | |
| if (RequestData != NULL) { | |
| HelperManageabilityDebugPrint ((VOID *)RequestData, RequestDataSize, "KCS Request Data:\n"); | |
| } | |
| if ((TransmitTrailer != NULL) && (TransmitTrailerSize != 0)) { | |
| HelperManageabilityDebugPrint ((VOID *)TransmitTrailer, (UINT32)TransmitTrailerSize, "KCS Transmit Trailer:\n"); | |
| } | |
| if ((TransmitHeader != NULL) || (RequestData != NULL)) { | |
| Status = KcsTransportWrite ( | |
| TransmitHeader, | |
| TransmitHeaderSize, | |
| TransmitTrailer, | |
| TransmitTrailerSize, | |
| RequestData, | |
| RequestDataSize | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "KCS Write Failed with Status(%r)\n", Status)); | |
| return Status; | |
| } | |
| } | |
| if ((ResponseData != NULL) && (ResponseDataSize != NULL) && (*ResponseDataSize != 0)) { | |
| // | |
| // Read the response header | |
| // | |
| Status = KcsReadResponseHeader (&RspHeader, AdditionalStatus); | |
| if (EFI_ERROR (Status)) { | |
| return (Status); | |
| } | |
| // | |
| // Override ResposeDataSize if the manageability protocol is MCTP. | |
| // | |
| if (CompareGuid (&gManageabilityProtocolMctpGuid, mSingleSessionToken->Token.ManageabilityProtocolSpecification)) { | |
| if (*ResponseDataSize < ((MANAGEABILITY_MCTP_KCS_HEADER *)RspHeader)->ByteCount) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: Error! MANAGEABILITY_MCTP_KCS_HEADER.ByteCount (0x%02x) is bigger than provided buffer (0x%02x)\n", | |
| __func__, | |
| ((MANAGEABILITY_MCTP_KCS_HEADER *)RspHeader)->ByteCount, | |
| *ResponseDataSize | |
| )); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| *ResponseDataSize = ((MANAGEABILITY_MCTP_KCS_HEADER *)RspHeader)->ByteCount; | |
| } | |
| FreePool (RspHeader); | |
| ExpectedResponseDataSize = *ResponseDataSize; | |
| Status = KcsTransportRead (ResponseData, ResponseDataSize); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "KCS response read Failed with Status(%r)\n", Status)); | |
| } | |
| // Print out the response payloads. | |
| if (*ResponseDataSize != 0) { | |
| if (ExpectedResponseDataSize != *ResponseDataSize) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "Expected KCS response size : %d is not matched to returned size : %d.\n", | |
| ExpectedResponseDataSize, | |
| *ResponseDataSize | |
| )); | |
| return EFI_DEVICE_ERROR; | |
| } | |
| HelperManageabilityDebugPrint ((VOID *)ResponseData, (UINT32)*ResponseDataSize, "KCS Response Data:\n"); | |
| Status = KcsCheckResponseData (ResponseData, *ResponseDataSize, AdditionalStatus); | |
| } else { | |
| DEBUG ((DEBUG_ERROR, "No response, can't determine Completion Code.\n")); | |
| } | |
| } else { | |
| *ResponseDataSize = 0; | |
| } | |
| return Status; | |
| } | |
| /** | |
| This function reads 8-bit value from register address. | |
| @param[in] Address This represents either 16-bit IO address | |
| or 32-bit memory mapped address. | |
| @retval UINT8 8-bit value. | |
| **/ | |
| UINT8 | |
| KcsRegisterRead8 ( | |
| MANAGEABILITY_TRANSPORT_HARDWARE_IO Address | |
| ) | |
| { | |
| UINT8 Value; | |
| if (mKcsHardwareInfo.MemoryMap == MANAGEABILITY_TRANSPORT_KCS_MEMORY_MAP_IO) { | |
| // Read 8-bit value from 32-bit Memory mapped address. | |
| Value = MmioRead8 ((UINTN)Address.IoAddress32); | |
| } else { | |
| // Read 8-bit value from 16-bit I/O address | |
| Value = IoRead8 ((UINTN)Address.IoAddress16); | |
| } | |
| return Value; | |
| } | |
| /** | |
| This function writes 8-bit value to register address. | |
| @param[in] Address This represents either 16-bit IO address | |
| or 32-bit memory mapped address. | |
| @param[in] Value 8-bit value write to register address | |
| **/ | |
| VOID | |
| KcsRegisterWrite8 ( | |
| MANAGEABILITY_TRANSPORT_HARDWARE_IO Address, | |
| UINT8 Value | |
| ) | |
| { | |
| if (mKcsHardwareInfo.MemoryMap == MANAGEABILITY_TRANSPORT_KCS_MEMORY_MAP_IO) { | |
| // Write 8-bit value to 32-bit Memory mapped address. | |
| MmioWrite8 ((UINTN)Address.IoAddress32, Value); | |
| } else { | |
| // Write 8-bit value to 16-bit I/O address | |
| IoWrite8 ((UINTN)Address.IoAddress16, Value); | |
| } | |
| } |