/** @file KCS Transport Hook. Copyright (c) 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "KcsBmc.h" #include EFI_STATUS KcsErrorExit ( UINT64 KcsTimeoutPeriod, UINT16 KcsPort ) /*++ Routine Description: Check the KCS error status Arguments: KcsPort - The base port of KCS Returns: EFI_DEVICE_ERROR - The device error happened EFI_SUCCESS - Successfully check the KCS error status --*/ { EFI_STATUS Status; UINT8 KcsData; EFI_KCS_STATUS KcsStatus; UINT8 BmcStatus; UINT8 RetryCount; UINT64 TimeOut; TimeOut = 0; RetryCount = 0; while (RetryCount < KCS_ABORT_RETRY_COUNT) { do { MicroSecondDelay(KCS_DELAY_UNIT); KcsStatus.RawData = IoRead8 (KcsPort + 1); if (KcsStatus.RawData == 0xFF || (TimeOut >= KcsTimeoutPeriod)) { RetryCount = KCS_ABORT_RETRY_COUNT; break; } TimeOut++; } while (KcsStatus.Status.Ibf); if (RetryCount >= KCS_ABORT_RETRY_COUNT) { break; } KcsData = KCS_ABORT; IoWrite8 ((KcsPort + 1), KcsData); TimeOut = 0; do { MicroSecondDelay(KCS_DELAY_UNIT); KcsStatus.RawData = IoRead8 (KcsPort + 1); if (KcsStatus.RawData == 0xFF || (TimeOut >= KcsTimeoutPeriod)) { Status = EFI_DEVICE_ERROR; goto LabelError; } TimeOut++; } while (KcsStatus.Status.Ibf); KcsData = IoRead8 (KcsPort); KcsData = 0x0; IoWrite8 (KcsPort, KcsData); TimeOut = 0; do { MicroSecondDelay(KCS_DELAY_UNIT); KcsStatus.RawData = IoRead8 (KcsPort + 1); if (KcsStatus.RawData == 0xFF || (TimeOut >= KcsTimeoutPeriod)) { Status = EFI_DEVICE_ERROR; goto LabelError; } TimeOut++; } while (KcsStatus.Status.Ibf); if (KcsStatus.Status.State == KcsReadState) { TimeOut = 0; do { MicroSecondDelay(KCS_DELAY_UNIT); KcsStatus.RawData = IoRead8 (KcsPort + 1); if (KcsStatus.RawData == 0xFF || (TimeOut >= KcsTimeoutPeriod)) { Status = EFI_DEVICE_ERROR; goto LabelError; } TimeOut++; } while (!KcsStatus.Status.Obf); BmcStatus = IoRead8 (KcsPort); KcsData = KCS_READ; IoWrite8 (KcsPort, KcsData); TimeOut = 0; do { MicroSecondDelay(KCS_DELAY_UNIT); KcsStatus.RawData = IoRead8 (KcsPort + 1); if (KcsStatus.RawData == 0xFF || (TimeOut >= KcsTimeoutPeriod)) { Status = EFI_DEVICE_ERROR; goto LabelError; } TimeOut++; } while (KcsStatus.Status.Ibf); if (KcsStatus.Status.State == KcsIdleState) { TimeOut = 0; do { MicroSecondDelay(KCS_DELAY_UNIT); KcsStatus.RawData = IoRead8 (KcsPort + 1); if (KcsStatus.RawData == 0xFF || (TimeOut >= KcsTimeoutPeriod)) { Status = EFI_DEVICE_ERROR; goto LabelError; } TimeOut++; } while (!KcsStatus.Status.Obf); KcsData = IoRead8 (KcsPort); break; } else { RetryCount++; continue; } } else { RetryCount++; continue; } } if (RetryCount >= KCS_ABORT_RETRY_COUNT) { Status = EFI_DEVICE_ERROR; goto LabelError; } return EFI_SUCCESS; LabelError: return Status; } EFI_STATUS KcsCheckStatus ( UINT64 KcsTimeoutPeriod, UINT16 KcsPort, KCS_STATE KcsState, BOOLEAN *Idle ) /*++ Routine Description: Ckeck KCS status Arguments: KcsPort - The base port of KCS KcsState - The state of KCS to be checked Idle - If the KCS is idle Returns: EFI_SUCCESS - Checked the KCS status successfully --*/ { EFI_STATUS Status = 0; EFI_KCS_STATUS KcsStatus = { 0 }; UINT8 KcsData = 0; UINT64 TimeOut = 0; if(Idle == NULL ){ return EFI_INVALID_PARAMETER; } *Idle = FALSE; do { MicroSecondDelay(KCS_DELAY_UNIT); KcsStatus.RawData = IoRead8 (KcsPort + 1); if (KcsStatus.RawData == 0xFF || (TimeOut >= KcsTimeoutPeriod)) { Status = EFI_DEVICE_ERROR; goto LabelError; } TimeOut++; } while (KcsStatus.Status.Ibf); if (KcsState == KcsWriteState) { KcsData = IoRead8 (KcsPort); } if (KcsStatus.Status.State != KcsState) { if ((KcsStatus.Status.State == KcsIdleState) && (KcsState == KcsReadState)) { *Idle = TRUE; } else { Status = KcsErrorExit (KcsTimeoutPeriod, KcsPort); goto LabelError; } } if (KcsState == KcsReadState) { TimeOut = 0; do { MicroSecondDelay(KCS_DELAY_UNIT); KcsStatus.RawData = IoRead8 (KcsPort + 1); if (KcsStatus.RawData == 0xFF || (TimeOut >= KcsTimeoutPeriod)) { Status = EFI_DEVICE_ERROR; goto LabelError; } TimeOut++; } while (!KcsStatus.Status.Obf); } if (KcsState == KcsWriteState || Idle) { KcsData = IoRead8 (KcsPort); } return EFI_SUCCESS; LabelError: return Status; } EFI_STATUS SendDataToBmc ( UINT64 KcsTimeoutPeriod, UINT16 KcsPort, UINT8 *Data, UINT8 DataSize ) /*++ Routine Description: Send data to BMC Arguments: Data - The data pointer to be sent DataSize - The data size Returns: EFI_SUCCESS - Send out the data successfully --*/ { EFI_KCS_STATUS KcsStatus; UINT8 KcsData; UINT16 KcsIoBase; EFI_STATUS Status; UINT8 i; BOOLEAN Idle; UINT64 TimeOut = 0; DEBUG ((DEBUG_INFO, "SendDataToBmc (%ld, 0x%x) - ", KcsTimeoutPeriod, KcsPort)); for (i = 0; i < DataSize; i++) { DEBUG ((DEBUG_INFO, "%02x ", Data[i])); } DEBUG ((DEBUG_INFO, "\n")); KcsIoBase = KcsPort; do { MicroSecondDelay(KCS_DELAY_UNIT); KcsStatus.RawData = IoRead8 (KcsIoBase + 1); if ((KcsStatus.RawData == 0xFF) || (TimeOut >= KcsTimeoutPeriod)) { if ((Status = KcsErrorExit (KcsTimeoutPeriod, KcsIoBase)) != EFI_SUCCESS) { DEBUG ((DEBUG_INFO, "KcsErrorExit - %r\n", Status)); return Status; } } TimeOut++; } while (KcsStatus.Status.Ibf); KcsData = KCS_WRITE_START; IoWrite8 ((KcsIoBase + 1), KcsData); if ((Status = KcsCheckStatus (KcsTimeoutPeriod, KcsIoBase, KcsWriteState, &Idle)) != EFI_SUCCESS) { DEBUG ((DEBUG_INFO, "KcsCheckStatus 1 - %r\n", Status)); return Status; } for (i = 0; i < DataSize; i++) { if (i == (DataSize - 1)) { if ((Status = KcsCheckStatus (KcsTimeoutPeriod, KcsIoBase, KcsWriteState, &Idle)) != EFI_SUCCESS) { DEBUG ((DEBUG_INFO, "KcsCheckStatus 2 - %r\n", Status)); return Status; } KcsData = KCS_WRITE_END; IoWrite8 ((KcsIoBase + 1), KcsData); } Status = KcsCheckStatus (KcsTimeoutPeriod, KcsIoBase, KcsWriteState, &Idle); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "KcsCheckStatus 3 - %r\n", Status)); return Status; } IoWrite8 (KcsIoBase, Data[i]); } return EFI_SUCCESS; } EFI_STATUS ReceiveBmcData ( UINT64 KcsTimeoutPeriod, UINT16 KcsPort, UINT8 *Data, UINT8 *DataSize ) /*++ Routine Description: Routine Description: Receive data from BMC Arguments: Data - The buffer pointer DataSize - The buffer size Returns: EFI_SUCCESS - Received data successfully --*/ { UINT8 KcsData; UINT16 KcsIoBase; EFI_STATUS Status; BOOLEAN Idle; UINT8 Count; Count = 0; KcsIoBase = KcsPort; DEBUG ((DEBUG_INFO, "ReceiveBmcData (%ld, 0x%x)...\n", KcsTimeoutPeriod, KcsPort)); while (TRUE) { if ((Status = KcsCheckStatus (KcsTimeoutPeriod, KcsIoBase, KcsReadState, &Idle)) != EFI_SUCCESS) { DEBUG ((DEBUG_INFO, "KcsCheckStatus - %r\n", Status)); return Status; } if (Idle) { DEBUG ((DEBUG_INFO, "DataSize - 0x%x\n", Count)); *DataSize = Count; break; } if (Count > *DataSize) { DEBUG ((DEBUG_INFO, "ERROR: Count(0x%x) > *DataSize(0x%x)\n", Count, *DataSize)); return EFI_DEVICE_ERROR; } Data[Count] = IoRead8 (KcsIoBase); Count++; KcsData = KCS_READ; IoWrite8 (KcsIoBase, KcsData); } DEBUG ((DEBUG_INFO, "ReceiveBmcData (%ld, 0x%x) - ", KcsTimeoutPeriod, KcsPort)); for (Count = 0; Count < *DataSize; Count++) { DEBUG ((DEBUG_INFO, "%02x ", Data[Count])); } DEBUG ((DEBUG_INFO, "\n")); return EFI_SUCCESS; } EFI_STATUS ReceiveBmcDataFromPort ( UINT64 KcsTimeoutPeriod, UINT16 KcsPort, UINT8 *Data, UINT8 *DataSize ) /*++ Routine Description: Receive data from BMC Arguments: Data - The buffer pointer to receive data DataSize - The buffer size Returns: EFI_SUCCESS - Received the data successfully --*/ { EFI_STATUS Status; UINT16 KcsIoBase; UINT8 i; UINT8 MyDataSize; MyDataSize = *DataSize; KcsIoBase = KcsPort; for (i = 0; i < KCS_ABORT_RETRY_COUNT; i++) { Status = ReceiveBmcData (KcsTimeoutPeriod, KcsIoBase, Data, DataSize); if (EFI_ERROR (Status)) { if ((Status = KcsErrorExit (KcsTimeoutPeriod, KcsIoBase)) != EFI_SUCCESS) { return Status; } *DataSize = MyDataSize; } else { return Status; } } return EFI_DEVICE_ERROR; } EFI_STATUS SendDataToBmcPort ( UINT64 KcsTimeoutPeriod, UINT16 KcsPort, UINT8 *Data, UINT8 DataSize ) /*++ Routine Description: Send data to BMC Arguments: Data - The data pointer to be sent DataSize - The data size Returns: EFI_SUCCESS - Send out the data successfully --*/ { EFI_STATUS Status; UINT16 KcsIoBase; UINT8 i; KcsIoBase = KcsPort; for (i = 0; i < KCS_ABORT_RETRY_COUNT; i++) { Status = SendDataToBmc (KcsTimeoutPeriod, KcsIoBase, Data, DataSize); if (EFI_ERROR (Status)) { if ((Status = KcsErrorExit (KcsTimeoutPeriod, KcsIoBase)) != EFI_SUCCESS) { return Status; } } else { return Status; } } return EFI_DEVICE_ERROR; }