/* * 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 /* XXX XXX libk XXX XXX */ int memcmp(char *s1, char *s2, size_t n) { size_t i; for(i = 0; i < n; i++) { if (s1[i] != s2[i]) return 1; } return 0; } /* -------------------------------------------------------------------------- */ #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_madt(madt_t *madt) { extern paddr_t lapic_pa; lapic_pa = (paddr_t)madt->Address; x86_printf("-> LAPIC address: %Z\n", lapic_pa); } 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); }