Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753272AbcD1KlM (ORCPT ); Thu, 28 Apr 2016 06:41:12 -0400 Received: from terminus.zytor.com ([198.137.202.10]:48008 "EHLO terminus.zytor.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752555AbcD1KlH (ORCPT ); Thu, 28 Apr 2016 06:41:07 -0400 Date: Thu, 28 Apr 2016 03:40:01 -0700 From: "tip-bot for Compostella, Jeremy" Message-ID: Cc: bp@alien8.de, hpa@zytor.com, ard.biesheuvel@linaro.org, matt@codeblueprint.co.uk, stefan.stanacar@intel.com, jeremy.compostella@intel.com, mingo@kernel.org, tglx@linutronix.de, linux-kernel@vger.kernel.org, peterz@infradead.org Reply-To: mingo@kernel.org, jeremy.compostella@intel.com, stefan.stanacar@intel.com, matt@codeblueprint.co.uk, peterz@infradead.org, linux-kernel@vger.kernel.org, tglx@linutronix.de, hpa@zytor.com, bp@alien8.de, ard.biesheuvel@linaro.org In-Reply-To: <1461614832-17633-26-git-send-email-matt@codeblueprint.co.uk> References: <1461614832-17633-26-git-send-email-matt@codeblueprint.co.uk> To: linux-tip-commits@vger.kernel.org Subject: [tip:efi/core] efibc: Add EFI Bootloader Control module Git-Commit-ID: 06f7d4a1618dbb086e738c93cd1ef416ab01027d X-Mailer: tip-git-log-daemon Robot-ID: Robot-Unsubscribe: Contact to get blacklisted from these emails MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset=UTF-8 Content-Disposition: inline Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 6788 Lines: 203 Commit-ID: 06f7d4a1618dbb086e738c93cd1ef416ab01027d Gitweb: http://git.kernel.org/tip/06f7d4a1618dbb086e738c93cd1ef416ab01027d Author: Compostella, Jeremy AuthorDate: Mon, 25 Apr 2016 21:06:57 +0100 Committer: Ingo Molnar CommitDate: Thu, 28 Apr 2016 11:34:02 +0200 efibc: Add EFI Bootloader Control module This module installs a reboot callback, such that if reboot() is invoked with a string argument NNN, "NNN" is copied to the "LoaderEntryOneShot" EFI variable, to be read by the bootloader. If the string matches one of the boot labels defined in its configuration, the bootloader will boot once to that label. The "LoaderEntryRebootReason" EFI variable is set with the reboot reason: "reboot", "shutdown". The bootloader reads this reboot reason and takes particular action according to its policy. There are reboot implementations that do "reboot ", such as Android's reboot command and Upstart's reboot replacement, which pass the reason as an argument to the reboot syscall. There is no platform-agnostic way how those could be modified to pass the reason to the bootloader, regardless of platform or bootloader. Signed-off-by: Jeremy Compostella Signed-off-by: Matt Fleming Cc: Ard Biesheuvel Cc: Borislav Petkov Cc: Peter Zijlstra Cc: Stefan Stanacar Cc: Thomas Gleixner Cc: linux-efi@vger.kernel.org Link: http://lkml.kernel.org/r/1461614832-17633-26-git-send-email-matt@codeblueprint.co.uk Signed-off-by: Ingo Molnar --- drivers/firmware/efi/Kconfig | 15 +++++++ drivers/firmware/efi/Makefile | 1 + drivers/firmware/efi/efibc.c | 101 ++++++++++++++++++++++++++++++++++++++++++ include/linux/efi.h | 4 ++ 4 files changed, 121 insertions(+) diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index e1670d5..0b0b635 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -87,6 +87,21 @@ config EFI_RUNTIME_WRAPPERS config EFI_ARMSTUB bool +config EFI_BOOTLOADER_CONTROL + tristate "EFI Bootloader Control" + depends on EFI_VARS + default n + ---help--- + This module installs a reboot hook, such that if reboot() is + invoked with a string argument NNN, "NNN" is copied to the + "LoaderEntryOneShot" EFI variable, to be read by the + bootloader. If the string matches one of the boot labels + defined in its configuration, the bootloader will boot once + to that label. The "LoaderEntryRebootReason" EFI variable is + set with the reboot reason: "reboot" or "shutdown". The + bootloader reads this reboot reason and takes particular + action according to its policy. + endmenu config UEFI_CPER diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index d5be623..b080808 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o obj-$(CONFIG_EFI_STUB) += libstub/ obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o +obj-$(CONFIG_EFI_BOOTLOADER_CONTROL) += efibc.o arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o obj-$(CONFIG_ARM) += $(arm-obj-y) diff --git a/drivers/firmware/efi/efibc.c b/drivers/firmware/efi/efibc.c new file mode 100644 index 0000000..2e0c7cc --- /dev/null +++ b/drivers/firmware/efi/efibc.c @@ -0,0 +1,101 @@ +/* + * efibc: control EFI bootloaders which obey LoaderEntryOneShot var + * Copyright (c) 2013-2016, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#define pr_fmt(fmt) "efibc: " fmt + +#include +#include +#include + +static void efibc_str_to_str16(const char *str, efi_char16_t *str16) +{ + size_t i; + + for (i = 0; i < strlen(str); i++) + str16[i] = str[i]; + + str16[i] = '\0'; +} + +static void efibc_set_variable(const char *name, const char *value) +{ + int ret; + efi_guid_t guid = LINUX_EFI_LOADER_ENTRY_GUID; + struct efivar_entry entry; + size_t size = (strlen(value) + 1) * sizeof(efi_char16_t); + + if (size > sizeof(entry.var.Data)) + pr_err("value is too large"); + + efibc_str_to_str16(name, entry.var.VariableName); + efibc_str_to_str16(value, (efi_char16_t *)entry.var.Data); + memcpy(&entry.var.VendorGuid, &guid, sizeof(guid)); + + ret = efivar_entry_set(&entry, + EFI_VARIABLE_NON_VOLATILE + | EFI_VARIABLE_BOOTSERVICE_ACCESS + | EFI_VARIABLE_RUNTIME_ACCESS, + size, entry.var.Data, NULL); + if (ret) + pr_err("failed to set %s EFI variable: 0x%x\n", + name, ret); +} + +static int efibc_reboot_notifier_call(struct notifier_block *notifier, + unsigned long event, void *data) +{ + const char *reason = "shutdown"; + + if (event == SYS_RESTART) + reason = "reboot"; + + efibc_set_variable("LoaderEntryRebootReason", reason); + + if (!data) + return NOTIFY_DONE; + + efibc_set_variable("LoaderEntryOneShot", (char *)data); + + return NOTIFY_DONE; +} + +static struct notifier_block efibc_reboot_notifier = { + .notifier_call = efibc_reboot_notifier_call, +}; + +static int __init efibc_init(void) +{ + int ret; + + if (!efi_enabled(EFI_RUNTIME_SERVICES)) + return -ENODEV; + + ret = register_reboot_notifier(&efibc_reboot_notifier); + if (ret) + pr_err("unable to register reboot notifier\n"); + + return ret; +} +module_init(efibc_init); + +static void __exit efibc_exit(void) +{ + unregister_reboot_notifier(&efibc_reboot_notifier); +} +module_exit(efibc_exit); + +MODULE_AUTHOR("Jeremy Compostella "); +MODULE_AUTHOR("Matt Gumbel