diff options
author | Richard Braun <rbraun@sceen.net> | 2017-05-16 01:57:40 +0200 |
---|---|---|
committer | Richard Braun <rbraun@sceen.net> | 2017-05-16 01:57:40 +0200 |
commit | fca151d3e8b551483cc3f5d43394a1d90b79ee34 (patch) | |
tree | 5d589f612de3d85f88bf9b1b61ea6b11141682ed | |
parent | 27781cb2cbed5752861483623ac00e740a71678b (diff) |
x86/{cpu,pit}: implement cpu_delay using the timestamp counter
-rw-r--r-- | arch/x86/machine/boot.c | 2 | ||||
-rw-r--r-- | arch/x86/machine/cpu.c | 47 | ||||
-rw-r--r-- | arch/x86/machine/cpu.h | 19 | ||||
-rw-r--r-- | arch/x86/machine/pit.c | 8 | ||||
-rw-r--r-- | arch/x86/machine/pit.h | 11 |
5 files changed, 77 insertions, 10 deletions
diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index b111a3c0..09ed36eb 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -461,6 +461,7 @@ boot_main(void) syscnt_setup(); percpu_bootstrap(); trap_setup(); + pit_setup_free_running(); cpu_setup(); thread_bootstrap(); cga_setup(); @@ -476,7 +477,6 @@ boot_main(void) boot_save_data(); biosmem_free_usable(); pic_setup(); - pit_setup(); cpu_mp_probe(); kernel_main(); diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index ff860ba0..d62b68f9 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -34,10 +34,16 @@ #include <machine/cpu.h> #include <machine/io.h> #include <machine/lapic.h> +#include <machine/pit.h> #include <machine/pmap.h> #include <machine/trap.h> #include <vm/vm_page.h> +/* + * Delay used for frequency measurement, in microseconds. + */ +#define CPU_FREQ_CAL_DELAY 1000000 + #define CPU_TYPE_MASK 0x00003000 #define CPU_TYPE_SHIFT 12 #define CPU_FAMILY_MASK 0x00000f00 @@ -141,6 +147,11 @@ struct cpu cpu_desc __percpu; unsigned int cpu_nr_active __read_mostly; /* + * Processor frequency, assumed fixed and equal on all processors. + */ +static uint64_t cpu_freq __read_mostly; + +/* * Interrupt descriptor table. */ static struct cpu_gate_desc cpu_idt[CPU_IDT_SIZE] __aligned(8) __read_mostly; @@ -154,6 +165,24 @@ static struct cpu_gate_desc cpu_idt[CPU_IDT_SIZE] __aligned(8) __read_mostly; static unsigned long cpu_double_fault_handler; static char cpu_double_fault_stack[STACK_SIZE] __aligned(DATA_ALIGN); +void +cpu_delay(unsigned long usecs) +{ + int64_t total, prev, count, diff; + + assert(usecs != 0); + + total = DIV_CEIL((int64_t)usecs * cpu_freq, 1000000); + prev = cpu_get_tsc(); + + do { + count = cpu_get_tsc(); + diff = count - prev; + prev = count; + total -= diff; + } while (total > 0); +} + void * __init cpu_get_boot_stack(void) { @@ -508,6 +537,18 @@ cpu_init(struct cpu *cpu) cpu->state = CPU_STATE_ON; } +static void __init +cpu_measure_freq(void) +{ + uint64_t start, end; + + start = cpu_get_tsc(); + pit_delay(CPU_FREQ_CAL_DELAY); + end = cpu_get_tsc(); + + cpu_freq = (end - start) / (1000000 / CPU_FREQ_CAL_DELAY); +} + void __init cpu_setup(void) { @@ -518,6 +559,8 @@ cpu_setup(void) cpu->double_fault_stack = cpu_double_fault_stack; /* XXX */ cpu_init(cpu); cpu_nr_active = 1; + + cpu_measure_freq(); } static void __init @@ -566,6 +609,10 @@ cpu_info(const struct cpu *cpu) printf("cpu%u: address widths: physical: %hu, virtual: %hu\n", cpu->id, cpu->phys_addr_width, cpu->virt_addr_width); } + + printf("cpu%u: frequency: %llu.%02llu MHz\n", cpu->id, + (unsigned long long)cpu_freq / 1000000, + (unsigned long long)cpu_freq % 1000000); } void __init diff --git a/arch/x86/machine/cpu.h b/arch/x86/machine/cpu.h index 3bf1ade3..2360ea7d 100644 --- a/arch/x86/machine/cpu.h +++ b/arch/x86/machine/cpu.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2014 Richard Braun. + * Copyright (c) 2010-2017 Richard Braun. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -479,6 +479,15 @@ cpu_set_msr(uint32_t msr, uint32_t high, uint32_t low) asm volatile("wrmsr" : : "c" (msr), "a" (low), "d" (high)); } +static __always_inline uint64_t +cpu_get_tsc(void) +{ + uint32_t high, low; + + asm volatile("rdtsc" : "=a" (low), "=d" (high)); + return ((uint64_t)high << 32) | low; +} + /* * Flush non-global TLB entries. * @@ -528,13 +537,9 @@ cpu_tlb_flush_va(unsigned long va) } /* - * XXX For now, directly use the PIT. + * Busy-wait for a given amount of time, in microseconds. */ -static inline void -cpu_delay(unsigned long usecs) -{ - pit_delay(usecs); -} +void cpu_delay(unsigned long usecs); /* * Return the address of the boot stack allocated for the current processor. diff --git a/arch/x86/machine/pit.c b/arch/x86/machine/pit.c index d3fece51..bf53ee11 100644 --- a/arch/x86/machine/pit.c +++ b/arch/x86/machine/pit.c @@ -45,7 +45,7 @@ #define PIT_MAX_COUNT 0xffff void __init -pit_setup(void) +pit_setup_free_running(void) { io_write_byte(PIT_PORT_MODE, PIT_MODE_RATE_GEN | PIT_MODE_RW_LSB | PIT_MODE_RW_MSB); @@ -53,6 +53,12 @@ pit_setup(void) io_write_byte(PIT_PORT_COUNTER0, PIT_MAX_COUNT >> 8); } +void __init +pit_setup(void) +{ + /* TODO Implement */ +} + static unsigned int pit_read(void) { diff --git a/arch/x86/machine/pit.h b/arch/x86/machine/pit.h index 3b4da60b..7bf7a7b7 100644 --- a/arch/x86/machine/pit.h +++ b/arch/x86/machine/pit.h @@ -19,7 +19,16 @@ #define _X86_PIT_H /* - * Set up the i8253 Programmable Interval Timer. + * Initialize the PIT as a free running counter. + * + * This is used during early initialization to measure the frequency of + * other clocks. The PIT is used despite its lack of precision because + * it's the only architectural timer with a known frequency. + */ +void pit_setup_free_running(void); + +/* + * Initialize the pit module. */ void pit_setup(void); |