feat: graphics/net PnP, keyboard driver improvements

This commit is contained in:
2026-03-04 21:10:34 +01:00
parent ba7b7dca87
commit fcb6f8e247
18 changed files with 836 additions and 244 deletions
+1 -1
View File
@@ -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];
+91 -12
View File
@@ -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...";
+5 -7
View File
@@ -7,6 +7,7 @@
#pragma once
#include <cstdint>
#include <Pci/Pci.hpp>
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();
+110 -6
View File
@@ -5,17 +5,122 @@
*/
#include "Init.hpp"
#include <Pci/Pci.hpp>
#include <Drivers/Graphics/IntelGPU.hpp>
#include <Drivers/Net/E1000.hpp>
#include <Drivers/Net/E1000E.hpp>
#include <Drivers/USB/Xhci.hpp>
#include <Graphics/Cursor.hpp>
#include <Net/Net.hpp>
#include <Terminal/Terminal.hpp>
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 InitializeNetwork() {
Net::E1000::Initialize();
if (!Net::E1000::IsInitialized()) {
Kt::KernelLogStream(Kt::INFO, "Init") << "E1000 not found, trying E1000E...";
Net::E1000E::Initialize();
void ProbeNormal() {
Pci::ProbeAll(g_driverTable, g_driverTableCount, Pci::ProbePhase::Normal);
}
void InitializeNetwork() {
::Net::Initialize();
}
+8 -2
View File
@@ -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();
}
+79 -16
View File
@@ -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
+4
View File
@@ -6,6 +6,7 @@
#pragma once
#include <cstdint>
#include <Pci/Pci.hpp>
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);
+127 -141
View File
@@ -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);
}
// -------------------------------------------------------------------------
+4
View File
@@ -6,6 +6,7 @@
#pragma once
#include <cstdint>
#include <Pci/Pci.hpp>
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);
+44 -22
View File
@@ -7,6 +7,7 @@
#include "HidKeyboard.hpp"
#include <Drivers/PS2/Keyboard.hpp>
#include <Terminal/Terminal.hpp>
#include <Timekeeping/ApicTimer.hpp>
#include <CppLib/Stream.hpp>
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
// Typematic repeat state (timestamp-based, independent of report rate)
static uint8_t g_RepeatKey = 0; // HID usage ID currently repeating
static uint16_t g_HoldCount = 0; // Reports since key was first held
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;
}
}
}
+3
View File
@@ -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();
};
+4 -4
View File
@@ -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
}
}
+226 -19
View File
@@ -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";
+2 -10
View File
@@ -6,6 +6,7 @@
#pragma once
#include <cstdint>
#include <Pci/Pci.hpp>
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)
+2 -3
View File
@@ -26,7 +26,6 @@
#include <Drivers/PS2/PS2Controller.hpp>
#include <Drivers/PS2/Keyboard.hpp>
#include <Drivers/PS2/Mouse.hpp>
#include <Drivers/USB/Xhci.hpp>
#include <Drivers/Init.hpp>
#include <Graphics/Cursor.hpp>
#include <Hal/MSR.hpp>
@@ -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
+80
View File
@@ -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";
+43
View File
@@ -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);
};
+2
View File
@@ -13,6 +13,7 @@
#include <Sched/Scheduler.hpp>
#include <Drivers/Net/E1000E.hpp>
#include <Drivers/USB/Xhci.hpp>
#include <Drivers/USB/HidKeyboard.hpp>
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();