/* multiboot.c - boot a multiboot OS image. */ /* * VAS_EBOOT -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc. * * VAS_EBOOT 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. * * VAS_EBOOT 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 VAS_EBOOT. 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 #ifdef VAS_EBOOT_USE_MULTIBOOT2 #include #define VAS_EBOOT_MULTIBOOT_CONSOLE_FRAMEBUFFER VAS_EBOOT_MULTIBOOT2_CONSOLE_FRAMEBUFFER #define VAS_EBOOT_MULTIBOOT_CONSOLE_EGA_TEXT VAS_EBOOT_MULTIBOOT2_CONSOLE_EGA_TEXT #define VAS_EBOOT_MULTIBOOT(x) VasEBoot_multiboot2_ ## x #else #include #define VAS_EBOOT_MULTIBOOT(x) VasEBoot_multiboot_ ## x #endif #include #include #include #include #include #include #include #include #include #include #include #include #include VAS_EBOOT_MOD_LICENSE ("GPLv3+"); #ifdef VAS_EBOOT_MACHINE_EFI #include #endif struct VasEBoot_relocator *VAS_EBOOT_MULTIBOOT (relocator) = NULL; VasEBoot_uint32_t VAS_EBOOT_MULTIBOOT (payload_eip); #if defined (VAS_EBOOT_MACHINE_PCBIOS) || defined (VAS_EBOOT_MACHINE_MULTIBOOT) || defined (VAS_EBOOT_MACHINE_COREBOOT) || defined (VAS_EBOOT_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 VAS_EBOOT_MULTIBOOT (get_mmap_count) (void) { VasEBoot_size_t count = 0; VasEBoot_mmap_iterate (count_hook, &count); return count; } VasEBoot_err_t VAS_EBOOT_MULTIBOOT (set_video_mode) (void) { VasEBoot_err_t err; const char *modevar; #if VAS_EBOOT_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 VAS_EBOOT_MACHINE_HAS_VGA_TEXT else err = VasEBoot_video_set_mode ("text", 0, 0); #endif return err; } #ifdef VAS_EBOOT_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) { #ifdef VAS_EBOOT_USE_MULTIBOOT2 struct VasEBoot_relocator_efi_state state_efi = MULTIBOOT2_EFI_INITIAL_STATE; #else struct VasEBoot_relocator_efi_state state_efi = MULTIBOOT_EFI_INITIAL_STATE; #endif state_efi.MULTIBOOT_EFI_ENTRY_REGISTER = VAS_EBOOT_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; #ifdef VAS_EBOOT_USE_MULTIBOOT2 struct VasEBoot_relocator32_state state = MULTIBOOT2_INITIAL_STATE; #else struct VasEBoot_relocator32_state state = MULTIBOOT_INITIAL_STATE; #endif state.MULTIBOOT_ENTRY_REGISTER = VAS_EBOOT_MULTIBOOT (payload_eip); err = VAS_EBOOT_MULTIBOOT (make_mbi) (&state.MULTIBOOT_MBI_REGISTER); if (err) return err; if (VasEBoot_efi_is_finished) normal_boot (VAS_EBOOT_MULTIBOOT (relocator), state); else efi_boot (VAS_EBOOT_MULTIBOOT (relocator), state.MULTIBOOT_MBI_REGISTER); /* Not reached. */ return VAS_EBOOT_ERR_NONE; } static VasEBoot_err_t VasEBoot_multiboot_unload (void) { VAS_EBOOT_MULTIBOOT (free_mbi) (); VasEBoot_relocator_unload (VAS_EBOOT_MULTIBOOT (relocator)); VAS_EBOOT_MULTIBOOT (relocator) = NULL; VasEBoot_dl_unref (my_mod); return VAS_EBOOT_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 VAS_EBOOT_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 (VAS_EBOOT_ERR_UNKNOWN_OS, N_("invalid arch-dependent ELF magic")); } VasEBoot_err_t VAS_EBOOT_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 & (VAS_EBOOT_MULTIBOOT_CONSOLE_FRAMEBUFFER | (VAS_EBOOT_MACHINE_HAS_VGA_TEXT ? VAS_EBOOT_MULTIBOOT_CONSOLE_EGA_TEXT : 0)))) { if (console_required) return VasEBoot_error (VAS_EBOOT_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 VAS_EBOOT_ERR_NONE; } if (console_type == VAS_EBOOT_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 VAS_EBOOT_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 & VAS_EBOOT_MULTIBOOT_CONSOLE_FRAMEBUFFER); accepts_ega_text = !!(accepted_consoles & VAS_EBOOT_MULTIBOOT_CONSOLE_EGA_TEXT); return VAS_EBOOT_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 VAS_EBOOT_USE_MULTIBOOT2 VasEBoot_multiboot_quirks = VAS_EBOOT_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 |= VAS_EBOOT_MULTIBOOT_QUIRK_BAD_KLUDGE; } if (argc != 0 && VasEBoot_strcmp (argv[0], "--quirk-modules-after-kernel") == 0) { argc--; argv++; option_found = 1; VasEBoot_multiboot_quirks |= VAS_EBOOT_MULTIBOOT_QUIRK_MODULES_AFTER_KERNEL; } } while (option_found); #endif if (argc == 0) return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, N_("filename expected")); file = VasEBoot_file_open (argv[0], VAS_EBOOT_FILE_TYPE_MULTIBOOT_KERNEL); if (! file) return VasEBoot_errno; VasEBoot_dl_ref (my_mod); /* Skip filename. */ VAS_EBOOT_MULTIBOOT (init_mbi) (argc - 1, argv + 1); VasEBoot_relocator_unload (VAS_EBOOT_MULTIBOOT (relocator)); VAS_EBOOT_MULTIBOOT (relocator) = VasEBoot_relocator_new (); if (!VAS_EBOOT_MULTIBOOT (relocator)) goto fail; err = VAS_EBOOT_MULTIBOOT (load) (file, argv[0]); if (err) goto fail; VAS_EBOOT_MULTIBOOT (set_bootdev) (); VasEBoot_loader_set (VasEBoot_multiboot_boot, VasEBoot_multiboot_unload, 0); fail: if (file) VasEBoot_file_close (file); if (VasEBoot_errno != VAS_EBOOT_ERR_NONE) { VasEBoot_relocator_unload (VAS_EBOOT_MULTIBOOT (relocator)); VAS_EBOOT_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 (VAS_EBOOT_ERR_BAD_ARGUMENT, N_("filename expected")); if (VasEBoot_strcmp (argv[0], "--nounzip") == 0) { argv++; argc--; nounzip = 1; } if (argc == 0) return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, N_("filename expected")); if (!VAS_EBOOT_MULTIBOOT (relocator)) return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, N_("you need to load the kernel first")); file = VasEBoot_file_open (argv[0], VAS_EBOOT_FILE_TYPE_MULTIBOOT_MODULE | (nounzip ? VAS_EBOOT_FILE_TYPE_NO_DECOMPRESS : VAS_EBOOT_FILE_TYPE_NONE)); if (! file) return VasEBoot_errno; #ifndef VAS_EBOOT_USE_MULTIBOOT2 lowest_addr = 0x100000; if (VasEBoot_multiboot_quirks & VAS_EBOOT_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 (VAS_EBOOT_MULTIBOOT (relocator), &ch, lowest_addr, UP_TO_TOP32 (size), size, MULTIBOOT_MOD_ALIGN, VAS_EBOOT_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; } if (size && VasEBoot_file_read (file, module, size) != size) { VasEBoot_file_close (file); if (!VasEBoot_errno) VasEBoot_error (VAS_EBOOT_ERR_FILE_READ_ERROR, N_("premature end of file %s"), argv[0]); return VasEBoot_errno; } VasEBoot_file_close (file); return VAS_EBOOT_MULTIBOOT (add_module) (target, size, argc - 1, argv + 1); } static VasEBoot_command_t cmd_multiboot, cmd_module; VAS_EBOOT_MOD_INIT(multiboot) { cmd_multiboot = #ifdef VAS_EBOOT_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; } VAS_EBOOT_MOD_FINI(multiboot) { VasEBoot_unregister_command (cmd_multiboot); VasEBoot_unregister_command (cmd_module); }