Skip to content

Commit

Permalink
x86: implement CPUID 15h support
Browse files Browse the repository at this point in the history
This allows us to get TSC and APIC rates without calibration on
processors supporting this leaf.

Signed-off-by: Peter Shkenev <[email protected]>
  • Loading branch information
petershh committed Dec 4, 2024
1 parent 3bd3ab9 commit bbedf62
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 19 deletions.
20 changes: 9 additions & 11 deletions kernel/arch/x86_64/apic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}};
Expand Down Expand Up @@ -636,30 +635,29 @@ 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);
}

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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();

Expand Down
52 changes: 44 additions & 8 deletions kernel/arch/x86_64/cpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand Down
4 changes: 4 additions & 0 deletions kernel/include/onyx/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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)
{
Expand Down

0 comments on commit bbedf62

Please sign in to comment.