summaryrefslogtreecommitdiffstats
path: root/grub-core/commands/i386
diff options
context:
space:
mode:
Diffstat (limited to 'grub-core/commands/i386')
-rw-r--r--grub-core/commands/i386/cmosdump.c64
-rw-r--r--grub-core/commands/i386/cmostest.c124
-rw-r--r--grub-core/commands/i386/coreboot/cb_timestamps.c126
-rw-r--r--grub-core/commands/i386/coreboot/cbls.c143
-rw-r--r--grub-core/commands/i386/cpuid.c125
-rw-r--r--grub-core/commands/i386/pc/drivemap.c428
-rw-r--r--grub-core/commands/i386/pc/drivemap_int13h.S124
-rw-r--r--grub-core/commands/i386/pc/halt.c126
-rw-r--r--grub-core/commands/i386/pc/lsapm.c115
-rw-r--r--grub-core/commands/i386/pc/play.c197
-rw-r--r--grub-core/commands/i386/pc/sendkey.c387
-rw-r--r--grub-core/commands/i386/pc/smbios.c52
-rw-r--r--grub-core/commands/i386/rdmsr.c102
-rw-r--r--grub-core/commands/i386/wrmsr.c94
14 files changed, 2207 insertions, 0 deletions
diff --git a/grub-core/commands/i386/cmosdump.c b/grub-core/commands/i386/cmosdump.c
new file mode 100644
index 0000000..626485c
--- /dev/null
+++ b/grub-core/commands/i386/cmosdump.c
@@ -0,0 +1,64 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009,2013 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/command.h>
+#include <grub/misc.h>
+#include <grub/cmos.h>
+#include <grub/i18n.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_err_t
+grub_cmd_cmosdump (struct grub_command *cmd __attribute__ ((unused)),
+ int argc __attribute__ ((unused)), char *argv[] __attribute__ ((unused)))
+{
+ int i;
+
+ for (i = 0; i < 256; i++)
+ {
+ grub_err_t err;
+ grub_uint8_t value;
+ if ((i & 0xf) == 0)
+ grub_printf ("%02x: ", i);
+
+ err = grub_cmos_read (i, &value);
+ if (err)
+ return err;
+
+ grub_printf ("%02x ", value);
+ if ((i & 0xf) == 0xf)
+ grub_printf ("\n");
+ }
+ return GRUB_ERR_NONE;
+}
+
+static grub_command_t cmd;
+
+
+GRUB_MOD_INIT(cmosdump)
+{
+ cmd = grub_register_command ("cmosdump", grub_cmd_cmosdump,
+ 0,
+ N_("Show raw dump of the CMOS contents."));
+}
+
+GRUB_MOD_FINI(cmosdump)
+{
+ grub_unregister_command (cmd);
+}
diff --git a/grub-core/commands/i386/cmostest.c b/grub-core/commands/i386/cmostest.c
new file mode 100644
index 0000000..9f6b56a
--- /dev/null
+++ b/grub-core/commands/i386/cmostest.c
@@ -0,0 +1,124 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/command.h>
+#include <grub/misc.h>
+#include <grub/cmos.h>
+#include <grub/i18n.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_err_t
+parse_args (int argc, char *argv[], int *byte, int *bit)
+{
+ const char *rest;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "address required");
+
+ *byte = grub_strtoul (argv[0], &rest, 0);
+ if (*rest != ':')
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "address required");
+
+ *bit = grub_strtoul (rest + 1, 0, 0);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_cmostest (struct grub_command *cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ int byte = 0, bit = 0;
+ grub_err_t err;
+ grub_uint8_t value;
+
+ err = parse_args (argc, argv, &byte, &bit);
+ if (err)
+ return err;
+
+ err = grub_cmos_read (byte, &value);
+ if (err)
+ return err;
+
+ if (value & (1 << bit))
+ return GRUB_ERR_NONE;
+
+ return grub_error (GRUB_ERR_TEST_FAILURE, N_("false"));
+}
+
+static grub_err_t
+grub_cmd_cmosclean (struct grub_command *cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ int byte = 0, bit = 0;
+ grub_err_t err;
+ grub_uint8_t value;
+
+ err = parse_args (argc, argv, &byte, &bit);
+ if (err)
+ return err;
+ err = grub_cmos_read (byte, &value);
+ if (err)
+ return err;
+
+ return grub_cmos_write (byte, value & (~(1 << bit)));
+}
+
+static grub_err_t
+grub_cmd_cmosset (struct grub_command *cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ int byte = 0, bit = 0;
+ grub_err_t err;
+ grub_uint8_t value;
+
+ err = parse_args (argc, argv, &byte, &bit);
+ if (err)
+ return err;
+ err = grub_cmos_read (byte, &value);
+ if (err)
+ return err;
+
+ return grub_cmos_write (byte, value | (1 << bit));
+}
+
+static grub_command_t cmd, cmd_clean, cmd_set;
+
+
+GRUB_MOD_INIT(cmostest)
+{
+ cmd = grub_register_command ("cmostest", grub_cmd_cmostest,
+ N_("BYTE:BIT"),
+ N_("Test bit at BYTE:BIT in CMOS."));
+ cmd_clean = grub_register_command ("cmosclean", grub_cmd_cmosclean,
+ N_("BYTE:BIT"),
+ N_("Clear bit at BYTE:BIT in CMOS."));
+ cmd_set = grub_register_command ("cmosset", grub_cmd_cmosset,
+ N_("BYTE:BIT"),
+ /* TRANSLATORS: A bit may be either set (1) or clear (0). */
+ N_("Set bit at BYTE:BIT in CMOS."));
+}
+
+GRUB_MOD_FINI(cmostest)
+{
+ grub_unregister_command (cmd);
+ grub_unregister_command (cmd_clean);
+ grub_unregister_command (cmd_set);
+}
diff --git a/grub-core/commands/i386/coreboot/cb_timestamps.c b/grub-core/commands/i386/coreboot/cb_timestamps.c
new file mode 100644
index 0000000..e97ea6b
--- /dev/null
+++ b/grub-core/commands/i386/coreboot/cb_timestamps.c
@@ -0,0 +1,126 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+#include <grub/coreboot/lbio.h>
+#include <grub/i386/tsc.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_uint32_t
+tsc2ms (grub_uint64_t tsc)
+{
+ grub_uint64_t ah = tsc >> 32;
+ grub_uint64_t al = tsc & 0xffffffff;
+
+ return ((al * grub_tsc_rate) >> 32) + ah * grub_tsc_rate;
+}
+
+static const char *descs[] = {
+ [1] = "romstage",
+ [2] = "before RAM init",
+ [3] = "after RAM init",
+ [4] = "end of romstage",
+ [5] = "start of verified boot",
+ [6] = "end of verified boot",
+ [8] = "start of RAM copy",
+ [9] = "end of RAM copy",
+ [10] = "start of ramstage",
+ [11] = "start of bootblock",
+ [12] = "end of bootblock",
+ [13] = "starting to load romstage",
+ [14] = "finished loading romstage",
+ [15] = "starting LZMA decompress (ignore for x86)",
+ [16] = "finished LZMA decompress (ignore for x86)",
+ [30] = "device enumerate",
+ [40] = "device configure",
+ [50] = "device enable",
+ [60] = "device initialize",
+ [70] = "device done",
+ [75] = "CBMEM POST",
+ [80] = "writing tables",
+ [90] = "loading payload",
+ [98] = "wake jump",
+ [99] = "selfboot jump",
+};
+
+static int
+iterate_linuxbios_table (grub_linuxbios_table_item_t table_item,
+ void *data)
+{
+ int *available = data;
+ grub_uint64_t last_tsc = 0;
+ struct grub_linuxbios_timestamp_table *ts_table;
+ unsigned i;
+
+ if (table_item->tag != GRUB_LINUXBIOS_MEMBER_TIMESTAMPS)
+ return 0;
+
+ *available = 1;
+ ts_table = (struct grub_linuxbios_timestamp_table *) (grub_addr_t)
+ *(grub_uint64_t *) (table_item + 1);
+
+ for (i = 0; i < ts_table->used; i++)
+ {
+ grub_uint32_t tmabs = tsc2ms (ts_table->entries[i].tsc);
+ grub_uint32_t tmrel = tsc2ms (ts_table->entries[i].tsc - last_tsc);
+ last_tsc = ts_table->entries[i].tsc;
+
+ grub_printf ("%3d.%03ds %2d.%03ds %02d %s\n",
+ tmabs / 1000, tmabs % 1000, tmrel / 1000, tmrel % 1000,
+ ts_table->entries[i].id,
+ (ts_table->entries[i].id < ARRAY_SIZE (descs)
+ && descs[ts_table->entries[i].id])
+ ? descs[ts_table->entries[i].id] : "");
+ }
+ return 1;
+}
+
+
+static grub_err_t
+grub_cmd_coreboot_boottime (struct grub_command *cmd __attribute__ ((unused)),
+ int argc __attribute__ ((unused)),
+ char *argv[] __attribute__ ((unused)))
+{
+ int available = 0;
+
+ grub_linuxbios_table_iterate (iterate_linuxbios_table, &available);
+ if (!available)
+ {
+ grub_puts_ (N_("No boot time statistics is available\n"));
+ return 0;
+ }
+ return 0;
+}
+
+static grub_command_t cmd_boottime;
+
+GRUB_MOD_INIT(cbtime)
+{
+ cmd_boottime =
+ grub_register_command ("coreboot_boottime", grub_cmd_coreboot_boottime,
+ 0, N_("Show coreboot boot time statistics."));
+}
+
+GRUB_MOD_FINI(cbtime)
+{
+ grub_unregister_command (cmd_boottime);
+}
diff --git a/grub-core/commands/i386/coreboot/cbls.c b/grub-core/commands/i386/coreboot/cbls.c
new file mode 100644
index 0000000..102291f
--- /dev/null
+++ b/grub-core/commands/i386/coreboot/cbls.c
@@ -0,0 +1,143 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+#include <grub/coreboot/lbio.h>
+#include <grub/i386/tsc.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static const char *console_descs[] = {
+ "8250 UART",
+ "VGA",
+ "BTEXT",
+ "log buffer console",
+ "SROM",
+ "EHCI debug",
+ "memory-mapped 8250 UART"
+};
+
+static const char *descs[] = {
+ [GRUB_LINUXBIOS_MEMBER_MEMORY] = "memory map (`lsmmap' to list)",
+ [GRUB_LINUXBIOS_MEMBER_MAINBOARD] = "mainboard",
+ [4] = "version",
+ [5] = "extra version",
+ [6] = "build",
+ [7] = "compile time",
+ [8] = "compile by",
+ [9] = "compile host",
+ [0xa] = "compile domain",
+ [0xb] = "compiler",
+ [0xc] = "linker",
+ [0xd] = "assembler",
+ [0xf] = "serial",
+ [GRUB_LINUXBIOS_MEMBER_CONSOLE] = "console",
+ [GRUB_LINUXBIOS_MEMBER_FRAMEBUFFER] = "framebuffer",
+ [0x13] = "GPIO",
+ [0x15] = "VDAT",
+ [GRUB_LINUXBIOS_MEMBER_TIMESTAMPS] = "timestamps (`coreboot_boottime' to list)",
+ [GRUB_LINUXBIOS_MEMBER_CBMEMC] = "CBMEM console (`cbmemc' to list)",
+ [0x18] = "MRC cache",
+ [0x19] = "VBNV",
+ [0xc8] = "CMOS option table",
+ [0xc9] = "CMOS option",
+ [0xca] = "CMOS option enum",
+ [0xcb] = "CMOS option defaults",
+ [0xcc] = "CMOS checksum",
+};
+
+static int
+iterate_linuxbios_table (grub_linuxbios_table_item_t table_item,
+ void *data __attribute__ ((unused)))
+{
+ if (table_item->tag < ARRAY_SIZE (descs) && descs[table_item->tag])
+ grub_printf ("tag=%02x size=%02x %s",
+ table_item->tag, table_item->size, descs[table_item->tag]);
+ else
+ grub_printf ("tag=%02x size=%02x",
+ table_item->tag, table_item->size);
+
+ switch (table_item->tag)
+ {
+ case GRUB_LINUXBIOS_MEMBER_FRAMEBUFFER:
+ {
+ struct grub_linuxbios_table_framebuffer *fb;
+ fb = (struct grub_linuxbios_table_framebuffer *) (table_item + 1);
+
+ grub_printf (": %dx%dx%d pitch=%d lfb=0x%llx %d/%d/%d/%d %d/%d/%d/%d",
+ fb->width, fb->height,
+ fb->bpp, fb->pitch,
+ (unsigned long long) fb->lfb,
+ fb->red_mask_size, fb->green_mask_size,
+ fb->blue_mask_size, fb->reserved_mask_size,
+ fb->red_field_pos, fb->green_field_pos,
+ fb->blue_field_pos, fb->reserved_field_pos);
+ break;
+ }
+ case GRUB_LINUXBIOS_MEMBER_MAINBOARD:
+ {
+ struct grub_linuxbios_mainboard *mb;
+ mb = (struct grub_linuxbios_mainboard *) (table_item + 1);
+ grub_printf (": vendor=`%s' part_number=`%s'",
+ mb->strings + mb->vendor,
+ mb->strings + mb->part_number);
+ break;
+ }
+ case 0x04 ... 0x0d:
+ grub_printf (": `%s'", (char *) (table_item + 1));
+ break;
+ case GRUB_LINUXBIOS_MEMBER_CONSOLE:
+ {
+ grub_uint16_t *val = (grub_uint16_t *) (table_item + 1);
+ grub_printf (": id=%d", *val);
+ if (*val < ARRAY_SIZE (console_descs)
+ && console_descs[*val])
+ grub_printf (" %s", console_descs[*val]);
+ }
+ }
+ grub_printf ("\n");
+
+ return 0;
+}
+
+
+static grub_err_t
+grub_cmd_lscoreboot (struct grub_command *cmd __attribute__ ((unused)),
+ int argc __attribute__ ((unused)),
+ char *argv[] __attribute__ ((unused)))
+{
+ grub_linuxbios_table_iterate (iterate_linuxbios_table, 0);
+ return 0;
+}
+
+static grub_command_t cmd;
+
+GRUB_MOD_INIT(cbls)
+{
+ cmd =
+ grub_register_command ("lscoreboot", grub_cmd_lscoreboot,
+ 0, N_("List coreboot tables."));
+}
+
+GRUB_MOD_FINI(cbls)
+{
+ grub_unregister_command (cmd);
+}
diff --git a/grub-core/commands/i386/cpuid.c b/grub-core/commands/i386/cpuid.c
new file mode 100644
index 0000000..42b9841
--- /dev/null
+++ b/grub-core/commands/i386/cpuid.c
@@ -0,0 +1,125 @@
+/* cpuid.c - test for CPU features */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2006, 2007, 2009 Free Software Foundation, Inc.
+ * Based on gcc/gcc/config/i386/driver-i386.c
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/env.h>
+#include <grub/command.h>
+#include <grub/extcmd.h>
+#include <grub/i386/cpuid.h>
+#include <grub/i18n.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static const struct grub_arg_option options[] =
+ {
+ /* TRANSLATORS: "(default)" at the end means that this option is used if
+ no argument is specified. */
+ {"long-mode", 'l', 0, N_("Check if CPU supports 64-bit (long) mode (default)."), 0, 0},
+ {"pae", 'p', 0, N_("Check if CPU supports Physical Address Extension."), 0, 0},
+ {0, 0, 0, 0, 0, 0}
+ };
+
+enum
+ {
+ MODE_LM = 0,
+ MODE_PAE = 1
+ };
+
+enum
+ {
+ bit_PAE = (1 << 6),
+ };
+enum
+ {
+ bit_LM = (1 << 29)
+ };
+
+unsigned char grub_cpuid_has_longmode = 0, grub_cpuid_has_pae = 0;
+
+static grub_err_t
+grub_cmd_cpuid (grub_extcmd_context_t ctxt,
+ int argc __attribute__ ((unused)),
+ char **args __attribute__ ((unused)))
+{
+ int val = 0;
+ if (ctxt->state[MODE_PAE].set)
+ val = grub_cpuid_has_pae;
+ else
+ val = grub_cpuid_has_longmode;
+ return val ? GRUB_ERR_NONE
+ /* TRANSLATORS: it's a standalone boolean value,
+ opposite of "true". */
+ : grub_error (GRUB_ERR_TEST_FAILURE, N_("false"));
+}
+
+static grub_extcmd_t cmd;
+
+GRUB_MOD_INIT(cpuid)
+{
+#ifdef __x86_64__
+ /* grub-emu */
+ grub_cpuid_has_longmode = 1;
+ grub_cpuid_has_pae = 1;
+#else
+ unsigned int eax, ebx, ecx, edx;
+ unsigned int max_level;
+ unsigned int ext_level;
+
+ /* See if we can use cpuid. */
+ asm volatile ("pushfl; pushfl; popl %0; movl %0,%1; xorl %2,%0;"
+ "pushl %0; popfl; pushfl; popl %0; popfl"
+ : "=&r" (eax), "=&r" (ebx)
+ : "i" (0x00200000));
+ if (((eax ^ ebx) & 0x00200000) == 0)
+ goto done;
+
+ /* Check the highest input value for eax. */
+ grub_cpuid (0, eax, ebx, ecx, edx);
+ /* We only look at the first four characters. */
+ max_level = eax;
+ if (max_level == 0)
+ goto done;
+
+ if (max_level >= 1)
+ {
+ grub_cpuid (1, eax, ebx, ecx, edx);
+ grub_cpuid_has_pae = !!(edx & bit_PAE);
+ }
+
+ grub_cpuid (0x80000000, eax, ebx, ecx, edx);
+ ext_level = eax;
+ if (ext_level < 0x80000000)
+ goto done;
+
+ grub_cpuid (0x80000001, eax, ebx, ecx, edx);
+ grub_cpuid_has_longmode = !!(edx & bit_LM);
+done:
+#endif
+
+ cmd = grub_register_extcmd ("cpuid", grub_cmd_cpuid, 0,
+ "[-l]", N_("Check for CPU features."), options);
+}
+
+GRUB_MOD_FINI(cpuid)
+{
+ grub_unregister_extcmd (cmd);
+}
diff --git a/grub-core/commands/i386/pc/drivemap.c b/grub-core/commands/i386/pc/drivemap.c
new file mode 100644
index 0000000..7f7f2d4
--- /dev/null
+++ b/grub-core/commands/i386/pc/drivemap.c
@@ -0,0 +1,428 @@
+/* drivemap.c - command to manage the BIOS drive mappings. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008, 2009 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/extcmd.h>
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/disk.h>
+#include <grub/loader.h>
+#include <grub/env.h>
+#include <grub/machine/biosnum.h>
+#include <grub/i18n.h>
+#include <grub/memory.h>
+#include <grub/machine/memory.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+/* Real mode IVT slot (seg:off far pointer) for interrupt 0x13. */
+static grub_uint32_t *const int13slot = (grub_uint32_t *) (4 * 0x13);
+
+/* Remember to update enum opt_idxs accordingly. */
+static const struct grub_arg_option options[] = {
+ /* TRANSLATORS: In this file "mapping" refers to a change GRUB makes so if
+ your language doesn't have an equivalent of "mapping" you can
+ use the word like "rerouting".
+ */
+ {"list", 'l', 0, N_("Show the current mappings."), 0, 0},
+ {"reset", 'r', 0, N_("Reset all mappings to the default values."), 0, 0},
+ {"swap", 's', 0, N_("Perform both direct and reverse mappings."), 0, 0},
+ {0, 0, 0, 0, 0, 0}
+};
+
+/* Remember to update options[] accordingly. */
+enum opt_idxs
+{
+ OPTIDX_LIST = 0,
+ OPTIDX_RESET,
+ OPTIDX_SWAP,
+};
+
+/* Realmode far ptr (2 * 16b) to the previous INT13h handler. */
+extern grub_uint32_t grub_drivemap_oldhandler;
+
+/* The type "void" is used for imported assembly labels, takes no storage and
+ serves just to take the address with &label. */
+
+/* The assembly function to replace the old INT13h handler. It does not follow
+ any C callspecs and returns with IRET. */
+extern const void grub_drivemap_handler;
+
+/* Start of the drive mappings area (space reserved at runtime). */
+extern const void grub_drivemap_mapstart;
+
+typedef struct drivemap_node
+{
+ struct drivemap_node *next;
+ grub_uint8_t newdrive;
+ grub_uint8_t redirto;
+} drivemap_node_t;
+
+typedef struct GRUB_PACKED int13map_node
+{
+ grub_uint8_t disknum;
+ grub_uint8_t mapto;
+} int13map_node_t;
+
+#define INT13H_OFFSET(x) \
+ (((grub_uint8_t *)(x)) - ((grub_uint8_t *)&grub_drivemap_handler))
+
+static drivemap_node_t *map_head;
+static void *drivemap_hook;
+static int drivemap_mmap;
+
+/* Puts the specified mapping into the table, replacing an existing mapping
+ for newdrive or adding a new one if required. */
+static grub_err_t
+drivemap_set (grub_uint8_t newdrive, grub_uint8_t redirto)
+{
+ drivemap_node_t *mapping = 0;
+ drivemap_node_t *search = map_head;
+ while (search)
+ {
+ if (search->newdrive == newdrive)
+ {
+ mapping = search;
+ break;
+ }
+ search = search->next;
+ }
+
+ /* Check for pre-existing mappings to modify before creating a new one. */
+ if (mapping)
+ mapping->redirto = redirto;
+ else
+ {
+ mapping = grub_malloc (sizeof (drivemap_node_t));
+ if (! mapping)
+ return grub_errno;
+ mapping->newdrive = newdrive;
+ mapping->redirto = redirto;
+ mapping->next = map_head;
+ map_head = mapping;
+ }
+ return GRUB_ERR_NONE;
+}
+
+/* Removes the mapping for newdrive from the table. If there is no mapping,
+ then this function behaves like a no-op on the map. */
+static void
+drivemap_remove (grub_uint8_t newdrive)
+{
+ drivemap_node_t *mapping = 0;
+ drivemap_node_t *search = map_head;
+ drivemap_node_t *previous = 0;
+
+ while (search)
+ {
+ if (search->newdrive == newdrive)
+ {
+ mapping = search;
+ break;
+ }
+ previous = search;
+ search = search->next;
+ }
+
+ if (mapping)
+ {
+ if (previous)
+ previous->next = mapping->next;
+ else
+ map_head = mapping->next;
+ grub_free (mapping);
+ }
+}
+
+/* Given a GRUB-like device name and a convenient location, stores the
+ related BIOS disk number. Accepts devices like \((f|h)dN\), with
+ 0 <= N < 128. */
+static grub_err_t
+tryparse_diskstring (const char *str, grub_uint8_t *output)
+{
+ /* Skip opening paren in order to allow both (hd0) and hd0. */
+ if (*str == '(')
+ str++;
+ if ((str[0] == 'f' || str[0] == 'h') && str[1] == 'd')
+ {
+ grub_uint8_t bios_num = (str[0] == 'h') ? 0x80 : 0x00;
+ unsigned long drivenum = grub_strtoul (str + 2, 0, 0);
+ if (grub_errno == GRUB_ERR_NONE && drivenum < 128)
+ {
+ bios_num |= drivenum;
+ if (output)
+ *output = bios_num;
+ return GRUB_ERR_NONE;
+ }
+ }
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "device format \"%s\" "
+ "invalid: must be (f|h)dN, with 0 <= N < 128", str);
+}
+
+static grub_err_t
+list_mappings (void)
+{
+ /* Show: list mappings. */
+ if (! map_head)
+ {
+ grub_puts_ (N_("No drives have been remapped"));
+ return GRUB_ERR_NONE;
+ }
+
+ /* TRANSLATORS: This is the header of mapping list.
+ On the left is how OS will see the disks and
+ on the right current GRUB vision. */
+ grub_puts_ (N_("OS disk #num ------> GRUB/BIOS device"));
+ drivemap_node_t *curnode = map_head;
+ while (curnode)
+ {
+ grub_printf ("%cD #%-3u (0x%02x) %cd%d\n",
+ (curnode->newdrive & 0x80) ? 'H' : 'F',
+ curnode->newdrive & 0x7F, curnode->newdrive,
+ (curnode->redirto & 0x80) ? 'h' : 'f',
+ curnode->redirto & 0x7F
+ );
+ curnode = curnode->next;
+ }
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_drivemap (struct grub_extcmd_context *ctxt, int argc, char **args)
+{
+ if (ctxt->state[OPTIDX_LIST].set)
+ {
+ return list_mappings ();
+ }
+ else if (ctxt->state[OPTIDX_RESET].set)
+ {
+ /* Reset: just delete all mappings, freeing their memory. */
+ drivemap_node_t *curnode = map_head;
+ drivemap_node_t *prevnode = 0;
+ while (curnode)
+ {
+ prevnode = curnode;
+ curnode = curnode->next;
+ grub_free (prevnode);
+ }
+ map_head = 0;
+ return GRUB_ERR_NONE;
+ }
+ else if (!ctxt->state[OPTIDX_SWAP].set && argc == 0)
+ {
+ /* No arguments */
+ return list_mappings ();
+ }
+
+ /* Neither flag: put mapping. */
+ grub_uint8_t mapfrom = 0;
+ grub_uint8_t mapto = 0xFF;
+ grub_err_t err;
+
+ if (argc != 2)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected"));
+
+ err = tryparse_diskstring (args[0], &mapfrom);
+ if (err != GRUB_ERR_NONE)
+ return err;
+
+ err = tryparse_diskstring (args[1], &mapto);
+ if (err != GRUB_ERR_NONE)
+ return err;
+
+ if (mapto == mapfrom)
+ {
+ /* Reset to default. */
+ grub_dprintf ("drivemap", "Removing mapping for %s (%02x)\n",
+ args[0], mapfrom);
+ drivemap_remove (mapfrom);
+ return GRUB_ERR_NONE;
+ }
+ /* Set the mapping for the disk (overwrites any existing mapping). */
+ grub_dprintf ("drivemap", "%s %s (%02x) = %s (%02x)\n",
+ ctxt->state[OPTIDX_SWAP].set ? "Swapping" : "Mapping",
+ args[1], mapto, args[0], mapfrom);
+ err = drivemap_set (mapto, mapfrom);
+ /* If -s, perform the reverse mapping too (only if the first was OK). */
+ if (ctxt->state[OPTIDX_SWAP].set && err == GRUB_ERR_NONE)
+ err = drivemap_set (mapfrom, mapto);
+ return err;
+}
+
+/* Int13h handler installer - reserves conventional memory for the handler,
+ copies it over and sets the IVT entry for int13h.
+ This code rests on the assumption that GRUB does not activate any kind
+ of memory mapping apart from identity paging, since it accesses
+ realmode structures by their absolute addresses, like the IVT at 0;
+ and transforms a pmode pointer into a rmode seg:off far ptr. */
+static grub_err_t
+install_int13_handler (int noret __attribute__ ((unused)))
+{
+ /* Size of the full int13 handler "bundle", including code and map. */
+ grub_uint32_t total_size;
+ /* Base address of the space reserved for the handler bundle. */
+ grub_uint8_t *handler_base = 0;
+ /* Address of the map within the deployed bundle. */
+ int13map_node_t *handler_map;
+
+ int i;
+ int entries = 0;
+ drivemap_node_t *curentry = map_head;
+
+ /* Count entries to prepare a contiguous map block. */
+ while (curentry)
+ {
+ entries++;
+ curentry = curentry->next;
+ }
+ if (entries == 0)
+ {
+ /* No need to install the int13h handler. */
+ grub_dprintf ("drivemap", "No drives marked as remapped, not installing "
+ "our int13h handler.\n");
+ return GRUB_ERR_NONE;
+ }
+
+ grub_dprintf ("drivemap", "Installing our int13h handler\n");
+
+ /* Save the pointer to the old handler. */
+ grub_drivemap_oldhandler = *int13slot;
+ grub_dprintf ("drivemap", "Original int13 handler: %04x:%04x\n",
+ (grub_drivemap_oldhandler >> 16) & 0x0ffff,
+ grub_drivemap_oldhandler & 0x0ffff);
+
+ /* Find a rmode-segment-aligned zone in conventional memory big
+ enough to hold the handler and its data. */
+ total_size = INT13H_OFFSET (&grub_drivemap_mapstart)
+ + (entries + 1) * sizeof (int13map_node_t);
+ grub_dprintf ("drivemap", "Payload is %u bytes long\n", total_size);
+ handler_base = grub_mmap_malign_and_register (16, ALIGN_UP (total_size, 16),
+ &drivemap_mmap,
+ GRUB_MEMORY_RESERVED,
+ GRUB_MMAP_MALLOC_LOW);
+ if (! handler_base)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't reserve "
+ "memory for the int13h handler");
+
+ /* Copy int13h handler bundle to reserved area. */
+ grub_dprintf ("drivemap", "Reserved memory at %p, copying handler\n",
+ handler_base);
+ grub_memcpy (handler_base, &grub_drivemap_handler,
+ INT13H_OFFSET (&grub_drivemap_mapstart));
+
+ /* Copy the mappings to the reserved area. */
+ curentry = map_head;
+ handler_map = (int13map_node_t *) (handler_base +
+ INT13H_OFFSET (&grub_drivemap_mapstart));
+ grub_dprintf ("drivemap", "Target map at %p, copying mappings\n", handler_map);
+ for (i = 0; i < entries; ++i, curentry = curentry->next)
+ {
+ handler_map[i].disknum = curentry->newdrive;
+ handler_map[i].mapto = curentry->redirto;
+ grub_dprintf ("drivemap", "\t#%d: 0x%02x <- 0x%02x\n", i,
+ handler_map[i].disknum, handler_map[i].mapto);
+ }
+ /* Signal end-of-map. */
+ handler_map[i].disknum = 0;
+ handler_map[i].mapto = 0;
+ grub_dprintf ("drivemap", "\t#%d: 0x00 <- 0x00 (end)\n", i);
+
+ /* Install our function as the int13h handler in the IVT. */
+ *int13slot = ((grub_uint32_t) handler_base) << 12; /* Segment address. */
+ grub_dprintf ("drivemap", "New int13 handler: %04x:%04x\n",
+ (*int13slot >> 16) & 0x0ffff, *int13slot & 0x0ffff);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+uninstall_int13_handler (void)
+{
+ if (! grub_drivemap_oldhandler)
+ return GRUB_ERR_NONE;
+
+ *int13slot = grub_drivemap_oldhandler;
+ grub_mmap_free_and_unregister (drivemap_mmap);
+ grub_drivemap_oldhandler = 0;
+ grub_dprintf ("drivemap", "Restored int13 handler: %04x:%04x\n",
+ (*int13slot >> 16) & 0x0ffff, *int13slot & 0x0ffff);
+
+ return GRUB_ERR_NONE;
+}
+
+static int
+grub_get_root_biosnumber_drivemap (void)
+{
+ const char *biosnum;
+ int ret = -1;
+ grub_device_t dev;
+
+ biosnum = grub_env_get ("biosnum");
+
+ if (biosnum)
+ return grub_strtoul (biosnum, 0, 0);
+
+ dev = grub_device_open (0);
+ if (dev && dev->disk && dev->disk->dev
+ && dev->disk->dev->id == GRUB_DISK_DEVICE_BIOSDISK_ID)
+ {
+ drivemap_node_t *curnode = map_head;
+ ret = (int) dev->disk->id;
+ while (curnode)
+ {
+ if (curnode->redirto == ret)
+ {
+ ret = curnode->newdrive;
+ break;
+ }
+ curnode = curnode->next;
+ }
+
+ }
+
+ if (dev)
+ grub_device_close (dev);
+
+ return ret;
+}
+
+static grub_extcmd_t cmd;
+static int (*grub_get_root_biosnumber_saved) (void);
+
+GRUB_MOD_INIT (drivemap)
+{
+ grub_get_root_biosnumber_saved = grub_get_root_biosnumber;
+ grub_get_root_biosnumber = grub_get_root_biosnumber_drivemap;
+ cmd = grub_register_extcmd ("drivemap", grub_cmd_drivemap, 0,
+ N_("-l | -r | [-s] grubdev osdisk."),
+ N_("Manage the BIOS drive mappings."),
+ options);
+ drivemap_hook =
+ grub_loader_register_preboot_hook (&install_int13_handler,
+ &uninstall_int13_handler,
+ GRUB_LOADER_PREBOOT_HOOK_PRIO_NORMAL);
+}
+
+GRUB_MOD_FINI (drivemap)
+{
+ grub_get_root_biosnumber = grub_get_root_biosnumber_saved;
+ grub_loader_unregister_preboot_hook (drivemap_hook);
+ drivemap_hook = 0;
+ grub_unregister_extcmd (cmd);
+}
diff --git a/grub-core/commands/i386/pc/drivemap_int13h.S b/grub-core/commands/i386/pc/drivemap_int13h.S
new file mode 100644
index 0000000..3c87521
--- /dev/null
+++ b/grub-core/commands/i386/pc/drivemap_int13h.S
@@ -0,0 +1,124 @@
+/* drivemap_int13h.S - interrupt handler for the BIOS drive remapper */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008, 2009 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/symbol.h>
+
+#define INT13H_OFFSET(x) ((x) - LOCAL (base))
+
+.code16
+
+/* Copy starts here. When deployed, this code must be segment-aligned. */
+
+/* The replacement int13 handler. Preserve all registers. */
+FUNCTION(grub_drivemap_handler)
+LOCAL (base):
+ /* Save %dx for future restore. */
+ push %dx
+ /* Push flags. Used to simulate interrupt with original flags. */
+ pushf
+
+ /* Map the drive number (always in DL). */
+ push %ax
+ push %bx
+#ifdef __APPLE__
+ LOCAL(mapstart_offset) = INT13H_OFFSET(LOCAL (mapstart))
+ movw $LOCAL(mapstart_offset), %bx
+#else
+ movw $INT13H_OFFSET(LOCAL (mapstart)), %bx
+#endif
+
+more_remaining:
+ movw %cs:(%bx), %ax
+ cmpb %ah, %al
+ jz not_found /* DRV=DST => map end - drive not remapped, keep DL. */
+ inc %bx
+ inc %bx
+ cmpb %dl, %al
+ jnz more_remaining /* Not found, but more remaining, loop. */
+ movb %ah, %dl /* Found - drive remapped, modify DL. */
+
+not_found:
+ pop %bx
+ pop %ax
+
+ /* If the call isn't ah=0x8 or ah=0x15 we must restore %dx. */
+ cmpb $0x8, %ah
+ jz norestore
+ cmpb $0x15, %ah
+ jz norestore
+
+ /* Restore flags. */
+ popf
+ pushf
+
+#ifdef __APPLE__
+ LOCAL(oldhandler_offset) = INT13H_OFFSET (LOCAL (oldhandler))
+ lcall *%cs:LOCAL(oldhandler_offset)
+#else
+ lcall *%cs:INT13H_OFFSET (LOCAL (oldhandler))
+#endif
+
+ push %bp
+ mov %sp, %bp
+
+tail:
+ /* Save new flags below %esp so the caller will recieve new flags. */
+ pushf
+ pop %dx
+ mov %dx, 8(%bp)
+
+ pop %bp
+
+ /* Restore %dx. */
+ pop %dx
+ iret
+
+norestore:
+
+ /* Restore flags. */
+ popf
+ pushf
+
+#ifdef __APPLE__
+ lcall *%cs:LOCAL(oldhandler_offset)
+#else
+ lcall *%cs:INT13H_OFFSET (LOCAL (oldhandler))
+#endif
+
+ push %bp
+ mov %sp, %bp
+
+ /* Save %dx. So it won't be restored to original value. */
+ mov %dx, 2(%bp)
+
+ jmp tail
+
+/* Far pointer to the old handler. Stored as a CS:IP in the style of real-mode
+ IVT entries (thus PI:SC in mem). */
+VARIABLE(grub_drivemap_oldhandler)
+LOCAL (oldhandler):
+ .word 0x0, 0x0
+
+/* This label MUST be at the end of the copied block, since the installer code
+ reserves additional space for mappings at runtime and copies them over it. */
+ .align 2
+
+VARIABLE(grub_drivemap_mapstart)
+LOCAL (mapstart):
+ .byte 0
diff --git a/grub-core/commands/i386/pc/halt.c b/grub-core/commands/i386/pc/halt.c
new file mode 100644
index 0000000..1e7c2c9
--- /dev/null
+++ b/grub-core/commands/i386/pc/halt.c
@@ -0,0 +1,126 @@
+/* halt.c - command to halt the computer. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2005,2007,2009 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/extcmd.h>
+#include <grub/i18n.h>
+#include <grub/machine/int.h>
+#include <grub/acpi.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static const struct grub_arg_option options[] =
+ {
+ {"no-apm", 'n', 0, N_("Do not use APM to halt the computer."), 0, 0},
+ {0, 0, 0, 0, 0, 0}
+ };
+
+static inline void __attribute__ ((noreturn))
+stop (void)
+{
+ while (1)
+ {
+ asm volatile ("hlt");
+ }
+}
+/*
+ * Halt the system, using APM if possible. If NO_APM is true, don't use
+ * APM even if it is available.
+ */
+void __attribute__ ((noreturn))
+grub_halt (int no_apm)
+{
+ struct grub_bios_int_registers regs;
+
+ grub_acpi_halt ();
+
+ if (no_apm)
+ stop ();
+
+ /* detect APM */
+ regs.eax = 0x5300;
+ regs.ebx = 0;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x15, &regs);
+
+ if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY)
+ stop ();
+
+ /* disconnect APM first */
+ regs.eax = 0x5304;
+ regs.ebx = 0;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x15, &regs);
+
+ /* connect APM */
+ regs.eax = 0x5301;
+ regs.ebx = 0;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x15, &regs);
+ if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY)
+ stop ();
+
+ /* set APM protocol level - 1.1 or bust. (this covers APM 1.2 also) */
+ regs.eax = 0x530E;
+ regs.ebx = 0;
+ regs.ecx = 0x0101;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x15, &regs);
+ if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY)
+ stop ();
+
+ /* set the power state to off */
+ regs.eax = 0x5307;
+ regs.ebx = 1;
+ regs.ecx = 3;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x15, &regs);
+
+ /* shouldn't reach here */
+ stop ();
+}
+
+static grub_err_t __attribute__ ((noreturn))
+grub_cmd_halt (grub_extcmd_context_t ctxt,
+ int argc __attribute__ ((unused)),
+ char **args __attribute__ ((unused)))
+
+{
+ struct grub_arg_list *state = ctxt->state;
+ int no_apm = 0;
+
+ if (state[0].set)
+ no_apm = 1;
+ grub_halt (no_apm);
+}
+
+static grub_extcmd_t cmd;
+
+GRUB_MOD_INIT(halt)
+{
+ cmd = grub_register_extcmd ("halt", grub_cmd_halt, 0, "[-n]",
+ N_("Halt the system, if possible using APM."),
+ options);
+}
+
+GRUB_MOD_FINI(halt)
+{
+ grub_unregister_extcmd (cmd);
+}
diff --git a/grub-core/commands/i386/pc/lsapm.c b/grub-core/commands/i386/pc/lsapm.c
new file mode 100644
index 0000000..c82476d
--- /dev/null
+++ b/grub-core/commands/i386/pc/lsapm.c
@@ -0,0 +1,115 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2010 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/machine/int.h>
+#include <grub/machine/apm.h>
+#include <grub/dl.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+int
+grub_apm_get_info (struct grub_apm_info *info)
+{
+ struct grub_bios_int_registers regs;
+
+ /* detect APM */
+ regs.eax = 0x5300;
+ regs.ebx = 0;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x15, &regs);
+
+ if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY)
+ return 0;
+ info->version = regs.eax & 0xffff;
+ info->flags = regs.ecx & 0xffff;
+
+ /* disconnect APM first */
+ regs.eax = 0x5304;
+ regs.ebx = 0;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x15, &regs);
+
+ /* connect APM */
+ regs.eax = 0x5303;
+ regs.ebx = 0;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x15, &regs);
+
+ if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY)
+ return 0;
+
+ info->cseg = regs.eax & 0xffff;
+ info->offset = regs.ebx;
+ info->cseg_16 = regs.ecx & 0xffff;
+ info->dseg = regs.edx & 0xffff;
+ info->cseg_len = regs.esi >> 16;
+ info->cseg_16_len = regs.esi & 0xffff;
+ info->dseg_len = regs.edi;
+
+ return 1;
+}
+
+static grub_err_t
+grub_cmd_lsapm (grub_command_t cmd __attribute__ ((unused)),
+ int argc __attribute__ ((unused)), char **args __attribute__ ((unused)))
+{
+ struct grub_apm_info info;
+ if (!grub_apm_get_info (&info))
+ return grub_error (GRUB_ERR_IO, N_("no APM found"));
+
+ grub_printf_ (N_("Version %u.%u\n"
+ "32-bit CS = 0x%x, len = 0x%x, offset = 0x%x\n"
+ "16-bit CS = 0x%x, len = 0x%x\n"
+ "DS = 0x%x, len = 0x%x\n"),
+ info.version >> 8, info.version & 0xff,
+ info.cseg, info.cseg_len, info.offset,
+ info.cseg_16, info.cseg_16_len,
+ info.dseg, info.dseg_len);
+ grub_xputs (info.flags & GRUB_APM_FLAGS_16BITPROTECTED_SUPPORTED
+ ? _("16-bit protected interface supported\n")
+ : _("16-bit protected interface unsupported\n"));
+ grub_xputs (info.flags & GRUB_APM_FLAGS_32BITPROTECTED_SUPPORTED
+ ? _("32-bit protected interface supported\n")
+ : _("32-bit protected interface unsupported\n"));
+ grub_xputs (info.flags & GRUB_APM_FLAGS_CPUIDLE_SLOWS_DOWN
+ ? _("CPU Idle slows down processor\n")
+ : _("CPU Idle doesn't slow down processor\n"));
+ grub_xputs (info.flags & GRUB_APM_FLAGS_DISABLED
+ ? _("APM disabled\n") : _("APM enabled\n"));
+ grub_xputs (info.flags & GRUB_APM_FLAGS_DISENGAGED
+ ? _("APM disengaged\n") : _("APM engaged\n"));
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_command_t cmd;
+
+GRUB_MOD_INIT(lsapm)
+{
+ cmd = grub_register_command ("lsapm", grub_cmd_lsapm, 0,
+ N_("Show APM information."));
+}
+
+GRUB_MOD_FINI(lsapm)
+{
+ grub_unregister_command (cmd);
+}
+
+
diff --git a/grub-core/commands/i386/pc/play.c b/grub-core/commands/i386/pc/play.c
new file mode 100644
index 0000000..a980e46
--- /dev/null
+++ b/grub-core/commands/i386/pc/play.c
@@ -0,0 +1,197 @@
+/* play.c - command to play a tune */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2005,2007,2009 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* Lots of this file is borrowed from GNU/Hurd generic-speaker driver. */
+
+#include <grub/dl.h>
+#include <grub/file.h>
+#include <grub/disk.h>
+#include <grub/term.h>
+#include <grub/misc.h>
+#include <grub/cpu/io.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+#include <grub/time.h>
+#include <grub/speaker.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define BASE_TEMPO (60 * 1000)
+
+
+#define T_REST ((grub_uint16_t) 0)
+#define T_FINE ((grub_uint16_t) -1)
+
+struct note
+{
+ grub_uint16_t pitch;
+ grub_uint16_t duration;
+};
+
+/* Returns whether playing should continue. */
+static int
+play (unsigned tempo, struct note *note)
+{
+ grub_uint64_t to;
+
+ if (note->pitch == T_FINE || grub_getkey_noblock () != GRUB_TERM_NO_KEY)
+ return 1;
+
+ grub_dprintf ("play", "pitch = %d, duration = %d\n", note->pitch,
+ note->duration);
+
+ switch (note->pitch)
+ {
+ case T_REST:
+ grub_speaker_beep_off ();
+ break;
+
+ default:
+ grub_speaker_beep_on (note->pitch);
+ break;
+ }
+
+ to = grub_get_time_ms () + BASE_TEMPO * note->duration / tempo;
+ while ((grub_get_time_ms () <= to)
+ && (grub_getkey_noblock () == GRUB_TERM_NO_KEY));
+
+ return 0;
+}
+
+static grub_err_t
+grub_cmd_play (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char **args)
+{
+
+ if (argc < 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ /* TRANSLATORS: It's musical notes, not the notes
+ you take. Play command expects arguments which can
+ be either a filename or tempo+notes.
+ This error happens if none is specified. */
+ N_("filename or tempo and notes expected"));
+
+ if (argc == 1)
+ {
+ struct note buf;
+ grub_uint32_t tempo;
+ grub_file_t file;
+
+ file = grub_file_open (args[0], GRUB_FILE_TYPE_AUDIO);
+
+ if (! file)
+ return grub_errno;
+
+ if (grub_file_read (file, &tempo, sizeof (tempo)) != sizeof (tempo))
+ {
+ grub_file_close (file);
+ if (!grub_errno)
+ grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
+ args[0]);
+ return grub_errno;
+ }
+
+ if (!tempo)
+ {
+ grub_file_close (file);
+ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid tempo in %s"),
+ args[0]);
+ return grub_errno;
+ }
+
+ tempo = grub_le_to_cpu32 (tempo);
+ grub_dprintf ("play","tempo = %d\n", tempo);
+
+ while (grub_file_read (file, &buf,
+ sizeof (struct note)) == sizeof (struct note))
+ {
+ buf.pitch = grub_le_to_cpu16 (buf.pitch);
+ buf.duration = grub_le_to_cpu16 (buf.duration);
+
+ if (play (tempo, &buf))
+ break;
+ }
+
+ grub_file_close (file);
+ }
+ else
+ {
+ const char *end;
+ unsigned tempo;
+ struct note note;
+ int i;
+
+ tempo = grub_strtoul (args[0], &end, 0);
+
+ if (!tempo)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid tempo in %s"),
+ args[0]);
+ return grub_errno;
+ }
+
+ if (*end)
+ /* Was not a number either, assume it was supposed to be a file name. */
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), args[0]);
+
+ grub_dprintf ("play","tempo = %d\n", tempo);
+
+ for (i = 1; i + 1 < argc; i += 2)
+ {
+ note.pitch = grub_strtoul (args[i], &end, 0);
+ if (grub_errno)
+ break;
+ if (*end)
+ {
+ grub_error (GRUB_ERR_BAD_NUMBER, N_("unrecognized number"));
+ break;
+ }
+
+ note.duration = grub_strtoul (args[i + 1], &end, 0);
+ if (grub_errno)
+ break;
+ if (*end)
+ {
+ grub_error (GRUB_ERR_BAD_NUMBER, N_("unrecognized number"));
+ break;
+ }
+
+ if (play (tempo, &note))
+ break;
+ }
+ }
+
+ grub_speaker_beep_off ();
+
+ return 0;
+}
+
+static grub_command_t cmd;
+
+GRUB_MOD_INIT(play)
+{
+ cmd = grub_register_command ("play", grub_cmd_play,
+ N_("FILE | TEMPO [PITCH1 DURATION1] [PITCH2 DURATION2] ... "),
+ N_("Play a tune."));
+}
+
+GRUB_MOD_FINI(play)
+{
+ grub_unregister_command (cmd);
+}
diff --git a/grub-core/commands/i386/pc/sendkey.c b/grub-core/commands/i386/pc/sendkey.c
new file mode 100644
index 0000000..26d9acd
--- /dev/null
+++ b/grub-core/commands/i386/pc/sendkey.c
@@ -0,0 +1,387 @@
+/* sendkey.c - fake keystroke. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/extcmd.h>
+#include <grub/cpu/io.h>
+#include <grub/loader.h>
+#include <grub/i18n.h>
+
+GRUB_MOD_LICENSE ("GPLv2+");
+
+static char sendkey[0x20];
+/* Length of sendkey. */
+static int keylen = 0;
+static int noled = 0;
+static const struct grub_arg_option options[] =
+ {
+ {"num", 'n', 0, N_("set numlock mode"), "[on|off]", ARG_TYPE_STRING},
+ {"caps", 'c', 0, N_("set capslock mode"), "[on|off]", ARG_TYPE_STRING},
+ {"scroll", 's', 0, N_("set scrolllock mode"), "[on|off]", ARG_TYPE_STRING},
+ {"insert", 0, 0, N_("set insert mode"), "[on|off]", ARG_TYPE_STRING},
+ {"pause", 0, 0, N_("set pause mode"), "[on|off]", ARG_TYPE_STRING},
+ {"left-shift", 0, 0, N_("press left shift"), "[on|off]", ARG_TYPE_STRING},
+ {"right-shift", 0, 0, N_("press right shift"), "[on|off]", ARG_TYPE_STRING},
+ {"sysrq", 0, 0, N_("press SysRq"), "[on|off]", ARG_TYPE_STRING},
+ {"numkey", 0, 0, N_("press NumLock key"), "[on|off]", ARG_TYPE_STRING},
+ {"capskey", 0, 0, N_("press CapsLock key"), "[on|off]", ARG_TYPE_STRING},
+ {"scrollkey", 0, 0, N_("press ScrollLock key"), "[on|off]", ARG_TYPE_STRING},
+ {"insertkey", 0, 0, N_("press Insert key"), "[on|off]", ARG_TYPE_STRING},
+ {"left-alt", 0, 0, N_("press left alt"), "[on|off]", ARG_TYPE_STRING},
+ {"right-alt", 0, 0, N_("press right alt"), "[on|off]", ARG_TYPE_STRING},
+ {"left-ctrl", 0, 0, N_("press left ctrl"), "[on|off]", ARG_TYPE_STRING},
+ {"right-ctrl", 0, 0, N_("press right ctrl"), "[on|off]", ARG_TYPE_STRING},
+ {"no-led", 0, 0, N_("don't update LED state"), 0, 0},
+ {0, 0, 0, 0, 0, 0}
+ };
+static int simple_flag_offsets[]
+= {5, 6, 4, 7, 11, 1, 0, 10, 13, 14, 12, 15, 9, 3, 8, 2};
+
+static grub_uint32_t andmask = 0xffffffff, ormask = 0;
+
+struct
+keysym
+{
+ const char *unshifted_name; /* the name in unshifted state */
+ const char *shifted_name; /* the name in shifted state */
+ unsigned char unshifted_ascii; /* the ascii code in unshifted state */
+ unsigned char shifted_ascii; /* the ascii code in shifted state */
+ unsigned char keycode; /* keyboard scancode */
+};
+
+/* The table for key symbols. If the "shifted" member of an entry is
+ NULL, the entry does not have shifted state. Copied from GRUB Legacy setkey fuction */
+static struct keysym keysym_table[] =
+{
+ {"escape", 0, 0x1b, 0, 0x01},
+ {"1", "exclam", '1', '!', 0x02},
+ {"2", "at", '2', '@', 0x03},
+ {"3", "numbersign", '3', '#', 0x04},
+ {"4", "dollar", '4', '$', 0x05},
+ {"5", "percent", '5', '%', 0x06},
+ {"6", "caret", '6', '^', 0x07},
+ {"7", "ampersand", '7', '&', 0x08},
+ {"8", "asterisk", '8', '*', 0x09},
+ {"9", "parenleft", '9', '(', 0x0a},
+ {"0", "parenright", '0', ')', 0x0b},
+ {"minus", "underscore", '-', '_', 0x0c},
+ {"equal", "plus", '=', '+', 0x0d},
+ {"backspace", 0, '\b', 0, 0x0e},
+ {"tab", 0, '\t', 0, 0x0f},
+ {"q", "Q", 'q', 'Q', 0x10},
+ {"w", "W", 'w', 'W', 0x11},
+ {"e", "E", 'e', 'E', 0x12},
+ {"r", "R", 'r', 'R', 0x13},
+ {"t", "T", 't', 'T', 0x14},
+ {"y", "Y", 'y', 'Y', 0x15},
+ {"u", "U", 'u', 'U', 0x16},
+ {"i", "I", 'i', 'I', 0x17},
+ {"o", "O", 'o', 'O', 0x18},
+ {"p", "P", 'p', 'P', 0x19},
+ {"bracketleft", "braceleft", '[', '{', 0x1a},
+ {"bracketright", "braceright", ']', '}', 0x1b},
+ {"enter", 0, '\r', 0, 0x1c},
+ {"control", 0, 0, 0, 0x1d},
+ {"a", "A", 'a', 'A', 0x1e},
+ {"s", "S", 's', 'S', 0x1f},
+ {"d", "D", 'd', 'D', 0x20},
+ {"f", "F", 'f', 'F', 0x21},
+ {"g", "G", 'g', 'G', 0x22},
+ {"h", "H", 'h', 'H', 0x23},
+ {"j", "J", 'j', 'J', 0x24},
+ {"k", "K", 'k', 'K', 0x25},
+ {"l", "L", 'l', 'L', 0x26},
+ {"semicolon", "colon", ';', ':', 0x27},
+ {"quote", "doublequote", '\'', '"', 0x28},
+ {"backquote", "tilde", '`', '~', 0x29},
+ {"shift", 0, 0, 0, 0x2a},
+ {"backslash", "bar", '\\', '|', 0x2b},
+ {"z", "Z", 'z', 'Z', 0x2c},
+ {"x", "X", 'x', 'X', 0x2d},
+ {"c", "C", 'c', 'C', 0x2e},
+ {"v", "V", 'v', 'V', 0x2f},
+ {"b", "B", 'b', 'B', 0x30},
+ {"n", "N", 'n', 'N', 0x31},
+ {"m", "M", 'm', 'M', 0x32},
+ {"comma", "less", ',', '<', 0x33},
+ {"period", "greater", '.', '>', 0x34},
+ {"slash", "question", '/', '?', 0x35},
+ {"rshift", 0, 0, 0, 0x36},
+ {"numasterisk", 0, '*', 0, 0x37},
+ {"alt", 0, 0, 0, 0x38},
+ {"space", 0, ' ', 0, 0x39},
+ {"capslock", 0, 0, 0, 0x3a},
+ {"F1", 0, 0, 0, 0x3b},
+ {"F2", 0, 0, 0, 0x3c},
+ {"F3", 0, 0, 0, 0x3d},
+ {"F4", 0, 0, 0, 0x3e},
+ {"F5", 0, 0, 0, 0x3f},
+ {"F6", 0, 0, 0, 0x40},
+ {"F7", 0, 0, 0, 0x41},
+ {"F8", 0, 0, 0, 0x42},
+ {"F9", 0, 0, 0, 0x43},
+ {"F10", 0, 0, 0, 0x44},
+ {"num7", "numhome", '7', 0, 0x47},
+ {"num8", "numup", '8', 0, 0x48},
+ {"num9", "numpgup", '9', 0, 0x49},
+ {"numminus", 0, '-', 0, 0x4a},
+ {"num4", "numleft", '4', 0, 0x4b},
+ {"num5", "numcenter", '5', 0, 0x4c},
+ {"num6", "numright", '6', 0, 0x4d},
+ {"numplus", 0, '-', 0, 0x4e},
+ {"num1", "numend", '1', 0, 0x4f},
+ {"num2", "numdown", '2', 0, 0x50},
+ {"num3", "numpgdown", '3', 0, 0x51},
+ {"num0", "numinsert", '0', 0, 0x52},
+ {"numperiod", "numdelete", 0, 0x7f, 0x53},
+ {"F11", 0, 0, 0, 0x57},
+ {"F12", 0, 0, 0, 0x58},
+ {"numenter", 0, '\r', 0, 0xe0},
+ {"numslash", 0, '/', 0, 0xe0},
+ {"delete", 0, 0x7f, 0, 0xe0},
+ {"insert", 0, 0xe0, 0, 0x52},
+ {"home", 0, 0xe0, 0, 0x47},
+ {"end", 0, 0xe0, 0, 0x4f},
+ {"pgdown", 0, 0xe0, 0, 0x51},
+ {"pgup", 0, 0xe0, 0, 0x49},
+ {"down", 0, 0xe0, 0, 0x50},
+ {"up", 0, 0xe0, 0, 0x48},
+ {"left", 0, 0xe0, 0, 0x4b},
+ {"right", 0, 0xe0, 0, 0x4d}
+};
+
+/* Set a simple flag in flags variable
+ OUTOFFSET - offset of flag in FLAGS,
+ OP - action id
+*/
+static void
+grub_sendkey_set_simple_flag (int outoffset, int op)
+{
+ if (op == 2)
+ {
+ andmask |= (1 << outoffset);
+ ormask &= ~(1 << outoffset);
+ }
+ else
+ {
+ andmask &= (~(1 << outoffset));
+ if (op == 1)
+ ormask |= (1 << outoffset);
+ else
+ ormask &= ~(1 << outoffset);
+ }
+}
+
+static int
+grub_sendkey_parse_op (struct grub_arg_list state)
+{
+ if (! state.set)
+ return 2;
+
+ if (grub_strcmp (state.arg, "off") == 0 || grub_strcmp (state.arg, "0") == 0
+ || grub_strcmp (state.arg, "unpress") == 0)
+ return 0;
+
+ if (grub_strcmp (state.arg, "on") == 0 || grub_strcmp (state.arg, "1") == 0
+ || grub_strcmp (state.arg, "press") == 0)
+ return 1;
+
+ return 2;
+}
+
+static grub_uint32_t oldflags;
+
+static grub_err_t
+grub_sendkey_postboot (void)
+{
+ /* For convention: pointer to flags. */
+ grub_uint32_t *flags = (grub_uint32_t *) 0x417;
+
+ *flags = oldflags;
+
+ *((char *) 0x41a) = 0x1e;
+ *((char *) 0x41c) = 0x1e;
+
+ return GRUB_ERR_NONE;
+}
+
+/* Set keyboard buffer to our sendkey */
+static grub_err_t
+grub_sendkey_preboot (int noret __attribute__ ((unused)))
+{
+ /* For convention: pointer to flags. */
+ grub_uint32_t *flags = (grub_uint32_t *) 0x417;
+
+ oldflags = *flags;
+
+ /* Set the sendkey. */
+ *((char *) 0x41a) = 0x1e;
+ *((char *) 0x41c) = keylen + 0x1e;
+ grub_memcpy ((char *) 0x41e, sendkey, 0x20);
+
+ /* Transform "any ctrl" to "right ctrl" flag. */
+ if (*flags & (1 << 8))
+ *flags &= ~(1 << 2);
+
+ /* Transform "any alt" to "right alt" flag. */
+ if (*flags & (1 << 9))
+ *flags &= ~(1 << 3);
+
+ *flags = (*flags & andmask) | ormask;
+
+ /* Transform "right ctrl" to "any ctrl" flag. */
+ if (*flags & (1 << 8))
+ *flags |= (1 << 2);
+
+ /* Transform "right alt" to "any alt" flag. */
+ if (*flags & (1 << 9))
+ *flags |= (1 << 3);
+
+ /* Write new LED state */
+ if (!noled)
+ {
+ int value = 0;
+ int failed;
+ /* Try 5 times */
+ for (failed = 0; failed < 5; failed++)
+ {
+ value = 0;
+ /* Send command change LEDs */
+ grub_outb (0xed, 0x60);
+
+ /* Wait */
+ do
+ value = grub_inb (0x60);
+ while ((value != 0xfa) && (value != 0xfe));
+
+ if (value == 0xfa)
+ {
+ /* Set new LEDs*/
+ grub_outb ((*flags >> 4) & 7, 0x60);
+ break;
+ }
+ }
+ }
+ return GRUB_ERR_NONE;
+}
+
+/* Helper for grub_cmd_sendkey. */
+static int
+find_key_code (char *key)
+{
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(keysym_table); i++)
+ {
+ if (keysym_table[i].unshifted_name
+ && grub_strcmp (key, keysym_table[i].unshifted_name) == 0)
+ return keysym_table[i].keycode;
+ else if (keysym_table[i].shifted_name
+ && grub_strcmp (key, keysym_table[i].shifted_name) == 0)
+ return keysym_table[i].keycode;
+ }
+
+ return 0;
+}
+
+/* Helper for grub_cmd_sendkey. */
+static int
+find_ascii_code (char *key)
+{
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(keysym_table); i++)
+ {
+ if (keysym_table[i].unshifted_name
+ && grub_strcmp (key, keysym_table[i].unshifted_name) == 0)
+ return keysym_table[i].unshifted_ascii;
+ else if (keysym_table[i].shifted_name
+ && grub_strcmp (key, keysym_table[i].shifted_name) == 0)
+ return keysym_table[i].shifted_ascii;
+ }
+
+ return 0;
+}
+
+static grub_err_t
+grub_cmd_sendkey (grub_extcmd_context_t ctxt, int argc, char **args)
+{
+ struct grub_arg_list *state = ctxt->state;
+
+ andmask = 0xffffffff;
+ ormask = 0;
+
+ {
+ int i;
+
+ keylen = 0;
+
+ for (i = 0; i < argc && keylen < 0x20; i++)
+ {
+ int key_code;
+
+ key_code = find_key_code (args[i]);
+ if (key_code)
+ {
+ sendkey[keylen++] = find_ascii_code (args[i]);
+ sendkey[keylen++] = key_code;
+ }
+ }
+ }
+
+ {
+ unsigned i;
+ for (i = 0; i < ARRAY_SIZE(simple_flag_offsets); i++)
+ grub_sendkey_set_simple_flag (simple_flag_offsets[i],
+ grub_sendkey_parse_op(state[i]));
+ }
+
+ /* Set noled. */
+ noled = (state[ARRAY_SIZE(simple_flag_offsets)].set);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_extcmd_t cmd;
+static struct grub_preboot *preboot_hook;
+
+GRUB_MOD_INIT (sendkey)
+{
+ cmd = grub_register_extcmd ("sendkey", grub_cmd_sendkey, 0,
+ N_("[KEYSTROKE1] [KEYSTROKE2] ..."),
+ /* TRANSLATORS: It can emulate multiple
+ keypresses. */
+ N_("Emulate a keystroke sequence"), options);
+
+ preboot_hook
+ = grub_loader_register_preboot_hook (grub_sendkey_preboot,
+ grub_sendkey_postboot,
+ GRUB_LOADER_PREBOOT_HOOK_PRIO_CONSOLE);
+}
+
+GRUB_MOD_FINI (sendkey)
+{
+ grub_unregister_extcmd (cmd);
+ grub_loader_unregister_preboot_hook (preboot_hook);
+}
diff --git a/grub-core/commands/i386/pc/smbios.c b/grub-core/commands/i386/pc/smbios.c
new file mode 100644
index 0000000..069d663
--- /dev/null
+++ b/grub-core/commands/i386/pc/smbios.c
@@ -0,0 +1,52 @@
+/* smbios.c - get smbios tables. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2019 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/acpi.h>
+#include <grub/smbios.h>
+#include <grub/misc.h>
+
+struct grub_smbios_eps *
+grub_machine_smbios_get_eps (void)
+{
+ grub_uint8_t *ptr;
+
+ grub_dprintf ("smbios", "Looking for SMBIOS EPS. Scanning BIOS\n");
+
+ for (ptr = (grub_uint8_t *) 0xf0000; ptr < (grub_uint8_t *) 0x100000; ptr += 16)
+ if (grub_memcmp (ptr, "_SM_", 4) == 0
+ && grub_byte_checksum (ptr, sizeof (struct grub_smbios_eps)) == 0)
+ return (struct grub_smbios_eps *) ptr;
+
+ return 0;
+}
+
+struct grub_smbios_eps3 *
+grub_machine_smbios_get_eps3 (void)
+{
+ grub_uint8_t *ptr;
+
+ grub_dprintf ("smbios", "Looking for SMBIOS3 EPS. Scanning BIOS\n");
+
+ for (ptr = (grub_uint8_t *) 0xf0000; ptr < (grub_uint8_t *) 0x100000; ptr += 16)
+ if (grub_memcmp (ptr, "_SM3_", 5) == 0
+ && grub_byte_checksum (ptr, sizeof (struct grub_smbios_eps3)) == 0)
+ return (struct grub_smbios_eps3 *) ptr;
+
+ return 0;
+}
diff --git a/grub-core/commands/i386/rdmsr.c b/grub-core/commands/i386/rdmsr.c
new file mode 100644
index 0000000..46c4346
--- /dev/null
+++ b/grub-core/commands/i386/rdmsr.c
@@ -0,0 +1,102 @@
+/* rdmsr.c - Read CPU model-specific registers. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2019 Free Software Foundation, Inc.
+ * Based on gcc/gcc/config/i386/driver-i386.c
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/env.h>
+#include <grub/command.h>
+#include <grub/extcmd.h>
+#include <grub/i18n.h>
+#include <grub/i386/cpuid.h>
+#include <grub/i386/rdmsr.h>
+
+GRUB_MOD_LICENSE("GPLv3+");
+
+static grub_extcmd_t cmd_read;
+
+static const struct grub_arg_option options[] =
+{
+ {0, 'v', 0, N_("Save read value into variable VARNAME."),
+ N_("VARNAME"), ARG_TYPE_STRING},
+ {0, 0, 0, 0, 0, 0}
+};
+
+static grub_err_t
+grub_cmd_msr_read (grub_extcmd_context_t ctxt, int argc, char **argv)
+{
+ grub_uint32_t manufacturer[3], max_cpuid, a, b, c, features, addr;
+ grub_uint64_t value;
+ const char *ptr;
+ char buf[sizeof("1122334455667788")];
+
+ /*
+ * The CPUID instruction should be used to determine whether MSRs
+ * are supported. (CPUID.01H:EDX[5] = 1)
+ */
+ if (! grub_cpu_is_cpuid_supported ())
+ return grub_error (GRUB_ERR_BUG, N_("unsupported instruction"));
+
+ grub_cpuid (0, max_cpuid, manufacturer[0], manufacturer[2], manufacturer[1]);
+
+ if (max_cpuid < 1)
+ return grub_error (GRUB_ERR_BUG, N_("unsupported instruction"));
+
+ grub_cpuid (1, a, b, c, features);
+
+ if (!(features & (1 << 5)))
+ return grub_error (GRUB_ERR_BUG, N_("unsupported instruction"));
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
+
+ grub_errno = GRUB_ERR_NONE;
+ ptr = argv[0];
+ addr = grub_strtoul (ptr, &ptr, 0);
+
+ if (grub_errno != GRUB_ERR_NONE)
+ return grub_errno;
+ if (*ptr != '\0')
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid argument"));
+
+ value = grub_msr_read (addr);
+
+ if (ctxt->state[0].set)
+ {
+ grub_snprintf (buf, sizeof(buf), "%llx", (unsigned long long) value);
+ grub_env_set (ctxt->state[0].arg, buf);
+ }
+ else
+ grub_printf ("0x%llx\n", (unsigned long long) value);
+
+ return GRUB_ERR_NONE;
+}
+
+GRUB_MOD_INIT(rdmsr)
+{
+ cmd_read = grub_register_extcmd ("rdmsr", grub_cmd_msr_read, 0, N_("ADDR"),
+ N_("Read a CPU model specific register."),
+ options);
+}
+
+GRUB_MOD_FINI(rdmsr)
+{
+ grub_unregister_extcmd (cmd_read);
+}
diff --git a/grub-core/commands/i386/wrmsr.c b/grub-core/commands/i386/wrmsr.c
new file mode 100644
index 0000000..1b143b8
--- /dev/null
+++ b/grub-core/commands/i386/wrmsr.c
@@ -0,0 +1,94 @@
+/* wrmsr.c - Write CPU model-specific registers. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2019 Free Software Foundation, Inc.
+ * Based on gcc/gcc/config/i386/driver-i386.c
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/env.h>
+#include <grub/command.h>
+#include <grub/extcmd.h>
+#include <grub/lockdown.h>
+#include <grub/i18n.h>
+#include <grub/i386/cpuid.h>
+#include <grub/i386/wrmsr.h>
+
+GRUB_MOD_LICENSE("GPLv3+");
+
+static grub_command_t cmd_write;
+
+static grub_err_t
+grub_cmd_msr_write (grub_command_t cmd __attribute__ ((unused)), int argc, char **argv)
+{
+ grub_uint32_t manufacturer[3], max_cpuid, a, b, c, features, addr;
+ grub_uint64_t value;
+ const char *ptr;
+
+ /*
+ * The CPUID instruction should be used to determine whether MSRs
+ * are supported. (CPUID.01H:EDX[5] = 1)
+ */
+ if (!grub_cpu_is_cpuid_supported ())
+ return grub_error (GRUB_ERR_BUG, N_("unsupported instruction"));
+
+ grub_cpuid (0, max_cpuid, manufacturer[0], manufacturer[2], manufacturer[1]);
+
+ if (max_cpuid < 1)
+ return grub_error (GRUB_ERR_BUG, N_("unsupported instruction"));
+
+ grub_cpuid (1, a, b, c, features);
+
+ if (!(features & (1 << 5)))
+ return grub_error (GRUB_ERR_BUG, N_("unsupported instruction"));
+
+ if (argc != 2)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected"));
+
+ grub_errno = GRUB_ERR_NONE;
+ ptr = argv[0];
+ addr = grub_strtoul (ptr, &ptr, 0);
+
+ if (grub_errno != GRUB_ERR_NONE)
+ return grub_errno;
+ if (*ptr != '\0')
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid argument"));
+
+ ptr = argv[1];
+ value = grub_strtoull (ptr, &ptr, 0);
+
+ if (grub_errno != GRUB_ERR_NONE)
+ return grub_errno;
+ if (*ptr != '\0')
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid argument"));
+
+ grub_msr_write (addr, value);
+
+ return GRUB_ERR_NONE;
+}
+
+GRUB_MOD_INIT(wrmsr)
+{
+ cmd_write = grub_register_command_lockdown ("wrmsr", grub_cmd_msr_write, N_("ADDR VALUE"),
+ N_("Write a value to a CPU model specific register."));
+}
+
+GRUB_MOD_FINI(wrmsr)
+{
+ grub_unregister_command (cmd_write);
+}