=== modified file 'ChangeLog' --- ChangeLog 2008-06-21 14:21:03 +0000 +++ ChangeLog 2008-06-23 14:40:22 +0000 @@ -1,3 +1,87 @@ +2008-06-22 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. + + * 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. + + * 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. Now it is + not included in the kernel when a platform defines a specialized + grub_millisleep() implementation. + + * 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. + + * kern/i386/tsc.c (grub_get_time_ms): x86 TSC support providing a high + resolution clock. + (grub_tsc_calibrate): Static function to calibrate the TSC using RTC. + (grub_time_init): Implemented to call the static grub_tsc_calibrate() + function to calibrate the TSC. + + * include/grub/kernel.h (grub_time_init): Add declaration for the + grub_time_init() platform specific time initialization function. This + function should be implemented by all platforms. If kern/i386/tsc.c + is linked in, it will provide grub_time_init(). + + * 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): Don't define + grub_millisleep() -- it just called grub_millisleep_generic() but now + we just need to link with kern/generic/millisleep.c to use the generic + implementation. + + * kern/i386/pc/init.c (grub_millisleep): Likewise. + + * kern/ieee1275/init.c (grub_millisleep): Don't define + grub_millisleep() -- it just called grub_millisleep_generic() but now + we just need to link with kern/generic/millisleep.c to use the generic + implementation. + (grub_get_time_ms): Implement this required function. Simply uses the + prior implementation of grub_get_rtc(). + (grub_get_rtc): Now calls grub_get_time_ms(), which does the real + work. + (grub_time_init): Define this empty but required function. No further + initialization necessary. + + * kern/sparc64/ieee1275/init.c (grub_millisleep): Don't define + grub_millisleep(), just link with kern/generic/millisleep.c. + (grub_time_init): Define this required function. Does nothing since + the generic RTC-based functions are used. + + * kern/i386/linuxbios/init.c (grub_get_time_ms): + Define grub_get_time_ms() to always return 0. This should be fixed + when RTC functionality is implemented. + (grub_time_init): Define this required function as a no-op. Inserted + a comment to remind us delete this function when proper time support + is added to i386-linuxbios. + + * kern/main.c (grub_main): Call grub_time_init() right after + grub_machine_init(). + + * kern/misc.c (grub_millisleep_generic): 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. + 2008-06-21 Javier Martín * util/i386/pc/grub-setup.c (setup): Remove literal "core.img" in a === modified file 'conf/i386-efi.rmk' --- conf/i386-efi.rmk 2008-06-19 05:14:16 +0000 +++ conf/i386-efi.rmk 2008-06-23 02:29:15 +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-pc.rmk' --- conf/i386-pc.rmk 2008-06-19 05:14:16 +0000 +++ conf/i386-pc.rmk 2008-06-23 02:29:15 +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 05:14:16 +0000 +++ conf/powerpc-ieee1275.rmk 2008-06-23 14:33:56 +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 01:04:59 +0000 +++ conf/sparc64-ieee1275.rmk 2008-06-23 14:33:56 +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-06-17 20:14:24 +0000 @@ -0,0 +1,41 @@ +/* + * 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; +} + +#endif /* ! KERNEL_CPU_TSC_HEADER */ === modified file 'include/grub/kernel.h' --- include/grub/kernel.h 2008-01-21 00:04:04 +0000 +++ include/grub/kernel.h 2008-06-17 15:04:05 +0000 @@ -58,6 +58,9 @@ /* The machine-specific prefix initialization. */ void grub_machine_set_prefix (void); +/* The machine-specific time source initialization. */ +void grub_time_init (void); + /* Register all the exported symbols. This is automatically generated. */ void grub_register_exported_symbols (void); === modified file 'include/grub/time.h' --- include/grub/time.h 2007-10-22 20:02:16 +0000 +++ include/grub/time.h 2008-06-17 20:14:24 +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-06-17 15:04:05 +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-06-23 14:33:56 +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) +{ + /* Note: Perhaps this function should use grub_get_time_ms() instead of + * grub_get_rtc()so it can take advantage of high resolution timing + * on platforms where it is provided. */ + + 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 (); +} === modified file 'kern/i386/efi/init.c' --- kern/i386/efi/init.c 2007-10-22 19:59:33 +0000 +++ kern/i386/efi/init.c 2008-06-17 15:04:05 +0000 @@ -25,13 +25,6 @@ #include #include #include -#include - -void -grub_millisleep (grub_uint32_t ms) -{ - grub_millisleep_generic (ms); -} void grub_machine_init (void) === modified file 'kern/i386/linuxbios/init.c' --- kern/i386/linuxbios/init.c 2008-02-13 20:04:37 +0000 +++ kern/i386/linuxbios/init.c 2008-06-23 14:33:56 +0000 @@ -33,6 +33,7 @@ #include #include #include +#include #define GRUB_FLOPPY_REG_DIGITAL_OUTPUT 0x3f2 @@ -52,6 +53,23 @@ return grub_time_tics; } +grub_uint64_t +grub_get_time_ms (void) +{ + /* FIXME: Delete this function and link to `kern/i386/tsc.c' once RTC + * is implemented. See also comment below in grub_time_init(). */ + return 0; +} + +void +grub_time_init (void) +{ + /* FIXME: Delete this function and link with `kern/i386/tsc.c' once RTC + * is implemented. Until then, this dummy function simply defines + * grub_time_init(), which is called by grub_main() in `kern/main.c'. + * Without RTC, TSC calibration will hang waiting for an RTC edge. */ +} + /* Stop the floppy drive from spinning, so that other software is jumped to with a known state. */ void === modified file 'kern/i386/pc/init.c' --- kern/i386/pc/init.c 2008-06-15 18:21:16 +0000 +++ kern/i386/pc/init.c 2008-06-17 16:28:36 +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))) === added file 'kern/i386/tsc.c' --- kern/i386/tsc.c 1970-01-01 00:00:00 +0000 +++ kern/i386/tsc.c 2008-06-17 15:04:05 +0000 @@ -0,0 +1,99 @@ +/* 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) 2007 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 + +/* 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 = 0; + +/* TSC rate. TSC ticks per millisecond. + * Begin with default (incorrect) value of assuming a 1 GHz machine. + * grub_tsc_calibrate() must be called to set this properly. */ +static grub_uint64_t tsc_ticks_per_ms = 1000000; + + +/* 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 + +/* Called by the machine-specific time initialization function + * grub_time_init() below. */ +static 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; +} + +/* Implement the machine-specific time initialization function. */ +void +grub_time_init (void) +{ + grub_tsc_calibrate (); +} === modified file 'kern/ieee1275/init.c' --- kern/ieee1275/init.c 2008-04-25 19:41:48 +0000 +++ kern/ieee1275/init.c 2008-06-17 15:04:05 +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 (); @@ -257,8 +251,13 @@ grub_console_fini (); } -grub_uint32_t -grub_get_rtc (void) +void +grub_time_init (void) +{ +} + +grub_uint64_t +grub_get_time_ms (void) { grub_uint32_t msecs = 0; @@ -267,6 +266,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/main.c' --- kern/main.c 2008-06-19 20:08:57 +0000 +++ kern/main.c 2008-06-23 02:29:15 +0000 @@ -112,6 +112,7 @@ { /* First of all, initialize the machine. */ grub_machine_init (); + grub_time_init (); /* Hello. */ grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT); === modified file 'kern/misc.c' --- kern/misc.c 2008-06-16 00:42:48 +0000 +++ kern/misc.c 2008-06-17 16:28:36 +0000 @@ -1018,17 +1018,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 19:59:33 +0000 +++ kern/sparc64/ieee1275/init.c 2008-06-23 14:33:56 +0000 @@ -66,10 +66,10 @@ /* Never reached. */ } +/* Define the required grub_time_init() function. */ void -grub_millisleep (grub_uint32_t ms) +grub_time_init (void) { - grub_millisleep_generic (ms); } int