From 6e6efaecdc518b9efb7118d6697439593003adf7 Mon Sep 17 00:00:00 2001 From: Jessica Hamilton Date: Thu, 24 Apr 2014 23:35:40 +1200 Subject: [PATCH] EFI: add ACPI support Also add support in the kernel to get the ACPI RSDP from the bootloader, and pass onto the ACPI driver using get_boot_item. --- .../kernel/arch/x86/arch_kernel_args.h | 2 + .../kernel/bus_managers/acpi/ACPICAHaiku.cpp | 14 +- src/system/boot/platform/efi/Jamfile | 1 + src/system/boot/platform/efi/acpi.cpp | 272 ++++++++++++++++++ src/system/boot/platform/efi/acpi.h | 22 ++ src/system/boot/platform/efi/start.cpp | 5 + src/system/kernel/arch/x86/arch_platform.cpp | 82 +++--- 7 files changed, 355 insertions(+), 43 deletions(-) create mode 100644 src/system/boot/platform/efi/acpi.cpp create mode 100644 src/system/boot/platform/efi/acpi.h diff --git a/headers/private/kernel/arch/x86/arch_kernel_args.h b/headers/private/kernel/arch/x86/arch_kernel_args.h index cada9db4a0..bd7659157b 100644 --- a/headers/private/kernel/arch/x86/arch_kernel_args.h +++ b/headers/private/kernel/arch/x86/arch_kernel_args.h @@ -38,6 +38,8 @@ typedef struct { // hpet stuff uint32 hpet_phys; FixedWidthPointer hpet; + // needed for UEFI, otherwise kernel acpi support can't find ACPI root + FixedWidthPointer acpi_root; } _PACKED arch_kernel_args; #endif /* KERNEL_ARCH_x86_KERNEL_ARGS_H */ diff --git a/src/add-ons/kernel/bus_managers/acpi/ACPICAHaiku.cpp b/src/add-ons/kernel/bus_managers/acpi/ACPICAHaiku.cpp index 00a05b7391..90bbb79e86 100644 --- a/src/add-ons/kernel/bus_managers/acpi/ACPICAHaiku.cpp +++ b/src/add-ons/kernel/bus_managers/acpi/ACPICAHaiku.cpp @@ -127,6 +127,7 @@ # include # include +# include # include # include #endif @@ -181,7 +182,7 @@ extern void *gDPCHandle; extern FILE *AcpiGbl_DebugFile; FILE *AcpiGbl_OutputFile; -static uint32 sACPIRoot = 0; +static ACPI_PHYSICAL_ADDRESS sACPIRoot = 0; static void *sInterruptHandlerData[32]; @@ -233,12 +234,15 @@ AcpiOsGetRootPointer() { #ifdef _KERNEL_MODE ACPI_PHYSICAL_ADDRESS address; - ACPI_STATUS status; + ACPI_STATUS status = AE_OK; DEBUG_FUNCTION(); if (sACPIRoot == 0) { - status = AcpiFindRootPointer(&address); - if (status == AE_OK) - sACPIRoot = address; + sACPIRoot = (ACPI_PHYSICAL_ADDRESS)get_boot_item("ACPI_ROOT_POINTER", NULL); + if (sACPIRoot == 0) { + status = AcpiFindRootPointer(&address); + if (status == AE_OK) + sACPIRoot = address; + } } return sACPIRoot; #else diff --git a/src/system/boot/platform/efi/Jamfile b/src/system/boot/platform/efi/Jamfile index f9312a794f..db60aef07f 100644 --- a/src/system/boot/platform/efi/Jamfile +++ b/src/system/boot/platform/efi/Jamfile @@ -27,6 +27,7 @@ local platform_src = entry.S mmu.cpp heap.cpp + acpi.cpp menu.cpp devices.cpp ; diff --git a/src/system/boot/platform/efi/acpi.cpp b/src/system/boot/platform/efi/acpi.cpp new file mode 100644 index 0000000000..76364ec4b8 --- /dev/null +++ b/src/system/boot/platform/efi/acpi.cpp @@ -0,0 +1,272 @@ +/* + * Copyright 2014, Jessica Hamilton, jessica.l.hamilton@gmail.com. + * Copyright 2011, Rene Gollent, rene@gollent.com. + * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved. + * Copyright 2007, Michael Lotz, mmlr@mlotz.ch + * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + * + * Copyright 2001, Travis Geiselbrecht. All rights reserved. + * Distributed under the terms of the NewOS License. +*/ + + +#include + +#include +#include + +#include +#include +#include +#include + +#include "efi_platform.h" +#include "acpi.h" +#include "mmu.h" + + +#define TRACE_ACPI +#ifdef TRACE_ACPI +# define TRACE(x) dprintf x +#else +# define TRACE(x) ; +#endif + + +static acpi_descriptor_header* sAcpiRsdt; // System Description Table +static acpi_descriptor_header* sAcpiXsdt; // Extended System Description Table +static int32 sNumEntries = -1; + + +extern "C" addr_t +mmu_map_physical_memory(addr_t physicalAddress, size_t size, uint32 flags) +{ + return physicalAddress; +} + +extern "C" void +mmu_free(void *virtualAddress, size_t size) +{ + return; +} + + +static status_t +acpi_validate_rsdp(acpi_rsdp* rsdp) +{ + const char* data = (const char*)rsdp; + unsigned char checksum = 0; + for (uint32 i = 0; i < sizeof(acpi_rsdp_legacy); i++) + checksum += data[i]; + + if ((checksum & 0xff) != 0) { + TRACE(("acpi: rsdp failed basic checksum\n")); + return B_BAD_DATA; + } + + // for ACPI 2.0+ we need to also validate the extended checksum + if (rsdp->revision > 0) { + for (uint32 i = sizeof(acpi_rsdp_legacy); + i < sizeof(acpi_rsdp_extended); i++) { + checksum += data[i]; + } + + if ((checksum & 0xff) != 0) { + TRACE(("acpi: rsdp failed extended checksum\n")); + return B_BAD_DATA; + } + } + + return B_OK; +} + + + +static status_t +acpi_validate_rsdt(acpi_descriptor_header* rsdt) +{ + const char* data = (const char*)rsdt; + unsigned char checksum = 0; + for (uint32 i = 0; i < rsdt->length; i++) + checksum += data[i]; + + return checksum == 0 ? B_OK : B_BAD_DATA; +} + + +static status_t +acpi_check_rsdt(acpi_rsdp* rsdp) +{ + if (acpi_validate_rsdp(rsdp) != B_OK) + return B_BAD_DATA; + + bool usingXsdt = false; + + TRACE(("acpi: found rsdp at %p oem id: %.6s, rev %d\n", + rsdp, rsdp->oem_id, rsdp->revision)); + TRACE(("acpi: rsdp points to rsdt at 0x%x\n", rsdp->rsdt_address)); + + uint32 length = 0; + acpi_descriptor_header* rsdt = NULL; + if (rsdp->revision > 0) { + length = rsdp->xsdt_length; + rsdt = (acpi_descriptor_header*)mmu_map_physical_memory( + (uint32)rsdp->xsdt_address, rsdp->xsdt_length, kDefaultPageFlags); + if (rsdt != NULL + && strncmp(rsdt->signature, ACPI_XSDT_SIGNATURE, 4) != 0) { + mmu_free(rsdt, rsdp->xsdt_length); + rsdt = NULL; + TRACE(("acpi: invalid extended system description table\n")); + } else + usingXsdt = true; + } + + // if we're ACPI v1 or we fail to map the XSDT for some reason, + // attempt to use the RSDT instead. + if (rsdt == NULL) { + // map and validate the root system description table + rsdt = (acpi_descriptor_header*)mmu_map_physical_memory( + rsdp->rsdt_address, sizeof(acpi_descriptor_header), + kDefaultPageFlags); + if (rsdt == NULL) { + TRACE(("acpi: couldn't map rsdt header\n")); + return B_ERROR; + } + if (strncmp(rsdt->signature, ACPI_RSDT_SIGNATURE, 4) != 0) { + mmu_free(rsdt, sizeof(acpi_descriptor_header)); + rsdt = NULL; + TRACE(("acpi: invalid root system description table\n")); + return B_ERROR; + } + + length = rsdt->length; + // Map the whole table, not just the header + TRACE(("acpi: rsdt length: %u\n", length)); + mmu_free(rsdt, sizeof(acpi_descriptor_header)); + rsdt = (acpi_descriptor_header*)mmu_map_physical_memory( + rsdp->rsdt_address, length, kDefaultPageFlags); + } + + if (rsdt != NULL) { + if (acpi_validate_rsdt(rsdt) != B_OK) { + TRACE(("acpi: rsdt failed checksum validation\n")); + mmu_free(rsdt, length); + return B_ERROR; + } else { + if (usingXsdt) + sAcpiXsdt = rsdt; + else + sAcpiRsdt = rsdt; + TRACE(("acpi: found valid %s at %p\n", + usingXsdt ? ACPI_XSDT_SIGNATURE : ACPI_RSDT_SIGNATURE, + rsdt)); + } + } else + return B_ERROR; + + return B_OK; +} + +template +acpi_descriptor_header* +acpi_find_table_generic(const char* signature, acpi_descriptor_header* acpiSdt) +{ + if (acpiSdt == NULL) + return NULL; + + if (sNumEntries == -1) { + // if using the xsdt, our entries are 64 bits wide. + sNumEntries = (acpiSdt->length + - sizeof(acpi_descriptor_header)) + / sizeof(PointerType); + } + + if (sNumEntries <= 0) { + TRACE(("acpi: root system description table is empty\n")); + return NULL; + } + + TRACE(("acpi: searching %d entries for table '%.4s'\n", sNumEntries, + signature)); + + PointerType* pointer = (PointerType*)((uint8*)acpiSdt + + sizeof(acpi_descriptor_header)); + + acpi_descriptor_header* header = NULL; + for (int32 j = 0; j < sNumEntries; j++, pointer++) { + header = (acpi_descriptor_header*) + mmu_map_physical_memory((uint32)*pointer, + sizeof(acpi_descriptor_header), kDefaultPageFlags); + + if (header == NULL + || strncmp(header->signature, signature, 4) != 0) { + // not interesting for us + TRACE(("acpi: Looking for '%.4s'. Skipping '%.4s'\n", + signature, header != NULL ? header->signature : "null")); + + if (header != NULL) { + mmu_free(header, sizeof(acpi_descriptor_header)); + header = NULL; + } + + continue; + } + + TRACE(("acpi: Found '%.4s' @ %p\n", signature, pointer)); + break; + } + + + if (header == NULL) + return NULL; + + // Map the whole table, not just the header + uint32 length = header->length; + mmu_free(header, sizeof(acpi_descriptor_header)); + + return (acpi_descriptor_header*)mmu_map_physical_memory( + (uint32)*pointer, length, kDefaultPageFlags); +} + + +acpi_descriptor_header* +acpi_find_table(const char* signature) +{ + if (sAcpiRsdt != NULL) + return acpi_find_table_generic(signature, sAcpiRsdt); + else if (sAcpiXsdt != NULL) + return acpi_find_table_generic(signature, sAcpiXsdt); + + return NULL; +} + + +void +acpi_init() +{ + EFI_GUID acpi = ACPI_20_TABLE_GUID; + EFI_CONFIGURATION_TABLE *table = kSystemTable->ConfigurationTable; + UINTN entries = kSystemTable->NumberOfTableEntries; + + // Try to find the ACPI RSDP. + for (uint32 i = 0; i < entries; i++) { + acpi_rsdp *rsdp = NULL; + + EFI_GUID vendor = table[i].VendorGuid; + + if (vendor.Data1 == acpi.Data1 + && vendor.Data2 == acpi.Data2 + && vendor.Data3 == acpi.Data3 + && strncmp((char *)vendor.Data4, (char *)acpi.Data4, 8) == 0) { + rsdp = (acpi_rsdp *)(table[i].VendorTable); + if (strncmp((char *)rsdp, ACPI_RSDP_SIGNATURE, 8) == 0) + TRACE(("acpi_init: found ACPI RSDP signature at %p\n", rsdp)); + + if (rsdp != NULL && acpi_check_rsdt(rsdp) == B_OK) { + gKernelArgs.arch_args.acpi_root = rsdp; + break; + } + } + } +} diff --git a/src/system/boot/platform/efi/acpi.h b/src/system/boot/platform/efi/acpi.h new file mode 100644 index 0000000000..8d45c4b4fc --- /dev/null +++ b/src/system/boot/platform/efi/acpi.h @@ -0,0 +1,22 @@ +/* + * Copyright 2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef ACPI_H +#define ACPI_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +acpi_descriptor_header *acpi_find_table(const char *signature); +void acpi_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* ACPI_H */ diff --git a/src/system/boot/platform/efi/start.cpp b/src/system/boot/platform/efi/start.cpp index 3e07eea0b8..43f5155822 100644 --- a/src/system/boot/platform/efi/start.cpp +++ b/src/system/boot/platform/efi/start.cpp @@ -13,11 +13,13 @@ #include #include +#include #include #include #include #include +#include "acpi.h" #include "console.h" #include "efi_platform.h" #include "mmu.h" @@ -270,6 +272,9 @@ efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systemTable) // disable apm in case we ever load a 32-bit kernel... gKernelArgs.platform_args.apm.version = 0; + + acpi_init(); + gKernelArgs.num_cpus = 1; gKernelArgs.arch_args.hpet_phys = 0; gKernelArgs.arch_args.hpet = NULL; diff --git a/src/system/kernel/arch/x86/arch_platform.cpp b/src/system/kernel/arch/x86/arch_platform.cpp index d967139701..ac15e227df 100644 --- a/src/system/kernel/arch/x86/arch_platform.cpp +++ b/src/system/kernel/arch/x86/arch_platform.cpp @@ -1,38 +1,44 @@ -/* - * Copyright 2006, Haiku, Inc. All Rights Reserved. - * Distributed under the terms of the MIT License. - * - * Authors: - * Ingo Weinhold - * Axel Dörfler, axeld@pinc-software.de - */ - - -#include -#include - - -status_t -arch_platform_init(struct kernel_args *args) -{ - return B_OK; -} - - -status_t -arch_platform_init_post_vm(struct kernel_args *args) -{ - return B_OK; -} - - -status_t -arch_platform_init_post_thread(struct kernel_args *args) -{ - // APM is not supported on x86_64. -#ifndef __x86_64__ - apm_init(args); -#endif - return B_OK; -} - +/* + * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + * Axel Dörfler, axeld@pinc-software.de + */ + + +#include +#include +#include +#include + + +status_t +arch_platform_init(struct kernel_args *args) +{ + return B_OK; +} + + +status_t +arch_platform_init_post_vm(struct kernel_args *args) +{ + // Now we can add boot items; pass on the ACPI root pointer + add_boot_item("ACPI_ROOT_POINTER", + args->arch_args.acpi_root.Pointer(), sizeof(void*)); + + return B_OK; +} + + +status_t +arch_platform_init_post_thread(struct kernel_args *args) +{ + // APM is not supported on x86_64. +#ifndef __x86_64__ + apm_init(args); +#endif + return B_OK; +} +