diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:29:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:29:51 +0000 |
commit | 6e7a315eb67cb6c113cf37e1d66c4f11a51a2b3e (patch) | |
tree | 32451fa3cdd9321fb2591fada9891b2cb70a9cd1 /grub-core/loader/i386/pc | |
parent | Initial commit. (diff) | |
download | grub2-6e7a315eb67cb6c113cf37e1d66c4f11a51a2b3e.tar.xz grub2-6e7a315eb67cb6c113cf37e1d66c4f11a51a2b3e.zip |
Adding upstream version 2.06.upstream/2.06upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'grub-core/loader/i386/pc')
-rw-r--r-- | grub-core/loader/i386/pc/chainloader.c | 310 | ||||
-rw-r--r-- | grub-core/loader/i386/pc/freedos.c | 190 | ||||
-rw-r--r-- | grub-core/loader/i386/pc/linux.c | 494 | ||||
-rw-r--r-- | grub-core/loader/i386/pc/ntldr.c | 162 | ||||
-rw-r--r-- | grub-core/loader/i386/pc/plan9.c | 607 | ||||
-rw-r--r-- | grub-core/loader/i386/pc/pxechainloader.c | 168 | ||||
-rw-r--r-- | grub-core/loader/i386/pc/truecrypt.c | 233 |
7 files changed, 2164 insertions, 0 deletions
diff --git a/grub-core/loader/i386/pc/chainloader.c b/grub-core/loader/i386/pc/chainloader.c new file mode 100644 index 0000000..976fea7 --- /dev/null +++ b/grub-core/loader/i386/pc/chainloader.c @@ -0,0 +1,310 @@ +/* chainloader.c - boot another boot loader */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2004,2007,2009,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/loader.h> +#include <grub/machine/chainloader.h> +#include <grub/machine/biosdisk.h> +#include <grub/machine/memory.h> +#include <grub/file.h> +#include <grub/err.h> +#include <grub/device.h> +#include <grub/disk.h> +#include <grub/misc.h> +#include <grub/types.h> +#include <grub/partition.h> +#include <grub/memory.h> +#include <grub/dl.h> +#include <grub/command.h> +#include <grub/msdos_partition.h> +#include <grub/machine/biosnum.h> +#include <grub/cpu/floppy.h> +#include <grub/i18n.h> +#include <grub/video.h> +#include <grub/mm.h> +#include <grub/fat.h> +#include <grub/ntfs.h> +#include <grub/i386/relocator.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_dl_t my_mod; +static int boot_drive; +static grub_addr_t boot_part_addr; +static struct grub_relocator *rel; + +typedef enum + { + GRUB_CHAINLOADER_FORCE = 0x1, + GRUB_CHAINLOADER_BPB = 0x2, + } grub_chainloader_flags_t; + +static grub_err_t +grub_chainloader_boot (void) +{ + struct grub_relocator16_state state = { + .edx = boot_drive, + .esi = boot_part_addr, + .ds = 0, + .es = 0, + .fs = 0, + .gs = 0, + .ss = 0, + .cs = 0, + .sp = GRUB_MEMORY_MACHINE_BOOT_LOADER_ADDR, + .ip = GRUB_MEMORY_MACHINE_BOOT_LOADER_ADDR, + .a20 = 0 + }; + grub_video_set_mode ("text", 0, 0); + + return grub_relocator16_boot (rel, state); +} + +static grub_err_t +grub_chainloader_unload (void) +{ + grub_relocator_unload (rel); + rel = NULL; + grub_dl_unref (my_mod); + return GRUB_ERR_NONE; +} + +void +grub_chainloader_patch_bpb (void *bs, grub_device_t dev, grub_uint8_t dl) +{ + grub_uint32_t part_start = 0, heads = 0, sectors = 0; + if (dev && dev->disk) + { + part_start = grub_partition_get_start (dev->disk->partition); + if (dev->disk->data) + { + heads = ((struct grub_biosdisk_data *)(dev->disk->data))->heads; + sectors = ((struct grub_biosdisk_data *)(dev->disk->data))->sectors; + } + } + if (grub_memcmp ((char *) &((struct grub_ntfs_bpb *) bs)->oem_name, + "NTFS", 4) == 0) + { + struct grub_ntfs_bpb *bpb = (struct grub_ntfs_bpb *) bs; + bpb->num_hidden_sectors = grub_cpu_to_le32 (part_start); + bpb->bios_drive = dl; + return; + } + + do + { + struct grub_fat_bpb *bpb = (struct grub_fat_bpb *) bs; + if (grub_strncmp((const char *) bpb->version_specific.fat12_or_fat16.fstype, "FAT12", 5) + && grub_strncmp((const char *) bpb->version_specific.fat12_or_fat16.fstype, "FAT16", 5) + && grub_strncmp((const char *) bpb->version_specific.fat32.fstype, "FAT32", 5)) + break; + + if (grub_le_to_cpu16 (bpb->bytes_per_sector) < 512 + || (grub_le_to_cpu16 (bpb->bytes_per_sector) + & (grub_le_to_cpu16 (bpb->bytes_per_sector) - 1))) + break; + + if (bpb->sectors_per_cluster == 0 + || (bpb->sectors_per_cluster & (bpb->sectors_per_cluster - 1))) + break; + + if (bpb->num_reserved_sectors == 0) + break; + if (bpb->num_total_sectors_16 == 0 && bpb->num_total_sectors_32 == 0) + break; + + if (bpb->num_fats == 0) + break; + + if (bpb->sectors_per_fat_16) + { + bpb->num_hidden_sectors = grub_cpu_to_le32 (part_start); + bpb->version_specific.fat12_or_fat16.num_ph_drive = dl; + if (sectors) + bpb->sectors_per_track = grub_cpu_to_le16 (sectors); + if (heads) + bpb->num_heads = grub_cpu_to_le16 (heads); + return; + } + if (bpb->version_specific.fat32.sectors_per_fat_32) + { + bpb->num_hidden_sectors = grub_cpu_to_le32 (part_start); + bpb->version_specific.fat32.num_ph_drive = dl; + if (sectors) + bpb->sectors_per_track = grub_cpu_to_le16 (sectors); + if (heads) + bpb->num_heads = grub_cpu_to_le16 (heads); + return; + } + break; + } + while (0); +} + +static void +grub_chainloader_cmd (const char *filename, grub_chainloader_flags_t flags) +{ + grub_file_t file = 0; + grub_uint16_t signature; + grub_device_t dev; + int drive = -1; + grub_addr_t part_addr = 0; + grub_uint8_t *bs, *ptable; + + rel = grub_relocator_new (); + if (!rel) + goto fail; + + grub_dl_ref (my_mod); + + file = grub_file_open (filename, GRUB_FILE_TYPE_PCCHAINLOADER + | GRUB_FILE_TYPE_NO_DECOMPRESS); + if (! file) + goto fail; + + { + grub_relocator_chunk_t ch; + grub_err_t err; + + err = grub_relocator_alloc_chunk_addr (rel, &ch, 0x7C00, + GRUB_DISK_SECTOR_SIZE); + if (err) + goto fail; + bs = get_virtual_current_address (ch); + err = grub_relocator_alloc_chunk_addr (rel, &ch, + GRUB_MEMORY_MACHINE_PART_TABLE_ADDR, + 64); + if (err) + goto fail; + ptable = get_virtual_current_address (ch); + } + + /* Read the first block. */ + if (grub_file_read (file, bs, GRUB_DISK_SECTOR_SIZE) + != GRUB_DISK_SECTOR_SIZE) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + filename); + + goto fail; + } + + /* Check the signature. */ + signature = *((grub_uint16_t *) (bs + GRUB_DISK_SECTOR_SIZE - 2)); + if (signature != grub_le_to_cpu16 (0xaa55) + && ! (flags & GRUB_CHAINLOADER_FORCE)) + { + grub_error (GRUB_ERR_BAD_OS, "invalid signature"); + goto fail; + } + + grub_file_close (file); + + /* Obtain the partition table from the root device. */ + drive = grub_get_root_biosnumber (); + dev = grub_device_open (0); + if (dev && dev->disk && dev->disk->partition) + { + grub_disk_t disk = dev->disk; + + if (disk) + { + grub_partition_t p = disk->partition; + + if (p && grub_strcmp (p->partmap->name, "msdos") == 0) + { + disk->partition = p->parent; + grub_disk_read (disk, p->offset, 446, 64, ptable); + part_addr = (GRUB_MEMORY_MACHINE_PART_TABLE_ADDR + + (p->index << 4)); + disk->partition = p; + } + } + } + + if (flags & GRUB_CHAINLOADER_BPB) + grub_chainloader_patch_bpb ((void *) 0x7C00, dev, drive); + + if (dev) + grub_device_close (dev); + + /* Ignore errors. Perhaps it's not fatal. */ + grub_errno = GRUB_ERR_NONE; + + boot_drive = drive; + boot_part_addr = part_addr; + + grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 1); + return; + + fail: + + if (file) + grub_file_close (file); + + grub_dl_unref (my_mod); +} + +static grub_err_t +grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_chainloader_flags_t flags = 0; + + while (argc > 0) + { + if (grub_strcmp (argv[0], "--force") == 0) + { + flags |= GRUB_CHAINLOADER_FORCE; + argc--; + argv++; + continue; + } + if (grub_strcmp (argv[0], "--bpb") == 0) + { + flags |= GRUB_CHAINLOADER_BPB; + argc--; + argv++; + continue; + } + break; + } + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + grub_chainloader_cmd (argv[0], flags); + + return grub_errno; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(chainloader) +{ + cmd = grub_register_command ("chainloader", grub_cmd_chainloader, + N_("[--force|--bpb] FILE"), + N_("Load another boot loader.")); + my_mod = mod; +} + +GRUB_MOD_FINI(chainloader) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/loader/i386/pc/freedos.c b/grub-core/loader/i386/pc/freedos.c new file mode 100644 index 0000000..aac6c97 --- /dev/null +++ b/grub-core/loader/i386/pc/freedos.c @@ -0,0 +1,190 @@ +/* chainloader.c - boot another boot loader */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2004,2007,2009,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/loader.h> +#include <grub/file.h> +#include <grub/err.h> +#include <grub/device.h> +#include <grub/disk.h> +#include <grub/misc.h> +#include <grub/types.h> +#include <grub/partition.h> +#include <grub/dl.h> +#include <grub/command.h> +#include <grub/machine/biosnum.h> +#include <grub/i18n.h> +#include <grub/video.h> +#include <grub/mm.h> +#include <grub/cpu/relocator.h> +#include <grub/machine/chainloader.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_dl_t my_mod; +static struct grub_relocator *rel; +static grub_uint32_t ebx = 0xffffffff; + +#define GRUB_FREEDOS_SEGMENT 0x60 +#define GRUB_FREEDOS_ADDR (GRUB_FREEDOS_SEGMENT << 4) +#define GRUB_FREEDOS_STACK_SEGMENT 0x1fe0 +#define GRUB_FREEDOS_STACK_BPB_POINTER 0x7c00 +#define GRUB_FREEDOS_BPB_ADDR ((GRUB_FREEDOS_STACK_SEGMENT << 4) \ + + GRUB_FREEDOS_STACK_BPB_POINTER) + +/* FreeDOS boot.asm passes register sp as exactly this. Importantly, + it must point below the BPB (to avoid overwriting any of it). */ +#define GRUB_FREEDOS_STACK_POINTER (GRUB_FREEDOS_STACK_BPB_POINTER \ + - 0x60) + +/* In this, the additional 8192 bytes are the stack reservation; the + remaining parts trivially give the maximum allowed size. */ +#define GRUB_FREEDOS_MAX_SIZE ((GRUB_FREEDOS_STACK_SEGMENT << 4) \ + + GRUB_FREEDOS_STACK_POINTER \ + - GRUB_FREEDOS_ADDR \ + - 8192) + +static grub_err_t +grub_freedos_boot (void) +{ + struct grub_relocator16_state state = { + .cs = GRUB_FREEDOS_SEGMENT, + .ip = 0, + + .ds = GRUB_FREEDOS_STACK_SEGMENT, + .es = 0, + .fs = 0, + .gs = 0, + .ss = GRUB_FREEDOS_STACK_SEGMENT, + .sp = GRUB_FREEDOS_STACK_POINTER, + .ebp = GRUB_FREEDOS_STACK_BPB_POINTER, + .ebx = ebx, + .edx = ebx, + .a20 = 1 + }; + grub_video_set_mode ("text", 0, 0); + + return grub_relocator16_boot (rel, state); +} + +static grub_err_t +grub_freedos_unload (void) +{ + grub_relocator_unload (rel); + rel = NULL; + grub_dl_unref (my_mod); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_freedos (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file = 0; + grub_err_t err; + void *bs, *kernelsys; + grub_size_t kernelsyssize; + grub_device_t dev; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + grub_dl_ref (my_mod); + + rel = grub_relocator_new (); + if (!rel) + goto fail; + + file = grub_file_open (argv[0], GRUB_FILE_TYPE_FREEDOS); + if (! file) + goto fail; + + { + grub_relocator_chunk_t ch; + err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_FREEDOS_BPB_ADDR, + GRUB_DISK_SECTOR_SIZE); + if (err) + goto fail; + bs = get_virtual_current_address (ch); + } + + ebx = grub_get_root_biosnumber (); + dev = grub_device_open (0); + + if (dev && dev->disk) + { + err = grub_disk_read (dev->disk, 0, 0, GRUB_DISK_SECTOR_SIZE, bs); + if (err) + { + grub_device_close (dev); + goto fail; + } + grub_chainloader_patch_bpb (bs, dev, ebx); + } + + if (dev) + grub_device_close (dev); + + kernelsyssize = grub_file_size (file); + + if (kernelsyssize > GRUB_FREEDOS_MAX_SIZE) + { + grub_error (GRUB_ERR_BAD_OS, + N_("the size of `%s' is too large"), argv[0]); + goto fail; + } + + { + grub_relocator_chunk_t ch; + err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_FREEDOS_ADDR, + kernelsyssize); + if (err) + goto fail; + kernelsys = get_virtual_current_address (ch); + } + + if (grub_file_read (file, kernelsys, kernelsyssize) + != (grub_ssize_t) kernelsyssize) + goto fail; + + grub_loader_set (grub_freedos_boot, grub_freedos_unload, 1); + return GRUB_ERR_NONE; + + fail: + + if (file) + grub_file_close (file); + + grub_freedos_unload (); + + return grub_errno; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(freedos) +{ + cmd = grub_register_command ("freedos", grub_cmd_freedos, + 0, N_("Load FreeDOS kernel.sys.")); + my_mod = mod; +} + +GRUB_MOD_FINI(freedos) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/loader/i386/pc/linux.c b/grub-core/loader/i386/pc/linux.c new file mode 100644 index 0000000..2a29952 --- /dev/null +++ b/grub-core/loader/i386/pc/linux.c @@ -0,0 +1,494 @@ +/* linux.c - boot Linux zImage or bzImage */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009,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/loader.h> +#include <grub/file.h> +#include <grub/err.h> +#include <grub/device.h> +#include <grub/disk.h> +#include <grub/misc.h> +#include <grub/types.h> +#include <grub/memory.h> +#include <grub/dl.h> +#include <grub/cpu/linux.h> +#include <grub/command.h> +#include <grub/i18n.h> +#include <grub/mm.h> +#include <grub/cpu/relocator.h> +#include <grub/video.h> +#include <grub/i386/floppy.h> +#include <grub/lib/cmdline.h> +#include <grub/linux.h> +#include <grub/safemath.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define GRUB_LINUX_CL_OFFSET 0x9000 + +static grub_dl_t my_mod; + +static grub_size_t linux_mem_size; +static int loaded; +static struct grub_relocator *relocator = NULL; +static grub_addr_t grub_linux_real_target; +static char *grub_linux_real_chunk; +static grub_size_t grub_linux16_prot_size; +static grub_size_t maximal_cmdline_size; + +static grub_err_t +grub_linux16_boot (void) +{ + grub_uint16_t segment; + struct grub_relocator16_state state; + + segment = grub_linux_real_target >> 4; + state.gs = state.fs = state.es = state.ds = state.ss = segment; + state.sp = GRUB_LINUX_SETUP_STACK; + state.cs = segment + 0x20; + state.ip = 0; + state.a20 = 1; + + grub_video_set_mode ("text", 0, 0); + + grub_stop_floppy (); + + return grub_relocator16_boot (relocator, state); +} + +static grub_err_t +grub_linux_unload (void) +{ + grub_dl_unref (my_mod); + loaded = 0; + grub_relocator_unload (relocator); + relocator = NULL; + return GRUB_ERR_NONE; +} + +static int +target_hook (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type, + void *data) +{ + grub_uint64_t *result = data; + grub_uint64_t candidate; + + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + if (addr >= 0xa0000) + return 0; + if (addr + size >= 0xa0000) + size = 0xa0000 - addr; + + /* Put the real mode part at as a high location as possible. */ + candidate = addr + size - (GRUB_LINUX_CL_OFFSET + maximal_cmdline_size); + /* But it must not exceed the traditional area. */ + if (candidate > GRUB_LINUX_OLD_REAL_MODE_ADDR) + candidate = GRUB_LINUX_OLD_REAL_MODE_ADDR; + if (candidate < addr) + return 0; + + if (candidate > *result || *result == (grub_uint64_t) -1) + *result = candidate; + return 0; +} + +static grub_addr_t +grub_find_real_target (void) +{ + grub_uint64_t result = (grub_uint64_t) -1; + + grub_mmap_iterate (target_hook, &result); + return result; +} + +static grub_err_t +grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file = 0; + struct linux_i386_kernel_header lh; + grub_uint8_t setup_sects; + grub_size_t real_size; + grub_ssize_t len; + int i; + char *grub_linux_prot_chunk; + int grub_linux_is_bzimage; + grub_addr_t grub_linux_prot_target; + grub_err_t err; + + grub_dl_ref (my_mod); + + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + goto fail; + } + + file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL); + if (! file) + goto fail; + + if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh)) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + argv[0]); + goto fail; + } + + if (lh.boot_flag != grub_cpu_to_le16_compile_time (0xaa55)) + { + grub_error (GRUB_ERR_BAD_OS, "invalid magic number"); + goto fail; + } + + if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS) + { + grub_error (GRUB_ERR_BAD_OS, "too many setup sectors"); + goto fail; + } + + grub_linux_is_bzimage = 0; + setup_sects = lh.setup_sects; + linux_mem_size = 0; + + maximal_cmdline_size = 256; + + if (lh.header == grub_cpu_to_le32_compile_time (GRUB_LINUX_I386_MAGIC_SIGNATURE) + && grub_le_to_cpu16 (lh.version) >= 0x0200) + { + grub_linux_is_bzimage = (lh.loadflags & GRUB_LINUX_FLAG_BIG_KERNEL); + lh.type_of_loader = GRUB_LINUX_BOOT_LOADER_TYPE; + + if (grub_le_to_cpu16 (lh.version) >= 0x0206) + maximal_cmdline_size = grub_le_to_cpu32 (lh.cmdline_size) + 1; + + grub_linux_real_target = grub_find_real_target (); + if (grub_linux_real_target == (grub_addr_t)-1) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, + "no appropriate low memory found"); + goto fail; + } + + if (grub_le_to_cpu16 (lh.version) >= 0x0201) + { + lh.heap_end_ptr = grub_cpu_to_le16_compile_time (GRUB_LINUX_HEAP_END_OFFSET); + lh.loadflags |= GRUB_LINUX_FLAG_CAN_USE_HEAP; + } + + if (grub_le_to_cpu16 (lh.version) >= 0x0202) + lh.cmd_line_ptr = grub_linux_real_target + GRUB_LINUX_CL_OFFSET; + else + { + lh.cl_magic = grub_cpu_to_le16_compile_time (GRUB_LINUX_CL_MAGIC); + lh.cl_offset = grub_cpu_to_le16_compile_time (GRUB_LINUX_CL_OFFSET); + lh.setup_move_size = grub_cpu_to_le16_compile_time (GRUB_LINUX_CL_OFFSET + + maximal_cmdline_size); + } + } + else + { + /* Your kernel is quite old... */ + lh.cl_magic = grub_cpu_to_le16_compile_time (GRUB_LINUX_CL_MAGIC); + lh.cl_offset = grub_cpu_to_le16_compile_time (GRUB_LINUX_CL_OFFSET); + + setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS; + + grub_linux_real_target = GRUB_LINUX_OLD_REAL_MODE_ADDR; + } + + /* If SETUP_SECTS is not set, set it to the default (4). */ + if (! setup_sects) + setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS; + + real_size = setup_sects << GRUB_DISK_SECTOR_BITS; + if (grub_sub (grub_file_size (file), real_size, &grub_linux16_prot_size) || + grub_sub (grub_linux16_prot_size, GRUB_DISK_SECTOR_SIZE, &grub_linux16_prot_size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + goto fail; + } + + if (! grub_linux_is_bzimage + && GRUB_LINUX_ZIMAGE_ADDR + grub_linux16_prot_size + > grub_linux_real_target) + { + grub_error (GRUB_ERR_BAD_OS, "too big zImage (0x%" PRIxGRUB_SIZE + " > 0x%" PRIxGRUB_ADDR "), use bzImage instead", + GRUB_LINUX_ZIMAGE_ADDR + grub_linux16_prot_size, + grub_linux_real_target); + goto fail; + } + + grub_dprintf ("linux", "[Linux-%s, setup=0x%x, size=0x%x]\n", + grub_linux_is_bzimage ? "bzImage" : "zImage", + (unsigned) real_size, + (unsigned) grub_linux16_prot_size); + + for (i = 1; i < argc; i++) + if (grub_memcmp (argv[i], "vga=", 4) == 0) + { + /* Video mode selection support. */ + grub_uint16_t vid_mode; + char *val = argv[i] + 4; + + if (grub_strcmp (val, "normal") == 0) + vid_mode = GRUB_LINUX_VID_MODE_NORMAL; + else if (grub_strcmp (val, "ext") == 0) + vid_mode = GRUB_LINUX_VID_MODE_EXTENDED; + else if (grub_strcmp (val, "ask") == 0) + vid_mode = GRUB_LINUX_VID_MODE_ASK; + else + vid_mode = (grub_uint16_t) grub_strtoul (val, 0, 0); + + if (grub_errno) + goto fail; + + lh.vid_mode = grub_cpu_to_le16 (vid_mode); + } + else if (grub_memcmp (argv[i], "mem=", 4) == 0) + { + const char *val = argv[i] + 4; + + linux_mem_size = grub_strtoul (val, &val, 0); + + if (grub_errno) + { + grub_errno = GRUB_ERR_NONE; + linux_mem_size = 0; + } + else + { + int shift = 0; + + switch (grub_tolower (val[0])) + { + case 'g': + shift += 10; + /* Fallthrough. */ + case 'm': + shift += 10; + /* Fallthrough. */ + case 'k': + shift += 10; + /* Fallthrough. */ + default: + break; + } + + /* Check an overflow. */ + if (linux_mem_size > (~0UL >> shift)) + linux_mem_size = 0; + else + linux_mem_size <<= shift; + } + } + + relocator = grub_relocator_new (); + if (!relocator) + goto fail; + + { + grub_relocator_chunk_t ch; + err = grub_relocator_alloc_chunk_addr (relocator, &ch, + grub_linux_real_target, + GRUB_LINUX_CL_OFFSET + + maximal_cmdline_size); + if (err) + return err; + grub_linux_real_chunk = get_virtual_current_address (ch); + } + + /* Put the real mode code at the temporary address. */ + grub_memmove (grub_linux_real_chunk, &lh, sizeof (lh)); + + len = real_size + GRUB_DISK_SECTOR_SIZE - sizeof (lh); + if (grub_file_read (file, grub_linux_real_chunk + sizeof (lh), len) != len) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + argv[0]); + goto fail; + } + + if (lh.header != grub_cpu_to_le32_compile_time (GRUB_LINUX_I386_MAGIC_SIGNATURE) + || grub_le_to_cpu16 (lh.version) < 0x0200) + /* Clear the heap space. */ + grub_memset (grub_linux_real_chunk + + ((setup_sects + 1) << GRUB_DISK_SECTOR_BITS), + 0, + ((GRUB_LINUX_MAX_SETUP_SECTS - setup_sects - 1) + << GRUB_DISK_SECTOR_BITS)); + + /* Create kernel command line. */ + grub_memcpy ((char *)grub_linux_real_chunk + GRUB_LINUX_CL_OFFSET, + LINUX_IMAGE, sizeof (LINUX_IMAGE)); + err = grub_create_loader_cmdline (argc, argv, + (char *)grub_linux_real_chunk + + GRUB_LINUX_CL_OFFSET + sizeof (LINUX_IMAGE) - 1, + maximal_cmdline_size + - (sizeof (LINUX_IMAGE) - 1), + GRUB_VERIFY_KERNEL_CMDLINE); + if (err) + goto fail; + + if (grub_linux_is_bzimage) + grub_linux_prot_target = GRUB_LINUX_BZIMAGE_ADDR; + else + grub_linux_prot_target = GRUB_LINUX_ZIMAGE_ADDR; + { + grub_relocator_chunk_t ch; + err = grub_relocator_alloc_chunk_addr (relocator, &ch, + grub_linux_prot_target, + grub_linux16_prot_size); + if (err) + return err; + grub_linux_prot_chunk = get_virtual_current_address (ch); + } + + len = grub_linux16_prot_size; + if (grub_file_read (file, grub_linux_prot_chunk, len) != len && !grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + argv[0]); + + if (grub_errno == GRUB_ERR_NONE) + { + grub_loader_set (grub_linux16_boot, grub_linux_unload, 0); + loaded = 1; + } + + fail: + + if (file) + grub_file_close (file); + + if (grub_errno != GRUB_ERR_NONE) + { + grub_dl_unref (my_mod); + loaded = 0; + grub_relocator_unload (relocator); + } + + return grub_errno; +} + +static grub_err_t +grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_size_t size = 0; + grub_addr_t addr_max, addr_min; + struct linux_i386_kernel_header *lh; + grub_uint8_t *initrd_chunk; + grub_addr_t initrd_addr; + grub_err_t err; + struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 }; + + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + goto fail; + } + + if (!loaded) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first")); + goto fail; + } + + lh = (struct linux_i386_kernel_header *) grub_linux_real_chunk; + + if (!(lh->header == grub_cpu_to_le32_compile_time (GRUB_LINUX_I386_MAGIC_SIGNATURE) + && grub_le_to_cpu16 (lh->version) >= 0x0200)) + { + grub_error (GRUB_ERR_BAD_OS, "the kernel is too old for initrd"); + goto fail; + } + + /* Get the highest address available for the initrd. */ + if (grub_le_to_cpu16 (lh->version) >= 0x0203) + { + addr_max = grub_cpu_to_le32 (lh->initrd_addr_max); + + /* XXX in reality, Linux specifies a bogus value, so + it is necessary to make sure that ADDR_MAX does not exceed + 0x3fffffff. */ + if (addr_max > GRUB_LINUX_INITRD_MAX_ADDRESS) + addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS; + } + else + addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS; + + if (linux_mem_size != 0 && linux_mem_size < addr_max) + addr_max = linux_mem_size; + + /* Linux 2.3.xx has a bug in the memory range check, so avoid + the last page. + Linux 2.2.xx has a bug in the memory range check, which is + worse than that of Linux 2.3.xx, so avoid the last 64kb. */ + addr_max -= 0x10000; + + addr_min = GRUB_LINUX_BZIMAGE_ADDR + grub_linux16_prot_size; + + if (grub_initrd_init (argc, argv, &initrd_ctx)) + goto fail; + + size = grub_get_initrd_size (&initrd_ctx); + + { + grub_relocator_chunk_t ch; + err = grub_relocator_alloc_chunk_align_safe (relocator, &ch, addr_min, addr_max, size, + 0x1000, GRUB_RELOCATOR_PREFERENCE_HIGH, 0); + if (err) + return err; + initrd_chunk = get_virtual_current_address (ch); + initrd_addr = get_physical_target_address (ch); + } + + if (grub_initrd_load (&initrd_ctx, argv, initrd_chunk)) + goto fail; + + lh->ramdisk_image = initrd_addr; + lh->ramdisk_size = size; + + fail: + grub_initrd_close (&initrd_ctx); + + return grub_errno; +} + +static grub_command_t cmd_linux, cmd_initrd; + +GRUB_MOD_INIT(linux16) +{ + cmd_linux = + grub_register_command ("linux16", grub_cmd_linux, + 0, N_("Load Linux.")); + cmd_initrd = + grub_register_command ("initrd16", grub_cmd_initrd, + 0, N_("Load initrd.")); + my_mod = mod; +} + +GRUB_MOD_FINI(linux16) +{ + grub_unregister_command (cmd_linux); + grub_unregister_command (cmd_initrd); +} diff --git a/grub-core/loader/i386/pc/ntldr.c b/grub-core/loader/i386/pc/ntldr.c new file mode 100644 index 0000000..f0d7414 --- /dev/null +++ b/grub-core/loader/i386/pc/ntldr.c @@ -0,0 +1,162 @@ +/* chainloader.c - boot another boot loader */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2004,2007,2009,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/loader.h> +#include <grub/file.h> +#include <grub/err.h> +#include <grub/device.h> +#include <grub/disk.h> +#include <grub/misc.h> +#include <grub/types.h> +#include <grub/partition.h> +#include <grub/dl.h> +#include <grub/command.h> +#include <grub/machine/biosnum.h> +#include <grub/i18n.h> +#include <grub/video.h> +#include <grub/mm.h> +#include <grub/cpu/relocator.h> +#include <grub/machine/chainloader.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_dl_t my_mod; +static struct grub_relocator *rel; +static grub_uint32_t edx = 0xffffffff; + +#define GRUB_NTLDR_SEGMENT 0x2000 + +static grub_err_t +grub_ntldr_boot (void) +{ + struct grub_relocator16_state state = { + .cs = GRUB_NTLDR_SEGMENT, + .ip = 0, + .ds = 0, + .es = 0, + .fs = 0, + .gs = 0, + .ss = 0, + .sp = 0x7c00, + .edx = edx, + .a20 = 1 + }; + grub_video_set_mode ("text", 0, 0); + + return grub_relocator16_boot (rel, state); +} + +static grub_err_t +grub_ntldr_unload (void) +{ + grub_relocator_unload (rel); + rel = NULL; + grub_dl_unref (my_mod); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_ntldr (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file = 0; + grub_err_t err; + void *bs, *ntldr; + grub_size_t ntldrsize; + grub_device_t dev; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + grub_dl_ref (my_mod); + + rel = grub_relocator_new (); + if (!rel) + goto fail; + + file = grub_file_open (argv[0], GRUB_FILE_TYPE_NTLDR); + if (! file) + goto fail; + + { + grub_relocator_chunk_t ch; + err = grub_relocator_alloc_chunk_addr (rel, &ch, 0x7C00, + GRUB_DISK_SECTOR_SIZE); + if (err) + goto fail; + bs = get_virtual_current_address (ch); + } + + edx = grub_get_root_biosnumber (); + dev = grub_device_open (0); + + if (dev && dev->disk) + { + err = grub_disk_read (dev->disk, 0, 0, GRUB_DISK_SECTOR_SIZE, bs); + if (err) + { + grub_device_close (dev); + goto fail; + } + grub_chainloader_patch_bpb (bs, dev, edx); + } + + if (dev) + grub_device_close (dev); + + ntldrsize = grub_file_size (file); + { + grub_relocator_chunk_t ch; + err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_NTLDR_SEGMENT << 4, + ntldrsize); + if (err) + goto fail; + ntldr = get_virtual_current_address (ch); + } + + if (grub_file_read (file, ntldr, ntldrsize) + != (grub_ssize_t) ntldrsize) + goto fail; + + grub_loader_set (grub_ntldr_boot, grub_ntldr_unload, 1); + return GRUB_ERR_NONE; + + fail: + + if (file) + grub_file_close (file); + + grub_ntldr_unload (); + + return grub_errno; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(ntldr) +{ + cmd = grub_register_command ("ntldr", grub_cmd_ntldr, + 0, N_("Load NTLDR or BootMGR.")); + my_mod = mod; +} + +GRUB_MOD_FINI(ntldr) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/loader/i386/pc/plan9.c b/grub-core/loader/i386/pc/plan9.c new file mode 100644 index 0000000..3755015 --- /dev/null +++ b/grub-core/loader/i386/pc/plan9.c @@ -0,0 +1,607 @@ +/* + * 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/loader.h> +#include <grub/file.h> +#include <grub/err.h> +#include <grub/device.h> +#include <grub/disk.h> +#include <grub/misc.h> +#include <grub/types.h> +#include <grub/partition.h> +#include <grub/msdos_partition.h> +#include <grub/scsi.h> +#include <grub/dl.h> +#include <grub/command.h> +#include <grub/i18n.h> +#include <grub/video.h> +#include <grub/mm.h> +#include <grub/cpu/relocator.h> +#include <grub/extcmd.h> +#include <grub/verify.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_dl_t my_mod; +static struct grub_relocator *rel; +static grub_uint32_t eip = 0xffffffff; + +#define GRUB_PLAN9_TARGET 0x100000 +#define GRUB_PLAN9_ALIGN 4096 +#define GRUB_PLAN9_CONFIG_ADDR 0x001200 +#define GRUB_PLAN9_CONFIG_PATH_SIZE 0x000040 +#define GRUB_PLAN9_CONFIG_MAGIC "ZORT 0\r\n" + +static const struct grub_arg_option options[] = + { + {"map", 'm', GRUB_ARG_OPTION_REPEATABLE, + /* TRANSLATORS: it's about guessing which GRUB disk + is which Plan9 disk. If your language has no + word "mapping" you can use another word which + means that the GRUBDEVICE and PLAN9DEVICE are + actually the same device, just named differently + in OS and GRUB. */ + N_("Override guessed mapping of Plan9 devices."), + N_("GRUBDEVICE=PLAN9DEVICE"), + ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} + }; + +struct grub_plan9_header +{ + grub_uint32_t magic; +#define GRUB_PLAN9_MAGIC 0x1eb + grub_uint32_t text_size; + grub_uint32_t data_size; + grub_uint32_t bss_size; + grub_uint32_t sectiona; + grub_uint32_t entry_addr; + grub_uint32_t zero; + grub_uint32_t sectionb; +}; + +static grub_err_t +grub_plan9_boot (void) +{ + struct grub_relocator32_state state = { + .eax = 0, + .eip = eip, + .ebx = 0, + .ecx = 0, + .edx = 0, + .edi = 0, + .esp = 0, + .ebp = 0, + .esi = 0 + }; + grub_video_set_mode ("text", 0, 0); + + return grub_relocator32_boot (rel, state, 0); +} + +static grub_err_t +grub_plan9_unload (void) +{ + grub_relocator_unload (rel); + rel = NULL; + grub_dl_unref (my_mod); + return GRUB_ERR_NONE; +} + +/* Context for grub_cmd_plan9. */ +struct grub_cmd_plan9_ctx +{ + grub_extcmd_context_t ctxt; + grub_file_t file; + char *pmap; + grub_size_t pmapalloc; + grub_size_t pmapptr; + int noslash; + int prefixescnt[5]; + char *bootdisk, *bootpart; +}; + +static const char prefixes[5][10] = { + "dos", "plan9", "ntfs", "linux", "linuxswap" +}; + +#include <grub/err.h> + +static inline grub_err_t +grub_extend_alloc (grub_size_t sz, grub_size_t *allocated, char **ptr) +{ + void *n; + if (sz < *allocated) + return GRUB_ERR_NONE; + + *allocated = 2 * sz; + n = grub_realloc (*ptr, *allocated); + if (!n) + return grub_errno; + *ptr = n; + return GRUB_ERR_NONE; +} + +/* Helper for grub_cmd_plan9. */ +static int +fill_partition (grub_disk_t disk, const grub_partition_t partition, void *data) +{ + struct grub_cmd_plan9_ctx *fill_ctx = data; + int file_disk = 0; + int pstart, pend; + + if (!fill_ctx->noslash) + { + if (grub_extend_alloc (fill_ctx->pmapptr + 1, &fill_ctx->pmapalloc, + &fill_ctx->pmap)) + return 1; + fill_ctx->pmap[fill_ctx->pmapptr++] = '/'; + } + fill_ctx->noslash = 0; + + file_disk = fill_ctx->file->device->disk + && disk->id == fill_ctx->file->device->disk->id + && disk->dev->id == fill_ctx->file->device->disk->dev->id; + + pstart = fill_ctx->pmapptr; + if (grub_strcmp (partition->partmap->name, "plan") == 0) + { + unsigned ptr = partition->index + sizeof ("part ") - 1; + grub_err_t err; + disk->partition = partition->parent; + do + { + if (grub_extend_alloc (fill_ctx->pmapptr + 1, &fill_ctx->pmapalloc, + &fill_ctx->pmap)) + return 1; + err = grub_disk_read (disk, 1, ptr, 1, + fill_ctx->pmap + fill_ctx->pmapptr); + if (err) + { + disk->partition = 0; + return err; + } + ptr++; + fill_ctx->pmapptr++; + } + while (grub_isalpha (fill_ctx->pmap[fill_ctx->pmapptr - 1]) + || grub_isdigit (fill_ctx->pmap[fill_ctx->pmapptr - 1])); + fill_ctx->pmapptr--; + } + else + { + char name[50]; + int c = 0; + if (grub_strcmp (partition->partmap->name, "msdos") == 0) + { + switch (partition->msdostype) + { + case GRUB_PC_PARTITION_TYPE_PLAN9: + c = 1; + break; + case GRUB_PC_PARTITION_TYPE_NTFS: + c = 2; + break; + case GRUB_PC_PARTITION_TYPE_MINIX: + case GRUB_PC_PARTITION_TYPE_LINUX_MINIX: + case GRUB_PC_PARTITION_TYPE_EXT2FS: + c = 3; + break; + case GRUB_PC_PARTITION_TYPE_LINUX_SWAP: + c = 4; + break; + } + } + + if (fill_ctx->prefixescnt[c] == 0) + grub_strcpy (name, prefixes[c]); + else + grub_snprintf (name, sizeof (name), "%s.%d", prefixes[c], + fill_ctx->prefixescnt[c]); + fill_ctx->prefixescnt[c]++; + if (grub_extend_alloc (fill_ctx->pmapptr + grub_strlen (name) + 1, + &fill_ctx->pmapalloc, &fill_ctx->pmap)) + return 1; + grub_strcpy (fill_ctx->pmap + fill_ctx->pmapptr, name); + fill_ctx->pmapptr += grub_strlen (name); + } + pend = fill_ctx->pmapptr; + if (grub_extend_alloc (fill_ctx->pmapptr + 2 + 25 + 5 + 25, + &fill_ctx->pmapalloc, &fill_ctx->pmap)) + return 1; + fill_ctx->pmap[fill_ctx->pmapptr++] = ' '; + grub_snprintf (fill_ctx->pmap + fill_ctx->pmapptr, 25 + 5 + 25, + "%" PRIuGRUB_UINT64_T " %" PRIuGRUB_UINT64_T, + grub_partition_get_start (partition), + grub_partition_get_start (partition) + + grub_partition_get_len (partition)); + if (file_disk && grub_partition_get_start (partition) + == grub_partition_get_start (fill_ctx->file->device->disk->partition) + && grub_partition_get_len (partition) + == grub_partition_get_len (fill_ctx->file->device->disk->partition)) + { + grub_free (fill_ctx->bootpart); + fill_ctx->bootpart = grub_strndup (fill_ctx->pmap + pstart, + pend - pstart); + } + + fill_ctx->pmapptr += grub_strlen (fill_ctx->pmap + fill_ctx->pmapptr); + return 0; +} + +/* Helper for grub_cmd_plan9. */ +static int +fill_disk (const char *name, void *data) +{ + struct grub_cmd_plan9_ctx *fill_ctx = data; + grub_device_t dev; + char *plan9name = NULL; + unsigned i; + int file_disk = 0; + + dev = grub_device_open (name); + if (!dev) + { + grub_print_error (); + return 0; + } + if (!dev->disk) + { + grub_device_close (dev); + return 0; + } + file_disk = fill_ctx->file->device->disk + && dev->disk->id == fill_ctx->file->device->disk->id + && dev->disk->dev->id == fill_ctx->file->device->disk->dev->id; + for (i = 0; + fill_ctx->ctxt->state[0].args && fill_ctx->ctxt->state[0].args[i]; i++) + if (grub_strncmp (name, fill_ctx->ctxt->state[0].args[i], + grub_strlen (name)) == 0 + && fill_ctx->ctxt->state[0].args[i][grub_strlen (name)] == '=') + break; + if (fill_ctx->ctxt->state[0].args && fill_ctx->ctxt->state[0].args[i]) + plan9name = grub_strdup (fill_ctx->ctxt->state[0].args[i] + + grub_strlen (name) + 1); + else + switch (dev->disk->dev->id) + { + case GRUB_DISK_DEVICE_BIOSDISK_ID: + if (dev->disk->id & 0x80) + plan9name = grub_xasprintf ("sdB%u", + (unsigned) (dev->disk->id & 0x7f)); + else + plan9name = grub_xasprintf ("fd%u", + (unsigned) (dev->disk->id & 0x7f)); + break; + /* Shouldn't happen as Plan9 doesn't work on these platforms. */ + case GRUB_DISK_DEVICE_OFDISK_ID: + case GRUB_DISK_DEVICE_EFIDISK_ID: + + /* Plan9 doesn't see those. */ + default: + + /* Not sure how to handle those. */ + case GRUB_DISK_DEVICE_NAND_ID: + if (!file_disk) + { + grub_device_close (dev); + return 0; + } + + /* if it's the disk the kernel is loaded from we need to name + it nevertheless. */ + plan9name = grub_strdup ("sdZ0"); + break; + + case GRUB_DISK_DEVICE_ATA_ID: + { + unsigned unit; + if (grub_strlen (dev->disk->name) < sizeof ("ata0") - 1) + unit = 0; + else + unit = grub_strtoul (dev->disk->name + sizeof ("ata0") - 1, 0, 0); + plan9name = grub_xasprintf ("sd%c%d", 'C' + unit / 2, unit % 2); + } + break; + case GRUB_DISK_DEVICE_SCSI_ID: + if (((dev->disk->id >> GRUB_SCSI_ID_SUBSYSTEM_SHIFT) & 0xff) + == GRUB_SCSI_SUBSYSTEM_PATA) + { + unsigned unit; + if (grub_strlen (dev->disk->name) < sizeof ("ata0") - 1) + unit = 0; + else + unit = grub_strtoul (dev->disk->name + sizeof ("ata0") - 1, + 0, 0); + plan9name = grub_xasprintf ("sd%c%d", 'C' + unit / 2, unit % 2); + break; + } + + /* FIXME: how does Plan9 number controllers? + We probably need save the SCSI devices and sort them */ + plan9name + = grub_xasprintf ("sd0%u", (unsigned) + ((dev->disk->id >> GRUB_SCSI_ID_BUS_SHIFT) + & 0xf)); + break; + } + if (!plan9name) + { + grub_print_error (); + grub_device_close (dev); + return 0; + } + if (grub_extend_alloc (fill_ctx->pmapptr + grub_strlen (plan9name) + + sizeof ("part="), &fill_ctx->pmapalloc, + &fill_ctx->pmap)) + { + grub_free (plan9name); + grub_device_close (dev); + return 1; + } + grub_strcpy (fill_ctx->pmap + fill_ctx->pmapptr, plan9name); + fill_ctx->pmapptr += grub_strlen (plan9name); + if (!file_disk) + grub_free (plan9name); + else + { + grub_free (fill_ctx->bootdisk); + fill_ctx->bootdisk = plan9name; + } + grub_strcpy (fill_ctx->pmap + fill_ctx->pmapptr, "part="); + fill_ctx->pmapptr += sizeof ("part=") - 1; + + fill_ctx->noslash = 1; + grub_memset (fill_ctx->prefixescnt, 0, sizeof (fill_ctx->prefixescnt)); + if (grub_partition_iterate (dev->disk, fill_partition, fill_ctx)) + { + grub_device_close (dev); + return 1; + } + if (grub_extend_alloc (fill_ctx->pmapptr + 1, &fill_ctx->pmapalloc, + &fill_ctx->pmap)) + { + grub_device_close (dev); + return 1; + } + fill_ctx->pmap[fill_ctx->pmapptr++] = '\n'; + + grub_device_close (dev); + return 0; +} + +static grub_err_t +grub_cmd_plan9 (grub_extcmd_context_t ctxt, int argc, char *argv[]) +{ + struct grub_cmd_plan9_ctx fill_ctx = { + .ctxt = ctxt, + .file = 0, + .pmap = NULL, + .pmapalloc = 256, + .pmapptr = 0, + .noslash = 1, + .bootdisk = NULL, + .bootpart = NULL + }; + void *mem; + grub_size_t memsize, padsize; + struct grub_plan9_header hdr; + char *config, *configptr; + grub_size_t configsize; + char *bootpath = NULL; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + grub_dl_ref (my_mod); + + rel = grub_relocator_new (); + if (!rel) + goto fail; + + fill_ctx.file = grub_file_open (argv[0], GRUB_FILE_TYPE_PLAN9_KERNEL); + if (! fill_ctx.file) + goto fail; + + fill_ctx.pmap = grub_malloc (fill_ctx.pmapalloc); + if (!fill_ctx.pmap) + goto fail; + + if (grub_disk_dev_iterate (fill_disk, &fill_ctx)) + goto fail; + + if (grub_extend_alloc (fill_ctx.pmapptr + 1, &fill_ctx.pmapalloc, + &fill_ctx.pmap)) + goto fail; + fill_ctx.pmap[fill_ctx.pmapptr] = 0; + + { + char *file_name = grub_strchr (argv[0], ')'); + if (file_name) + file_name++; + else + file_name = argv[0]; + if (*file_name) + file_name++; + + if (fill_ctx.bootpart) + bootpath = grub_xasprintf ("%s!%s!%s", fill_ctx.bootdisk, + fill_ctx.bootpart, file_name); + else + bootpath = grub_xasprintf ("%s!%s", fill_ctx.bootdisk, file_name); + grub_free (fill_ctx.bootdisk); + grub_free (fill_ctx.bootpart); + } + if (!bootpath) + goto fail; + + if (grub_file_read (fill_ctx.file, &hdr, + sizeof (hdr)) != (grub_ssize_t) sizeof (hdr)) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + argv[0]); + goto fail; + } + + if (grub_be_to_cpu32 (hdr.magic) != GRUB_PLAN9_MAGIC + || hdr.zero) + { + grub_error (GRUB_ERR_BAD_OS, "unsupported Plan9"); + goto fail; + } + + memsize = ALIGN_UP (grub_be_to_cpu32 (hdr.text_size) + sizeof (hdr), + GRUB_PLAN9_ALIGN); + memsize += ALIGN_UP (grub_be_to_cpu32 (hdr.data_size), GRUB_PLAN9_ALIGN); + memsize += ALIGN_UP(grub_be_to_cpu32 (hdr.bss_size), GRUB_PLAN9_ALIGN); + eip = grub_be_to_cpu32 (hdr.entry_addr) & 0xfffffff; + + /* path */ + configsize = GRUB_PLAN9_CONFIG_PATH_SIZE; + /* magic */ + configsize += sizeof (GRUB_PLAN9_CONFIG_MAGIC) - 1; + { + int i; + for (i = 1; i < argc; i++) + configsize += grub_strlen (argv[i]) + 1; + } + configsize += (sizeof ("bootfile=") - 1) + grub_strlen (bootpath) + 1; + configsize += fill_ctx.pmapptr; + /* Terminating \0. */ + configsize++; + + { + grub_relocator_chunk_t ch; + grub_err_t err; + err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_PLAN9_CONFIG_ADDR, + configsize); + if (err) + goto fail; + config = get_virtual_current_address (ch); + } + + grub_memset (config, 0, GRUB_PLAN9_CONFIG_PATH_SIZE); + grub_strncpy (config, bootpath, GRUB_PLAN9_CONFIG_PATH_SIZE - 1); + + configptr = config + GRUB_PLAN9_CONFIG_PATH_SIZE; + grub_memcpy (configptr, GRUB_PLAN9_CONFIG_MAGIC, + sizeof (GRUB_PLAN9_CONFIG_MAGIC) - 1); + configptr += sizeof (GRUB_PLAN9_CONFIG_MAGIC) - 1; + configptr = grub_stpcpy (configptr, "bootfile="); + configptr = grub_stpcpy (configptr, bootpath); + *configptr++ = '\n'; + char *cmdline = configptr; + { + int i; + for (i = 1; i < argc; i++) + { + configptr = grub_stpcpy (configptr, argv[i]); + *configptr++ = '\n'; + } + } + + { + grub_err_t err; + *configptr = '\0'; + err = grub_verify_string (cmdline, GRUB_VERIFY_KERNEL_CMDLINE); + if (err) + goto fail; + } + + configptr = grub_stpcpy (configptr, fill_ctx.pmap); + + { + grub_relocator_chunk_t ch; + grub_err_t err; + + err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_PLAN9_TARGET, + memsize); + if (err) + goto fail; + mem = get_virtual_current_address (ch); + } + + { + grub_uint8_t *ptr; + ptr = mem; + grub_memcpy (ptr, &hdr, sizeof (hdr)); + ptr += sizeof (hdr); + + if (grub_file_read (fill_ctx.file, ptr, grub_be_to_cpu32 (hdr.text_size)) + != (grub_ssize_t) grub_be_to_cpu32 (hdr.text_size)) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + argv[0]); + goto fail; + } + ptr += grub_be_to_cpu32 (hdr.text_size); + padsize = ALIGN_UP (grub_be_to_cpu32 (hdr.text_size) + sizeof (hdr), + GRUB_PLAN9_ALIGN) - grub_be_to_cpu32 (hdr.text_size) + - sizeof (hdr); + + grub_memset (ptr, 0, padsize); + ptr += padsize; + + if (grub_file_read (fill_ctx.file, ptr, grub_be_to_cpu32 (hdr.data_size)) + != (grub_ssize_t) grub_be_to_cpu32 (hdr.data_size)) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + argv[0]); + goto fail; + } + ptr += grub_be_to_cpu32 (hdr.data_size); + padsize = ALIGN_UP (grub_be_to_cpu32 (hdr.data_size), GRUB_PLAN9_ALIGN) + - grub_be_to_cpu32 (hdr.data_size); + + grub_memset (ptr, 0, padsize); + ptr += padsize; + grub_memset (ptr, 0, ALIGN_UP(grub_be_to_cpu32 (hdr.bss_size), + GRUB_PLAN9_ALIGN)); + } + grub_loader_set (grub_plan9_boot, grub_plan9_unload, 1); + return GRUB_ERR_NONE; + + fail: + grub_free (fill_ctx.pmap); + + if (fill_ctx.file) + grub_file_close (fill_ctx.file); + + grub_plan9_unload (); + + return grub_errno; +} + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(plan9) +{ + cmd = grub_register_extcmd ("plan9", grub_cmd_plan9, + GRUB_COMMAND_OPTIONS_AT_START, + N_("KERNEL ARGS"), N_("Load Plan9 kernel."), + options); + my_mod = mod; +} + +GRUB_MOD_FINI(plan9) +{ + grub_unregister_extcmd (cmd); +} diff --git a/grub-core/loader/i386/pc/pxechainloader.c b/grub-core/loader/i386/pc/pxechainloader.c new file mode 100644 index 0000000..acb0611 --- /dev/null +++ b/grub-core/loader/i386/pc/pxechainloader.c @@ -0,0 +1,168 @@ +/* chainloader.c - boot another boot loader */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2004,2007,2009,2010,2012 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/loader.h> +#include <grub/file.h> +#include <grub/err.h> +#include <grub/device.h> +#include <grub/disk.h> +#include <grub/misc.h> +#include <grub/types.h> +#include <grub/partition.h> +#include <grub/dl.h> +#include <grub/command.h> +#include <grub/machine/biosnum.h> +#include <grub/i18n.h> +#include <grub/video.h> +#include <grub/mm.h> +#include <grub/cpu/relocator.h> +#include <grub/machine/pxe.h> +#include <grub/net.h> + +static grub_dl_t my_mod; +static struct grub_relocator *rel; +static grub_uint32_t edx = 0xffffffff; +static char boot_file[128]; +static char server_name[64]; + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t +grub_pxechain_boot (void) +{ + struct grub_relocator16_state state = { + .cs = 0, + .ip = 0x7c00, + .ds = 0, + .es = 0, + .fs = 0, + .gs = 0, + .ss = 0, + .sp = 0x7c00, + .edx = edx + }; + struct grub_net_bootp_packet *bp; + + bp = grub_pxe_get_cached (GRUB_PXENV_PACKET_TYPE_DHCP_ACK); + + grub_video_set_mode ("text", 0, 0); + + if (bp && boot_file[0]) + grub_memcpy (bp->boot_file, boot_file, sizeof (bp->boot_file)); + if (bp && server_name[0]) + grub_memcpy (bp->server_name, server_name, sizeof (bp->server_name)); + + return grub_relocator16_boot (rel, state); +} + +static grub_err_t +grub_pxechain_unload (void) +{ + grub_relocator_unload (rel); + rel = NULL; + grub_dl_unref (my_mod); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_pxechain (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file = 0; + grub_err_t err; + void *image; + grub_size_t imagesize; + char *fname; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + grub_dl_ref (my_mod); + + rel = grub_relocator_new (); + if (!rel) + goto fail; + + file = grub_file_open (argv[0], GRUB_FILE_TYPE_PXECHAINLOADER); + if (! file) + goto fail; + + if (file->device->net && file->device->net->name) + fname = file->device->net->name; + else + { + fname = argv[0]; + if (fname[0] == '(') + { + fname = grub_strchr (fname, ')'); + if (fname) + fname++; + else + fname = argv[0]; + } + } + + grub_memset (boot_file, 0, sizeof (boot_file)); + grub_strncpy (boot_file, fname, sizeof (boot_file)); + + grub_memset (server_name, 0, sizeof (server_name)); + if (file->device->net && file->device->net->server) + grub_strncpy (server_name, file->device->net->server, sizeof (server_name)); + + edx = grub_get_root_biosnumber (); + + imagesize = grub_file_size (file); + { + grub_relocator_chunk_t ch; + err = grub_relocator_alloc_chunk_addr (rel, &ch, 0x7c00, imagesize); + if (err) + goto fail; + image = get_virtual_current_address (ch); + } + + if (grub_file_read (file, image, imagesize) != (grub_ssize_t) imagesize) + goto fail; + + grub_loader_set (grub_pxechain_boot, grub_pxechain_unload, + GRUB_LOADER_FLAG_NORETURN | GRUB_LOADER_FLAG_PXE_NOT_UNLOAD); + return GRUB_ERR_NONE; + + fail: + + if (file) + grub_file_close (file); + + grub_pxechain_unload (); + + return grub_errno; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(pxechainloader) +{ + cmd = grub_register_command ("pxechainloader", grub_cmd_pxechain, + 0, N_("Load a PXE image.")); + my_mod = mod; +} + +GRUB_MOD_FINI(pxechainloader) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/loader/i386/pc/truecrypt.c b/grub-core/loader/i386/pc/truecrypt.c new file mode 100644 index 0000000..cbeeec7 --- /dev/null +++ b/grub-core/loader/i386/pc/truecrypt.c @@ -0,0 +1,233 @@ +/* chainloader.c - boot another boot loader */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2004,2007,2009,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/loader.h> +#include <grub/file.h> +#include <grub/err.h> +#include <grub/device.h> +#include <grub/disk.h> +#include <grub/misc.h> +#include <grub/types.h> +#include <grub/partition.h> +#include <grub/dl.h> +#include <grub/command.h> +#include <grub/machine/biosnum.h> +#include <grub/i18n.h> +#include <grub/video.h> +#include <grub/mm.h> +#include <grub/cpu/relocator.h> +#include <grub/deflate.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_dl_t my_mod; +static struct grub_relocator *rel; +static grub_uint32_t edx = 0xffffffff; +static grub_uint16_t sp; +static grub_uint32_t destaddr; + +#define GRUB_TRUECRYPT_SEGMENT 0x2000 + +static grub_err_t +grub_truecrypt_boot (void) +{ + grub_uint16_t segment = destaddr >> 4; + struct grub_relocator16_state state = { + .cs = segment, + .ds = segment, + .es = segment, + .fs = segment, + .gs = segment, + .ss = segment, + .ip = 0x100, + .sp = sp, + .edx = edx, + .a20 = 1 + }; + grub_video_set_mode ("text", 0, 0); + + return grub_relocator16_boot (rel, state); +} + +static grub_err_t +grub_truecrypt_unload (void) +{ + grub_relocator_unload (rel); + rel = NULL; + grub_dl_unref (my_mod); + return GRUB_ERR_NONE; +} + +/* Information on protocol supplied by Attila Lendvai. */ +#define MAGIC "\0CD001\1EL TORITO SPECIFICATION" + +static grub_err_t +grub_cmd_truecrypt (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file = 0; + grub_err_t err; + void *truecrypt; + grub_size_t truecryptsize; + const grub_size_t truecryptmemsize = 42 * 1024; + grub_uint8_t dh; + grub_uint32_t catalog, rba; + grub_uint8_t buf[128]; + char *compressed = NULL; + char *uncompressed = NULL; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + rel = NULL; + + grub_dl_ref (my_mod); + + file = grub_file_open (argv[0], GRUB_FILE_TYPE_TRUECRYPT); + if (! file) + goto fail; + + if (grub_file_seek (file, 17 * 2048) == (grub_size_t) -1) + goto fail; + + if (grub_file_read (file, buf, sizeof (buf)) + != sizeof (buf)) + goto fail; + + if (grub_memcmp (buf, MAGIC, sizeof (MAGIC)) != 0) + { + grub_error (GRUB_ERR_BAD_OS, "invalid eltorito signature"); + goto fail; + } + + catalog = grub_get_unaligned32 (buf + 0x47); + + if (grub_file_seek (file, catalog * 2048) == (grub_size_t)-1) + goto fail; + + if (grub_file_read (file, buf, sizeof (buf)) + != sizeof (buf)) + goto fail; + + if (buf[0] != 1 || buf[1] != 0 || buf[0x1e] != 0x55 + || buf[0x1f] != 0xaa || buf[0x20] != 0x88 + || buf[0x26] != 1 || buf[0x27] != 0) + { + grub_error (GRUB_ERR_BAD_OS, "invalid eltorito catalog"); + goto fail; + } + + rba = grub_get_unaligned32 (buf + 0x28); + + if (grub_file_seek (file, rba * 2048 + 0x1b7) == (grub_size_t) -1) + goto fail; + + if (grub_file_read (file, &dh, 1) + != 1) + goto fail; + + if (grub_file_seek (file, rba * 2048 + 512 + 2048) == (grub_size_t) -1) + goto fail; + + compressed = grub_malloc (57 * 512); + if (!compressed) + goto fail; + + if (grub_file_read (file, compressed, 57 * 512) + != 57 * 512) + goto fail; + + uncompressed = grub_malloc (truecryptmemsize); + if (!uncompressed) + goto fail; + + /* It's actually gzip but our gzip decompressor isn't able to handle + trailing garbage, hence let's use raw decompressor. */ + truecryptsize = grub_deflate_decompress (compressed + 10, 57 * 512 - 10, + 0, uncompressed, truecryptmemsize); + if ((grub_ssize_t) truecryptsize < 0) + goto fail; + + if (truecryptmemsize <= truecryptsize + 0x100) + { + grub_error (GRUB_ERR_BAD_OS, "file is too big"); + goto fail; + } + + rel = grub_relocator_new (); + if (!rel) + goto fail; + + edx = (dh << 8) | grub_get_root_biosnumber (); + + destaddr = ALIGN_DOWN (grub_min (0x90000, grub_mmap_get_lower ()) + - truecryptmemsize, 64 * 1024); + + { + grub_relocator_chunk_t ch; + err = grub_relocator_alloc_chunk_addr (rel, &ch, destaddr, + truecryptmemsize); + if (err) + goto fail; + truecrypt = get_virtual_current_address (ch); + } + + grub_memset (truecrypt, 0, 0x100); + grub_memcpy ((char *) truecrypt + 0x100, uncompressed, truecryptsize); + + grub_memset ((char *) truecrypt + truecryptsize + 0x100, + 0, truecryptmemsize - truecryptsize - 0x100); + sp = truecryptmemsize - 4; + + grub_loader_set (grub_truecrypt_boot, grub_truecrypt_unload, 1); + + grub_free (uncompressed); + grub_free (compressed); + + return GRUB_ERR_NONE; + + fail: + + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, "bad truecrypt ISO"); + + if (file) + grub_file_close (file); + + grub_truecrypt_unload (); + + grub_free (uncompressed); + grub_free (compressed); + + return grub_errno; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(truecrypt) +{ + cmd = grub_register_command ("truecrypt", grub_cmd_truecrypt, + 0, N_("Load Truecrypt ISO.")); + my_mod = mod; +} + +GRUB_MOD_FINI(truecrypt) +{ + grub_unregister_command (cmd); +} |