From fcb6f8e2470cc5e0bfc755aba531538c10aeb703 Mon Sep 17 00:00:00 2001 From: Daniel Hammer Date: Wed, 4 Mar 2026 21:10:34 +0100 Subject: [PATCH] feat: graphics/net PnP, keyboard driver improvements --- kernel/src/Api/Info.hpp | 2 +- kernel/src/Drivers/Graphics/IntelGPU.cpp | 103 ++++++++- kernel/src/Drivers/Graphics/IntelGPU.hpp | 12 +- kernel/src/Drivers/Init.cpp | 116 +++++++++- kernel/src/Drivers/Init.hpp | 10 +- kernel/src/Drivers/Net/E1000.cpp | 95 ++++++-- kernel/src/Drivers/Net/E1000.hpp | 4 + kernel/src/Drivers/Net/E1000E.cpp | 268 +++++++++++------------ kernel/src/Drivers/Net/E1000E.hpp | 4 + kernel/src/Drivers/USB/HidKeyboard.cpp | 68 ++++-- kernel/src/Drivers/USB/HidKeyboard.hpp | 3 + kernel/src/Drivers/USB/UsbDevice.cpp | 8 +- kernel/src/Drivers/USB/Xhci.cpp | 245 +++++++++++++++++++-- kernel/src/Drivers/USB/Xhci.hpp | 12 +- kernel/src/Main.cpp | 5 +- kernel/src/Pci/Pci.cpp | 80 +++++++ kernel/src/Pci/Pci.hpp | 43 ++++ kernel/src/Timekeeping/ApicTimer.cpp | 2 + 18 files changed, 836 insertions(+), 244 deletions(-) diff --git a/kernel/src/Api/Info.hpp b/kernel/src/Api/Info.hpp index f306d5b..d8fc704 100644 --- a/kernel/src/Api/Info.hpp +++ b/kernel/src/Api/Info.hpp @@ -16,7 +16,7 @@ namespace Montauk { // Copy strings into fixed-size arrays (user-accessible) const char* name = "MontaukOS"; - const char* ver = "0.1.1"; + const char* ver = "0.1.2"; for (int i = 0; name[i]; i++) outInfo->osName[i] = name[i]; outInfo->osName[9] = '\0'; for (int i = 0; ver[i]; i++) outInfo->osVersion[i] = ver[i]; diff --git a/kernel/src/Drivers/Graphics/IntelGPU.cpp b/kernel/src/Drivers/Graphics/IntelGPU.cpp index 77516d4..42a263f 100644 --- a/kernel/src/Drivers/Graphics/IntelGPU.cpp +++ b/kernel/src/Drivers/Graphics/IntelGPU.cpp @@ -132,14 +132,7 @@ namespace Drivers::Graphics::IntelGPU { uint8_t func = g_gpuInfo.pciFunction; // Read BAR0 - uint32_t bar0Low = Pci::LegacyRead32(bus, dev, func, (uint8_t)PCI_REG_BAR0); - uint64_t mmioPhys = bar0Low & 0xFFFFFFF0u; // Mask off type/prefetchable bits - - // Check for 64-bit BAR (bit 2 of BAR type field) - if (bar0Low & 0x04) { - uint32_t bar0High = Pci::LegacyRead32(bus, dev, func, (uint8_t)(PCI_REG_BAR0 + 4)); - mmioPhys |= ((uint64_t)bar0High << 32); - } + uint64_t mmioPhys = Pci::ReadBar0(bus, dev, func); g_gpuInfo.mmioPhys = mmioPhys; g_gpuInfo.mmioSize = 0x200000; // Map 2MB of MMIO space @@ -155,10 +148,8 @@ namespace Drivers::Graphics::IntelGPU { KernelLogStream(OK, "IntelGPU") << "MMIO mapped at virtual " << base::hex << (uint64_t)g_mmioBase; - // Enable memory space and bus master in PCI command register - uint16_t pciCmd = Pci::LegacyRead16(bus, dev, func, (uint8_t)PCI_REG_COMMAND); - pciCmd |= PCI_CMD_MEM_SPACE | PCI_CMD_BUS_MASTER; - Pci::LegacyWrite16(bus, dev, func, (uint8_t)PCI_REG_COMMAND, pciCmd); + // Enable memory space and bus master + Pci::EnableBusMaster(bus, dev, func); KernelLogStream(OK, "IntelGPU") << "PCI memory space and bus mastering enabled"; @@ -473,6 +464,94 @@ namespace Drivers::Graphics::IntelGPU { // Public API // ========================================================================= + // ========================================================================= + // Probe (called by PCI driver matching framework) + // ========================================================================= + + bool Probe(const Pci::PciDevice& dev) { + if (g_initialized) return false; + + // Fill in PCI location + g_gpuInfo.pciBus = dev.Bus; + g_gpuInfo.pciDevice = dev.Device; + g_gpuInfo.pciFunction = dev.Function; + g_gpuInfo.deviceId = dev.DeviceId; + + // Match device ID against supported table + const DeviceInfo* matchedInfo = nullptr; + for (int j = 0; j < SupportedDeviceCount; j++) { + if (dev.DeviceId == SupportedDevices[j].deviceId) { + matchedInfo = &SupportedDevices[j]; + break; + } + } + + if (matchedInfo != nullptr) { + g_gpuInfo.gen = matchedInfo->gen; + g_gpuInfo.name = matchedInfo->name; + KernelLogStream(OK, "IntelGPU") << "Found " << matchedInfo->name + << " (device " << base::hex << (uint64_t)dev.DeviceId << ")" + << " at PCI " << (uint64_t)dev.Bus << ":" + << (uint64_t)dev.Device << "." << (uint64_t)dev.Function; + } else { + g_gpuInfo.gen = 7; + g_gpuInfo.name = "Intel GPU"; + KernelLogStream(WARNING, "IntelGPU") << "Unknown Intel display controller " + << "(device " << base::hex << (uint64_t)dev.DeviceId << ")" + << " at PCI " << (uint64_t)dev.Bus << ":" + << (uint64_t)dev.Device << "." << (uint64_t)dev.Function + << " - attempting generic initialization"; + } + + g_gpuGen = g_gpuInfo.gen; + + if (!MapMmio()) { + KernelLogStream(ERROR, "IntelGPU") << "Failed to map MMIO region"; + return false; + } + + DisableVga(); + + if (!ReadDisplayState()) { + KernelLogStream(ERROR, "IntelGPU") << "Failed to read display state"; + return false; + } + + if (!InitializeGtt()) { + KernelLogStream(ERROR, "IntelGPU") << "Failed to initialize GTT"; + return false; + } + + if (!SetupFramebuffer()) { + KernelLogStream(ERROR, "IntelGPU") << "Failed to set up framebuffer"; + return false; + } + + ProgramDisplayPlane(); + g_initialized = true; + + uint64_t fwWidth = ::Graphics::Cursor::GetFramebufferWidth(); + uint64_t fwHeight = ::Graphics::Cursor::GetFramebufferHeight(); + uint64_t fwPitch = ::Graphics::Cursor::GetFramebufferPitch(); + + if (g_fbWidth != fwWidth || g_fbHeight != fwHeight || g_fbPitch != fwPitch) { + KernelLogStream(WARNING, "IntelGPU") << "GPU dimensions differ from firmware!"; + KernelLogStream(WARNING, "IntelGPU") << " GPU: " + << base::dec << g_fbWidth << "x" << g_fbHeight << " pitch=" << g_fbPitch; + KernelLogStream(WARNING, "IntelGPU") << " Firmware: " + << base::dec << fwWidth << "x" << fwHeight << " pitch=" << fwPitch; + } + + KernelLogStream(OK, "IntelGPU") << "Initialization complete: " + << base::dec << g_fbWidth << "x" << g_fbHeight + << " @ " << base::hex << (uint64_t)g_fbBase; + return true; + } + + // ========================================================================= + // Initialize (standalone fallback path) + // ========================================================================= + void Initialize() { KernelLogStream(INFO, "IntelGPU") << "Scanning for Intel integrated graphics..."; diff --git a/kernel/src/Drivers/Graphics/IntelGPU.hpp b/kernel/src/Drivers/Graphics/IntelGPU.hpp index 0f3b42d..d065630 100644 --- a/kernel/src/Drivers/Graphics/IntelGPU.hpp +++ b/kernel/src/Drivers/Graphics/IntelGPU.hpp @@ -7,6 +7,7 @@ #pragma once #include +#include namespace Drivers::Graphics::IntelGPU { @@ -18,14 +19,8 @@ namespace Drivers::Graphics::IntelGPU { static constexpr uint8_t ClassDisplay = 0x03; static constexpr uint8_t SubclassVGA = 0x00; - // PCI config space offsets - static constexpr uint16_t PCI_REG_BAR0 = 0x10; + // PCI config space offsets (GPU-specific, not in shared Pci::) static constexpr uint16_t PCI_REG_BAR2 = 0x18; - static constexpr uint16_t PCI_REG_COMMAND = 0x04; - static constexpr uint16_t PCI_CMD_MEM_SPACE = (1 << 1); - static constexpr uint16_t PCI_CMD_BUS_MASTER = (1 << 2); - - // Graphics control register (PCI config offset 0x50 on SNB+) static constexpr uint16_t PCI_REG_GMCH_CTL = 0x50; // Supported Intel GPU device IDs (representative subset) @@ -400,6 +395,9 @@ namespace Drivers::Graphics::IntelGPU { // Initialize the Intel GPU driver (scans PCI, maps MMIO, sets up GTT + FB) void Initialize(); + // Probe a specific PCI device (called by PCI driver matching framework) + bool Probe(const Pci::PciDevice& dev); + // Check if an Intel GPU was found and initialized bool IsInitialized(); diff --git a/kernel/src/Drivers/Init.cpp b/kernel/src/Drivers/Init.cpp index 8291cf7..98159dc 100644 --- a/kernel/src/Drivers/Init.cpp +++ b/kernel/src/Drivers/Init.cpp @@ -5,17 +5,122 @@ */ #include "Init.hpp" +#include #include #include #include +#include #include #include #include namespace Drivers { + // ------------------------------------------------------------------------- + // Device ID whitelists + // ------------------------------------------------------------------------- + + static constexpr uint16_t g_e1000Ids[] = { + 0x100E, + }; + + static constexpr uint16_t g_e1000eIds[] = { + // I217 + 0x153A, 0x153B, + // I218 + 0x155A, 0x1559, 0x15A0, 0x15A1, 0x15A2, 0x15A3, + // I219-LM + 0x156F, 0x15B7, 0x15BB, 0x15BD, 0x15DF, 0x15E1, 0x15E3, + 0x15D7, 0x0D4C, 0x0D4E, 0x0D53, 0x0D55, 0x0DC5, 0x0DC7, + 0x1A1C, 0x1A1E, + // I219-V + 0x1570, 0x15B8, 0x15BC, 0x15BE, 0x15E0, 0x15E2, 0x15D6, + 0x15D8, 0x0D4D, 0x0D4F, 0x0D54, 0x0DC6, 0x0DC8, 0x1A1D, + 0x1A1F, + }; + + // ------------------------------------------------------------------------- + // Probe wrappers (adapt namespace::Probe to PciProbeFunc signature) + // ------------------------------------------------------------------------- + + static bool ProbeIntelGPU(const Pci::PciDevice& dev) { + return Graphics::IntelGPU::Probe(dev); + } + + static bool ProbeXhci(const Pci::PciDevice& dev) { + return USB::Xhci::Probe(dev); + } + + static bool ProbeE1000(const Pci::PciDevice& dev) { + return Net::E1000::Probe(dev); + } + + static bool ProbeE1000E(const Pci::PciDevice& dev) { + return Net::E1000E::Probe(dev); + } + + // ------------------------------------------------------------------------- + // Driver table + // ------------------------------------------------------------------------- + + static constexpr Pci::PciDriverDesc g_driverTable[] = { + // Order 1: Intel GPU — Early phase, match vendor=0x8086 + class=0x03 + { + "IntelGPU", + 0x8086, // VendorId + 0x03, // ClassCode (Display) + 0xFF, // SubClass (any) + 0xFF, // ProgIf (any) + nullptr, // DeviceIds (internal gen lookup) + 0, // DeviceIdCount + Pci::ProbePhase::Early, + ProbeIntelGPU, + }, + // Order 2: xHCI — Normal phase, match class=0x0C/0x03/0x30 + { + "xHCI", + 0, // VendorId (any) + 0x0C, // ClassCode (Serial Bus) + 0x03, // SubClass (USB) + 0x30, // ProgIf (xHCI) + nullptr, + 0, + Pci::ProbePhase::Normal, + ProbeXhci, + }, + // Order 3: E1000 — Normal phase, vendor=0x8086 + deviceId={0x100E} + { + "E1000", + 0x8086, + 0xFF, 0xFF, 0xFF, + g_e1000Ids, + sizeof(g_e1000Ids) / sizeof(g_e1000Ids[0]), + Pci::ProbePhase::Normal, + ProbeE1000, + }, + // Order 4: E1000E — Normal phase, vendor=0x8086 + deviceIds list + { + "E1000E", + 0x8086, + 0xFF, 0xFF, 0xFF, + g_e1000eIds, + sizeof(g_e1000eIds) / sizeof(g_e1000eIds[0]), + Pci::ProbePhase::Normal, + ProbeE1000E, + }, + }; + + static constexpr uint16_t g_driverTableCount = sizeof(g_driverTable) / sizeof(g_driverTable[0]); + + // ------------------------------------------------------------------------- + // Public API + // ------------------------------------------------------------------------- + + void ProbeEarly() { + Pci::ProbeAll(g_driverTable, g_driverTableCount, Pci::ProbePhase::Early); + } + void InitializeGraphics() { - Graphics::IntelGPU::Initialize(); if (Graphics::IntelGPU::IsInitialized()) { ::Graphics::Cursor::SetFramebuffer( Graphics::IntelGPU::GetFramebufferBase(), @@ -26,12 +131,11 @@ namespace Drivers { } } + void ProbeNormal() { + Pci::ProbeAll(g_driverTable, g_driverTableCount, Pci::ProbePhase::Normal); + } + void InitializeNetwork() { - Net::E1000::Initialize(); - if (!Net::E1000::IsInitialized()) { - Kt::KernelLogStream(Kt::INFO, "Init") << "E1000 not found, trying E1000E..."; - Net::E1000E::Initialize(); - } ::Net::Initialize(); } diff --git a/kernel/src/Drivers/Init.hpp b/kernel/src/Drivers/Init.hpp index f6aee5e..3313eca 100644 --- a/kernel/src/Drivers/Init.hpp +++ b/kernel/src/Drivers/Init.hpp @@ -8,10 +8,16 @@ namespace Drivers { - // Initialize Intel GPU driver and update the cursor framebuffer if found. + // Probe PCI devices for Early-phase drivers (GPU). + void ProbeEarly(); + + // Post-probe: wire up GPU framebuffer to cursor subsystem. void InitializeGraphics(); - // Initialize network driver (E1000, falling back to E1000E) and the net stack. + // Probe PCI devices for Normal-phase drivers (xHCI, E1000, E1000E). + void ProbeNormal(); + + // Post-probe: initialize network stack. void InitializeNetwork(); } diff --git a/kernel/src/Drivers/Net/E1000.cpp b/kernel/src/Drivers/Net/E1000.cpp index b2c5163..6fa7ab0 100644 --- a/kernel/src/Drivers/Net/E1000.cpp +++ b/kernel/src/Drivers/Net/E1000.cpp @@ -23,15 +23,6 @@ namespace Drivers::Net::E1000 { static constexpr uint16_t VendorIntel = 0x8086; static constexpr uint16_t DeviceE1000 = 0x100E; - // PCI config space offsets - static constexpr uint8_t PCI_REG_BAR0 = 0x10; - static constexpr uint8_t PCI_REG_COMMAND = 0x04; - static constexpr uint8_t PCI_REG_INTERRUPT = 0x3C; - - // PCI command register bits - static constexpr uint16_t PCI_CMD_BUS_MASTER = (1 << 2); - static constexpr uint16_t PCI_CMD_MEM_SPACE = (1 << 1); - // Driver state static bool g_initialized = false; static volatile uint8_t* g_mmioBase = nullptr; @@ -296,6 +287,81 @@ namespace Drivers::Net::E1000 { // Public API // ------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + // Probe (called by PCI driver matching framework) + // ------------------------------------------------------------------------- + + bool Probe(const Pci::PciDevice& dev) { + if (g_initialized) return false; + + KernelLogStream(OK, "E1000") << "Found E1000 at PCI " + << base::hex << (uint64_t)dev.Bus << ":" + << (uint64_t)dev.Device << "." << (uint64_t)dev.Function; + + // Read BAR0 (MMIO base address) + uint64_t mmioPhys = Pci::ReadBar0(dev.Bus, dev.Device, dev.Function); + KernelLogStream(INFO, "E1000") << "BAR0 physical: " << base::hex << mmioPhys; + + // Map the MMIO region (128KB = 32 pages) + constexpr uint64_t MmioSize = 0x20000; + for (uint64_t offset = 0; offset < MmioSize; offset += 0x1000) { + Memory::VMM::g_paging->MapMMIO(mmioPhys + offset, Memory::HHDM(mmioPhys + offset)); + } + g_mmioBase = (volatile uint8_t*)Memory::HHDM(mmioPhys); + + // Enable bus mastering and memory space + Pci::EnableBusMaster(dev.Bus, dev.Device, dev.Function); + KernelLogStream(OK, "E1000") << "Bus mastering enabled"; + + // Read interrupt line from PCI config + g_irqLine = Pci::LegacyRead8(dev.Bus, dev.Device, dev.Function, (uint8_t)Pci::PCI_REG_INTERRUPT); + KernelLogStream(INFO, "E1000") << "IRQ line: " << base::dec << (uint64_t)g_irqLine; + + // Reset the device + uint32_t ctrl = ReadReg(REG_CTRL); + WriteReg(REG_CTRL, ctrl | CTRL_RST); + for (int i = 0; i < 100000; i++) { + if (!(ReadReg(REG_CTRL) & CTRL_RST)) break; + } + + WriteReg(REG_IMC, 0xFFFFFFFF); + + ctrl = ReadReg(REG_CTRL); + ctrl |= CTRL_SLU; + ctrl &= ~(1u << 3); + ctrl &= ~(1u << 31); + ctrl &= ~(1u << 7); + WriteReg(REG_CTRL, ctrl); + + ReadMacAddress(); + KernelLogStream(OK, "E1000") << "MAC: " << base::hex + << (uint64_t)g_macAddress[0] << ":" << (uint64_t)g_macAddress[1] << ":" + << (uint64_t)g_macAddress[2] << ":" << (uint64_t)g_macAddress[3] << ":" + << (uint64_t)g_macAddress[4] << ":" << (uint64_t)g_macAddress[5]; + + for (uint32_t i = 0; i < 128; i++) { + WriteReg(REG_MTA + (i * 4), 0); + } + + SetupRx(); + SetupTx(); + + Hal::RegisterIrqHandler(g_irqLine, HandleInterrupt); + Hal::IoApic::UnmaskIrq(Hal::IoApic::GetGsiForIrq(g_irqLine)); + WriteReg(REG_IMS, ICR_RXT0 | ICR_TXDW | ICR_TXQE | ICR_LSC | ICR_RXDMT0); + + g_initialized = true; + + uint32_t status = ReadReg(REG_STATUS); + bool linkUp = (status & (1 << 1)) != 0; + KernelLogStream(OK, "E1000") << "Initialization complete, link: " << (linkUp ? "UP" : "DOWN"); + return true; + } + + // ------------------------------------------------------------------------- + // Initialize (standalone fallback path) + // ------------------------------------------------------------------------- + void Initialize() { KernelLogStream(INFO, "E1000") << "Scanning for Intel E1000 NIC..."; @@ -320,8 +386,7 @@ namespace Drivers::Net::E1000 { << (uint64_t)e1000Dev->Device << "." << (uint64_t)e1000Dev->Function; // Read BAR0 (MMIO base address) - uint32_t bar0 = Pci::LegacyRead32(e1000Dev->Bus, e1000Dev->Device, e1000Dev->Function, PCI_REG_BAR0); - uint64_t mmioPhys = bar0 & 0xFFFFFFF0; // Mask low 4 bits (type/prefetchable flags) + uint64_t mmioPhys = Pci::ReadBar0(e1000Dev->Bus, e1000Dev->Device, e1000Dev->Function); KernelLogStream(INFO, "E1000") << "BAR0 physical: " << base::hex << mmioPhys; @@ -333,15 +398,13 @@ namespace Drivers::Net::E1000 { g_mmioBase = (volatile uint8_t*)Memory::HHDM(mmioPhys); - // Enable bus mastering and memory space in PCI command register - uint16_t pciCmd = Pci::LegacyRead16(e1000Dev->Bus, e1000Dev->Device, e1000Dev->Function, PCI_REG_COMMAND); - pciCmd |= PCI_CMD_BUS_MASTER | PCI_CMD_MEM_SPACE; - Pci::LegacyWrite16(e1000Dev->Bus, e1000Dev->Device, e1000Dev->Function, PCI_REG_COMMAND, pciCmd); + // Enable bus mastering and memory space + Pci::EnableBusMaster(e1000Dev->Bus, e1000Dev->Device, e1000Dev->Function); KernelLogStream(OK, "E1000") << "Bus mastering enabled"; // Read interrupt line from PCI config - g_irqLine = Pci::LegacyRead8(e1000Dev->Bus, e1000Dev->Device, e1000Dev->Function, PCI_REG_INTERRUPT); + g_irqLine = Pci::LegacyRead8(e1000Dev->Bus, e1000Dev->Device, e1000Dev->Function, (uint8_t)Pci::PCI_REG_INTERRUPT); KernelLogStream(INFO, "E1000") << "IRQ line: " << base::dec << (uint64_t)g_irqLine; // Reset the device diff --git a/kernel/src/Drivers/Net/E1000.hpp b/kernel/src/Drivers/Net/E1000.hpp index 7aa5157..3f67a1e 100644 --- a/kernel/src/Drivers/Net/E1000.hpp +++ b/kernel/src/Drivers/Net/E1000.hpp @@ -6,6 +6,7 @@ #pragma once #include +#include namespace Drivers::Net::E1000 { @@ -102,6 +103,9 @@ namespace Drivers::Net::E1000 { // Initialize the E1000 driver (scans PCI for the device) void Initialize(); + // Probe a specific PCI device (called by PCI driver matching framework) + bool Probe(const Pci::PciDevice& dev); + // Send a raw Ethernet frame bool SendPacket(const uint8_t* data, uint16_t length); diff --git a/kernel/src/Drivers/Net/E1000E.cpp b/kernel/src/Drivers/Net/E1000E.cpp index 6d7b766..abb059f 100644 --- a/kernel/src/Drivers/Net/E1000E.cpp +++ b/kernel/src/Drivers/Net/E1000E.cpp @@ -77,17 +77,6 @@ namespace Drivers::Net::E1000E { static constexpr uint32_t g_deviceTableSize = sizeof(g_deviceTable) / sizeof(g_deviceTable[0]); - // PCI config space offsets - static constexpr uint8_t PCI_REG_BAR0 = 0x10; - static constexpr uint8_t PCI_REG_BAR1 = 0x14; - static constexpr uint8_t PCI_REG_COMMAND = 0x04; - static constexpr uint8_t PCI_REG_INTERRUPT = 0x3C; - - // PCI command register bits - static constexpr uint16_t PCI_CMD_BUS_MASTER = (1 << 2); - static constexpr uint16_t PCI_CMD_MEM_SPACE = (1 << 1); - static constexpr uint16_t PCI_CMD_INTX_DISABLE = (1 << 10); - // MSI configuration static constexpr uint8_t MSI_IRQ = 24; // IRQ slot 24 = vector 56 (first MSI slot) static constexpr uint32_t MSI_VECTOR = 56; // IRQ_VECTOR_BASE + MSI_IRQ @@ -451,9 +440,9 @@ namespace Drivers::Net::E1000E { Pci::LegacyWrite16(bus, dev, func, cap + 2, msgCtrl); // Disable legacy INTx in PCI command register - uint16_t pciCmd = Pci::LegacyRead16(bus, dev, func, PCI_REG_COMMAND); - pciCmd |= PCI_CMD_INTX_DISABLE; - Pci::LegacyWrite16(bus, dev, func, PCI_REG_COMMAND, pciCmd); + uint16_t pciCmd = Pci::LegacyRead16(bus, dev, func, (uint8_t)Pci::PCI_REG_COMMAND); + pciCmd |= Pci::PCI_CMD_INTX_DISABLE; + Pci::LegacyWrite16(bus, dev, func, (uint8_t)Pci::PCI_REG_COMMAND, pciCmd); // Register the interrupt handler for MSI vector Hal::RegisterIrqHandler(MSI_IRQ, HandleInterrupt); @@ -518,6 +507,129 @@ namespace Drivers::Net::E1000E { // Public API // ------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + // Shared init logic (used by both Probe and Initialize) + // ------------------------------------------------------------------------- + + static bool InitDevice(uint8_t bus, uint8_t device, uint8_t function, const char* deviceName) { + KernelLogStream(OK, "E1000E") << "Found " << deviceName << " at PCI " + << base::hex << (uint64_t)bus << ":" + << (uint64_t)device << "." << (uint64_t)function; + + // Read BAR0 + uint64_t mmioPhys = Pci::ReadBar0(bus, device, function); + KernelLogStream(INFO, "E1000E") << "BAR0 physical: " << base::hex << mmioPhys; + + // Map the MMIO region (128KB = 32 pages) + constexpr uint64_t MmioSize = 0x20000; + for (uint64_t offset = 0; offset < MmioSize; offset += 0x1000) { + Memory::VMM::g_paging->MapMMIO(mmioPhys + offset, Memory::HHDM(mmioPhys + offset)); + } + + g_mmioBase = (volatile uint8_t*)Memory::HHDM(mmioPhys); + + // Enable bus mastering and memory space + Pci::EnableBusMaster(bus, device, function); + KernelLogStream(OK, "E1000E") << "Bus mastering enabled"; + + // Read interrupt line from PCI config (used for legacy IRQ fallback) + g_irqLine = Pci::LegacyRead8(bus, device, function, (uint8_t)Pci::PCI_REG_INTERRUPT); + KernelLogStream(INFO, "E1000E") << "PCI IRQ line: " << base::dec << (uint64_t)g_irqLine; + + // --- ICH/PCH reset sequence --- + + KernelLogStream(INFO, "E1000E") << "Disabling interrupts..."; + WriteReg(REG_IMC, 0xFFFFFFFF); + ReadReg(REG_ICR); + + KernelLogStream(INFO, "E1000E") << "Acquiring semaphore..."; + AcquireSwFwSync(); + + KernelLogStream(INFO, "E1000E") << "Resetting device..."; + uint32_t ctrl = ReadReg(REG_CTRL); + WriteReg(REG_CTRL, ctrl | CTRL_RST); + + for (int i = 0; i < 100000; i++) { + asm volatile("" ::: "memory"); + } + for (int i = 0; i < 10000; i++) { + if (!(ReadReg(REG_CTRL) & CTRL_RST)) break; + } + + ReleaseSwFwSync(); + + WriteReg(REG_IMC, 0xFFFFFFFF); + ReadReg(REG_ICR); + + KernelLogStream(OK, "E1000E") << "Reset complete"; + + ctrl = ReadReg(REG_CTRL); + ctrl |= CTRL_SLU; + ctrl &= ~CTRL_FRCSPD; + ctrl &= ~CTRL_FRCDPLX; + ctrl &= ~(1u << 3); + ctrl &= ~(1u << 31); + ctrl &= ~(1u << 7); + WriteReg(REG_CTRL, ctrl); + + InitPhy(); + ReadMacAddress(); + + KernelLogStream(OK, "E1000E") << "MAC: " << base::hex + << (uint64_t)g_macAddress[0] << ":" << (uint64_t)g_macAddress[1] << ":" + << (uint64_t)g_macAddress[2] << ":" << (uint64_t)g_macAddress[3] << ":" + << (uint64_t)g_macAddress[4] << ":" << (uint64_t)g_macAddress[5]; + + for (uint32_t i = 0; i < 128; i++) { + WriteReg(REG_MTA + (i * 4), 0); + } + + SetupRx(); + SetupTx(); + + if (SetupMsi(bus, device, function)) { + WriteReg(REG_IMS, ICR_RXT0 | ICR_TXDW | ICR_TXQE | ICR_LSC | ICR_RXDMT0); + } else if (g_irqLine != 0xFF) { + KernelLogStream(INFO, "E1000E") << "Falling back to legacy IRQ " << base::dec << (uint64_t)g_irqLine; + Hal::RegisterIrqHandler(g_irqLine, HandleInterrupt); + Hal::IoApic::UnmaskIrq(Hal::IoApic::GetGsiForIrq(g_irqLine)); + WriteReg(REG_IMS, ICR_RXT0 | ICR_TXDW | ICR_TXQE | ICR_LSC | ICR_RXDMT0); + } else { + KernelLogStream(WARNING, "E1000E") << "No MSI or legacy IRQ available, using polling mode"; + g_pollingMode = true; + } + + g_initialized = true; + + uint32_t status = ReadReg(REG_STATUS); + bool linkUp = (status & (1 << 1)) != 0; + KernelLogStream(OK, "E1000E") << "Initialization complete, link: " << (linkUp ? "UP" : "DOWN"); + return true; + } + + // ------------------------------------------------------------------------- + // Probe (called by PCI driver matching framework) + // ------------------------------------------------------------------------- + + bool Probe(const Pci::PciDevice& dev) { + if (g_initialized) return false; + + // Look up device name from table + const char* name = "e1000e"; + for (uint32_t j = 0; j < g_deviceTableSize; j++) { + if (dev.DeviceId == g_deviceTable[j].DeviceId) { + name = g_deviceTable[j].Name; + break; + } + } + + return InitDevice(dev.Bus, dev.Device, dev.Function, name); + } + + // ------------------------------------------------------------------------- + // Initialize (standalone fallback path) + // ------------------------------------------------------------------------- + void Initialize() { KernelLogStream(INFO, "E1000E") << "Scanning for Intel e1000e NIC..."; @@ -546,133 +658,7 @@ namespace Drivers::Net::E1000E { return; } - KernelLogStream(OK, "E1000E") << "Found " << foundName << " at PCI " - << base::hex << (uint64_t)foundDev->Bus << ":" - << (uint64_t)foundDev->Device << "." << (uint64_t)foundDev->Function; - - // Read BAR0 (MMIO base address), check for 64-bit BAR - uint32_t bar0 = Pci::LegacyRead32(foundDev->Bus, foundDev->Device, foundDev->Function, PCI_REG_BAR0); - uint64_t mmioPhys = bar0 & 0xFFFFFFF0; - - // Check if 64-bit BAR (type field bits 2:1 == 0b10) - if ((bar0 & 0x06) == 0x04) { - uint32_t bar1 = Pci::LegacyRead32(foundDev->Bus, foundDev->Device, foundDev->Function, PCI_REG_BAR1); - mmioPhys |= ((uint64_t)bar1 << 32); - } - - KernelLogStream(INFO, "E1000E") << "BAR0 physical: " << base::hex << mmioPhys; - - // Map the MMIO region (128KB = 32 pages) - constexpr uint64_t MmioSize = 0x20000; - for (uint64_t offset = 0; offset < MmioSize; offset += 0x1000) { - Memory::VMM::g_paging->MapMMIO(mmioPhys + offset, Memory::HHDM(mmioPhys + offset)); - } - - g_mmioBase = (volatile uint8_t*)Memory::HHDM(mmioPhys); - - // Enable bus mastering and memory space in PCI command register - uint16_t pciCmd = Pci::LegacyRead16(foundDev->Bus, foundDev->Device, foundDev->Function, PCI_REG_COMMAND); - pciCmd |= PCI_CMD_BUS_MASTER | PCI_CMD_MEM_SPACE; - Pci::LegacyWrite16(foundDev->Bus, foundDev->Device, foundDev->Function, PCI_REG_COMMAND, pciCmd); - - KernelLogStream(OK, "E1000E") << "Bus mastering enabled"; - - // Read interrupt line from PCI config (used for legacy IRQ fallback) - g_irqLine = Pci::LegacyRead8(foundDev->Bus, foundDev->Device, foundDev->Function, PCI_REG_INTERRUPT); - KernelLogStream(INFO, "E1000E") << "PCI IRQ line: " << base::dec << (uint64_t)g_irqLine; - - // --- ICH/PCH reset sequence --- - - // 1. Disable interrupts, flush ICR - KernelLogStream(INFO, "E1000E") << "Disabling interrupts..."; - WriteReg(REG_IMC, 0xFFFFFFFF); - ReadReg(REG_ICR); - - // 2. Acquire SW/FW semaphore (non-fatal if it fails) - KernelLogStream(INFO, "E1000E") << "Acquiring semaphore..."; - AcquireSwFwSync(); - - // 3. Reset the device - KernelLogStream(INFO, "E1000E") << "Resetting device..."; - uint32_t ctrl = ReadReg(REG_CTRL); - WriteReg(REG_CTRL, ctrl | CTRL_RST); - - // Post-reset settling delay (give hardware time before polling) - for (int i = 0; i < 100000; i++) { - asm volatile("" ::: "memory"); - } - - // Poll for reset completion - for (int i = 0; i < 10000; i++) { - if (!(ReadReg(REG_CTRL) & CTRL_RST)) { - break; - } - } - - // 4. Release semaphore - ReleaseSwFwSync(); - - // 5. Disable interrupts again after reset - WriteReg(REG_IMC, 0xFFFFFFFF); - ReadReg(REG_ICR); - - KernelLogStream(OK, "E1000E") << "Reset complete"; - - // Set link up: SLU on, but let auto-negotiation decide speed/duplex - ctrl = ReadReg(REG_CTRL); - ctrl |= CTRL_SLU; - ctrl &= ~CTRL_FRCSPD; - ctrl &= ~CTRL_FRCDPLX; - ctrl &= ~(1u << 3); // Clear LRST - ctrl &= ~(1u << 31); // Clear PHY_RST - ctrl &= ~(1u << 7); // Clear ILOS - WriteReg(REG_CTRL, ctrl); - - // Initialize PHY - InitPhy(); - - // Read MAC address - ReadMacAddress(); - - KernelLogStream(OK, "E1000E") << "MAC: " - << base::hex - << (uint64_t)g_macAddress[0] << ":" - << (uint64_t)g_macAddress[1] << ":" - << (uint64_t)g_macAddress[2] << ":" - << (uint64_t)g_macAddress[3] << ":" - << (uint64_t)g_macAddress[4] << ":" - << (uint64_t)g_macAddress[5]; - - // Zero out the Multicast Table Array (128 entries) - for (uint32_t i = 0; i < 128; i++) { - WriteReg(REG_MTA + (i * 4), 0); - } - - // Set up RX and TX descriptor rings - SetupRx(); - SetupTx(); - - // Three-tier interrupt strategy: MSI → legacy IRQ → polling - if (SetupMsi(foundDev->Bus, foundDev->Device, foundDev->Function)) { - // MSI configured — enable NIC interrupt causes - WriteReg(REG_IMS, ICR_RXT0 | ICR_TXDW | ICR_TXQE | ICR_LSC | ICR_RXDMT0); - } else if (g_irqLine != 0xFF) { - // Legacy IRQ fallback - KernelLogStream(INFO, "E1000E") << "Falling back to legacy IRQ " << base::dec << (uint64_t)g_irqLine; - Hal::RegisterIrqHandler(g_irqLine, HandleInterrupt); - Hal::IoApic::UnmaskIrq(Hal::IoApic::GetGsiForIrq(g_irqLine)); - WriteReg(REG_IMS, ICR_RXT0 | ICR_TXDW | ICR_TXQE | ICR_LSC | ICR_RXDMT0); - } else { - // Polling as last resort - KernelLogStream(WARNING, "E1000E") << "No MSI or legacy IRQ available, using polling mode"; - g_pollingMode = true; - } - - g_initialized = true; - - uint32_t status = ReadReg(REG_STATUS); - bool linkUp = (status & (1 << 1)) != 0; - KernelLogStream(OK, "E1000E") << "Initialization complete, link: " << (linkUp ? "UP" : "DOWN"); + InitDevice(foundDev->Bus, foundDev->Device, foundDev->Function, foundName); } // ------------------------------------------------------------------------- diff --git a/kernel/src/Drivers/Net/E1000E.hpp b/kernel/src/Drivers/Net/E1000E.hpp index 9f996be..7034690 100644 --- a/kernel/src/Drivers/Net/E1000E.hpp +++ b/kernel/src/Drivers/Net/E1000E.hpp @@ -6,6 +6,7 @@ #pragma once #include +#include namespace Drivers::Net::E1000E { @@ -134,6 +135,9 @@ namespace Drivers::Net::E1000E { // Initialize the E1000E driver (scans PCI for the device) void Initialize(); + // Probe a specific PCI device (called by PCI driver matching framework) + bool Probe(const Pci::PciDevice& dev); + // Send a raw Ethernet frame bool SendPacket(const uint8_t* data, uint16_t length); diff --git a/kernel/src/Drivers/USB/HidKeyboard.cpp b/kernel/src/Drivers/USB/HidKeyboard.cpp index 06f626a..db77371 100644 --- a/kernel/src/Drivers/USB/HidKeyboard.cpp +++ b/kernel/src/Drivers/USB/HidKeyboard.cpp @@ -7,6 +7,7 @@ #include "HidKeyboard.hpp" #include #include +#include #include using namespace Kt; @@ -171,13 +172,13 @@ namespace Drivers::USB::HidKeyboard { static uint8_t g_PrevKeys[6] = {}; static uint8_t g_PrevModifiers = 0; - // Typematic repeat state - static uint8_t g_RepeatKey = 0; // HID usage ID currently repeating - static uint16_t g_HoldCount = 0; // Reports since key was first held + // Typematic repeat state (timestamp-based, independent of report rate) + static uint8_t g_RepeatKey = 0; // HID usage ID currently repeating + static uint64_t g_RepeatStartMs = 0; // Timestamp when key was first held + static uint64_t g_LastRepeatMs = 0; // Timestamp of last repeat event - // Tuned for ~16ms report interval (SET_IDLE(4)) - constexpr uint16_t TYPEMATIC_DELAY = 31; // ~500ms before repeat starts - constexpr uint16_t TYPEMATIC_PERIOD = 2; // ~32ms between repeats (~31 chars/sec) + constexpr uint64_t TYPEMATIC_DELAY_MS = 500; // ms before repeat starts + constexpr uint64_t TYPEMATIC_PERIOD_MS = 32; // ms between repeats (~31 chars/sec) // ------------------------------------------------------------------------- // Helpers @@ -252,7 +253,8 @@ namespace Drivers::USB::HidKeyboard { for (int i = 0; i < 6; i++) g_PrevKeys[i] = 0; g_PrevModifiers = 0; g_RepeatKey = 0; - g_HoldCount = 0; + g_RepeatStartMs = 0; + g_LastRepeatMs = 0; KernelLogStream(OK, "USB/KB") << "Registered HID keyboard on slot " << (uint64_t)slotId; } @@ -264,6 +266,16 @@ namespace Drivers::USB::HidKeyboard { // data[1] is reserved const uint8_t* keys = &data[2]; + // ----------------------------------------------------------------- + // Detect ErrorRollOver (phantom keys) — ignore entire report + // When the keyboard can't reliably report all pressed keys, it + // fills key slots with 0x01. Updating state from this would + // falsely release all held keys, so we skip the report entirely. + // ----------------------------------------------------------------- + for (int i = 0; i < 6; i++) { + if (keys[i] == 0x01) return; + } + // ----------------------------------------------------------------- // Handle modifier changes // ----------------------------------------------------------------- @@ -291,6 +303,7 @@ namespace Drivers::USB::HidKeyboard { // ----------------------------------------------------------------- // Detect newly pressed keys (in current but not in previous) // ----------------------------------------------------------------- + uint64_t now = Timekeeping::GetMilliseconds(); bool newKeyPressed = false; for (int i = 0; i < 6; i++) { uint8_t key = keys[i]; @@ -301,7 +314,8 @@ namespace Drivers::USB::HidKeyboard { if (scancode != 0) { InjectKey(scancode, true, modifiers, IsNonCharKey(key)); g_RepeatKey = key; - g_HoldCount = 0; + g_RepeatStartMs = now; + g_LastRepeatMs = 0; newKeyPressed = true; } } @@ -321,21 +335,6 @@ namespace Drivers::USB::HidKeyboard { } if (key == g_RepeatKey) { g_RepeatKey = 0; - g_HoldCount = 0; - } - } - } - - // ----------------------------------------------------------------- - // Typematic repeat for held keys - // ----------------------------------------------------------------- - if (!newKeyPressed && g_RepeatKey != 0 && KeyInArray(g_RepeatKey, keys)) { - g_HoldCount++; - if (g_HoldCount >= TYPEMATIC_DELAY && - (g_HoldCount - TYPEMATIC_DELAY) % TYPEMATIC_PERIOD == 0) { - uint8_t scancode = g_HidToScancode[g_RepeatKey]; - if (scancode != 0) { - InjectKey(scancode, true, modifiers, IsNonCharKey(g_RepeatKey)); } } } @@ -347,4 +346,27 @@ namespace Drivers::USB::HidKeyboard { g_PrevModifiers = modifiers; } + // ------------------------------------------------------------------------- + // Tick — called from periodic timer (1ms) to drive typematic repeat + // With SET_IDLE(0) the keyboard only reports on changes, so we need + // an external clock to fire repeats while a key is held unchanged. + // ------------------------------------------------------------------------- + + void Tick() { + if (g_RepeatKey == 0) return; + + uint64_t now = Timekeeping::GetMilliseconds(); + uint64_t held = now - g_RepeatStartMs; + if (held < TYPEMATIC_DELAY_MS) return; + + if (g_LastRepeatMs == 0 || + (now - g_LastRepeatMs) >= TYPEMATIC_PERIOD_MS) { + uint8_t scancode = g_HidToScancode[g_RepeatKey]; + if (scancode != 0) { + InjectKey(scancode, true, g_PrevModifiers, IsNonCharKey(g_RepeatKey)); + } + g_LastRepeatMs = now; + } + } + } diff --git a/kernel/src/Drivers/USB/HidKeyboard.hpp b/kernel/src/Drivers/USB/HidKeyboard.hpp index 0f3fe10..1159b94 100644 --- a/kernel/src/Drivers/USB/HidKeyboard.hpp +++ b/kernel/src/Drivers/USB/HidKeyboard.hpp @@ -15,4 +15,7 @@ namespace Drivers::USB::HidKeyboard { // Process an 8-byte boot protocol keyboard report void ProcessReport(const uint8_t* data, uint16_t length); + // Timer tick for typematic repeat (call from periodic timer, e.g. 1ms) + void Tick(); + }; diff --git a/kernel/src/Drivers/USB/UsbDevice.cpp b/kernel/src/Drivers/USB/UsbDevice.cpp index 03577c4..f8fb39b 100644 --- a/kernel/src/Drivers/USB/UsbDevice.cpp +++ b/kernel/src/Drivers/USB/UsbDevice.cpp @@ -501,14 +501,14 @@ namespace Drivers::USB::UsbDevice { } // ----------------------------------------------------------------- - // Step 11: SET_IDLE(4) -- 16ms idle rate for software typematic + // Step 11: SET_IDLE(0) -- only report on changes (no idle reports) // ----------------------------------------------------------------- if (foundEp && dev->InterfaceProtocol == PROTOCOL_KEYBOARD) { - // wValue upper byte = duration in 4ms units, lower byte = report ID + // wValue upper byte = duration (0 = indefinite), lower byte = report ID cc = Xhci::ControlTransfer(slotId, REQTYPE_CLASS_IFACE, REQ_SET_IDLE, - (4 << 8), 0, 0, nullptr, false); + (0 << 8), 0, 0, nullptr, false); if (cc != Xhci::CC_SUCCESS) { - KernelLogStream(WARNING, "USB") << "SET_IDLE(4) failed, cc=" << (uint64_t)cc; + KernelLogStream(WARNING, "USB") << "SET_IDLE(0) failed, cc=" << (uint64_t)cc; // Non-fatal: not all devices support SET_IDLE } } diff --git a/kernel/src/Drivers/USB/Xhci.cpp b/kernel/src/Drivers/USB/Xhci.cpp index c87891f..440091e 100644 --- a/kernel/src/Drivers/USB/Xhci.cpp +++ b/kernel/src/Drivers/USB/Xhci.cpp @@ -216,9 +216,9 @@ namespace Drivers::USB::Xhci { Pci::LegacyWrite16(bus, dev, func, cap + 2, msgCtrl); // Disable legacy INTx in PCI command register - uint16_t pciCmd = Pci::LegacyRead16(bus, dev, func, PCI_REG_COMMAND); - pciCmd |= PCI_CMD_INTX_DISABLE; - Pci::LegacyWrite16(bus, dev, func, PCI_REG_COMMAND, pciCmd); + uint16_t pciCmd = Pci::LegacyRead16(bus, dev, func, (uint8_t)Pci::PCI_REG_COMMAND); + pciCmd |= Pci::PCI_CMD_INTX_DISABLE; + Pci::LegacyWrite16(bus, dev, func, (uint8_t)Pci::PCI_REG_COMMAND, pciCmd); // Register the interrupt handler for MSI vector Hal::RegisterIrqHandler(MSI_IRQ, HandleInterrupt); @@ -299,15 +299,17 @@ namespace Drivers::USB::Xhci { HidMouse::ProcessReport(g_interruptDataBuf[slotId], len); } } - - // Only re-queue on success — re-queuing on error would - // create an infinite loop of failed transfers. - QueueInterruptTransfer(slotId); } else { KernelLogStream(WARNING, "xHCI") << "Transfer error on slot " << base::dec << (uint64_t)slotId << " ep " << (uint64_t)epDci << " cc=" << (uint64_t)completionCode; } + + // Always re-queue the next interrupt transfer so the + // device can continue delivering reports. Transient + // errors (stall, babble, etc.) must not permanently + // kill the transfer chain. + QueueInterruptTransfer(slotId); } break; } @@ -645,7 +647,221 @@ namespace Drivers::USB::Xhci { } // ------------------------------------------------------------------------- - // Initialize + // Probe (called by PCI driver matching framework) + // ------------------------------------------------------------------------- + + bool Probe(const Pci::PciDevice& dev) { + if (g_initialized) return false; + + KernelLogStream(OK, "xHCI") << "Found controller at PCI " + << base::hex << (uint64_t)dev.Bus << ":" + << (uint64_t)dev.Device << "." << (uint64_t)dev.Function; + + // Read BAR0 and map MMIO region + uint64_t mmioPhys = Pci::ReadBar0(dev.Bus, dev.Device, dev.Function); + + KernelLogStream(INFO, "xHCI") << "BAR0 physical: " << base::hex << mmioPhys; + + constexpr uint64_t MmioSize = 0x10000; + for (uint64_t offset = 0; offset < MmioSize; offset += 0x1000) { + Memory::VMM::g_paging->MapMMIO(mmioPhys + offset, Memory::HHDM(mmioPhys + offset)); + } + + g_mmioBase = (volatile uint8_t*)Memory::HHDM(mmioPhys); + + Pci::EnableBusMaster(dev.Bus, dev.Device, dev.Function); + KernelLogStream(OK, "xHCI") << "Bus mastering enabled"; + + // Parse capability registers + g_capLength = *(volatile uint8_t*)(g_mmioBase + CAP_CAPLENGTH); + + uint32_t hciVersion = *(volatile uint16_t*)(g_mmioBase + CAP_HCIVERSION); + KernelLogStream(INFO, "xHCI") << "Version: " << base::hex << (uint64_t)hciVersion + << ", CapLength: " << (uint64_t)g_capLength; + + uint32_t hcsParams1 = ReadCap(CAP_HCSPARAMS1); + g_maxSlots = hcsParams1 & 0xFF; + g_maxPorts = (hcsParams1 >> 24) & 0xFF; + + uint32_t hcsParams2 = ReadCap(CAP_HCSPARAMS2); + uint32_t scratchpadBufsHi = (hcsParams2 >> 21) & 0x1F; + uint32_t scratchpadBufsLo = (hcsParams2 >> 27) & 0x1F; + uint32_t maxScratchpadBufs = (scratchpadBufsHi << 5) | scratchpadBufsLo; + + uint32_t dbOff = ReadCap(CAP_DBOFF) & ~0x3u; + uint32_t rtsOff = ReadCap(CAP_RTSOFF) & ~0x1Fu; + + g_opBase = g_mmioBase + g_capLength; + g_rtBase = g_mmioBase + rtsOff; + g_dbBase = g_mmioBase + dbOff; + + KernelLogStream(INFO, "xHCI") << "MaxSlots: " << base::dec << (uint64_t)g_maxSlots + << ", MaxPorts: " << (uint64_t)g_maxPorts + << ", ScratchpadBufs: " << (uint64_t)maxScratchpadBufs; + + if (g_maxSlots > MAX_SLOTS) g_maxSlots = MAX_SLOTS; + if (g_maxPorts > MAX_PORTS) g_maxPorts = MAX_PORTS; + + // Halt controller + uint32_t usbcmd = ReadOp(OP_USBCMD); + usbcmd &= ~USBCMD_RS; + WriteOp(OP_USBCMD, usbcmd); + + for (uint32_t i = 0; i < 100000; i++) { + if (ReadOp(OP_USBSTS) & USBSTS_HCH) break; + for (int j = 0; j < 10; j++) asm volatile("" ::: "memory"); + } + + if (!(ReadOp(OP_USBSTS) & USBSTS_HCH)) { + KernelLogStream(WARNING, "xHCI") << "Controller failed to halt"; + } + KernelLogStream(OK, "xHCI") << "Controller halted"; + + // Reset controller + WriteOp(OP_USBCMD, USBCMD_HCRST); + for (uint32_t i = 0; i < 100000; i++) { + if (!(ReadOp(OP_USBCMD) & USBCMD_HCRST)) break; + for (int j = 0; j < 10; j++) asm volatile("" ::: "memory"); + } + for (uint32_t i = 0; i < 100000; i++) { + if (!(ReadOp(OP_USBSTS) & USBSTS_CNR)) break; + for (int j = 0; j < 10; j++) asm volatile("" ::: "memory"); + } + if (ReadOp(OP_USBSTS) & USBSTS_CNR) { + KernelLogStream(WARNING, "xHCI") << "Controller not ready after reset"; + } + KernelLogStream(OK, "xHCI") << "Controller reset complete"; + + // Program CONFIG + WriteOp(OP_CONFIG, g_maxSlots); + + // Allocate DCBAA + g_dcbaa = (uint64_t*)AllocateDmaBuffer(g_dcbaaPhys); + WriteOp(OP_DCBAAP, (uint32_t)(g_dcbaaPhys & 0xFFFFFFFF)); + WriteOp(OP_DCBAAP + 4, (uint32_t)(g_dcbaaPhys >> 32)); + KernelLogStream(OK, "xHCI") << "DCBAA at phys " << base::hex << g_dcbaaPhys; + + // Scratchpad buffers + if (maxScratchpadBufs > 0) { + uint64_t spArrayPhys; + g_scratchpadBufs = (uint64_t*)AllocateDmaBuffer(spArrayPhys); + for (uint32_t i = 0; i < maxScratchpadBufs; i++) { + uint64_t bufPhys; + AllocateDmaBuffer(bufPhys); + g_scratchpadBufs[i] = bufPhys; + } + g_dcbaa[0] = spArrayPhys; + KernelLogStream(OK, "xHCI") << "Allocated " << base::dec << (uint64_t)maxScratchpadBufs << " scratchpad buffers"; + } + + // Command ring + g_cmdRing = (TRB*)AllocateDmaBuffer(g_cmdRingPhys); + TRB& linkTrb = g_cmdRing[CMD_RING_SIZE - 1]; + linkTrb.Parameter0 = (uint32_t)(g_cmdRingPhys & 0xFFFFFFFF); + linkTrb.Parameter1 = (uint32_t)(g_cmdRingPhys >> 32); + linkTrb.Status = 0; + linkTrb.Control = (TRB_LINK << TRB_TYPE_SHIFT) | TRB_ENT; + uint64_t crcr = g_cmdRingPhys | TRB_CYCLE_BIT; + WriteOp(OP_CRCR, (uint32_t)(crcr & 0xFFFFFFFF)); + WriteOp(OP_CRCR + 4, (uint32_t)(crcr >> 32)); + g_cmdRingCCS = true; + g_cmdRingEnqueue = 0; + KernelLogStream(OK, "xHCI") << "Command ring at phys " << base::hex << g_cmdRingPhys; + + // Event ring + ERST + g_evtRing = (TRB*)AllocateDmaBuffer(g_evtRingPhys); + g_erst = (ERSTEntry*)AllocateDmaBuffer(g_erstPhys); + g_erst[0].RingSegmentBase = g_evtRingPhys; + g_erst[0].RingSegmentSize = EVT_RING_SIZE; + g_erst[0].Reserved = 0; + WriteRt(IR0_ERSTSZ, 1); + WriteRt(IR0_ERDP, (uint32_t)(g_evtRingPhys & 0xFFFFFFFF)); + WriteRt(IR0_ERDP + 4, (uint32_t)(g_evtRingPhys >> 32)); + WriteRt(IR0_ERSTBA, (uint32_t)(g_erstPhys & 0xFFFFFFFF)); + WriteRt(IR0_ERSTBA + 4, (uint32_t)(g_erstPhys >> 32)); + g_evtRingCCS = true; + g_evtRingDequeue = 0; + KernelLogStream(OK, "xHCI") << "Event ring at phys " << base::hex << g_evtRingPhys; + + // MSI setup + if (!SetupMsi(dev.Bus, dev.Device, dev.Function)) { + KernelLogStream(WARNING, "xHCI") << "MSI not available, using poll mode"; + } + + // Enable interrupter 0 + WriteRt(IR0_IMAN, IMAN_IE); + WriteRt(IR0_IMOD, 0); + + // Start controller + WriteOp(OP_USBCMD, USBCMD_RS | USBCMD_INTE | USBCMD_HSEE); + for (uint32_t i = 0; i < 100000; i++) { + if (!(ReadOp(OP_USBSTS) & USBSTS_HCH)) break; + for (int j = 0; j < 10; j++) asm volatile("" ::: "memory"); + } + KernelLogStream(OK, "xHCI") << "Controller started"; + g_initialized = true; + + // Power on all ports + for (uint32_t port = 0; port < g_maxPorts; port++) { + uint32_t portsc = ReadOp(OP_PORTSC_BASE + port * OP_PORTSC_STRIDE); + if (!(portsc & PORTSC_PP)) { + WriteOp(OP_PORTSC_BASE + port * OP_PORTSC_STRIDE, PORTSC_PP); + } + } + BusyWaitMs(20); + KernelLogStream(OK, "xHCI") << "All ports powered"; + + // Port scanning + for (uint32_t port = 0; port < g_maxPorts; port++) { + uint32_t portsc = ReadOp(OP_PORTSC_BASE + port * OP_PORTSC_STRIDE); + if (!(portsc & PORTSC_CCS)) continue; + + KernelLogStream(INFO, "xHCI") << "Port " << base::dec << (uint64_t)(port + 1) + << ": device connected, PORTSC=" << base::hex << (uint64_t)portsc; + + WriteOp(OP_PORTSC_BASE + port * OP_PORTSC_STRIDE, + (portsc & PORTSC_PRESERVE) | PORTSC_PR | PORTSC_CHANGE_BITS); + + bool resetDone = false; + for (uint32_t i = 0; i < 100000; i++) { + PollEvents(); + uint32_t ps = ReadOp(OP_PORTSC_BASE + port * OP_PORTSC_STRIDE); + if (ps & PORTSC_PRC) { resetDone = true; break; } + for (int j = 0; j < 100; j++) asm volatile("" ::: "memory"); + } + + if (!resetDone) { + KernelLogStream(WARNING, "xHCI") << "Port " << base::dec << (uint64_t)(port + 1) << " reset timeout"; + continue; + } + + portsc = ReadOp(OP_PORTSC_BASE + port * OP_PORTSC_STRIDE); + uint32_t speed = (portsc >> 10) & 0xF; + WriteOp(OP_PORTSC_BASE + port * OP_PORTSC_STRIDE, + (portsc & PORTSC_PRESERVE) | PORTSC_CHANGE_BITS); + + const char* speedStr = "Unknown"; + switch (speed) { + case SPEED_FULL: speedStr = "Full (12 Mbps)"; break; + case SPEED_LOW: speedStr = "Low (1.5 Mbps)"; break; + case SPEED_HIGH: speedStr = "High (480 Mbps)"; break; + case SPEED_SUPER: speedStr = "Super (5 Gbps)"; break; + } + + KernelLogStream(OK, "xHCI") << "Port " << base::dec << (uint64_t)(port + 1) + << ": reset complete, speed=" << speedStr; + + BusyWaitMs(10); + UsbDevice::EnumerateDevice(port + 1, speed); + } + + g_bootScanComplete = true; + KernelLogStream(OK, "xHCI") << "Initialization complete"; + return true; + } + + // ------------------------------------------------------------------------- + // Initialize (standalone fallback path) // ------------------------------------------------------------------------- void Initialize() { @@ -678,14 +894,7 @@ namespace Drivers::USB::Xhci { // ----------------------------------------------------------------- // Step 2: Read BAR0 and map MMIO region // ----------------------------------------------------------------- - uint32_t bar0 = Pci::LegacyRead32(foundDev->Bus, foundDev->Device, foundDev->Function, PCI_REG_BAR0); - uint64_t mmioPhys = bar0 & 0xFFFFFFF0; - - // Check if 64-bit BAR (type field bits 2:1 == 0b10) - if ((bar0 & 0x06) == 0x04) { - uint32_t bar1 = Pci::LegacyRead32(foundDev->Bus, foundDev->Device, foundDev->Function, PCI_REG_BAR1); - mmioPhys |= ((uint64_t)bar1 << 32); - } + uint64_t mmioPhys = Pci::ReadBar0(foundDev->Bus, foundDev->Device, foundDev->Function); KernelLogStream(INFO, "xHCI") << "BAR0 physical: " << base::hex << mmioPhys; @@ -700,9 +909,7 @@ namespace Drivers::USB::Xhci { // ----------------------------------------------------------------- // Step 3: Enable PCI bus master and memory space // ----------------------------------------------------------------- - uint16_t pciCmd = Pci::LegacyRead16(foundDev->Bus, foundDev->Device, foundDev->Function, PCI_REG_COMMAND); - pciCmd |= PCI_CMD_BUS_MASTER | PCI_CMD_MEM_SPACE; - Pci::LegacyWrite16(foundDev->Bus, foundDev->Device, foundDev->Function, PCI_REG_COMMAND, pciCmd); + Pci::EnableBusMaster(foundDev->Bus, foundDev->Device, foundDev->Function); KernelLogStream(OK, "xHCI") << "Bus mastering enabled"; diff --git a/kernel/src/Drivers/USB/Xhci.hpp b/kernel/src/Drivers/USB/Xhci.hpp index f977d0a..2319f07 100644 --- a/kernel/src/Drivers/USB/Xhci.hpp +++ b/kernel/src/Drivers/USB/Xhci.hpp @@ -6,6 +6,7 @@ #pragma once #include +#include namespace Drivers::USB::Xhci { @@ -29,16 +30,6 @@ namespace Drivers::USB::Xhci { constexpr uint8_t PCI_SUBCLASS_USB = 0x03; constexpr uint8_t PCI_PROGIF_XHCI = 0x30; - // PCI config space offsets - constexpr uint8_t PCI_REG_BAR0 = 0x10; - constexpr uint8_t PCI_REG_BAR1 = 0x14; - constexpr uint8_t PCI_REG_COMMAND = 0x04; - - // PCI command register bits - constexpr uint16_t PCI_CMD_BUS_MASTER = (1 << 2); - constexpr uint16_t PCI_CMD_MEM_SPACE = (1 << 1); - constexpr uint16_t PCI_CMD_INTX_DISABLE = (1 << 10); - // --------------------------------------------------------------------------- // xHCI Capability Register offsets (from BAR0) // --------------------------------------------------------------------------- @@ -272,6 +263,7 @@ namespace Drivers::USB::Xhci { // --------------------------------------------------------------------------- void Initialize(); + bool Probe(const Pci::PciDevice& dev); bool IsInitialized(); // Deferred hot-plug processing (call from timer tick, not interrupt context) diff --git a/kernel/src/Main.cpp b/kernel/src/Main.cpp index e1f9bf0..fcf8d02 100644 --- a/kernel/src/Main.cpp +++ b/kernel/src/Main.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -141,6 +140,7 @@ extern "C" void kmain() { Pci::Initialize(g_acpi.GetXSDT()); + Drivers::ProbeEarly(); Drivers::InitializeGraphics(); Timekeeping::ApicTimerInitialize(); @@ -149,8 +149,7 @@ extern "C" void kmain() { Drivers::PS2::Keyboard::Initialize(); Drivers::PS2::Mouse::Initialize(); - Drivers::USB::Xhci::Initialize(); - + Drivers::ProbeNormal(); Drivers::InitializeNetwork(); } #endif diff --git a/kernel/src/Pci/Pci.cpp b/kernel/src/Pci/Pci.cpp index 63f683d..625b80d 100644 --- a/kernel/src/Pci/Pci.cpp +++ b/kernel/src/Pci/Pci.cpp @@ -398,6 +398,86 @@ namespace Pci { return g_devices; } + // ------------------------------------------------------------------------- + // Shared PCI utilities + // ------------------------------------------------------------------------- + + uint64_t ReadBar0(uint8_t bus, uint8_t device, uint8_t function) { + uint32_t bar0Low = LegacyRead32(bus, device, function, (uint8_t)PCI_REG_BAR0); + uint64_t addr = bar0Low & 0xFFFFFFF0u; + + // Check for 64-bit BAR (type field bits 2:1 == 0b10) + if ((bar0Low & 0x06) == 0x04) { + uint32_t bar0High = LegacyRead32(bus, device, function, (uint8_t)(PCI_REG_BAR0 + 4)); + addr |= ((uint64_t)bar0High << 32); + } + + return addr; + } + + void EnableBusMaster(uint8_t bus, uint8_t device, uint8_t function) { + uint16_t cmd = LegacyRead16(bus, device, function, (uint8_t)PCI_REG_COMMAND); + cmd |= PCI_CMD_MEM_SPACE | PCI_CMD_BUS_MASTER; + LegacyWrite16(bus, device, function, (uint8_t)PCI_REG_COMMAND, cmd); + } + + // ------------------------------------------------------------------------- + // PCI driver matching + // ------------------------------------------------------------------------- + + static bool MatchDriver(const PciDriverDesc& drv, const PciDevice& dev) { + if (drv.VendorId != 0 && drv.VendorId != dev.VendorId) + return false; + if (drv.ClassCode != 0xFF && drv.ClassCode != dev.ClassCode) + return false; + if (drv.SubClass != 0xFF && drv.SubClass != dev.SubClass) + return false; + if (drv.ProgIf != 0xFF && drv.ProgIf != dev.ProgIf) + return false; + + if (drv.DeviceIdCount > 0 && drv.DeviceIds != nullptr) { + bool found = false; + for (uint16_t i = 0; i < drv.DeviceIdCount; i++) { + if (drv.DeviceIds[i] == dev.DeviceId) { + found = true; + break; + } + } + if (!found) return false; + } + + return true; + } + + void ProbeAll(const PciDriverDesc* table, uint16_t count, ProbePhase phase) { + for (uint64_t d = 0; d < g_devices.size(); d++) { + const PciDevice& dev = g_devices[d]; + + for (uint16_t i = 0; i < count; i++) { + if (table[i].Phase != phase) + continue; + if (!MatchDriver(table[i], dev)) + continue; + + KernelLogStream(INFO, "PCI") << "Probing " << table[i].Name + << " for device " << base::hex + << (uint64_t)dev.Bus << ":" << (uint64_t)dev.Device + << "." << (uint64_t)dev.Function; + + if (table[i].Probe(dev)) { + KernelLogStream(OK, "PCI") << table[i].Name << " claimed device " + << base::hex << (uint64_t)dev.Bus << ":" + << (uint64_t)dev.Device << "." << (uint64_t)dev.Function; + break; // device claimed, move to next device + } + } + } + } + + // ------------------------------------------------------------------------- + // Public API + // ------------------------------------------------------------------------- + void Initialize(Hal::ACPI::CommonSDTHeader* xsdt) { KernelLogStream(INFO, "PCI") << "Initializing PCI subsystem"; diff --git a/kernel/src/Pci/Pci.hpp b/kernel/src/Pci/Pci.hpp index 93a317e..aa64915 100644 --- a/kernel/src/Pci/Pci.hpp +++ b/kernel/src/Pci/Pci.hpp @@ -76,4 +76,47 @@ namespace Pci { // Initialize PCI subsystem: parse MCFG, enumerate devices // xsdt: pointer to the XSDT (already HHDM-mapped) void Initialize(Hal::ACPI::CommonSDTHeader* xsdt); + + // ========================================================================= + // Shared PCI constants + // ========================================================================= + + constexpr uint16_t PCI_REG_COMMAND = 0x04; + constexpr uint16_t PCI_REG_BAR0 = 0x10; + constexpr uint16_t PCI_REG_BAR1 = 0x14; + constexpr uint16_t PCI_REG_INTERRUPT = 0x3C; + + constexpr uint16_t PCI_CMD_MEM_SPACE = (1 << 1); + constexpr uint16_t PCI_CMD_BUS_MASTER = (1 << 2); + constexpr uint16_t PCI_CMD_INTX_DISABLE = (1 << 10); + + // Read BAR0, handling 32/64-bit BARs. Returns physical base address. + uint64_t ReadBar0(uint8_t bus, uint8_t device, uint8_t function); + + // Enable memory space access and bus mastering in PCI command register. + void EnableBusMaster(uint8_t bus, uint8_t device, uint8_t function); + + // ========================================================================= + // PCI driver matching + // ========================================================================= + + enum class ProbePhase : uint8_t { Early = 0, Normal = 1 }; + + using PciProbeFunc = bool(*)(const PciDevice& dev); + + struct PciDriverDesc { + const char* Name; + uint16_t VendorId; // 0 = any + uint8_t ClassCode; // 0xFF = any + uint8_t SubClass; // 0xFF = any + uint8_t ProgIf; // 0xFF = any + const uint16_t* DeviceIds; // optional device ID whitelist + uint16_t DeviceIdCount; // 0 = skip device ID check + ProbePhase Phase; + PciProbeFunc Probe; + }; + + // Probe all PCI devices against a driver table for a given phase. + // First driver whose Probe() returns true claims each device. + void ProbeAll(const PciDriverDesc* table, uint16_t count, ProbePhase phase); }; diff --git a/kernel/src/Timekeeping/ApicTimer.cpp b/kernel/src/Timekeeping/ApicTimer.cpp index 70e7350..b09ac45 100644 --- a/kernel/src/Timekeeping/ApicTimer.cpp +++ b/kernel/src/Timekeeping/ApicTimer.cpp @@ -13,6 +13,7 @@ #include #include #include +#include using namespace Kt; @@ -47,6 +48,7 @@ namespace Timekeeping { // (equivalent to a real NIC IRQ handler, runs with interrupts disabled) Drivers::Net::E1000E::Poll(); Drivers::USB::Xhci::ProcessDeferredWork(); + Drivers::USB::HidKeyboard::Tick(); if (g_schedEnabled) { Sched::Tick();