feat: Xhci BIOS-to-OS handoff
This commit is contained in:
@@ -230,6 +230,93 @@ namespace Drivers::USB::Xhci {
|
||||
|
||||
static void HandleInterrupt(uint8_t irq);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// BIOS-to-OS handoff (USB Legacy Support)
|
||||
//
|
||||
// Per xHCI spec section 7.1: walk the extended capability list starting at
|
||||
// xECP (HCCPARAMS1[31:16]) to find Cap ID 0x01 (USB Legacy Support). Set
|
||||
// the OS-Owned semaphore, wait for BIOS to clear its semaphore, and then
|
||||
// disable all USB-Legacy SMI sources.
|
||||
//
|
||||
// Without this, BIOS SMI handlers that emulate PS/2 from USB devices stay
|
||||
// active and steal i8042 events, so the built-in keyboard/trackpad input
|
||||
// does not reach the OS until USB activity wakes the SMI handler.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static constexpr uint8_t XHCI_EXTCAP_LEGACY = 0x01;
|
||||
static constexpr uint32_t USBLEGSUP_BIOS_OWNED = (1u << 16);
|
||||
static constexpr uint32_t USBLEGSUP_OS_OWNED = (1u << 24);
|
||||
static constexpr uint32_t USBLEGCTLSTS_SMI_ENABLES =
|
||||
(1u << 0) // USB SMI Enable
|
||||
| (1u << 4) // SMI on Host System Error Enable
|
||||
| (1u << 13) // SMI on OS Ownership Enable
|
||||
| (1u << 14) // SMI on PCI Command Enable
|
||||
| (1u << 15); // SMI on BAR Enable
|
||||
static constexpr uint32_t USBLEGCTLSTS_W1C_STATUS =
|
||||
(1u << 20) // SMI on OS Ownership Change
|
||||
| (1u << 21) // SMI on PCI Command
|
||||
| (1u << 22); // SMI on BAR
|
||||
|
||||
static void PerformBiosHandoff() {
|
||||
uint32_t hccparams1 = ReadCap(CAP_HCCPARAMS1);
|
||||
uint32_t xecp = (hccparams1 >> 16) & 0xFFFF;
|
||||
|
||||
if (xecp == 0) {
|
||||
KernelLogStream(INFO, "xHCI") << "No extended capabilities (xECP=0)";
|
||||
return;
|
||||
}
|
||||
|
||||
volatile uint32_t* extCap = (volatile uint32_t*)(g_mmioBase + xecp * 4);
|
||||
|
||||
for (int i = 0; i < 64; i++) {
|
||||
uint32_t capReg = *extCap;
|
||||
uint8_t capId = capReg & 0xFF;
|
||||
uint8_t nextOffset = (capReg >> 8) & 0xFF;
|
||||
|
||||
if (capId == XHCI_EXTCAP_LEGACY) {
|
||||
KernelLogStream(INFO, "xHCI") << "USB Legacy Support found, requesting BIOS handoff";
|
||||
|
||||
// Assert OS-Owned. Preserve the BIOS-Owned bit so BIOS sees the
|
||||
// request and can finish cleanly before clearing its own bit.
|
||||
*extCap = capReg | USBLEGSUP_OS_OWNED;
|
||||
|
||||
uint64_t deadline = Timekeeping::GetMilliseconds() + 1000;
|
||||
bool released = false;
|
||||
while (Timekeeping::GetMilliseconds() < deadline) {
|
||||
if (!(*extCap & USBLEGSUP_BIOS_OWNED)) {
|
||||
released = true;
|
||||
break;
|
||||
}
|
||||
BusyWaitMs(10);
|
||||
}
|
||||
|
||||
if (!released) {
|
||||
KernelLogStream(WARNING, "xHCI")
|
||||
<< "BIOS did not release controller in 1s, forcing handoff";
|
||||
uint32_t v = *extCap;
|
||||
v &= ~USBLEGSUP_BIOS_OWNED;
|
||||
v |= USBLEGSUP_OS_OWNED;
|
||||
*extCap = v;
|
||||
} else {
|
||||
KernelLogStream(OK, "xHCI") << "BIOS released xHCI ownership";
|
||||
}
|
||||
|
||||
// Disable all USB-Legacy SMI sources and clear any pending
|
||||
// W1C status bits. Preserves RsvdP bits via read-modify-write.
|
||||
volatile uint32_t* ctlsts = extCap + 1;
|
||||
uint32_t v = *ctlsts;
|
||||
v &= ~USBLEGCTLSTS_SMI_ENABLES;
|
||||
v |= USBLEGCTLSTS_W1C_STATUS;
|
||||
*ctlsts = v;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (nextOffset == 0) return;
|
||||
extCap += nextOffset;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// MSI setup (same pattern as E1000E)
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -1011,6 +1098,11 @@ namespace Drivers::USB::Xhci {
|
||||
if (g_maxSlots > MAX_SLOTS) g_maxSlots = MAX_SLOTS;
|
||||
if (g_maxPorts > MAX_PORTS) g_maxPorts = MAX_PORTS;
|
||||
|
||||
// Tell BIOS to release the controller and disable USB-Legacy SMIs.
|
||||
// Must run before we halt/reset so BIOS gets a chance to shut down
|
||||
// its own use of the controller cleanly.
|
||||
PerformBiosHandoff();
|
||||
|
||||
// Halt controller
|
||||
uint32_t usbcmd = ReadOp(OP_USBCMD);
|
||||
usbcmd &= ~USBCMD_RS;
|
||||
@@ -1259,6 +1351,11 @@ namespace Drivers::USB::Xhci {
|
||||
g_maxPorts = MAX_PORTS;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Step 4.5: BIOS-to-OS handoff (USB Legacy Support)
|
||||
// -----------------------------------------------------------------
|
||||
PerformBiosHandoff();
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Step 5: Halt controller
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user