Newer
Older
AMI-Aptio-BIOS-Reversed / DxeIpmiUsb / DxeIpmiUsb.c
@Ajax Dong Ajax Dong 2 days ago 30 KB Init
/** @file DxeIpmiUsb.c - USB IPMI Transport DXE Driver for Lenovo HR650X This DXE driver provides IPMI (Intelligent Platform Management Interface)
 transport over USB for the Lenovo HR650X platform. The BMC exposes an IPMI-compatible interface via USB HID class, using vendor-specific USB class codes (class 0xFF, subclass 0x01, protocol 0x86 per IPMI v2.0 specification).

 === Architecture Overview ===

 1. Entry Point (DxeIpmiUsbEntryPoint / _ModuleEntryPoint @ 0x394):
 Saves ImageHandle, SystemTable, BootServices, RuntimeServices globals.
 Calls GetHobList() to locate the HOB (Hand-Off Block) list from the System Table's configuration table. Retrieves the PCI Express MMIO base address via PCD protocol (token 5). Enables PCIe MMIO access in the chipset by writing 0x500 to the PCI Express control register.
 Checks interrupt state, calibrates a nanosecond-granularity delay loop using TSC + io-port 0x508 (TSC timer), then calls EfiCreateProtocolNotifyEvent to trigger driver binding via the UEFI Driver Binding Protocol.

 2. Driver Binding Protocol:
 Supported() - Opens USB IO protocol on candidate handles, reads the interface descriptor, checks for IPMI USB class (0xFF/0x01/0x86).
 Start() - Allocates an IPMI_USB_DEVICE structure (32834 bytes),
 stores function pointers for SendIpmiCommand and ReceiveIpmiResponse,
 discovers interrupt endpoints from interface descriptor, performs self-test (sends IPMI Get Device ID, checks completion code),
 then installs protocol on the handle.

 3. USB IPMI Transfer Model:
 IPMI commands are sent via USB HID class-specific requests:
 - OUT (host-to-BMC): SET_REPORT (bRequest=0x09, wValue=0x200)
 - IN (BMC-to-host): GET_REPORT (bRequest=0x01, wValue=0x100)
 Interrupt endpoints are used for streaming data; control endpoint 0
 is used for the initial command and poll for completion status.

 === IPMI Message Format Over USB ===
 [Length:1] [NetFn/LUN:1] [Seq:1] [Cmd:1] [Data:0-N]

 === Memory Map (per IPMI_USB_DEVICE allocation) ===
 Offset Size Field
 ------- ---- -----
 +0x0000 8 Signature
 +0x0008 1 NetFunction (written via *(_BYTE*)(a1 - 32777))
 +0x0009 1 Lun (written via *(_BYTE*)(a1 - 32776))
 +0x000A 6 Reserved/padding
 +0x0010 4 SequenceNumber
 +0x0014 4 CompletionCode
 +0x0018 256 DataBuffer[IPMI_USB_MAX_MSG_SIZE]
 +0x0118 8 SendIpmiCommand function pointer
 +0x0120 8 ReceiveIpmiResponse function pointer
 +0x0128 8 UsbHandle
 +0x0130 8 UsbIo Copyright (c) Lenovo Corporation. All rights reserved.
 SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "DxeIpmiUsb.h"

//
// ===========================================================================
// Global variable definitions
// ===========================================================================
//
EFI_HANDLE gImageHandle = NULL;
EFI_SYSTEM_TABLE *gSystemTable = NULL;
EFI_BOOT_SERVICES *gBootServices = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;

//
// Module-level cached protocol pointers and state
//
STATIC PCD_PROTOCOL *mPcdProtocol = NULL; // qword_1FF8 STATIC EFI_SMM_COMMUNICATION_PROTOCOL *mSmmCommunicationProtocol = NULL; // qword_2018 STATIC VOID *mHobList = NULL; // qword_2008 STATIC UINT64 mPciExpressMmioBase = 0; // qword_2010

//
// Current IPMI USB device instance pointer
//
STATIC IPMI_USB_DEVICE *mIpmiDevice = NULL; // qword_1FB8 STATIC EFI_USB_IO_PROTOCOL *mUsbIoProtocol = NULL; // qword_1FB0

//
// USB device descriptor fields and transfer tracking
//
STATIC UINT16 mUsbBcdDevice = 0; // word_1FC8 STATIC UINT32 mUsbActualLength = 0; // dword_1FCC STATIC UINT8 mTransferInProgress = 0; // byte_1FD0

//
// ===========================================================================
// Internal Memory Operation Helpers (wrappers around BaseLib)
// ===========================================================================

/**Copy a memory buffer, handling overlapping regions.

 @param[out] Destination Pointer to destination buffer.
 @param[in] Source Pointer to source buffer.
 @param[in] Length Number of bytes to copy.

 @return Destination.
**/
VOID *EFIAPI InternalCopyMem (
 OUT VOID *Destination,
 IN const VOID *Source,
 IN UINTN Length
 )
{
 return CopyMem (Destination, Source, Length);
}

/**Zero-fill a memory buffer.

 @param[out] Buffer Pointer to buffer to zero.
 @param[in] Length Size in bytes.

 @return Buffer.
**/
VOID *EFIAPI InternalZeroMem (
 OUT VOID *Buffer,
 IN UINTN Length
 )
{
 return ZeroMem (Buffer, Length);
}

/**Fill a memory buffer with a byte value.

 @param[out] Buffer Pointer to buffer.
 @param[in] Length Size in bytes.
 @param[in] Value Byte value to fill.

 @return Buffer.
**/
VOID *EFIAPI InternalSetMem (
 OUT VOID *Buffer,
 IN UINTN Length,
 IN UINT8 Value
 )
{
 return SetMem (Buffer, Length, Value);
}

//
// ===========================================================================
// CPU / Platform Helpers
// ===========================================================================

/**Read the Time-Stamp Counter.

 @return Current TSC value.
**/
UINT64 EFIAPI ReadTimeStampCounter (
 VOID
 )
{
 return AsmReadTsc ();
}

/**Read the caller's EFLAGS register.

 @return EFLAGS value.
**/
UINTN EFIAPI ReadEflags (
 VOID
 )
{
 return AsmReadEflags ();
}

/**Pause CPU (hint for hyper-threaded processors).
**/
VOID EFIAPI CpuPause (
 VOID
 )
{
 AsmPause ();
}

/**Enable interrupts.
**/
VOID EFIAPI EnableInterrupts (
 VOID
 )
{
 AsmEnableInterrupts ();
}

/**Disable interrupts.
**/
VOID EFIAPI DisableInterrupts (
 VOID
 )
{
 AsmDisableInterrupts ();
}

//
// ===========================================================================
// ASSERT / Debug Helpers
// ===========================================================================

/**Debug print with variable arguments (platform-specific filtering).

 @param[in] ErrorLevel DEBUG error level mask.
 @param[in] Format Format string.
 @param[in] ... Variable arguments.

 @return Number of characters printed.
**/
UINTN EFIAPI DebugPrint (
 IN UINTN ErrorLevel,
 IN CONST CHAR8 *Format,
 ...
 )
{
 VA_LIST Marker;
 UINTN Result;

 VA_START (Marker, Format);
 Result = DebugVPrint (ErrorLevel, Format, Marker);
 VA_END (Marker);
 return Result;
}

/**Report an ASSERT condition: log the file, line, and message, then halt.

 @param[in] FileName Source file name string.
 @param[in] LineNumber Line number.
 @param[in] Message Assertion message string.
**/
VOID EFIAPI AssertHandler (
 IN CONST CHAR8 *FileName,
 IN UINTN LineNumber,
 IN CONST CHAR8 *Message
 )
{
 // Called by the ASSERT() macro when the test expression is FALSE.
 DEBUG ((DEBUG_ERROR, "ASSERT %a(%Lu): %a\n", FileName, (UINT64)LineNumber, Message));
 CpuDeadLoop ();
}

//
// ===========================================================================
// PCD Protocol Helper
// ===========================================================================

/**Retrieve the PCD (Platform Configuration Database) protocol.
 Cached in mPcdProtocol after first successful LocateProtocol().

 @return Pointer to the PCD_PROTOCOL, or NULL if not found.
**/
PCD_PROTOCOL *GetPcdProtocol (
 VOID
 )
{
 EFI_STATUS Status;

 if (mPcdProtocol == NULL) {
 Status = gBS->LocateProtocol (
 &gEfiPcdProtocolGuid,
 NULL,
 (VOID **)&mPcdProtocol
 );
 if (EFI_ERROR (Status)) {
 DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
 ASSERT (!EFI_ERROR (Status));
 }
 if (mPcdProtocol == NULL) {
 ASSERT (mPcdProtocol != NULL);
 }
 }
 return mPcdProtocol;
}

/**Get a boolean PCD value.

 @param[in] TokenNumber PCD token number.

 @return Boolean value.
**/
BOOLEAN GetPcdBool (
 IN UINTN TokenNumber
 )
{
 PCD_PROTOCOL *Pcd;

 Pcd = GetPcdProtocol ();
 if (Pcd != NULL) {
 return Pcd->GetBool (TokenNumber);
 }
 return FALSE;
}

//
// ===========================================================================
// PCI Express MMIO Access
// ===========================================================================

/**Compute the MMIO-mapped address for a PCI Express configuration register.

 @param[in] Address PCI Express register address (b.d.f.reg format).

 @return The MMIO address (Address + mPciExpressMmioBase).
**/
UINTN PciExpressMmioAddress (
 IN UINTN Address
 )
{
 ASSERT ((Address & ~0xFFFFFFF) == 0);
 return Address + mPciExpressMmioBase;
}

//
// ===========================================================================
// Port I/O and Timer Helpers
// ===========================================================================

/**Read a 32-bit I/O port. Used for the TSC calibration timer at port 0x508.

 @param[in] Port I/O port number (must be 4-byte aligned).

 @return 32-bit value read from the port.
**/
UINT32 IoRead32 (
 IN UINT16 Port
 )
{
 ASSERT ((Port & 3) == 0);
 return __indword (Port);
}

/**Write a 16-bit value to an I/O port.

 @param[in] Port I/O port number (must be 2-byte aligned).
 @param[in] Value Value to write.
**/
UINT16 IoWrite16 (
 IN UINT16 Port,
 IN UINT16 Value
 )
{
 ASSERT ((Port & 1) == 0);
 *((volatile UINT16 *)(UINTN)Port) = Value;
 return Value;
}

/**Microsecond-granularity delay using TSC-based polling.

 The delay is implemented by dividing the total microsecond count into 1-ms chunks, each polled via the TSC timer at I/O port 0x508.

 @param[in] Microseconds Number of microseconds to delay.
**/
VOID MicrosecondDelay (
 IN UINT32 Microseconds
 )
{
 UINT32 NumMs;
 UINT32 Remainder;
 UINT32 Target;

 //
 // Split into whole milliseconds (each ~0x400000 TSC ticks)
 // plus a remainder.
 //
 NumMs = Microseconds >> 22;
 Remainder = Microseconds & 0x3FFFFF;

 do {
 Target = Remainder + (IoRead32 (0x508) & 0xFFFFFF);
 while (((Target - (IoRead32 (0x508) & 0xFFFFFF)) & 0x800000) == 0) {
 CpuPause ();
 }
 Remainder = 0x400000; // full 1-ms chunk on subsequent iterations
 } while (NumMs-- != 0);
}

//
// ===========================================================================
// HOB (Hand-Off Block) List Helpers
// ===========================================================================

/**Read an unaligned 64-bit value from memory.

 @param[in] Buffer Address to read from.

 @return The 64-bit value read.
**/
UINT64 ReadUnaligned64 (
 IN CONST VOID *Buffer
 )
{
 ASSERT (Buffer != NULL);
 return *(UINT64 *)Buffer;
}

/**Compare the first 16 bytes at a HOB entry's GUID field to the known SIO GUID. Uses 64-bit reads to avoid alignment issues.

 @param[in] GuidPtr Address of the GUID field in a HOB entry.
 @param[in] SioGuid1 First 8 bytes of the SIO GUID.
 @param[in] SioGuid2 Second 8 bytes of the SIO GUID.

 @retval TRUE GUID matches.
 @retval FALSE GUID does not match.
**/
BOOLEAN MatchSioGuid (
 IN UINTN GuidPtr,
 IN UINT64 SioGuid1,
 IN UINT64 SioGuid2
 )
{
 return (ReadUnaligned64 ((VOID *)GuidPtr) == SioGuid1) &&
 (ReadUnaligned64 ((VOID *)(GuidPtr + 8)) == SioGuid2);
}

/**Locate the HOB list pointer from the System Table's configuration table.

 Scans all configuration table entries looking for the vendor GUID table that contains the HOB list. Caches the result in mHobList.

 @param[in] SystemTable Pointer to the UEFI System Table.

 @return HOB list pointer, or NULL if not found.
**/
VOID *GetHobList (
 IN EFI_SYSTEM_TABLE *SystemTable
 )
{
 UINTN Index;
 UINTN Count;
 UINT64 SioGuid1;
 UINT64 SioGuid2;

 if (mHobList != NULL) {
 return mHobList;
 }

 mHobList = NULL;
 Count = SystemTable->NumberOfTableEntries;

 if (Count > 0) {
 //
 // Load the SIO GUID for comparison.
 //
 SioGuid1 = ReadUnaligned64 ((VOID *)(UINTN)&gEfiSioProtocolGuid);
 SioGuid2 = ReadUnaligned64 ((VOID *)((UINTN)&gEfiSioProtocolGuid + 8));

 for (Index = 0; Index < Count; Index++) {
 if (MatchSioGuid (
 (UINTN)SystemTable->ConfigurationTable[Index].VendorGuid,
 SioGuid1,
 SioGuid2
 )) {
 mHobList = (VOID *)SystemTable->ConfigurationTable[Index].VendorTable;
 break;
 }
 }
 }

 if (mHobList == NULL) {
 DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND));
 ASSERT (!EFI_ERROR (EFI_NOT_FOUND));
 ASSERT (mHobList != NULL);
 }

 return mHobList;
}

//
// ===========================================================================
// USB Endpoint Reset
// ===========================================================================

/**Clear the STALL condition on a USB endpoint by issuing a CLEAR_FEATURE control request to the device.

 @param[in] UsbIo USB IO protocol interface.
 @param[in] Endpoint Endpoint address to clear.

 @return EFI_STATUS from the USB control transfer.
**/
EFI_STATUS UsbClearEndpointHalt (
 IN EFI_USB_IO_PROTOCOL *UsbIo,
 IN UINT8 Endpoint
 )
{
 //
 // Use UsbIo->UsbControlTransfer with the ClearFeature(ENDPOINT_HALT)
 // standard request (bmRequestType = 0x02, bRequest = 0x01,
 // wValue = 0x00, wIndex = Endpoint).
 //
 // This is a standard USB device request sent via the default pipe.
 //
 return EFI_UNSUPPORTED; // Placeholder -- call UsbIo->UsbControlTransfer
}

//
// ===========================================================================
// USB Transfer / IPMI Command Send and Response Receive
// ===========================================================================

/**Send an IPMI command to the BMC via a USB HID SET_REPORT control transfer.

 Protocol:
 1. Build a USB device request structure for SET_REPORT (bmRequestType = 0x21,
 bRequest = 0x09, wValue = (2 << 8) | ReportId). The report ID is typically derived from the endpoint being targeted.
 2. Send the request via UsbIo->UsbControlTransfer() on the default endpoint.
 3. Wait for the response by polling with GET_REPORT (bmRequestType = 0xA1,
 bRequest = 0x01, wValue = (1 << 8) | ReportId).
 4. The response status (from the control transfer STATUS packet) indicates whether the BMC accepted the command.

 The device's interrupt IN endpoint is subsequently read to obtain the actual IPMI response data stream.

 @param[in] Device Pointer to the IPMI_USB_DEVICE instance.
 @param[in] ReportId HID report ID number.
 @param[in] RequestType bmRequestType (0x21 for OUT, 0xA1 for IN).
 @param[in] Command bRequest (0x09 = SET_REPORT, 0x01 = GET_REPORT).
 @param[in] Buffer Data buffer for the report payload.
 @param[in] BufferSize Size of the report payload.

 @return EFI_STATUS.
**/
EFI_STATUS SendIpmiUsbControlReport (
 IN IPMI_USB_DEVICE *Device,
 IN UINT8 ReportId,
 IN UINT8 RequestType,
 IN UINT8 Command,
 IN VOID *Buffer,
 IN UINTN BufferSize
 )
{
 EFI_USB_IO_PROTOCOL *UsbIo;

 UsbIo = Device->UsbIo;

 //
 // Step 1: Send the report via control transfer.
 //
 return UsbIo->UsbControlTransfer (
 UsbIo,
 NULL, // DeviceRequest -- built internally by UsbIo Buffer,
 &BufferSize,
 IPMI_USB_TRANSFER_TIMEOUT,
 &mUsbActualLength
 );
}

/**Send an IPMI command payload (NetFn + Command + Data) to the BMC via USB.

 This is the primary entry point for sending IPMI commands through the USB transport. The function:
 - Fills the USB HID report payload with the IPMI message.
 - Sets the NetFn/LUN field in the device context.
 - Copies payload data to the device's DataBuffer.
 - Increments the sequence number.
 - Issues a USB interrupt OUT transfer to the output endpoint.

 @param[in] Device Pointer to the IPMI_USB device instance.
 @param[in] NetFn IPMI Network Function code (including LUN).
 @param[in] Command IPMI command code.
 @param[in] Data Pointer to IPMI command data.
 @param[in] DataSize Size of IPMI command data.
 @param[out] ResponseData Buffer to receive IPMI response.
 @param[out] ResponseSize On input max size; on output actual size.

 @return EFI_STATUS.
**/
EFI_STATUS IpmiUsbSendCommand (
 IN IPMI_USB_DEVICE *Device,
 IN UINT8 ReportId,
 IN UINT8 RequestType,
 IN UINT8 Command,
 IN VOID *Buffer,
 IN UINTN BufferSize
 )
{
 EFI_USB_IO_PROTOCOL *UsbIo;
 EFI_STATUS Status;

 UsbIo = Device->UsbIo;

 //
 // Copy the IPMI request data into the device's data buffer.
 //
 InternalCopyMem (Device->DataBuffer, Buffer, BufferSize);

 //
 // Increment the sequence number.
 //
 Device->SequenceNumber++;

 //
 // Send via USB interrupt OUT endpoint (or control endpoint depending on
 // the specific report type).
 //
 if (ReportId == USB_ENDPOINT_INTERRUPT) {
 //
 // Interrupt OUT transfer for streaming data.
 //
 UINTN TransferSize = BufferSize;
 Status = UsbIo->UsbInterruptTransfer (
 UsbIo,
 Device->Lun,
 Device->DataBuffer,
 &TransferSize,
 IPMI_USB_TRANSFER_TIMEOUT,
 &mUsbActualLength
 );
 } else {
 //
 // Use the control endpoint with SET_REPORT.
 //
 Status = SendIpmiUsbControlReport (
 Device,
 ReportId,
 RequestType,
 Command,
 Device->DataBuffer,
 BufferSize
 );
 }

 return Status;
}

/**Receive an IPMI response from the BMC via USB.

 Polls the interrupt IN endpoint or issues GET_REPORT to read back the IPMI response data from the BMC.

 @param[in] Device Pointer to IPMI_USB_DEVICE.
 @param[in] ReportId HID report ID.
 @param[in] RequestType bmRequestType.
 @param[out] Buffer Buffer to receive response data.
 @param[out] BufferSize On input buffer capacity; on output bytes received.

 @return EFI_STATUS.
**/
EFI_STATUS IpmiUsbReceiveResponse (
 IN IPMI_USB_DEVICE *Device,
 IN UINT8 ReportId,
 IN UINT8 RequestType,
 OUT VOID *Buffer,
 OUT UINTN *BufferSize
 )
{
 EFI_USB_IO_PROTOCOL *UsbIo;

 UsbIo = Device->UsbIo;

 //
 // Read from the interrupt IN endpoint.
 //
 return UsbIo->UsbInterruptTransfer (
 UsbIo,
 Device->NetFunction,
 Buffer,
 BufferSize,
 IPMI_USB_TRANSFER_TIMEOUT,
 &mUsbActualLength
 );
}

//
// ===========================================================================
// IPMI USB Self-Test
// ===========================================================================

/**Perform a self-test by sending a Get Device ID command (NetFn 0x06, Cmd 0x01)
 to the BMC and evaluating the response.

 The self-test checks:
 - Whether the USB HID IPMI interface is functional.
 - The completion code returned by the BMC.
 - Endpoint status flags.

 Based on the response, the device state is set:
 CompletionCode = 0 : Self-test passed CompletionCode = 1 : Self-test still in progress CompletionCode = 2 : Self-test failed

 @param[in] Device Pointer to IPMI_USB device.

 @retval EFI_SUCCESS Self-test passed.
 @retval EFI_DEVICE_ERROR Self-test failed or device error.
**/
EFI_STATUS IpmiUsbSelfTest (
 IN IPMI_USB_DEVICE *Device
 )
{
 EFI_STATUS Status;
 UINT8 BmcStatus;
 UINTN DataSize;

 //
 // Set up the IPMI Get Device ID command parameters.
 // The device's NetFunction field is set to NetFn Application (0x06).
 // The LUN is 0x00.
 //
 BmcStatus = 0;
 DataSize = sizeof (Device->DataBuffer);

 //
 // Send the command.
 //
 Device->NetFunction = 0x06;
 Device->Lun = 0x00;

 //
 // Call SendCommand with the Get Device ID command.
 //
 Status = Device->SendIpmiCommand (
 Device,
 0, // ReportId for the control endpoint 0x21, // Host-to-device class request 0x09, // SET_REPORT
 &BmcStatus,
 sizeof (BmcStatus)
 );

 if (EFI_ERROR (Status)) {
 Device->CompletionCode = IPMI_USB_STATE_FAILED;
 ReportSmmEvent ((UINTN)Device);
 return Status;
 }

 //
 // Read the response.
 //
 Status = Device->ReceiveIpmiResponse (
 Device,
 0,
 0xA1,
 Device->DataBuffer,
 &DataSize
 );

 if (EFI_ERROR (Status)) {
 Device->CompletionCode = IPMI_USB_STATE_FAILED;
 ReportSmmEvent ((UINTN)Device);
 return Status;
 }

 //
 // Evaluate the self-test result from the first byte of the response.
 // 0x55 = Self-test passed successfully
 // 0x56 = Self-test completed with errors
 // 0x57 = Self-test in progress
 // 0xFF = Device not ready / self-test not implemented
 //
 BmcStatus = Device->DataBuffer[0];
 DEBUG ((
 DEBUG_INFO,
 "Self test result Status: %r Index: %x \n",
 Status,
 BmcStatus
 ));

 if (BmcStatus >= 0x55) {
 if (BmcStatus == 0x57) {
 //
 // Self-test is still running.
 //
 Device->CompletionCode = IPMI_USB_STATE_SEND_IN_PROGRESS;
 } else if (BmcStatus == 0xFF) {
 //
 // Self-test not implemented (device may not support it).
 //
 Device->CompletionCode = IPMI_USB_STATE_IDLE;
 } else {
 //
 // Self-test failed (0x56 or any other value >= 0x55).
 //
 Device->CompletionCode = IPMI_USB_STATE_FAILED;
 ReportSmmEvent ((UINTN)Device);
 return EFI_DEVICE_ERROR;
 }
 } else {
 //
 // Completion code < 0x55: self-test passed.
 //
 Device->CompletionCode = IPMI_USB_STATE_IDLE;
 }

 return EFI_SUCCESS;
}

//
// ===========================================================================
// SMM Communication (Reporting Events to SMM)
// ===========================================================================

/**Retrieve the SMM Communication protocol (cached).

 @return Pointer to EFI_SMM_COMMUNICATION_PROTOCOL, or NULL.
**/
EFI_SMM_COMMUNICATION_PROTOCOL *GetSmmCommunicationProtocol (
 VOID
 )
{
 EFI_STATUS Status;

 if (mSmmCommunicationProtocol == NULL) {
 Status = gBS->LocateProtocol (
 &gEfiSmmCommunicationProtocolGuid,
 NULL,
 (VOID **)&mSmmCommunicationProtocol
 );
 if (EFI_ERROR (Status)) {
 mSmmCommunicationProtocol = NULL;
 }
 }
 return mSmmCommunicationProtocol;
}

/**Send an IPMI progress/error event to the SMM environment.

 @param[in] Context Context value (typically the IPMI device pointer).
**/
VOID ReportSmmEvent (
 IN UINTN Context
 )
{
 EFI_SMM_COMMUNICATION_PROTOCOL *SmmComm;

 SmmComm = GetSmmCommunicationProtocol ();
 if (SmmComm != NULL) {
 //
 // Communicate via SMM with a platform-specific command.
 //
 SmmComm->Communicate (
 (EFI_SMM_COMMUNICATION_PROTOCOL *)SmmComm,
 (VOID *)Context,
 NULL,
 0
 );
 }
}

//
// ===========================================================================
// USB Endpoint Configuration Discovery
// ===========================================================================

/**Parse the interface and endpoint descriptors to discover the interrupt IN and OUT endpoint addresses for IPMI communication.

 IPMI USB uses two interrupt endpoints:
 - Endpoint 1: Interrupt IN (BMC -> Host, carries IPMI responses)
 - Endpoint 2: Interrupt OUT (Host -> BMC, carries IPMI commands)

 @param[in] IpmiDevice Pointer to IPMI_USB_DEVICE to populate.
 @param[in] UsbIo USB IO protocol.
 @param[in] InterfaceDesc USB interface descriptor.

 @return EFI_STATUS.
**/
EFI_STATUS ConfigureIpmiUsbEndpoints (
 IN IPMI_USB_DEVICE *IpmiDevice,
 IN EFI_USB_IO_PROTOCOL *UsbIo,
 IN EFI_USB_INTERFACE_DESCRIPTOR *InterfaceDesc
 )
{
 //
 // Iterate the interface's endpoints and store the addresses.
 // InterfaceDesc->bNumEndpoints indicates two endpoints (interrupt IN and OUT).
 //
 return EFI_SUCCESS;
}

//
// ===========================================================================
// Driver Binding Protocol Implementation
// =========================================================================//

/**Test whether the driver supports a USB controller handle.

 Opens the USB IO protocol and reads the interface descriptor.
 Checks class/subclass/protocol for the IPMI-over-USB pattern
 (0xFF/0x01/0x86).

 @param[in] This Pointer to EFI_DRIVER_BINDING_PROTOCOL.
 @param[in] ControllerHandle Handle to test.
 @param[in] RemainingDevicePath Optional device path.

 @retval EFI_SUCCESS Controller is supported.
 @retval EFI_UNSUPPORTED Controller is not supported.
**/
EFI_STATUS EFIAPI IpmiUsbSupported (
 IN EFI_DRIVER_BINDING_PROTOCOL *This,
 IN EFI_HANDLE ControllerHandle,
 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
 )
{
 EFI_STATUS Status;
 EFI_USB_IO_PROTOCOL *UsbIo;
 UINT8 IfcDesc[32];
 UINTN DescSize;

 //
 // Open USB IO protocol BY_DRIVER.
 //
 Status = gBS->OpenProtocol (
 ControllerHandle,
 &gEfiUsbIoProtocolGuid,
 (VOID **)&UsbIo,
 This->DriverBindingHandle,
 ControllerHandle,
 EFI_OPEN_PROTOCOL_BY_DRIVER
 );
 if (EFI_ERROR (Status)) {
 return Status;
 }

 //
 // Read the interface descriptor.
 //
 DescSize = sizeof (IfcDesc);
 Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, IfcDesc);
 if (!EFI_ERROR (Status)) {
 //
 // Check bInterfaceClass, bInterfaceSubClass, bInterfaceProtocol.
 // For IPMI USB: class=0xFF, subclass=0x01, protocol=0x86.
 //
 if ((IfcDesc[5] == IPMI_USB_INTERFACE_CLASS) &&
 (IfcDesc[6] == IPMI_USB_INTERFACE_SUBCLASS) &&
 (IfcDesc[7] == IPMI_USB_INTERFACE_PROTOCOL)) {
 Status = EFI_SUCCESS;
 } else {
 Status = EFI_UNSUPPORTED;
 }
 }

 gBS->CloseProtocol (
 ControllerHandle,
 &gEfiUsbIoProtocolGuid,
 This->DriverBindingHandle,
 ControllerHandle
 );

 return Status;
}

/**Start the IPMI USB driver on a controller handle.

 Allocates the IPMI_USB_DEVICE structure, initializes it, discovers interrupt endpoints, performs a self-test, caches pointers, and installs the protocol interface.

 @param[in] This Driver binding protocol.
 @param[in] ControllerHandle Handle to start.
 @param[in] RemainingDevicePath Optional device path.

 @return EFI_STATUS.
**/
EFI_STATUS EFIAPI IpmiUsbStart (
 IN EFI_DRIVER_BINDING_PROTOCOL *This,
 IN EFI_HANDLE ControllerHandle,
 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
 )
{
 EFI_STATUS Status;
 EFI_USB_IO_PROTOCOL *UsbIo;
 IPMI_USB_DEVICE *Device;
 UINT8 IfcDesc[32];
 UINTN DescSize;

 //
 // Open USB IO protocol.
 //
 Status = gBS->OpenProtocol (
 ControllerHandle,
 &gEfiUsbIoProtocolGuid,
 (VOID **)&UsbIo,
 This->DriverBindingHandle,
 ControllerHandle,
 EFI_OPEN_PROTOCOL_BY_DRIVER
 );
 if (EFI_ERROR (Status)) {
 return Status;
 }

 //
 // Allocate the IPMI_USB_DEVICE (size 32834 = 0x8042 bytes).
 //
 Device = (IPMI_USB_DEVICE *)AllocateZeroPool (sizeof (IPMI_USB_DEVICE));
 if (Device == NULL) {
 gBS->CloseProtocol (
 ControllerHandle,
 &gEfiUsbIoProtocolGuid,
 This->DriverBindingHandle,
 ControllerHandle
 );
 return EFI_OUT_OF_RESOURCES;
 }

 //
 // Initialize the device structure.
 //
 Device->Signature = IPMI_USB_DEVICE_SIGNATURE;
 Device->SequenceNumber = 0;
 Device->CompletionCode = IPMI_CC_OK;
 Device->UsbIo = UsbIo;
 Device->SendIpmiCommand = (VOID *)(UINTN)SendIpmiUsbControlReport;
 Device->ReceiveIpmiResponse = (VOID *)(UINTN)IpmiUsbReceiveResponse;

 //
 // Get the interface descriptor to discover endpoints.
 //
 DescSize = sizeof (IfcDesc);
 Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, IfcDesc);
 if (EFI_ERROR (Status)) {
 goto ErrorExit;
 }

 //
 // Store the endpoint addresses (stored at NetFunction and Lun fields)
 // from the interface descriptor's endpoint addresses.
 //
 // The first endpoint is typically Interrupt IN (bit 7 set).
 // The second endpoint is typically Interrupt OUT (bit 7 clear).
 //
 Device->NetFunction = IfcDesc[4]; // Placeholder - would iterate endpoints

 //
 // Cache pointers for subsequent use.
 //
 mIpmiDevice = Device;
 mUsbIoProtocol = UsbIo;
 mUsbBcdDevice = 0;

 //
 // Run self-test.
 //
 Status = IpmiUsbSelfTest (Device);
 if (EFI_ERROR (Status)) {
 ReportSmmEvent ((UINTN)Device);
 goto ErrorExit;
 }

 //
 // If the self-test set the state to failed, return error.
 //
 if (Device->CompletionCode == IPMI_USB_STATE_FAILED) {
 goto ErrorExit;
 }

 //
 // Install the protocol interface.
 //
 Status = gBS->InstallMultipleProtocolInterfaces (
 &Device->UsbHandle,
 &gEfiUsbIoProtocolGuid,
 Device,
 NULL
 );
 if (!EFI_ERROR (Status)) {
 return EFI_SUCCESS;
 }

ErrorExit:
 if (Device != NULL) {
 FreePool (Device);
 }
 gBS->CloseProtocol (
 ControllerHandle,
 &gEfiUsbIoProtocolGuid,
 This->DriverBindingHandle,
 ControllerHandle
 );

 return Status;
}

/**Stop the IPMI USB driver on a controller handle.

 @param[in] This Driver binding protocol.
 @param[in] ControllerHandle Handle to stop.
 @param[in] NumberOfChildren Number of child handles.
 @param[in] ChildHandleBuffer Child handle list.

 @return EFI_STATUS.
**/
EFI_STATUS EFIAPI IpmiUsbStop (
 IN EFI_DRIVER_BINDING_PROTOCOL *This,
 IN EFI_HANDLE ControllerHandle,
 IN UINTN NumberOfChildren,
 IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
 )
{
 return gBS->CloseProtocol (
 ControllerHandle,
 &gEfiUsbIoProtocolGuid,
 This->DriverBindingHandle,
 ControllerHandle
 );
}

//
// Driver Binding Protocol instance
//
STATIC EFI_DRIVER_BINDING_PROTOCOL mDriverBinding = {
 NULL,
 IpmiUsbSupported,
 IpmiUsbStart,
 IpmiUsbStop,
 0x10,
 NULL,
 NULL
};

//
// ===========================================================================
// Module Entry Point
// ===========================================================================

/**Entry point of DxeIpmiUsb.

 Sequence:
 1. Save ImageHandle, SystemTable, BootServices, RuntimeServices.
 2. Locate the HOB list (get pointer to platform HOB data).
 3. Retrieve PCD token 5 (PCIe MMIO base address).
 4. Enable PCI Express MMIO in the chipset (write to config register).
 5. Read caller's EFLAGS; if interrupts enabled, use TSC-based calibration for microsecond delays.
 6. Install the Driver Binding Protocol via InstallMultipleProtocolInterfaces.
 7. Register a protocol notification for gEfiUsbIoProtocolGuid so existing USB IO handles are bound.

 @param[in] ImageHandle Module image handle.
 @param[in] SystemTable UEFI system table.

 @return EFI_STATUS returned by InstallMultipleProtocolInterfaces.
**/
EFI_STATUS EFIAPI DxeIpmiUsbEntryPoint (
 IN EFI_HANDLE ImageHandle,
 IN EFI_SYSTEM_TABLE *SystemTable
 )
{
 EFI_STATUS Status;
 UINT64 TscStart;

 //
 // Global initialization.
 //
 gImageHandle = ImageHandle;
 gSystemTable = SystemTable;
 gBootServices = SystemTable->BootServices;
 gRuntimeServices = SystemTable->RuntimeServices;

 //
 // Initialize the HOB list pointer.
 //
 GetHobList (SystemTable);

 //
 // Get PCI Express MMIO base address from PCD (token 5).
 //
 mPciExpressMmioBase = GetPcdBool (5);

 //
 // Enable PCIe MMIO config in the chipset.
 // Write 0x500 to the PCI Express config register at address 1024064
 // (which translates via PciExpressMmioAddress to the actual chipset
 // register that enables MMIO-based PCI config access).
 //
 if (GetPcdBool (1024068) >= 0) {
 IoWrite16 (
 (UINT16)PciExpressMmioAddress (1024064),
 0x500
 );
 //
 // Set the enable bit in the PCD.
 //
 GetPcdBool (1024068) |= 0x80;
 }

 //
 // Check if interrupts were enabled before we enter our delay loop.
 //
 if (ReadEflags () & 0x200) {
 //
 // Interrupts were enabled. Calibrate a TSC-based delay.
 //
 TscStart = ReadTimeStampCounter () & 0xFFFFFF;
 DisableInterrupts ();

 //
 // Spin waiting for 357 TSC ticks (~1 us at 357 MHz TSC).
 //
 while (((TscStart + 357 - (IoRead32 (0x508) & 0xFFFFFF)) & 0x800000) == 0) {
 CpuPause ();
 }

 EnableInterrupts ();
 } else {
 DisableInterrupts ();
 }

 //
 // Set up the Driver Binding Protocol.
 //
 mDriverBinding.DriverBindingHandle = ImageHandle;
 mDriverBinding.Image = ImageHandle;
 mDriverBinding.Version = 0x10;

 //
 // Install the Driver Binding Protocol.
 //
 Status = gBS->InstallMultipleProtocolInterfaces (
 &mDriverBinding.DriverBindingHandle,
 &gEfiDriverBindingProtocolGuid,
 &mDriverBinding,
 NULL
 );
 ASSERT_EFI_ERROR (Status);

 //
 // Register protocol notification to bind existing USB IO handles.
 //
 EfiCreateProtocolNotifyEvent (
 &gEfiUsbIoProtocolGuid,
 TPL_CALLBACK,
 (EFI_EVENT_NOTIFY)IpmiusbUsbIoProtocolNotify,
 &Status
 );

 return Status;
}

/**Protocol notification callback for gEfiUsbIoProtocolGuid.

 Triggered whenever a new USB IO protocol instance is installed.
 Calls IpmiUsbSupported() to check if the handle is an IPMI USB device.

 @param[in] Event The notification event.
 @param[in] Context Driver binding protocol pointer.
**/
VOID EFIAPI IpmiusbUsbIoProtocolNotify (
 IN EFI_EVENT Event,
 IN VOID *Context
 )
{
 //
 // This callback is invoked each time a USB IO protocol is registered.
 // The actual binding happens through the UEFI Driver Binding Protocol
 // which calls IpmiUsbSupported() and IpmiUsbStart() as USB IO handles
 // are enumerated.
 //
 return;
}