X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/060df5ea7c632b1ac8cc8aac1fb59758165c2084..c3c9b80d004dbbfdf763edeb97968c6997e3b45b:/osfmk/i386/acpi.c diff --git a/osfmk/i386/acpi.c b/osfmk/i386/acpi.c index fb2cbe334..2af7ed288 100644 --- a/osfmk/i386/acpi.c +++ b/osfmk/i386/acpi.c @@ -1,8 +1,8 @@ /* - * Copyright (c) 2000-2009 Apple Inc. All rights reserved. + * Copyright (c) 2000-2018 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -11,10 +11,10 @@ * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. - * + * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -22,7 +22,7 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ @@ -32,10 +32,16 @@ #include #include #include +#if CONFIG_MTRR #include +#endif +#if HYPERVISOR +#include +#endif #if CONFIG_VMX #include #endif +#include #include #include #include @@ -49,23 +55,48 @@ #include +#define UINT64 uint64_t +#define UINT32 uint32_t +#define UINT16 uint16_t +#define UINT8 uint8_t +#define RSDP_VERSION_ACPI10 0 +#define RSDP_VERSION_ACPI20 2 +#include +#include +#include + #include +#include +#include #include +#include #include #if HIBERNATION #include #endif #include - #include +#if MONOTONIC +#include +#endif /* MONOTONIC */ + #if CONFIG_SLEEP -extern void acpi_sleep_cpu(acpi_sleep_callback, void * refcon); -extern void acpi_wake_prot(void); +extern void acpi_sleep_cpu(acpi_sleep_callback, void * refcon); +extern void acpi_wake_prot(void); #endif +extern kern_return_t IOCPURunPlatformQuiesceActions(void); +extern kern_return_t IOCPURunPlatformActiveActions(void); +extern kern_return_t IOCPURunPlatformHaltRestartActions(uint32_t message); + +extern void fpinit(void); -extern void fpinit(void); +#if DEVELOPMENT || DEBUG +#define DBG(x...) kprintf(x) +#else +#define DBG(x...) +#endif vm_offset_t acpi_install_wake_handler(void) @@ -78,6 +109,14 @@ acpi_install_wake_handler(void) #endif } +#if CONFIG_SLEEP + +unsigned int save_kdebug_enable = 0; +static uint64_t acpi_sleep_abstime; +static uint64_t acpi_idle_abstime; +static uint64_t acpi_wake_abstime, acpi_wake_postrebase_abstime; +boolean_t deep_idle_rebase = TRUE; + #if HIBERNATION struct acpi_hibernate_callback_data { acpi_sleep_callback func; @@ -85,67 +124,57 @@ struct acpi_hibernate_callback_data { }; typedef struct acpi_hibernate_callback_data acpi_hibernate_callback_data_t; -unsigned int save_kdebug_enable = 0; -static uint64_t acpi_sleep_abstime; - - -#if CONFIG_SLEEP static void acpi_hibernate(void *refcon) { uint32_t mode; acpi_hibernate_callback_data_t *data = - (acpi_hibernate_callback_data_t *)refcon; - - if (current_cpu_datap()->cpu_hibernate) - { -#if defined(__i386__) - cpu_IA32e_enable(current_cpu_datap()); -#endif + (acpi_hibernate_callback_data_t *)refcon; + if (current_cpu_datap()->cpu_hibernate) { mode = hibernate_write_image(); - if( mode == kIOHibernatePostWriteHalt ) - { + if (mode == kIOHibernatePostWriteHalt) { // off HIBLOG("power off\n"); - if (PE_halt_restart) (*PE_halt_restart)(kPEHaltCPU); - } - else if( mode == kIOHibernatePostWriteRestart ) - { + IOCPURunPlatformHaltRestartActions(kPEHaltCPU); + if (PE_halt_restart) { + (*PE_halt_restart)(kPEHaltCPU); + } + } else if (mode == kIOHibernatePostWriteRestart) { // restart HIBLOG("restart\n"); - if (PE_halt_restart) (*PE_halt_restart)(kPERestartCPU); - } - else - { + IOCPURunPlatformHaltRestartActions(kPERestartCPU); + if (PE_halt_restart) { + (*PE_halt_restart)(kPERestartCPU); + } + } else { // sleep HIBLOG("sleep\n"); - + // should we come back via regular wake, set the state in memory. - cpu_datap(0)->cpu_hibernate = 0; + cpu_datap(0)->cpu_hibernate = 0; } + } -#if defined(__i386__) - /* - * If we're in 64-bit mode, drop back into legacy mode during sleep. - */ - cpu_IA32e_disable(current_cpu_datap()); +#if CONFIG_VMX + vmx_suspend(); #endif - } kdebug_enable = 0; + IOCPURunPlatformQuiesceActions(); + acpi_sleep_abstime = mach_absolute_time(); (data->func)(data->refcon); /* should never get here! */ } -#endif /* CONFIG_SLEEP */ #endif /* HIBERNATION */ +#endif /* CONFIG_SLEEP */ -extern void slave_pstart(void); +extern void slave_pstart(void); void acpi_sleep_kernel(acpi_sleep_callback func, void *refcon) @@ -154,57 +183,56 @@ acpi_sleep_kernel(acpi_sleep_callback func, void *refcon) acpi_hibernate_callback_data_t data; #endif boolean_t did_hibernate; - unsigned int cpu; - kern_return_t rc; - unsigned int my_cpu; - uint64_t now; - uint64_t my_tsc; - uint64_t my_abs; - - kprintf("acpi_sleep_kernel hib=%d\n", - current_cpu_datap()->cpu_hibernate); - - /* Get all CPUs to be in the "off" state */ - my_cpu = cpu_number(); + cpu_data_t *cdp = current_cpu_datap(); + unsigned int cpu; + kern_return_t rc; + unsigned int my_cpu; + uint64_t start; + uint64_t elapsed = 0; + uint64_t elapsed_trace_start = 0; + + my_cpu = cpu_number(); + kprintf("acpi_sleep_kernel hib=%d, cpu=%d\n", cdp->cpu_hibernate, + my_cpu); + + /* Get all CPUs to be in the "off" state */ for (cpu = 0; cpu < real_ncpus; cpu += 1) { - if (cpu == my_cpu) + if (cpu == my_cpu) { continue; + } rc = pmCPUExitHaltToOff(cpu); - if (rc != KERN_SUCCESS) + if (rc != KERN_SUCCESS) { panic("Error %d trying to transition CPU %d to OFF", - rc, cpu); + rc, cpu); + } } - /* shutdown local APIC before passing control to BIOS */ - lapic_shutdown(); + /* shutdown local APIC before passing control to firmware */ + lapic_shutdown(true); #if HIBERNATION data.func = func; data.refcon = refcon; #endif +#if MONOTONIC + mt_cpu_down(cdp); +#endif /* MONOTONIC */ + /* Save power management timer state */ pmTimerSave(); -#if CONFIG_VMX - /* - * Turn off VT, otherwise switching to legacy mode will fail - */ - vmx_suspend(); +#if HYPERVISOR + /* Notify hypervisor that we are about to sleep */ + hv_suspend(); #endif -#if defined(__i386__) - /* - * If we're in 64-bit mode, drop back into legacy mode during sleep. - */ - cpu_IA32e_disable(current_cpu_datap()); -#endif /* * Enable FPU/SIMD unit for potential hibernate acceleration */ - clear_ts(); + clear_ts(); - KERNEL_DEBUG_CONSTANT(IOKDBG_CODE(DBG_HIBERNATE, 0) | DBG_FUNC_START, 0, 0, 0, 0, 0); + KDBG(IOKDBG_CODE(DBG_HIBERNATE, 0) | DBG_FUNC_START); save_kdebug_enable = kdebug_enable; kdebug_enable = 0; @@ -217,18 +245,25 @@ acpi_sleep_kernel(acpi_sleep_callback func, void *refcon) * Will not return until platform is woken up, * or if sleep failed. */ -#ifdef __x86_64__ uint64_t old_cr3 = x86_64_pre_sleep(); -#endif #if HIBERNATION acpi_sleep_cpu(acpi_hibernate, &data); #else +#if CONFIG_VMX + vmx_suspend(); +#endif acpi_sleep_cpu(func, refcon); #endif -#ifdef __x86_64__ + acpi_wake_abstime = mach_absolute_time(); + /* Rebase TSC->absolute time conversion, using timestamp + * recorded before sleep. + */ + rtc_nanotime_init(acpi_sleep_abstime); + acpi_wake_postrebase_abstime = start = mach_absolute_time(); + assert(start >= acpi_sleep_abstime); + x86_64_post_sleep(old_cr3); -#endif #endif /* CONFIG_SLEEP */ @@ -237,77 +272,91 @@ acpi_sleep_kernel(acpi_sleep_callback func, void *refcon) * for compatibility with firewire kprintf. */ - if (FALSE == disable_serial_output) - serial_init(); + if (FALSE == disable_serial_output) { + pal_serial_init(); + } #if HIBERNATION if (current_cpu_datap()->cpu_hibernate) { -#if defined(__i386__) - int i; - for (i = 0; i < PMAP_NWINDOWS; i++) - *current_cpu_datap()->cpu_pmap->mapwindow[i].prv_CMAP = 0; -#endif did_hibernate = TRUE; - } else -#endif +#endif { did_hibernate = FALSE; } - /* Re-enable mode (including 64-bit if applicable) */ - cpu_mode_init(current_cpu_datap()); + /* Re-enable fast syscall */ + cpu_syscall_init(current_cpu_datap()); #if CONFIG_MCA /* Re-enable machine check handling */ mca_cpu_init(); #endif +#if CONFIG_MTRR /* restore MTRR settings */ mtrr_update_cpu(); +#endif + + /* update CPU microcode and apply CPU workarounds */ + ucode_update_wake_and_apply_cpu_was(); + +#if CONFIG_MTRR + /* set up PAT following boot processor power up */ + pat_init(); +#endif #if CONFIG_VMX - /* + /* * Restore VT mode */ - vmx_resume(); + vmx_resume(did_hibernate); #endif - /* set up PAT following boot processor power up */ - pat_init(); - /* * Go through all of the CPUs and mark them as requiring * a full restart. */ pmMarkAllCPUsOff(); - ml_get_timebase(&now); /* re-enable and re-init local apic (prior to starting timers) */ - if (lapic_probe()) - lapic_configure(); + if (lapic_probe()) { + lapic_configure(true); + } + +#if KASAN + /* + * The sleep implementation uses indirect noreturn calls, so we miss stack + * unpoisoning. Do it explicitly. + */ + kasan_unpoison_curstack(true); +#endif - /* let the realtime clock reset */ - rtc_sleep_wakeup(acpi_sleep_abstime); + elapsed += mach_absolute_time() - start; + rtc_decrementer_configure(); kdebug_enable = save_kdebug_enable; - if (did_hibernate) { - - my_tsc = (now >> 32) | (now << 32); - my_abs = tmrCvt(my_tsc, tscFCvtt2n); + if (kdebug_enable == 0) { + elapsed_trace_start += kdebug_wake(); + } + start = mach_absolute_time(); - KERNEL_DEBUG_CONSTANT(IOKDBG_CODE(DBG_HIBERNATE, 2) | DBG_FUNC_START, - (uint32_t)(my_abs >> 32), (uint32_t)my_abs, 0, 0, 0); - hibernate_machine_init(); - KERNEL_DEBUG_CONSTANT(IOKDBG_CODE(DBG_HIBERNATE, 2) | DBG_FUNC_END, 0, 0, 0, 0, 0); + /* Reconfigure FP/SIMD unit */ + init_fpu(); + clear_ts(); - current_cpu_datap()->cpu_hibernate = 0; - KERNEL_DEBUG_CONSTANT(IOKDBG_CODE(DBG_HIBERNATE, 0) | DBG_FUNC_END, 0, 0, 0, 0, 0); - } else - KERNEL_DEBUG_CONSTANT(IOKDBG_CODE(DBG_HIBERNATE, 0) | DBG_FUNC_END, 0, 0, 0, 0, 0); +#if HYPERVISOR + /* Notify hypervisor that we are about to resume */ + hv_resume(); +#endif + + IOCPURunPlatformActiveActions(); + + KDBG(IOKDBG_CODE(DBG_HIBERNATE, 0) | DBG_FUNC_END, start, elapsed, + elapsed_trace_start, acpi_wake_abstime); /* Restore power management register state */ pmCPUMarkRunning(current_cpu_datap()); @@ -318,28 +367,147 @@ acpi_sleep_kernel(acpi_sleep_callback func, void *refcon) /* Restart timer interrupts */ rtc_timer_start(); - /* Reconfigure FP/SIMD unit */ - init_fpu(); +#if MONOTONIC + mt_cpu_up(cdp); +#endif /* MONOTONIC */ #if HIBERNATION -#ifdef __i386__ - /* The image is written out using the copy engine, which disables - * preemption. Since the copy engine writes out the page which contains - * the preemption variable when it is disabled, we need to explicitly - * enable it here */ - if (did_hibernate) - enable_preemption(); -#endif - kprintf("ret from acpi_sleep_cpu hib=%d\n", did_hibernate); -#endif +#endif /* HIBERNATION */ #if CONFIG_SLEEP /* Becase we don't save the bootstrap page, and we share it - * between sleep and mp slave init, we need to recreate it + * between sleep and mp slave init, we need to recreate it * after coming back from sleep or hibernate */ install_real_mode_bootstrap(slave_pstart); -#endif +#endif /* CONFIG_SLEEP */ +} + +void +ml_hibernate_active_pre(void) +{ +#if HIBERNATION + hibernate_rebuild_vm_structs(); +#endif /* HIBERNATION */ +} + +void +ml_hibernate_active_post(void) +{ +#if HIBERNATION + if (current_cpu_datap()->cpu_hibernate) { + KDBG(IOKDBG_CODE(DBG_HIBERNATE, 2) | DBG_FUNC_START); + hibernate_machine_init(); + KDBG(IOKDBG_CODE(DBG_HIBERNATE, 2) | DBG_FUNC_END); + current_cpu_datap()->cpu_hibernate = 0; + } +#endif /* HIBERNATION */ +} + +/* + * acpi_idle_kernel is called by the ACPI Platform kext to request the kernel + * to idle the boot processor in the deepest C-state for S0 sleep. All slave + * processors are expected already to have been offlined in the deepest C-state. + * + * The contract with ACPI is that although the kernel is called with interrupts + * disabled, interrupts may need to be re-enabled to dismiss any pending timer + * interrupt. However, the callback function will be called once this has + * occurred and interrupts are guaranteed to be disabled at that time, + * and to remain disabled during C-state entry, exit (wake) and return + * from acpi_idle_kernel. + */ +void +acpi_idle_kernel(acpi_sleep_callback func, void *refcon) +{ + boolean_t istate = ml_get_interrupts_enabled(); + + kprintf("acpi_idle_kernel, cpu=%d, interrupts %s\n", + cpu_number(), istate ? "enabled" : "disabled"); + + assert(cpu_number() == master_cpu); + +#if MONOTONIC + mt_cpu_down(cpu_datap(0)); +#endif /* MONOTONIC */ + + /* Cancel any pending deadline */ + setPop(0); + while (lapic_is_interrupting(LAPIC_TIMER_VECTOR) +#if MONOTONIC + || lapic_is_interrupting(LAPIC_VECTOR(PERFCNT)) +#endif /* MONOTONIC */ + ) { + (void) ml_set_interrupts_enabled(TRUE); + setPop(0); + ml_set_interrupts_enabled(FALSE); + } + + if (current_cpu_datap()->cpu_hibernate) { + /* Call hibernate_write_image() to put disk to low power state */ + hibernate_write_image(); + cpu_datap(0)->cpu_hibernate = 0; + } + + /* + * Call back to caller to indicate that interrupts will remain + * disabled while we deep idle, wake and return. + */ + IOCPURunPlatformQuiesceActions(); + + func(refcon); + + acpi_idle_abstime = mach_absolute_time(); + + KERNEL_DEBUG_CONSTANT( + MACHDBG_CODE(DBG_MACH_SCHED, MACH_DEEP_IDLE) | DBG_FUNC_START, + acpi_idle_abstime, deep_idle_rebase, 0, 0, 0); + + /* + * Disable tracing during S0-sleep + * unless overridden by sysctl -w tsc.deep_idle_rebase=0 + */ + if (deep_idle_rebase) { + save_kdebug_enable = kdebug_enable; + kdebug_enable = 0; + } + + /* + * Call into power-management to enter the lowest C-state. + * Note when called on the boot processor this routine will + * return directly when awoken. + */ + pmCPUHalt(PM_HALT_SLEEP); + + /* + * Get wakeup time relative to the TSC which has progressed. + * Then rebase nanotime to reflect time not progressing over sleep + * - unless overriden so that tracing can occur during deep_idle. + */ + acpi_wake_abstime = mach_absolute_time(); + if (deep_idle_rebase) { + rtc_sleep_wakeup(acpi_idle_abstime); + kdebug_enable = save_kdebug_enable; + } + acpi_wake_postrebase_abstime = mach_absolute_time(); + assert(mach_absolute_time() >= acpi_idle_abstime); + + KERNEL_DEBUG_CONSTANT( + MACHDBG_CODE(DBG_MACH_SCHED, MACH_DEEP_IDLE) | DBG_FUNC_END, + acpi_wake_abstime, acpi_wake_abstime - acpi_idle_abstime, 0, 0, 0); + +#if MONOTONIC + mt_cpu_up(cpu_datap(0)); +#endif /* MONOTONIC */ + + /* Like S3 sleep, turn on tracing if trace_wake boot-arg is present */ + if (kdebug_enable == 0) { + kdebug_wake(); + } + + IOCPURunPlatformActiveActions(); + + /* Restart timer interrupts */ + rtc_timer_start(); } extern char real_mode_bootstrap_end[]; @@ -356,18 +524,223 @@ install_real_mode_bootstrap(void *prot_entry) * mode and then jumping to the common startup, _start(). */ bcopy_phys(kvtophys((vm_offset_t) real_mode_bootstrap_base), - (addr64_t) REAL_MODE_BOOTSTRAP_OFFSET, - real_mode_bootstrap_end-real_mode_bootstrap_base); + (addr64_t) REAL_MODE_BOOTSTRAP_OFFSET, + real_mode_bootstrap_end - real_mode_bootstrap_base); /* * Set the location at the base of the stack to point to the * common startup entry. */ ml_phys_write_word( - PROT_MODE_START+REAL_MODE_BOOTSTRAP_OFFSET, + PROT_MODE_START + REAL_MODE_BOOTSTRAP_OFFSET, (unsigned int)kvtophys((vm_offset_t)prot_entry)); - + /* Flush caches */ __asm__("wbinvd"); } +boolean_t +ml_recent_wake(void) +{ + uint64_t ctime = mach_absolute_time(); + assert(ctime > acpi_wake_postrebase_abstime); + return (ctime - acpi_wake_postrebase_abstime) < 5 * NSEC_PER_SEC; +} + +static uint8_t +cksum8(uint8_t *ptr, uint32_t size) +{ + uint8_t sum = 0; + uint32_t i; + + for (i = 0; i < size; i++) { + sum += ptr[i]; + } + + return sum; +} + +/* + * Parameterized search for a specified table given an sdtp (either RSDT or XSDT). + * Note that efiboot does not modify the addresses of tables in the RSDT or XSDT + * TableOffsetEntry array, so we do not need to "convert" from efiboot virtual to + * physical. + */ +#define SEARCH_FOR_ACPI_TABLE(sdtp, signature, entry_type) \ +{ \ + uint32_t i, pointer_count; \ + \ + /* Walk the list of tables in the *SDT, looking for the signature passed in */ \ + pointer_count = ((sdtp)->Length - sizeof(ACPI_TABLE_HEADER)) / sizeof(entry_type); \ + \ + for (i = 0; i < pointer_count; i++) { \ + ACPI_TABLE_HEADER *next_table = \ + (ACPI_TABLE_HEADER *)PHYSMAP_PTOV( \ + (uintptr_t)(sdtp)->TableOffsetEntry[i]); \ + if (strncmp(&next_table->Signature[0], (signature), 4) == 0) { \ + /* \ + * Checksum the table first, then return it if the checksum \ + * is valid. \ + */ \ + if (cksum8((uint8_t *)next_table, next_table->Length) == 0) { \ + return next_table; \ + } else { \ + DBG("Invalid checksum for table [%s]@0x%lx!\n", (signature), \ + (unsigned long)(sdtp)->TableOffsetEntry[i]); \ + return NULL; \ + } \ + } \ + } \ + \ + return NULL; \ +} + +static ACPI_TABLE_HEADER * +acpi_find_table_via_xsdt(XSDT_DESCRIPTOR *xsdtp, const char *signature) +{ + SEARCH_FOR_ACPI_TABLE(xsdtp, signature, UINT64); +} + +static ACPI_TABLE_HEADER * +acpi_find_table_via_rsdt(RSDT_DESCRIPTOR *rsdtp, const char *signature) +{ + SEARCH_FOR_ACPI_TABLE(rsdtp, signature, UINT32); +} + +/* + * Returns a pointer to an ACPI table header corresponding to the table + * whose signature is passed in, or NULL if no such table could be found. + */ +static ACPI_TABLE_HEADER * +acpi_find_table(uintptr_t rsdp_physaddr, const char *signature) +{ + static RSDP_DESCRIPTOR *rsdp = NULL; + static XSDT_DESCRIPTOR *xsdtp = NULL; + static RSDT_DESCRIPTOR *rsdtp = NULL; + + if (signature == NULL) { + DBG("Invalid NULL signature passed to acpi_find_table\n"); + return NULL; + } + + /* + * RSDT or XSDT is required; without it, we cannot locate other tables. + */ + if (__improbable(rsdp == NULL || (rsdtp == NULL && xsdtp == NULL))) { + rsdp = PHYSMAP_PTOV(rsdp_physaddr); + + /* Verify RSDP signature */ + if (__improbable(strncmp((void *)rsdp, "RSD PTR ", 8) != 0)) { + DBG("RSDP signature mismatch: Aborting acpi_find_table\n"); + rsdp = NULL; + return NULL; + } + + /* Verify RSDP checksum */ + if (__improbable(cksum8((uint8_t *)rsdp, sizeof(RSDP_DESCRIPTOR)) != 0)) { + DBG("RSDP@0x%lx signature mismatch: Aborting acpi_find_table\n", + (unsigned long)rsdp_physaddr); + rsdp = NULL; + return NULL; + } + + /* Ensure the revision of the RSDP indicates the presence of an RSDT or XSDT */ + if (__improbable(rsdp->Revision >= RSDP_VERSION_ACPI20 && rsdp->XsdtPhysicalAddress == 0ULL)) { + DBG("RSDP XSDT Physical Address is 0!: Aborting acpi_find_table\n"); + rsdp = NULL; + return NULL; + } else if (__probable(rsdp->Revision >= RSDP_VERSION_ACPI20)) { + /* XSDT (with 64-bit pointers to tables) */ + rsdtp = NULL; + xsdtp = PHYSMAP_PTOV(rsdp->XsdtPhysicalAddress); + if (cksum8((uint8_t *)xsdtp, xsdtp->Length) != 0) { + DBG("ERROR: XSDT@0x%lx checksum is non-zero; not using this XSDT\n", + (unsigned long)rsdp->XsdtPhysicalAddress); + xsdtp = NULL; + return NULL; + } + } else if (__improbable(rsdp->Revision == RSDP_VERSION_ACPI10 && rsdp->RsdtPhysicalAddress == 0)) { + DBG("RSDP RSDT Physical Address is 0!: Aborting acpi_find_table\n"); + rsdp = NULL; + return NULL; + } else if (__improbable(rsdp->Revision == RSDP_VERSION_ACPI10)) { + /* RSDT (with 32-bit pointers to tables) */ + xsdtp = NULL; + rsdtp = PHYSMAP_PTOV((uintptr_t)rsdp->RsdtPhysicalAddress); + if (cksum8((uint8_t *)rsdtp, rsdtp->Length) != 0) { + DBG("ERROR: RSDT@0x%lx checksum is non-zero; not using this RSDT\n", + (unsigned long)rsdp->RsdtPhysicalAddress); + rsdtp = NULL; + return NULL; + } + } else { + DBG("Unrecognized RSDP Revision (0x%x): Aborting acpi_find_table\n", + rsdp->Revision); + rsdp = NULL; + return NULL; + } + } + + assert(xsdtp != NULL || rsdtp != NULL); + + if (__probable(xsdtp != NULL)) { + return acpi_find_table_via_xsdt(xsdtp, signature); + } else if (rsdtp != NULL) { + return acpi_find_table_via_rsdt(rsdtp, signature); + } + + return NULL; +} + +/* + * Returns the count of enabled logical processors present in the ACPI + * MADT, or 0 if the MADT could not be located. + */ +uint32_t +acpi_count_enabled_logical_processors(void) +{ + MULTIPLE_APIC_TABLE *madtp; + void *end_ptr; + APIC_HEADER *next_apic_entryp; + uint32_t enabled_cpu_count = 0; + uint64_t rsdp_physaddr; + + rsdp_physaddr = efi_get_rsdp_physaddr(); + if (__improbable(rsdp_physaddr == 0)) { + DBG("acpi_count_enabled_logical_processors: Could not get RSDP physaddr from EFI.\n"); + return 0; + } + + madtp = (MULTIPLE_APIC_TABLE *)acpi_find_table(rsdp_physaddr, ACPI_SIG_MADT); + + if (__improbable(madtp == NULL)) { + DBG("acpi_count_enabled_logical_processors: Could not find the MADT.\n"); + return 0; + } + + end_ptr = (void *)((uintptr_t)madtp + madtp->Length); + next_apic_entryp = (APIC_HEADER *)((uintptr_t)madtp + sizeof(MULTIPLE_APIC_TABLE)); + + while ((void *)next_apic_entryp < end_ptr) { + switch (next_apic_entryp->Type) { + case APIC_PROCESSOR: + { + MADT_PROCESSOR_APIC *madt_procp = (MADT_PROCESSOR_APIC *)next_apic_entryp; + if (madt_procp->ProcessorEnabled) { + enabled_cpu_count++; + } + + break; + } + + default: + DBG("Ignoring MADT entry type 0x%x length 0x%x\n", next_apic_entryp->Type, + next_apic_entryp->Length); + break; + } + + next_apic_entryp = (APIC_HEADER *)((uintptr_t)next_apic_entryp + next_apic_entryp->Length); + } + + return enabled_cpu_count; +}