=== modified file 'ChangeLog'
--- ChangeLog 2008-07-04 02:26:10 +0000
+++ ChangeLog 2008-07-04 18:08:36 +0000
@@ -1,3 +1,70 @@
+2008-07-04 Colin D Bennett
+
+ High resolution timer support. Implemented for i386 CPU using TSC.
+ Extracted generic grub_millisleep() so it's linked in only as needed.
+ This requires a Pentium compatible CPU; currently the code does not
+ check for this (so it will fail on 386 and 486 machines).
+
+ * conf/i386-efi.rmk: Added TSC high resolution time module, link with
+ generic grub_millisleep() function.
+
+ * conf/i386-pc.rmk: Likewise.
+
+ * conf/sparc64-ieee1275.rmk: Add kern/generic/millisleep.c and
+ kern/generic/get_time_ms.c to kernel, to use generic time functions.
+
+ * conf/powerpc-ieee1275.rmk: Add kern/generic/millisleep.c to kernel,
+ to use generic grub_millisleep() function.
+
+ * conf/i386-linuxbios.rmk: Added kern/generic/get_time_ms.c to the
+ kernel.
+
+ * kern/generic/get_time_ms.c (grub_get_time_ms): New file. Platform
+ independent implementation of grub_get_time_ms() using the RTC that
+ can be linked into a platform's kernel when it does not implement its
+ own specialized grub_get_time_ms() function.
+
+ * kern/generic/millisleep.c (grub_millisleep): New file. Extracted
+ from grub_millisleep_generic() in kern/misc.c and renamed. Changed it
+ to use grub_get_time_ms() instead of grub_get_rtc() for better
+ precision on when high resolution time is available.
+
+ * kern/misc.c (grub_millisleep_generic): Deleted. Moved to
+ kern/generic/millisleep.c so that it is only included in the kernel
+ image when a platform does not define a specialized version.
+
+ * commands/sleep.c (grub_interruptible_millisleep): Uses
+ grub_get_time_ms() instead of grub_get_rtc() to stay in sync with
+ grub_millisleep() from kern/generic/millisleep.c.
+
+ * include/grub/i386/tsc.h (grub_get_tsc): New file. Inline function
+ grub_get_tsc() uses x86 RDTSC instruction (available on Pentium+ CPUs)
+ to read the counter value for the TSC.
+ (grub_tsc_calibrate): Declare this function for grub_machine_init().
+
+ * kern/i386/tsc.c (grub_get_time_ms): x86 TSC support providing a high
+ resolution clock.
+ (grub_tsc_calibrate): New function to calibrate the TSC using RTC.
+
+ * include/grub/time.h (grub_get_time_ms): Added grub_get_time_ms()
+ function to return the current time in millseconds since the epoch.
+ This supports higher resolution time than grub_get_rtc() on some
+ platforms such as i386-pc, where the RTC has only about 1/18 s
+ precision but a higher precision timer such as the TSC is available.
+
+ * kern/i386/efi/init.c (grub_millisleep): Deleted. Don't define
+ grub_millisleep() -- it just called grub_millisleep_generic() but now
+ it is linked to kern/generic/millisleep.c for the implementation.
+
+ * kern/sparc64/ieee1275/init.c (grub_millisleep): Deleted.
+
+ * kern/i386/pc/init.c (grub_machine_init): Call grub_tsc_calibrate().
+ (grub_millisleep): Deleted.
+
+ * kern/ieee1275/init.c (grub_millisleep): Deleted.
+ (grub_get_rtc): Now calls grub_get_time_ms(), which does the real
+ work.
+
2008-07-04 Pavel Roskin
* kern/i386/linuxbios/init.c (grub_machine_init): Cast addr to
=== modified file 'commands/sleep.c'
--- commands/sleep.c 2008-05-16 20:55:29 +0000
+++ commands/sleep.c 2008-07-04 16:55:48 +0000
@@ -43,15 +43,15 @@
grub_printf ("%d ", n);
}
-/* Based on grub_millisleep() from kern/misc.c. */
+/* Based on grub_millisleep() from kern/generic/millisleep.c. */
static int
grub_interruptible_millisleep (grub_uint32_t ms)
{
- grub_uint32_t end_at;
-
- end_at = grub_get_rtc () + grub_div_roundup (ms * GRUB_TICKS_PER_SECOND, 1000);
-
- while (grub_get_rtc () < end_at)
+ grub_uint64_t start;
+
+ start = grub_get_time_ms ();
+
+ while (grub_get_time_ms () - start < ms)
if (grub_checkkey () >= 0 &&
GRUB_TERM_ASCII_CHAR (grub_getkey ()) == GRUB_TERM_ESC)
return 1;
=== modified file 'conf/i386-efi.rmk'
--- conf/i386-efi.rmk 2008-06-19 04:14:16 +0000
+++ conf/i386-efi.rmk 2008-07-03 04:19:16 +0000
@@ -84,7 +84,9 @@
kern/misc.c kern/mm.c kern/loader.c kern/rescue.c kern/term.c \
kern/i386/dl.c kern/i386/efi/init.c kern/parser.c kern/partition.c \
kern/env.c symlist.c kern/efi/efi.c kern/efi/init.c kern/efi/mm.c \
- term/efi/console.c disk/efi/efidisk.c
+ term/efi/console.c disk/efi/efidisk.c \
+ kern/i386/tsc.c \
+ kern/generic/millisleep.c
kernel_mod_HEADERS = arg.h boot.h cache.h device.h disk.h dl.h elf.h elfload.h \
env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \
partition.h pc_partition.h rescue.h symbol.h term.h time.h types.h \
=== modified file 'conf/i386-linuxbios.rmk'
--- conf/i386-linuxbios.rmk 2008-06-19 04:14:16 +0000
+++ conf/i386-linuxbios.rmk 2008-07-04 16:55:48 +0000
@@ -18,6 +18,7 @@
kern/misc.c kern/mm.c kern/loader.c kern/rescue.c kern/term.c \
kern/i386/dl.c kern/parser.c kern/partition.c \
kern/env.c \
+ kern/generic/get_time_ms.c \
term/i386/pc/console.c \
term/i386/pc/at_keyboard.c term/i386/pc/vga_text.c \
symlist.c
=== modified file 'conf/i386-pc.rmk'
--- conf/i386-pc.rmk 2008-06-19 04:14:16 +0000
+++ conf/i386-pc.rmk 2008-07-03 04:19:16 +0000
@@ -43,6 +43,8 @@
kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \
kern/misc.c kern/mm.c kern/loader.c kern/rescue.c kern/term.c \
kern/i386/dl.c kern/i386/pc/init.c kern/parser.c kern/partition.c \
+ kern/i386/tsc.c \
+ kern/generic/millisleep.c \
kern/env.c \
term/i386/pc/console.c \
symlist.c
=== modified file 'conf/powerpc-ieee1275.rmk'
--- conf/powerpc-ieee1275.rmk 2008-06-19 04:14:16 +0000
+++ conf/powerpc-ieee1275.rmk 2008-07-03 04:19:16 +0000
@@ -85,6 +85,7 @@
kern/ieee1275/init.c term/ieee1275/ofconsole.c \
kern/ieee1275/openfw.c disk/ieee1275/ofdisk.c \
kern/parser.c kern/partition.c kern/env.c kern/powerpc/dl.c \
+ kern/generic/millisleep.c \
symlist.c kern/powerpc/cache.S
kernel_elf_HEADERS = grub/powerpc/ieee1275/ieee1275.h
kernel_elf_CFLAGS = $(COMMON_CFLAGS)
=== modified file 'conf/sparc64-ieee1275.rmk'
--- conf/sparc64-ieee1275.rmk 2008-06-19 00:04:59 +0000
+++ conf/sparc64-ieee1275.rmk 2008-07-03 04:19:16 +0000
@@ -73,6 +73,7 @@
kern/rescue.c kern/term.c term/ieee1275/ofconsole.c \
kern/sparc64/ieee1275/openfw.c disk/ieee1275/ofdisk.c \
kern/partition.c kern/env.c kern/sparc64/dl.c symlist.c \
+ kern/generic/millisleep.c kern/generic/get_time_ms.c \
kern/sparc64/cache.S kern/parser.c
kernel_elf_HEADERS = grub/sparc64/ieee1275/ieee1275.h
kernel_elf_CFLAGS = $(COMMON_CFLAGS)
=== added file 'include/grub/i386/tsc.h'
--- include/grub/i386/tsc.h 1970-01-01 00:00:00 +0000
+++ include/grub/i386/tsc.h 2008-07-04 17:55:21 +0000
@@ -0,0 +1,43 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see .
+ */
+
+#ifndef KERNEL_CPU_TSC_HEADER
+#define KERNEL_CPU_TSC_HEADER 1
+
+#include
+
+/* Read the TSC value, which increments with each CPU clock cycle. */
+static __inline grub_uint64_t
+grub_get_tsc (void)
+{
+ grub_uint32_t lo, hi;
+
+ /* The CPUID instruction is a 'serializing' instruction, and
+ avoids out-of-order execution of the RDTSC instruction. */
+ __asm__ __volatile__ ("xorl %%eax, %%eax\n\t"
+ "cpuid":::"%rax", "%rbx", "%rcx", "%rdx");
+ /* Read TSC value. We cannot use "=A", since this would use
+ %rax on x86_64. */
+ __asm__ __volatile__ ("rdtsc":"=a" (lo), "=d" (hi));
+
+ return (((grub_uint64_t) hi) << 32) | lo;
+}
+
+void grub_tsc_calibrate (void);
+
+#endif /* ! KERNEL_CPU_TSC_HEADER */
=== modified file 'include/grub/time.h'
--- include/grub/time.h 2007-10-22 19:02:16 +0000
+++ include/grub/time.h 2008-07-03 04:19:16 +0000
@@ -1,6 +1,6 @@
/*
* GRUB -- GRand Unified Bootloader
- * Copyright (C) 2007 Free Software Foundation, Inc.
+ * Copyright (C) 2007, 2008 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,12 +19,13 @@
#ifndef KERNEL_TIME_HEADER
#define KERNEL_TIME_HEADER 1
+#include
#include
#include
#include
void EXPORT_FUNC(grub_millisleep) (grub_uint32_t ms);
-void EXPORT_FUNC(grub_millisleep_generic) (grub_uint32_t ms);
+grub_uint64_t EXPORT_FUNC(grub_get_time_ms) (void);
static __inline void
grub_sleep (grub_uint32_t s)
=== added directory 'kern/generic'
=== added file 'kern/generic/get_time_ms.c'
--- kern/generic/get_time_ms.c 1970-01-01 00:00:00 +0000
+++ kern/generic/get_time_ms.c 2008-07-04 16:55:48 +0000
@@ -0,0 +1,37 @@
+/* get_time_ms.c - generic time implementation -- using platform RTC.
+ * The generic implementation of these functions can be used for architectures
+ * or platforms that do not have a more specialized implementation. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see .
+ */
+
+#include
+#include
+
+/* Calculate the time in milliseconds since the epoch based on the RTC. */
+grub_uint64_t
+grub_get_time_ms (void)
+{
+ /* By dimensional analysis:
+
+ 1000 ms N rtc ticks 1 s
+ ------- * ----------- * ----------- = 1000*N/T ms
+ 1 s 1 T rtc ticks
+ */
+ grub_uint64_t ticks_ms_per_sec = ((grub_uint64_t) 1000) * grub_get_rtc ();
+ return grub_divmod64 (ticks_ms_per_sec, GRUB_TICKS_PER_SECOND, 0);
+}
=== added file 'kern/generic/millisleep.c'
--- kern/generic/millisleep.c 1970-01-01 00:00:00 +0000
+++ kern/generic/millisleep.c 2008-07-04 16:55:48 +0000
@@ -0,0 +1,39 @@
+/* millisleep.c - generic millisleep function.
+ * The generic implementation of these functions can be used for architectures
+ * or platforms that do not have a more specialized implementation. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see .
+ */
+
+#include
+#include
+
+void
+grub_millisleep (grub_uint32_t ms)
+{
+ grub_uint64_t start;
+
+ start = grub_get_time_ms ();
+
+ /* Instead of setting an end time and looping while the current time is
+ less than that, comparing the elapsed sleep time with the desired sleep
+ time handles the (unlikely!) case that the timer would wrap around
+ during the sleep. */
+
+ while (grub_get_time_ms () - start < ms)
+ grub_cpu_idle ();
+}
=== modified file 'kern/i386/efi/init.c'
--- kern/i386/efi/init.c 2007-10-22 18:59:33 +0000
+++ kern/i386/efi/init.c 2008-07-04 17:55:21 +0000
@@ -25,18 +25,13 @@
#include
#include
#include
-#include
-
-void
-grub_millisleep (grub_uint32_t ms)
-{
- grub_millisleep_generic (ms);
-}
+#include
void
grub_machine_init (void)
{
grub_efi_init ();
+ grub_tsc_calibrate ();
}
void
=== modified file 'kern/i386/pc/init.c'
--- kern/i386/pc/init.c 2008-06-15 17:21:16 +0000
+++ kern/i386/pc/init.c 2008-07-04 18:03:26 +0000
@@ -30,6 +30,7 @@
#include
#include
#include
+#include
struct mem_region
{
@@ -46,12 +47,6 @@
grub_size_t grub_os_area_size;
grub_size_t grub_lower_mem, grub_upper_mem;
-void
-grub_millisleep (grub_uint32_t ms)
-{
- grub_millisleep_generic (ms);
-}
-
void
grub_arch_sync_caches (void *address __attribute__ ((unused)),
grub_size_t len __attribute__ ((unused)))
@@ -231,6 +226,8 @@
if (! grub_os_area_addr)
grub_fatal ("no upper memory");
+
+ grub_tsc_calibrate ();
}
void
=== added file 'kern/i386/tsc.c'
--- kern/i386/tsc.c 1970-01-01 00:00:00 +0000
+++ kern/i386/tsc.c 2008-07-04 17:55:21 +0000
@@ -0,0 +1,89 @@
+/* kern/i386/tsc.c - x86 TSC time source implementation
+ * Requires Pentium or better x86 CPU that supports the RDTSC instruction.
+ * This module uses the RTC (via grub_get_rtc()) to calibrate the TSC to
+ * real time.
+ *
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+
+/* Calibrated reference for TSC=0. This defines the time since the epoch in
+ milliseconds that TSC=0 refers to. */
+static grub_uint64_t tsc_boot_time;
+
+/* Calibrated TSC rate. (In TSC ticks per millisecond.) */
+static grub_uint64_t tsc_ticks_per_ms;
+
+
+/* Declared in . */
+grub_uint64_t
+grub_get_time_ms (void)
+{
+ return tsc_boot_time + grub_divmod64 (grub_get_tsc (), tsc_ticks_per_ms, 0);
+}
+
+
+/* How many RTC ticks to use for calibration loop. (>= 1) */
+#define CALIBRATION_TICKS 2
+
+/* Calibrate the TSC based on the RTC. */
+void
+grub_tsc_calibrate (void)
+{
+ /* First calbrate the TSC rate (relative, not absolute time). */
+ grub_uint64_t start_tsc;
+ grub_uint64_t end_tsc;
+ grub_uint32_t initial_tick;
+ grub_uint32_t start_tick;
+ grub_uint32_t end_tick;
+
+ /* Wait for the start of the next tick;
+ we'll base out timing off this edge. */
+ initial_tick = grub_get_rtc ();
+ do
+ {
+ start_tick = grub_get_rtc ();
+ }
+ while (start_tick == initial_tick);
+ start_tsc = grub_get_tsc ();
+
+ /* Wait for the start of the next tick. This will
+ be the end of the 1-tick period. */
+ do
+ {
+ end_tick = grub_get_rtc ();
+ }
+ while (end_tick - start_tick < CALIBRATION_TICKS);
+ end_tsc = grub_get_tsc ();
+
+ tsc_ticks_per_ms =
+ grub_divmod64 (grub_divmod64
+ (end_tsc - start_tsc, end_tick - start_tick, 0)
+ * GRUB_TICKS_PER_SECOND, 1000, 0);
+
+ /* Reference the TSC zero (boot time) to the epoch to
+ get an absolute real time reference. */
+ grub_uint64_t ms_since_boot = grub_divmod64 (end_tsc, tsc_ticks_per_ms, 0);
+ grub_uint64_t mstime_now = grub_divmod64 ((grub_uint64_t) 1000 * end_tick,
+ GRUB_TICKS_PER_SECOND,
+ 0);
+ tsc_boot_time = mstime_now - ms_since_boot;
+}
=== modified file 'kern/ieee1275/init.c'
--- kern/ieee1275/init.c 2008-07-04 02:01:55 +0000
+++ kern/ieee1275/init.c 2008-07-04 18:06:56 +0000
@@ -47,12 +47,6 @@
extern char _end[];
void
-grub_millisleep (grub_uint32_t ms)
-{
- grub_millisleep_generic (ms);
-}
-
-void
grub_exit (void)
{
grub_ieee1275_exit ();
@@ -260,8 +254,8 @@
grub_console_fini ();
}
-grub_uint32_t
-grub_get_rtc (void)
+grub_uint64_t
+grub_get_time_ms (void)
{
grub_uint32_t msecs = 0;
@@ -270,6 +264,12 @@
return msecs;
}
+grub_uint32_t
+grub_get_rtc (void)
+{
+ return grub_get_time_ms ();
+}
+
grub_addr_t
grub_arch_modules_addr (void)
{
=== modified file 'kern/misc.c'
--- kern/misc.c 2008-06-15 23:42:48 +0000
+++ kern/misc.c 2008-07-04 18:03:26 +0000
@@ -23,7 +23,6 @@
#include
#include
#include
-#include
void *
grub_memmove (void *dest, const void *src, grub_size_t n)
@@ -1018,17 +1017,6 @@
return p - dest;
}
-void
-grub_millisleep_generic (grub_uint32_t ms)
-{
- grub_uint32_t end_at;
-
- end_at = grub_get_rtc () + grub_div_roundup (ms * GRUB_TICKS_PER_SECOND, 1000);
-
- while (grub_get_rtc () < end_at)
- grub_cpu_idle ();
-}
-
/* Abort GRUB. This function does not return. */
void
grub_abort (void)
=== modified file 'kern/sparc64/ieee1275/init.c'
--- kern/sparc64/ieee1275/init.c 2007-10-22 18:59:33 +0000
+++ kern/sparc64/ieee1275/init.c 2008-07-04 17:55:21 +0000
@@ -66,12 +66,6 @@
/* Never reached. */
}
-void
-grub_millisleep (grub_uint32_t ms)
-{
- grub_millisleep_generic (ms);
-}
-
int
grub_ieee1275_test_flag (enum grub_ieee1275_flag flag)
{