/** * ACPI (Advanced Configuration and Power Interface) functionality. */ module hulk.acpi; import hulk.hurl; import hulk.klog; import hulk.memory; struct Acpi { private 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; 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. */ 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; size_t n_sgma_descs() { return (header.length - MCFG.sgma_descs.offsetof) / MCFG.SGMADesc.sizeof; } } public __gshared ulong apic_address; public __gshared MADT * madt; public __gshared MCFG * mcfg; public static void initialize(ulong acpi_xsdt_phys) { Klog.writefln("\a3Initialize ACPI"); /* 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.fatal_error("XSDT signature invalid"); 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) { madt = cast(MADT *)address; madt.initialize(); } else if (signature == MCFG_SIGNATURE) { mcfg = cast(MCFG *)address; } } if (madt == null) { Klog.fatal_error("MADT table not found"); } if (mcfg == null) { Klog.fatal_error("MCFG table not found"); } } private static void map_table(ulong address, ulong length) { Hurl.identity_map_range(address, length, PT_WRITABLE | PT_DISABLE_CACHE | PT_NO_EXECUTE); } }