/* * VasEBoot -- GRand Unified Bootloader * Copyright (C) 2009 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 . */ /* This is an emulation of EFI runtime services. This allows a more uniform boot on i386 machines. As it emulates only runtime service it isn't able to chainload EFI bootloader on non-EFI system. */ #include #include #include #include #include #include #include #include #include VasEBoot_MOD_LICENSE ("GPLv3+"); /* System table. Two version depending on mode */ VasEBoot_efi_system_table32_t *VasEBoot_efiemu_system_table32 = 0; VasEBoot_efi_system_table64_t *VasEBoot_efiemu_system_table64 = 0; /* Modules may need to execute some actions after memory allocation happens */ static struct VasEBoot_efiemu_prepare_hook *efiemu_prepare_hooks = 0; /* Linked list of configuration tables */ static struct VasEBoot_efiemu_configuration_table *efiemu_config_tables = 0; static int prepared = 0; /* Free all allocated space */ VasEBoot_err_t VasEBoot_efiemu_unload (void) { struct VasEBoot_efiemu_configuration_table *cur, *d; struct VasEBoot_efiemu_prepare_hook *curhook, *d2; VasEBoot_efiemu_loadcore_unload (); VasEBoot_efiemu_mm_unload (); for (cur = efiemu_config_tables; cur;) { d = cur->next; if (cur->unload) cur->unload (cur->data); VasEBoot_free (cur); cur = d; } efiemu_config_tables = 0; for (curhook = efiemu_prepare_hooks; curhook;) { d2 = curhook->next; if (curhook->unload) curhook->unload (curhook->data); VasEBoot_free (curhook); curhook = d2; } efiemu_prepare_hooks = 0; prepared = 0; return VasEBoot_ERR_NONE; } /* Remove previously registered table from the list */ VasEBoot_err_t VasEBoot_efiemu_unregister_configuration_table (VasEBoot_efi_guid_t guid) { struct VasEBoot_efiemu_configuration_table *cur, *prev; /* Special treating if head is to remove */ while (efiemu_config_tables && !VasEBoot_memcmp (&(efiemu_config_tables->guid), &guid, sizeof (guid))) { if (efiemu_config_tables->unload) efiemu_config_tables->unload (efiemu_config_tables->data); cur = efiemu_config_tables->next; VasEBoot_free (efiemu_config_tables); efiemu_config_tables = cur; } if (!efiemu_config_tables) return VasEBoot_ERR_NONE; /* Remove from chain */ for (prev = efiemu_config_tables, cur = prev->next; cur;) if (VasEBoot_memcmp (&(cur->guid), &guid, sizeof (guid)) == 0) { if (cur->unload) cur->unload (cur->data); prev->next = cur->next; VasEBoot_free (cur); cur = prev->next; } else { prev = cur; cur = cur->next; } return VasEBoot_ERR_NONE; } VasEBoot_err_t VasEBoot_efiemu_register_prepare_hook (VasEBoot_err_t (*hook) (void *data), void (*unload) (void *data), void *data) { struct VasEBoot_efiemu_prepare_hook *nhook; nhook = (struct VasEBoot_efiemu_prepare_hook *) VasEBoot_malloc (sizeof (*nhook)); if (! nhook) return VasEBoot_errno; nhook->hook = hook; nhook->unload = unload; nhook->data = data; nhook->next = efiemu_prepare_hooks; efiemu_prepare_hooks = nhook; return VasEBoot_ERR_NONE; } /* Register a configuration table either supplying the address directly or with a hook */ VasEBoot_err_t VasEBoot_efiemu_register_configuration_table (VasEBoot_efi_guid_t guid, void * (*get_table) (void *data), void (*unload) (void *data), void *data) { struct VasEBoot_efiemu_configuration_table *tbl; VasEBoot_err_t err; err = VasEBoot_efiemu_unregister_configuration_table (guid); if (err) return err; tbl = (struct VasEBoot_efiemu_configuration_table *) VasEBoot_malloc (sizeof (*tbl)); if (! tbl) return VasEBoot_errno; tbl->guid = guid; tbl->get_table = get_table; tbl->unload = unload; tbl->data = data; tbl->next = efiemu_config_tables; efiemu_config_tables = tbl; return VasEBoot_ERR_NONE; } static VasEBoot_err_t VasEBoot_cmd_efiemu_unload (VasEBoot_command_t cmd __attribute__ ((unused)), int argc __attribute__ ((unused)), char *args[] __attribute__ ((unused))) { return VasEBoot_efiemu_unload (); } static VasEBoot_err_t VasEBoot_cmd_efiemu_prepare (VasEBoot_command_t cmd __attribute__ ((unused)), int argc __attribute__ ((unused)), char *args[] __attribute__ ((unused))) { return VasEBoot_efiemu_prepare (); } /* Load the runtime from the file FILENAME. */ static VasEBoot_err_t VasEBoot_efiemu_load_file (const char *filename) { VasEBoot_file_t file; VasEBoot_err_t err; file = VasEBoot_file_open (filename); if (! file) return VasEBoot_errno; err = VasEBoot_efiemu_mm_init (); if (err) { VasEBoot_file_close (file); VasEBoot_efiemu_unload (); return err; } VasEBoot_dprintf ("efiemu", "mm initialized\n"); err = VasEBoot_efiemu_loadcore_init (file, filename); if (err) { VasEBoot_file_close (file); VasEBoot_efiemu_unload (); return err; } VasEBoot_file_close (file); /* For configuration tables entry in system table. */ VasEBoot_efiemu_request_symbols (1); return VasEBoot_ERR_NONE; } VasEBoot_err_t VasEBoot_efiemu_autocore (void) { const char *prefix; char *filename; const char *suffix; VasEBoot_err_t err; if (VasEBoot_efiemu_sizeof_uintn_t () != 0) return VasEBoot_ERR_NONE; prefix = VasEBoot_env_get ("prefix"); if (! prefix) return VasEBoot_error (VasEBoot_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "prefix"); suffix = VasEBoot_efiemu_get_default_core_name (); filename = VasEBoot_xasprintf ("%s/" VasEBoot_TARGET_CPU "-" VasEBoot_PLATFORM "/%s", prefix, suffix); if (! filename) return VasEBoot_errno; err = VasEBoot_efiemu_load_file (filename); VasEBoot_free (filename); if (err) return err; #ifndef VasEBoot_MACHINE_EMU err = VasEBoot_machine_efiemu_init_tables (); if (err) return err; #endif return VasEBoot_ERR_NONE; } VasEBoot_err_t VasEBoot_efiemu_prepare (void) { VasEBoot_err_t err; if (prepared) return VasEBoot_ERR_NONE; err = VasEBoot_efiemu_autocore (); if (err) return err; VasEBoot_dprintf ("efiemu", "Preparing %d-bit efiemu\n", 8 * VasEBoot_efiemu_sizeof_uintn_t ()); /* Create NVRAM. */ VasEBoot_efiemu_pnvram (); prepared = 1; if (VasEBoot_efiemu_sizeof_uintn_t () == 4) return VasEBoot_efiemu_prepare32 (efiemu_prepare_hooks, efiemu_config_tables); else return VasEBoot_efiemu_prepare64 (efiemu_prepare_hooks, efiemu_config_tables); } static VasEBoot_err_t VasEBoot_cmd_efiemu_load (VasEBoot_command_t cmd __attribute__ ((unused)), int argc, char *args[]) { VasEBoot_err_t err; VasEBoot_efiemu_unload (); if (argc != 1) return VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT, N_("filename expected")); err = VasEBoot_efiemu_load_file (args[0]); if (err) return err; #ifndef VasEBoot_MACHINE_EMU err = VasEBoot_machine_efiemu_init_tables (); if (err) return err; #endif return VasEBoot_ERR_NONE; } static VasEBoot_command_t cmd_loadcore, cmd_prepare, cmd_unload; VasEBoot_MOD_INIT(efiemu) { cmd_loadcore = VasEBoot_register_command ("efiemu_loadcore", VasEBoot_cmd_efiemu_load, N_("FILE"), N_("Load and initialize EFI emulator.")); cmd_prepare = VasEBoot_register_command ("efiemu_prepare", VasEBoot_cmd_efiemu_prepare, 0, N_("Finalize loading of EFI emulator.")); cmd_unload = VasEBoot_register_command ("efiemu_unload", VasEBoot_cmd_efiemu_unload, 0, N_("Unload EFI emulator.")); } VasEBoot_MOD_FINI(efiemu) { VasEBoot_unregister_command (cmd_loadcore); VasEBoot_unregister_command (cmd_prepare); VasEBoot_unregister_command (cmd_unload); }