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 <[email protected]>
Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
Augustin Cavalier
2025-02-18 21:04:59 +00:00
committed by waddlesplash
parent b801f14f55
commit 3d840c4289
11 changed files with 59 additions and 176 deletions
+1
View File
@@ -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);
@@ -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<void> apic;
uint32 ioapic_phys;