| /* |
| * GRUB -- GRand Unified Bootloader |
| * Copyright (C) 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 <http://www.gnu.org/licenses/>. |
| */ |
| |
| /* 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 <grub/file.h> |
| #include <grub/err.h> |
| #include <grub/normal.h> |
| #include <grub/mm.h> |
| #include <grub/dl.h> |
| #include <grub/misc.h> |
| #include <grub/efiemu/efiemu.h> |
| #include <grub/command.h> |
| #include <grub/i18n.h> |
| |
| GRUB_MOD_LICENSE ("GPLv3+"); |
| |
| /* System table. Two version depending on mode */ |
| grub_efi_system_table32_t *grub_efiemu_system_table32 = 0; |
| grub_efi_system_table64_t *grub_efiemu_system_table64 = 0; |
| /* Modules may need to execute some actions after memory allocation happens */ |
| static struct grub_efiemu_prepare_hook *efiemu_prepare_hooks = 0; |
| /* Linked list of configuration tables */ |
| static struct grub_efiemu_configuration_table *efiemu_config_tables = 0; |
| static int prepared = 0; |
| |
| /* Free all allocated space */ |
| grub_err_t |
| grub_efiemu_unload (void) |
| { |
| struct grub_efiemu_configuration_table *cur, *d; |
| struct grub_efiemu_prepare_hook *curhook, *d2; |
| grub_efiemu_loadcore_unload (); |
| |
| grub_efiemu_mm_unload (); |
| |
| for (cur = efiemu_config_tables; cur;) |
| { |
| d = cur->next; |
| if (cur->unload) |
| cur->unload (cur->data); |
| grub_free (cur); |
| cur = d; |
| } |
| efiemu_config_tables = 0; |
| |
| for (curhook = efiemu_prepare_hooks; curhook;) |
| { |
| d2 = curhook->next; |
| if (curhook->unload) |
| curhook->unload (curhook->data); |
| grub_free (curhook); |
| curhook = d2; |
| } |
| efiemu_prepare_hooks = 0; |
| |
| prepared = 0; |
| |
| return GRUB_ERR_NONE; |
| } |
| |
| /* Remove previously registered table from the list */ |
| grub_err_t |
| grub_efiemu_unregister_configuration_table (grub_efi_guid_t guid) |
| { |
| struct grub_efiemu_configuration_table *cur, *prev; |
| |
| /* Special treating if head is to remove */ |
| while (efiemu_config_tables |
| && !grub_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; |
| grub_free (efiemu_config_tables); |
| efiemu_config_tables = cur; |
| } |
| if (!efiemu_config_tables) |
| return GRUB_ERR_NONE; |
| |
| /* Remove from chain */ |
| for (prev = efiemu_config_tables, cur = prev->next; cur;) |
| if (grub_memcmp (&(cur->guid), &guid, sizeof (guid)) == 0) |
| { |
| if (cur->unload) |
| cur->unload (cur->data); |
| prev->next = cur->next; |
| grub_free (cur); |
| cur = prev->next; |
| } |
| else |
| { |
| prev = cur; |
| cur = cur->next; |
| } |
| return GRUB_ERR_NONE; |
| } |
| |
| grub_err_t |
| grub_efiemu_register_prepare_hook (grub_err_t (*hook) (void *data), |
| void (*unload) (void *data), |
| void *data) |
| { |
| struct grub_efiemu_prepare_hook *nhook; |
| nhook = (struct grub_efiemu_prepare_hook *) grub_malloc (sizeof (*nhook)); |
| if (! nhook) |
| return grub_errno; |
| nhook->hook = hook; |
| nhook->unload = unload; |
| nhook->data = data; |
| nhook->next = efiemu_prepare_hooks; |
| efiemu_prepare_hooks = nhook; |
| return GRUB_ERR_NONE; |
| } |
| |
| /* Register a configuration table either supplying the address directly |
| or with a hook |
| */ |
| grub_err_t |
| grub_efiemu_register_configuration_table (grub_efi_guid_t guid, |
| void * (*get_table) (void *data), |
| void (*unload) (void *data), |
| void *data) |
| { |
| struct grub_efiemu_configuration_table *tbl; |
| grub_err_t err; |
| |
| err = grub_efiemu_unregister_configuration_table (guid); |
| if (err) |
| return err; |
| |
| tbl = (struct grub_efiemu_configuration_table *) grub_malloc (sizeof (*tbl)); |
| if (! tbl) |
| return grub_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 GRUB_ERR_NONE; |
| } |
| |
| static grub_err_t |
| grub_cmd_efiemu_unload (grub_command_t cmd __attribute__ ((unused)), |
| int argc __attribute__ ((unused)), |
| char *args[] __attribute__ ((unused))) |
| { |
| return grub_efiemu_unload (); |
| } |
| |
| static grub_err_t |
| grub_cmd_efiemu_prepare (grub_command_t cmd __attribute__ ((unused)), |
| int argc __attribute__ ((unused)), |
| char *args[] __attribute__ ((unused))) |
| { |
| return grub_efiemu_prepare (); |
| } |
| |
| |
| |
| /* Load the runtime from the file FILENAME. */ |
| static grub_err_t |
| grub_efiemu_load_file (const char *filename) |
| { |
| grub_file_t file; |
| grub_err_t err; |
| |
| file = grub_file_open (filename, GRUB_FILE_TYPE_GRUB_MODULE); |
| if (! file) |
| return grub_errno; |
| |
| err = grub_efiemu_mm_init (); |
| if (err) |
| { |
| grub_file_close (file); |
| grub_efiemu_unload (); |
| return err; |
| } |
| |
| grub_dprintf ("efiemu", "mm initialized\n"); |
| |
| err = grub_efiemu_loadcore_init (file, filename); |
| if (err) |
| { |
| grub_file_close (file); |
| grub_efiemu_unload (); |
| return err; |
| } |
| |
| grub_file_close (file); |
| |
| /* For configuration tables entry in system table. */ |
| grub_efiemu_request_symbols (1); |
| |
| return GRUB_ERR_NONE; |
| } |
| |
| grub_err_t |
| grub_efiemu_autocore (void) |
| { |
| const char *prefix; |
| char *filename; |
| const char *suffix; |
| grub_err_t err; |
| |
| if (grub_efiemu_sizeof_uintn_t () != 0) |
| return GRUB_ERR_NONE; |
| |
| prefix = grub_env_get ("prefix"); |
| |
| if (! prefix) |
| return grub_error (GRUB_ERR_FILE_NOT_FOUND, |
| N_("variable `%s' isn't set"), "prefix"); |
| |
| suffix = grub_efiemu_get_default_core_name (); |
| |
| filename = grub_xasprintf ("%s/" GRUB_TARGET_CPU "-" GRUB_PLATFORM "/%s", |
| prefix, suffix); |
| if (! filename) |
| return grub_errno; |
| |
| err = grub_efiemu_load_file (filename); |
| grub_free (filename); |
| if (err) |
| return err; |
| #ifndef GRUB_MACHINE_EMU |
| err = grub_machine_efiemu_init_tables (); |
| if (err) |
| return err; |
| #endif |
| |
| return GRUB_ERR_NONE; |
| } |
| |
| grub_err_t |
| grub_efiemu_prepare (void) |
| { |
| grub_err_t err; |
| |
| if (prepared) |
| return GRUB_ERR_NONE; |
| |
| err = grub_efiemu_autocore (); |
| if (err) |
| return err; |
| |
| grub_dprintf ("efiemu", "Preparing %d-bit efiemu\n", |
| 8 * grub_efiemu_sizeof_uintn_t ()); |
| |
| /* Create NVRAM. */ |
| grub_efiemu_pnvram (); |
| |
| prepared = 1; |
| |
| if (grub_efiemu_sizeof_uintn_t () == 4) |
| return grub_efiemu_prepare32 (efiemu_prepare_hooks, efiemu_config_tables); |
| else |
| return grub_efiemu_prepare64 (efiemu_prepare_hooks, efiemu_config_tables); |
| } |
| |
| |
| static grub_err_t |
| grub_cmd_efiemu_load (grub_command_t cmd __attribute__ ((unused)), |
| int argc, char *args[]) |
| { |
| grub_err_t err; |
| |
| grub_efiemu_unload (); |
| |
| if (argc != 1) |
| return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); |
| |
| err = grub_efiemu_load_file (args[0]); |
| if (err) |
| return err; |
| #ifndef GRUB_MACHINE_EMU |
| err = grub_machine_efiemu_init_tables (); |
| if (err) |
| return err; |
| #endif |
| return GRUB_ERR_NONE; |
| } |
| |
| static grub_command_t cmd_loadcore, cmd_prepare, cmd_unload; |
| |
| GRUB_MOD_INIT(efiemu) |
| { |
| cmd_loadcore = grub_register_command ("efiemu_loadcore", |
| grub_cmd_efiemu_load, |
| N_("FILE"), |
| N_("Load and initialize EFI emulator.")); |
| cmd_prepare = grub_register_command ("efiemu_prepare", |
| grub_cmd_efiemu_prepare, |
| 0, |
| N_("Finalize loading of EFI emulator.")); |
| cmd_unload = grub_register_command ("efiemu_unload", grub_cmd_efiemu_unload, |
| 0, |
| N_("Unload EFI emulator.")); |
| } |
| |
| GRUB_MOD_FINI(efiemu) |
| { |
| grub_unregister_command (cmd_loadcore); |
| grub_unregister_command (cmd_prepare); |
| grub_unregister_command (cmd_unload); |
| } |