]>
Commit | Line | Data |
---|---|---|
06f7d4a1 CJ |
1 | /* |
2 | * efibc: control EFI bootloaders which obey LoaderEntryOneShot var | |
3 | * Copyright (c) 2013-2016, Intel Corporation. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms and conditions of the GNU General Public License, | |
7 | * version 2, as published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope it will be useful, but WITHOUT | |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | * more details. | |
13 | */ | |
14 | ||
15 | #define pr_fmt(fmt) "efibc: " fmt | |
16 | ||
17 | #include <linux/efi.h> | |
18 | #include <linux/module.h> | |
19 | #include <linux/reboot.h> | |
2e121d71 | 20 | #include <linux/slab.h> |
06f7d4a1 CJ |
21 | |
22 | static void efibc_str_to_str16(const char *str, efi_char16_t *str16) | |
23 | { | |
24 | size_t i; | |
25 | ||
26 | for (i = 0; i < strlen(str); i++) | |
27 | str16[i] = str[i]; | |
28 | ||
29 | str16[i] = '\0'; | |
30 | } | |
31 | ||
2e121d71 | 32 | static int efibc_set_variable(const char *name, const char *value) |
06f7d4a1 CJ |
33 | { |
34 | int ret; | |
35 | efi_guid_t guid = LINUX_EFI_LOADER_ENTRY_GUID; | |
2e121d71 | 36 | struct efivar_entry *entry; |
06f7d4a1 CJ |
37 | size_t size = (strlen(value) + 1) * sizeof(efi_char16_t); |
38 | ||
2e121d71 | 39 | if (size > sizeof(entry->var.Data)) { |
5356c327 | 40 | pr_err("value is too large (%zu bytes) for '%s' EFI variable\n", size, name); |
2e121d71 JC |
41 | return -EINVAL; |
42 | } | |
06f7d4a1 | 43 | |
2e121d71 JC |
44 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); |
45 | if (!entry) { | |
5356c327 | 46 | pr_err("failed to allocate efivar entry for '%s' EFI variable\n", name); |
2e121d71 JC |
47 | return -ENOMEM; |
48 | } | |
06f7d4a1 | 49 | |
2e121d71 JC |
50 | efibc_str_to_str16(name, entry->var.VariableName); |
51 | efibc_str_to_str16(value, (efi_char16_t *)entry->var.Data); | |
52 | memcpy(&entry->var.VendorGuid, &guid, sizeof(guid)); | |
53 | ||
54 | ret = efivar_entry_set(entry, | |
06f7d4a1 CJ |
55 | EFI_VARIABLE_NON_VOLATILE |
56 | | EFI_VARIABLE_BOOTSERVICE_ACCESS | |
57 | | EFI_VARIABLE_RUNTIME_ACCESS, | |
2e121d71 | 58 | size, entry->var.Data, NULL); |
06f7d4a1 CJ |
59 | if (ret) |
60 | pr_err("failed to set %s EFI variable: 0x%x\n", | |
61 | name, ret); | |
2e121d71 JC |
62 | |
63 | kfree(entry); | |
64 | return ret; | |
06f7d4a1 CJ |
65 | } |
66 | ||
67 | static int efibc_reboot_notifier_call(struct notifier_block *notifier, | |
68 | unsigned long event, void *data) | |
69 | { | |
70 | const char *reason = "shutdown"; | |
2e121d71 | 71 | int ret; |
06f7d4a1 CJ |
72 | |
73 | if (event == SYS_RESTART) | |
74 | reason = "reboot"; | |
75 | ||
2e121d71 JC |
76 | ret = efibc_set_variable("LoaderEntryRebootReason", reason); |
77 | if (ret || !data) | |
06f7d4a1 CJ |
78 | return NOTIFY_DONE; |
79 | ||
80 | efibc_set_variable("LoaderEntryOneShot", (char *)data); | |
81 | ||
82 | return NOTIFY_DONE; | |
83 | } | |
84 | ||
85 | static struct notifier_block efibc_reboot_notifier = { | |
86 | .notifier_call = efibc_reboot_notifier_call, | |
87 | }; | |
88 | ||
89 | static int __init efibc_init(void) | |
90 | { | |
91 | int ret; | |
92 | ||
93 | if (!efi_enabled(EFI_RUNTIME_SERVICES)) | |
94 | return -ENODEV; | |
95 | ||
96 | ret = register_reboot_notifier(&efibc_reboot_notifier); | |
97 | if (ret) | |
98 | pr_err("unable to register reboot notifier\n"); | |
99 | ||
100 | return ret; | |
101 | } | |
102 | module_init(efibc_init); | |
103 | ||
104 | static void __exit efibc_exit(void) | |
105 | { | |
106 | unregister_reboot_notifier(&efibc_reboot_notifier); | |
107 | } | |
108 | module_exit(efibc_exit); | |
109 | ||
110 | MODULE_AUTHOR("Jeremy Compostella <jeremy.compostella@intel.com>"); | |
111 | MODULE_AUTHOR("Matt Gumbel <matthew.k.gumbel@intel.com"); | |
112 | MODULE_DESCRIPTION("EFI Bootloader Control"); | |
113 | MODULE_LICENSE("GPL v2"); |