From bbedf628fa4fa582a1ee8c56d2fc0e46226c9200 Mon Sep 17 00:00:00 2001 From: Peter Shkenev Date: Fri, 29 Nov 2024 19:33:43 +0300 Subject: [PATCH] x86: implement CPUID 15h support This allows us to get TSC and APIC rates without calibration on processors supporting this leaf. Signed-off-by: Peter Shkenev --- kernel/arch/x86_64/apic.cpp | 20 +++++++------- kernel/arch/x86_64/cpu.cpp | 52 +++++++++++++++++++++++++++++++------ kernel/include/onyx/cpu.h | 4 +++ 3 files changed, 57 insertions(+), 19 deletions(-) diff --git a/kernel/arch/x86_64/apic.cpp b/kernel/arch/x86_64/apic.cpp index eb3759278..215383741 100644 --- a/kernel/arch/x86_64/apic.cpp +++ b/kernel/arch/x86_64/apic.cpp @@ -504,7 +504,6 @@ irqstatus_t apic_timer_irq(struct irq_context *ctx, void *cookie) return IRQ_HANDLED; } -unsigned long apic_rate = 0; unsigned long us_apic_rate = 0; driver apic_driver = {.name = "apic-timer", .bus_type_node = {&apic_driver}}; @@ -636,10 +635,12 @@ void timer_calibrate() calib.duration[2] = 10; /* No need to cal*/ bool calibrate_tsc = x86_get_tsc_rate() == 0; + bool calibrate_apic = x86_get_apic_rate() == 0; for (int i = 0; i < CALIBRATION_TRIALS; i++) { - apic_calibrate(i); + if (calibrate_apic) + apic_calibrate(i); if (calibrate_tsc) tsc_calibrate(i); } @@ -647,19 +648,16 @@ void timer_calibrate() unsigned long deltas[3]; for (int i = 0; i < 3; i++) - { deltas[i] = calib.tsc_calib[i].best_delta; - } if (calibrate_tsc) x86_set_tsc_rate(calculate_frequency(deltas, 1000)); for (int i = 0; i < 3; i++) - { deltas[i] = calib.apic_ticks[i]; - } - apic_rate = calculate_frequency(deltas, 1); + if (calibrate_apic) + x86_set_apic_rate(calculate_frequency(deltas, 1)); } void apic_set_oneshot_tsc(hrtime_t deadline) @@ -727,7 +725,7 @@ void apic_set_oneshot(hrtime_t deadline) void apic_timer_set_periodic(hrtime_t period_ns) { hrtime_t period_ms = period_ns / NS_PER_MS; - uint32_t counter = apic_rate * period_ms; + uint32_t counter = x86_get_apic_rate() * period_ms; lapic_write(LAPIC_LVT_TIMER, 34 | LAPIC_LVT_TIMER_MODE_PERIODIC); lapic_write(LAPIC_TIMER_INITCNT, counter); @@ -755,10 +753,10 @@ void apic_timer_init() */ lapic_write(LAPIC_TIMER_DIV, 3); - printf("apic: apic timer rate: %lu\n", apic_rate); - us_apic_rate = INT_DIV_ROUND_CLOSEST(apic_rate, 1000); + printf("apic: apic timer rate: %lu\n", x86_get_apic_rate()); + us_apic_rate = INT_DIV_ROUND_CLOSEST(x86_get_apic_rate(), 1000); - fp_32_64_div_32_32(&apic_ticks_per_ns, apic_rate, NS_PER_MS); + fp_32_64_div_32_32(&apic_ticks_per_ns, x86_get_apic_rate(), NS_PER_MS); DISABLE_INTERRUPTS(); diff --git a/kernel/arch/x86_64/cpu.cpp b/kernel/arch/x86_64/cpu.cpp index 52ac65ef7..65264d4ed 100644 --- a/kernel/arch/x86_64/cpu.cpp +++ b/kernel/arch/x86_64/cpu.cpp @@ -82,6 +82,16 @@ uint64_t x86_get_tsc_rate(void) return bootcpu_info.tsc_rate; } +void x86_set_apic_rate(uint64_t rate) +{ + bootcpu_info.apic_rate = rate; +} + +uint64_t x86_get_apic_rate(void) +{ + return bootcpu_info.apic_rate; +} + extern "C" void intel_ucode_load(void); static void __cpu_identify(void); @@ -147,14 +157,40 @@ static void __cpu_identify(void) if ((bootcpu_info.family == 0xf && bootcpu_info.model > 0x2) || (bootcpu_info.family == 0x6 && bootcpu_info.model >= 0xe)) bootcpu_info.constant_tsc = true; -#if 0 - /* TODO: Add 15h support */ - if(__get_cpuid(0x15, &eax, &ebx, &ecx, &edx)) - { - INFO("x86cpu", "0x15 supported!\n"); - halt(); - } -#endif + + bootcpu_info.apic_rate = 0; + bootcpu_info.tsc_rate = 0; + if (__get_cpuid(0x15, &eax, &ebx, &ecx, &edx)) + { + if (ebx == 0 || eax == 0) + { + pr_info("x86cpu: CPUID 0x15 is useless, reverting to calibration\n"); + } + else if (ecx == 0) + { + /* Some Intel SoCs don't report crystal clock. Though it can be + * highly accurately calculated from CPU base frequency + */ + uint32_t numerator = ebx; + uint32_t denominator = eax; + if (__get_cpuid(0x16, &eax, &ebx, &ecx, &edx)) + { + bootcpu_info.apic_rate = (uint64_t) eax * 1000 * denominator / numerator; + bootcpu_info.tsc_rate = (uint64_t) eax * 1000000; + pr_info("x86cpu: CPUID reported TSC rate: %lu, apic rate: %lu\n", + bootcpu_info.tsc_rate, bootcpu_info.apic_rate); + } + else + pr_info("x86cpu: CPUID 0x15 is useless and 0x16 is not available\n"); + } + else + { + bootcpu_info.apic_rate = ecx / 1000; + bootcpu_info.tsc_rate = (uint64_t) ecx * ebx / eax; + pr_info("x86cpu: CPUID reported TSC rate: %lu, apic rate: %lu\n", bootcpu_info.tsc_rate, + bootcpu_info.apic_rate); + } + } cpu_get_sign(); x86_do_alternatives(); diff --git a/kernel/include/onyx/cpu.h b/kernel/include/onyx/cpu.h index 3c82f1971..875f57fd8 100644 --- a/kernel/include/onyx/cpu.h +++ b/kernel/include/onyx/cpu.h @@ -252,6 +252,7 @@ typedef struct cpu bool invariant_tsc; bool constant_tsc; uint64_t tsc_rate; + uint64_t apic_rate; int virtualAddressSpace, physicalAddressSpace; unsigned long manufacturer; /* Add more as needed */ @@ -266,6 +267,9 @@ void x86_set_tsc_rate(uint64_t rate); uint64_t x86_get_tsc_rate(void); void x86_load_ucode(void); +void x86_set_apic_rate(uint64_t rate); +uint64_t x86_get_apic_rate(void); + /* Linux kernel-like cpu_relax, does a pause instruction */ static inline void cpu_relax(void) {