Sixty-five headers sat side by side in include/, so `#include "irq.h"` said nothing about where irq.h was or what it belonged to, and errno.h, stat.h and fcntl.h at the top shadowed the system's for any host tool built with -I./include. They are in folders now -- arch/ core/ fs/ dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and every include names the folder, as Linux's do: "core/proc.h". Done mechanically and checked the hard way: the kernel built from this tree disassembles identically to the one built from the tree before it. The full image boots, and userland is the same set of programs. Paths in docs and comments follow; docs/guides/layout.md has a table of what is where, and tools/fix-includes.py rewrites old-style includes from the folders in the tree, for code written before this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
497 lines
15 KiB
C
497 lines
15 KiB
C
// Find the processors, the way firmware from this century describes them.
|
|
//
|
|
// mpinit() walks the Intel MP table, which is from 1994. Modern firmware fills
|
|
// it in minimally or omits it: measured 2026-08-08, QEMU/SeaBIOS with -smp 4
|
|
// publishes a 208-byte MP table containing exactly *one* processor entry, then
|
|
// buses and interrupt assignments. Everything since has expected the operating
|
|
// system to read the ACPI MADT instead.
|
|
//
|
|
// The consequence was not that SMP was slow, it was that ncpu was 1 and SMP was
|
|
// silently never exercised. Machines whose firmware does publish a full MP
|
|
// table -- VMware, older hardware -- took the other path, started their APs,
|
|
// and triple faulted. So this file is a prerequisite for debugging that: until
|
|
// the CPUs are visible under QEMU there is no way to reproduce the crash except
|
|
// on the hardware it happens on.
|
|
//
|
|
// Written from the ACPI specification. No table layout here is copied from
|
|
// another kernel; they are all in chapter 5 of the published standard.
|
|
|
|
#include "core/types.h"
|
|
#include "core/hwinfo.h"
|
|
#include "core/defs.h"
|
|
#include "arch/schboot.h" // SCHBOOT_RSDP_ADDR: the loader/kernel hand-off
|
|
#include "core/param.h"
|
|
#include "arch/memlayout.h"
|
|
#include "arch/mmu.h"
|
|
#include "core/proc.h"
|
|
#include "arch/x86.h"
|
|
#include "core/irq.h"
|
|
#include "dev/acpi.h"
|
|
|
|
extern struct cpu cpus[NCPU];
|
|
extern int ncpu;
|
|
extern uchar ioapicid;
|
|
extern uint64 ioapic_phys;
|
|
|
|
// The ACPI power-management timer: a 3.579545 MHz counter at an I/O port the
|
|
// FADT names. 0 when there is none. lapic.c times the TSC against it when the
|
|
// PIT does not count -- see calibrate_without_pit().
|
|
uint32 acpi_pmtmr_port;
|
|
int acpi_pmtmr_32; // 32 bits wide rather than 24
|
|
extern volatile uint *lapic;
|
|
|
|
// The Root System Description Pointer. Found by signature, not by being told
|
|
// where it is -- it sits either in the Extended BIOS Data Area or in the BIOS
|
|
// read-only region, on a 16-byte boundary.
|
|
struct acpi_rsdp {
|
|
char sig[8]; // "RSD PTR "
|
|
uint8 checksum; // over the first 20 bytes
|
|
char oemid[6];
|
|
uint8 revision; // 0 = ACPI 1.0 (RSDT), 2+ = 2.0 (XSDT)
|
|
uint32 rsdt_addr;
|
|
// ACPI 2.0 and later only, and only valid when revision >= 2.
|
|
uint32 length;
|
|
uint64 xsdt_addr;
|
|
uint8 ext_checksum; // over `length` bytes
|
|
uint8 reserved[3];
|
|
} __attribute__((packed));
|
|
|
|
// Every ACPI table starts with this.
|
|
struct acpi_sdt {
|
|
char sig[4];
|
|
uint32 length;
|
|
uint8 revision;
|
|
uint8 checksum;
|
|
char oemid[6];
|
|
char oem_table_id[8];
|
|
uint32 oem_revision;
|
|
uint32 creator_id;
|
|
uint32 creator_revision;
|
|
} __attribute__((packed));
|
|
|
|
// The Multiple APIC Description Table: signature "APIC".
|
|
struct acpi_madt {
|
|
struct acpi_sdt h;
|
|
uint32 lapic_addr;
|
|
uint32 flags; // bit 0: legacy 8259 PICs are present
|
|
} __attribute__((packed));
|
|
|
|
#define MADT_PCAT_COMPAT 1
|
|
|
|
// MADT entry types. Only these three are read; the rest are interrupt
|
|
// routing overrides that ioapic.c does not consult yet.
|
|
#define MADT_LAPIC 0
|
|
#define MADT_IOAPIC 1
|
|
#define MADT_ISO 2
|
|
#define MADT_LAPIC_OVERRIDE 5
|
|
#define MADT_X2APIC 9
|
|
|
|
struct madt_entry {
|
|
uint8 type;
|
|
uint8 length;
|
|
} __attribute__((packed));
|
|
|
|
struct madt_lapic {
|
|
struct madt_entry e;
|
|
uint8 acpi_id;
|
|
uint8 apic_id;
|
|
uint32 flags; // bit 0: enabled, bit 1: online-capable
|
|
} __attribute__((packed));
|
|
|
|
#define LAPIC_ENABLED 1
|
|
#define LAPIC_ONLINE_CAPABLE 2
|
|
|
|
struct madt_ioapic {
|
|
struct madt_entry e;
|
|
uint8 id;
|
|
uint8 reserved;
|
|
uint32 addr;
|
|
uint32 gsi_base;
|
|
} __attribute__((packed));
|
|
|
|
// The same as MADT_LAPIC, for a processor whose APIC ID does not fit in a
|
|
// byte -- or whose firmware simply prefers this form. Firmware that hands over
|
|
// in x2APIC mode is entitled to describe every processor this way and none
|
|
// the old way, and then a parser that reads only type 0 finds no CPUs at all.
|
|
struct madt_x2apic {
|
|
struct madt_entry e;
|
|
uint16 reserved;
|
|
uint32 apic_id;
|
|
uint32 flags;
|
|
uint32 acpi_uid;
|
|
} __attribute__((packed));
|
|
|
|
struct madt_lapic_override {
|
|
struct madt_entry e;
|
|
uint16 reserved;
|
|
uint64 addr;
|
|
} __attribute__((packed));
|
|
|
|
// Interrupt source override: ISA IRQ `source` is really GSI `gsi`, signalled
|
|
// as `flags` says -- polarity in bits 1:0, trigger in 3:2, each 0 for "as the
|
|
// bus does it", 1 for high/edge and 3 for low/level.
|
|
struct madt_iso {
|
|
struct madt_entry e;
|
|
uint8 bus;
|
|
uint8 source;
|
|
uint32 gsi;
|
|
uint16 flags;
|
|
} __attribute__((packed));
|
|
|
|
static struct {
|
|
int set;
|
|
uint32 gsi;
|
|
uint16 flags;
|
|
} iso[16];
|
|
|
|
static uint64 rsdp_phys;
|
|
|
|
static int
|
|
checksum_ok(const void *p, uint32 len)
|
|
{
|
|
const uint8 *b = p;
|
|
uint8 sum = 0;
|
|
for (uint32 i = 0; i < len; i++)
|
|
sum += b[i];
|
|
return sum == 0;
|
|
}
|
|
|
|
// Scan a physical range for the RSDP signature. It is 16-byte aligned by
|
|
// specification, which makes this cheap enough to do twice.
|
|
static struct acpi_rsdp *
|
|
scan_rsdp(uint64 base, uint64 len)
|
|
{
|
|
for (uint64 off = 0; off + sizeof(struct acpi_rsdp) <= len; off += 16) {
|
|
struct acpi_rsdp *r = (struct acpi_rsdp *)P2V(base + off);
|
|
if (memcmp(r->sig, "RSD PTR ", 8) != 0)
|
|
continue;
|
|
// Signature alone is not enough: the string can appear in unrelated data,
|
|
// and following a bad pointer here means faulting before there is anything
|
|
// to report the fault with.
|
|
if (!checksum_ok(r, 20))
|
|
continue;
|
|
return r;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct acpi_rsdp *
|
|
find_rsdp(void)
|
|
{
|
|
// A UEFI loader hands the address over directly, because the scan below is a
|
|
// BIOS convention that UEFI firmware need not honour and OVMF does not: the
|
|
// kernel found no RSDP, so no MADT, so no IOAPIC, and nothing routed IRQ 4 --
|
|
// it reached a shell prompt on polled serial output and then ignored every
|
|
// key.
|
|
//
|
|
// Validated, never trusted. The BIOS path does not write this slot, so there
|
|
// it holds whatever happened to be in memory, and following a wild pointer
|
|
// faults before there is anything to report the fault with. The bound is what
|
|
// the direct map covers; the signature and checksum below do the rest.
|
|
//
|
|
// Deliberately *not* requiring 16-byte alignment. That rule applies to
|
|
// searching the BIOS area, not to a pointer handed over by firmware -- OVMF's
|
|
// sits at 0x3f77e014, and demanding alignment threw away the very address
|
|
// this code was added to use.
|
|
uint64 handed = *(uint64 *)P2V(SCHBOOT_RSDP_ADDR);
|
|
if (handed && handed < 0x100000000ULL) {
|
|
struct acpi_rsdp *r = (struct acpi_rsdp *)P2V(handed);
|
|
if (memcmp(r->sig, "RSD PTR ", 8) == 0 && checksum_ok(r, 20))
|
|
return r;
|
|
}
|
|
|
|
// The EBDA, whose segment address the BIOS leaves at 0x40E.
|
|
uint16 ebda_seg = *(uint16 *)P2V(0x40E);
|
|
if (ebda_seg) {
|
|
struct acpi_rsdp *r = scan_rsdp((uint64)ebda_seg << 4, 1024);
|
|
if (r)
|
|
return r;
|
|
}
|
|
return scan_rsdp(0xE0000, 0x20000);
|
|
}
|
|
|
|
// Walk the RSDT or XSDT looking for a table by signature.
|
|
//
|
|
// Two nearly identical loops rather than one clever one: the only difference is
|
|
// the pointer width -- 32-bit entries in the RSDT, 64-bit in the XSDT -- and
|
|
// reading a 64-bit pointer out of a 32-bit array is exactly the sort of bug
|
|
// that produces a plausible address pointing at nothing.
|
|
static struct acpi_sdt *
|
|
find_table(struct acpi_rsdp *rsdp, const char *sig)
|
|
{
|
|
struct acpi_sdt *root;
|
|
int wide;
|
|
|
|
if (rsdp->revision >= 2 && rsdp->xsdt_addr) {
|
|
root = (struct acpi_sdt *)P2V(rsdp->xsdt_addr);
|
|
wide = 1;
|
|
} else {
|
|
root = (struct acpi_sdt *)P2V((uint64)rsdp->rsdt_addr);
|
|
wide = 0;
|
|
}
|
|
|
|
if (!checksum_ok(root, root->length))
|
|
return 0;
|
|
|
|
uint32 n = (root->length - sizeof(struct acpi_sdt)) / (wide ? 8 : 4);
|
|
for (uint32 i = 0; i < n; i++) {
|
|
uint64 pa;
|
|
if (wide) {
|
|
// Byte-wise, because the array is packed and 8-byte entries in it are
|
|
// not guaranteed to be 8-byte aligned.
|
|
uint8 *p = (uint8 *)(root + 1) + i * 8;
|
|
pa = 0;
|
|
for (int b = 7; b >= 0; b--)
|
|
pa = (pa << 8) | p[b];
|
|
} else {
|
|
pa = ((uint32 *)(root + 1))[i];
|
|
}
|
|
struct acpi_sdt *t = (struct acpi_sdt *)P2V(pa);
|
|
if (memcmp(t->sig, sig, 4) == 0)
|
|
return t;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// The n-th table, for /sys/firmware/acpi/tables: 0, 1 for a slot with nothing
|
|
// readable in it (go on to n + 1), or -1 past the end. The root's entries in order,
|
|
// then the DSDT and FACS: no root lists those two, only the FADT does, and
|
|
// they are the ones anybody debugging a machine wants first.
|
|
int
|
|
acpi_table_nth(int n, uint64 *pa, uint32 *len, char sig[4])
|
|
{
|
|
struct acpi_rsdp *rsdp;
|
|
struct acpi_sdt *root;
|
|
int wide;
|
|
|
|
if (rsdp_phys == 0)
|
|
return -1;
|
|
rsdp = (struct acpi_rsdp *)P2V(rsdp_phys);
|
|
if (rsdp->revision >= 2 && rsdp->xsdt_addr) {
|
|
root = (struct acpi_sdt *)P2V(rsdp->xsdt_addr);
|
|
wide = 1;
|
|
} else {
|
|
root = (struct acpi_sdt *)P2V((uint64)rsdp->rsdt_addr);
|
|
wide = 0;
|
|
}
|
|
if (!checksum_ok(root, root->length))
|
|
return -1;
|
|
|
|
uint32 cnt = (root->length - sizeof(struct acpi_sdt)) / (wide ? 8 : 4);
|
|
uint64 fadt = 0;
|
|
for (uint32 i = 0; i < cnt; i++) {
|
|
uint64 p;
|
|
if (wide) {
|
|
uint8 *b = (uint8 *)(root + 1) + i * 8;
|
|
p = 0;
|
|
for (int k = 7; k >= 0; k--)
|
|
p = (p << 8) | b[k];
|
|
} else {
|
|
p = ((uint32 *)(root + 1))[i];
|
|
}
|
|
if (p == 0 || p >= 0x100000000ULL) { /* outside the direct map */
|
|
if ((int)i == n)
|
|
return 1;
|
|
continue;
|
|
}
|
|
struct acpi_sdt *t = (struct acpi_sdt *)P2V(p);
|
|
if (memcmp(t->sig, "FACP", 4) == 0)
|
|
fadt = p;
|
|
if ((int)i == n) {
|
|
*pa = p;
|
|
*len = t->length;
|
|
memmove(sig, t->sig, 4);
|
|
return 0;
|
|
}
|
|
}
|
|
if (fadt == 0)
|
|
return -1;
|
|
|
|
// From the FADT: DSDT at 40 (32-bit) or X_DSDT at 140, FACS at 36 or
|
|
// X_FIRMWARE_CTRL at 132. The wide ones win when the table is long enough
|
|
// to have them and they are set.
|
|
struct acpi_sdt *f = (struct acpi_sdt *)P2V(fadt);
|
|
uint8 *fb = (uint8 *)f;
|
|
uint64 dsdt = *(uint32 *)(fb + 40), facs = *(uint32 *)(fb + 36);
|
|
if (f->length >= 148 && *(uint64 *)(fb + 140))
|
|
dsdt = *(uint64 *)(fb + 140);
|
|
if (f->length >= 140 && *(uint64 *)(fb + 132))
|
|
facs = *(uint64 *)(fb + 132);
|
|
|
|
uint64 extra[2] = { dsdt, facs };
|
|
for (int k = 0; k < 2; k++) {
|
|
if ((int)cnt + k != n)
|
|
continue;
|
|
uint64 p = extra[k];
|
|
if (p == 0 || p >= 0x100000000ULL)
|
|
return 1;
|
|
uint8 *t = (uint8 *)P2V(p);
|
|
memmove(sig, t, 4);
|
|
*pa = p;
|
|
*len = *(uint32 *)(t + 4); /* both keep the length at 4 */
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// Returns 1 if the processors were enumerated, 0 to fall back to mpinit().
|
|
// One processor from the MADT.
|
|
//
|
|
// Enabled processors only. "Online capable" means the processor is *not*
|
|
// running but could be brought up later by hot-plug, which this kernel does
|
|
// not do -- and AMD firmware routinely lists every core the socket could have
|
|
// that way, present or not. Counting those as CPUs sent startothers() a
|
|
// STARTUP to an APIC that does not exist, and then it waited for an answer
|
|
// for ever: a hang after "acpi: N cpus" on exactly the machines whose table
|
|
// was the most complete.
|
|
//
|
|
// An ID above 254 is refused too: xAPIC addresses a processor with eight bits
|
|
// (255 is broadcast), and that is the mode this kernel runs the APIC in.
|
|
static void
|
|
add_cpu(uint32 apic_id, uint32 flags)
|
|
{
|
|
if (!(flags & LAPIC_ENABLED))
|
|
return;
|
|
if (apic_id > 254) {
|
|
cprintf("acpi: cpu with apic id %u needs x2APIC; not using it\n", apic_id);
|
|
return;
|
|
}
|
|
for (int i = 0; i < ncpu; i++)
|
|
if (cpus[i].apicid == apic_id)
|
|
return; // listed as both type 0 and type 9
|
|
if (ncpu < NCPU) {
|
|
cpus[ncpu].apicid = (uchar)apic_id;
|
|
ncpu++;
|
|
}
|
|
}
|
|
|
|
int
|
|
acpi_init(void)
|
|
{
|
|
struct acpi_rsdp *rsdp = find_rsdp();
|
|
if (!rsdp)
|
|
return 0;
|
|
rsdp_phys = V2P(rsdp);
|
|
|
|
// The FADT first, for the PM timer: PM_TMR_BLK at offset 76, and bit 8 of
|
|
// the flags at 112 says whether it counts 32 bits or 24. A hardware-reduced
|
|
// platform has neither and says so with a zero port.
|
|
struct acpi_sdt *fadt = find_table(rsdp, "FACP");
|
|
if (fadt && fadt->length >= 116 && checksum_ok(fadt, fadt->length)) {
|
|
acpi_pmtmr_port = *(uint32 *)((uint8 *)fadt + 76);
|
|
acpi_pmtmr_32 = (*(uint32 *)((uint8 *)fadt + 112) >> 8) & 1;
|
|
if (acpi_pmtmr_port > 0xFFFF)
|
|
acpi_pmtmr_port = 0;
|
|
}
|
|
|
|
struct acpi_madt *madt = (struct acpi_madt *)find_table(rsdp, "APIC");
|
|
if (!madt || !checksum_ok(madt, madt->h.length))
|
|
return 0;
|
|
|
|
lapic = (uint *)P2V((uint64)madt->lapic_addr);
|
|
|
|
uint8 *p = (uint8 *)madt + sizeof(struct acpi_madt);
|
|
uint8 *e = (uint8 *)madt + madt->h.length;
|
|
int found_ioapic = 0;
|
|
|
|
while (p + sizeof(struct madt_entry) <= e) {
|
|
struct madt_entry *ent = (struct madt_entry *)p;
|
|
// A zero length would spin here forever. Firmware tables are not always
|
|
// well formed, and an infinite loop this early is indistinguishable from a
|
|
// hang with no output at all.
|
|
if (ent->length < sizeof(struct madt_entry))
|
|
break;
|
|
|
|
switch (ent->type) {
|
|
case MADT_LAPIC: {
|
|
struct madt_lapic *l = (struct madt_lapic *)p;
|
|
add_cpu(l->apic_id, l->flags);
|
|
break;
|
|
}
|
|
case MADT_X2APIC: {
|
|
struct madt_x2apic *x = (struct madt_x2apic *)p;
|
|
add_cpu(x->apic_id, x->flags);
|
|
break;
|
|
}
|
|
case MADT_IOAPIC: {
|
|
struct madt_ioapic *io = (struct madt_ioapic *)p;
|
|
// One only: the one whose inputs start at GSI 0, which is where the ISA
|
|
// lines and the chipset's PCI lines are. Multi-I/O-APIC routing needs
|
|
// the interrupt source overrides too, which nothing here reads yet, so
|
|
// claiming to support it would be worse than not.
|
|
//
|
|
// And its address from the table rather than 0xFEC00000. That is the
|
|
// usual place and not a promised one: a second I/O APIC listed first,
|
|
// or a board that moved the first, used to get its registers written
|
|
// at an address where there were none.
|
|
if (!found_ioapic || io->gsi_base == 0) {
|
|
ioapicid = io->id;
|
|
ioapic_phys = io->addr;
|
|
found_ioapic = 1;
|
|
}
|
|
break;
|
|
}
|
|
case MADT_ISO: {
|
|
struct madt_iso *o = (struct madt_iso *)p;
|
|
if (o->source < 16) {
|
|
iso[o->source].set = 1;
|
|
iso[o->source].gsi = o->gsi;
|
|
iso[o->source].flags = o->flags;
|
|
}
|
|
break;
|
|
}
|
|
case MADT_LAPIC_OVERRIDE: {
|
|
struct madt_lapic_override *o = (struct madt_lapic_override *)p;
|
|
lapic = (uint *)P2V(o->addr);
|
|
break;
|
|
}
|
|
}
|
|
p += ent->length;
|
|
}
|
|
|
|
if (ncpu == 0)
|
|
return 0;
|
|
|
|
// The 8259s are still there and still wired to the CPU. On a system with an
|
|
// IMCR, this switches interrupt delivery from the PICs to the APIC; on one
|
|
// without, the ports are unclaimed and the writes go nowhere. mpinit() does
|
|
// the same thing guarded by the MP table's imcrp flag.
|
|
if (madt->flags & MADT_PCAT_COMPAT) {
|
|
outb(0x22, 0x70);
|
|
outb(0x23, inb(0x23) | 1);
|
|
}
|
|
|
|
kinfo("acpi: %d cpu%s, lapic at %p, ioapic id %d\n",
|
|
ncpu, ncpu == 1 ? "" : "s", lapic, ioapicid);
|
|
return 1;
|
|
}
|
|
|
|
uint64
|
|
acpi_rsdp_phys(void)
|
|
{
|
|
return rsdp_phys;
|
|
}
|
|
|
|
int
|
|
acpi_isa_gsi(int isa, int dflt, int *mode)
|
|
{
|
|
int level, low;
|
|
|
|
*mode = dflt;
|
|
if (isa < 0 || isa >= 16 || !iso[isa].set)
|
|
return isa;
|
|
|
|
level = IRQ_IS_LEVEL(dflt);
|
|
low = dflt == IRQ_LEVEL_LOW || dflt == IRQ_EDGE_LOW;
|
|
switch (iso[isa].flags & 3) {
|
|
case 1: low = 0; break;
|
|
case 3: low = 1; break;
|
|
}
|
|
switch ((iso[isa].flags >> 2) & 3) {
|
|
case 1: level = 0; break;
|
|
case 3: level = 1; break;
|
|
}
|
|
*mode = level ? (low ? IRQ_LEVEL_LOW : IRQ_LEVEL_HIGH)
|
|
: (low ? IRQ_EDGE_LOW : IRQ_EDGE_HIGH);
|
|
return iso[isa].gsi;
|
|
}
|