From 3d840c42898146d6928f3add9a814fd2e6df9922 Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Mon, 17 Feb 2025 17:39:35 -0500 Subject: [PATCH] kernel/x86: Determine the APIC frequency in the kernel instead of the bootloader. We use the TSC to measure the APIC frequency, but the TSC frequency is only fully initialized in the kernel (where it's read from CPUID on recent hardware). Furthermore, on some more recent systems, it seems there may only be X2APIC and no MMIO APIC timer at all, and the bootloader does not handle that case. The method in the kernel uses spin() instead of a fixed instruction count as well as system_time_nsecs() and a double for (hopefully) more accurate calibration. At least on VMware, this method seems more accurate: the APIC frequency read from the hypervisor CPUID leaf is 66,000,000; the old method in in the bootloader yielded values like "65,801,075" and "65,106,382", while the new one yields values like "65,963,920" and "65,962,580" (those pairs of values are from two consecutive boots.) The difference was sometimes similar (but smaller as a percentage) in QEMU with software emulation: e.g. "993,218,085" vs. "992,965,761". But sometimes it wasn't: e.g "991,619,585" vs. "993,689,669". (QEMU in software mode doesn't report a frequency via hypervisor CPUID leaf.) Change-Id: I4fb8535d1d984f13867e2f84e7dfad1ceed42c13 Reviewed-on: https://review.haiku-os.org/c/haiku/+/8999 Tested-by: Commit checker robot Reviewed-by: waddlesplash --- headers/private/kernel/arch/x86/apic.h | 1 + .../kernel/arch/x86/arch_kernel_args.h | 2 +- src/system/boot/platform/bios_ia32/smp.cpp | 40 --------------- src/system/boot/platform/bios_ia32/support.S | 23 --------- src/system/boot/platform/efi/arch/x86/Jamfile | 1 - .../boot/platform/efi/arch/x86/arch_smp.cpp | 42 ---------------- .../boot/platform/efi/arch/x86/support.S | 35 ------------- .../boot/platform/efi/arch/x86_64/Jamfile | 1 - .../boot/platform/efi/arch/x86_64/support.S | 31 ------------ src/system/kernel/arch/x86/apic.cpp | 10 ++++ .../kernel/arch/x86/timers/x86_apic.cpp | 49 ++++++++++++++++++- 11 files changed, 59 insertions(+), 176 deletions(-) delete mode 100644 src/system/boot/platform/efi/arch/x86/support.S delete mode 100644 src/system/boot/platform/efi/arch/x86_64/support.S diff --git a/headers/private/kernel/arch/x86/apic.h b/headers/private/kernel/arch/x86/apic.h index 23605e6c8c..0a3ef8d547 100644 --- a/headers/private/kernel/arch/x86/apic.h +++ b/headers/private/kernel/arch/x86/apic.h @@ -131,6 +131,7 @@ uint32 apic_lvt_error(); void apic_set_lvt_error(uint32 config); uint32 apic_lvt_initial_timer_count(); void apic_set_lvt_initial_timer_count(uint32 config); +uint32 apic_lvt_current_timer_count(); uint32 apic_lvt_timer_divide_config(); void apic_set_lvt_timer_divide_config(uint32 config); diff --git a/headers/private/kernel/arch/x86/arch_kernel_args.h b/headers/private/kernel/arch/x86/arch_kernel_args.h index e9d2e83775..91be4cd561 100644 --- a/headers/private/kernel/arch/x86/arch_kernel_args.h +++ b/headers/private/kernel/arch/x86/arch_kernel_args.h @@ -29,7 +29,7 @@ typedef struct { uint64 virtual_end; uint64 page_hole; // smp stuff - uint32 apic_time_cv_factor; // apic ticks per second + uint32 _unused; // previously: apic ticks per second uint32 apic_phys; FixedWidthPointer apic; uint32 ioapic_phys; diff --git a/src/system/boot/platform/bios_ia32/smp.cpp b/src/system/boot/platform/bios_ia32/smp.cpp index 3286b005c6..68acf3f830 100644 --- a/src/system/boot/platform/bios_ia32/smp.cpp +++ b/src/system/boot/platform/bios_ia32/smp.cpp @@ -44,8 +44,6 @@ static struct scan_spots_struct smp_scan_spots[] = { { 0, 0, 0 } }; -extern "C" void execute_n_instructions(int count); - extern "C" void smp_trampoline(void); extern "C" void smp_trampoline_end(void); @@ -319,41 +317,6 @@ smp_do_acpi_config(void) } -static void -calculate_apic_timer_conversion_factor(void) -{ - int64 t1, t2; - uint32 config; - uint32 count; - - // setup the timer - config = apic_read(APIC_LVT_TIMER); - config = (config & APIC_LVT_TIMER_MASK) + APIC_LVT_MASKED; - // timer masked, vector 0 - apic_write(APIC_LVT_TIMER, config); - - config = (apic_read(APIC_TIMER_DIVIDE_CONFIG) & ~0x0000000f); - apic_write(APIC_TIMER_DIVIDE_CONFIG, config | APIC_TIMER_DIVIDE_CONFIG_1); - // divide clock by one - - t1 = system_time(); - apic_write(APIC_INITIAL_TIMER_COUNT, 0xffffffff); // start the counter - - execute_n_instructions(128 * 20000); - - count = apic_read(APIC_CURRENT_TIMER_COUNT); - t2 = system_time(); - - count = 0xffffffff - count; - - gKernelArgs.arch_args.apic_time_cv_factor - = (uint32)((1000000.0/(t2 - t1)) * count); - - dprintf("APIC ticks/sec = %d\n", - gKernelArgs.arch_args.apic_time_cv_factor); -} - - // #pragma mark - @@ -403,9 +366,6 @@ smp_init_other_cpus(void) dprintf("smp: apic (mapped) = %p\n", (void *)gKernelArgs.arch_args.apic); - // calculate how fast the apic timer is - calculate_apic_timer_conversion_factor(); - if (gKernelArgs.num_cpus < 2) return; diff --git a/src/system/boot/platform/bios_ia32/support.S b/src/system/boot/platform/bios_ia32/support.S index 442b72dcb5..c6a70e1401 100644 --- a/src/system/boot/platform/bios_ia32/support.S +++ b/src/system/boot/platform/bios_ia32/support.S @@ -7,29 +7,6 @@ #define FUNCTION(x) .global x; .type x,@function; x -FUNCTION(execute_n_instructions): - movl 4(%esp), %ecx - shrl $4, %ecx -.again: - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - loop .again - ret - null_idt_descr: .word 0 .word 0,0 diff --git a/src/system/boot/platform/efi/arch/x86/Jamfile b/src/system/boot/platform/efi/arch/x86/Jamfile index 32e5143072..f353dfa097 100644 --- a/src/system/boot/platform/efi/arch/x86/Jamfile +++ b/src/system/boot/platform/efi/arch/x86/Jamfile @@ -20,7 +20,6 @@ for platform in [ MultiBootSubDirSetup efi ] { crt0-efi-$(TARGET_ARCH).S entry.S smp_trampoline.S - support.S relocation_func.cpp arch_mmu.cpp arch_smp.cpp diff --git a/src/system/boot/platform/efi/arch/x86/arch_smp.cpp b/src/system/boot/platform/efi/arch/x86/arch_smp.cpp index fd0f283c6b..56435b5fc3 100644 --- a/src/system/boot/platform/efi/arch/x86/arch_smp.cpp +++ b/src/system/boot/platform/efi/arch/x86/arch_smp.cpp @@ -36,8 +36,6 @@ #endif -extern "C" void execute_n_instructions(int count); - void copy_trampoline_code(uint64 trampolineCode, uint64 trampolineStack); void prepare_trampoline_args(uint64 trampolineCode, uint64 trampolineStack, uint32 pagedir, uint64 kernelEntry, addr_t virtKernelArgs, @@ -125,43 +123,6 @@ acpi_do_smp_config(void) } -static void -calculate_apic_timer_conversion_factor(void) -{ - int64 t1, t2; - uint32 config; - uint32 count; - - TRACE("calculating apic timer conversion factor\n"); - - // setup the timer - config = apic_read(APIC_LVT_TIMER); - config = (config & APIC_LVT_TIMER_MASK) + APIC_LVT_MASKED; - // timer masked, vector 0 - apic_write(APIC_LVT_TIMER, config); - - config = (apic_read(APIC_TIMER_DIVIDE_CONFIG) & ~0x0000000f); - apic_write(APIC_TIMER_DIVIDE_CONFIG, config | APIC_TIMER_DIVIDE_CONFIG_1); - // divide clock by one - - t1 = system_time(); - apic_write(APIC_INITIAL_TIMER_COUNT, 0xffffffff); // start the counter - - execute_n_instructions(128 * 20000); - - count = apic_read(APIC_CURRENT_TIMER_COUNT); - t2 = system_time(); - - count = 0xffffffff - count; - - gKernelArgs.arch_args.apic_time_cv_factor - = (uint32)((1000000.0/(t2 - t1)) * count); - - TRACE("APIC ticks/sec = %" B_PRId32 "\n", - gKernelArgs.arch_args.apic_time_cv_factor); -} - - // #pragma mark - @@ -211,9 +172,6 @@ arch_smp_init_other_cpus(void) TRACE("smp: apic (mapped) = %lx\n", (addr_t)gKernelArgs.arch_args.apic.Pointer()); - // calculate how fast the apic timer is - calculate_apic_timer_conversion_factor(); - if (gKernelArgs.num_cpus < 2) return; diff --git a/src/system/boot/platform/efi/arch/x86/support.S b/src/system/boot/platform/efi/arch/x86/support.S deleted file mode 100644 index 4fe95030bc..0000000000 --- a/src/system/boot/platform/efi/arch/x86/support.S +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2021-2022 Haiku, Inc. All rights reserved. - * Released under the terms of the MIT License. - * - * Copyright 2001, Travis Geiselbrecht. All rights reserved. - * Distributed under the terms of the NewOS License. - */ - - -#define FUNCTION(x) .global x; .type x,@function; x - - -FUNCTION(execute_n_instructions): - movl 4(%esp), %ecx - shrl $4, %ecx -.again: - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - loop .again - ret - diff --git a/src/system/boot/platform/efi/arch/x86_64/Jamfile b/src/system/boot/platform/efi/arch/x86_64/Jamfile index 57dbca2481..86bb0484f3 100644 --- a/src/system/boot/platform/efi/arch/x86_64/Jamfile +++ b/src/system/boot/platform/efi/arch/x86_64/Jamfile @@ -19,7 +19,6 @@ for platform in [ MultiBootSubDirSetup efi ] { crt0-efi-$(TARGET_ARCH).S entry.S long_smp_trampoline.S - support.S relocation_func.cpp arch_start.cpp arch_smp.cpp diff --git a/src/system/boot/platform/efi/arch/x86_64/support.S b/src/system/boot/platform/efi/arch/x86_64/support.S deleted file mode 100644 index 4c77461fae..0000000000 --- a/src/system/boot/platform/efi/arch/x86_64/support.S +++ /dev/null @@ -1,31 +0,0 @@ -/* -** Copyright 2001, Travis Geiselbrecht. All rights reserved. -** Distributed under the terms of the NewOS License. -*/ - - -#define FUNCTION(x) .global x; .type x,@function; x - - -FUNCTION(execute_n_instructions): - movl %edi, %ecx - shrl $4, %ecx -.again: - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - xorl %eax, %eax - loop .again - ret diff --git a/src/system/kernel/arch/x86/apic.cpp b/src/system/kernel/arch/x86/apic.cpp index 2226519915..e06408412f 100644 --- a/src/system/kernel/arch/x86/apic.cpp +++ b/src/system/kernel/arch/x86/apic.cpp @@ -236,6 +236,16 @@ apic_set_lvt_initial_timer_count(uint32 config) } +uint32 +apic_lvt_current_timer_count() +{ + if (sX2APIC) + return x86_read_msr(IA32_MSR_APIC_CURRENT_TIMER_COUNT); + else + return apic_read(APIC_CURRENT_TIMER_COUNT); +} + + uint32 apic_lvt_timer_divide_config() { diff --git a/src/system/kernel/arch/x86/timers/x86_apic.cpp b/src/system/kernel/arch/x86/timers/x86_apic.cpp index c8c2351acb..4116a980ad 100644 --- a/src/system/kernel/arch/x86/timers/x86_apic.cpp +++ b/src/system/kernel/arch/x86/timers/x86_apic.cpp @@ -1,6 +1,7 @@ /* + * Copyright 2025, Haiku, Inc. All rights reserved. * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved. - * Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de. All rights reserved. * Distributed under the terms of the MIT License. * * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. @@ -106,19 +107,62 @@ apic_timer_clear_hardware_timer() } +// #pragma mark - initialization + + +static uint32 +calculate_apic_timer_conversion_factor() +{ + // setup the timer + uint32 config = apic_lvt_timer() & APIC_LVT_TIMER_MASK; + config |= APIC_LVT_MASKED; + // timer masked, vector 0 + apic_set_lvt_timer(config); + + config = (apic_lvt_timer_divide_config() & ~0xf); + apic_set_lvt_timer_divide_config(config | APIC_TIMER_DIVIDE_CONFIG_1); + // divide clock by one + + apic_set_lvt_initial_timer_count(UINT32_MAX); // start the counter + + // Use CPUID as a fence (same as in TSC calibration). + asm volatile ("cpuid" : : : "eax", "ebx", "ecx", "edx"); + int64 t1 = system_time_nsecs(); + uint32 startCount = apic_lvt_current_timer_count(); + + spin(5000); + + asm volatile ("cpuid" : : : "eax", "ebx", "ecx", "edx"); + int64 t2 = system_time_nsecs(); + uint32 endCount = apic_lvt_current_timer_count(); + + uint32 count = startCount - endCount; + + uint32 factor + = (uint32)(((double(1) * 1000 * 1000 * 1000) / (t2 - t1)) * count); + + dprintf("APIC timer frequency: %d\n", factor); + + return factor; +} + + static status_t apic_timer_init(struct kernel_args *args) { if (!apic_available()) return B_ERROR; - sApicTicsPerSec = args->arch_args.apic_time_cv_factor; + sApicTicsPerSec = calculate_apic_timer_conversion_factor(); reserve_io_interrupt_vectors(1, 0xfb - ARCH_INTERRUPT_BASE, INTERRUPT_TYPE_LOCAL_IRQ); install_io_interrupt_handler(0xfb - ARCH_INTERRUPT_BASE, &apic_timer_interrupt, NULL, B_NO_LOCK_VECTOR); + apic_timer_per_cpu_init(args, 0); + // will be called on non-boot CPUs by apic_per_cpu_init() + return B_OK; } @@ -136,5 +180,6 @@ apic_timer_per_cpu_init(struct kernel_args *args, int32 cpu) config = apic_lvt_timer_divide_config() & 0xfffffff0; config |= APIC_TIMER_DIVIDE_CONFIG_1; // clock division by 1 apic_set_lvt_timer_divide_config(config); + return B_OK; }