1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * efibc: control EFI bootloaders which obey LoaderEntryOneShot var 4 * Copyright (c) 2013-2016, Intel Corporation. 5 */ 6 7 #define pr_fmt(fmt) "efibc: " fmt 8 9 #include <linux/efi.h> 10 #include <linux/module.h> 11 #include <linux/reboot.h> 12 #include <linux/slab.h> 13 #include <linux/ucs2_string.h> 14 15 #define MAX_DATA_LEN 512 16 17 static int efibc_set_variable(efi_char16_t *name, efi_char16_t *value, 18 unsigned long len) 19 { 20 efi_status_t status; 21 22 status = efi.set_variable(name, &LINUX_EFI_LOADER_ENTRY_GUID, 23 EFI_VARIABLE_NON_VOLATILE 24 | EFI_VARIABLE_BOOTSERVICE_ACCESS 25 | EFI_VARIABLE_RUNTIME_ACCESS, 26 len * sizeof(efi_char16_t), value); 27 28 if (status != EFI_SUCCESS) { 29 pr_err("failed to set EFI variable: 0x%lx\n", status); 30 return -EIO; 31 } 32 return 0; 33 } 34 35 static int efibc_reboot_notifier_call(struct notifier_block *notifier, 36 unsigned long event, void *data) 37 { 38 efi_char16_t *reason = event == SYS_RESTART ? L"reboot" 39 : L"shutdown"; 40 const u8 *str = data; 41 efi_char16_t *wdata; 42 unsigned long l; 43 int ret; 44 45 ret = efibc_set_variable(L"LoaderEntryRebootReason", reason, 46 ucs2_strlen(reason)); 47 if (ret || !data) 48 return NOTIFY_DONE; 49 50 wdata = kmalloc(MAX_DATA_LEN * sizeof(efi_char16_t), GFP_KERNEL); 51 for (l = 0; l < MAX_DATA_LEN - 1 && str[l] != '\0'; l++) 52 wdata[l] = str[l]; 53 wdata[l] = L'\0'; 54 55 efibc_set_variable(L"LoaderEntryOneShot", wdata, l); 56 57 kfree(wdata); 58 return NOTIFY_DONE; 59 } 60 61 static struct notifier_block efibc_reboot_notifier = { 62 .notifier_call = efibc_reboot_notifier_call, 63 }; 64 65 static int __init efibc_init(void) 66 { 67 int ret; 68 69 if (!efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE)) 70 return -ENODEV; 71 72 ret = register_reboot_notifier(&efibc_reboot_notifier); 73 if (ret) 74 pr_err("unable to register reboot notifier\n"); 75 76 return ret; 77 } 78 module_init(efibc_init); 79 80 static void __exit efibc_exit(void) 81 { 82 unregister_reboot_notifier(&efibc_reboot_notifier); 83 } 84 module_exit(efibc_exit); 85 86 MODULE_AUTHOR("Jeremy Compostella <jeremy.compostella@intel.com>"); 87 MODULE_AUTHOR("Matt Gumbel <matthew.k.gumbel@intel.com"); 88 MODULE_DESCRIPTION("EFI Bootloader Control"); 89 MODULE_LICENSE("GPL v2"); 90