From 6e7a315eb67cb6c113cf37e1d66c4f11a51a2b3e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 18:29:51 +0200 Subject: Adding upstream version 2.06. Signed-off-by: Daniel Baumann --- grub-core/commands/i386/pc/drivemap.c | 428 +++++++++++++++++++++++++++ grub-core/commands/i386/pc/drivemap_int13h.S | 124 ++++++++ grub-core/commands/i386/pc/halt.c | 126 ++++++++ grub-core/commands/i386/pc/lsapm.c | 115 +++++++ grub-core/commands/i386/pc/play.c | 197 ++++++++++++ grub-core/commands/i386/pc/sendkey.c | 387 ++++++++++++++++++++++++ grub-core/commands/i386/pc/smbios.c | 52 ++++ 7 files changed, 1429 insertions(+) create mode 100644 grub-core/commands/i386/pc/drivemap.c create mode 100644 grub-core/commands/i386/pc/drivemap_int13h.S create mode 100644 grub-core/commands/i386/pc/halt.c create mode 100644 grub-core/commands/i386/pc/lsapm.c create mode 100644 grub-core/commands/i386/pc/play.c create mode 100644 grub-core/commands/i386/pc/sendkey.c create mode 100644 grub-core/commands/i386/pc/smbios.c (limited to 'grub-core/commands/i386/pc') 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 . + */ + +#include + +#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 . + */ + +#include +#include +#include +#include +#include +#include + +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, ®s); + + 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, ®s); + + /* connect APM */ + regs.eax = 0x5301; + regs.ebx = 0; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x15, ®s); + 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, ®s); + 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, ®s); + + /* 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 . + */ + +#include +#include +#include +#include +#include + +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, ®s); + + 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, ®s); + + /* connect APM */ + regs.eax = 0x5303; + regs.ebx = 0; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x15, ®s); + + 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 . + */ + +/* Lots of this file is borrowed from GNU/Hurd generic-speaker driver. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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, ¬e)) + 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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 . + */ + +#include +#include +#include + +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; +} -- cgit v1.2.3