From 703ac12cc7b710cf5fd8ed08fdaaa760ffdff501 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Fri, 10 Oct 2014 11:11:09 +0200 Subject: [PATCH 116/176] calibrate_tsc(): use the Stall() EFI boot service on GRUB_MACHINE_EFI Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1150698 HyperV Gen2 virtual machines have no PIT; guest code should rely on UEFI services instead. Signed-off-by: RHEL Ninjas --- grub-core/kern/i386/tsc.c | 59 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/grub-core/kern/i386/tsc.c b/grub-core/kern/i386/tsc.c index 2e85289d8..0787b6f3e 100644 --- a/grub-core/kern/i386/tsc.c +++ b/grub-core/kern/i386/tsc.c @@ -24,6 +24,17 @@ #include #include #include +#ifdef GRUB_MACHINE_XEN +# include +#else +# ifdef GRUB_MACHINE_EFI +# include +# include +# else +# include +# endif +#endif +#include /* This defines the value TSC had at the epoch (that is, when we calibrated it). */ static grub_uint64_t tsc_boot_time; @@ -33,6 +44,44 @@ static grub_uint64_t tsc_boot_time; in 32-bit. */ grub_uint32_t grub_tsc_rate; +#ifndef GRUB_MACHINE_XEN + +static void +grub_stall (grub_uint16_t tics) +{ +# ifdef GRUB_MACHINE_EFI + grub_uint64_t microseconds; + + microseconds = (grub_uint64_t)tics * 1000 * 1000 * 3 / 3579545; + efi_call_1 (grub_efi_system_table->boot_services->stall, microseconds); +# else + /* Disable timer2 gate and speaker. */ + grub_outb (grub_inb (GRUB_PIT_SPEAKER_PORT) + & ~ (GRUB_PIT_SPK_DATA | GRUB_PIT_SPK_TMR2), + GRUB_PIT_SPEAKER_PORT); + + /* Set tics. */ + grub_outb (GRUB_PIT_CTRL_SELECT_2 | GRUB_PIT_CTRL_READLOAD_WORD, + GRUB_PIT_CTRL); + grub_outb (tics & 0xff, GRUB_PIT_COUNTER_2); + grub_outb (tics >> 8, GRUB_PIT_COUNTER_2); + + /* Enable timer2 gate, keep speaker disabled. */ + grub_outb ((grub_inb (GRUB_PIT_SPEAKER_PORT) & ~ GRUB_PIT_SPK_DATA) + | GRUB_PIT_SPK_TMR2, + GRUB_PIT_SPEAKER_PORT); + + /* Wait. */ + while ((grub_inb (GRUB_PIT_SPEAKER_PORT) & GRUB_PIT_SPK_TMR2_LATCH) == 0x00); + + /* Disable timer2 gate and speaker. */ + grub_outb (grub_inb (GRUB_PIT_SPEAKER_PORT) + & ~ (GRUB_PIT_SPK_DATA | GRUB_PIT_SPK_TMR2), + GRUB_PIT_SPEAKER_PORT); +# endif +} +#endif + static grub_uint64_t grub_tsc_get_time_ms (void) { @@ -46,8 +95,14 @@ grub_tsc_get_time_ms (void) static int calibrate_tsc_hardcode (void) { - grub_tsc_rate = 5368;/* 800 MHz */ - return 1; + /* First calibrate the TSC rate (relative, not absolute time). */ + grub_uint64_t end_tsc; + + tsc_boot_time = grub_get_tsc (); + grub_stall (0xffff); + end_tsc = grub_get_tsc (); + + grub_tsc_rate = grub_divmod64 ((55ULL << 32), end_tsc - tsc_boot_time, 0); } void -- 2.13.0