Files
MontaukOS/kernel/src/Drivers/Init.cpp
T

388 lines
15 KiB
C++

/*
* Init.cpp
* Driver initialization orchestration
* Copyright (c) 2025 Daniel Hammer
*/
#include "Init.hpp"
#include <Pci/Pci.hpp>
#include <Drivers/Graphics/IntelGPU.hpp>
#include <Drivers/Graphics/DgpuPower.hpp>
#include <Drivers/Net/E1000.hpp>
#include <Drivers/Net/E1000E.hpp>
#include <Drivers/Net/Wifi/Wifi.hpp>
#include <Drivers/USB/Xhci.hpp>
#include <Drivers/Storage/Ahci.hpp>
#include <Drivers/Storage/Nvme.hpp>
#include <Drivers/Storage/Sdhci.hpp>
#include <Drivers/Storage/Gpt.hpp>
#include <Drivers/Audio/IntelHda.hpp>
#include <Drivers/Bluetooth/Bluetooth.hpp>
#include <Graphics/Framebuffer.hpp>
#include <Net/Net.hpp>
#include <Terminal/Terminal.hpp>
#include <ACPI/AcpiDevices.hpp>
#include <Boot/Boot.hpp>
#include <ACPI/AML/AmlInterpreter.hpp>
#include <ACPI/AML/AmlResource.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,
};
// 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().
}
}