/* multiboot.c - boot a multiboot OS image. */ /* * VasEBoot -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc. * * VasEBoot 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. * * VasEBoot 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 VasEBoot. If not, see . */ /* * FIXME: The following features from the Multiboot specification still * need to be implemented: * - drives table * - ROM configuration table * - SMBIOS tables * - Networking information */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include VasEBoot_MOD_LICENSE ("GPLv3+"); #ifdef VasEBoot_MACHINE_EFI #include #endif struct VasEBoot_relocator *VasEBoot_multiboot_relocator = NULL; VasEBoot_uint32_t VasEBoot_multiboot_payload_eip; #if defined (VasEBoot_MACHINE_PCBIOS) || defined (VasEBoot_MACHINE_MULTIBOOT) || defined (VasEBoot_MACHINE_COREBOOT) || defined (VasEBoot_MACHINE_QEMU) #define DEFAULT_VIDEO_MODE "text" #else #define DEFAULT_VIDEO_MODE "auto" #endif static int accepts_video; static int accepts_ega_text; static int console_required; static VasEBoot_dl_t my_mod; /* Helper for VasEBoot_get_multiboot_mmap_count. */ static int count_hook (VasEBoot_uint64_t addr __attribute__ ((unused)), VasEBoot_uint64_t size __attribute__ ((unused)), VasEBoot_memory_type_t type __attribute__ ((unused)), void *data) { VasEBoot_size_t *count = data; (*count)++; return 0; } /* Return the length of the Multiboot mmap that will be needed to allocate our platform's map. */ VasEBoot_uint32_t VasEBoot_get_multiboot_mmap_count (void) { VasEBoot_size_t count = 0; VasEBoot_mmap_iterate (count_hook, &count); return count; } VasEBoot_err_t VasEBoot_multiboot_set_video_mode (void) { VasEBoot_err_t err; const char *modevar; #if VasEBoot_MACHINE_HAS_VGA_TEXT if (accepts_video) #endif { modevar = VasEBoot_env_get ("gfxpayload"); if (! modevar || *modevar == 0) err = VasEBoot_video_set_mode (DEFAULT_VIDEO_MODE, 0, 0); else { char *tmp; tmp = VasEBoot_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar); if (! tmp) return VasEBoot_errno; err = VasEBoot_video_set_mode (tmp, 0, 0); VasEBoot_free (tmp); } } #if VasEBoot_MACHINE_HAS_VGA_TEXT else err = VasEBoot_video_set_mode ("text", 0, 0); #endif return err; } #ifdef VasEBoot_MACHINE_EFI #ifdef __x86_64__ #define VasEBoot_relocator_efi_boot VasEBoot_relocator64_efi_boot #define VasEBoot_relocator_efi_state VasEBoot_relocator64_efi_state #endif #endif #ifdef VasEBoot_relocator_efi_boot static void efi_boot (struct VasEBoot_relocator *rel, VasEBoot_uint32_t target) { struct VasEBoot_relocator_efi_state state_efi = MULTIBOOT_EFI_INITIAL_STATE; state_efi.MULTIBOOT_EFI_ENTRY_REGISTER = VasEBoot_multiboot_payload_eip; state_efi.MULTIBOOT_EFI_MBI_REGISTER = target; VasEBoot_relocator_efi_boot (rel, state_efi); } #else #define VasEBoot_efi_is_finished 1 static void efi_boot (struct VasEBoot_relocator *rel __attribute__ ((unused)), VasEBoot_uint32_t target __attribute__ ((unused))) { } #endif #if defined (__i386__) || defined (__x86_64__) static void normal_boot (struct VasEBoot_relocator *rel, struct VasEBoot_relocator32_state state) { VasEBoot_relocator32_boot (rel, state, 0); } #else static void normal_boot (struct VasEBoot_relocator *rel, struct VasEBoot_relocator32_state state) { VasEBoot_relocator32_boot (rel, state); } #endif static VasEBoot_err_t VasEBoot_multiboot_boot (void) { VasEBoot_err_t err; struct VasEBoot_relocator32_state state = MULTIBOOT_INITIAL_STATE; state.MULTIBOOT_ENTRY_REGISTER = VasEBoot_multiboot_payload_eip; err = VasEBoot_multiboot_make_mbi (&state.MULTIBOOT_MBI_REGISTER); if (err) return err; if (VasEBoot_efi_is_finished) normal_boot (VasEBoot_multiboot_relocator, state); else efi_boot (VasEBoot_multiboot_relocator, state.MULTIBOOT_MBI_REGISTER); /* Not reached. */ return VasEBoot_ERR_NONE; } static VasEBoot_err_t VasEBoot_multiboot_unload (void) { VasEBoot_multiboot_free_mbi (); VasEBoot_relocator_unload (VasEBoot_multiboot_relocator); VasEBoot_multiboot_relocator = NULL; VasEBoot_dl_unref (my_mod); return VasEBoot_ERR_NONE; } static VasEBoot_uint64_t highest_load; #define MULTIBOOT_LOAD_ELF64 #include "multiboot_elfxx.c" #undef MULTIBOOT_LOAD_ELF64 #define MULTIBOOT_LOAD_ELF32 #include "multiboot_elfxx.c" #undef MULTIBOOT_LOAD_ELF32 /* Load ELF32 or ELF64. */ VasEBoot_err_t VasEBoot_multiboot_load_elf (mbi_load_data_t *mld) { if (VasEBoot_multiboot_is_elf32 (mld->buffer)) return VasEBoot_multiboot_load_elf32 (mld); else if (VasEBoot_multiboot_is_elf64 (mld->buffer)) return VasEBoot_multiboot_load_elf64 (mld); return VasEBoot_error (VasEBoot_ERR_UNKNOWN_OS, N_("invalid arch-dependent ELF magic")); } VasEBoot_err_t VasEBoot_multiboot_set_console (int console_type, int accepted_consoles, int width, int height, int depth, int console_req) { console_required = console_req; if (!(accepted_consoles & (VasEBoot_MULTIBOOT_CONSOLE_FRAMEBUFFER | (VasEBoot_MACHINE_HAS_VGA_TEXT ? VasEBoot_MULTIBOOT_CONSOLE_EGA_TEXT : 0)))) { if (console_required) return VasEBoot_error (VasEBoot_ERR_BAD_OS, "OS requires a console but none is available"); VasEBoot_puts_ (N_("WARNING: no console will be available to OS")); accepts_video = 0; accepts_ega_text = 0; return VasEBoot_ERR_NONE; } if (console_type == VasEBoot_MULTIBOOT_CONSOLE_FRAMEBUFFER) { char *buf; if (depth && width && height) buf = VasEBoot_xasprintf ("%dx%dx%d,%dx%d,auto", width, height, depth, width, height); else if (width && height) buf = VasEBoot_xasprintf ("%dx%d,auto", width, height); else buf = VasEBoot_strdup ("auto"); if (!buf) return VasEBoot_errno; VasEBoot_env_set ("gfxpayload", buf); VasEBoot_free (buf); } else { #if VasEBoot_MACHINE_HAS_VGA_TEXT VasEBoot_env_set ("gfxpayload", "text"); #else /* Always use video if no VGA text is available. */ VasEBoot_env_set ("gfxpayload", "auto"); #endif } accepts_video = !!(accepted_consoles & VasEBoot_MULTIBOOT_CONSOLE_FRAMEBUFFER); accepts_ega_text = !!(accepted_consoles & VasEBoot_MULTIBOOT_CONSOLE_EGA_TEXT); return VasEBoot_ERR_NONE; } static VasEBoot_err_t VasEBoot_cmd_multiboot (VasEBoot_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) { VasEBoot_file_t file = 0; VasEBoot_err_t err; VasEBoot_loader_unset (); highest_load = 0; #ifndef VasEBoot_USE_MULTIBOOT2 VasEBoot_multiboot_quirks = VasEBoot_MULTIBOOT_QUIRKS_NONE; int option_found = 0; do { option_found = 0; if (argc != 0 && VasEBoot_strcmp (argv[0], "--quirk-bad-kludge") == 0) { argc--; argv++; option_found = 1; VasEBoot_multiboot_quirks |= VasEBoot_MULTIBOOT_QUIRK_BAD_KLUDGE; } if (argc != 0 && VasEBoot_strcmp (argv[0], "--quirk-modules-after-kernel") == 0) { argc--; argv++; option_found = 1; VasEBoot_multiboot_quirks |= VasEBoot_MULTIBOOT_QUIRK_MODULES_AFTER_KERNEL; } } while (option_found); #endif if (argc == 0) return VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT, N_("filename expected")); file = VasEBoot_file_open (argv[0]); if (! file) return VasEBoot_errno; VasEBoot_dl_ref (my_mod); /* Skip filename. */ VasEBoot_multiboot_init_mbi (argc - 1, argv + 1); VasEBoot_relocator_unload (VasEBoot_multiboot_relocator); VasEBoot_multiboot_relocator = VasEBoot_relocator_new (); if (!VasEBoot_multiboot_relocator) goto fail; err = VasEBoot_multiboot_load (file, argv[0]); if (err) goto fail; VasEBoot_multiboot_set_bootdev (); VasEBoot_loader_set (VasEBoot_multiboot_boot, VasEBoot_multiboot_unload, 0); fail: if (file) VasEBoot_file_close (file); if (VasEBoot_errno != VasEBoot_ERR_NONE) { VasEBoot_relocator_unload (VasEBoot_multiboot_relocator); VasEBoot_multiboot_relocator = NULL; VasEBoot_dl_unref (my_mod); } return VasEBoot_errno; } static VasEBoot_err_t VasEBoot_cmd_module (VasEBoot_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) { VasEBoot_file_t file = 0; VasEBoot_ssize_t size; void *module = NULL; VasEBoot_addr_t target; VasEBoot_err_t err; int nounzip = 0; VasEBoot_uint64_t lowest_addr = 0; if (argc == 0) return VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT, N_("filename expected")); if (VasEBoot_strcmp (argv[0], "--nounzip") == 0) { argv++; argc--; nounzip = 1; } if (argc == 0) return VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT, N_("filename expected")); if (!VasEBoot_multiboot_relocator) return VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT, N_("you need to load the kernel first")); if (nounzip) VasEBoot_file_filter_disable_compression (); file = VasEBoot_file_open (argv[0]); if (! file) return VasEBoot_errno; #ifndef VasEBoot_USE_MULTIBOOT2 lowest_addr = 0x100000; if (VasEBoot_multiboot_quirks & VasEBoot_MULTIBOOT_QUIRK_MODULES_AFTER_KERNEL) lowest_addr = ALIGN_UP (highest_load + 1048576, 4096); #endif size = VasEBoot_file_size (file); if (size) { VasEBoot_relocator_chunk_t ch; err = VasEBoot_relocator_alloc_chunk_align (VasEBoot_multiboot_relocator, &ch, lowest_addr, (0xffffffff - size) + 1, size, MULTIBOOT_MOD_ALIGN, VasEBoot_RELOCATOR_PREFERENCE_NONE, 1); if (err) { VasEBoot_file_close (file); return err; } module = get_virtual_current_address (ch); target = get_physical_target_address (ch); } else { module = 0; target = 0; } err = VasEBoot_multiboot_add_module (target, size, argc - 1, argv + 1); if (err) { VasEBoot_file_close (file); return err; } if (size && VasEBoot_file_read (file, module, size) != size) { VasEBoot_file_close (file); if (!VasEBoot_errno) VasEBoot_error (VasEBoot_ERR_FILE_READ_ERROR, N_("premature end of file %s"), argv[0]); return VasEBoot_errno; } VasEBoot_file_close (file); VasEBoot_tpm_measure (module, size, VasEBoot_BINARY_PCR, "VasEBoot_multiboot", argv[0]); VasEBoot_print_error(); return VasEBoot_ERR_NONE; } static VasEBoot_command_t cmd_multiboot, cmd_module; VasEBoot_MOD_INIT(multiboot) { cmd_multiboot = #ifdef VasEBoot_USE_MULTIBOOT2 VasEBoot_register_command ("multiboot2", VasEBoot_cmd_multiboot, 0, N_("Load a multiboot 2 kernel.")); cmd_module = VasEBoot_register_command ("module2", VasEBoot_cmd_module, 0, N_("Load a multiboot 2 module.")); #else VasEBoot_register_command ("multiboot", VasEBoot_cmd_multiboot, 0, N_("Load a multiboot kernel.")); cmd_module = VasEBoot_register_command ("module", VasEBoot_cmd_module, 0, N_("Load a multiboot module.")); #endif my_mod = mod; } VasEBoot_MOD_FINI(multiboot) { VasEBoot_unregister_command (cmd_multiboot); VasEBoot_unregister_command (cmd_module); }