// SPDX-License-Identifier: GPL-2.0-or-later // // What the kernel uses AML for. See include/dev/acpi.h. // // Interrupt routing is the reason this exists. A PCI device's config space // names an interrupt line, but that number is the 8259's: the firmware // programmed it for an OS running the legacy PICs, and with the I/O APIC in // use the device is on some other pin entirely. Which pin is written down // only in the namespace, as each PCI root bridge's _PRT -- a table from // (device, INTx pin) to either a GSI or a "link device" whose _CRS holds the // GSI. Without reading it, every INTx device on a real board waited on the // wrong pin, and only MSI and the poller (kernel/dev/irq.c) kept anything // working at all. // // The rest is what every machine expects of an OS once it has ACPI: the // power button powers it off cleanly, and power-off and reset go through the // firmware's own registers rather than an emulator's magic ports. #include "core/types.h" #include "core/defs.h" #include "core/param.h" #include "arch/x86.h" #include "core/spinlock.h" #include "dev/pci.h" #include "core/irq.h" #include "dev/acpi.h" #include "fs/fat.h" #include #include #include #include #include #include #include static int ready; // \_S5's SLP_TYP values, read once at boot for acpi_poweroff_now(): -1 until // then, or if the firmware has none. static int s5_typ_a = -1, s5_typ_b = -1; static uacpi_interrupt_ret power_button(uacpi_handle ctx); static void cstate_find(void); void acpi_aml_init(void) { uacpi_status st; if (acpi_rsdp_phys() == 0) return; st = uacpi_initialize(0); if (uacpi_unlikely_error(st)) { cprintf("acpi: tables not usable: %s\n", uacpi_status_to_string(st)); return; } st = uacpi_namespace_load(); if (uacpi_unlikely_error(st)) { cprintf("acpi: AML would not load: %s\n", uacpi_status_to_string(st)); return; } // \_PIC(1): tell the firmware the I/O APIC is in use, so that _PRT answers // with I/O APIC inputs rather than PIC lines. Before _INI runs, which on // some boards reads the answer. uacpi_set_interrupt_model(UACPI_INTERRUPT_MODEL_IOAPIC); st = uacpi_namespace_initialize(); if (uacpi_unlikely_error(st)) { cprintf("acpi: namespace would not initialize: %s\n", uacpi_status_to_string(st)); return; } st = uacpi_install_fixed_event_handler(UACPI_FIXED_EVENT_POWER_BUTTON, power_button, 0); if (uacpi_unlikely_error(st) && st != UACPI_STATUS_COMPILED_OUT) kinfo("acpi: no fixed power button: %s\n", uacpi_status_to_string(st)); uacpi_finalize_gpe_initialization(); { uacpi_object *o = 0; uacpi_object_array p; uacpi_u64 ta, tb; if (uacpi_eval_package(0, "\\_S5", 0, &o) == UACPI_STATUS_OK) { if (uacpi_object_get_package(o, &p) == UACPI_STATUS_OK && p.count >= 1 && uacpi_object_get_integer(p.objects[0], &ta) == UACPI_STATUS_OK) { tb = ta; if (p.count >= 2) uacpi_object_get_integer(p.objects[1], &tb); s5_typ_a = ta & 7; s5_typ_b = tb & 7; } uacpi_object_unref(o); } } ready = 1; kinfo("acpi: namespace loaded\n"); cstate_find(); } // --- power ------------------------------------------------------------------- // Run in the worker thread, not the SCI handler: syncing the filesystems // sleeps, and S5 runs AML. static void power_off_work(uacpi_handle ctx) { (void)ctx; cprintf("acpi: power button pressed; syncing and powering off\n"); schfs_ksync_all(); fat_ksync_all(1); acpi_poweroff(); cprintf("acpi: power-off failed; it is safe to switch off now\n"); } static uacpi_interrupt_ret power_button(uacpi_handle ctx) { static int once; (void)ctx; if (!once) { once = 1; uacpi_kernel_schedule_work(UACPI_WORK_NOTIFICATION, power_off_work, 0); } return UACPI_INTERRUPT_HANDLED; } int acpi_poweroff(void) { if (!ready) return -1; if (uacpi_unlikely_error(uacpi_prepare_for_sleep_state(UACPI_SLEEP_STATE_S5))) return -1; cli(); uacpi_enter_sleep_state(UACPI_SLEEP_STATE_S5); sti(); return -1; } // Power off from a dead kernel: the PSOGM, after it has been up long enough to // be read. acpi_poweroff() runs _PTS and other AML, which can sleep and take // locks that another processor died holding, so it is no use here. This is // only the last step of it -- SLP_TYP from \_S5, then SLP_EN, written straight // to the PM1 control registers. The firmware's preparations are skipped; a // crashed machine has nothing to save. Returns only if the machine is still on. void acpi_poweroff_now(void) { uacpi_u64 cnt = 0, a, b; if (!ready || s5_typ_a < 0) return; uacpi_read_register(UACPI_REGISTER_PM1_CNT, &cnt); cnt &= ~(uacpi_u64)((7 << 10) | (1 << 13)); a = cnt | ((uacpi_u64)s5_typ_a << 10); b = cnt | ((uacpi_u64)s5_typ_b << 10); uacpi_write_registers(UACPI_REGISTER_PM1_CNT, a, b); uacpi_write_registers(UACPI_REGISTER_PM1_CNT, a | (1 << 13), b | (1 << 13)); microdelay(100000); } int acpi_reboot(void) { if (!ready) return -1; uacpi_reboot(); return -1; } // --- PCI interrupt routing --------------------------------------------------- #define MAXBUS 256 // The bridge leading to each secondary bus, from the devices pci_init found. static struct pci_dev *upstream[MAXBUS]; static uacpi_namespace_node *root_bridge; static uacpi_iteration_decision found_root(void *user, uacpi_namespace_node *node, uacpi_u32 depth) { uacpi_u64 bbn = 0; (void)user; (void)depth; // _BBN is the bridge's first bus. Absent means 0, and bus 0 is the one // whose devices are all that most machines have. uacpi_eval_simple_integer(node, "_BBN", &bbn); if (bbn == 0 && root_bridge == 0) { root_bridge = node; return UACPI_ITERATION_DECISION_BREAK; } return UACPI_ITERATION_DECISION_CONTINUE; } // The namespace node of the device at (slot, func) below `parent`: the child // whose _ADR says so. static uacpi_namespace_node * child_at(uacpi_namespace_node *parent, int slot, int func) { uacpi_namespace_node *child = 0; uacpi_u64 adr; while (uacpi_namespace_node_next(parent, &child) == UACPI_STATUS_OK) { if (uacpi_eval_simple_integer(child, "_ADR", &adr) == UACPI_STATUS_OK && adr == (((uacpi_u64)slot << 16) | (uacpi_u64)func)) return child; } return 0; } struct irqfind { uint32 index; int gsi; int mode; }; static int mode_of(int triggering, int polarity) { int level = triggering == UACPI_TRIGGERING_LEVEL; int low = polarity == UACPI_POLARITY_ACTIVE_LOW; return level ? (low ? IRQ_LEVEL_LOW : IRQ_LEVEL_HIGH) : (low ? IRQ_EDGE_LOW : IRQ_EDGE_HIGH); } static uacpi_iteration_decision link_irq(void *user, uacpi_resource *r) { struct irqfind *f = user; if (r->type == UACPI_RESOURCE_TYPE_IRQ && r->irq.num_irqs > 0) { f->gsi = r->irq.irqs[f->index < r->irq.num_irqs ? f->index : 0]; f->mode = mode_of(r->irq.triggering, r->irq.polarity); return UACPI_ITERATION_DECISION_BREAK; } if (r->type == UACPI_RESOURCE_TYPE_EXTENDED_IRQ && r->extended_irq.num_irqs > 0) { f->gsi = r->extended_irq.irqs[f->index < r->extended_irq.num_irqs ? f->index : 0]; f->mode = mode_of(r->extended_irq.triggering, r->extended_irq.polarity); return UACPI_ITERATION_DECISION_BREAK; } return UACPI_ITERATION_DECISION_CONTINUE; } // Look (slot, pin) up in a _PRT. Returns the GSI, or -1. A link device's // current setting is used as it stands: firmware programs every link at // boot, and one it left disabled is reported rather than reconfigured. static int prt_lookup(uacpi_pci_routing_table *t, int slot, int pin, int *mode) { for (uacpi_size i = 0; i < t->num_entries; i++) { uacpi_pci_routing_table_entry *e = &t->entries[i]; if ((e->address >> 16) != (uint32)slot || e->pin != pin) continue; if (e->source == 0) { *mode = IRQ_LEVEL_LOW; // a bare GSI is PCI's signalling return e->index; } uacpi_resources *res; struct irqfind f = { e->index, -1, IRQ_LEVEL_LOW }; if (uacpi_get_current_resources(e->source, &res) != UACPI_STATUS_OK) return -1; uacpi_for_each_resource(res, link_irq, &f); uacpi_free_resources(res); if (f.gsi <= 0) { uacpi_object_name nm = uacpi_namespace_node_name(e->source); char name[5] = { nm.text[0], nm.text[1], nm.text[2], nm.text[3], 0 }; kinfo("acpi: link %s for slot %d is disabled\n", name, slot); return -1; } *mode = f.mode; return f.gsi; } return -1; } static int route_one(struct pci_dev *d, uacpi_pci_routing_table *roottab, int *mode) { // The chain of bridges from bus 0 down to this device's bus. struct pci_dev *chain[8]; int n = 0, bus = d->bus; while (bus != 0 && n < 8 && upstream[bus]) { chain[n++] = upstream[bus]; bus = upstream[bus]->bus; } if (bus != 0) return -1; // a bus not reached from bus 0 // Walk down the namespace alongside it, as far as the namespace describes // the bridges. The deepest bridge with its own _PRT is where the lookup // happens; below it, INTx pins are swizzled by the PCI-to-PCI bridge rule. uacpi_namespace_node *node = root_bridge; uacpi_pci_routing_table *tab = roottab, *owned = 0; int at = n; // chain[at..] lies below tab's bus for (int i = n - 1; i >= 0 && node; i--) { node = child_at(node, chain[i]->slot, chain[i]->func); uacpi_pci_routing_table *t; if (node && uacpi_get_pci_routing_table(node, &t) == UACPI_STATUS_OK) { if (owned) uacpi_free_pci_routing_table(owned); tab = owned = t; at = i; } } int pin = pci_read8(d->bus, d->slot, d->func, 0x3D) - 1; // 0 = INTA int slot = d->slot; for (int i = 0; i < at; i++) { pin = (pin + slot) % 4; slot = chain[i]->slot; } // The device is now (slot, pin) on the bus `tab` describes: that bridge's // secondary bus, or bus 0 for the root table. int gsi = prt_lookup(tab, slot, pin, mode); if (owned) uacpi_free_pci_routing_table(owned); return gsi; } void acpi_pci_route(void) { uacpi_pci_routing_table *roottab; int i, routed = 0, beyond = 0; if (!ready) return; uacpi_find_devices("PNP0A08", found_root, 0); // PCI Express if (!root_bridge) uacpi_find_devices("PNP0A03", found_root, 0); // conventional PCI if (!root_bridge || uacpi_get_pci_routing_table(root_bridge, &roottab) != UACPI_STATUS_OK) { kinfo("acpi: no _PRT on the root bridge; PCI lines left as found\n"); return; } for (i = 0; i < pci_count(); i++) { struct pci_dev *d = pci_get(i); if ((d->header_type & 0x7F) == 1) { int sec = pci_read8(d->bus, d->slot, d->func, 0x19); if (sec > 0 && sec < MAXBUS) upstream[sec] = d; } } for (i = 0; i < pci_count(); i++) { struct pci_dev *d = pci_get(i); int mode, gsi; if (pci_read8(d->bus, d->slot, d->func, 0x3D) == 0) continue; // uses no INTx pin at all gsi = route_one(d, roottab, &mode); if (gsi < 0) continue; if (gsi >= NIRQ_IOAPIC) { // 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) kinfo("acpi: %d:%d.%d INT%c is gsi %d, not %d\n", d->bus, d->slot, d->func, 'A' + pci_read8(d->bus, d->slot, d->func, 0x3D) - 1, gsi, d->irq); d->irq = gsi; d->irq_mode = mode; routed++; } 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); } // --- batteries and adapters -------------------------------------------------- // // A laptop's battery is an ACPI device, PNP0C0A, with methods that answer in // packages: _BIX (or the older _BIF) for what it is -- capacities, units, // names -- and _BST for how it is now. The adapter is ACPI0003 and _PSR says // whether it is plugged in. Read on demand, from /sys/class/power_supply, // which is where fastfetch and every battery widget look. #define MAXPSU 4 static uacpi_namespace_node *psu_node[MAXPSU]; static int psu_isbat[MAXPSU]; static int npsu; static uacpi_iteration_decision found_psu(void *user, uacpi_namespace_node *node, uacpi_u32 depth) { (void)depth; if (npsu < MAXPSU) { psu_node[npsu] = node; psu_isbat[npsu] = user != 0; npsu++; } return UACPI_ITERATION_DECISION_CONTINUE; } static int psu_found; static void psu_find(void) { if (psu_found || !ready) return; psu_found = 1; uacpi_find_devices("PNP0C0A", found_psu, (void *)1); uacpi_find_devices("ACPI0003", found_psu, 0); } int acpi_psu_count(void) { psu_find(); return npsu; } static uint32 pkg_int(uacpi_object_array *p, uacpi_size i) { uacpi_u64 v; if (i >= p->count || uacpi_object_get_integer(p->objects[i], &v) != UACPI_STATUS_OK) return ACPI_UNKNOWN; return (uint32)v; } static void pkg_str(uacpi_object_array *p, uacpi_size i, char *out, int cap) { uacpi_data_view d; out[0] = 0; if (i >= p->count || uacpi_object_get_string_or_buffer(p->objects[i], &d) != UACPI_STATUS_OK) return; int n = 0; for (uacpi_size k = 0; k < d.length && n < cap - 1 && d.const_text[k]; k++) if (d.const_text[k] >= ' ' && d.const_text[k] < 127) out[n++] = d.const_text[k]; // Firmware pads with spaces. while (n > 0 && out[n - 1] == ' ') n--; out[n] = 0; } int acpi_psu_get(int i, struct acpi_psu *b) { uacpi_object *o; uacpi_object_array p; uacpi_u32 sta = 0xF; psu_find(); if (i < 0 || i >= npsu) return -1; memset(b, 0, sizeof(*b)); b->battery = psu_isbat[i]; if (!b->battery) { uacpi_u64 v = 0; uacpi_eval_simple_integer(psu_node[i], "_PSR", &v); b->online = v != 0; return 0; } // Bit 4 of _STA: a battery is in the bay. A missing _STA means present. uacpi_eval_sta(psu_node[i], &sta); b->present = (sta & 0x10) != 0; b->rate = b->remaining = b->voltage = ACPI_UNKNOWN; b->design = b->full = b->design_voltage = b->cycles = ACPI_UNKNOWN; if (!b->present) return 0; // _BIX: a revision first, then _BIF's fields shifted by one, with the // cycle count after the design voltage's neighbours. if (uacpi_eval_package(psu_node[i], "_BIX", 0, &o) == UACPI_STATUS_OK) { if (uacpi_object_get_package(o, &p) == UACPI_STATUS_OK) { b->unit = (int)pkg_int(&p, 1); b->design = pkg_int(&p, 2); b->full = pkg_int(&p, 3); b->technology = (int)pkg_int(&p, 4); b->design_voltage = pkg_int(&p, 5); b->cycles = pkg_int(&p, 8); pkg_str(&p, 16, b->model, sizeof(b->model)); pkg_str(&p, 17, b->serial, sizeof(b->serial)); pkg_str(&p, 18, b->kind, sizeof(b->kind)); pkg_str(&p, 19, b->oem, sizeof(b->oem)); } uacpi_object_unref(o); } else if (uacpi_eval_package(psu_node[i], "_BIF", 0, &o) == UACPI_STATUS_OK) { if (uacpi_object_get_package(o, &p) == UACPI_STATUS_OK) { b->unit = (int)pkg_int(&p, 0); b->design = pkg_int(&p, 1); b->full = pkg_int(&p, 2); b->technology = (int)pkg_int(&p, 3); b->design_voltage = pkg_int(&p, 4); pkg_str(&p, 9, b->model, sizeof(b->model)); pkg_str(&p, 10, b->serial, sizeof(b->serial)); pkg_str(&p, 11, b->kind, sizeof(b->kind)); pkg_str(&p, 12, b->oem, sizeof(b->oem)); } uacpi_object_unref(o); } if (uacpi_eval_package(psu_node[i], "_BST", 0, &o) == UACPI_STATUS_OK) { if (uacpi_object_get_package(o, &p) == UACPI_STATUS_OK) { b->state = pkg_int(&p, 0); b->rate = pkg_int(&p, 1); b->remaining = pkg_int(&p, 2); b->voltage = pkg_int(&p, 3); } uacpi_object_unref(o); } return 0; } // --- idle states -------------------------------------------------------------- // // An idle CPU used to run hlt and nothing else. hlt is C1: the core stops // executing and stays powered. The states below it -- CC6 on Zen, C3 and // deeper on Intel -- switch the core off, and the firmware lists how to reach // them in each processor's _CST. Without them a laptop sitting at a shell // keeps every core warm and the fans going. // // _CST is a count followed by one package per state: a register (a Generic // Register descriptor in a buffer), a type (1 = C1, 2 = C2, 3 = C3), the // worst-case wake-up latency in microseconds, and the power. A register in // system I/O space is entered by reading it; one in functional fixed hardware // is MWAIT, with the hint in the address. // // Only types 1 and 2 are taken. C3 asks the OS to stop bus-master activity // or flush caches first (ACPI 8.1.4), which this does not do. struct acpi_idle acpi_idle; static int cst_done; static void cst_parse(uacpi_namespace_node *node) { uacpi_object *o = 0; uacpi_object_array p, e; uacpi_data_view d; if (uacpi_eval_package(node, "_CST", 0, &o) != UACPI_STATUS_OK) return; if (uacpi_object_get_package(o, &p) == UACPI_STATUS_OK) { for (uacpi_size i = 1; i < p.count; i++) { if (uacpi_object_get_package(p.objects[i], &e) != UACPI_STATUS_OK || e.count < 4) continue; uint32 type = pkg_int(&e, 1), lat = pkg_int(&e, 2); if (type != 2 || lat > 1000) continue; if (uacpi_object_get_buffer(e.objects[0], &d) != UACPI_STATUS_OK || d.length < 15 || d.const_bytes[0] != 0x82) continue; uint8 space = d.const_bytes[3]; uint64 addr = 0; for (int k = 0; k < 8; k++) addr |= (uint64)d.const_bytes[7 + k] << (8 * k); if (space == UACPI_ADDRESS_SPACE_SYSTEM_IO && addr && addr < 0x10000) { acpi_idle.kind = ACPI_IDLE_IO; } else if (space == UACPI_ADDRESS_SPACE_FFIXEDHW && (d.const_bytes[4] == 1)) { // Intel's FFH encoding: vendor 1, class 2 (native C-state), and // the MWAIT hint in the low byte of the address. acpi_idle.kind = ACPI_IDLE_MWAIT; } else { continue; } acpi_idle.addr = addr; acpi_idle.latency = lat; break; } } uacpi_object_unref(o); } static uacpi_iteration_decision found_cpu(void *user, uacpi_namespace_node *node, uacpi_u32 depth) { (void)user; (void)depth; if (cst_done) return UACPI_ITERATION_DECISION_BREAK; cst_parse(node); cst_done = 1; // every processor's _CST is the same in practice return UACPI_ITERATION_DECISION_BREAK; } static void cstate_find(void) { uint32 a, b, c, dd; // The local APIC timer is what wakes an idle CPU for the scheduler tick. // Below C1 it may stop, unless the CPU says it keeps running (ARAT, // CPUID 6 EAX bit 2); without that an idle CPU would sleep for good. cpuid_leaf(6, &a, &b, &c, &dd); if (!(a & 4)) { kinfo("acpi: no always-running APIC timer; idle stays at C1\n"); return; } uacpi_namespace_for_each_child(uacpi_namespace_root(), found_cpu, 0, UACPI_OBJECT_PROCESSOR_BIT, UACPI_MAX_DEPTH_ANY, 0); if (!cst_done) uacpi_find_devices("ACPI0007", found_cpu, 0); if (acpi_idle.kind == ACPI_IDLE_MWAIT) { cpuid_leaf(1, &a, &b, &c, &dd); if (!(c & (1u << 3))) // no MONITOR/MWAIT after all acpi_idle.kind = ACPI_IDLE_HLT; } if (acpi_idle.kind == ACPI_IDLE_IO) kinfo("acpi: idle CPUs enter C2 through port %x (wake-up %d us)\n", (uint)acpi_idle.addr, acpi_idle.latency); else if (acpi_idle.kind == ACPI_IDLE_MWAIT) kinfo("acpi: idle CPUs enter C2 by MWAIT hint %x (wake-up %d us)\n", (uint)acpi_idle.addr, acpi_idle.latency); else kinfo("acpi: no usable C2 in _CST; idle stays at C1\n"); }