123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- /** @file
- KCS instance of Manageability Transport Library
- Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #include <Uefi.h>
- #include <IndustryStandard/IpmiKcs.h>
- #include <Library/BaseMemoryLib.h>
- #include <Library/IoLib.h>
- #include <Library/DebugLib.h>
- #include <Library/MemoryAllocationLib.h>
- #include <Library/TimerLib.h>
- #include "ManageabilityTransportKcs.h"
- extern MANAGEABILITY_TRANSPORT_KCS_HARDWARE_INFO mKcsHardwareInfo;
- /**
- 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 = 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 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 = 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] NetFunction Net function of the command.
- @param[in] Command IPMI Command.
- @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 UINT8 NetFunction,
- IN UINT8 Command,
- 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", __FUNCTION__));
- return EFI_INVALID_PARAMETER;
- }
- Length = sizeof (NetFunction) + sizeof (Command);
- if (RequestData != NULL) {
- Length = Length + RequestDataSize;
- }
- Buffer = AllocateZeroPool (Length);
- if (Buffer == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- //
- // Buffer[0] = NetFunction
- // Buffer[1] = Command
- // Buffer [2..RequestDataSize] = RequestData
- //
- BufferPtr = Buffer;
- CopyMem (BufferPtr, &NetFunction, sizeof (NetFunction));
- BufferPtr += sizeof (NetFunction);
- CopyMem (BufferPtr, &Command, sizeof (Command));
- BufferPtr += sizeof (Command);
- if (Length > (sizeof (NetFunction) + sizeof (Command))) {
- CopyMem (BufferPtr, RequestData, RequestDataSize);
- }
- 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 [in] 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", __FUNCTION__));
- 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 service communicates with BMC using KCS protocol.
- @param[in] NetFunction Net function of the command.
- @param[in] Command IPMI Command.
- @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.
- When IN, it is the expected data size
- of response data.
- When OUT, it is the data size of response
- exactly returned.
- @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 UINT8 NetFunction,
- IN UINT8 Command,
- IN UINT8 *RequestData OPTIONAL,
- IN UINT32 RequestDataSize,
- OUT UINT8 *ResponseData OPTIONAL,
- IN OUT UINT32 *ResponseDataSize OPTIONAL
- )
- {
- EFI_STATUS Status;
- UINT32 RspHeaderSize;
- IPMI_KCS_RESPONSE_HEADER RspHeader;
- if ((RequestData != NULL) && (RequestDataSize == 0)) {
- DEBUG((DEBUG_ERROR, "%a: Mismatched values of RequestData and RequestDataSize\n", __FUNCTION__));
- return EFI_INVALID_PARAMETER;
- }
- if ((ResponseData != NULL) && ((ResponseDataSize != NULL) && (*ResponseDataSize == 0))) {
- DEBUG((DEBUG_ERROR, "%a: Mismatched values of ResponseData and ResponseDataSize\n", __FUNCTION__));
- return EFI_INVALID_PARAMETER;
- }
- Status = KcsTransportWrite (
- (NetFunction << 2),
- Command,
- RequestData,
- RequestDataSize
- );
- if (EFI_ERROR (Status)) {
- DEBUG ((
- DEBUG_ERROR,
- "IPMI KCS Write Failed with Status(%r) for NetFunction(0x%x)," \
- " Command(0x%x).\n",
- Status,
- NetFunction,
- Command
- ));
- return Status;
- }
- //
- // Read the response header
- RspHeaderSize = sizeof (IPMI_KCS_RESPONSE_HEADER);
- Status = KcsTransportRead ((UINT8 *)&RspHeader, &RspHeaderSize);
- if (EFI_ERROR (Status)) {
- DEBUG ((
- DEBUG_ERROR,
- "IPMI KCS read response header failed Status(%r), " \
- "RspNetFunctionLun = 0x%x, " \
- "Comamnd = 0x%x \n",
- Status,
- RspHeader.NetFunc,
- RspHeader.Command
- ));
- return (Status);
- }
- Status = KcsTransportRead ((UINT8 *)ResponseData, ResponseDataSize);
- if (EFI_ERROR (Status)) {
- DEBUG ((
- DEBUG_ERROR,
- "IPMI KCS response read Failed with Status(%r) for NetFunction(0x%x)," \
- " Command(0x%x).\n",
- Status,
- NetFunction,
- Command
- ));
- }
- 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);
- }
- }
|