388 lines
15 KiB
C++
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().
|
|
}
|
|
|
|
}
|