# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: colin@gibibit.com-20080728165130-7dnmz92ea1uthuuy # target_branch: file:///home/cdb/grub/repo/trunk-clean/ # testament_sha1: 98a7e6a7e489ab64ab380972bef74ac3cf9cc782 # timestamp: 2008-07-28 09:52:30 -0700 # source_branch: http://grub.gibibit.com/bzr/trunk-clean # base_revision_id: colin@gibibit.com-20080728020000-lhbskg67rnn3gfp7 # # Begin patch === modified file 'ChangeLog' --- ChangeLog 2008-07-27 19:57:43 +0000 +++ ChangeLog 2008-07-28 16:51:30 +0000 @@ -1,3 +1,68 @@ +2008-07-28 Colin D Bennett + + High resolution timer support. Implemented for x86 CPUs using TSC. + Extracted generic grub_millisleep() so it's linked in only as needed. + This requires a Pentium compatible CPU; if the RDTSC instruction is + not supported, then it falls back on the generic grub_get_time_ms() + implementation that uses the machine's RTC. + + * conf/i386-efi.rmk: Added new source files to kernel_elf_SOURCES. + + * conf/i386-pc.rmk: Likewise. + + * conf/sparc64-ieee1275.rmk: Likewise. + + * conf/powerpc-ieee1275.rmk: Likewise. + + * conf/i386-coreboot.rmk: Likewise. + + * kern/generic/rtc_get_time_ms.c: New file. + + * kern/generic/millisleep.c: New file. + + * kern/misc.c (grub_millisleep_generic): Removed. + + * 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. New inline + function. + (grub_cpu_is_cpuid_supported): New inline function. + (grub_cpu_is_tsc_supported): New inline function. + (grub_tsc_init): New function prototype. + (grub_tsc_get_time_ms): New function prototype. + + * kern/i386/tsc.c (grub_get_time_ms): New file. New function. + (calibrate_tsc): New static function. + (grub_tsc_init): New function. + + * include/grub/time.h (grub_millisleep_generic): Removed. + (grub_get_time_ms): New function. + (grub_install_get_time_ms): New function. + + * kern/time.c (grub_get_time_ms): New function. + (grub_install_get_time_ms): New function. + + * kern/i386/efi/init.c (grub_millisleep): Removed. + (grub_machine_init): Call grub_tsc_init. + + * kern/i386/linuxbios/init.c (grub_machine_init): Install the RTC + get_time_ms() implementation. + + * kern/sparc64/ieee1275/init.c (grub_millisleep): Removed. + (ieee1275_get_time_ms): New function. + (grub_machine_init): Install get_time_ms() implementation. + + * kern/i386/pc/init.c (grub_machine_init): Call grub_tsc_init(). + (grub_millisleep): Removed. + + * kern/ieee1275/init.c (grub_millisleep): Removed. + (grub_machine_init): Install ieee1275_get_time_ms() implementation. + (ieee1275_get_time_ms): New static function. + (grub_get_rtc): Now calls ieee1275_get_time_ms(), which does the real + work. + 2008-07-27 Robert Millan * disk/ata.c (grub_ata_dumpinfo): Use grub_dprintf() for debugging === 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-coreboot.rmk' --- conf/i386-coreboot.rmk 2008-07-27 12:51:30 +0000 +++ conf/i386-coreboot.rmk 2008-07-28 16:23:46 +0000 @@ -16,6 +16,7 @@ kern/main.c kern/device.c \ 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/time.c \ kern/i386/dl.c kern/parser.c kern/partition.c \ kern/env.c \ term/i386/pc/console.c \ === modified file 'conf/i386-efi.rmk' --- conf/i386-efi.rmk 2008-07-27 12:51:30 +0000 +++ conf/i386-efi.rmk 2008-07-28 16:23:46 +0000 @@ -84,7 +84,10 @@ 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/rtc_get_time_ms.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-ieee1275.rmk' --- conf/i386-ieee1275.rmk 2008-07-27 12:51:30 +0000 +++ conf/i386-ieee1275.rmk 2008-07-28 16:23:46 +0000 @@ -19,6 +19,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/millisleep.c \ kern/ieee1275/ieee1275.c \ term/ieee1275/ofconsole.c term/i386/pc/at_keyboard.c \ disk/ieee1275/ofdisk.c \ === modified file 'conf/i386-pc.rmk' --- conf/i386-pc.rmk 2008-07-27 12:51:30 +0000 +++ conf/i386-pc.rmk 2008-07-28 16:23:46 +0000 @@ -42,7 +42,11 @@ kernel_img_SOURCES = kern/i386/pc/startup.S kern/main.c kern/device.c \ 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/time.c \ kern/i386/dl.c kern/i386/pc/init.c kern/parser.c kern/partition.c \ + kern/i386/tsc.c \ + kern/generic/rtc_get_time_ms.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-07-27 12:51:30 +0000 +++ conf/powerpc-ieee1275.rmk 2008-07-28 13:59:50 +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) === modified file 'conf/x86_64-efi.rmk' --- conf/x86_64-efi.rmk 2008-07-27 12:51:30 +0000 +++ conf/x86_64-efi.rmk 2008-07-28 16:23:46 +0000 @@ -87,6 +87,7 @@ kern/misc.c kern/mm.c kern/loader.c kern/rescue.c kern/term.c \ kern/x86_64/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 \ + kern/generic/millisleep.c kern/generic/rtc_get_time_ms.c \ term/efi/console.c disk/efi/efidisk.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 \ === 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-28 16:23:46 +0000 @@ -0,0 +1,80 @@ +/* + * 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; +} + +static __inline int +grub_cpu_is_cpuid_supported (void) +{ + grub_uint32_t id_supported; + + __asm__ ("pushfl\n\t" + "popl %%eax /* Get EFLAGS into EAX */\n\t" + "movl %%eax, %%ecx /* Save original flags in ECX */\n\t" + "xorl $0x200000, %%eax /* Flip ID bit in EFLAGS */\n\t" + "pushl %%eax /* Store modified EFLAGS on stack */\n\t" + "popfl /* Replace current EFLAGS */\n\t" + "pushfl /* Read back the EFLAGS */\n\t" + "popl %%eax /* Get EFLAGS into EAX */\n\t" + "xorl %%ecx, %%eax /* Check if flag could be modified */\n\t" + : "=a" (id_supported) + : /* No inputs. */ + : /* Clobbered: */ "%rcx"); + + return id_supported != 0; +} + +static __inline int +grub_cpu_is_tsc_supported (void) +{ + if (! grub_cpu_is_cpuid_supported ()) + return 0; + + grub_uint32_t features; + __asm__ ("movl $1, %%eax\n\t" + "cpuid" + : "=d" (features) + : /* No inputs. */ + : /* Clobbered: */ "%rax", "%rbx", "%rcx"); + return (features & (1 << 4)) != 0; +} + +void grub_tsc_init (void); +grub_uint64_t grub_tsc_get_time_ms (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-28 16:51:30 +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,15 @@ #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); + +grub_uint64_t grub_rtc_get_time_ms (void); static __inline void grub_sleep (grub_uint32_t s) @@ -32,4 +35,6 @@ grub_millisleep (1000 * s); } +void grub_install_get_time_ms (grub_uint64_t (*get_time_ms_func) (void)); + #endif /* ! KERNEL_TIME_HEADER */ === added directory 'kern/generic' === added file 'kern/generic/millisleep.c' --- kern/generic/millisleep.c 1970-01-01 00:00:00 +0000 +++ kern/generic/millisleep.c 2008-07-28 16:51:30 +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 (); +} === added file 'kern/generic/rtc_get_time_ms.c' --- kern/generic/rtc_get_time_ms.c 1970-01-01 00:00:00 +0000 +++ kern/generic/rtc_get_time_ms.c 2008-07-28 16:51:30 +0000 @@ -0,0 +1,37 @@ +/* rtc_get_time_ms.c - get_time_ms 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_rtc_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); +} === 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-28 16:23:46 +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_init (); } void === modified file 'kern/i386/linuxbios/init.c' --- kern/i386/linuxbios/init.c 2008-07-04 02:26:10 +0000 +++ kern/i386/linuxbios/init.c 2008-07-28 16:23:46 +0000 @@ -149,6 +149,8 @@ /* This variable indicates size, not offset. */ grub_upper_mem -= GRUB_MEMORY_MACHINE_UPPER_START; + + grub_install_get_time_ms (grub_rtc_get_time_ms); } 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-28 16:23:46 +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_init (); } void === added file 'kern/i386/tsc.c' --- kern/i386/tsc.c 1970-01-01 00:00:00 +0000 +++ kern/i386/tsc.c 2008-07-28 16:51:30 +0000 @@ -0,0 +1,102 @@ +/* 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; + + +grub_uint64_t +grub_tsc_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. */ +static void +calibrate_tsc (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; +} + +void +grub_tsc_init (void) +{ + if (grub_cpu_is_tsc_supported ()) + { + calibrate_tsc (); + grub_install_get_time_ms (grub_tsc_get_time_ms); + } + else + { + grub_install_get_time_ms (grub_rtc_get_time_ms); + } +} === modified file 'kern/ieee1275/init.c' --- kern/ieee1275/init.c 2008-07-04 02:01:55 +0000 +++ kern/ieee1275/init.c 2008-07-28 16:23:46 +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 (); @@ -202,6 +196,8 @@ #endif +static grub_uint64_t ieee1275_get_time_ms (void); + void grub_machine_init (void) { @@ -251,6 +247,8 @@ } } } + + grub_install_get_time_ms (ieee1275_get_time_ms); } void @@ -260,8 +258,8 @@ grub_console_fini (); } -grub_uint32_t -grub_get_rtc (void) +static grub_uint64_t +ieee1275_get_time_ms (void) { grub_uint32_t msecs = 0; @@ -270,6 +268,12 @@ return msecs; } +grub_uint32_t +grub_get_rtc (void) +{ + return ieee1275_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-28 16:23:46 +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) { @@ -145,6 +139,8 @@ grub_free (prefix); } +grub_uint64_t ieee1275_get_time_ms (void); + void grub_machine_init (void) { @@ -201,6 +197,7 @@ } } + grub_install_get_time_ms (ieee1275_get_time_ms); } void @@ -216,6 +213,12 @@ grub_ieee1275_enter (); } +grub_uint64_t +ieee1275_get_time_ms (void) +{ + return grub_get_rtc (); +} + grub_uint32_t grub_get_rtc (void) { === added file 'kern/time.c' --- kern/time.c 1970-01-01 00:00:00 +0000 +++ kern/time.c 2008-07-28 16:23:46 +0000 @@ -0,0 +1,37 @@ +/* time.c - kernel time functions */ +/* + * 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 + +typedef grub_uint64_t (*get_time_ms_func_t) (void); + +/* Function pointer to the implementation in use. */ +static get_time_ms_func_t get_time_ms_func; + +grub_uint64_t +grub_get_time_ms (void) +{ + return get_time_ms_func (); +} + +void +grub_install_get_time_ms (get_time_ms_func_t func) +{ + get_time_ms_func = func; +} # Begin bundle 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