From 2c7cdc59a8d6cb7800c73b90aa75cc8b21807af6 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 24 May 2012 08:37:21 -0400 Subject: [PATCH] Add support for entering the firmware setup screen. This adds a command "fwsetup", with which you can enter your firmware setup screen. The mechanism is to set a global UEFI variable with a specific value and reboot. --- ChangeLog | 8 ++++ grub-core/Makefile.core.def | 6 +++ grub-core/commands/efi/efifwsetup.c | 88 +++++++++++++++++++++++++++++++++++ grub-core/kern/efi/efi.c | 30 ++++++++++++ include/grub/efi/api.h | 2 + include/grub/efi/efi.h | 5 ++ 6 files changed, 139 insertions(+) create mode 100644 grub-core/commands/efi/efifwsetup.c diff --git a/ChangeLog b/ChangeLog index ce52576..29ebcbd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2012-05-24 Peter Jones + + * grub-core/Makefile.core.def: add efifwsetup module + * grub-core/commands/efi/efifwsetup.c: add code for fwsetup command + * grub-core/kern/efi/efi.c (grub_efi_set_variable): New function + * include/grub/efi/api.h: add define for OsIndications variable + * include/grub/efi/efi.h: export grub_efi_set_variable + 2012-05-31 Vladimir Serbinenko * configure.ac: Bump to beta6. diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 000cf0d..d0c06d5 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -576,6 +576,12 @@ module = { }; module = { + name = efifwsetup; + efi = commands/efi/efifwsetup.c; + enable = efi; +}; + +module = { name = blocklist; common = commands/blocklist.c; }; diff --git a/grub-core/commands/efi/efifwsetup.c b/grub-core/commands/efi/efifwsetup.c new file mode 100644 index 0000000..756a14c --- /dev/null +++ b/grub-core/commands/efi/efifwsetup.c @@ -0,0 +1,88 @@ +/* fwsetup.c - Reboot into firmware setup menu. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2012 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 + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t +grub_cmd_fwsetup (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + grub_efi_uint64_t *old_os_indications; + grub_efi_uint64_t os_indications = GRUB_EFI_OS_INDICATIONS_BOOT_TO_FW_UI; + grub_err_t status; + grub_size_t oi_size; + grub_efi_guid_t global = GRUB_EFI_GLOBAL_VARIABLE_GUID; + + old_os_indications = grub_efi_get_variable("OsIndications", &global, + &oi_size); + + if (old_os_indications != NULL && oi_size == sizeof(*old_os_indications)) + os_indications |= *old_os_indications; + + status = grub_efi_set_variable("OsIndications", &global, &os_indications, + sizeof (os_indications)); + if (status != GRUB_ERR_NONE) + return status; + + grub_reboot(); + + return GRUB_ERR_BUG; +} + +static grub_command_t cmd = NULL; + +static grub_efi_boolean_t +efifwsetup_is_supported(void) +{ + grub_efi_uint64_t *os_indications_supported = NULL; + grub_size_t oi_size = 0; + grub_efi_guid_t global = GRUB_EFI_GLOBAL_VARIABLE_GUID; + + os_indications_supported = grub_efi_get_variable("OsIndicationsSupported", + &global, &oi_size); + + if (!os_indications_supported) + return 0; + + if (*os_indications_supported & GRUB_EFI_OS_INDICATIONS_BOOT_TO_FW_UI) + return 1; + + return 0; +} + +GRUB_MOD_INIT(efifwsetup) +{ + if (efifwsetup_is_supported()) + cmd = grub_register_command("fwsetup", grub_cmd_fwsetup, "", + "Reboot into firmware setup menu."); + +} + +GRUB_MOD_FINI(efifwsetup) +{ + if (cmd) + grub_unregister_command (cmd); +} diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index 6f12c76..7a418a6 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -230,6 +230,36 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid, return NULL; } +grub_err_t +grub_efi_set_variable(const char *var, const grub_efi_guid_t *guid, + void *data, grub_size_t datasize) +{ + grub_efi_status_t status; + grub_efi_runtime_services_t *r; + grub_efi_char16_t *var16; + grub_size_t len, len16; + + len = grub_strlen (var); + len16 = len * GRUB_MAX_UTF16_PER_UTF8; + var16 = grub_malloc ((len16 + 1) * sizeof (var16[0])); + if (!var16) + return grub_errno; + len16 = grub_utf8_to_utf16 (var16, len16, (grub_uint8_t *) var, len, NULL); + var16[len16] = 0; + + r = grub_efi_system_table->runtime_services; + + grub_efi_uint32_t attributes = GRUB_EFI_VARIABLE_NON_VOLATILE | + GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS | + GRUB_EFI_VARIABLE_RUNTIME_ACCESS; + + status = efi_call_5 (r->set_variable, var16, guid, attributes, datasize,data); + if (status == GRUB_EFI_SUCCESS) + return GRUB_ERR_NONE; + + return grub_error (GRUB_ERR_IO, "could not set EFI variable `%s'", var); +} + grub_uint64_t grub_rtc_get_time_ms (void) { diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index 26127de..a47a4e3 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -58,6 +58,8 @@ #define GRUB_EFI_OPEN_PROTOCOL_BY_DRIVER 0x00000010 #define GRUB_EFI_OPEN_PROTOCOL_BY_EXCLUSIVE 0x00000020 +#define GRUB_EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL + #define GRUB_EFI_VARIABLE_NON_VOLATILE 0x0000000000000001 #define GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002 #define GRUB_EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004 diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index e67d92b..489cf9e 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -64,6 +64,11 @@ grub_err_t EXPORT_FUNC (grub_efi_set_virtual_address_map) (grub_efi_uintn_t memo void *EXPORT_FUNC (grub_efi_get_variable) (const char *variable, const grub_efi_guid_t *guid, grub_size_t *datasize_out); +grub_err_t +EXPORT_FUNC (grub_efi_set_variable) (const char *var, + const grub_efi_guid_t *guid, + void *data, + grub_size_t datasize); int EXPORT_FUNC (grub_efi_compare_device_paths) (const grub_efi_device_path_t *dp1, const grub_efi_device_path_t *dp2); -- 1.7.10.1