104851772SMatt Fleming #include <linux/efi.h> 204851772SMatt Fleming #include <linux/module.h> 304851772SMatt Fleming #include <linux/pstore.h> 420b4fb48SLinus Torvalds #include <linux/slab.h> 5a614e192SMatt Fleming #include <linux/ucs2_string.h> 604851772SMatt Fleming 704851772SMatt Fleming #define DUMP_NAME_LEN 52 804851772SMatt Fleming 904851772SMatt Fleming static bool efivars_pstore_disable = 1004851772SMatt Fleming IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE); 1104851772SMatt Fleming 1204851772SMatt Fleming module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644); 1304851772SMatt Fleming 1404851772SMatt Fleming #define PSTORE_EFI_ATTRIBUTES \ 1504851772SMatt Fleming (EFI_VARIABLE_NON_VOLATILE | \ 1604851772SMatt Fleming EFI_VARIABLE_BOOTSERVICE_ACCESS | \ 1704851772SMatt Fleming EFI_VARIABLE_RUNTIME_ACCESS) 1804851772SMatt Fleming 1904851772SMatt Fleming static int efi_pstore_open(struct pstore_info *psi) 2004851772SMatt Fleming { 2104851772SMatt Fleming psi->data = NULL; 2204851772SMatt Fleming return 0; 2304851772SMatt Fleming } 2404851772SMatt Fleming 2504851772SMatt Fleming static int efi_pstore_close(struct pstore_info *psi) 2604851772SMatt Fleming { 2704851772SMatt Fleming psi->data = NULL; 2804851772SMatt Fleming return 0; 2904851772SMatt Fleming } 3004851772SMatt Fleming 31fdeadb43SMadper Xie static inline u64 generic_id(unsigned long timestamp, 32fdeadb43SMadper Xie unsigned int part, int count) 33fdeadb43SMadper Xie { 34783ee431SAndrzej Zaborowski return ((u64) timestamp * 100 + part) * 1000 + count; 35fdeadb43SMadper Xie } 36fdeadb43SMadper Xie 37125cc42bSKees Cook static int efi_pstore_read_func(struct efivar_entry *entry, 38125cc42bSKees Cook struct pstore_record *record) 3904851772SMatt Fleming { 4004851772SMatt Fleming efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 41f8c62f34SAruna Balakrishnaiah char name[DUMP_NAME_LEN], data_type; 4204851772SMatt Fleming int i; 4304851772SMatt Fleming int cnt; 4404851772SMatt Fleming unsigned int part; 4504851772SMatt Fleming unsigned long time, size; 4604851772SMatt Fleming 4704851772SMatt Fleming if (efi_guidcmp(entry->var.VendorGuid, vendor)) 4804851772SMatt Fleming return 0; 4904851772SMatt Fleming 5004851772SMatt Fleming for (i = 0; i < DUMP_NAME_LEN; i++) 5104851772SMatt Fleming name[i] = entry->var.VariableName[i]; 5204851772SMatt Fleming 53f8c62f34SAruna Balakrishnaiah if (sscanf(name, "dump-type%u-%u-%d-%lu-%c", 54125cc42bSKees Cook &record->type, &part, &cnt, &time, &data_type) == 5) { 55125cc42bSKees Cook record->id = generic_id(time, part, cnt); 56*c10e8031SKees Cook record->part = part; 57125cc42bSKees Cook record->count = cnt; 58125cc42bSKees Cook record->time.tv_sec = time; 59125cc42bSKees Cook record->time.tv_nsec = 0; 60f8c62f34SAruna Balakrishnaiah if (data_type == 'C') 61125cc42bSKees Cook record->compressed = true; 62f8c62f34SAruna Balakrishnaiah else 63125cc42bSKees Cook record->compressed = false; 64125cc42bSKees Cook record->ecc_notice_size = 0; 65f8c62f34SAruna Balakrishnaiah } else if (sscanf(name, "dump-type%u-%u-%d-%lu", 66125cc42bSKees Cook &record->type, &part, &cnt, &time) == 4) { 67125cc42bSKees Cook record->id = generic_id(time, part, cnt); 68*c10e8031SKees Cook record->part = part; 69125cc42bSKees Cook record->count = cnt; 70125cc42bSKees Cook record->time.tv_sec = time; 71125cc42bSKees Cook record->time.tv_nsec = 0; 72125cc42bSKees Cook record->compressed = false; 73125cc42bSKees Cook record->ecc_notice_size = 0; 7404851772SMatt Fleming } else if (sscanf(name, "dump-type%u-%u-%lu", 75125cc42bSKees Cook &record->type, &part, &time) == 3) { 7604851772SMatt Fleming /* 7704851772SMatt Fleming * Check if an old format, 7804851772SMatt Fleming * which doesn't support holding 7904851772SMatt Fleming * multiple logs, remains. 8004851772SMatt Fleming */ 81125cc42bSKees Cook record->id = generic_id(time, part, 0); 82*c10e8031SKees Cook record->part = part; 83125cc42bSKees Cook record->count = 0; 84125cc42bSKees Cook record->time.tv_sec = time; 85125cc42bSKees Cook record->time.tv_nsec = 0; 86125cc42bSKees Cook record->compressed = false; 87125cc42bSKees Cook record->ecc_notice_size = 0; 8804851772SMatt Fleming } else 8904851772SMatt Fleming return 0; 9004851772SMatt Fleming 918a415b8cSMatt Fleming entry->var.DataSize = 1024; 928a415b8cSMatt Fleming __efivar_entry_get(entry, &entry->var.Attributes, 938a415b8cSMatt Fleming &entry->var.DataSize, entry->var.Data); 948a415b8cSMatt Fleming size = entry->var.DataSize; 95125cc42bSKees Cook memcpy(record->buf, entry->var.Data, 96e0d59733SSeiji Aguchi (size_t)min_t(unsigned long, EFIVARS_DATA_SIZE_MAX, size)); 978a415b8cSMatt Fleming 9804851772SMatt Fleming return size; 9904851772SMatt Fleming } 10004851772SMatt Fleming 101e0d59733SSeiji Aguchi /** 102e0d59733SSeiji Aguchi * efi_pstore_scan_sysfs_enter 103a07e7449SGeliang Tang * @pos: scanning entry 104e0d59733SSeiji Aguchi * @next: next entry 105e0d59733SSeiji Aguchi * @head: list head 106e0d59733SSeiji Aguchi */ 107e0d59733SSeiji Aguchi static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos, 108e0d59733SSeiji Aguchi struct efivar_entry *next, 109e0d59733SSeiji Aguchi struct list_head *head) 110e0d59733SSeiji Aguchi { 111e0d59733SSeiji Aguchi pos->scanning = true; 112e0d59733SSeiji Aguchi if (&next->list != head) 113e0d59733SSeiji Aguchi next->scanning = true; 114e0d59733SSeiji Aguchi } 115e0d59733SSeiji Aguchi 116e0d59733SSeiji Aguchi /** 117e0d59733SSeiji Aguchi * __efi_pstore_scan_sysfs_exit 118e0d59733SSeiji Aguchi * @entry: deleting entry 119e0d59733SSeiji Aguchi * @turn_off_scanning: Check if a scanning flag should be turned off 120e0d59733SSeiji Aguchi */ 12121b3ddd3SSylvain Chouleur static inline int __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry, 122e0d59733SSeiji Aguchi bool turn_off_scanning) 123e0d59733SSeiji Aguchi { 124e0d59733SSeiji Aguchi if (entry->deleting) { 125e0d59733SSeiji Aguchi list_del(&entry->list); 126e0d59733SSeiji Aguchi efivar_entry_iter_end(); 127e0d59733SSeiji Aguchi efivar_unregister(entry); 12821b3ddd3SSylvain Chouleur if (efivar_entry_iter_begin()) 12921b3ddd3SSylvain Chouleur return -EINTR; 130e0d59733SSeiji Aguchi } else if (turn_off_scanning) 131e0d59733SSeiji Aguchi entry->scanning = false; 13221b3ddd3SSylvain Chouleur 13321b3ddd3SSylvain Chouleur return 0; 134e0d59733SSeiji Aguchi } 135e0d59733SSeiji Aguchi 136e0d59733SSeiji Aguchi /** 137e0d59733SSeiji Aguchi * efi_pstore_scan_sysfs_exit 138e0d59733SSeiji Aguchi * @pos: scanning entry 139e0d59733SSeiji Aguchi * @next: next entry 140e0d59733SSeiji Aguchi * @head: list head 141e0d59733SSeiji Aguchi * @stop: a flag checking if scanning will stop 142e0d59733SSeiji Aguchi */ 14321b3ddd3SSylvain Chouleur static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos, 144e0d59733SSeiji Aguchi struct efivar_entry *next, 145e0d59733SSeiji Aguchi struct list_head *head, bool stop) 146e0d59733SSeiji Aguchi { 14721b3ddd3SSylvain Chouleur int ret = __efi_pstore_scan_sysfs_exit(pos, true); 14821b3ddd3SSylvain Chouleur 14921b3ddd3SSylvain Chouleur if (ret) 15021b3ddd3SSylvain Chouleur return ret; 15121b3ddd3SSylvain Chouleur 152e0d59733SSeiji Aguchi if (stop) 15321b3ddd3SSylvain Chouleur ret = __efi_pstore_scan_sysfs_exit(next, &next->list != head); 15421b3ddd3SSylvain Chouleur return ret; 155e0d59733SSeiji Aguchi } 156e0d59733SSeiji Aguchi 157e0d59733SSeiji Aguchi /** 158e0d59733SSeiji Aguchi * efi_pstore_sysfs_entry_iter 159e0d59733SSeiji Aguchi * 160125cc42bSKees Cook * @record: pstore record to pass to callback 161e0d59733SSeiji Aguchi * 162e0d59733SSeiji Aguchi * You MUST call efivar_enter_iter_begin() before this function, and 163e0d59733SSeiji Aguchi * efivar_entry_iter_end() afterwards. 164e0d59733SSeiji Aguchi * 165e0d59733SSeiji Aguchi */ 1666f61dd3aSKees Cook static int efi_pstore_sysfs_entry_iter(struct pstore_record *record) 167e0d59733SSeiji Aguchi { 1686f61dd3aSKees Cook struct efivar_entry **pos = (struct efivar_entry **)&record->psi->data; 169e0d59733SSeiji Aguchi struct efivar_entry *entry, *n; 170e0d59733SSeiji Aguchi struct list_head *head = &efivar_sysfs_list; 171e0d59733SSeiji Aguchi int size = 0; 17221b3ddd3SSylvain Chouleur int ret; 173e0d59733SSeiji Aguchi 174e0d59733SSeiji Aguchi if (!*pos) { 175e0d59733SSeiji Aguchi list_for_each_entry_safe(entry, n, head, list) { 176e0d59733SSeiji Aguchi efi_pstore_scan_sysfs_enter(entry, n, head); 177e0d59733SSeiji Aguchi 178125cc42bSKees Cook size = efi_pstore_read_func(entry, record); 17921b3ddd3SSylvain Chouleur ret = efi_pstore_scan_sysfs_exit(entry, n, head, 18021b3ddd3SSylvain Chouleur size < 0); 18121b3ddd3SSylvain Chouleur if (ret) 18221b3ddd3SSylvain Chouleur return ret; 183e0d59733SSeiji Aguchi if (size) 184e0d59733SSeiji Aguchi break; 185e0d59733SSeiji Aguchi } 186e0d59733SSeiji Aguchi *pos = n; 187e0d59733SSeiji Aguchi return size; 188e0d59733SSeiji Aguchi } 189e0d59733SSeiji Aguchi 190e0d59733SSeiji Aguchi list_for_each_entry_safe_from((*pos), n, head, list) { 191e0d59733SSeiji Aguchi efi_pstore_scan_sysfs_enter((*pos), n, head); 192e0d59733SSeiji Aguchi 193125cc42bSKees Cook size = efi_pstore_read_func((*pos), record); 19421b3ddd3SSylvain Chouleur ret = efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0); 19521b3ddd3SSylvain Chouleur if (ret) 19621b3ddd3SSylvain Chouleur return ret; 197e0d59733SSeiji Aguchi if (size) 198e0d59733SSeiji Aguchi break; 199e0d59733SSeiji Aguchi } 200e0d59733SSeiji Aguchi *pos = n; 201e0d59733SSeiji Aguchi return size; 202e0d59733SSeiji Aguchi } 203e0d59733SSeiji Aguchi 204e0d59733SSeiji Aguchi /** 205e0d59733SSeiji Aguchi * efi_pstore_read 206e0d59733SSeiji Aguchi * 207e0d59733SSeiji Aguchi * This function returns a size of NVRAM entry logged via efi_pstore_write(). 208e0d59733SSeiji Aguchi * The meaning and behavior of efi_pstore/pstore are as below. 209e0d59733SSeiji Aguchi * 210e0d59733SSeiji Aguchi * size > 0: Got data of an entry logged via efi_pstore_write() successfully, 211e0d59733SSeiji Aguchi * and pstore filesystem will continue reading subsequent entries. 212e0d59733SSeiji Aguchi * size == 0: Entry was not logged via efi_pstore_write(), 213e0d59733SSeiji Aguchi * and efi_pstore driver will continue reading subsequent entries. 214e0d59733SSeiji Aguchi * size < 0: Failed to get data of entry logging via efi_pstore_write(), 215e0d59733SSeiji Aguchi * and pstore will stop reading entry. 216e0d59733SSeiji Aguchi */ 217125cc42bSKees Cook static ssize_t efi_pstore_read(struct pstore_record *record) 21804851772SMatt Fleming { 219e0d59733SSeiji Aguchi ssize_t size; 22004851772SMatt Fleming 221125cc42bSKees Cook record->buf = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL); 222125cc42bSKees Cook if (!record->buf) 223e0d59733SSeiji Aguchi return -ENOMEM; 224e0d59733SSeiji Aguchi 22521b3ddd3SSylvain Chouleur if (efivar_entry_iter_begin()) { 226125cc42bSKees Cook size = -EINTR; 227125cc42bSKees Cook goto out; 22821b3ddd3SSylvain Chouleur } 2296f61dd3aSKees Cook size = efi_pstore_sysfs_entry_iter(record); 230e0d59733SSeiji Aguchi efivar_entry_iter_end(); 231125cc42bSKees Cook 232125cc42bSKees Cook out: 233125cc42bSKees Cook if (size <= 0) { 234125cc42bSKees Cook kfree(record->buf); 235125cc42bSKees Cook record->buf = NULL; 236125cc42bSKees Cook } 237e0d59733SSeiji Aguchi return size; 23804851772SMatt Fleming } 23904851772SMatt Fleming 24076cc9580SKees Cook static int efi_pstore_write(struct pstore_record *record) 24104851772SMatt Fleming { 24204851772SMatt Fleming char name[DUMP_NAME_LEN]; 24304851772SMatt Fleming efi_char16_t efi_name[DUMP_NAME_LEN]; 24404851772SMatt Fleming efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 24504851772SMatt Fleming int i, ret = 0; 24604851772SMatt Fleming 247*c10e8031SKees Cook record->time.tv_sec = get_seconds(); 248*c10e8031SKees Cook record->time.tv_nsec = 0; 249*c10e8031SKees Cook 250*c10e8031SKees Cook record->id = generic_id(record->time.tv_sec, record->part, 251*c10e8031SKees Cook record->count); 252*c10e8031SKees Cook 25376cc9580SKees Cook snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lu-%c", 25476cc9580SKees Cook record->type, record->part, record->count, 255*c10e8031SKees Cook record->time.tv_sec, record->compressed ? 'C' : 'D'); 25604851772SMatt Fleming 25704851772SMatt Fleming for (i = 0; i < DUMP_NAME_LEN; i++) 25804851772SMatt Fleming efi_name[i] = name[i]; 25904851772SMatt Fleming 26004851772SMatt Fleming efivar_entry_set_safe(efi_name, vendor, PSTORE_EFI_ATTRIBUTES, 26176cc9580SKees Cook !pstore_cannot_block_path(record->reason), 26276cc9580SKees Cook record->size, record->psi->buf); 26304851772SMatt Fleming 26476cc9580SKees Cook if (record->reason == KMSG_DUMP_OOPS) 26504851772SMatt Fleming efivar_run_worker(); 26604851772SMatt Fleming 26704851772SMatt Fleming return ret; 26804851772SMatt Fleming }; 26904851772SMatt Fleming 27004851772SMatt Fleming struct pstore_erase_data { 271a61072aaSKees Cook struct pstore_record *record; 27204851772SMatt Fleming efi_char16_t *name; 27304851772SMatt Fleming }; 27404851772SMatt Fleming 27504851772SMatt Fleming /* 27604851772SMatt Fleming * Clean up an entry with the same name 27704851772SMatt Fleming */ 27804851772SMatt Fleming static int efi_pstore_erase_func(struct efivar_entry *entry, void *data) 27904851772SMatt Fleming { 28004851772SMatt Fleming struct pstore_erase_data *ed = data; 28104851772SMatt Fleming efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 28204851772SMatt Fleming efi_char16_t efi_name_old[DUMP_NAME_LEN]; 28304851772SMatt Fleming efi_char16_t *efi_name = ed->name; 284a614e192SMatt Fleming unsigned long ucs2_len = ucs2_strlen(ed->name); 28504851772SMatt Fleming char name_old[DUMP_NAME_LEN]; 28604851772SMatt Fleming int i; 28704851772SMatt Fleming 28804851772SMatt Fleming if (efi_guidcmp(entry->var.VendorGuid, vendor)) 28904851772SMatt Fleming return 0; 29004851772SMatt Fleming 291a614e192SMatt Fleming if (ucs2_strncmp(entry->var.VariableName, 292a614e192SMatt Fleming efi_name, (size_t)ucs2_len)) { 29304851772SMatt Fleming /* 29404851772SMatt Fleming * Check if an old format, which doesn't support 29504851772SMatt Fleming * holding multiple logs, remains. 29604851772SMatt Fleming */ 297a61072aaSKees Cook snprintf(name_old, sizeof(name_old), "dump-type%u-%u-%lu", 298*c10e8031SKees Cook ed->record->type, ed->record->part, 299a61072aaSKees Cook ed->record->time.tv_sec); 30004851772SMatt Fleming 30104851772SMatt Fleming for (i = 0; i < DUMP_NAME_LEN; i++) 30204851772SMatt Fleming efi_name_old[i] = name_old[i]; 30304851772SMatt Fleming 304a614e192SMatt Fleming if (ucs2_strncmp(entry->var.VariableName, efi_name_old, 305a614e192SMatt Fleming ucs2_strlen(efi_name_old))) 30604851772SMatt Fleming return 0; 30704851772SMatt Fleming } 30804851772SMatt Fleming 309e0d59733SSeiji Aguchi if (entry->scanning) { 310e0d59733SSeiji Aguchi /* 311e0d59733SSeiji Aguchi * Skip deletion because this entry will be deleted 312e0d59733SSeiji Aguchi * after scanning is completed. 313e0d59733SSeiji Aguchi */ 314e0d59733SSeiji Aguchi entry->deleting = true; 315e0d59733SSeiji Aguchi } else 316e0d59733SSeiji Aguchi list_del(&entry->list); 317e0d59733SSeiji Aguchi 31804851772SMatt Fleming /* found */ 31904851772SMatt Fleming __efivar_entry_delete(entry); 32012abcfdeSMatt Fleming 32104851772SMatt Fleming return 1; 32204851772SMatt Fleming } 32304851772SMatt Fleming 324a61072aaSKees Cook static int efi_pstore_erase(struct pstore_record *record) 32504851772SMatt Fleming { 32604851772SMatt Fleming struct pstore_erase_data edata; 3274ee39e97SMatt Fleming struct efivar_entry *entry = NULL; 32804851772SMatt Fleming char name[DUMP_NAME_LEN]; 32904851772SMatt Fleming efi_char16_t efi_name[DUMP_NAME_LEN]; 33004851772SMatt Fleming int found, i; 33104851772SMatt Fleming 332a61072aaSKees Cook snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lu", 333a61072aaSKees Cook record->type, record->part, record->count, 334a61072aaSKees Cook record->time.tv_sec); 33504851772SMatt Fleming 33604851772SMatt Fleming for (i = 0; i < DUMP_NAME_LEN; i++) 33704851772SMatt Fleming efi_name[i] = name[i]; 33804851772SMatt Fleming 339a61072aaSKees Cook edata.record = record; 34004851772SMatt Fleming edata.name = efi_name; 34104851772SMatt Fleming 34221b3ddd3SSylvain Chouleur if (efivar_entry_iter_begin()) 34321b3ddd3SSylvain Chouleur return -EINTR; 34404851772SMatt Fleming found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry); 34504851772SMatt Fleming 346e0d59733SSeiji Aguchi if (found && !entry->scanning) { 347e0d59733SSeiji Aguchi efivar_entry_iter_end(); 34804851772SMatt Fleming efivar_unregister(entry); 349e0d59733SSeiji Aguchi } else 350e0d59733SSeiji Aguchi efivar_entry_iter_end(); 35104851772SMatt Fleming 35204851772SMatt Fleming return 0; 35304851772SMatt Fleming } 35404851772SMatt Fleming 35504851772SMatt Fleming static struct pstore_info efi_pstore_info = { 35604851772SMatt Fleming .owner = THIS_MODULE, 35704851772SMatt Fleming .name = "efi", 358c950fd6fSNamhyung Kim .flags = PSTORE_FLAGS_DMESG, 35904851772SMatt Fleming .open = efi_pstore_open, 36004851772SMatt Fleming .close = efi_pstore_close, 36104851772SMatt Fleming .read = efi_pstore_read, 36204851772SMatt Fleming .write = efi_pstore_write, 36304851772SMatt Fleming .erase = efi_pstore_erase, 36404851772SMatt Fleming }; 36504851772SMatt Fleming 36604851772SMatt Fleming static __init int efivars_pstore_init(void) 36704851772SMatt Fleming { 36804851772SMatt Fleming if (!efi_enabled(EFI_RUNTIME_SERVICES)) 36904851772SMatt Fleming return 0; 37004851772SMatt Fleming 37104851772SMatt Fleming if (!efivars_kobject()) 37204851772SMatt Fleming return 0; 37304851772SMatt Fleming 37404851772SMatt Fleming if (efivars_pstore_disable) 37504851772SMatt Fleming return 0; 37604851772SMatt Fleming 37704851772SMatt Fleming efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL); 37804851772SMatt Fleming if (!efi_pstore_info.buf) 37904851772SMatt Fleming return -ENOMEM; 38004851772SMatt Fleming 38104851772SMatt Fleming efi_pstore_info.bufsize = 1024; 38204851772SMatt Fleming spin_lock_init(&efi_pstore_info.buf_lock); 38304851772SMatt Fleming 3840d838347SLenny Szubowicz if (pstore_register(&efi_pstore_info)) { 3850d838347SLenny Szubowicz kfree(efi_pstore_info.buf); 3860d838347SLenny Szubowicz efi_pstore_info.buf = NULL; 3870d838347SLenny Szubowicz efi_pstore_info.bufsize = 0; 3880d838347SLenny Szubowicz } 38904851772SMatt Fleming 39004851772SMatt Fleming return 0; 39104851772SMatt Fleming } 39204851772SMatt Fleming 39304851772SMatt Fleming static __exit void efivars_pstore_exit(void) 39404851772SMatt Fleming { 395cae73167SGeliang Tang if (!efi_pstore_info.bufsize) 396cae73167SGeliang Tang return; 397cae73167SGeliang Tang 398cae73167SGeliang Tang pstore_unregister(&efi_pstore_info); 399cae73167SGeliang Tang kfree(efi_pstore_info.buf); 400cae73167SGeliang Tang efi_pstore_info.buf = NULL; 401cae73167SGeliang Tang efi_pstore_info.bufsize = 0; 40204851772SMatt Fleming } 40304851772SMatt Fleming 40404851772SMatt Fleming module_init(efivars_pstore_init); 40504851772SMatt Fleming module_exit(efivars_pstore_exit); 40604851772SMatt Fleming 40704851772SMatt Fleming MODULE_DESCRIPTION("EFI variable backend for pstore"); 40804851772SMatt Fleming MODULE_LICENSE("GPL"); 4099ac4d5abSBen Hutchings MODULE_ALIAS("platform:efivars"); 410