From 416aa52b09a48fbcb995950ddb791dd14253ff02 Mon Sep 17 00:00:00 2001 From: Nexoniarz Date: Thu, 22 Oct 2026 23:47:17 +0200 Subject: [PATCH] acpi, vga: say what the first real boot actually found "acpi: 0 PCI interrupts routed through _PRT" on an AMD board meant that every PCIe bridge's devices are wired to the second I/O APIC, which is not driven. It says so now, and that those devices need MSI. "vga: framebuffer disagreement" on an NVIDIA card compared the GOP's framebuffer against BAR0, a 16 MB register block big enough to pass for a screen. The BAR that contains the framebuffer is preferred now. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4 --- kernel/acpi/aml.c | 10 ++++++++-- kernel/drivers/gpu/vga/vga_pci.c | 14 ++++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/kernel/acpi/aml.c b/kernel/acpi/aml.c index 39be089..5306a53 100644 --- a/kernel/acpi/aml.c +++ b/kernel/acpi/aml.c @@ -284,7 +284,7 @@ void acpi_pci_route(void) { uacpi_pci_routing_table *roottab; - int i, routed = 0; + int i, routed = 0, beyond = 0; if (!ready) return; @@ -316,8 +316,11 @@ acpi_pci_route(void) if (gsi < 0) continue; if (gsi >= NIRQ_IOAPIC) { - // A second I/O APIC's input. Not reachable here; MSI still is. + // A second I/O APIC's input. Not reachable here; MSI still is. AMD + // boards wire every PCIe bridge's devices to the GNB's I/O APIC, so + // on those this is most of the machine. d->irq = 0xFF; + beyond++; continue; } if (gsi != d->irq) @@ -331,4 +334,7 @@ acpi_pci_route(void) uacpi_free_pci_routing_table(roottab); kinfo("acpi: %d PCI interrupt%s routed through _PRT\n", routed, routed == 1 ? "" : "s"); + if (beyond) + kinfo("acpi: %d more on a second I/O APIC, which is not driven; " + "those devices need MSI\n", beyond); } diff --git a/kernel/drivers/gpu/vga/vga_pci.c b/kernel/drivers/gpu/vga/vga_pci.c index 2c11d61..21fafe7 100644 --- a/kernel/drivers/gpu/vga/vga_pci.c +++ b/kernel/drivers/gpu/vga/vga_pci.c @@ -32,11 +32,21 @@ // Display controllers commonly expose two apertures -- the framebuffer and a // much smaller register block -- and which index they land on varies, so the // choice is made by size rather than by position. +// +// Except that the one the loader's mode actually lives in wins outright. An +// NVIDIA card's BAR0 is a 16 MB register block, which is big enough to pass +// for an 800x600 framebuffer and is not one; the screen is in BAR1. Taking +// the first that fits reported a "disagreement" that was the driver's own. static struct pci_bar * framebuffer_bar(struct pci_dev *d) { - uint64 need = vga_fb_size(); + uint64 need = vga_fb_size(), vbe = vga_fb_phys(); + for (int i = 0; i < 6; i++) { + struct pci_bar *b = &d->bar[i]; + if (b->base && !b->is_io && vbe >= b->base && vbe < b->base + b->size) + return b; + } for (int i = 0; i < 6; i++) { struct pci_bar *b = &d->bar[i]; if (b->base == 0 || b->is_io) @@ -64,7 +74,7 @@ vga_pci_probe(struct pci_dev *d) return 0; // still ours; nothing more to do } - if (vbe != 0 && bar->base != vbe) { + if (vbe != 0 && (vbe < bar->base || vbe >= bar->base + bar->size)) { // Trust VBE: it describes the mode that is actually programmed and being // displayed, which is the thing vga.c is writing into. The BAR is the // card's whole aperture and the mode may sit at an offset within it. Say