From 6b45c59a9ad886bcb2a4c791bdcdc59da68659ba Mon Sep 17 00:00:00 2001 From: Daniel Hammer Date: Sun, 24 May 2026 10:23:16 +0200 Subject: [PATCH] feat: Xhci BIOS-to-OS handoff --- kernel/src/Drivers/USB/Xhci.cpp | 97 +++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/kernel/src/Drivers/USB/Xhci.cpp b/kernel/src/Drivers/USB/Xhci.cpp index d13c2d6..0d6820f 100644 --- a/kernel/src/Drivers/USB/Xhci.cpp +++ b/kernel/src/Drivers/USB/Xhci.cpp @@ -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 // -----------------------------------------------------------------