diff --git a/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/arm/linux.c b/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/arm/linux.c new file mode 100644 index 00000000..51684914 --- /dev/null +++ b/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/arm/linux.c @@ -0,0 +1,509 @@ +/* linux.c - boot Linux */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_dl_t my_mod; + +static grub_addr_t initrd_start; +static grub_addr_t initrd_end; + +static grub_addr_t linux_addr; +static grub_size_t linux_size; + +static char *linux_args; + +static grub_uint32_t machine_type; +static const void *current_fdt; + +typedef void (*kernel_entry_t) (int, unsigned long, void *); + +#define LINUX_PHYS_OFFSET (0x00008000) +#define LINUX_INITRD_PHYS_OFFSET (LINUX_PHYS_OFFSET + 0x03000000) +#define LINUX_FDT_PHYS_OFFSET (LINUX_INITRD_PHYS_OFFSET - 0x10000) + +static grub_size_t +get_atag_size (const grub_uint32_t *atag) +{ + const grub_uint32_t *atag0 = atag; + while (atag[0] && atag[1]) + atag += atag[0]; + return atag - atag0; +} + +/* + * linux_prepare_fdt(): + * Prepares a loaded FDT for being passed to Linux. + * Merges in command line parameters and sets up initrd addresses. + */ +static grub_err_t +linux_prepare_atag (void *target_atag) +{ + const grub_uint32_t *atag_orig = (const grub_uint32_t *) current_fdt; + grub_uint32_t *tmp_atag, *to; + const grub_uint32_t *from; + grub_size_t tmp_size; + grub_size_t arg_size = grub_strlen (linux_args); + char *cmdline_orig = NULL; + grub_size_t cmdline_orig_len = 0; + + /* some place for cmdline, initrd and terminator. */ + tmp_size = get_atag_size (atag_orig) + 20 + (arg_size) / 4; + tmp_atag = grub_malloc (tmp_size * sizeof (grub_uint32_t)); + if (!tmp_atag) + return grub_errno; + + for (from = atag_orig, to = tmp_atag; from[0] && from[1]; + from += from[0]) + switch (from[1]) + { + case 0x54410004: + case 0x54410005: + case 0x54420005: + break; + case 0x54410009: + if (*(char *) (from + 2)) + { + cmdline_orig = (char *) (from + 2); + cmdline_orig_len = grub_strlen (cmdline_orig) + 1; + } + break; + default: + grub_memcpy (to, from, sizeof (grub_uint32_t) * from[0]); + to += from[0]; + break; + } + + grub_dprintf ("linux", "linux inherited args: '%s'\n", + cmdline_orig ? : ""); + grub_dprintf ("linux", "linux_args: '%s'\n", linux_args); + + /* Generate and set command line */ + to[0] = 3 + (arg_size + cmdline_orig_len) / 4; + to[1] = 0x54410009; + if (cmdline_orig) + { + grub_memcpy ((char *) to + 8, cmdline_orig, cmdline_orig_len - 1); + *((char *) to + 8 + cmdline_orig_len - 1) = ' '; + } + grub_memcpy ((char *) to + 8 + cmdline_orig_len, linux_args, arg_size); + grub_memset ((char *) to + 8 + cmdline_orig_len + arg_size, 0, + 4 - ((arg_size + cmdline_orig_len) & 3)); + to += to[0]; + + if (initrd_start && initrd_end) + { + /* + * We're using physical addresses, so even if we have LPAE, we're + * restricted to a 32-bit address space. + */ + grub_dprintf ("loader", "Initrd @ 0x%08x-0x%08x\n", + initrd_start, initrd_end); + + to[0] = 4; + to[1] = 0x54420005; + to[2] = initrd_start; + to[3] = initrd_end - initrd_start; + to += 4; + } + + to[0] = 0; + to[1] = 0; + to += 2; + + /* Copy updated FDT to its launch location */ + grub_memcpy (target_atag, tmp_atag, sizeof (grub_uint32_t) * (to - tmp_atag)); + grub_free (tmp_atag); + + grub_dprintf ("loader", "ATAG updated for Linux boot\n"); + + return GRUB_ERR_NONE; +} + +/* + * linux_prepare_fdt(): + * Prepares a loaded FDT for being passed to Linux. + * Merges in command line parameters and sets up initrd addresses. + */ +static grub_err_t +linux_prepare_fdt (void *target_fdt) +{ + int node; + int retval; + int tmp_size; + void *tmp_fdt; + + tmp_size = grub_fdt_get_totalsize (current_fdt) + 0x100 + grub_strlen (linux_args); + tmp_fdt = grub_malloc (tmp_size); + if (!tmp_fdt) + return grub_errno; + + grub_memcpy (tmp_fdt, current_fdt, grub_fdt_get_totalsize (current_fdt)); + grub_fdt_set_totalsize (tmp_fdt, tmp_size); + + /* Find or create '/chosen' node */ + node = grub_fdt_find_subnode (tmp_fdt, 0, "chosen"); + if (node < 0) + { + grub_dprintf ("linux", "No 'chosen' node in FDT - creating.\n"); + node = grub_fdt_add_subnode (tmp_fdt, 0, "chosen"); + if (node < 0) + goto failure; + } + + grub_dprintf ("linux", "linux_args: '%s'\n", linux_args); + + /* Generate and set command line */ + retval = grub_fdt_set_prop (tmp_fdt, node, "bootargs", linux_args, + grub_strlen (linux_args) + 1); + if (retval) + goto failure; + + if (initrd_start && initrd_end) + { + /* + * We're using physical addresses, so even if we have LPAE, we're + * restricted to a 32-bit address space. + */ + grub_dprintf ("loader", "Initrd @ 0x%08x-0x%08x\n", + initrd_start, initrd_end); + + retval = grub_fdt_set_prop32 (tmp_fdt, node, "linux,initrd-start", + initrd_start); + if (retval) + goto failure; + retval = grub_fdt_set_prop32 (tmp_fdt, node, "linux,initrd-end", + initrd_end); + if (retval) + goto failure; + } + + /* Copy updated FDT to its launch location */ + grub_memcpy (target_fdt, tmp_fdt, tmp_size); + grub_free (tmp_fdt); + + grub_dprintf ("loader", "FDT updated for Linux boot\n"); + + return GRUB_ERR_NONE; + +failure: + grub_free (tmp_fdt); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unable to prepare FDT"); +} + +static grub_err_t +linux_boot (void) +{ + kernel_entry_t linuxmain; + int fdt_valid, atag_valid; + void *target_fdt = 0; + + fdt_valid = (current_fdt && grub_fdt_check_header_nosize (current_fdt) == 0); + atag_valid = ((((const grub_uint16_t *) current_fdt)[3] & ~3) == 0x5440 + && *((const grub_uint32_t *) current_fdt)); + grub_dprintf ("loader", "atag: %p, %x, %x, %s, %s\n", + current_fdt, + ((const grub_uint16_t *) current_fdt)[3], + *((const grub_uint32_t *) current_fdt), + (const char *) current_fdt, + (const char *) current_fdt + 1); + + if (!fdt_valid && machine_type == GRUB_ARM_MACHINE_TYPE_FDT) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, + N_("device tree must be supplied (see `devicetree' command)")); + + grub_arch_sync_caches ((void *) linux_addr, linux_size); + + grub_dprintf ("loader", "Kernel at: 0x%x\n", linux_addr); + + if (fdt_valid || atag_valid) + { +#ifdef GRUB_MACHINE_EFI + grub_size_t size; + if (fdt_valid) + size = grub_fdt_get_totalsize (current_fdt); + else + size = 4 * get_atag_size (current_fdt); + size += grub_strlen (linux_args) + 256; + target_fdt = grub_efi_allocate_loader_memory (LINUX_FDT_PHYS_OFFSET, size); + if (!target_fdt) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); +#else + target_fdt = (void *) LINUX_FDT_ADDRESS; +#endif + } + + if (fdt_valid) + { + grub_err_t err; + + err = linux_prepare_fdt (target_fdt); + if (err) + return err; + grub_dprintf ("loader", "FDT @ %p\n", target_fdt); + } + else if (atag_valid) + { + grub_err_t err; + + err = linux_prepare_atag (target_fdt); + if (err) + return err; + grub_dprintf ("loader", "ATAG @ %p\n", target_fdt); + } + + grub_dprintf ("loader", "Jumping to Linux...\n"); + + /* Boot the kernel. + * Arguments to kernel: + * r0 - 0 + * r1 - machine type + * r2 - address of DTB + */ + linuxmain = (kernel_entry_t) linux_addr; + + grub_arm_disable_caches_mmu (); + + linuxmain (0, machine_type, target_fdt); + + return grub_error (GRUB_ERR_BAD_OS, "Linux call returned"); +} + +/* + * Only support zImage, so no relocations necessary + */ +static grub_err_t +linux_load (const char *filename, grub_file_t file) +{ + struct linux_arm_kernel_header *lh; + int size; + + size = grub_file_size (file); + + linux_addr = LINUX_ADDRESS; + grub_dprintf ("loader", "Loading Linux to 0x%08x\n", + (grub_addr_t) linux_addr); + + if (grub_file_read (file, (void *) linux_addr, size) != size) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + filename); + return grub_errno; + } + + lh = (void *) linux_addr; + + if ((grub_size_t) size > sizeof (*lh) && + lh->magic == GRUB_LINUX_ARM_MAGIC_SIGNATURE) + ; + else if (size > 0x8000 && *(grub_uint32_t *) (linux_addr) == 0xea000006 + && machine_type == GRUB_ARM_MACHINE_TYPE_RASPBERRY_PI) + grub_memmove ((void *) linux_addr, (void *) (linux_addr + 0x8000), + size - 0x8000); + else + return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("invalid zImage")); + + linux_size = size; + + return GRUB_ERR_NONE; +} + +static grub_err_t +linux_unload (void) +{ + grub_dl_unref (my_mod); + + grub_free (linux_args); + linux_args = NULL; + + initrd_start = initrd_end = 0; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + int size; + grub_err_t err; + grub_file_t file; + grub_dl_ref (my_mod); + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL); + if (!file) + goto fail; + + err = linux_load (argv[0], file); + grub_file_close (file); + if (err) + goto fail; + + grub_loader_set (linux_boot, linux_unload, 0); + + size = grub_loader_cmdline_size (argc, argv); + linux_args = grub_malloc (size + sizeof (LINUX_IMAGE)); + if (!linux_args) + { + grub_loader_unset(); + goto fail; + } + + /* Create kernel command line. */ + grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE)); + err = grub_create_loader_cmdline (argc, argv, + linux_args + sizeof (LINUX_IMAGE) - 1, size, + GRUB_VERIFY_KERNEL_CMDLINE); + if (err) + goto fail; + + return GRUB_ERR_NONE; + +fail: + grub_dl_unref (my_mod); + return grub_errno; +} + +static grub_err_t +grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file; + grub_size_t size = 0; + struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 }; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_INITRD); + if (!file) + return grub_errno; + + if (grub_initrd_init (argc, argv, &initrd_ctx)) + goto fail; + + size = grub_get_initrd_size (&initrd_ctx); + + initrd_start = LINUX_INITRD_ADDRESS; + + grub_dprintf ("loader", "Loading initrd to 0x%08x\n", + (grub_addr_t) initrd_start); + + if (grub_initrd_load (&initrd_ctx, argv, (void *) initrd_start)) + goto fail; + + initrd_end = initrd_start + size; + + return GRUB_ERR_NONE; + +fail: + grub_file_close (file); + + return grub_errno; +} + +static grub_err_t +load_dtb (grub_file_t dtb, int size) +{ + void *new_fdt = grub_zalloc (size); + if (!new_fdt) + return grub_errno; + grub_dprintf ("loader", "Loading device tree to %p\n", + new_fdt); + if ((grub_file_read (dtb, new_fdt, size) != size) + || (grub_fdt_check_header (new_fdt, size) != 0)) + { + grub_free (new_fdt); + return grub_error (GRUB_ERR_BAD_OS, N_("invalid device tree")); + } + + grub_fdt_set_totalsize (new_fdt, size); + current_fdt = new_fdt; + /* + * We've successfully loaded an FDT, so any machine type passed + * from firmware is now obsolete. + */ + machine_type = GRUB_ARM_MACHINE_TYPE_FDT; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_devicetree (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t dtb; + int size; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + dtb = grub_file_open (argv[0], GRUB_FILE_TYPE_DEVICE_TREE_IMAGE); + if (!dtb) + return grub_errno; + + size = grub_file_size (dtb); + if (size == 0) + grub_error (GRUB_ERR_BAD_OS, "empty file"); + else + load_dtb (dtb, size); + grub_file_close (dtb); + + return grub_errno; +} + +static grub_command_t cmd_linux, cmd_initrd, cmd_devicetree; + +GRUB_MOD_INIT (linux) +{ + cmd_linux = grub_register_command ("linux", grub_cmd_linux, + 0, N_("Load Linux.")); + cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd, + 0, N_("Load initrd.")); + cmd_devicetree = grub_register_command ("devicetree", grub_cmd_devicetree, + /* TRANSLATORS: DTB stands for device tree blob. */ + 0, N_("Load DTB file.")); + my_mod = mod; + current_fdt = (const void *) grub_arm_firmware_get_boot_data (); + machine_type = grub_arm_firmware_get_machine_type (); +} + +GRUB_MOD_FINI (linux) +{ + grub_unregister_command (cmd_linux); + grub_unregister_command (cmd_initrd); + grub_unregister_command (cmd_devicetree); +} diff --git a/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/i386/pc/linux.c b/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/i386/pc/linux.c new file mode 100644 index 00000000..47ea2945 --- /dev/null +++ b/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/i386/pc/linux.c @@ -0,0 +1,490 @@ +/* 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; + grub_linux16_prot_size = grub_file_size (file) + - real_size - GRUB_DISK_SECTOR_SIZE; + + 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%x > 0x%x), use bzImage instead", + (char *) GRUB_LINUX_ZIMAGE_ADDR + grub_linux16_prot_size, + (grub_size_t) 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) + { + 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 (relocator, &ch, + addr_min, addr_max - size, + 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/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/i386/xen.c b/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/i386/xen.c new file mode 100644 index 00000000..8f662c8a --- /dev/null +++ b/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/i386/xen.c @@ -0,0 +1,978 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#ifdef __x86_64__ +#define NUMBER_OF_LEVELS 4 +#define INTERMEDIATE_OR (GRUB_PAGE_PRESENT | GRUB_PAGE_RW | GRUB_PAGE_USER) +#define VIRT_MASK 0x0000ffffffffffffULL +#else +#define NUMBER_OF_LEVELS 3 +#define INTERMEDIATE_OR (GRUB_PAGE_PRESENT | GRUB_PAGE_RW) +#define VIRT_MASK 0x00000000ffffffffULL +#define HYPERVISOR_PUD_ADDRESS 0xc0000000ULL +#endif + +struct grub_xen_mapping_lvl { + grub_uint64_t virt_start; + grub_uint64_t virt_end; + grub_uint64_t pfn_start; + grub_uint64_t n_pt_pages; +}; + +struct grub_xen_mapping { + grub_uint64_t *where; + struct grub_xen_mapping_lvl area; + struct grub_xen_mapping_lvl lvls[NUMBER_OF_LEVELS]; +}; + +struct xen_loader_state { + struct grub_relocator *relocator; + struct grub_relocator_xen_state state; + struct start_info next_start; + struct grub_xen_file_info xen_inf; + grub_xen_mfn_t *virt_mfn_list; + struct start_info *virt_start_info; + grub_xen_mfn_t console_pfn; + grub_uint64_t max_addr; + grub_uint64_t pgtbl_end; + struct xen_multiboot_mod_list *module_info_page; + grub_uint64_t modules_target_start; + grub_size_t n_modules; + struct grub_xen_mapping *map_reloc; + struct grub_xen_mapping mappings[XEN_MAX_MAPPINGS]; + int n_mappings; + int loaded; +}; + +static struct xen_loader_state xen_state; + +static grub_dl_t my_mod; + +#define PAGE_SIZE (1UL << PAGE_SHIFT) +#define MAX_MODULES (PAGE_SIZE / sizeof (struct xen_multiboot_mod_list)) +#define STACK_SIZE 1048576 +#define ADDITIONAL_SIZE (1 << 19) +#define ALIGN_SIZE (1 << 22) +#define LOG_POINTERS_PER_PAGE 9 +#define POINTERS_PER_PAGE (1 << LOG_POINTERS_PER_PAGE) + +static grub_uint64_t +page2offset (grub_uint64_t page) +{ + return page << PAGE_SHIFT; +} + +static grub_err_t +get_pgtable_size (grub_uint64_t from, grub_uint64_t to, grub_uint64_t pfn) +{ + struct grub_xen_mapping *map, *map_cmp; + grub_uint64_t mask, bits; + int i, m; + + if (xen_state.n_mappings == XEN_MAX_MAPPINGS) + return grub_error (GRUB_ERR_BUG, "too many mapped areas"); + + grub_dprintf ("xen", "get_pgtable_size %d from=%llx, to=%llx, pfn=%llx\n", + xen_state.n_mappings, (unsigned long long) from, + (unsigned long long) to, (unsigned long long) pfn); + + map = xen_state.mappings + xen_state.n_mappings; + grub_memset (map, 0, sizeof (*map)); + + map->area.virt_start = from & VIRT_MASK; + map->area.virt_end = (to - 1) & VIRT_MASK; + map->area.n_pt_pages = 0; + + for (i = NUMBER_OF_LEVELS - 1; i >= 0; i--) + { + map->lvls[i].pfn_start = pfn + map->area.n_pt_pages; + if (i == NUMBER_OF_LEVELS - 1) + { + if (xen_state.n_mappings == 0) + { + map->lvls[i].virt_start = 0; + map->lvls[i].virt_end = VIRT_MASK; + map->lvls[i].n_pt_pages = 1; + map->area.n_pt_pages++; + } + continue; + } + + bits = PAGE_SHIFT + (i + 1) * LOG_POINTERS_PER_PAGE; + mask = (1ULL << bits) - 1; + map->lvls[i].virt_start = map->area.virt_start & ~mask; + map->lvls[i].virt_end = map->area.virt_end | mask; +#ifdef __i386__ + /* PAE wants last root directory present. */ + if (i == 1 && to <= HYPERVISOR_PUD_ADDRESS && xen_state.n_mappings == 0) + map->lvls[i].virt_end = VIRT_MASK; +#endif + for (m = 0; m < xen_state.n_mappings; m++) + { + map_cmp = xen_state.mappings + m; + if (map_cmp->lvls[i].virt_start == map_cmp->lvls[i].virt_end) + continue; + if (map->lvls[i].virt_start >= map_cmp->lvls[i].virt_start && + map->lvls[i].virt_end <= map_cmp->lvls[i].virt_end) + { + map->lvls[i].virt_start = 0; + map->lvls[i].virt_end = 0; + break; + } + if (map->lvls[i].virt_start >= map_cmp->lvls[i].virt_start && + map->lvls[i].virt_start <= map_cmp->lvls[i].virt_end) + map->lvls[i].virt_start = map_cmp->lvls[i].virt_end + 1; + if (map->lvls[i].virt_end >= map_cmp->lvls[i].virt_start && + map->lvls[i].virt_end <= map_cmp->lvls[i].virt_end) + map->lvls[i].virt_end = map_cmp->lvls[i].virt_start - 1; + } + if (map->lvls[i].virt_start < map->lvls[i].virt_end) + map->lvls[i].n_pt_pages = + ((map->lvls[i].virt_end - map->lvls[i].virt_start) >> bits) + 1; + map->area.n_pt_pages += map->lvls[i].n_pt_pages; + grub_dprintf ("xen", "get_pgtable_size level %d: virt %llx-%llx %d pts\n", + i, (unsigned long long) map->lvls[i].virt_start, + (unsigned long long) map->lvls[i].virt_end, + (int) map->lvls[i].n_pt_pages); + } + + grub_dprintf ("xen", "get_pgtable_size return: %d page tables\n", + (int) map->area.n_pt_pages); + + xen_state.state.paging_start[xen_state.n_mappings] = pfn; + xen_state.state.paging_size[xen_state.n_mappings] = map->area.n_pt_pages; + + return GRUB_ERR_NONE; +} + +static grub_uint64_t * +get_pg_table_virt (int mapping, int level) +{ + grub_uint64_t pfn; + struct grub_xen_mapping *map; + + map = xen_state.mappings + mapping; + pfn = map->lvls[level].pfn_start - map->lvls[NUMBER_OF_LEVELS - 1].pfn_start; + return map->where + pfn * POINTERS_PER_PAGE; +} + +static grub_uint64_t +get_pg_table_prot (int level, grub_uint64_t pfn) +{ + int m; + grub_uint64_t pfn_s, pfn_e; + + if (level > 0) + return INTERMEDIATE_OR; + for (m = 0; m < xen_state.n_mappings; m++) + { + pfn_s = xen_state.mappings[m].lvls[NUMBER_OF_LEVELS - 1].pfn_start; + pfn_e = xen_state.mappings[m].area.n_pt_pages + pfn_s; + if (pfn >= pfn_s && pfn < pfn_e) + return GRUB_PAGE_PRESENT | GRUB_PAGE_USER; + } + return GRUB_PAGE_PRESENT | GRUB_PAGE_RW | GRUB_PAGE_USER; +} + +static void +generate_page_table (grub_xen_mfn_t *mfn_list) +{ + int l, m1, m2; + long p, p_s, p_e; + grub_uint64_t start, end, pfn; + grub_uint64_t *pg; + struct grub_xen_mapping_lvl *lvl; + + for (m1 = 0; m1 < xen_state.n_mappings; m1++) + grub_memset (xen_state.mappings[m1].where, 0, + xen_state.mappings[m1].area.n_pt_pages * PAGE_SIZE); + + for (l = NUMBER_OF_LEVELS - 1; l >= 0; l--) + { + for (m1 = 0; m1 < xen_state.n_mappings; m1++) + { + start = xen_state.mappings[m1].lvls[l].virt_start; + end = xen_state.mappings[m1].lvls[l].virt_end; + pg = get_pg_table_virt(m1, l); + for (m2 = 0; m2 < xen_state.n_mappings; m2++) + { + lvl = (l > 0) ? xen_state.mappings[m2].lvls + l - 1 + : &xen_state.mappings[m2].area; + if (l > 0 && lvl->n_pt_pages == 0) + continue; + if (lvl->virt_start >= end || lvl->virt_end <= start) + continue; + p_s = (grub_max (start, lvl->virt_start) - start) >> + (PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE); + p_e = (grub_min (end, lvl->virt_end) - start) >> + (PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE); + pfn = ((grub_max (start, lvl->virt_start) - lvl->virt_start) >> + (PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE)) + lvl->pfn_start; + grub_dprintf ("xen", "write page table entries level %d pg %p " + "mapping %d/%d index %lx-%lx pfn %llx\n", + l, pg, m1, m2, p_s, p_e, (unsigned long long) pfn); + for (p = p_s; p <= p_e; p++) + { + pg[p] = page2offset (mfn_list[pfn]) | + get_pg_table_prot (l, pfn); + pfn++; + } + } + } + } +} + +static grub_err_t +set_mfns (grub_xen_mfn_t pfn) +{ + grub_xen_mfn_t i, t; + grub_xen_mfn_t cn_pfn = -1, st_pfn = -1; + struct mmu_update m2p_updates[4]; + + + for (i = 0; i < grub_xen_start_page_addr->nr_pages; i++) + { + if (xen_state.virt_mfn_list[i] == + grub_xen_start_page_addr->console.domU.mfn) + cn_pfn = i; + if (xen_state.virt_mfn_list[i] == grub_xen_start_page_addr->store_mfn) + st_pfn = i; + } + if (cn_pfn == (grub_xen_mfn_t)-1) + return grub_error (GRUB_ERR_BUG, "no console"); + if (st_pfn == (grub_xen_mfn_t)-1) + return grub_error (GRUB_ERR_BUG, "no store"); + t = xen_state.virt_mfn_list[pfn]; + xen_state.virt_mfn_list[pfn] = xen_state.virt_mfn_list[cn_pfn]; + xen_state.virt_mfn_list[cn_pfn] = t; + t = xen_state.virt_mfn_list[pfn + 1]; + xen_state.virt_mfn_list[pfn + 1] = xen_state.virt_mfn_list[st_pfn]; + xen_state.virt_mfn_list[st_pfn] = t; + + m2p_updates[0].ptr = + page2offset (xen_state.virt_mfn_list[pfn]) | MMU_MACHPHYS_UPDATE; + m2p_updates[0].val = pfn; + m2p_updates[1].ptr = + page2offset (xen_state.virt_mfn_list[pfn + 1]) | MMU_MACHPHYS_UPDATE; + m2p_updates[1].val = pfn + 1; + m2p_updates[2].ptr = + page2offset (xen_state.virt_mfn_list[cn_pfn]) | MMU_MACHPHYS_UPDATE; + m2p_updates[2].val = cn_pfn; + m2p_updates[3].ptr = + page2offset (xen_state.virt_mfn_list[st_pfn]) | MMU_MACHPHYS_UPDATE; + m2p_updates[3].val = st_pfn; + + grub_xen_mmu_update (m2p_updates, 4, NULL, DOMID_SELF); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_xen_p2m_alloc (void) +{ + grub_relocator_chunk_t ch; + grub_size_t p2msize, p2malloc; + grub_err_t err; + struct grub_xen_mapping *map; + + if (xen_state.virt_mfn_list) + return GRUB_ERR_NONE; + + map = xen_state.mappings + xen_state.n_mappings; + p2msize = ALIGN_UP (sizeof (grub_xen_mfn_t) * + grub_xen_start_page_addr->nr_pages, PAGE_SIZE); + if (xen_state.xen_inf.has_p2m_base) + { + err = get_pgtable_size (xen_state.xen_inf.p2m_base, + xen_state.xen_inf.p2m_base + p2msize, + (xen_state.max_addr + p2msize) >> PAGE_SHIFT); + if (err) + return err; + + map->area.pfn_start = xen_state.max_addr >> PAGE_SHIFT; + p2malloc = p2msize + page2offset (map->area.n_pt_pages); + xen_state.n_mappings++; + xen_state.next_start.mfn_list = xen_state.xen_inf.p2m_base; + xen_state.next_start.first_p2m_pfn = map->area.pfn_start; + xen_state.next_start.nr_p2m_frames = p2malloc >> PAGE_SHIFT; + } + else + { + xen_state.next_start.mfn_list = + xen_state.max_addr + xen_state.xen_inf.virt_base; + p2malloc = p2msize; + } + + xen_state.state.mfn_list = xen_state.max_addr; + err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch, + xen_state.max_addr, p2malloc); + if (err) + return err; + xen_state.virt_mfn_list = get_virtual_current_address (ch); + if (xen_state.xen_inf.has_p2m_base) + map->where = (grub_uint64_t *) xen_state.virt_mfn_list + + p2msize / sizeof (grub_uint64_t); + grub_memcpy (xen_state.virt_mfn_list, + (void *) grub_xen_start_page_addr->mfn_list, p2msize); + xen_state.max_addr += p2malloc; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_xen_special_alloc (void) +{ + grub_relocator_chunk_t ch; + grub_err_t err; + + if (xen_state.virt_start_info) + return GRUB_ERR_NONE; + + err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch, + xen_state.max_addr, + sizeof (xen_state.next_start)); + if (err) + return err; + xen_state.state.start_info = xen_state.max_addr + xen_state.xen_inf.virt_base; + xen_state.virt_start_info = get_virtual_current_address (ch); + xen_state.max_addr = + ALIGN_UP (xen_state.max_addr + sizeof (xen_state.next_start), PAGE_SIZE); + xen_state.console_pfn = xen_state.max_addr >> PAGE_SHIFT; + xen_state.max_addr += 2 * PAGE_SIZE; + + xen_state.next_start.nr_pages = grub_xen_start_page_addr->nr_pages; + grub_memcpy (xen_state.next_start.magic, grub_xen_start_page_addr->magic, + sizeof (xen_state.next_start.magic)); + xen_state.next_start.store_mfn = grub_xen_start_page_addr->store_mfn; + xen_state.next_start.store_evtchn = grub_xen_start_page_addr->store_evtchn; + xen_state.next_start.console.domU = grub_xen_start_page_addr->console.domU; + xen_state.next_start.shared_info = grub_xen_start_page_addr->shared_info; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_xen_pt_alloc (void) +{ + grub_relocator_chunk_t ch; + grub_err_t err; + grub_uint64_t nr_info_pages; + grub_uint64_t nr_need_pages; + grub_uint64_t try_virt_end; + struct grub_xen_mapping *map; + + if (xen_state.pgtbl_end) + return GRUB_ERR_NONE; + + map = xen_state.mappings + xen_state.n_mappings; + xen_state.map_reloc = map + 1; + + xen_state.next_start.pt_base = + xen_state.max_addr + xen_state.xen_inf.virt_base; + nr_info_pages = xen_state.max_addr >> PAGE_SHIFT; + nr_need_pages = nr_info_pages; + + while (1) + { + try_virt_end = ALIGN_UP (xen_state.xen_inf.virt_base + + page2offset (nr_need_pages) + + ADDITIONAL_SIZE + STACK_SIZE, ALIGN_SIZE); + + err = get_pgtable_size (xen_state.xen_inf.virt_base, try_virt_end, + nr_info_pages); + if (err) + return err; + xen_state.n_mappings++; + + /* Map the relocator page either at virtual 0 or after end of area. */ + nr_need_pages = nr_info_pages + map->area.n_pt_pages; + if (xen_state.xen_inf.virt_base) + err = get_pgtable_size (0, PAGE_SIZE, nr_need_pages); + else + err = get_pgtable_size (try_virt_end, try_virt_end + PAGE_SIZE, + nr_need_pages); + if (err) + return err; + nr_need_pages += xen_state.map_reloc->area.n_pt_pages; + + if (xen_state.xen_inf.virt_base + page2offset (nr_need_pages) <= + try_virt_end) + break; + + xen_state.n_mappings--; + } + + xen_state.n_mappings++; + nr_need_pages = map->area.n_pt_pages + xen_state.map_reloc->area.n_pt_pages; + err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch, + xen_state.max_addr, + page2offset (nr_need_pages)); + if (err) + return err; + + map->where = get_virtual_current_address (ch); + map->area.pfn_start = 0; + xen_state.max_addr += page2offset (nr_need_pages); + xen_state.state.stack = + xen_state.max_addr + STACK_SIZE + xen_state.xen_inf.virt_base; + xen_state.next_start.nr_pt_frames = nr_need_pages; + xen_state.max_addr = try_virt_end - xen_state.xen_inf.virt_base; + xen_state.pgtbl_end = xen_state.max_addr >> PAGE_SHIFT; + xen_state.map_reloc->where = (grub_uint64_t *) ((char *) map->where + + page2offset (map->area.n_pt_pages)); + + return GRUB_ERR_NONE; +} + +/* Allocate all not yet allocated areas mapped by initial page tables. */ +static grub_err_t +grub_xen_alloc_boot_data (void) +{ + grub_err_t err; + + if (!xen_state.xen_inf.has_p2m_base) + { + err = grub_xen_p2m_alloc (); + if (err) + return err; + } + err = grub_xen_special_alloc (); + if (err) + return err; + err = grub_xen_pt_alloc (); + if (err) + return err; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_xen_boot (void) +{ + grub_err_t err; + grub_uint64_t nr_pages; + struct gnttab_set_version gnttab_setver; + grub_size_t i; + + if (grub_xen_n_allocated_shared_pages) + return grub_error (GRUB_ERR_BUG, "active grants"); + + err = grub_xen_alloc_boot_data (); + if (err) + return err; + if (xen_state.xen_inf.has_p2m_base) + { + err = grub_xen_p2m_alloc (); + if (err) + return err; + } + + err = set_mfns (xen_state.console_pfn); + if (err) + return err; + + nr_pages = xen_state.max_addr >> PAGE_SHIFT; + + grub_dprintf ("xen", "bootstrap domain %llx+%llx\n", + (unsigned long long) xen_state.xen_inf.virt_base, + (unsigned long long) page2offset (nr_pages)); + + xen_state.map_reloc->area.pfn_start = nr_pages; + generate_page_table (xen_state.virt_mfn_list); + + xen_state.state.entry_point = xen_state.xen_inf.entry_point; + + *xen_state.virt_start_info = xen_state.next_start; + + grub_memset (&gnttab_setver, 0, sizeof (gnttab_setver)); + + gnttab_setver.version = 1; + grub_xen_grant_table_op (GNTTABOP_set_version, &gnttab_setver, 1); + + for (i = 0; i < ARRAY_SIZE (grub_xen_shared_info->evtchn_pending); i++) + grub_xen_shared_info->evtchn_pending[i] = 0; + + return grub_relocator_xen_boot (xen_state.relocator, xen_state.state, nr_pages, + xen_state.xen_inf.virt_base < + PAGE_SIZE ? page2offset (nr_pages) : 0, + xen_state.pgtbl_end - 1, + page2offset (xen_state.pgtbl_end - 1) + + xen_state.xen_inf.virt_base); +} + +static void +grub_xen_reset (void) +{ + grub_relocator_unload (xen_state.relocator); + + grub_memset (&xen_state, 0, sizeof (xen_state)); +} + +static grub_err_t +grub_xen_unload (void) +{ + grub_xen_reset (); + grub_dl_unref (my_mod); + return GRUB_ERR_NONE; +} + +#define HYPERCALL_INTERFACE_SIZE 32 + +#ifdef __x86_64__ +static grub_uint8_t template[] = + { + 0x51, /* push %rcx */ + 0x41, 0x53, /* push %r11 */ + 0x48, 0xc7, 0xc0, 0xbb, 0xaa, 0x00, 0x00, /* mov $0xaabb,%rax */ + 0x0f, 0x05, /* syscall */ + 0x41, 0x5b, /* pop %r11 */ + 0x59, /* pop %rcx */ + 0xc3 /* ret */ + }; + +static grub_uint8_t template_iret[] = + { + 0x51, /* push %rcx */ + 0x41, 0x53, /* push %r11 */ + 0x50, /* push %rax */ + 0x48, 0xc7, 0xc0, 0x17, 0x00, 0x00, 0x00, /* mov $0x17,%rax */ + 0x0f, 0x05 /* syscall */ + }; +#define CALLNO_OFFSET 6 +#else + +static grub_uint8_t template[] = + { + 0xb8, 0xbb, 0xaa, 0x00, 0x00, /* mov imm32, %eax */ + 0xcd, 0x82, /* int $0x82 */ + 0xc3 /* ret */ + }; + +static grub_uint8_t template_iret[] = + { + 0x50, /* push %eax */ + 0xb8, 0x17, 0x00, 0x00, 0x00, /* mov $0x17,%eax */ + 0xcd, 0x82, /* int $0x82 */ + }; +#define CALLNO_OFFSET 1 + +#endif + + +static void +set_hypercall_interface (grub_uint8_t *tgt, unsigned callno) +{ + if (callno == 0x17) + { + grub_memcpy (tgt, template_iret, ARRAY_SIZE (template_iret)); + grub_memset (tgt + ARRAY_SIZE (template_iret), 0xcc, + HYPERCALL_INTERFACE_SIZE - ARRAY_SIZE (template_iret)); + return; + } + grub_memcpy (tgt, template, ARRAY_SIZE (template)); + grub_memset (tgt + ARRAY_SIZE (template), 0xcc, + HYPERCALL_INTERFACE_SIZE - ARRAY_SIZE (template)); + tgt[CALLNO_OFFSET] = callno & 0xff; + tgt[CALLNO_OFFSET + 1] = callno >> 8; +} + +#ifdef __x86_64__ +#define grub_elfXX_load grub_elf64_load +#else +#define grub_elfXX_load grub_elf32_load +#endif + +static grub_err_t +grub_cmd_xen (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file; + grub_elf_t elf; + grub_err_t err; + void *kern_chunk_src; + grub_relocator_chunk_t ch; + grub_addr_t kern_start; + grub_addr_t kern_end; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + /* Call grub_loader_unset early to avoid it being called by grub_loader_set */ + grub_loader_unset (); + + grub_xen_reset (); + + err = grub_create_loader_cmdline (argc - 1, argv + 1, + (char *) xen_state.next_start.cmd_line, + sizeof (xen_state.next_start.cmd_line) - 1, + GRUB_VERIFY_KERNEL_CMDLINE); + if (err) + return err; + + file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL); + if (!file) + return grub_errno; + + elf = grub_xen_file (file); + if (!elf) + goto fail; + + err = grub_xen_get_info (elf, &xen_state.xen_inf); + if (err) + goto fail; +#ifdef __x86_64__ + if (xen_state.xen_inf.arch != GRUB_XEN_FILE_X86_64) +#else + if (xen_state.xen_inf.arch != GRUB_XEN_FILE_I386_PAE + && xen_state.xen_inf.arch != GRUB_XEN_FILE_I386_PAE_BIMODE) +#endif + { + grub_error (GRUB_ERR_BAD_OS, "incompatible architecture: %d", + xen_state.xen_inf.arch); + goto fail; + } + + if (xen_state.xen_inf.virt_base & (PAGE_SIZE - 1)) + { + grub_error (GRUB_ERR_BAD_OS, "unaligned virt_base"); + goto fail; + } + grub_dprintf ("xen", "virt_base = %llx, entry = %llx\n", + (unsigned long long) xen_state.xen_inf.virt_base, + (unsigned long long) xen_state.xen_inf.entry_point); + + xen_state.relocator = grub_relocator_new (); + if (!xen_state.relocator) + goto fail; + + kern_start = xen_state.xen_inf.kern_start - xen_state.xen_inf.paddr_offset; + kern_end = xen_state.xen_inf.kern_end - xen_state.xen_inf.paddr_offset; + + if (xen_state.xen_inf.has_hypercall_page) + { + grub_dprintf ("xen", "hypercall page at 0x%llx\n", + (unsigned long long) xen_state.xen_inf.hypercall_page); + kern_start = grub_min (kern_start, xen_state.xen_inf.hypercall_page - + xen_state.xen_inf.virt_base); + kern_end = grub_max (kern_end, xen_state.xen_inf.hypercall_page - + xen_state.xen_inf.virt_base + PAGE_SIZE); + } + + xen_state.max_addr = ALIGN_UP (kern_end, PAGE_SIZE); + + err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch, kern_start, + kern_end - kern_start); + if (err) + goto fail; + kern_chunk_src = get_virtual_current_address (ch); + + grub_dprintf ("xen", "paddr_offset = 0x%llx\n", + (unsigned long long) xen_state.xen_inf.paddr_offset); + grub_dprintf ("xen", "kern_start = 0x%llx, kern_end = 0x%llx\n", + (unsigned long long) xen_state.xen_inf.kern_start, + (unsigned long long) xen_state.xen_inf.kern_end); + + err = grub_elfXX_load (elf, argv[0], + (grub_uint8_t *) kern_chunk_src - kern_start + - xen_state.xen_inf.paddr_offset, 0, 0, 0); + + if (xen_state.xen_inf.has_hypercall_page) + { + unsigned i; + for (i = 0; i < PAGE_SIZE / HYPERCALL_INTERFACE_SIZE; i++) + set_hypercall_interface ((grub_uint8_t *) kern_chunk_src + + i * HYPERCALL_INTERFACE_SIZE + + xen_state.xen_inf.hypercall_page - + xen_state.xen_inf.virt_base - kern_start, i); + } + + if (err) + goto fail; + + grub_dl_ref (my_mod); + xen_state.loaded = 1; + + grub_loader_set (grub_xen_boot, grub_xen_unload, 0); + + goto fail; + +fail: + /* grub_errno might be clobbered by further calls, save the error reason. */ + err = grub_errno; + + if (elf) + grub_elf_close (elf); + else if (file) + grub_file_close (file); + + if (err != GRUB_ERR_NONE) + grub_xen_reset (); + + return err; +} + +static grub_err_t +grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_size_t size = 0; + grub_err_t err; + struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 }; + grub_relocator_chunk_t ch; + + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + goto fail; + } + + if (!xen_state.loaded) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("you need to load the kernel first")); + goto fail; + } + + if (xen_state.next_start.mod_start || xen_state.next_start.mod_len) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("initrd already loaded")); + goto fail; + } + + if (xen_state.xen_inf.unmapped_initrd) + { + err = grub_xen_alloc_boot_data (); + if (err) + goto fail; + } + + if (grub_initrd_init (argc, argv, &initrd_ctx)) + goto fail; + + size = grub_get_initrd_size (&initrd_ctx); + + if (size) + { + err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch, + xen_state.max_addr, size); + if (err) + goto fail; + + if (grub_initrd_load (&initrd_ctx, argv, + get_virtual_current_address (ch))) + goto fail; + } + + xen_state.next_start.mod_len = size; + + if (xen_state.xen_inf.unmapped_initrd) + { + xen_state.next_start.flags |= SIF_MOD_START_PFN; + xen_state.next_start.mod_start = xen_state.max_addr >> PAGE_SHIFT; + } + else + xen_state.next_start.mod_start = + xen_state.max_addr + xen_state.xen_inf.virt_base; + + grub_dprintf ("xen", "Initrd, addr=0x%x, size=0x%x\n", + (unsigned) (xen_state.max_addr + xen_state.xen_inf.virt_base), + (unsigned) size); + + xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, PAGE_SIZE); + +fail: + grub_initrd_close (&initrd_ctx); + + return grub_errno; +} + +static grub_err_t +grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_size_t size = 0; + grub_err_t err; + grub_relocator_chunk_t ch; + grub_size_t cmdline_len; + int nounzip = 0; + grub_file_t file; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + if (grub_strcmp (argv[0], "--nounzip") == 0) + { + argv++; + argc--; + nounzip = 1; + } + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + if (!xen_state.loaded) + { + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("you need to load the kernel first")); + } + + if ((xen_state.next_start.mod_start || xen_state.next_start.mod_len) && + !xen_state.module_info_page) + { + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("initrd already loaded")); + } + + /* Leave one space for terminator. */ + if (xen_state.n_modules >= MAX_MODULES - 1) + { + return grub_error (GRUB_ERR_BAD_ARGUMENT, "too many modules"); + } + + if (!xen_state.module_info_page) + { + xen_state.xen_inf.unmapped_initrd = 0; + xen_state.n_modules = 0; + xen_state.max_addr = ALIGN_UP (xen_state.max_addr, PAGE_SIZE); + xen_state.modules_target_start = xen_state.max_addr; + xen_state.next_start.mod_start = + xen_state.max_addr + xen_state.xen_inf.virt_base; + xen_state.next_start.flags |= SIF_MULTIBOOT_MOD; + + err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch, + xen_state.max_addr, MAX_MODULES + * + sizeof (xen_state.module_info_page + [0])); + if (err) + return err; + xen_state.module_info_page = get_virtual_current_address (ch); + grub_memset (xen_state.module_info_page, 0, MAX_MODULES + * sizeof (xen_state.module_info_page[0])); + xen_state.max_addr += + MAX_MODULES * sizeof (xen_state.module_info_page[0]); + } + + xen_state.max_addr = ALIGN_UP (xen_state.max_addr, PAGE_SIZE); + + file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_INITRD | + (nounzip ? GRUB_FILE_TYPE_NO_DECOMPRESS : GRUB_FILE_TYPE_NONE)); + if (!file) + return grub_errno; + size = grub_file_size (file); + + cmdline_len = grub_loader_cmdline_size (argc - 1, argv + 1); + + err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch, + xen_state.max_addr, cmdline_len); + if (err) + goto fail; + + err = grub_create_loader_cmdline (argc - 1, argv + 1, + get_virtual_current_address (ch), cmdline_len, + GRUB_VERIFY_MODULE_CMDLINE); + if (err) + goto fail; + + xen_state.module_info_page[xen_state.n_modules].cmdline = + xen_state.max_addr - xen_state.modules_target_start; + xen_state.max_addr = ALIGN_UP (xen_state.max_addr + cmdline_len, PAGE_SIZE); + + if (size) + { + err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch, + xen_state.max_addr, size); + if (err) + goto fail; + if (grub_file_read (file, get_virtual_current_address (ch), size) + != (grub_ssize_t) size) + { + if (!grub_errno) + grub_error (GRUB_ERR_FILE_READ_ERROR, + N_("premature end of file %s"), argv[0]); + goto fail; + } + } + xen_state.next_start.mod_len = + xen_state.max_addr + size - xen_state.modules_target_start; + xen_state.module_info_page[xen_state.n_modules].mod_start = + xen_state.max_addr - xen_state.modules_target_start; + xen_state.module_info_page[xen_state.n_modules].mod_end = + xen_state.max_addr + size - xen_state.modules_target_start; + + xen_state.n_modules++; + grub_dprintf ("xen", "module, addr=0x%x, size=0x%x\n", + (unsigned) xen_state.max_addr, (unsigned) size); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, PAGE_SIZE); + + +fail: + grub_file_close (file); + + return grub_errno; +} + +static grub_command_t cmd_xen, cmd_initrd, cmd_module, cmd_multiboot; + +GRUB_MOD_INIT (xen) +{ + cmd_xen = grub_register_command ("linux", grub_cmd_xen, + 0, N_("Load Linux.")); + cmd_multiboot = grub_register_command ("multiboot", grub_cmd_xen, + 0, N_("Load Linux.")); + cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd, + 0, N_("Load initrd.")); + cmd_module = grub_register_command ("module", grub_cmd_module, + 0, N_("Load module.")); + my_mod = mod; +} + +GRUB_MOD_FINI (xen) +{ + grub_unregister_command (cmd_xen); + grub_unregister_command (cmd_initrd); + grub_unregister_command (cmd_multiboot); + grub_unregister_command (cmd_module); +} diff --git a/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/ia64/efi/linux.c b/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/ia64/efi/linux.c new file mode 100644 index 00000000..7987fd1b --- /dev/null +++ b/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/ia64/efi/linux.c @@ -0,0 +1,607 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#pragma GCC diagnostic ignored "-Wcast-align" + +#define ALIGN_MIN (256*1024*1024) + +#define GRUB_ELF_SEARCH 1024 + +#define BOOT_PARAM_SIZE 16384 + +struct ia64_boot_param +{ + grub_uint64_t command_line; /* physical address of command line. */ + grub_uint64_t efi_systab; /* physical address of EFI system table */ + grub_uint64_t efi_memmap; /* physical address of EFI memory map */ + grub_uint64_t efi_memmap_size; /* size of EFI memory map */ + grub_uint64_t efi_memdesc_size; /* size of an EFI memory map descriptor */ + grub_uint32_t efi_memdesc_version; /* memory descriptor version */ + struct + { + grub_uint16_t num_cols; /* number of columns on console output dev */ + grub_uint16_t num_rows; /* number of rows on console output device */ + grub_uint16_t orig_x; /* cursor's x position */ + grub_uint16_t orig_y; /* cursor's y position */ + } console_info; + grub_uint64_t fpswa; /* physical address of the fpswa interface */ + grub_uint64_t initrd_start; + grub_uint64_t initrd_size; +}; + +typedef struct +{ + grub_uint32_t revision; + grub_uint32_t reserved; + void *fpswa; +} fpswa_interface_t; +static fpswa_interface_t *fpswa; + +#define NEXT_MEMORY_DESCRIPTOR(desc, size) \ + ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size))) + +static grub_dl_t my_mod; + +static int loaded; + +/* Kernel base and size. */ +static void *kernel_mem; +static grub_efi_uintn_t kernel_pages; +static grub_uint64_t entry; + +/* Initrd base and size. */ +static void *initrd_mem; +static grub_efi_uintn_t initrd_pages; +static grub_efi_uintn_t initrd_size; + +static struct ia64_boot_param *boot_param; +static grub_efi_uintn_t boot_param_pages; + +static inline grub_size_t +page_align (grub_size_t size) +{ + return (size + (1 << 12) - 1) & (~((1 << 12) - 1)); +} + +static void +query_fpswa (void) +{ + grub_efi_handle_t fpswa_image; + grub_efi_boot_services_t *bs; + grub_efi_status_t status; + grub_efi_uintn_t size; + static const grub_efi_guid_t fpswa_protocol = + { 0xc41b6531, 0x97b9, 0x11d3, + {0x9a, 0x29, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d} }; + + if (fpswa != NULL) + return; + + size = sizeof(grub_efi_handle_t); + + bs = grub_efi_system_table->boot_services; + status = bs->locate_handle (GRUB_EFI_BY_PROTOCOL, + (void *) &fpswa_protocol, + NULL, &size, &fpswa_image); + if (status != GRUB_EFI_SUCCESS) + { + grub_printf ("%s\n", _("Could not locate FPSWA driver")); + return; + } + status = bs->handle_protocol (fpswa_image, + (void *) &fpswa_protocol, (void *) &fpswa); + if (status != GRUB_EFI_SUCCESS) + { + grub_printf ("%s\n", + _("FPSWA protocol wasn't able to find the interface")); + return; + } +} + +static void +free_pages (void) +{ + if (kernel_mem) + { + grub_efi_free_pages ((grub_addr_t) kernel_mem, kernel_pages); + kernel_mem = 0; + } + + if (initrd_mem) + { + grub_efi_free_pages ((grub_addr_t) initrd_mem, initrd_pages); + initrd_mem = 0; + } + + if (boot_param) + { + /* Free bootparam. */ + grub_efi_free_pages ((grub_efi_physical_address_t) boot_param, + boot_param_pages); + boot_param = 0; + } +} + +static void * +allocate_pages (grub_uint64_t align, grub_uint64_t size_pages, + grub_uint64_t nobase) +{ + grub_uint64_t size; + grub_efi_uintn_t desc_size; + grub_efi_memory_descriptor_t *mmap, *mmap_end; + grub_efi_uintn_t mmap_size, tmp_mmap_size; + grub_efi_memory_descriptor_t *desc; + void *mem = NULL; + + size = size_pages << 12; + + mmap_size = grub_efi_find_mmap_size (); + if (!mmap_size) + return 0; + + /* Read the memory map temporarily, to find free space. */ + mmap = grub_malloc (mmap_size); + if (! mmap) + return 0; + + tmp_mmap_size = mmap_size; + if (grub_efi_get_memory_map (&tmp_mmap_size, mmap, 0, &desc_size, 0) <= 0) + { + grub_error (GRUB_ERR_IO, "cannot get memory map"); + goto fail; + } + + mmap_end = NEXT_MEMORY_DESCRIPTOR (mmap, tmp_mmap_size); + + /* First, find free pages for the real mode code + and the memory map buffer. */ + for (desc = mmap; + desc < mmap_end; + desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size)) + { + grub_uint64_t start, end; + grub_uint64_t aligned_start; + + if (desc->type != GRUB_EFI_CONVENTIONAL_MEMORY) + continue; + + start = desc->physical_start; + end = start + (desc->num_pages << 12); + /* Align is a power of 2. */ + aligned_start = (start + align - 1) & ~(align - 1); + if (aligned_start + size > end) + continue; + if (aligned_start == nobase) + aligned_start += align; + if (aligned_start + size > end) + continue; + mem = grub_efi_allocate_fixed (aligned_start, size_pages); + if (! mem) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory"); + goto fail; + } + break; + } + + if (! mem) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory"); + goto fail; + } + + grub_free (mmap); + return mem; + + fail: + grub_free (mmap); + free_pages (); + return 0; +} + +static void +set_boot_param_console (void) +{ + grub_efi_simple_text_output_interface_t *conout; + grub_efi_uintn_t cols, rows; + + conout = grub_efi_system_table->con_out; + if (conout->query_mode (conout, conout->mode->mode, &cols, &rows) + != GRUB_EFI_SUCCESS) + return; + + grub_dprintf ("linux", + "Console info: cols=%lu rows=%lu x=%u y=%u\n", + cols, rows, + conout->mode->cursor_column, conout->mode->cursor_row); + + boot_param->console_info.num_cols = cols; + boot_param->console_info.num_rows = rows; + boot_param->console_info.orig_x = conout->mode->cursor_column; + boot_param->console_info.orig_y = conout->mode->cursor_row; +} + +static grub_err_t +grub_linux_boot (void) +{ + grub_efi_uintn_t mmap_size; + grub_efi_uintn_t map_key; + grub_efi_uintn_t desc_size; + grub_efi_uint32_t desc_version; + grub_efi_memory_descriptor_t *mmap_buf; + grub_err_t err; + + /* FPSWA. */ + query_fpswa (); + boot_param->fpswa = (grub_uint64_t)fpswa; + + /* Initrd. */ + boot_param->initrd_start = (grub_uint64_t)initrd_mem; + boot_param->initrd_size = (grub_uint64_t)initrd_size; + + set_boot_param_console (); + + grub_dprintf ("linux", "Jump to %016lx\n", entry); + + /* MDT. + Must be done after grub_machine_fini because map_key is used by + exit_boot_services. */ + mmap_size = grub_efi_find_mmap_size (); + if (! mmap_size) + return grub_errno; + mmap_buf = grub_efi_allocate_any_pages (page_align (mmap_size) >> 12); + if (! mmap_buf) + return grub_error (GRUB_ERR_IO, "cannot allocate memory map"); + err = grub_efi_finish_boot_services (&mmap_size, mmap_buf, &map_key, + &desc_size, &desc_version); + if (err) + return err; + + boot_param->efi_memmap = (grub_uint64_t)mmap_buf; + boot_param->efi_memmap_size = mmap_size; + boot_param->efi_memdesc_size = desc_size; + boot_param->efi_memdesc_version = desc_version; + + /* See you next boot. */ + asm volatile ("mov r28=%1; br.sptk.few %0" :: "b"(entry),"r"(boot_param)); + + /* Never reach here. */ + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_linux_unload (void) +{ + free_pages (); + grub_dl_unref (my_mod); + loaded = 0; + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_load_elf64 (grub_file_t file, void *buffer, const char *filename) +{ + Elf64_Ehdr *ehdr = (Elf64_Ehdr *) buffer; + Elf64_Phdr *phdr; + int i; + grub_uint64_t low_addr; + grub_uint64_t high_addr; + grub_uint64_t align; + grub_uint64_t reloc_offset; + const char *relocate; + + if (ehdr->e_ident[EI_MAG0] != ELFMAG0 + || ehdr->e_ident[EI_MAG1] != ELFMAG1 + || ehdr->e_ident[EI_MAG2] != ELFMAG2 + || ehdr->e_ident[EI_MAG3] != ELFMAG3 + || ehdr->e_ident[EI_DATA] != ELFDATA2LSB) + return grub_error(GRUB_ERR_UNKNOWN_OS, + N_("invalid arch-independent ELF magic")); + + if (ehdr->e_ident[EI_CLASS] != ELFCLASS64 + || ehdr->e_version != EV_CURRENT + || ehdr->e_machine != EM_IA_64) + return grub_error (GRUB_ERR_UNKNOWN_OS, + N_("invalid arch-dependent ELF magic")); + + if (ehdr->e_type != ET_EXEC) + return grub_error (GRUB_ERR_UNKNOWN_OS, + N_("this ELF file is not of the right type")); + + /* FIXME: Should we support program headers at strange locations? */ + if (ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize > GRUB_ELF_SEARCH) + return grub_error (GRUB_ERR_BAD_OS, "program header at a too high offset"); + + entry = ehdr->e_entry; + + /* Compute low, high and align addresses. */ + low_addr = ~0UL; + high_addr = 0; + align = 0; + for (i = 0; i < ehdr->e_phnum; i++) + { + phdr = (Elf64_Phdr *) ((char *) buffer + ehdr->e_phoff + + i * ehdr->e_phentsize); + if (phdr->p_type == PT_LOAD) + { + if (phdr->p_paddr < low_addr) + low_addr = phdr->p_paddr; + if (phdr->p_paddr + phdr->p_memsz > high_addr) + high_addr = phdr->p_paddr + phdr->p_memsz; + if (phdr->p_align > align) + align = phdr->p_align; + } + } + + if (align < ALIGN_MIN) + align = ALIGN_MIN; + + if (high_addr == 0) + return grub_error (GRUB_ERR_BAD_OS, "no program entries"); + + kernel_pages = page_align (high_addr - low_addr) >> 12; + + /* Undocumented on purpose. */ + relocate = grub_env_get ("linux_relocate"); + if (!relocate || grub_strcmp (relocate, "force") != 0) + { + kernel_mem = grub_efi_allocate_fixed (low_addr, kernel_pages); + reloc_offset = 0; + } + /* Try to relocate. */ + if (! kernel_mem && (!relocate || grub_strcmp (relocate, "off") != 0)) + { + kernel_mem = allocate_pages (align, kernel_pages, low_addr); + if (kernel_mem) + { + reloc_offset = (grub_uint64_t)kernel_mem - low_addr; + grub_dprintf ("linux", " Relocated at %p (offset=%016lx)\n", + kernel_mem, reloc_offset); + entry += reloc_offset; + } + } + if (! kernel_mem) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "cannot allocate memory for OS"); + + /* Load every loadable segment in memory. */ + for (i = 0; i < ehdr->e_phnum; i++) + { + phdr = (Elf64_Phdr *) ((char *) buffer + ehdr->e_phoff + + i * ehdr->e_phentsize); + if (phdr->p_type == PT_LOAD) + { + grub_dprintf ("linux", " [paddr=%lx load=%lx memsz=%08lx " + "off=%lx flags=%x]\n", + phdr->p_paddr, phdr->p_paddr + reloc_offset, + phdr->p_memsz, phdr->p_offset, phdr->p_flags); + + if (grub_file_seek (file, phdr->p_offset) == (grub_off_t)-1) + return grub_errno; + + if (grub_file_read (file, (void *) (phdr->p_paddr + reloc_offset), + phdr->p_filesz) + != (grub_ssize_t) phdr->p_filesz) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + filename); + return grub_errno; + } + + if (phdr->p_filesz < phdr->p_memsz) + grub_memset + ((char *)(phdr->p_paddr + reloc_offset + phdr->p_filesz), + 0, phdr->p_memsz - phdr->p_filesz); + + /* Sync caches if necessary. */ + if (phdr->p_flags & PF_X) + grub_arch_sync_caches + ((void *)(phdr->p_paddr + reloc_offset), phdr->p_memsz); + } + } + loaded = 1; + return 0; +} + +static grub_err_t +grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file = 0; + char buffer[GRUB_ELF_SEARCH]; + char *cmdline, *p; + grub_ssize_t len; + int i; + + grub_dl_ref (my_mod); + + grub_loader_unset (); + + 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; + + len = grub_file_read (file, buffer, sizeof (buffer)); + if (len < (grub_ssize_t) sizeof (Elf64_Ehdr)) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + argv[0]); + goto fail; + } + + grub_dprintf ("linux", "Loading linux: %s\n", argv[0]); + + if (grub_load_elf64 (file, buffer, argv[0])) + goto fail; + + len = sizeof("BOOT_IMAGE=") + 8; + for (i = 0; i < argc; i++) + len += grub_strlen (argv[i]) + 1; + len += sizeof (struct ia64_boot_param) + 512; /* Room for extensions. */ + boot_param_pages = page_align (len) >> 12; + boot_param = grub_efi_allocate_any_pages (boot_param_pages); + if (boot_param == 0) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, + "cannot allocate memory for bootparams"); + goto fail; + } + + grub_memset (boot_param, 0, len); + cmdline = ((char *)(boot_param + 1)) + 256; + + /* Build cmdline. */ + p = grub_stpcpy (cmdline, "BOOT_IMAGE"); + for (i = 0; i < argc; i++) + { + *p++ = ' '; + p = grub_stpcpy (p, argv[i]); + } + cmdline[10] = '='; + + *p = '\0'; + + if (grub_verify_string (cmdline, GRUB_VERIFY_KERNEL_CMDLINE)) + goto fail; + + boot_param->command_line = (grub_uint64_t) cmdline; + boot_param->efi_systab = (grub_uint64_t) grub_efi_system_table; + + grub_errno = GRUB_ERR_NONE; + + grub_loader_set (grub_linux_boot, grub_linux_unload, 0); + + fail: + if (file) + grub_file_close (file); + + if (grub_errno != GRUB_ERR_NONE) + { + grub_efi_free_pages ((grub_efi_physical_address_t) boot_param, + boot_param_pages); + grub_dl_unref (my_mod); + } + return grub_errno; +} + +static grub_err_t +grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + 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; + } + + if (grub_initrd_init (argc, argv, &initrd_ctx)) + goto fail; + + initrd_size = grub_get_initrd_size (&initrd_ctx); + grub_dprintf ("linux", "Loading initrd\n"); + + initrd_pages = (page_align (initrd_size) >> 12); + initrd_mem = grub_efi_allocate_any_pages (initrd_pages); + if (! initrd_mem) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate pages"); + goto fail; + } + + grub_dprintf ("linux", "[addr=0x%lx, size=0x%lx]\n", + (grub_uint64_t) initrd_mem, initrd_size); + + if (grub_initrd_load (&initrd_ctx, argv, initrd_mem)) + goto fail; + fail: + grub_initrd_close (&initrd_ctx); + return grub_errno; +} + +static grub_err_t +grub_cmd_fpswa (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__((unused)), + char *argv[] __attribute__((unused))) +{ + query_fpswa (); + if (fpswa == NULL) + grub_puts_ (N_("No FPSWA found")); + else + grub_printf (_("FPSWA revision: %x\n"), fpswa->revision); + return GRUB_ERR_NONE; +} + +static grub_command_t cmd_linux, cmd_initrd, cmd_fpswa; + +GRUB_MOD_INIT(linux) +{ + cmd_linux = grub_register_command ("linux", grub_cmd_linux, + N_("FILE [ARGS...]"), N_("Load Linux.")); + + cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd, + N_("FILE"), N_("Load initrd.")); + + cmd_fpswa = grub_register_command ("fpswa", grub_cmd_fpswa, + "", N_("Display FPSWA version.")); + + my_mod = mod; +} + +GRUB_MOD_FINI(linux) +{ + grub_unregister_command (cmd_linux); + grub_unregister_command (cmd_initrd); + grub_unregister_command (cmd_fpswa); +} diff --git a/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/mips/linux.c b/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/mips/linux.c new file mode 100644 index 00000000..7b723bf1 --- /dev/null +++ b/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/mips/linux.c @@ -0,0 +1,511 @@ +/* linux.c - boot Linux */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2004,2005,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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#pragma GCC diagnostic ignored "-Wcast-align" + +/* For frequencies. */ +#include + +#ifdef GRUB_MACHINE_MIPS_LOONGSON +#include +#include + +const char loongson_machtypes[][60] = + { + [GRUB_ARCH_MACHINE_YEELOONG] = "machtype=lemote-yeeloong-2f-8.9inches", + [GRUB_ARCH_MACHINE_FULOONG2F] = "machtype=lemote-fuloong-2f-box", + [GRUB_ARCH_MACHINE_FULOONG2E] = "machtype=lemote-fuloong-2e-unknown" + }; +#endif + +static grub_dl_t my_mod; + +static int loaded; + +static grub_size_t linux_size; + +static struct grub_relocator *relocator; +static grub_uint8_t *playground; +static grub_addr_t target_addr, entry_addr; +#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS +static char *params; +#else +static int linux_argc; +static grub_off_t argv_off; +#ifdef GRUB_MACHINE_MIPS_LOONGSON +static grub_off_t envp_off; +#endif +static grub_off_t rd_addr_arg_off, rd_size_arg_off; +#endif +static int initrd_loaded = 0; + +static grub_err_t +grub_linux_boot (void) +{ + struct grub_relocator32_state state; + + grub_memset (&state, 0, sizeof (state)); + + /* Boot the kernel. */ + state.gpr[1] = entry_addr; + +#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS + { + grub_err_t err; + grub_relocator_chunk_t ch; + grub_uint32_t *memsize; + grub_uint32_t *magic; + char *str; + + err = grub_relocator_alloc_chunk_addr (relocator, &ch, + ((16 << 20) - 264), + grub_strlen (params) + 1 + 8); + if (err) + return err; + memsize = get_virtual_current_address (ch); + magic = memsize + 1; + *memsize = grub_mmap_get_lower (); + *magic = 0x12345678; + str = (char *) (magic + 1); + grub_strcpy (str, params); + } +#endif + +#ifndef GRUB_MACHINE_MIPS_QEMU_MIPS + state.gpr[4] = linux_argc; + state.gpr[5] = target_addr + argv_off; +#ifdef GRUB_MACHINE_MIPS_LOONGSON + state.gpr[6] = target_addr + envp_off; +#else + state.gpr[6] = 0; +#endif + state.gpr[7] = 0; +#endif + state.jumpreg = 1; + grub_relocator32_boot (relocator, state); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_linux_unload (void) +{ + grub_relocator_unload (relocator); + grub_dl_unref (my_mod); + +#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS + grub_free (params); + params = 0; +#endif + + loaded = 0; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_linux_load32 (grub_elf_t elf, const char *filename, + void **extra_mem, grub_size_t extra_size) +{ + Elf32_Addr base; + int extraoff; + grub_err_t err; + + /* Linux's entry point incorrectly contains a virtual address. */ + entry_addr = elf->ehdr.ehdr32.e_entry; + + linux_size = grub_elf32_size (elf, &base, 0); + if (linux_size == 0) + return grub_errno; + target_addr = base; + /* Pad it; the kernel scribbles over memory beyond its load address. */ + linux_size += 0x100000; + linux_size = ALIGN_UP (base + linux_size, 4) - base; + extraoff = linux_size; + linux_size += extra_size; + + relocator = grub_relocator_new (); + if (!relocator) + return grub_errno; + + { + grub_relocator_chunk_t ch; + err = grub_relocator_alloc_chunk_addr (relocator, &ch, + target_addr & 0x1fffffff, + linux_size); + if (err) + return err; + playground = get_virtual_current_address (ch); + } + + *extra_mem = playground + extraoff; + + /* Now load the segments into the area we claimed. */ + return grub_elf32_load (elf, filename, playground - base, GRUB_ELF_LOAD_FLAGS_NONE, 0, 0); +} + +static grub_err_t +grub_linux_load64 (grub_elf_t elf, const char *filename, + void **extra_mem, grub_size_t extra_size) +{ + Elf64_Addr base; + int extraoff; + grub_err_t err; + + /* Linux's entry point incorrectly contains a virtual address. */ + entry_addr = elf->ehdr.ehdr64.e_entry; + + linux_size = grub_elf64_size (elf, &base, 0); + if (linux_size == 0) + return grub_errno; + target_addr = base; + /* Pad it; the kernel scribbles over memory beyond its load address. */ + linux_size += 0x100000; + linux_size = ALIGN_UP (base + linux_size, 4) - base; + extraoff = linux_size; + linux_size += extra_size; + + relocator = grub_relocator_new (); + if (!relocator) + return grub_errno; + + { + grub_relocator_chunk_t ch; + err = grub_relocator_alloc_chunk_addr (relocator, &ch, + target_addr & 0x1fffffff, + linux_size); + if (err) + return err; + playground = get_virtual_current_address (ch); + } + + *extra_mem = playground + extraoff; + + /* Now load the segments into the area we claimed. */ + return grub_elf64_load (elf, filename, playground - base, GRUB_ELF_LOAD_FLAGS_NONE, 0, 0); +} + +static grub_err_t +grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_elf_t elf = 0; + int size; + void *extra = NULL; +#ifndef GRUB_MACHINE_MIPS_QEMU_MIPS + int i; + grub_uint32_t *linux_argv; + char *linux_args; +#endif + grub_err_t err; +#ifdef GRUB_MACHINE_MIPS_LOONGSON + char *linux_envs; + grub_uint32_t *linux_envp; +#endif + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + elf = grub_elf_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL); + if (! elf) + return grub_errno; + + if (elf->ehdr.ehdr32.e_type != ET_EXEC) + { + grub_elf_close (elf); + return grub_error (GRUB_ERR_UNKNOWN_OS, + N_("this ELF file is not of the right type")); + } + + /* Release the previously used memory. */ + grub_loader_unset (); + loaded = 0; + +#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS + size = 0; +#else + /* For arguments. */ + linux_argc = argc; +#ifdef GRUB_MACHINE_MIPS_LOONGSON + linux_argc++; +#endif + /* Main arguments. */ + size = (linux_argc) * sizeof (grub_uint32_t); + /* Initrd address and size. */ + size += 2 * sizeof (grub_uint32_t); + /* NULL terminator. */ + size += sizeof (grub_uint32_t); + + /* First argument is always "a0". */ + size += ALIGN_UP (sizeof ("a0"), 4); + /* Normal arguments. */ + for (i = 1; i < argc; i++) + size += ALIGN_UP (grub_strlen (argv[i]) + 1, 4); +#ifdef GRUB_MACHINE_MIPS_LOONGSON + size += ALIGN_UP (sizeof (loongson_machtypes[0]), 4); +#endif + + /* rd arguments. */ + size += ALIGN_UP (sizeof ("rd_start=0xXXXXXXXXXXXXXXXX"), 4); + size += ALIGN_UP (sizeof ("rd_size=0xXXXXXXXXXXXXXXXX"), 4); + + /* For the environment. */ + size += sizeof (grub_uint32_t); + size += 4 * sizeof (grub_uint32_t); + size += ALIGN_UP (sizeof ("memsize=XXXXXXXXXXXXXXXXXXXX"), 4) + + ALIGN_UP (sizeof ("highmemsize=XXXXXXXXXXXXXXXXXXXX"), 4) + + ALIGN_UP (sizeof ("busclock=XXXXXXXXXX"), 4) + + ALIGN_UP (sizeof ("cpuclock=XXXXXXXXXX"), 4); +#endif + + if (grub_elf_is_elf32 (elf)) + err = grub_linux_load32 (elf, argv[0], &extra, size); + else + if (grub_elf_is_elf64 (elf)) + err = grub_linux_load64 (elf, argv[0], &extra, size); + else + err = grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic")); + + grub_elf_close (elf); + + if (err) + return err; + +#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS + /* Create kernel command line. */ + size = grub_loader_cmdline_size(argc, argv); + params = grub_malloc (size + sizeof (LINUX_IMAGE)); + if (! params) + { + grub_linux_unload (); + return grub_errno; + } + + grub_memcpy (params, LINUX_IMAGE, sizeof (LINUX_IMAGE)); + grub_create_loader_cmdline (argc, argv, params + sizeof (LINUX_IMAGE) - 1, + size, GRUB_VERIFY_KERNEL_CMDLINE); +#else + linux_argv = extra; + argv_off = (grub_uint8_t *) linux_argv - (grub_uint8_t *) playground; + extra = linux_argv + (linux_argc + 1 + 2); + linux_args = extra; + + grub_memcpy (linux_args, "a0", sizeof ("a0")); + *linux_argv = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground + + target_addr; + linux_argv++; + linux_args += ALIGN_UP (sizeof ("a0"), 4); + + char *params = linux_args; + +#ifdef GRUB_MACHINE_MIPS_LOONGSON + { + unsigned mtype = grub_arch_machine; + if (mtype >= ARRAY_SIZE (loongson_machtypes)) + mtype = 0; + /* In Loongson platform, it is the responsibility of the bootloader/firmware + to supply the OS kernel with machine type information. */ + grub_memcpy (linux_args, loongson_machtypes[mtype], + sizeof (loongson_machtypes[mtype])); + *linux_argv = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground + + target_addr; + linux_argv++; + linux_args += ALIGN_UP (sizeof (loongson_machtypes[mtype]), 4); + } +#endif + + for (i = 1; i < argc; i++) + { + grub_memcpy (linux_args, argv[i], grub_strlen (argv[i]) + 1); + *linux_argv = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground + + target_addr; + linux_argv++; + linux_args += ALIGN_UP (grub_strlen (argv[i]) + 1, 4); + } + + *linux_args = '\0'; + + err = grub_verify_string (params, GRUB_VERIFY_KERNEL_CMDLINE); + if (err) + return err; + + /* Reserve space for rd arguments. */ + rd_addr_arg_off = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground; + linux_args += ALIGN_UP (sizeof ("rd_start=0xXXXXXXXXXXXXXXXX"), 4); + *linux_argv = 0; + linux_argv++; + + rd_size_arg_off = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground; + linux_args += ALIGN_UP (sizeof ("rd_size=0xXXXXXXXXXXXXXXXX"), 4); + *linux_argv = 0; + linux_argv++; + + *linux_argv = 0; + + extra = linux_args; + +#ifdef GRUB_MACHINE_MIPS_LOONGSON + linux_envp = extra; + envp_off = (grub_uint8_t *) linux_envp - (grub_uint8_t *) playground; + linux_envs = (char *) (linux_envp + 5); + grub_snprintf (linux_envs, sizeof ("memsize=XXXXXXXXXXXXXXXXXXXX"), + "memsize=%lld", + (unsigned long long) grub_mmap_get_lower () >> 20); + linux_envp[0] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground + + target_addr; + linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4); + grub_snprintf (linux_envs, sizeof ("highmemsize=XXXXXXXXXXXXXXXXXXXX"), + "highmemsize=%lld", + (unsigned long long) grub_mmap_get_upper () >> 20); + linux_envp[1] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground + + target_addr; + linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4); + + grub_snprintf (linux_envs, sizeof ("busclock=XXXXXXXXXX"), + "busclock=%d", grub_arch_busclock); + linux_envp[2] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground + + target_addr; + linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4); + grub_snprintf (linux_envs, sizeof ("cpuclock=XXXXXXXXXX"), + "cpuclock=%d", grub_arch_cpuclock); + linux_envp[3] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground + + target_addr; + linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4); + + linux_envp[4] = 0; +#endif +#endif + + grub_loader_set (grub_linux_boot, grub_linux_unload, 1); + initrd_loaded = 0; + loaded = 1; + grub_dl_ref (my_mod); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_size_t size = 0; + void *initrd_src; + grub_addr_t initrd_dest; + grub_err_t err; + struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 }; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + if (!loaded) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first")); + + if (initrd_loaded) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "only one initrd command can be issued."); + + 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 (relocator, &ch, + (target_addr & 0x1fffffff) + + linux_size + 0x10000, + (0x10000000 - size), + size, 0x10000, + GRUB_RELOCATOR_PREFERENCE_NONE, 0); + + if (err) + goto fail; + initrd_src = get_virtual_current_address (ch); + initrd_dest = get_physical_target_address (ch) | 0x80000000; + } + + if (grub_initrd_load (&initrd_ctx, argv, initrd_src)) + goto fail; + +#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS + { + char *tmp; + tmp = grub_xasprintf ("%s rd_start=0x%" PRIxGRUB_ADDR + " rd_size=0x%" PRIxGRUB_ADDR, params, + initrd_dest, size); + if (!tmp) + goto fail; + grub_free (params); + params = tmp; + } +#else + grub_snprintf ((char *) playground + rd_addr_arg_off, + sizeof ("rd_start=0xXXXXXXXXXXXXXXXX"), "rd_start=0x%llx", + (unsigned long long) initrd_dest); + ((grub_uint32_t *) (playground + argv_off))[linux_argc] + = target_addr + rd_addr_arg_off; + linux_argc++; + + grub_snprintf ((char *) playground + rd_size_arg_off, + sizeof ("rd_size=0xXXXXXXXXXXXXXXXXX"), "rd_size=0x%llx", + (unsigned long long) size); + ((grub_uint32_t *) (playground + argv_off))[linux_argc] + = target_addr + rd_size_arg_off; + linux_argc++; +#endif + + initrd_loaded = 1; + + fail: + grub_initrd_close (&initrd_ctx); + + return grub_errno; +} + +static grub_command_t cmd_linux, cmd_initrd; + +GRUB_MOD_INIT(linux) +{ + cmd_linux = grub_register_command ("linux", grub_cmd_linux, + 0, N_("Load Linux.")); + cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd, + 0, N_("Load initrd.")); + my_mod = mod; +} + +GRUB_MOD_FINI(linux) +{ + grub_unregister_command (cmd_linux); + grub_unregister_command (cmd_initrd); +} diff --git a/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/powerpc/ieee1275/linux.c b/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/powerpc/ieee1275/linux.c new file mode 100644 index 00000000..818b2a86 --- /dev/null +++ b/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/powerpc/ieee1275/linux.c @@ -0,0 +1,393 @@ +/* linux.c - boot Linux */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2004,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 +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define ELF32_LOADMASK (0xc0000000UL) +#define ELF64_LOADMASK (0xc000000000000000ULL) + +static grub_dl_t my_mod; + +static int loaded; + +static grub_addr_t initrd_addr; +static grub_size_t initrd_size; + +static grub_addr_t linux_addr; +static grub_addr_t linux_entry; +static grub_size_t linux_size; + +static char *linux_args; + +typedef void (*kernel_entry_t) (void *, unsigned long, int (void *), + unsigned long, unsigned long); + +/* Context for grub_linux_claimmap_iterate. */ +struct grub_linux_claimmap_iterate_ctx +{ + grub_addr_t target; + grub_size_t size; + grub_size_t align; + grub_addr_t found_addr; +}; + +/* Helper for grub_linux_claimmap_iterate. */ +static int +alloc_mem (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, + void *data) +{ + struct grub_linux_claimmap_iterate_ctx *ctx = data; + + grub_uint64_t end = addr + len; + addr = ALIGN_UP (addr, ctx->align); + ctx->target = ALIGN_UP (ctx->target, ctx->align); + + /* Target above the memory chunk. */ + if (type != GRUB_MEMORY_AVAILABLE || ctx->target > end) + return 0; + + /* Target inside the memory chunk. */ + if (ctx->target >= addr && ctx->target < end && + ctx->size <= end - ctx->target) + { + if (grub_claimmap (ctx->target, ctx->size) == GRUB_ERR_NONE) + { + ctx->found_addr = ctx->target; + return 1; + } + grub_print_error (); + } + /* Target below the memory chunk. */ + if (ctx->target < addr && addr + ctx->size <= end) + { + if (grub_claimmap (addr, ctx->size) == GRUB_ERR_NONE) + { + ctx->found_addr = addr; + return 1; + } + grub_print_error (); + } + return 0; +} + +static grub_addr_t +grub_linux_claimmap_iterate (grub_addr_t target, grub_size_t size, + grub_size_t align) +{ + struct grub_linux_claimmap_iterate_ctx ctx = { + .target = target, + .size = size, + .align = align, + .found_addr = (grub_addr_t) -1 + }; + + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM)) + { + grub_uint64_t addr = target; + if (addr < GRUB_IEEE1275_STATIC_HEAP_START + + GRUB_IEEE1275_STATIC_HEAP_LEN) + addr = GRUB_IEEE1275_STATIC_HEAP_START + + GRUB_IEEE1275_STATIC_HEAP_LEN; + addr = ALIGN_UP (addr, align); + if (grub_claimmap (addr, size) == GRUB_ERR_NONE) + return addr; + return (grub_addr_t) -1; + } + + + grub_machine_mmap_iterate (alloc_mem, &ctx); + + return ctx.found_addr; +} + +static grub_err_t +grub_linux_boot (void) +{ + kernel_entry_t linuxmain; + grub_ssize_t actual; + + grub_arch_sync_caches ((void *) linux_addr, linux_size); + /* Set the command line arguments. */ + grub_ieee1275_set_property (grub_ieee1275_chosen, "bootargs", linux_args, + grub_strlen (linux_args) + 1, &actual); + + grub_dprintf ("loader", "Entry point: 0x%x\n", linux_entry); + grub_dprintf ("loader", "Initrd at: 0x%x, size 0x%x\n", initrd_addr, + initrd_size); + grub_dprintf ("loader", "Boot arguments: %s\n", linux_args); + grub_dprintf ("loader", "Jumping to Linux...\n"); + + /* Boot the kernel. */ + linuxmain = (kernel_entry_t) linux_entry; + linuxmain ((void *) initrd_addr, initrd_size, grub_ieee1275_entry_fn, 0, 0); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_linux_release_mem (void) +{ + grub_free (linux_args); + linux_args = 0; + + if (linux_addr && grub_ieee1275_release (linux_addr, linux_size)) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot release memory"); + + if (initrd_addr && grub_ieee1275_release (initrd_addr, initrd_size)) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot release memory"); + + linux_addr = 0; + initrd_addr = 0; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_linux_unload (void) +{ + grub_err_t err; + + err = grub_linux_release_mem (); + grub_dl_unref (my_mod); + + loaded = 0; + + return err; +} + +static grub_err_t +grub_linux_load32 (grub_elf_t elf, const char *filename) +{ + Elf32_Addr base_addr; + grub_addr_t seg_addr; + grub_uint32_t align; + grub_uint32_t offset; + Elf32_Addr entry; + + linux_size = grub_elf32_size (elf, &base_addr, &align); + if (linux_size == 0) + return grub_errno; + /* Pad it; the kernel scribbles over memory beyond its load address. */ + linux_size += 0x100000; + + /* Linux's entry point incorrectly contains a virtual address. */ + entry = elf->ehdr.ehdr32.e_entry & ~ELF32_LOADMASK; + + /* Linux's incorrectly contains a virtual address. */ + base_addr &= ~ELF32_LOADMASK; + offset = entry - base_addr; + + /* On some systems, firmware occupies the memory we're trying to use. + * Happily, Linux can be loaded anywhere (it relocates itself). Iterate + * until we find an open area. */ + seg_addr = grub_linux_claimmap_iterate (base_addr & ~ELF32_LOADMASK, linux_size, align); + if (seg_addr == (grub_addr_t) -1) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't claim memory"); + + linux_entry = seg_addr + offset; + linux_addr = seg_addr; + + /* Now load the segments into the area we claimed. */ + return grub_elf32_load (elf, filename, (void *) (seg_addr - base_addr), GRUB_ELF_LOAD_FLAGS_30BITS, 0, 0); +} + +static grub_err_t +grub_linux_load64 (grub_elf_t elf, const char *filename) +{ + Elf64_Addr base_addr; + grub_addr_t seg_addr; + grub_uint64_t align; + grub_uint64_t offset; + Elf64_Addr entry; + + linux_size = grub_elf64_size (elf, &base_addr, &align); + if (linux_size == 0) + return grub_errno; + /* Pad it; the kernel scribbles over memory beyond its load address. */ + linux_size += 0x100000; + + base_addr &= ~ELF64_LOADMASK; + entry = elf->ehdr.ehdr64.e_entry & ~ELF64_LOADMASK; + offset = entry - base_addr; + /* Linux's incorrectly contains a virtual address. */ + + /* On some systems, firmware occupies the memory we're trying to use. + * Happily, Linux can be loaded anywhere (it relocates itself). Iterate + * until we find an open area. */ + seg_addr = grub_linux_claimmap_iterate (base_addr & ~ELF64_LOADMASK, linux_size, align); + if (seg_addr == (grub_addr_t) -1) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't claim memory"); + + linux_entry = seg_addr + offset; + linux_addr = seg_addr; + + /* Now load the segments into the area we claimed. */ + return grub_elf64_load (elf, filename, (void *) (grub_addr_t) (seg_addr - base_addr), GRUB_ELF_LOAD_FLAGS_62BITS, 0, 0); +} + +static grub_err_t +grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_elf_t elf = 0; + int size; + + grub_dl_ref (my_mod); + + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + goto out; + } + + elf = grub_elf_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL); + if (! elf) + goto out; + + if (elf->ehdr.ehdr32.e_type != ET_EXEC && elf->ehdr.ehdr32.e_type != ET_DYN) + { + grub_error (GRUB_ERR_UNKNOWN_OS, + N_("this ELF file is not of the right type")); + goto out; + } + + /* Release the previously used memory. */ + grub_loader_unset (); + + if (grub_elf_is_elf32 (elf)) + grub_linux_load32 (elf, argv[0]); + else + if (grub_elf_is_elf64 (elf)) + grub_linux_load64 (elf, argv[0]); + else + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("invalid arch-dependent ELF magic")); + goto out; + } + + size = grub_loader_cmdline_size(argc, argv); + linux_args = grub_malloc (size + sizeof (LINUX_IMAGE)); + if (! linux_args) + goto out; + + /* Create kernel command line. */ + grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE)); + if (grub_create_loader_cmdline (argc, argv, linux_args + sizeof (LINUX_IMAGE) - 1, + size, GRUB_VERIFY_KERNEL_CMDLINE)) + goto out; + +out: + + if (elf) + grub_elf_close (elf); + + if (grub_errno != GRUB_ERR_NONE) + { + grub_linux_release_mem (); + grub_dl_unref (my_mod); + loaded = 0; + } + else + { + grub_loader_set (grub_linux_boot, grub_linux_unload, 1); + initrd_addr = 0; + loaded = 1; + } + + 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 first_addr; + grub_addr_t addr; + 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; + } + + if (grub_initrd_init (argc, argv, &initrd_ctx)) + goto fail; + + size = grub_get_initrd_size (&initrd_ctx); + + first_addr = linux_addr + linux_size; + + /* Attempt to claim at a series of addresses until successful in + the same way that grub_rescue_cmd_linux does. */ + addr = grub_linux_claimmap_iterate (first_addr, size, 0x100000); + if (addr == (grub_addr_t) -1) + goto fail; + + grub_dprintf ("loader", "Loading initrd at 0x%x, size 0x%x\n", addr, size); + + if (grub_initrd_load (&initrd_ctx, argv, (void *) addr)) + goto fail; + + initrd_addr = addr; + initrd_size = size; + + fail: + grub_initrd_close (&initrd_ctx); + + return grub_errno; +} + +static grub_command_t cmd_linux, cmd_initrd; + +GRUB_MOD_INIT(linux) +{ + cmd_linux = grub_register_command ("linux", grub_cmd_linux, + 0, N_("Load Linux.")); + cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd, + 0, N_("Load initrd.")); + my_mod = mod; +} + +GRUB_MOD_FINI(linux) +{ + grub_unregister_command (cmd_linux); + grub_unregister_command (cmd_initrd); +} diff --git a/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/sparc64/ieee1275/linux.c b/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/sparc64/ieee1275/linux.c new file mode 100644 index 00000000..bb47ee0c --- /dev/null +++ b/GRUB2/MOD_SRC/grub-2.04/grub-core/loader/sparc64/ieee1275/linux.c @@ -0,0 +1,521 @@ +/* linux.c - boot Linux */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003, 2004, 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 +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_dl_t my_mod; + +static int loaded; + +/* /virtual-memory/translations property layout */ +struct grub_ieee1275_translation { + grub_uint64_t vaddr; + grub_uint64_t size; + grub_uint64_t data; +}; + +static struct grub_ieee1275_translation *of_trans; +static int of_num_trans; + +static grub_addr_t phys_base; +static grub_addr_t grub_phys_start; +static grub_addr_t grub_phys_end; + +static grub_addr_t initrd_addr; +static grub_addr_t initrd_paddr; +static grub_size_t initrd_size; + +static Elf64_Addr linux_entry; +static grub_addr_t linux_addr; +static grub_addr_t linux_paddr; +static grub_size_t linux_size; + +static char *linux_args; + +struct linux_bootstr_info { + int len, valid; + char buf[]; +}; + +struct linux_hdrs { + /* All HdrS versions support these fields. */ + unsigned int start_insns[2]; + char magic[4]; /* "HdrS" */ + unsigned int linux_kernel_version; /* LINUX_VERSION_CODE */ + unsigned short hdrs_version; + unsigned short root_flags; + unsigned short root_dev; + unsigned short ram_flags; + unsigned int __deprecated_ramdisk_image; + unsigned int ramdisk_size; + + /* HdrS versions 0x0201 and higher only */ + char *reboot_command; + + /* HdrS versions 0x0202 and higher only */ + struct linux_bootstr_info *bootstr_info; + + /* HdrS versions 0x0301 and higher only */ + unsigned long ramdisk_image; +}; + +static grub_err_t +grub_linux_boot (void) +{ + struct linux_bootstr_info *bp; + struct linux_hdrs *hp; + grub_addr_t addr; + + hp = (struct linux_hdrs *) linux_addr; + + /* Any pointer we dereference in the kernel image must be relocated + to where we actually loaded the kernel. */ + addr = (grub_addr_t) hp->bootstr_info; + addr += (linux_addr - linux_entry); + bp = (struct linux_bootstr_info *) addr; + + /* Set the command line arguments, unless the kernel has been + built with a fixed CONFIG_CMDLINE. */ + if (!bp->valid) + { + int len = grub_strlen (linux_args) + 1; + if (bp->len < len) + len = bp->len; + grub_memcpy(bp->buf, linux_args, len); + bp->buf[len-1] = '\0'; + bp->valid = 1; + } + + if (initrd_addr) + { + /* The kernel expects the physical address, adjusted relative + to the lowest address advertised in "/memory"'s available + property. + + The history of this is that back when the kernel only supported + specifying a 32-bit ramdisk address, this was the way to still + be able to specify the ramdisk physical address even if memory + started at some place above 4GB. + + The magic 0x400000 is KERNBASE, I have no idea why SILO adds + that term into the address, but it does and thus we have to do + it too as this is what the kernel expects. */ + hp->ramdisk_image = initrd_paddr - phys_base + 0x400000; + hp->ramdisk_size = initrd_size; + } + + grub_dprintf ("loader", "Entry point: 0x%lx\n", linux_addr); + grub_dprintf ("loader", "Initrd at: 0x%lx, size 0x%lx\n", initrd_addr, + initrd_size); + grub_dprintf ("loader", "Boot arguments: %s\n", linux_args); + grub_dprintf ("loader", "Jumping to Linux...\n"); + + /* Boot the kernel. */ + asm volatile ("ldx %0, %%o4\n" + "ldx %1, %%o6\n" + "ldx %2, %%o5\n" + "mov %%g0, %%o0\n" + "mov %%g0, %%o2\n" + "mov %%g0, %%o3\n" + "jmp %%o5\n" + "mov %%g0, %%o1\n": : + "m"(grub_ieee1275_entry_fn), + "m"(grub_ieee1275_original_stack), + "m"(linux_addr)); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_linux_release_mem (void) +{ + grub_free (linux_args); + linux_args = 0; + linux_addr = 0; + initrd_addr = 0; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_linux_unload (void) +{ + grub_err_t err; + + err = grub_linux_release_mem (); + grub_dl_unref (my_mod); + + loaded = 0; + + return err; +} + +#define FOUR_MB (4 * 1024 * 1024) + +/* Context for alloc_phys. */ +struct alloc_phys_ctx +{ + grub_addr_t size; + grub_addr_t ret; +}; + +/* Helper for alloc_phys. */ +static int +alloc_phys_choose (grub_uint64_t addr, grub_uint64_t len, + grub_memory_type_t type, void *data) +{ + struct alloc_phys_ctx *ctx = data; + grub_addr_t end = addr + len; + + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + + addr = ALIGN_UP (addr, FOUR_MB); + if (addr + ctx->size >= end) + return 0; + + /* OBP available region contains grub. Start at grub_phys_end. */ + /* grub_phys_start does not start at the beginning of the memory region */ + if ((grub_phys_start >= addr && grub_phys_end < end) || + (addr > grub_phys_start && addr < grub_phys_end)) + { + addr = ALIGN_UP (grub_phys_end, FOUR_MB); + if (addr + ctx->size >= end) + return 0; + } + + grub_dprintf("loader", + "addr = 0x%lx grub_phys_start = 0x%lx grub_phys_end = 0x%lx\n", + addr, grub_phys_start, grub_phys_end); + + if (loaded) + { + grub_addr_t linux_end = ALIGN_UP (linux_paddr + linux_size, FOUR_MB); + + if (addr >= linux_paddr && addr < linux_end) + { + addr = linux_end; + if (addr + ctx->size >= end) + return 0; + } + if ((addr + ctx->size) >= linux_paddr + && (addr + ctx->size) < linux_end) + { + addr = linux_end; + if (addr + ctx->size >= end) + return 0; + } + } + + ctx->ret = addr; + return 1; +} + +static grub_addr_t +alloc_phys (grub_addr_t size) +{ + struct alloc_phys_ctx ctx = { + .size = size, + .ret = (grub_addr_t) -1 + }; + + grub_machine_mmap_iterate (alloc_phys_choose, &ctx); + + return ctx.ret; +} + +static grub_err_t +grub_linux_load64 (grub_elf_t elf, const char *filename) +{ + grub_addr_t off, paddr, base; + int ret; + + linux_entry = elf->ehdr.ehdr64.e_entry; + linux_addr = 0x40004000; + off = 0x4000; + linux_size = grub_elf64_size (elf, 0, 0); + if (linux_size == 0) + return grub_errno; + + grub_dprintf ("loader", "Attempting to claim at 0x%lx, size 0x%lx.\n", + linux_addr, linux_size); + + paddr = alloc_phys (linux_size + off); + if (paddr == (grub_addr_t) -1) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "couldn't allocate physical memory"); + ret = grub_ieee1275_map (paddr, linux_addr - off, + linux_size + off, IEEE1275_MAP_DEFAULT); + if (ret) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "couldn't map physical memory"); + + grub_dprintf ("loader", "Loading Linux at vaddr 0x%lx, paddr 0x%lx, size 0x%lx\n", + linux_addr, paddr, linux_size); + + linux_paddr = paddr; + + base = linux_entry - off; + + /* Now load the segments into the area we claimed. */ + return grub_elf64_load (elf, filename, (void *) (linux_addr - off - base), GRUB_ELF_LOAD_FLAGS_NONE, 0, 0); +} + +static grub_err_t +grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file = 0; + grub_elf_t elf = 0; + int size; + + grub_dl_ref (my_mod); + + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + goto out; + } + + file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL); + if (!file) + goto out; + + elf = grub_elf_file (file, argv[0]); + if (! elf) + goto out; + + if (elf->ehdr.ehdr32.e_type != ET_EXEC) + { + grub_error (GRUB_ERR_UNKNOWN_OS, + N_("this ELF file is not of the right type")); + goto out; + } + + /* Release the previously used memory. */ + grub_loader_unset (); + + if (grub_elf_is_elf64 (elf)) + grub_linux_load64 (elf, argv[0]); + else + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("invalid arch-dependent ELF magic")); + goto out; + } + + size = grub_loader_cmdline_size(argc, argv); + + linux_args = grub_malloc (size + sizeof (LINUX_IMAGE)); + if (! linux_args) + goto out; + + /* Create kernel command line. */ + grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE)); + if (grub_create_loader_cmdline (argc, argv, linux_args + sizeof (LINUX_IMAGE) - 1, + size, GRUB_VERIFY_KERNEL_CMDLINE)) + goto out; + +out: + if (elf) + grub_elf_close (elf); + else if (file) + grub_file_close (file); + + if (grub_errno != GRUB_ERR_NONE) + { + grub_linux_release_mem (); + grub_dl_unref (my_mod); + loaded = 0; + } + else + { + grub_loader_set (grub_linux_boot, grub_linux_unload, 1); + initrd_addr = 0; + loaded = 1; + } + + 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 paddr; + grub_addr_t addr; + int ret; + 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; + } + + if (grub_initrd_init (argc, argv, &initrd_ctx)) + goto fail; + + size = grub_get_initrd_size (&initrd_ctx); + + addr = 0x60000000; + + paddr = alloc_phys (size); + if (paddr == (grub_addr_t) -1) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, + "couldn't allocate physical memory"); + goto fail; + } + ret = grub_ieee1275_map (paddr, addr, size, IEEE1275_MAP_DEFAULT); + if (ret) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, + "couldn't map physical memory"); + goto fail; + } + + grub_dprintf ("loader", "Loading initrd at vaddr 0x%lx, paddr 0x%lx, size 0x%lx\n", + addr, paddr, size); + + if (grub_initrd_load (&initrd_ctx, argv, (void *) addr)) + goto fail; + + initrd_addr = addr; + initrd_paddr = paddr; + initrd_size = size; + + fail: + grub_initrd_close (&initrd_ctx); + + return grub_errno; +} + +/* Helper for determine_phys_base. */ +static int +get_physbase (grub_uint64_t addr, grub_uint64_t len __attribute__ ((unused)), + grub_memory_type_t type, void *data __attribute__ ((unused))) +{ + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + if (addr < phys_base) + phys_base = addr; + return 0; +} + +static void +determine_phys_base (void) +{ + phys_base = ~(grub_uint64_t) 0; + grub_machine_mmap_iterate (get_physbase, NULL); +} + +static void +fetch_translations (void) +{ + grub_ieee1275_phandle_t node; + grub_ssize_t actual; + int i; + + if (grub_ieee1275_finddevice ("/virtual-memory", &node)) + { + grub_printf ("Cannot find /virtual-memory node.\n"); + return; + } + + if (grub_ieee1275_get_property_length (node, "translations", &actual)) + { + grub_printf ("Cannot find /virtual-memory/translations size.\n"); + return; + } + + of_trans = grub_malloc (actual); + if (!of_trans) + { + grub_printf ("Cannot allocate translations buffer.\n"); + return; + } + + if (grub_ieee1275_get_property (node, "translations", of_trans, actual, &actual)) + { + grub_printf ("Cannot fetch /virtual-memory/translations property.\n"); + return; + } + + of_num_trans = actual / sizeof(struct grub_ieee1275_translation); + + for (i = 0; i < of_num_trans; i++) + { + struct grub_ieee1275_translation *p = &of_trans[i]; + + if (p->vaddr == 0x2000) + { + grub_addr_t phys, tte = p->data; + + phys = tte & ~(0xff00000000001fffULL); + + grub_phys_start = phys; + grub_phys_end = grub_phys_start + p->size; + grub_dprintf ("loader", "Grub lives at phys_start[%lx] phys_end[%lx]\n", + (unsigned long) grub_phys_start, + (unsigned long) grub_phys_end); + break; + } + } +} + + +static grub_command_t cmd_linux, cmd_initrd; + +GRUB_MOD_INIT(linux) +{ + determine_phys_base (); + fetch_translations (); + + cmd_linux = grub_register_command ("linux", grub_cmd_linux, + 0, N_("Load Linux.")); + cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd, + 0, N_("Load initrd.")); + my_mod = mod; +} + +GRUB_MOD_FINI(linux) +{ + grub_unregister_command (cmd_linux); + grub_unregister_command (cmd_initrd); +} diff --git a/GRUB2/MOD_SRC/grub-2.04/include/grub/linux.h b/GRUB2/MOD_SRC/grub-2.04/include/grub/linux.h new file mode 100644 index 00000000..594a3f30 --- /dev/null +++ b/GRUB2/MOD_SRC/grub-2.04/include/grub/linux.h @@ -0,0 +1,24 @@ +#include + +struct grub_linux_initrd_component; + +struct grub_linux_initrd_context +{ + int nfiles; + struct grub_linux_initrd_component *components; + grub_size_t size; +}; + +grub_err_t +grub_initrd_init (int argc, char *argv[], + struct grub_linux_initrd_context *ctx); + +grub_size_t +grub_get_initrd_size (struct grub_linux_initrd_context *ctx); + +void +grub_initrd_close (struct grub_linux_initrd_context *initrd_ctx); + +grub_err_t +grub_initrd_load (struct grub_linux_initrd_context *initrd_ctx, + char *argv[], void *target);