diff --git a/commands/date.c b/commands/date.c
new file mode 100644
index 0000000..2331918
--- /dev/null
+++ b/commands/date.c
@@ -0,0 +1,145 @@
+/* date.c - command to display/set current datetime. */
+/*
+ * 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
+#include
+#include
+
+#define GRUB_DATETIME_SET_YEAR 1
+#define GRUB_DATETIME_SET_MONTH 2
+#define GRUB_DATETIME_SET_DAY 4
+#define GRUB_DATETIME_SET_HOUR 8
+#define GRUB_DATETIME_SET_MINUTE 16
+#define GRUB_DATETIME_SET_SECOND 32
+
+static grub_err_t
+grub_cmd_date (struct grub_arg_list *state __attribute__ ((unused)),
+ int argc, char **args)
+{
+ struct grub_datetime datetime;
+ int limit[6][2] = {{1980, 2079}, {1, 12}, {1, 31}, {0, 23}, {0, 59}, {0, 59}};
+ int value[6], mask;
+
+ if (argc == 0)
+ {
+ if (grub_get_datetime (&datetime))
+ return grub_errno;
+
+ grub_printf ("%d-%02d-%02d %02d:%02d:%02d %s\n",
+ datetime.year, datetime.month, datetime.day,
+ datetime.hour, datetime.minute, datetime.second,
+ grub_get_weekday_name (&datetime));
+
+ return 0;
+ }
+
+ grub_memset (&value, 0, sizeof (value));
+ mask = 0;
+
+ for (; argc; argc--, args++)
+ {
+ char *p, c;
+ int m1, ofs, n, cur_mask;
+
+ p = args[0];
+ m1 = grub_strtoul (p, &p, 10);
+
+ c = *p;
+ if (c == '-')
+ ofs = 0;
+ else if (c == ':')
+ ofs = 3;
+ else
+ goto fail;
+
+ value[ofs] = m1;
+ cur_mask = (1 << ofs);
+ mask &= ~(cur_mask * (1 + 2 + 4));
+
+ for (n = 1; (n < 3) && (*p); n++)
+ {
+ if (*p != c)
+ goto fail;
+
+ value[ofs + n] = grub_strtoul (p + 1, &p, 10);
+ cur_mask |= (1 << (ofs + n));
+ }
+
+ if (*p)
+ goto fail;
+
+ if ((ofs == 0) && (n == 2))
+ {
+ value[ofs + 2] = value[ofs + 1];
+ value[ofs + 1] = value[ofs];
+ ofs++;
+ cur_mask <<= 1;
+ }
+
+ for (; n; n--, ofs++)
+ if ((value [ofs] < limit[ofs][0]) ||
+ (value [ofs] > limit[ofs][1]))
+ goto fail;
+
+ mask |= cur_mask;
+ }
+
+ if (grub_get_datetime (&datetime))
+ return grub_errno;
+
+ if (mask & GRUB_DATETIME_SET_YEAR)
+ datetime.year = value[0];
+
+ if (mask & GRUB_DATETIME_SET_MONTH)
+ datetime.month = value[1];
+
+ if (mask & GRUB_DATETIME_SET_DAY)
+ datetime.day = value[2];
+
+ if (mask & GRUB_DATETIME_SET_HOUR)
+ datetime.hour = value[3];
+
+ if (mask & GRUB_DATETIME_SET_MINUTE)
+ datetime.minute = value[4];
+
+ if (mask & GRUB_DATETIME_SET_SECOND)
+ datetime.second = value[5];
+
+ return grub_set_datetime (&datetime);
+
+fail:
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid datetime");
+}
+
+GRUB_MOD_INIT(date)
+{
+ (void) mod; /* To stop warning. */
+ grub_register_command ("date", grub_cmd_date,
+ GRUB_COMMAND_FLAG_BOTH,
+ "date [[year-]month-day] [hour:minute[:second]]",
+ "Command to display/set current datetime.", 0);
+}
+
+GRUB_MOD_FINI(date)
+{
+ grub_unregister_command ("date");
+}
diff --git a/conf/i386-coreboot.rmk b/conf/i386-coreboot.rmk
index 606b99c..8bd92b9 100644
--- a/conf/i386-coreboot.rmk
+++ b/conf/i386-coreboot.rmk
@@ -102,7 +102,7 @@ pkglib_MODULES = _linux.mod linux.mod normal.mod \
_multiboot.mod multiboot.mod aout.mod \
play.mod cpuid.mod serial.mod ata.mod \
memdisk.mod pci.mod lspci.mod reboot.mod \
- halt.mod
+ halt.mod datetime.mod date.mod
# For _linux.mod.
_linux_mod_SOURCES = loader/i386/pc/linux.c
@@ -187,4 +187,14 @@ lspci_mod_SOURCES = commands/lspci.c
lspci_mod_CFLAGS = $(COMMON_CFLAGS)
lspci_mod_LDFLAGS = $(COMMON_LDFLAGS)
+# For datetime.mod
+datetime_mod_SOURCES = hook/datetime.c lib/i386/datetime.c
+datetime_mod_CFLAGS = $(COMMON_CFLAGS)
+datetime_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For date.mod
+date_mod_SOURCES = commands/date.c
+date_mod_CFLAGS = $(COMMON_CFLAGS)
+date_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
include $(srcdir)/conf/common.mk
diff --git a/conf/i386-efi.rmk b/conf/i386-efi.rmk
index 2ce21b1..5126927 100644
--- a/conf/i386-efi.rmk
+++ b/conf/i386-efi.rmk
@@ -75,7 +75,8 @@ grub_install_SOURCES = util/i386/efi/grub-install.in
# Modules.
pkglib_MODULES = kernel.mod normal.mod _chain.mod chain.mod appleldr.mod \
- _linux.mod linux.mod cpuid.mod halt.mod reboot.mod pci.mod lspci.mod
+ _linux.mod linux.mod cpuid.mod halt.mod reboot.mod pci.mod lspci.mod \
+ datetime.mod date.mod
# For kernel.mod.
kernel_mod_EXPORTS = no
@@ -167,4 +168,14 @@ lspci_mod_SOURCES = commands/lspci.c
lspci_mod_CFLAGS = $(COMMON_CFLAGS)
lspci_mod_LDFLAGS = $(COMMON_LDFLAGS)
+# For datetime.mod
+datetime_mod_SOURCES = hook/datetime.c lib/efi/datetime.c
+datetime_mod_CFLAGS = $(COMMON_CFLAGS)
+datetime_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For date.mod
+date_mod_SOURCES = commands/date.c
+date_mod_CFLAGS = $(COMMON_CFLAGS)
+date_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
include $(srcdir)/conf/common.mk
diff --git a/conf/i386-ieee1275.rmk b/conf/i386-ieee1275.rmk
index a93845e..8ea12f5 100644
--- a/conf/i386-ieee1275.rmk
+++ b/conf/i386-ieee1275.rmk
@@ -104,7 +104,8 @@ grub_install_SOURCES = util/ieee1275/grub-install.in
# Modules.
pkglib_MODULES = normal.mod halt.mod reboot.mod suspend.mod cpuid.mod \
multiboot.mod _multiboot.mod aout.mod serial.mod linux.mod \
- _linux.mod nand.mod memdisk.mod pci.mod lspci.mod
+ _linux.mod nand.mod memdisk.mod pci.mod lspci.mod datetime.mod \
+ date.mod
# For normal.mod.
normal_mod_SOURCES = normal/arg.c normal/cmdline.c normal/command.c \
@@ -188,4 +189,14 @@ lspci_mod_SOURCES = commands/lspci.c
lspci_mod_CFLAGS = $(COMMON_CFLAGS)
lspci_mod_LDFLAGS = $(COMMON_LDFLAGS)
+# For datetime.mod
+datetime_mod_SOURCES = hook/datetime.c lib/i386/datetime.c
+datetime_mod_CFLAGS = $(COMMON_CFLAGS)
+datetime_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For date.mod
+date_mod_SOURCES = commands/date.c
+date_mod_CFLAGS = $(COMMON_CFLAGS)
+date_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
include $(srcdir)/conf/common.mk
diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk
index c1e4ac4..00f94ea 100644
--- a/conf/i386-pc.rmk
+++ b/conf/i386-pc.rmk
@@ -163,7 +163,7 @@ pkglib_MODULES = biosdisk.mod _chain.mod _linux.mod linux.mod normal.mod \
vbe.mod vbetest.mod vbeinfo.mod video.mod gfxterm.mod \
videotest.mod play.mod bitmap.mod tga.mod cpuid.mod serial.mod \
ata.mod vga.mod memdisk.mod jpeg.mod png.mod pci.mod lspci.mod \
- aout.mod _bsd.mod bsd.mod pxe.mod pxecmd.mod
+ aout.mod _bsd.mod bsd.mod pxe.mod pxecmd.mod datetime.mod date.mod
# For biosdisk.mod.
biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
@@ -340,4 +340,14 @@ pxecmd_mod_SOURCES = commands/i386/pc/pxecmd.c
pxecmd_mod_CFLAGS = $(COMMON_CFLAGS)
pxecmd_mod_LDFLAGS = $(COMMON_LDFLAGS)
+# For datetime.mod
+datetime_mod_SOURCES = hook/datetime.c lib/i386/datetime.c
+datetime_mod_CFLAGS = $(COMMON_CFLAGS)
+datetime_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For date.mod
+date_mod_SOURCES = commands/date.c
+date_mod_CFLAGS = $(COMMON_CFLAGS)
+date_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
include $(srcdir)/conf/common.mk
diff --git a/conf/x86_64-efi.rmk b/conf/x86_64-efi.rmk
index 4f8abba..9421c65 100644
--- a/conf/x86_64-efi.rmk
+++ b/conf/x86_64-efi.rmk
@@ -77,7 +77,8 @@ grub_install_SOURCES = util/i386/efi/grub-install.in
# Modules.
pkglib_MODULES = kernel.mod normal.mod _chain.mod chain.mod appleldr.mod \
- cpuid.mod halt.mod reboot.mod _linux.mod linux.mod pci.mod lspci.mod
+ cpuid.mod halt.mod reboot.mod _linux.mod linux.mod pci.mod lspci.mod \
+ datetime.mod date.mod
# For kernel.mod.
kernel_mod_EXPORTS = no
@@ -169,4 +170,14 @@ lspci_mod_SOURCES = commands/lspci.c
lspci_mod_CFLAGS = $(COMMON_CFLAGS)
lspci_mod_LDFLAGS = $(COMMON_LDFLAGS)
+# For datetime.mod
+datetime_mod_SOURCES = hook/datetime.c lib/efi/datetime.c
+datetime_mod_CFLAGS = $(COMMON_CFLAGS)
+datetime_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For date.mod
+date_mod_SOURCES = commands/date.c
+date_mod_CFLAGS = $(COMMON_CFLAGS)
+date_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
include $(srcdir)/conf/common.mk
diff --git a/hook/datetime.c b/hook/datetime.c
new file mode 100644
index 0000000..c43a692
--- /dev/null
+++ b/hook/datetime.c
@@ -0,0 +1,135 @@
+/* datetime.c - Module to export datetime variables. */
+/*
+ * 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
+#include
+#include
+
+static char *grub_weekday_names[] =
+{
+ "Sunday",
+ "Monday",
+ "Tuesday",
+ "Wednesday",
+ "Thursday",
+ "Friday",
+ "Saturday",
+};
+
+static char *grub_datetime_names[] =
+{
+ "YEAR",
+ "MONTH",
+ "DAY",
+ "HOUR",
+ "MINUTE",
+ "SECOND",
+ "WEEKDAY",
+};
+
+int
+grub_get_weekday (struct grub_datetime *datetime)
+{
+ int a, y, m;
+
+ a = (14 - datetime->month) / 12;
+ y = datetime->year - a;
+ m = datetime->month + 12 * a - 2;
+
+ return (datetime->day + y + y / 4 - y / 100 + y / 400 + (31 * m / 12)) % 7;
+}
+
+char *
+grub_get_weekday_name (struct grub_datetime *datetime)
+{
+ return grub_weekday_names[grub_get_weekday (datetime)];
+}
+
+static char *
+grub_read_hook_datetime (struct grub_env_var *var,
+ const char *val __attribute__ ((unused)))
+{
+ struct grub_datetime datetime;
+ static char buf[6];
+
+ buf[0] = 0;
+ if (! grub_get_datetime (&datetime))
+ {
+ int i;
+
+ for (i = 0; i < 7; i++)
+ if (! grub_strcmp (var->name, grub_datetime_names[i]))
+ {
+ int n;
+
+ switch (i)
+ {
+ case 0:
+ n = datetime.year;
+ break;
+ case 1:
+ n = datetime.month;
+ break;
+ case 2:
+ n = datetime.day;
+ break;
+ case 3:
+ n = datetime.hour;
+ break;
+ case 4:
+ n = datetime.minute;
+ break;
+ case 5:
+ n = datetime.second;
+ break;
+ default:
+ return grub_get_weekday_name (&datetime);
+ }
+
+ grub_sprintf (buf, "%d", n);
+ break;
+ }
+ }
+
+ return buf;
+}
+
+GRUB_MOD_INIT(datetime)
+{
+ (void)mod; /* To stop warning. */
+ int i;
+
+ for (i = 0; i < 7; i++)
+ grub_register_variable_hook (grub_datetime_names[i],
+ grub_read_hook_datetime, 0);
+}
+
+GRUB_MOD_FINI(datetime)
+{
+ int i;
+
+ for (i = 0; i < 7; i++)
+ {
+ grub_register_variable_hook (grub_datetime_names[i], 0, 0);
+ grub_env_unset (grub_datetime_names[i]);
+ }
+}
diff --git a/include/grub/i386/cmos.h b/include/grub/i386/cmos.h
new file mode 100644
index 0000000..1c0530d
--- /dev/null
+++ b/include/grub/i386/cmos.h
@@ -0,0 +1,74 @@
+/*
+ * 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 GRUB_CPU_CMOS_H
+#define GRUB_CPU_CMOS_H 1
+
+#include
+#include
+
+#define GRUB_CMOS_ADDR_REG 0x70
+#define GRUB_CMOS_DATA_REG 0x71
+
+#define GRUB_CMOS_INDEX_SECOND 0
+#define GRUB_CMOS_INDEX_SECOND_ALARM 1
+#define GRUB_CMOS_INDEX_MINUTE 2
+#define GRUB_CMOS_INDEX_MINUTE_ALARM 3
+#define GRUB_CMOS_INDEX_HOUR 4
+#define GRUB_CMOS_INDEX_HOUR_ALARM 5
+#define GRUB_CMOS_INDEX_DAY_OF_WEEK 6
+#define GRUB_CMOS_INDEX_DAY_OF_MONTH 7
+#define GRUB_CMOS_INDEX_MONTH 8
+#define GRUB_CMOS_INDEX_YEAR 9
+
+#define GRUB_CMOS_INDEX_STATUS_A 0xA
+#define GRUB_CMOS_INDEX_STATUS_B 0xB
+#define GRUB_CMOS_INDEX_STATUS_C 0xC
+#define GRUB_CMOS_INDEX_STATUS_D 0xD
+
+#define GRUB_CMOS_STATUS_B_DAYLIGHT 1
+#define GRUB_CMOS_STATUS_B_24HOUR 2
+#define GRUB_CMOS_STATUS_B_BINARY 4
+
+static inline grub_uint8_t
+grub_bcd_to_num (grub_uint8_t a)
+{
+ return ((a >> 4) * 10 + (a & 0xF));
+}
+
+static inline grub_uint8_t
+grub_num_to_bcd (grub_uint8_t a)
+{
+ return (((a / 10) << 4) + (a % 10));
+}
+
+static inline grub_uint8_t
+grub_cmos_read (grub_uint8_t index)
+{
+ grub_outb (index, GRUB_CMOS_ADDR_REG);
+ return grub_inb (GRUB_CMOS_DATA_REG);
+}
+
+static inline void
+grub_cmos_write (grub_uint8_t index, grub_uint8_t value)
+{
+ grub_outb (index, GRUB_CMOS_ADDR_REG);
+ grub_outb (value, GRUB_CMOS_DATA_REG);
+}
+
+#endif /* GRUB_CPU_CMOS_H */
diff --git a/include/grub/lib/datetime.h b/include/grub/lib/datetime.h
new file mode 100644
index 0000000..7b140cc
--- /dev/null
+++ b/include/grub/lib/datetime.h
@@ -0,0 +1,44 @@
+/*
+ * 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_DATETIME_HEADER
+#define KERNEL_DATETIME_HEADER 1
+
+#include
+#include
+
+struct grub_datetime
+{
+ grub_uint16_t year;
+ grub_uint8_t month;
+ grub_uint8_t day;
+ grub_uint8_t hour;
+ grub_uint8_t minute;
+ grub_uint8_t second;
+};
+
+/* Return date and time. */
+grub_err_t grub_get_datetime (struct grub_datetime *datetime);
+
+/* Set date and time. */
+grub_err_t grub_set_datetime (struct grub_datetime *datetime);
+
+int grub_get_weekday (struct grub_datetime *datetime);
+char *grub_get_weekday_name (struct grub_datetime *datetime);
+
+#endif /* ! KERNEL_DATETIME_HEADER */
diff --git a/lib/efi/datetime.c b/lib/efi/datetime.c
new file mode 100644
index 0000000..9fa72fd
--- /dev/null
+++ b/lib/efi/datetime.c
@@ -0,0 +1,79 @@
+/* kern/efi/datetime.c - efi datetime function.
+ *
+ * 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
+#include
+
+grub_err_t
+grub_get_datetime (struct grub_datetime *datetime)
+{
+ grub_efi_status_t status;
+ struct grub_efi_time efi_time;
+
+ status = efi_call_2 (grub_efi_system_table->runtime_services->get_time,
+ &efi_time, 0);
+
+ if (status)
+ return grub_error (GRUB_ERR_INVALID_COMMAND,
+ "can\'t get datetime using efi");
+ else
+ {
+ datetime->year = efi_time.year;
+ datetime->month = efi_time.month;
+ datetime->day = efi_time.day;
+ datetime->hour = efi_time.hour;
+ datetime->minute = efi_time.minute;
+ datetime->second = efi_time.second;
+ }
+
+ return 0;
+}
+
+grub_err_t
+grub_set_datetime (struct grub_datetime *datetime)
+{
+ grub_efi_status_t status;
+ struct grub_efi_time efi_time;
+
+ status = efi_call_2 (grub_efi_system_table->runtime_services->get_time,
+ &efi_time, 0);
+
+ if (status)
+ return grub_error (GRUB_ERR_INVALID_COMMAND,
+ "can\'t get datetime using efi");
+
+ efi_time.year = datetime->year;
+ efi_time.month = datetime->month;
+ efi_time.day = datetime->day;
+ efi_time.hour = datetime->hour;
+ efi_time.minute = datetime->minute;
+ efi_time.second = datetime->second;
+
+ status = efi_call_1 (grub_efi_system_table->runtime_services->set_time,
+ &efi_time);
+
+ if (status)
+ return grub_error (GRUB_ERR_INVALID_COMMAND,
+ "can\'t set datetime using efi");
+
+ return 0;
+}
diff --git a/lib/i386/datetime.c b/lib/i386/datetime.c
new file mode 100644
index 0000000..1e59746
--- /dev/null
+++ b/lib/i386/datetime.c
@@ -0,0 +1,155 @@
+/* kern/i386/datetime.c - x86 CMOS datetime function.
+ *
+ * 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
+
+grub_err_t
+grub_get_datetime (struct grub_datetime *datetime)
+{
+ int is_bcd, is_12hour;
+ grub_uint8_t value, flag;
+
+ flag = grub_cmos_read (GRUB_CMOS_INDEX_STATUS_B);
+
+ is_bcd = ! (flag & GRUB_CMOS_STATUS_B_BINARY);
+
+ value = grub_cmos_read (GRUB_CMOS_INDEX_YEAR);
+ if (is_bcd)
+ value = grub_bcd_to_num (value);
+
+ datetime->year = value;
+ datetime->year += (value < 80) ? 2000 : 1900;
+
+ value = grub_cmos_read (GRUB_CMOS_INDEX_MONTH);
+ if (is_bcd)
+ value = grub_bcd_to_num (value);
+
+ datetime->month = value;
+
+ value = grub_cmos_read (GRUB_CMOS_INDEX_DAY_OF_MONTH);
+ if (is_bcd)
+ value = grub_bcd_to_num (value);
+
+ datetime->day = value;
+
+ is_12hour = ! (flag & GRUB_CMOS_STATUS_B_24HOUR);
+
+ value = grub_cmos_read (GRUB_CMOS_INDEX_HOUR);
+ if (is_12hour)
+ {
+ is_12hour = (value & 0x80);
+
+ value &= 0x7F;
+ value--;
+ }
+
+ if (is_bcd)
+ value = grub_bcd_to_num (value);
+
+ if (is_12hour)
+ value += 12;
+
+ datetime->hour = value;
+
+ value = grub_cmos_read (GRUB_CMOS_INDEX_MINUTE);
+ if (is_bcd)
+ value = grub_bcd_to_num (value);
+
+ datetime->minute = value;
+
+ value = grub_cmos_read (GRUB_CMOS_INDEX_SECOND);
+ if (is_bcd)
+ value = grub_bcd_to_num (value);
+
+ datetime->second = value;
+
+ return 0;
+}
+
+grub_err_t
+grub_set_datetime (struct grub_datetime *datetime)
+{
+ int is_bcd, is_12hour;
+ grub_uint8_t value, flag;
+
+ flag = grub_cmos_read (GRUB_CMOS_INDEX_STATUS_B);
+
+ is_bcd = ! (flag & GRUB_CMOS_STATUS_B_BINARY);
+
+ value = ((datetime->year >= 2000) ? datetime->year - 2000 :
+ datetime->year - 1900);
+
+ if (is_bcd)
+ value = grub_num_to_bcd (value);
+
+ grub_cmos_write (GRUB_CMOS_INDEX_YEAR, value);
+
+ value = datetime->month;
+
+ if (is_bcd)
+ value = grub_num_to_bcd (value);
+
+ grub_cmos_write (GRUB_CMOS_INDEX_MONTH, value);
+
+ value = datetime->day;
+
+ if (is_bcd)
+ value = grub_num_to_bcd (value);
+
+ grub_cmos_write (GRUB_CMOS_INDEX_DAY_OF_MONTH, value);
+
+ value = datetime->hour;
+
+ is_12hour = (! (flag & GRUB_CMOS_STATUS_B_24HOUR));
+
+ if (is_12hour)
+ {
+ value++;
+
+ if (value > 12)
+ value -= 12;
+ else
+ is_12hour = 0;
+ }
+
+ if (is_bcd)
+ value = grub_num_to_bcd (value);
+
+ if (is_12hour)
+ value |= 0x80;
+
+ grub_cmos_write (GRUB_CMOS_INDEX_HOUR, value);
+
+ value = datetime->minute;
+
+ if (is_bcd)
+ value = grub_num_to_bcd (value);
+
+ grub_cmos_write (GRUB_CMOS_INDEX_MINUTE, value);
+
+ value = datetime->second;
+
+ if (is_bcd)
+ value = grub_num_to_bcd (value);
+
+ grub_cmos_write (GRUB_CMOS_INDEX_SECOND, value);
+
+ return 0;
+}