/* * hal_acpi.c - ACPI parser * * Copyright (c) 2017 Maxime Villard * * This file is part of ALMOS-MKH. * * ALMOS-MKH is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2.0 of the License. * * ALMOS-MKH is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ALMOS-MKH.; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include /* -------------------------------------------------------------------------- */ #define RSDT_NENTRIES(rsdt) \ ( (rsdt->Header.Length - sizeof(rsdt_t) + sizeof(uint32_t)) / sizeof(uint32_t) ) #define RSDT_ENTRIES(rsdt) \ ((uint32_t *)&rsdt->TableOffsetEntry[0]) static bool_t hal_acpi_table_correct(header_t *header) { uint8_t *ptr, sum = 0; size_t i, n; ptr = (uint8_t *)header; n = header->Length; /* Verify checksum */ for (i = 0; i < n; i++) { sum += ptr[i]; } return (sum == 0); } static vaddr_t hal_acpi_map_table(paddr_t headerpa, const char *sig) { paddr_t basepa, pa; vaddr_t baseva, retva, va; size_t off, size; size_t npages, ngrow, n; header_t *header; /* Allocate the VA for the header */ basepa = rounddown(headerpa, PAGE_SIZE); npages = roundup(headerpa + sizeof(header_t), PAGE_SIZE) - basepa; baseva = hal_gpt_bootstrap_valloc(npages); off = headerpa - basepa; /* Enter the VA, and get the total size of the structure */ hal_gpt_enter_range(baseva, basepa, npages); retva = (vaddr_t)(baseva + off); header = (header_t *)retva; size = header->Length; XASSERT(size >= sizeof(header_t)); /* Check the signature */ if (memcmp((void *)&header->Signature, sig, ACPI_NAME_SIZE)) return 0; /* Grow the VA to map the rest */ ngrow = roundup(headerpa + size, PAGE_SIZE) - basepa; n = ngrow - npages; va = hal_gpt_bootstrap_valloc(n); pa = basepa + npages * PAGE_SIZE; hal_gpt_enter_range(va, pa, n); /* Verify the checksum */ if (!hal_acpi_table_correct(header)) x86_panic("Wrong checksum for table"); return retva; } /* -------------------------------------------------------------------------- */ static void hal_acpi_parse_ioapic(madt_ioapic_t *ioapic) { extern paddr_t ioapic_pa; uint32_t irqbase; ioapic_pa = ioapic->Address; irqbase = ioapic->GlobalIrqBase; x86_printf("-> IOAPIC address: %Z\n", ioapic_pa); x86_printf("-> IOAPIC irqbase: %z\n", (uint64_t)irqbase); } static void hal_acpi_parse_madt(madt_t *madt) { madt_lapic_override_t *override; void *ptr, *end; subheader_t *sub; size_t nioapic = 0; size_t ncpu = 0; extern paddr_t lapic_pa; lapic_pa = (paddr_t)madt->Address; ptr = (void *)(madt + 1); end = (void *)madt + madt->Header.Length; while (ptr < end) { sub = (subheader_t *)ptr; if (sub->Type == ACPI_MADT_TYPE_IO_APIC) { hal_acpi_parse_ioapic((madt_ioapic_t *)sub); nioapic++; } else if (sub->Type == ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE) { override = (madt_lapic_override_t *)sub; lapic_pa = (paddr_t)override->Address; x86_printf("-> found LAPIC override\n"); } else if (sub->Type == ACPI_MADT_TYPE_LOCAL_APIC) { ncpu++; } ptr += sub->Length; } x86_printf("-> LAPIC address: %Z\n", lapic_pa); x86_printf("-> number of CPUs: %z\n", ncpu); x86_printf("-> number of IOAPICs: %z\n", nioapic); } static madt_t *hal_acpi_map_madt(rsdt_t *rsdt) { vaddr_t va; paddr_t pa; uint32_t *ent; size_t i, n; n = RSDT_NENTRIES(rsdt); ent = RSDT_ENTRIES(rsdt); for (i = 0; i < n; i++) { pa = (paddr_t)ent[i]; va = hal_acpi_map_table(pa, "APIC"); if (va == 0) continue; return (madt_t *)va; } return NULL; } /* -------------------------------------------------------------------------- */ static rsdt_t *hal_acpi_map_rsdt(rsdp_t *rsdp) { paddr_t rsdt_pa; rsdt_t *rsdt; rsdt_pa = (paddr_t)rsdp->RsdtPhysicalAddress; rsdt = (rsdt_t *)hal_acpi_map_table(rsdt_pa, "RSDT"); if (rsdt == 0) x86_panic("RSDT invalid"); return rsdt; } /* -------------------------------------------------------------------------- */ static bool_t hal_acpi_rsdp_correct(uint8_t *ptr) { uint8_t sum = 0; size_t i; /* Verify checksum */ for (i = 0; i < 20; i++) { sum += ptr[i]; } return (sum == 0); } static rsdp_t *hal_acpi_find_rsdp(vaddr_t va_start, vaddr_t va_end) { rsdp_t *rsdp; vaddr_t va = va_start; size_t i, n = PAGE_SIZE / ACPI_RSDP_ALIGN; char oem[ACPI_OEM_ID_SIZE + 1]; uint8_t *ptr; /* The table is on a 16bit boundary */ for (va = va_start; va < va_end; va += PAGE_SIZE) { for (i = 0; i < n; i++) { ptr = (uint8_t *)va + i * ACPI_RSDP_ALIGN; if (memcmp(ptr, ACPI_RSDP_SIGNATURE, ACPI_RSDP_SIGNATURE_SIZE)) continue; if (!hal_acpi_rsdp_correct(ptr)) continue; goto found; } } return NULL; found: rsdp = (rsdp_t *)ptr; memcpy(&oem, rsdp->OemId, ACPI_OEM_ID_SIZE); oem[ACPI_OEM_ID_SIZE] = '\0'; x86_printf("-> OEM: %s\n", oem); if (rsdp->Revision != 0) x86_printf("[!] Using ACPI 1.0\n"); return rsdp; } /* -------------------------------------------------------------------------- */ void hal_acpi_init() { rsdp_t *rsdp; rsdt_t *rsdt; madt_t *madt; paddr_t bios_min = 0x0E0000; paddr_t bios_max = 0x100000; vaddr_t vabase; size_t npages; npages = (bios_max - bios_min) / PAGE_SIZE; vabase = hal_gpt_bootstrap_valloc(npages); hal_gpt_enter_range(vabase, bios_min, npages); /* First, find RSDP */ rsdp = hal_acpi_find_rsdp(vabase, vabase + npages * PAGE_SIZE); if (rsdp == NULL) x86_panic("RSDP not found"); /* Then, map RSDT */ rsdt = hal_acpi_map_rsdt(rsdp); if (rsdt == NULL) x86_panic("RSDT not found"); /* Now, map MADT */ madt = hal_acpi_map_madt(rsdt); if (madt == NULL) x86_panic("MADT not found"); /* Parse it */ hal_acpi_parse_madt(madt); }