/* * Init.cpp * Driver initialization orchestration * Copyright (c) 2025 Daniel Hammer */ #include "Init.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #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, }; // Intel AX210-family Wi-Fi (CNVi and discrete). The driver additionally // checks CSR_HW_RF_ID at probe time and only claims RF type GF (AX211), // which is the firmware image shipped on the ramdisk. static constexpr uint16_t g_intelWifiIds[] = { 0x2725, // AX210 (discrete) 0x2726, // AX211 (discrete, GF2) 0x51f0, 0x51f1, 0x54f0, // AlderLake CNVi 0x7a70, 0x7af0, // RaptorLake / AlderLake-P CNVi 0x7e40, // MeteorLake CNVi 0x7f70, // RaptorLake-S CNVi }; // ------------------------------------------------------------------------- // 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); } static bool ProbeWifi(const Pci::PciDevice& dev) { return Net::Wifi::Probe(dev); } static bool ProbeAhci(const Pci::PciDevice& dev) { return Storage::Ahci::Probe(dev); } static bool ProbeNvme(const Pci::PciDevice& dev) { return Storage::Nvme::Probe(dev); } static bool ProbeSdhci(const Pci::PciDevice& dev) { return Storage::Sdhci::Probe(dev); } static bool ProbeIntelHda(const Pci::PciDevice& dev) { return Audio::IntelHda::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, }, // Order 5: Intel Wi-Fi — Normal phase, vendor=0x8086 + deviceIds list. // These are the AX210-family (CNVi "So") parts; the probe // rejects anything whose RF type is not GF, since only the // AX211 firmware image is bundled. { "IntelWiFi", 0x8086, 0xFF, 0xFF, 0xFF, g_intelWifiIds, sizeof(g_intelWifiIds) / sizeof(g_intelWifiIds[0]), Pci::ProbePhase::Normal, ProbeWifi, }, // Order 6: AHCI — Normal phase, match class=0x01/0x06/0x01 (SATA AHCI) { "AHCI", 0, // VendorId (any) 0x01, // ClassCode (Mass Storage) 0x06, // SubClass (SATA) 0x01, // ProgIf (AHCI 1.0) nullptr, 0, Pci::ProbePhase::Normal, ProbeAhci, }, // Order 7: NVMe — Normal phase, match class=0x01/0x08/0x02 (NVM Express) { "NVMe", 0, // VendorId (any) 0x01, // ClassCode (Mass Storage) 0x08, // SubClass (Non-Volatile Memory) 0x02, // ProgIf (NVM Express) nullptr, 0, Pci::ProbePhase::Normal, ProbeNvme, }, // Order 8: Intel HDA — Normal phase, match vendor=0x8086 + class=0x04 (Multimedia) // SubClass 0x03 = "Audio device" (plain HD Audio controller). // SubClass 0x01 = "Multimedia audio controller": on modern // laptops this is the SAME HDA controller enumerated with the // audio DSP (Smart Sound Technology) enabled, so it MUST be // matched — restricting to 0x03 made the speakers vanish with // no HDA logs at all. Legacy AC'97 (e.g. VirtualBox 8086:2415) // also reports 0x01, but it exposes an I/O-space BAR0: // ReadBar0 returns 0 for I/O BARs and Probe rejects it // cleanly, so matching it here is harmless. // SD host controller (standard interface) - class 0x08/0x05 { "SDHCI", 0, 0x08, // ClassCode (Base system peripheral) 0x05, // SubClass (SD host controller) 0xFF, nullptr, 0, Pci::ProbePhase::Normal, ProbeSdhci, }, { "IntelHDA", 0x8086, // VendorId (Intel) 0x04, // ClassCode (Multimedia) 0xFF, // SubClass (any — 0x03 HDA, 0x01 HDA-with-DSP; AC'97 rejected by BAR check) 0xFF, // ProgIf (any) nullptr, 0, Pci::ProbePhase::Normal, ProbeIntelHda, }, }; 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() { if (Graphics::IntelGPU::IsInitialized()) { ::Graphics::Framebuffer::Set( Graphics::IntelGPU::GetFramebufferBase(), Graphics::IntelGPU::GetWidth(), Graphics::IntelGPU::GetHeight(), Graphics::IntelGPU::GetPitch() ); } } void PowerDownUnusedGpus() { Graphics::DgpuPower::PowerDownUnused(); } // A device's _DMA (or its parent's) bounds the addresses it can reach. // DMA buffers come from anywhere in RAM, so RAM past the bound is a // configuration error: on the Raspberry Pi 4 the firmware's "Limit RAM // to 3 GB" setting must stay on. static void WarnIfRamBeyondDma(const char* devicePath) { char path[160]; int len = 0; while (devicePath[len] != '\0' && len < 120) { path[len] = devicePath[len]; len++; } Hal::AML::ResourceList dma; bool found = false; for (int attempt = 0; attempt < 2 && !found; attempt++) { const char suffix[] = "._DMA"; for (int i = 0; i < (int)sizeof(suffix); i++) path[len + i] = suffix[i]; Hal::AML::Object obj; if (Hal::AML::GetInterpreter().EvaluateObject(path, obj) && obj.Type == Hal::AML::ObjectType::Buffer && Hal::AML::ParseResourceTemplate(obj.Buffer.Data, obj.Buffer.Length, dma)) { found = true; break; } // Retry on the parent device. while (len > 0 && path[len - 1] != '.') len--; if (len == 0) return; len--; } uint64_t dmaBase = 0, dmaLength = 0; if (!found || !dma.GetMemory(dmaBase, dmaLength) || dmaLength == 0) return; uint64_t dmaEnd = dmaBase + dmaLength; const auto& map = montauk::boot::Info().memoryMap; for (uint64_t i = 0; i < map.count; i++) { const auto& r = map.regions[i]; if (r.kind == montauk::boot::MemoryKind::Usable && r.base + r.length > dmaEnd) { Kt::KernelLogStream(Kt::WARNING, "Drivers") << devicePath << " can only reach RAM below 0x" << base::hex << dmaEnd << ", but there is RAM up to 0x" << (r.base + r.length) << ": its DMA may fail (Raspberry Pi 4: keep 'Limit RAM to 3 GB' enabled)"; return; } } } // An xHCI controller ACPI describes as a platform device (PNP0D10) // instead of a PCI function: the Raspberry Pi 4/400's VL805 under UEFI // firmware in ACPI mode, where firmware has set up the non-ECAM PCIe // behind it. Its DMA is non-coherent unless _CCA says otherwise. static void ProbePlatformXhci() { if (USB::Xhci::IsInitialized()) return; Hal::AcpiDevices::DeviceInfo info{}; if (!Hal::AcpiDevices::FindDeviceByHid("PNP0D10", info) && !Hal::AcpiDevices::FindDeviceByHid("PNP0D15", info)) { return; } Hal::AML::ResourceList resources; uint64_t base = 0, length = 0; if (!Hal::AcpiDevices::EvaluateCrs(info.NodeIndex, resources) || !resources.GetMemory(base, length) || base == 0) { Kt::KernelLogStream(Kt::WARNING, "Drivers") << "xHCI " << info.Path << ": no register block in _CRS"; return; } uint32_t gsi = 0; bool level = true, activeLow = false; if (auto* irq = resources.FindFirst(Hal::AML::ResourceType::ExtendedIrq)) { gsi = irq->ExtendedIrq.Interrupt; level = (irq->ExtendedIrq.Flags & 1) == 0; activeLow = (irq->ExtendedIrq.Flags & 2) != 0; } bool coherent = false; Hal::AML::Object cca; if (Hal::AML::GetInterpreter().EvaluateChild(info.NodeIndex, "_CCA", nullptr, 0, cca) && cca.Type == Hal::AML::ObjectType::Integer) { coherent = cca.Integer != 0; } Kt::KernelLogStream(Kt::INFO, "Drivers") << "xHCI platform device " << info.Path << " at 0x" << base::hex << base << ", GSI " << base::dec << (uint64_t)gsi << (coherent ? ", coherent DMA" : ", non-coherent DMA"); WarnIfRamBeyondDma(info.Path); USB::Xhci::ProbePlatform(base, length, gsi, level, activeLow, coherent); } // SD host controllers ACPI describes directly: the Raspberry Pi 4's // EMMC2 (BRCME88C, the SD card slot) or a standard PNP0D40. static void ProbePlatformSdhci() { if (Storage::Sdhci::IsInitialized()) return; Hal::AcpiDevices::DeviceInfo info{}; if (!Hal::AcpiDevices::FindDeviceByHid("BRCME88C", info) && !Hal::AcpiDevices::FindDeviceByHid("PNP0D40", info)) { return; } Hal::AML::ResourceList resources; uint64_t base = 0, length = 0; if (!Hal::AcpiDevices::EvaluateCrs(info.NodeIndex, resources) || !resources.GetMemory(base, length) || base == 0) { return; } Storage::Sdhci::ProbePlatform(base, info.Path); } void ProbeNormal() { Pci::ProbeAll(g_driverTable, g_driverTableCount, Pci::ProbePhase::Normal); ProbePlatformXhci(); ProbePlatformSdhci(); Bluetooth::ProbePlatformAdapter(); } void InitializeNetwork() { ::Net::Initialize(); } void InitializeStorage() { // AHCI driver registered SATA devices as block devices during // ProbeNormal(). Now probe all block devices for GPT partitions. Storage::Gpt::ProbeAll(); } void ServiceStorageHotplug() { if (USB::Xhci::HasDeferredWork()) USB::Xhci::ProcessDeferredWork(); } void InitializeAudio() { // HDA driver initializes during ProbeNormal(). // Bluetooth audio (A2DP) initializes with its adapter: during USB // enumeration, or for an on-board UART controller (Raspberry Pi) from // the platform probe in ProbeNormal(). } }