diff options
author | Damien Zammit <damien@zamaudio.com> | 2023-03-06 07:05:03 +0000 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2023-03-07 01:03:08 +0100 |
commit | 29564c619f33702033ecf0a86caac7a294f1887f (patch) | |
tree | f1a496e5893996ffd4ef30536cdcc14562b769de /i386/i386at/ioapic.c | |
parent | dac167f036465e9d7cca10c52d8345773d2e6c3f (diff) | |
download | gnumach-29564c619f33702033ecf0a86caac7a294f1887f.tar.gz gnumach-29564c619f33702033ecf0a86caac7a294f1887f.tar.bz2 gnumach-29564c619f33702033ecf0a86caac7a294f1887f.zip |
lapic timer: Calibrate via mach timer not PIT
Previously the lapic timer was calibrated by one-shot PIT timer2.
This method can be buggy and generally unused in emulation environments.
This patch reworks the timer calibration to use a mach timer based
on regular PIT interrupts to remapped IOAPIC pin.
This also changes the primary clock source to use PIT timer0 remapped
to an IOAPIC pin when APIC mode is enabled, instead of a periodic lapic
timer.
Message-Id: <20230306070452.292697-1-damien@zamaudio.com>
Diffstat (limited to 'i386/i386at/ioapic.c')
-rw-r--r-- | i386/i386at/ioapic.c | 81 |
1 files changed, 46 insertions, 35 deletions
diff --git a/i386/i386at/ioapic.c b/i386/i386at/ioapic.c index d2ea84ad..c6da35e1 100644 --- a/i386/i386at/ioapic.c +++ b/i386/i386at/ioapic.c @@ -28,11 +28,13 @@ #include <i386/pio.h> #include <i386/pit.h> #include <i386/pic.h> /* only for macros */ +#include <i386/smp.h> #include <mach/machine.h> #include <kern/printf.h> +#include <kern/timer.h> static int has_irq_specific_eoi = 1; /* FIXME: Assume all machines have this */ -int duplicate_pin; +int timer_pin; uint32_t lapic_timer_val = 0; uint32_t calibrated_ticks = 0; @@ -43,7 +45,7 @@ int iunit[NINTR] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; interrupt_handler_fn ivect[NINTR] = { - /* 00 */ intnull, /* install timer later */ + /* 00 */ (interrupt_handler_fn)hardclock, /* 01 */ kdintr, /* kdintr, ... */ /* 02 */ intnull, /* 03 */ intnull, /* lnpoll, comintr, ... */ @@ -150,32 +152,58 @@ ioapic_toggle_entry(int apic, int pin, int mask) ioapic_write(apic, APIC_IO_REDIR_LOW(pin), entry.lo); } +static void timer_expiry_callback(void *arg) +{ + volatile int *done = arg; + *done = 1; +} + static uint32_t -pit_measure_10x_apic_hz(void) +timer_measure_10x_apic_hz(void) { - volatile int i; + volatile int done = 0; uint32_t start = 0xffffffff; + timer_elt_data_t tmp_timer; + tmp_timer.fcn = timer_expiry_callback; + tmp_timer.param = (void *)&done; - /* Prepare accurate delay for 1/hz seconds */ - pit_prepare_sleep(hz); + printf("timer calibration..."); /* Set APIC timer */ lapic->init_count.r = start; - /* zZz */ - for (i = 0; i < 10; i++) - pit_sleep(); + /* Delay for 10 * 1/hz seconds */ + set_timeout(&tmp_timer, hz / 10); + do { + cpu_pause(); + } while (!done); /* Stop APIC timer */ lapic->lvt_timer.r |= LAPIC_DISABLE; + printf(" done\n"); + return start - lapic->cur_count.r; } -void lapic_update_timer(void) +void +calibrate_lapic_timer(void) { - /* Timer decrements until zero and then calls this on every interrupt */ - lapic_timer_val += calibrated_ticks; + spl_t s; + + /* Set one-shot timer */ + lapic->divider_config.r = LAPIC_TIMER_DIVIDE_2; + lapic->lvt_timer.r = IOAPIC_INT_BASE; + + /* Measure number of APIC timer ticks in 10x 1/hz seconds + * but calibrate the timer to expire at rate of hz + * divide by 10 because we waited 10 times longer than we needed. */ + if (!calibrated_ticks) { + s = splhigh(); + spl0(); + calibrated_ticks = timer_measure_10x_apic_hz() / 10; + splx(s); + } } void @@ -306,16 +334,14 @@ ioapic_configure(void) /* Save timer info */ timer_gsi = gsi; } else { - /* Disable duplicated timer gsi */ + /* Remap timer irq */ if (gsi == timer_gsi) { - duplicate_pin = pin; - /* Remap this interrupt pin to GSI base - * so we don't duplicate vectors */ + timer_pin = pin; + /* Remap GSI base to timer pin so ivect[0] is the timer */ entry.both.vector = IOAPIC_INT_BASE; - ioapic_write_entry(apic, duplicate_pin, entry.both); - /* Mask the ioapic pin with deduplicated vector as - * we will never use it, since timer is on another gsi */ - mask_irq(duplicate_pin); + ioapic_write_entry(apic, timer_pin, entry.both); + /* Mask the duplicate pin 0 as we will be using timer_pin */ + mask_irq(0); } } } @@ -337,20 +363,5 @@ ioapic_configure(void) /* Start the IO APIC receiving interrupts */ lapic_enable(); - /* Set one-shot timer */ - lapic->divider_config.r = LAPIC_TIMER_DIVIDE_2; - lapic->lvt_timer.r = IOAPIC_INT_BASE; - - /* Measure number of APIC timer ticks in 10x 1/hz seconds - * but calibrate the timer to expire at rate of hz - * divide by 10 because we waited 10 times longer than we needed */ - calibrated_ticks = pit_measure_10x_apic_hz() / 10; - - /* Set up counter later */ - lapic->lvt_timer.r = LAPIC_DISABLE; - - /* Install clock interrupt handler on pin 0 */ - ivect[0] = (interrupt_handler_fn)hardclock; - printf("IOAPIC 0 configured\n"); } |