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 if (!wdata) 52 return NOTIFY_DONE; 53 54 for (l = 0; l < MAX_DATA_LEN - 1 && str[l] != '\0'; l++) 55 wdata[l] = str[l]; 56 wdata[l] = L'\0'; 57 58 efibc_set_variable(L"LoaderEntryOneShot", wdata, l); 59 60 kfree(wdata); 61 return NOTIFY_DONE; 62 } 63 64 static struct notifier_block efibc_reboot_notifier = { 65 .notifier_call = efibc_reboot_notifier_call, 66 }; 67 68 static int __init efibc_init(void) 69 { 70 int ret; 71 72 if (!efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE)) 73 return -ENODEV; 74 75 ret = register_reboot_notifier(&efibc_reboot_notifier); 76 if (ret) 77 pr_err("unable to register reboot notifier\n"); 78 79 return ret; 80 } 81 module_init(efibc_init); 82 83 static void __exit efibc_exit(void) 84 { 85 unregister_reboot_notifier(&efibc_reboot_notifier); 86 } 87 module_exit(efibc_exit); 88 89 MODULE_AUTHOR("Jeremy Compostella <jeremy.compostella@intel.com>"); 90 MODULE_AUTHOR("Matt Gumbel <matthew.k.gumbel@intel.com"); 91 MODULE_DESCRIPTION("EFI Bootloader Control"); 92 MODULE_LICENSE("GPL v2"); 93