/** * ACPI (Advanced Configuration and Power Interface) functionality. */ module hulk.acpi; import hulk.hurl; import hulk.klog; import hulk.memory; struct Acpi { static uint signature(string s) { return s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24); } enum uint APIC_SIGNATURE = signature("APIC"); enum uint MCFG_SIGNATURE = signature("MCFG"); enum uint XSDT_SIGNATURE = signature("XSDT"); static align(4) struct Header { uint signature; uint length; ubyte revision; ubyte checksum; char[6] oemid; ulong oemtableid; uint oem_revision; uint creator_id; uint creator_revision; } static struct XSDT { Header header; /* Table pointers are not ulong-aligned! They begin at offset 36. */ align(4) ulong[1] tables; } static struct MADT { static struct Entry { enum LOCAL_APIC_ADDRESS_OVERRIDE = 5; ubyte type; ubyte length; } Header header; uint local_apic_address; uint flags; void initialize() { apic_address = local_apic_address; Klog.writefln("Found 32-bit APIC address: 0x%x", apic_address); const(void) * end = cast(const(void) *)(&this) + header.length; const(Entry) * entry = cast(const(Entry) *)(cast(ulong)&this + MADT.sizeof); while (entry < end) { entry = cast(const(Entry) *)(cast(size_t)entry + entry.length); if (entry.type == Entry.LOCAL_APIC_ADDRESS_OVERRIDE) { /* Found a 64-bit Local APIC Address Override entry. */ Klog.writefln("Found 64-bit APIC address: 0x%x", apic_address); memcpy(cast(void *)&apic_address, cast(const(void) *)entry + 4u, apic_address.sizeof); } } } } static struct MCFG { /* PCI Segment Group Memory Area Descriptor. */ static struct SGMADesc { uint[2] base_address_a; ushort pci_segment_group_number; ubyte start_pci_bus_number; ubyte end_pci_bus_number; uint _reserved; public @property ulong base_address() const { return base_address_a[0] | (cast(ulong)base_address_a[1] << 32); } } static assert(SGMADesc.sizeof == 16); static assert(SGMADesc.alignof == 4); Header header; uint[2] _reserved; SGMADesc[0] sgma_descs; void initialize() { Klog.writefln("MCFG length = %u", header.length); size_t n_sgma_descs = (header.length - MCFG.sgma_descs.offsetof) / MCFG.SGMADesc.sizeof; Klog.writefln("# SGMA descriptors = %u", (header.length - MCFG.sgma_descs.offsetof) / MCFG.SGMADesc.sizeof); for (size_t i = 0; i < n_sgma_descs; i++) { Klog.writefln("SGMA %u: SG %u; Bus %u-%u; addr %p", i, sgma_descs[i].pci_segment_group_number, sgma_descs[i].start_pci_bus_number, sgma_descs[i].end_pci_bus_number, sgma_descs[i].base_address); } } } public static __gshared ulong apic_address; public static void initialize(ulong acpi_xsdt_phys) { /* Map the XSDT header. */ map_table(acpi_xsdt_phys, PAGE_SIZE); const(XSDT) * xsdt = cast(const(XSDT) *)acpi_xsdt_phys; if (xsdt.header.signature != XSDT_SIGNATURE) { Klog.writef("XSDT signature invalid\n"); return; } /* Map the entire XSDT. */ if (xsdt.header.length > PAGE_SIZE) { map_table(acpi_xsdt_phys, xsdt.header.length); } size_t n_entries = (xsdt.header.length - xsdt.header.sizeof) / xsdt.tables[0].sizeof; for (size_t i = 0u; i < n_entries; i++) { ulong address = xsdt.tables[i]; map_table(address, PAGE_SIZE); const(Header) * header = cast(const(Header) *)address; uint length = header.length; if (length > PAGE_SIZE) { map_table(address, length); } uint signature = header.signature; Klog.writefln("Found ACPI table %08x (%c%c%c%c)", signature, signature & 0xFFu, (signature >> 8u) & 0xFFu, (signature >> 16u) & 0xFFu, (signature >> 24u) & 0xFFu); if (signature == APIC_SIGNATURE) { (cast(MADT *)address).initialize(); } else if (signature == MCFG_SIGNATURE) { (cast(MCFG *)address).initialize(); } } } private static void map_table(ulong address, ulong length) { Hurl.identity_map_range(address, length, PT_DISABLE_CACHE | PT_NO_EXECUTE); } }