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); 56125cc42bSKees Cook record->count = cnt; 57125cc42bSKees Cook record->time.tv_sec = time; 58125cc42bSKees Cook record->time.tv_nsec = 0; 59f8c62f34SAruna Balakrishnaiah if (data_type == 'C') 60125cc42bSKees Cook record->compressed = true; 61f8c62f34SAruna Balakrishnaiah else 62125cc42bSKees Cook record->compressed = false; 63125cc42bSKees Cook record->ecc_notice_size = 0; 64f8c62f34SAruna Balakrishnaiah } else if (sscanf(name, "dump-type%u-%u-%d-%lu", 65125cc42bSKees Cook &record->type, &part, &cnt, &time) == 4) { 66125cc42bSKees Cook record->id = generic_id(time, part, cnt); 67125cc42bSKees Cook record->count = cnt; 68125cc42bSKees Cook record->time.tv_sec = time; 69125cc42bSKees Cook record->time.tv_nsec = 0; 70125cc42bSKees Cook record->compressed = false; 71125cc42bSKees Cook record->ecc_notice_size = 0; 7204851772SMatt Fleming } else if (sscanf(name, "dump-type%u-%u-%lu", 73125cc42bSKees Cook &record->type, &part, &time) == 3) { 7404851772SMatt Fleming /* 7504851772SMatt Fleming * Check if an old format, 7604851772SMatt Fleming * which doesn't support holding 7704851772SMatt Fleming * multiple logs, remains. 7804851772SMatt Fleming */ 79125cc42bSKees Cook record->id = generic_id(time, part, 0); 80125cc42bSKees Cook record->count = 0; 81125cc42bSKees Cook record->time.tv_sec = time; 82125cc42bSKees Cook record->time.tv_nsec = 0; 83125cc42bSKees Cook record->compressed = false; 84125cc42bSKees Cook record->ecc_notice_size = 0; 8504851772SMatt Fleming } else 8604851772SMatt Fleming return 0; 8704851772SMatt Fleming 888a415b8cSMatt Fleming entry->var.DataSize = 1024; 898a415b8cSMatt Fleming __efivar_entry_get(entry, &entry->var.Attributes, 908a415b8cSMatt Fleming &entry->var.DataSize, entry->var.Data); 918a415b8cSMatt Fleming size = entry->var.DataSize; 92125cc42bSKees Cook memcpy(record->buf, entry->var.Data, 93e0d59733SSeiji Aguchi (size_t)min_t(unsigned long, EFIVARS_DATA_SIZE_MAX, size)); 948a415b8cSMatt Fleming 9504851772SMatt Fleming return size; 9604851772SMatt Fleming } 9704851772SMatt Fleming 98e0d59733SSeiji Aguchi /** 99e0d59733SSeiji Aguchi * efi_pstore_scan_sysfs_enter 100a07e7449SGeliang Tang * @pos: scanning entry 101e0d59733SSeiji Aguchi * @next: next entry 102e0d59733SSeiji Aguchi * @head: list head 103e0d59733SSeiji Aguchi */ 104e0d59733SSeiji Aguchi static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos, 105e0d59733SSeiji Aguchi struct efivar_entry *next, 106e0d59733SSeiji Aguchi struct list_head *head) 107e0d59733SSeiji Aguchi { 108e0d59733SSeiji Aguchi pos->scanning = true; 109e0d59733SSeiji Aguchi if (&next->list != head) 110e0d59733SSeiji Aguchi next->scanning = true; 111e0d59733SSeiji Aguchi } 112e0d59733SSeiji Aguchi 113e0d59733SSeiji Aguchi /** 114e0d59733SSeiji Aguchi * __efi_pstore_scan_sysfs_exit 115e0d59733SSeiji Aguchi * @entry: deleting entry 116e0d59733SSeiji Aguchi * @turn_off_scanning: Check if a scanning flag should be turned off 117e0d59733SSeiji Aguchi */ 11821b3ddd3SSylvain Chouleur static inline int __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry, 119e0d59733SSeiji Aguchi bool turn_off_scanning) 120e0d59733SSeiji Aguchi { 121e0d59733SSeiji Aguchi if (entry->deleting) { 122e0d59733SSeiji Aguchi list_del(&entry->list); 123e0d59733SSeiji Aguchi efivar_entry_iter_end(); 124e0d59733SSeiji Aguchi efivar_unregister(entry); 12521b3ddd3SSylvain Chouleur if (efivar_entry_iter_begin()) 12621b3ddd3SSylvain Chouleur return -EINTR; 127e0d59733SSeiji Aguchi } else if (turn_off_scanning) 128e0d59733SSeiji Aguchi entry->scanning = false; 12921b3ddd3SSylvain Chouleur 13021b3ddd3SSylvain Chouleur return 0; 131e0d59733SSeiji Aguchi } 132e0d59733SSeiji Aguchi 133e0d59733SSeiji Aguchi /** 134e0d59733SSeiji Aguchi * efi_pstore_scan_sysfs_exit 135e0d59733SSeiji Aguchi * @pos: scanning entry 136e0d59733SSeiji Aguchi * @next: next entry 137e0d59733SSeiji Aguchi * @head: list head 138e0d59733SSeiji Aguchi * @stop: a flag checking if scanning will stop 139e0d59733SSeiji Aguchi */ 14021b3ddd3SSylvain Chouleur static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos, 141e0d59733SSeiji Aguchi struct efivar_entry *next, 142e0d59733SSeiji Aguchi struct list_head *head, bool stop) 143e0d59733SSeiji Aguchi { 14421b3ddd3SSylvain Chouleur int ret = __efi_pstore_scan_sysfs_exit(pos, true); 14521b3ddd3SSylvain Chouleur 14621b3ddd3SSylvain Chouleur if (ret) 14721b3ddd3SSylvain Chouleur return ret; 14821b3ddd3SSylvain Chouleur 149e0d59733SSeiji Aguchi if (stop) 15021b3ddd3SSylvain Chouleur ret = __efi_pstore_scan_sysfs_exit(next, &next->list != head); 15121b3ddd3SSylvain Chouleur return ret; 152e0d59733SSeiji Aguchi } 153e0d59733SSeiji Aguchi 154e0d59733SSeiji Aguchi /** 155e0d59733SSeiji Aguchi * efi_pstore_sysfs_entry_iter 156e0d59733SSeiji Aguchi * 157125cc42bSKees Cook * @record: pstore record to pass to callback 158e0d59733SSeiji Aguchi * @pos: entry to begin iterating from 159e0d59733SSeiji Aguchi * 160e0d59733SSeiji Aguchi * You MUST call efivar_enter_iter_begin() before this function, and 161e0d59733SSeiji Aguchi * efivar_entry_iter_end() afterwards. 162e0d59733SSeiji Aguchi * 163e0d59733SSeiji Aguchi * It is possible to begin iteration from an arbitrary entry within 164e0d59733SSeiji Aguchi * the list by passing @pos. @pos is updated on return to point to 165e0d59733SSeiji Aguchi * the next entry of the last one passed to efi_pstore_read_func(). 166e0d59733SSeiji Aguchi * To begin iterating from the beginning of the list @pos must be %NULL. 167e0d59733SSeiji Aguchi */ 168125cc42bSKees Cook static int efi_pstore_sysfs_entry_iter(struct pstore_record *record, 169125cc42bSKees Cook struct efivar_entry **pos) 170e0d59733SSeiji Aguchi { 171e0d59733SSeiji Aguchi struct efivar_entry *entry, *n; 172e0d59733SSeiji Aguchi struct list_head *head = &efivar_sysfs_list; 173e0d59733SSeiji Aguchi int size = 0; 17421b3ddd3SSylvain Chouleur int ret; 175e0d59733SSeiji Aguchi 176e0d59733SSeiji Aguchi if (!*pos) { 177e0d59733SSeiji Aguchi list_for_each_entry_safe(entry, n, head, list) { 178e0d59733SSeiji Aguchi efi_pstore_scan_sysfs_enter(entry, n, head); 179e0d59733SSeiji Aguchi 180125cc42bSKees Cook size = efi_pstore_read_func(entry, record); 18121b3ddd3SSylvain Chouleur ret = efi_pstore_scan_sysfs_exit(entry, n, head, 18221b3ddd3SSylvain Chouleur size < 0); 18321b3ddd3SSylvain Chouleur if (ret) 18421b3ddd3SSylvain Chouleur return ret; 185e0d59733SSeiji Aguchi if (size) 186e0d59733SSeiji Aguchi break; 187e0d59733SSeiji Aguchi } 188e0d59733SSeiji Aguchi *pos = n; 189e0d59733SSeiji Aguchi return size; 190e0d59733SSeiji Aguchi } 191e0d59733SSeiji Aguchi 192e0d59733SSeiji Aguchi list_for_each_entry_safe_from((*pos), n, head, list) { 193e0d59733SSeiji Aguchi efi_pstore_scan_sysfs_enter((*pos), n, head); 194e0d59733SSeiji Aguchi 195125cc42bSKees Cook size = efi_pstore_read_func((*pos), record); 19621b3ddd3SSylvain Chouleur ret = efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0); 19721b3ddd3SSylvain Chouleur if (ret) 19821b3ddd3SSylvain Chouleur return ret; 199e0d59733SSeiji Aguchi if (size) 200e0d59733SSeiji Aguchi break; 201e0d59733SSeiji Aguchi } 202e0d59733SSeiji Aguchi *pos = n; 203e0d59733SSeiji Aguchi return size; 204e0d59733SSeiji Aguchi } 205e0d59733SSeiji Aguchi 206e0d59733SSeiji Aguchi /** 207e0d59733SSeiji Aguchi * efi_pstore_read 208e0d59733SSeiji Aguchi * 209e0d59733SSeiji Aguchi * This function returns a size of NVRAM entry logged via efi_pstore_write(). 210e0d59733SSeiji Aguchi * The meaning and behavior of efi_pstore/pstore are as below. 211e0d59733SSeiji Aguchi * 212e0d59733SSeiji Aguchi * size > 0: Got data of an entry logged via efi_pstore_write() successfully, 213e0d59733SSeiji Aguchi * and pstore filesystem will continue reading subsequent entries. 214e0d59733SSeiji Aguchi * size == 0: Entry was not logged via efi_pstore_write(), 215e0d59733SSeiji Aguchi * and efi_pstore driver will continue reading subsequent entries. 216e0d59733SSeiji Aguchi * size < 0: Failed to get data of entry logging via efi_pstore_write(), 217e0d59733SSeiji Aguchi * and pstore will stop reading entry. 218e0d59733SSeiji Aguchi */ 219125cc42bSKees Cook static ssize_t efi_pstore_read(struct pstore_record *record) 22004851772SMatt Fleming { 221125cc42bSKees Cook struct efivar_entry *entry = (struct efivar_entry *)record->psi->data; 222e0d59733SSeiji Aguchi ssize_t size; 22304851772SMatt Fleming 224125cc42bSKees Cook record->buf = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL); 225125cc42bSKees Cook if (!record->buf) 226e0d59733SSeiji Aguchi return -ENOMEM; 227e0d59733SSeiji Aguchi 22821b3ddd3SSylvain Chouleur if (efivar_entry_iter_begin()) { 229125cc42bSKees Cook size = -EINTR; 230125cc42bSKees Cook goto out; 23121b3ddd3SSylvain Chouleur } 232125cc42bSKees Cook size = efi_pstore_sysfs_entry_iter(record, &entry); 233e0d59733SSeiji Aguchi efivar_entry_iter_end(); 234125cc42bSKees Cook 235125cc42bSKees Cook out: 236125cc42bSKees Cook if (size <= 0) { 237125cc42bSKees Cook kfree(record->buf); 238125cc42bSKees Cook record->buf = NULL; 239125cc42bSKees Cook } 240e0d59733SSeiji Aguchi return size; 24104851772SMatt Fleming } 24204851772SMatt Fleming 243*76cc9580SKees Cook static int efi_pstore_write(struct pstore_record *record) 24404851772SMatt Fleming { 24504851772SMatt Fleming char name[DUMP_NAME_LEN]; 24604851772SMatt Fleming efi_char16_t efi_name[DUMP_NAME_LEN]; 24704851772SMatt Fleming efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 24804851772SMatt Fleming int i, ret = 0; 24904851772SMatt Fleming 250*76cc9580SKees Cook snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lu-%c", 251*76cc9580SKees Cook record->type, record->part, record->count, 252*76cc9580SKees Cook get_seconds(), record->compressed ? 'C' : 'D'); 25304851772SMatt Fleming 25404851772SMatt Fleming for (i = 0; i < DUMP_NAME_LEN; i++) 25504851772SMatt Fleming efi_name[i] = name[i]; 25604851772SMatt Fleming 25704851772SMatt Fleming efivar_entry_set_safe(efi_name, vendor, PSTORE_EFI_ATTRIBUTES, 258*76cc9580SKees Cook !pstore_cannot_block_path(record->reason), 259*76cc9580SKees Cook record->size, record->psi->buf); 26004851772SMatt Fleming 261*76cc9580SKees Cook if (record->reason == KMSG_DUMP_OOPS) 26204851772SMatt Fleming efivar_run_worker(); 26304851772SMatt Fleming 264*76cc9580SKees Cook record->id = record->part; 26504851772SMatt Fleming return ret; 26604851772SMatt Fleming }; 26704851772SMatt Fleming 26804851772SMatt Fleming struct pstore_erase_data { 26904851772SMatt Fleming u64 id; 27004851772SMatt Fleming enum pstore_type_id type; 27104851772SMatt Fleming int count; 27204851772SMatt Fleming struct timespec time; 27304851772SMatt Fleming efi_char16_t *name; 27404851772SMatt Fleming }; 27504851772SMatt Fleming 27604851772SMatt Fleming /* 27704851772SMatt Fleming * Clean up an entry with the same name 27804851772SMatt Fleming */ 27904851772SMatt Fleming static int efi_pstore_erase_func(struct efivar_entry *entry, void *data) 28004851772SMatt Fleming { 28104851772SMatt Fleming struct pstore_erase_data *ed = data; 28204851772SMatt Fleming efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 28304851772SMatt Fleming efi_char16_t efi_name_old[DUMP_NAME_LEN]; 28404851772SMatt Fleming efi_char16_t *efi_name = ed->name; 285a614e192SMatt Fleming unsigned long ucs2_len = ucs2_strlen(ed->name); 28604851772SMatt Fleming char name_old[DUMP_NAME_LEN]; 28704851772SMatt Fleming int i; 28804851772SMatt Fleming 28904851772SMatt Fleming if (efi_guidcmp(entry->var.VendorGuid, vendor)) 29004851772SMatt Fleming return 0; 29104851772SMatt Fleming 292a614e192SMatt Fleming if (ucs2_strncmp(entry->var.VariableName, 293a614e192SMatt Fleming efi_name, (size_t)ucs2_len)) { 29404851772SMatt Fleming /* 29504851772SMatt Fleming * Check if an old format, which doesn't support 29604851772SMatt Fleming * holding multiple logs, remains. 29704851772SMatt Fleming */ 29804851772SMatt Fleming sprintf(name_old, "dump-type%u-%u-%lu", ed->type, 29904851772SMatt Fleming (unsigned int)ed->id, ed->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 32404851772SMatt Fleming static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, 32504851772SMatt Fleming struct timespec time, struct pstore_info *psi) 32604851772SMatt Fleming { 32704851772SMatt Fleming struct pstore_erase_data edata; 3284ee39e97SMatt Fleming struct efivar_entry *entry = NULL; 32904851772SMatt Fleming char name[DUMP_NAME_LEN]; 33004851772SMatt Fleming efi_char16_t efi_name[DUMP_NAME_LEN]; 33104851772SMatt Fleming int found, i; 332fdeadb43SMadper Xie unsigned int part; 33304851772SMatt Fleming 334fdeadb43SMadper Xie do_div(id, 1000); 335fdeadb43SMadper Xie part = do_div(id, 100); 336fdeadb43SMadper Xie sprintf(name, "dump-type%u-%u-%d-%lu", type, part, count, time.tv_sec); 33704851772SMatt Fleming 33804851772SMatt Fleming for (i = 0; i < DUMP_NAME_LEN; i++) 33904851772SMatt Fleming efi_name[i] = name[i]; 34004851772SMatt Fleming 341fdeadb43SMadper Xie edata.id = part; 34204851772SMatt Fleming edata.type = type; 34304851772SMatt Fleming edata.count = count; 34404851772SMatt Fleming edata.time = time; 34504851772SMatt Fleming edata.name = efi_name; 34604851772SMatt Fleming 34721b3ddd3SSylvain Chouleur if (efivar_entry_iter_begin()) 34821b3ddd3SSylvain Chouleur return -EINTR; 34904851772SMatt Fleming found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry); 35004851772SMatt Fleming 351e0d59733SSeiji Aguchi if (found && !entry->scanning) { 352e0d59733SSeiji Aguchi efivar_entry_iter_end(); 35304851772SMatt Fleming efivar_unregister(entry); 354e0d59733SSeiji Aguchi } else 355e0d59733SSeiji Aguchi efivar_entry_iter_end(); 35604851772SMatt Fleming 35704851772SMatt Fleming return 0; 35804851772SMatt Fleming } 35904851772SMatt Fleming 36004851772SMatt Fleming static struct pstore_info efi_pstore_info = { 36104851772SMatt Fleming .owner = THIS_MODULE, 36204851772SMatt Fleming .name = "efi", 363c950fd6fSNamhyung Kim .flags = PSTORE_FLAGS_DMESG, 36404851772SMatt Fleming .open = efi_pstore_open, 36504851772SMatt Fleming .close = efi_pstore_close, 36604851772SMatt Fleming .read = efi_pstore_read, 36704851772SMatt Fleming .write = efi_pstore_write, 36804851772SMatt Fleming .erase = efi_pstore_erase, 36904851772SMatt Fleming }; 37004851772SMatt Fleming 37104851772SMatt Fleming static __init int efivars_pstore_init(void) 37204851772SMatt Fleming { 37304851772SMatt Fleming if (!efi_enabled(EFI_RUNTIME_SERVICES)) 37404851772SMatt Fleming return 0; 37504851772SMatt Fleming 37604851772SMatt Fleming if (!efivars_kobject()) 37704851772SMatt Fleming return 0; 37804851772SMatt Fleming 37904851772SMatt Fleming if (efivars_pstore_disable) 38004851772SMatt Fleming return 0; 38104851772SMatt Fleming 38204851772SMatt Fleming efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL); 38304851772SMatt Fleming if (!efi_pstore_info.buf) 38404851772SMatt Fleming return -ENOMEM; 38504851772SMatt Fleming 38604851772SMatt Fleming efi_pstore_info.bufsize = 1024; 38704851772SMatt Fleming spin_lock_init(&efi_pstore_info.buf_lock); 38804851772SMatt Fleming 3890d838347SLenny Szubowicz if (pstore_register(&efi_pstore_info)) { 3900d838347SLenny Szubowicz kfree(efi_pstore_info.buf); 3910d838347SLenny Szubowicz efi_pstore_info.buf = NULL; 3920d838347SLenny Szubowicz efi_pstore_info.bufsize = 0; 3930d838347SLenny Szubowicz } 39404851772SMatt Fleming 39504851772SMatt Fleming return 0; 39604851772SMatt Fleming } 39704851772SMatt Fleming 39804851772SMatt Fleming static __exit void efivars_pstore_exit(void) 39904851772SMatt Fleming { 400cae73167SGeliang Tang if (!efi_pstore_info.bufsize) 401cae73167SGeliang Tang return; 402cae73167SGeliang Tang 403cae73167SGeliang Tang pstore_unregister(&efi_pstore_info); 404cae73167SGeliang Tang kfree(efi_pstore_info.buf); 405cae73167SGeliang Tang efi_pstore_info.buf = NULL; 406cae73167SGeliang Tang efi_pstore_info.bufsize = 0; 40704851772SMatt Fleming } 40804851772SMatt Fleming 40904851772SMatt Fleming module_init(efivars_pstore_init); 41004851772SMatt Fleming module_exit(efivars_pstore_exit); 41104851772SMatt Fleming 41204851772SMatt Fleming MODULE_DESCRIPTION("EFI variable backend for pstore"); 41304851772SMatt Fleming MODULE_LICENSE("GPL"); 4149ac4d5abSBen Hutchings MODULE_ALIAS("platform:efivars"); 415