1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * efibc: control EFI bootloaders which obey LoaderEntryOneShot var
4*4882a593Smuzhiyun * Copyright (c) 2013-2016, Intel Corporation.
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #define pr_fmt(fmt) "efibc: " fmt
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/efi.h>
10*4882a593Smuzhiyun #include <linux/module.h>
11*4882a593Smuzhiyun #include <linux/reboot.h>
12*4882a593Smuzhiyun #include <linux/slab.h>
13*4882a593Smuzhiyun
efibc_str_to_str16(const char * str,efi_char16_t * str16)14*4882a593Smuzhiyun static void efibc_str_to_str16(const char *str, efi_char16_t *str16)
15*4882a593Smuzhiyun {
16*4882a593Smuzhiyun size_t i;
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun for (i = 0; i < strlen(str); i++)
19*4882a593Smuzhiyun str16[i] = str[i];
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun str16[i] = '\0';
22*4882a593Smuzhiyun }
23*4882a593Smuzhiyun
efibc_set_variable(const char * name,const char * value)24*4882a593Smuzhiyun static int efibc_set_variable(const char *name, const char *value)
25*4882a593Smuzhiyun {
26*4882a593Smuzhiyun int ret;
27*4882a593Smuzhiyun efi_guid_t guid = LINUX_EFI_LOADER_ENTRY_GUID;
28*4882a593Smuzhiyun struct efivar_entry *entry;
29*4882a593Smuzhiyun size_t size = (strlen(value) + 1) * sizeof(efi_char16_t);
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun if (size > sizeof(entry->var.Data)) {
32*4882a593Smuzhiyun pr_err("value is too large (%zu bytes) for '%s' EFI variable\n", size, name);
33*4882a593Smuzhiyun return -EINVAL;
34*4882a593Smuzhiyun }
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun entry = kmalloc(sizeof(*entry), GFP_KERNEL);
37*4882a593Smuzhiyun if (!entry) {
38*4882a593Smuzhiyun pr_err("failed to allocate efivar entry for '%s' EFI variable\n", name);
39*4882a593Smuzhiyun return -ENOMEM;
40*4882a593Smuzhiyun }
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun efibc_str_to_str16(name, entry->var.VariableName);
43*4882a593Smuzhiyun efibc_str_to_str16(value, (efi_char16_t *)entry->var.Data);
44*4882a593Smuzhiyun memcpy(&entry->var.VendorGuid, &guid, sizeof(guid));
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun ret = efivar_entry_set_safe(entry->var.VariableName,
47*4882a593Smuzhiyun entry->var.VendorGuid,
48*4882a593Smuzhiyun EFI_VARIABLE_NON_VOLATILE
49*4882a593Smuzhiyun | EFI_VARIABLE_BOOTSERVICE_ACCESS
50*4882a593Smuzhiyun | EFI_VARIABLE_RUNTIME_ACCESS,
51*4882a593Smuzhiyun false, size, entry->var.Data);
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun if (ret)
54*4882a593Smuzhiyun pr_err("failed to set %s EFI variable: 0x%x\n",
55*4882a593Smuzhiyun name, ret);
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun kfree(entry);
58*4882a593Smuzhiyun return ret;
59*4882a593Smuzhiyun }
60*4882a593Smuzhiyun
efibc_reboot_notifier_call(struct notifier_block * notifier,unsigned long event,void * data)61*4882a593Smuzhiyun static int efibc_reboot_notifier_call(struct notifier_block *notifier,
62*4882a593Smuzhiyun unsigned long event, void *data)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun const char *reason = "shutdown";
65*4882a593Smuzhiyun int ret;
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun if (event == SYS_RESTART)
68*4882a593Smuzhiyun reason = "reboot";
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun ret = efibc_set_variable("LoaderEntryRebootReason", reason);
71*4882a593Smuzhiyun if (ret || !data)
72*4882a593Smuzhiyun return NOTIFY_DONE;
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun efibc_set_variable("LoaderEntryOneShot", (char *)data);
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun return NOTIFY_DONE;
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun static struct notifier_block efibc_reboot_notifier = {
80*4882a593Smuzhiyun .notifier_call = efibc_reboot_notifier_call,
81*4882a593Smuzhiyun };
82*4882a593Smuzhiyun
efibc_init(void)83*4882a593Smuzhiyun static int __init efibc_init(void)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun int ret;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun if (!efivars_kobject() || !efivar_supports_writes())
88*4882a593Smuzhiyun return -ENODEV;
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun ret = register_reboot_notifier(&efibc_reboot_notifier);
91*4882a593Smuzhiyun if (ret)
92*4882a593Smuzhiyun pr_err("unable to register reboot notifier\n");
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun return ret;
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun module_init(efibc_init);
97*4882a593Smuzhiyun
efibc_exit(void)98*4882a593Smuzhiyun static void __exit efibc_exit(void)
99*4882a593Smuzhiyun {
100*4882a593Smuzhiyun unregister_reboot_notifier(&efibc_reboot_notifier);
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun module_exit(efibc_exit);
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun MODULE_AUTHOR("Jeremy Compostella <jeremy.compostella@intel.com>");
105*4882a593Smuzhiyun MODULE_AUTHOR("Matt Gumbel <matthew.k.gumbel@intel.com");
106*4882a593Smuzhiyun MODULE_DESCRIPTION("EFI Bootloader Control");
107*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
108