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 3104851772SMatt Fleming struct pstore_read_data { 3204851772SMatt Fleming u64 *id; 3304851772SMatt Fleming enum pstore_type_id *type; 3404851772SMatt Fleming int *count; 3504851772SMatt Fleming struct timespec *timespec; 36f8c62f34SAruna Balakrishnaiah bool *compressed; 378cfc8ddcSGeliang Tang ssize_t *ecc_notice_size; 3804851772SMatt Fleming char **buf; 3904851772SMatt Fleming }; 4004851772SMatt Fleming 41fdeadb43SMadper Xie static inline u64 generic_id(unsigned long timestamp, 42fdeadb43SMadper Xie unsigned int part, int count) 43fdeadb43SMadper Xie { 44783ee431SAndrzej Zaborowski return ((u64) timestamp * 100 + part) * 1000 + count; 45fdeadb43SMadper Xie } 46fdeadb43SMadper Xie 4704851772SMatt Fleming static int efi_pstore_read_func(struct efivar_entry *entry, void *data) 4804851772SMatt Fleming { 4904851772SMatt Fleming efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 5004851772SMatt Fleming struct pstore_read_data *cb_data = data; 51f8c62f34SAruna Balakrishnaiah char name[DUMP_NAME_LEN], data_type; 5204851772SMatt Fleming int i; 5304851772SMatt Fleming int cnt; 5404851772SMatt Fleming unsigned int part; 5504851772SMatt Fleming unsigned long time, size; 5604851772SMatt Fleming 5704851772SMatt Fleming if (efi_guidcmp(entry->var.VendorGuid, vendor)) 5804851772SMatt Fleming return 0; 5904851772SMatt Fleming 6004851772SMatt Fleming for (i = 0; i < DUMP_NAME_LEN; i++) 6104851772SMatt Fleming name[i] = entry->var.VariableName[i]; 6204851772SMatt Fleming 63f8c62f34SAruna Balakrishnaiah if (sscanf(name, "dump-type%u-%u-%d-%lu-%c", 64f8c62f34SAruna Balakrishnaiah cb_data->type, &part, &cnt, &time, &data_type) == 5) { 65fdeadb43SMadper Xie *cb_data->id = generic_id(time, part, cnt); 66f8c62f34SAruna Balakrishnaiah *cb_data->count = cnt; 67f8c62f34SAruna Balakrishnaiah cb_data->timespec->tv_sec = time; 68f8c62f34SAruna Balakrishnaiah cb_data->timespec->tv_nsec = 0; 69f8c62f34SAruna Balakrishnaiah if (data_type == 'C') 70f8c62f34SAruna Balakrishnaiah *cb_data->compressed = true; 71f8c62f34SAruna Balakrishnaiah else 72f8c62f34SAruna Balakrishnaiah *cb_data->compressed = false; 738cfc8ddcSGeliang Tang *cb_data->ecc_notice_size = 0; 74f8c62f34SAruna Balakrishnaiah } else if (sscanf(name, "dump-type%u-%u-%d-%lu", 7504851772SMatt Fleming cb_data->type, &part, &cnt, &time) == 4) { 76fdeadb43SMadper Xie *cb_data->id = generic_id(time, part, cnt); 7704851772SMatt Fleming *cb_data->count = cnt; 7804851772SMatt Fleming cb_data->timespec->tv_sec = time; 7904851772SMatt Fleming cb_data->timespec->tv_nsec = 0; 80f8c62f34SAruna Balakrishnaiah *cb_data->compressed = false; 818cfc8ddcSGeliang Tang *cb_data->ecc_notice_size = 0; 8204851772SMatt Fleming } else if (sscanf(name, "dump-type%u-%u-%lu", 8304851772SMatt Fleming cb_data->type, &part, &time) == 3) { 8404851772SMatt Fleming /* 8504851772SMatt Fleming * Check if an old format, 8604851772SMatt Fleming * which doesn't support holding 8704851772SMatt Fleming * multiple logs, remains. 8804851772SMatt Fleming */ 89fdeadb43SMadper Xie *cb_data->id = generic_id(time, part, 0); 9004851772SMatt Fleming *cb_data->count = 0; 9104851772SMatt Fleming cb_data->timespec->tv_sec = time; 9204851772SMatt Fleming cb_data->timespec->tv_nsec = 0; 93f8c62f34SAruna Balakrishnaiah *cb_data->compressed = false; 948cfc8ddcSGeliang Tang *cb_data->ecc_notice_size = 0; 9504851772SMatt Fleming } else 9604851772SMatt Fleming return 0; 9704851772SMatt Fleming 988a415b8cSMatt Fleming entry->var.DataSize = 1024; 998a415b8cSMatt Fleming __efivar_entry_get(entry, &entry->var.Attributes, 1008a415b8cSMatt Fleming &entry->var.DataSize, entry->var.Data); 1018a415b8cSMatt Fleming size = entry->var.DataSize; 102e0d59733SSeiji Aguchi memcpy(*cb_data->buf, entry->var.Data, 103e0d59733SSeiji Aguchi (size_t)min_t(unsigned long, EFIVARS_DATA_SIZE_MAX, size)); 1048a415b8cSMatt Fleming 10504851772SMatt Fleming return size; 10604851772SMatt Fleming } 10704851772SMatt Fleming 108e0d59733SSeiji Aguchi /** 109e0d59733SSeiji Aguchi * efi_pstore_scan_sysfs_enter 110a07e7449SGeliang Tang * @pos: scanning entry 111e0d59733SSeiji Aguchi * @next: next entry 112e0d59733SSeiji Aguchi * @head: list head 113e0d59733SSeiji Aguchi */ 114e0d59733SSeiji Aguchi static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos, 115e0d59733SSeiji Aguchi struct efivar_entry *next, 116e0d59733SSeiji Aguchi struct list_head *head) 117e0d59733SSeiji Aguchi { 118e0d59733SSeiji Aguchi pos->scanning = true; 119e0d59733SSeiji Aguchi if (&next->list != head) 120e0d59733SSeiji Aguchi next->scanning = true; 121e0d59733SSeiji Aguchi } 122e0d59733SSeiji Aguchi 123e0d59733SSeiji Aguchi /** 124e0d59733SSeiji Aguchi * __efi_pstore_scan_sysfs_exit 125e0d59733SSeiji Aguchi * @entry: deleting entry 126e0d59733SSeiji Aguchi * @turn_off_scanning: Check if a scanning flag should be turned off 127e0d59733SSeiji Aguchi */ 128e0d59733SSeiji Aguchi static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry, 129e0d59733SSeiji Aguchi bool turn_off_scanning) 130e0d59733SSeiji Aguchi { 131e0d59733SSeiji Aguchi if (entry->deleting) { 132e0d59733SSeiji Aguchi list_del(&entry->list); 133e0d59733SSeiji Aguchi efivar_entry_iter_end(); 134e0d59733SSeiji Aguchi efivar_unregister(entry); 135e0d59733SSeiji Aguchi efivar_entry_iter_begin(); 136e0d59733SSeiji Aguchi } else if (turn_off_scanning) 137e0d59733SSeiji Aguchi entry->scanning = false; 138e0d59733SSeiji Aguchi } 139e0d59733SSeiji Aguchi 140e0d59733SSeiji Aguchi /** 141e0d59733SSeiji Aguchi * efi_pstore_scan_sysfs_exit 142e0d59733SSeiji Aguchi * @pos: scanning entry 143e0d59733SSeiji Aguchi * @next: next entry 144e0d59733SSeiji Aguchi * @head: list head 145e0d59733SSeiji Aguchi * @stop: a flag checking if scanning will stop 146e0d59733SSeiji Aguchi */ 147e0d59733SSeiji Aguchi static void efi_pstore_scan_sysfs_exit(struct efivar_entry *pos, 148e0d59733SSeiji Aguchi struct efivar_entry *next, 149e0d59733SSeiji Aguchi struct list_head *head, bool stop) 150e0d59733SSeiji Aguchi { 151e0d59733SSeiji Aguchi __efi_pstore_scan_sysfs_exit(pos, true); 152e0d59733SSeiji Aguchi if (stop) 153e0d59733SSeiji Aguchi __efi_pstore_scan_sysfs_exit(next, &next->list != head); 154e0d59733SSeiji Aguchi } 155e0d59733SSeiji Aguchi 156e0d59733SSeiji Aguchi /** 157e0d59733SSeiji Aguchi * efi_pstore_sysfs_entry_iter 158e0d59733SSeiji Aguchi * 159e0d59733SSeiji Aguchi * @data: function-specific data to pass to callback 160e0d59733SSeiji Aguchi * @pos: entry to begin iterating from 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 * It is possible to begin iteration from an arbitrary entry within 166e0d59733SSeiji Aguchi * the list by passing @pos. @pos is updated on return to point to 167e0d59733SSeiji Aguchi * the next entry of the last one passed to efi_pstore_read_func(). 168e0d59733SSeiji Aguchi * To begin iterating from the beginning of the list @pos must be %NULL. 169e0d59733SSeiji Aguchi */ 170e0d59733SSeiji Aguchi static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos) 171e0d59733SSeiji Aguchi { 172e0d59733SSeiji Aguchi struct efivar_entry *entry, *n; 173e0d59733SSeiji Aguchi struct list_head *head = &efivar_sysfs_list; 174e0d59733SSeiji Aguchi int size = 0; 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 180e0d59733SSeiji Aguchi size = efi_pstore_read_func(entry, data); 181e0d59733SSeiji Aguchi efi_pstore_scan_sysfs_exit(entry, n, head, size < 0); 182e0d59733SSeiji Aguchi if (size) 183e0d59733SSeiji Aguchi break; 184e0d59733SSeiji Aguchi } 185e0d59733SSeiji Aguchi *pos = n; 186e0d59733SSeiji Aguchi return size; 187e0d59733SSeiji Aguchi } 188e0d59733SSeiji Aguchi 189e0d59733SSeiji Aguchi list_for_each_entry_safe_from((*pos), n, head, list) { 190e0d59733SSeiji Aguchi efi_pstore_scan_sysfs_enter((*pos), n, head); 191e0d59733SSeiji Aguchi 192e0d59733SSeiji Aguchi size = efi_pstore_read_func((*pos), data); 193e0d59733SSeiji Aguchi efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0); 194e0d59733SSeiji Aguchi if (size) 195e0d59733SSeiji Aguchi break; 196e0d59733SSeiji Aguchi } 197e0d59733SSeiji Aguchi *pos = n; 198e0d59733SSeiji Aguchi return size; 199e0d59733SSeiji Aguchi } 200e0d59733SSeiji Aguchi 201e0d59733SSeiji Aguchi /** 202e0d59733SSeiji Aguchi * efi_pstore_read 203e0d59733SSeiji Aguchi * 204e0d59733SSeiji Aguchi * This function returns a size of NVRAM entry logged via efi_pstore_write(). 205e0d59733SSeiji Aguchi * The meaning and behavior of efi_pstore/pstore are as below. 206e0d59733SSeiji Aguchi * 207e0d59733SSeiji Aguchi * size > 0: Got data of an entry logged via efi_pstore_write() successfully, 208e0d59733SSeiji Aguchi * and pstore filesystem will continue reading subsequent entries. 209e0d59733SSeiji Aguchi * size == 0: Entry was not logged via efi_pstore_write(), 210e0d59733SSeiji Aguchi * and efi_pstore driver will continue reading subsequent entries. 211e0d59733SSeiji Aguchi * size < 0: Failed to get data of entry logging via efi_pstore_write(), 212e0d59733SSeiji Aguchi * and pstore will stop reading entry. 213e0d59733SSeiji Aguchi */ 21404851772SMatt Fleming static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, 21504851772SMatt Fleming int *count, struct timespec *timespec, 2169a4e1398SAruna Balakrishnaiah char **buf, bool *compressed, 2178cfc8ddcSGeliang Tang ssize_t *ecc_notice_size, 2189a4e1398SAruna Balakrishnaiah struct pstore_info *psi) 21904851772SMatt Fleming { 22004851772SMatt Fleming struct pstore_read_data data; 221e0d59733SSeiji Aguchi ssize_t size; 22204851772SMatt Fleming 22304851772SMatt Fleming data.id = id; 22404851772SMatt Fleming data.type = type; 22504851772SMatt Fleming data.count = count; 22604851772SMatt Fleming data.timespec = timespec; 227f8c62f34SAruna Balakrishnaiah data.compressed = compressed; 2288cfc8ddcSGeliang Tang data.ecc_notice_size = ecc_notice_size; 22904851772SMatt Fleming data.buf = buf; 23004851772SMatt Fleming 231e0d59733SSeiji Aguchi *data.buf = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL); 232e0d59733SSeiji Aguchi if (!*data.buf) 233e0d59733SSeiji Aguchi return -ENOMEM; 234e0d59733SSeiji Aguchi 235e0d59733SSeiji Aguchi efivar_entry_iter_begin(); 236e0d59733SSeiji Aguchi size = efi_pstore_sysfs_entry_iter(&data, 23704851772SMatt Fleming (struct efivar_entry **)&psi->data); 238e0d59733SSeiji Aguchi efivar_entry_iter_end(); 239e0d59733SSeiji Aguchi if (size <= 0) 240e0d59733SSeiji Aguchi kfree(*data.buf); 241e0d59733SSeiji Aguchi return size; 24204851772SMatt Fleming } 24304851772SMatt Fleming 24404851772SMatt Fleming static int efi_pstore_write(enum pstore_type_id type, 24504851772SMatt Fleming enum kmsg_dump_reason reason, u64 *id, 246b3b515bbSAruna Balakrishnaiah unsigned int part, int count, bool compressed, size_t size, 24704851772SMatt Fleming struct pstore_info *psi) 24804851772SMatt Fleming { 24904851772SMatt Fleming char name[DUMP_NAME_LEN]; 25004851772SMatt Fleming efi_char16_t efi_name[DUMP_NAME_LEN]; 25104851772SMatt Fleming efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 25204851772SMatt Fleming int i, ret = 0; 25304851772SMatt Fleming 254f8c62f34SAruna Balakrishnaiah sprintf(name, "dump-type%u-%u-%d-%lu-%c", type, part, count, 255f8c62f34SAruna Balakrishnaiah get_seconds(), 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, 26104851772SMatt Fleming !pstore_cannot_block_path(reason), 26204851772SMatt Fleming size, psi->buf); 26304851772SMatt Fleming 26404851772SMatt Fleming if (reason == KMSG_DUMP_OOPS) 26504851772SMatt Fleming efivar_run_worker(); 26604851772SMatt Fleming 26704851772SMatt Fleming *id = part; 26804851772SMatt Fleming return ret; 26904851772SMatt Fleming }; 27004851772SMatt Fleming 27104851772SMatt Fleming struct pstore_erase_data { 27204851772SMatt Fleming u64 id; 27304851772SMatt Fleming enum pstore_type_id type; 27404851772SMatt Fleming int count; 27504851772SMatt Fleming struct timespec time; 27604851772SMatt Fleming efi_char16_t *name; 27704851772SMatt Fleming }; 27804851772SMatt Fleming 27904851772SMatt Fleming /* 28004851772SMatt Fleming * Clean up an entry with the same name 28104851772SMatt Fleming */ 28204851772SMatt Fleming static int efi_pstore_erase_func(struct efivar_entry *entry, void *data) 28304851772SMatt Fleming { 28404851772SMatt Fleming struct pstore_erase_data *ed = data; 28504851772SMatt Fleming efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 28604851772SMatt Fleming efi_char16_t efi_name_old[DUMP_NAME_LEN]; 28704851772SMatt Fleming efi_char16_t *efi_name = ed->name; 288a614e192SMatt Fleming unsigned long ucs2_len = ucs2_strlen(ed->name); 28904851772SMatt Fleming char name_old[DUMP_NAME_LEN]; 29004851772SMatt Fleming int i; 29104851772SMatt Fleming 29204851772SMatt Fleming if (efi_guidcmp(entry->var.VendorGuid, vendor)) 29304851772SMatt Fleming return 0; 29404851772SMatt Fleming 295a614e192SMatt Fleming if (ucs2_strncmp(entry->var.VariableName, 296a614e192SMatt Fleming efi_name, (size_t)ucs2_len)) { 29704851772SMatt Fleming /* 29804851772SMatt Fleming * Check if an old format, which doesn't support 29904851772SMatt Fleming * holding multiple logs, remains. 30004851772SMatt Fleming */ 30104851772SMatt Fleming sprintf(name_old, "dump-type%u-%u-%lu", ed->type, 30204851772SMatt Fleming (unsigned int)ed->id, ed->time.tv_sec); 30304851772SMatt Fleming 30404851772SMatt Fleming for (i = 0; i < DUMP_NAME_LEN; i++) 30504851772SMatt Fleming efi_name_old[i] = name_old[i]; 30604851772SMatt Fleming 307a614e192SMatt Fleming if (ucs2_strncmp(entry->var.VariableName, efi_name_old, 308a614e192SMatt Fleming ucs2_strlen(efi_name_old))) 30904851772SMatt Fleming return 0; 31004851772SMatt Fleming } 31104851772SMatt Fleming 312e0d59733SSeiji Aguchi if (entry->scanning) { 313e0d59733SSeiji Aguchi /* 314e0d59733SSeiji Aguchi * Skip deletion because this entry will be deleted 315e0d59733SSeiji Aguchi * after scanning is completed. 316e0d59733SSeiji Aguchi */ 317e0d59733SSeiji Aguchi entry->deleting = true; 318e0d59733SSeiji Aguchi } else 319e0d59733SSeiji Aguchi list_del(&entry->list); 320e0d59733SSeiji Aguchi 32104851772SMatt Fleming /* found */ 32204851772SMatt Fleming __efivar_entry_delete(entry); 32312abcfdeSMatt Fleming 32404851772SMatt Fleming return 1; 32504851772SMatt Fleming } 32604851772SMatt Fleming 32704851772SMatt Fleming static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, 32804851772SMatt Fleming struct timespec time, struct pstore_info *psi) 32904851772SMatt Fleming { 33004851772SMatt Fleming struct pstore_erase_data edata; 3314ee39e97SMatt Fleming struct efivar_entry *entry = NULL; 33204851772SMatt Fleming char name[DUMP_NAME_LEN]; 33304851772SMatt Fleming efi_char16_t efi_name[DUMP_NAME_LEN]; 33404851772SMatt Fleming int found, i; 335fdeadb43SMadper Xie unsigned int part; 33604851772SMatt Fleming 337fdeadb43SMadper Xie do_div(id, 1000); 338fdeadb43SMadper Xie part = do_div(id, 100); 339fdeadb43SMadper Xie sprintf(name, "dump-type%u-%u-%d-%lu", type, part, count, time.tv_sec); 34004851772SMatt Fleming 34104851772SMatt Fleming for (i = 0; i < DUMP_NAME_LEN; i++) 34204851772SMatt Fleming efi_name[i] = name[i]; 34304851772SMatt Fleming 344fdeadb43SMadper Xie edata.id = part; 34504851772SMatt Fleming edata.type = type; 34604851772SMatt Fleming edata.count = count; 34704851772SMatt Fleming edata.time = time; 34804851772SMatt Fleming edata.name = efi_name; 34904851772SMatt Fleming 35004851772SMatt Fleming efivar_entry_iter_begin(); 35104851772SMatt Fleming found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry); 35204851772SMatt Fleming 353e0d59733SSeiji Aguchi if (found && !entry->scanning) { 354e0d59733SSeiji Aguchi efivar_entry_iter_end(); 35504851772SMatt Fleming efivar_unregister(entry); 356e0d59733SSeiji Aguchi } else 357e0d59733SSeiji Aguchi efivar_entry_iter_end(); 35804851772SMatt Fleming 35904851772SMatt Fleming return 0; 36004851772SMatt Fleming } 36104851772SMatt Fleming 36204851772SMatt Fleming static struct pstore_info efi_pstore_info = { 36304851772SMatt Fleming .owner = THIS_MODULE, 36404851772SMatt Fleming .name = "efi", 365df36ac1bSLuck, Tony .flags = PSTORE_FLAGS_FRAGILE, 36604851772SMatt Fleming .open = efi_pstore_open, 36704851772SMatt Fleming .close = efi_pstore_close, 36804851772SMatt Fleming .read = efi_pstore_read, 36904851772SMatt Fleming .write = efi_pstore_write, 37004851772SMatt Fleming .erase = efi_pstore_erase, 37104851772SMatt Fleming }; 37204851772SMatt Fleming 37304851772SMatt Fleming static __init int efivars_pstore_init(void) 37404851772SMatt Fleming { 37504851772SMatt Fleming if (!efi_enabled(EFI_RUNTIME_SERVICES)) 37604851772SMatt Fleming return 0; 37704851772SMatt Fleming 37804851772SMatt Fleming if (!efivars_kobject()) 37904851772SMatt Fleming return 0; 38004851772SMatt Fleming 38104851772SMatt Fleming if (efivars_pstore_disable) 38204851772SMatt Fleming return 0; 38304851772SMatt Fleming 38404851772SMatt Fleming efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL); 38504851772SMatt Fleming if (!efi_pstore_info.buf) 38604851772SMatt Fleming return -ENOMEM; 38704851772SMatt Fleming 38804851772SMatt Fleming efi_pstore_info.bufsize = 1024; 38904851772SMatt Fleming spin_lock_init(&efi_pstore_info.buf_lock); 39004851772SMatt Fleming 3910d838347SLenny Szubowicz if (pstore_register(&efi_pstore_info)) { 3920d838347SLenny Szubowicz kfree(efi_pstore_info.buf); 3930d838347SLenny Szubowicz efi_pstore_info.buf = NULL; 3940d838347SLenny Szubowicz efi_pstore_info.bufsize = 0; 3950d838347SLenny Szubowicz } 39604851772SMatt Fleming 39704851772SMatt Fleming return 0; 39804851772SMatt Fleming } 39904851772SMatt Fleming 40004851772SMatt Fleming static __exit void efivars_pstore_exit(void) 40104851772SMatt Fleming { 402*cae73167SGeliang Tang if (!efi_pstore_info.bufsize) 403*cae73167SGeliang Tang return; 404*cae73167SGeliang Tang 405*cae73167SGeliang Tang pstore_unregister(&efi_pstore_info); 406*cae73167SGeliang Tang kfree(efi_pstore_info.buf); 407*cae73167SGeliang Tang efi_pstore_info.buf = NULL; 408*cae73167SGeliang Tang efi_pstore_info.bufsize = 0; 40904851772SMatt Fleming } 41004851772SMatt Fleming 41104851772SMatt Fleming module_init(efivars_pstore_init); 41204851772SMatt Fleming module_exit(efivars_pstore_exit); 41304851772SMatt Fleming 41404851772SMatt Fleming MODULE_DESCRIPTION("EFI variable backend for pstore"); 41504851772SMatt Fleming MODULE_LICENSE("GPL"); 4169ac4d5abSBen Hutchings MODULE_ALIAS("platform:efivars"); 417