14febfb8dSArd Biesheuvel // SPDX-License-Identifier: GPL-2.0+ 24febfb8dSArd Biesheuvel 304851772SMatt Fleming #include <linux/efi.h> 404851772SMatt Fleming #include <linux/module.h> 504851772SMatt Fleming #include <linux/pstore.h> 620b4fb48SLinus Torvalds #include <linux/slab.h> 7a614e192SMatt Fleming #include <linux/ucs2_string.h> 804851772SMatt Fleming 9efb74e4bSKees Cook #define DUMP_NAME_LEN 66 1004851772SMatt Fleming 11*232f4eb6SArd Biesheuvel #define EFIVARS_DATA_SIZE_MAX 1024 12*232f4eb6SArd Biesheuvel 1304851772SMatt Fleming static bool efivars_pstore_disable = 1404851772SMatt Fleming IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE); 1504851772SMatt Fleming 1604851772SMatt Fleming module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644); 1704851772SMatt Fleming 1804851772SMatt Fleming #define PSTORE_EFI_ATTRIBUTES \ 1904851772SMatt Fleming (EFI_VARIABLE_NON_VOLATILE | \ 2004851772SMatt Fleming EFI_VARIABLE_BOOTSERVICE_ACCESS | \ 2104851772SMatt Fleming EFI_VARIABLE_RUNTIME_ACCESS) 2204851772SMatt Fleming 23*232f4eb6SArd Biesheuvel static LIST_HEAD(efi_pstore_list); 24*232f4eb6SArd Biesheuvel 2504851772SMatt Fleming static int efi_pstore_open(struct pstore_info *psi) 2604851772SMatt Fleming { 2704851772SMatt Fleming psi->data = NULL; 2804851772SMatt Fleming return 0; 2904851772SMatt Fleming } 3004851772SMatt Fleming 3104851772SMatt Fleming static int efi_pstore_close(struct pstore_info *psi) 3204851772SMatt Fleming { 3304851772SMatt Fleming psi->data = NULL; 3404851772SMatt Fleming return 0; 3504851772SMatt Fleming } 3604851772SMatt Fleming 377aaa822eSKees Cook static inline u64 generic_id(u64 timestamp, unsigned int part, int count) 38fdeadb43SMadper Xie { 397aaa822eSKees Cook return (timestamp * 100 + part) * 1000 + count; 40fdeadb43SMadper Xie } 41fdeadb43SMadper Xie 42125cc42bSKees Cook static int efi_pstore_read_func(struct efivar_entry *entry, 43125cc42bSKees Cook struct pstore_record *record) 4404851772SMatt Fleming { 4504851772SMatt Fleming efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 46f8c62f34SAruna Balakrishnaiah char name[DUMP_NAME_LEN], data_type; 4704851772SMatt Fleming int i; 4804851772SMatt Fleming int cnt; 4904851772SMatt Fleming unsigned int part; 507aaa822eSKees Cook unsigned long size; 517aaa822eSKees Cook u64 time; 5204851772SMatt Fleming 5304851772SMatt Fleming if (efi_guidcmp(entry->var.VendorGuid, vendor)) 5404851772SMatt Fleming return 0; 5504851772SMatt Fleming 5604851772SMatt Fleming for (i = 0; i < DUMP_NAME_LEN; i++) 5704851772SMatt Fleming name[i] = entry->var.VariableName[i]; 5804851772SMatt Fleming 597aaa822eSKees Cook if (sscanf(name, "dump-type%u-%u-%d-%llu-%c", 60125cc42bSKees Cook &record->type, &part, &cnt, &time, &data_type) == 5) { 61125cc42bSKees Cook record->id = generic_id(time, part, cnt); 62c10e8031SKees Cook record->part = part; 63125cc42bSKees Cook record->count = cnt; 64125cc42bSKees Cook record->time.tv_sec = time; 65125cc42bSKees Cook record->time.tv_nsec = 0; 66f8c62f34SAruna Balakrishnaiah if (data_type == 'C') 67125cc42bSKees Cook record->compressed = true; 68f8c62f34SAruna Balakrishnaiah else 69125cc42bSKees Cook record->compressed = false; 70125cc42bSKees Cook record->ecc_notice_size = 0; 717aaa822eSKees Cook } else if (sscanf(name, "dump-type%u-%u-%d-%llu", 72125cc42bSKees Cook &record->type, &part, &cnt, &time) == 4) { 73125cc42bSKees Cook record->id = generic_id(time, part, cnt); 74c10e8031SKees Cook record->part = part; 75125cc42bSKees Cook record->count = cnt; 76125cc42bSKees Cook record->time.tv_sec = time; 77125cc42bSKees Cook record->time.tv_nsec = 0; 78125cc42bSKees Cook record->compressed = false; 79125cc42bSKees Cook record->ecc_notice_size = 0; 807aaa822eSKees Cook } else if (sscanf(name, "dump-type%u-%u-%llu", 81125cc42bSKees Cook &record->type, &part, &time) == 3) { 8204851772SMatt Fleming /* 8304851772SMatt Fleming * Check if an old format, 8404851772SMatt Fleming * which doesn't support holding 8504851772SMatt Fleming * multiple logs, remains. 8604851772SMatt Fleming */ 87125cc42bSKees Cook record->id = generic_id(time, part, 0); 88c10e8031SKees Cook record->part = part; 89125cc42bSKees Cook record->count = 0; 90125cc42bSKees Cook record->time.tv_sec = time; 91125cc42bSKees Cook record->time.tv_nsec = 0; 92125cc42bSKees Cook record->compressed = false; 93125cc42bSKees Cook record->ecc_notice_size = 0; 9404851772SMatt Fleming } else 9504851772SMatt Fleming return 0; 9604851772SMatt Fleming 978a415b8cSMatt Fleming entry->var.DataSize = 1024; 988a415b8cSMatt Fleming __efivar_entry_get(entry, &entry->var.Attributes, 998a415b8cSMatt Fleming &entry->var.DataSize, entry->var.Data); 1008a415b8cSMatt Fleming size = entry->var.DataSize; 101125cc42bSKees Cook memcpy(record->buf, entry->var.Data, 102e0d59733SSeiji Aguchi (size_t)min_t(unsigned long, EFIVARS_DATA_SIZE_MAX, size)); 1038a415b8cSMatt Fleming 10404851772SMatt Fleming return size; 10504851772SMatt Fleming } 10604851772SMatt Fleming 107e0d59733SSeiji Aguchi /** 108e0d59733SSeiji Aguchi * efi_pstore_scan_sysfs_enter 109a07e7449SGeliang Tang * @pos: scanning entry 110e0d59733SSeiji Aguchi * @next: next entry 111e0d59733SSeiji Aguchi * @head: list head 112e0d59733SSeiji Aguchi */ 113e0d59733SSeiji Aguchi static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos, 114e0d59733SSeiji Aguchi struct efivar_entry *next, 115e0d59733SSeiji Aguchi struct list_head *head) 116e0d59733SSeiji Aguchi { 117e0d59733SSeiji Aguchi pos->scanning = true; 118e0d59733SSeiji Aguchi if (&next->list != head) 119e0d59733SSeiji Aguchi next->scanning = true; 120e0d59733SSeiji Aguchi } 121e0d59733SSeiji Aguchi 122e0d59733SSeiji Aguchi /** 123e0d59733SSeiji Aguchi * __efi_pstore_scan_sysfs_exit 124e0d59733SSeiji Aguchi * @entry: deleting entry 125e0d59733SSeiji Aguchi * @turn_off_scanning: Check if a scanning flag should be turned off 126e0d59733SSeiji Aguchi */ 12721b3ddd3SSylvain Chouleur static inline int __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry, 128e0d59733SSeiji Aguchi bool turn_off_scanning) 129e0d59733SSeiji Aguchi { 130e0d59733SSeiji Aguchi if (entry->deleting) { 131e0d59733SSeiji Aguchi list_del(&entry->list); 132e0d59733SSeiji Aguchi efivar_entry_iter_end(); 133*232f4eb6SArd Biesheuvel kfree(entry); 13421b3ddd3SSylvain Chouleur if (efivar_entry_iter_begin()) 13521b3ddd3SSylvain Chouleur return -EINTR; 136e0d59733SSeiji Aguchi } else if (turn_off_scanning) 137e0d59733SSeiji Aguchi entry->scanning = false; 13821b3ddd3SSylvain Chouleur 13921b3ddd3SSylvain Chouleur return 0; 140e0d59733SSeiji Aguchi } 141e0d59733SSeiji Aguchi 142e0d59733SSeiji Aguchi /** 143e0d59733SSeiji Aguchi * efi_pstore_scan_sysfs_exit 144e0d59733SSeiji Aguchi * @pos: scanning entry 145e0d59733SSeiji Aguchi * @next: next entry 146e0d59733SSeiji Aguchi * @head: list head 147e0d59733SSeiji Aguchi * @stop: a flag checking if scanning will stop 148e0d59733SSeiji Aguchi */ 14921b3ddd3SSylvain Chouleur static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos, 150e0d59733SSeiji Aguchi struct efivar_entry *next, 151e0d59733SSeiji Aguchi struct list_head *head, bool stop) 152e0d59733SSeiji Aguchi { 15321b3ddd3SSylvain Chouleur int ret = __efi_pstore_scan_sysfs_exit(pos, true); 15421b3ddd3SSylvain Chouleur 15521b3ddd3SSylvain Chouleur if (ret) 15621b3ddd3SSylvain Chouleur return ret; 15721b3ddd3SSylvain Chouleur 158e0d59733SSeiji Aguchi if (stop) 15921b3ddd3SSylvain Chouleur ret = __efi_pstore_scan_sysfs_exit(next, &next->list != head); 16021b3ddd3SSylvain Chouleur return ret; 161e0d59733SSeiji Aguchi } 162e0d59733SSeiji Aguchi 163e0d59733SSeiji Aguchi /** 164e0d59733SSeiji Aguchi * efi_pstore_sysfs_entry_iter 165e0d59733SSeiji Aguchi * 166125cc42bSKees Cook * @record: pstore record to pass to callback 167e0d59733SSeiji Aguchi * 1688d8ab66aSVladis Dronov * You MUST call efivar_entry_iter_begin() before this function, and 169e0d59733SSeiji Aguchi * efivar_entry_iter_end() afterwards. 170e0d59733SSeiji Aguchi * 171e0d59733SSeiji Aguchi */ 1726f61dd3aSKees Cook static int efi_pstore_sysfs_entry_iter(struct pstore_record *record) 173e0d59733SSeiji Aguchi { 1746f61dd3aSKees Cook struct efivar_entry **pos = (struct efivar_entry **)&record->psi->data; 175e0d59733SSeiji Aguchi struct efivar_entry *entry, *n; 176*232f4eb6SArd Biesheuvel struct list_head *head = &efi_pstore_list; 177e0d59733SSeiji Aguchi int size = 0; 17821b3ddd3SSylvain Chouleur int ret; 179e0d59733SSeiji Aguchi 180e0d59733SSeiji Aguchi if (!*pos) { 181e0d59733SSeiji Aguchi list_for_each_entry_safe(entry, n, head, list) { 182e0d59733SSeiji Aguchi efi_pstore_scan_sysfs_enter(entry, n, head); 183e0d59733SSeiji Aguchi 184125cc42bSKees Cook size = efi_pstore_read_func(entry, record); 18521b3ddd3SSylvain Chouleur ret = efi_pstore_scan_sysfs_exit(entry, n, head, 18621b3ddd3SSylvain Chouleur size < 0); 18721b3ddd3SSylvain Chouleur if (ret) 18821b3ddd3SSylvain Chouleur return ret; 189e0d59733SSeiji Aguchi if (size) 190e0d59733SSeiji Aguchi break; 191e0d59733SSeiji Aguchi } 192e0d59733SSeiji Aguchi *pos = n; 193e0d59733SSeiji Aguchi return size; 194e0d59733SSeiji Aguchi } 195e0d59733SSeiji Aguchi 196e0d59733SSeiji Aguchi list_for_each_entry_safe_from((*pos), n, head, list) { 197e0d59733SSeiji Aguchi efi_pstore_scan_sysfs_enter((*pos), n, head); 198e0d59733SSeiji Aguchi 199125cc42bSKees Cook size = efi_pstore_read_func((*pos), record); 20021b3ddd3SSylvain Chouleur ret = efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0); 20121b3ddd3SSylvain Chouleur if (ret) 20221b3ddd3SSylvain Chouleur return ret; 203e0d59733SSeiji Aguchi if (size) 204e0d59733SSeiji Aguchi break; 205e0d59733SSeiji Aguchi } 206e0d59733SSeiji Aguchi *pos = n; 207e0d59733SSeiji Aguchi return size; 208e0d59733SSeiji Aguchi } 209e0d59733SSeiji Aguchi 210e0d59733SSeiji Aguchi /** 211e0d59733SSeiji Aguchi * efi_pstore_read 212e0d59733SSeiji Aguchi * 213e0d59733SSeiji Aguchi * This function returns a size of NVRAM entry logged via efi_pstore_write(). 214e0d59733SSeiji Aguchi * The meaning and behavior of efi_pstore/pstore are as below. 215e0d59733SSeiji Aguchi * 216e0d59733SSeiji Aguchi * size > 0: Got data of an entry logged via efi_pstore_write() successfully, 217e0d59733SSeiji Aguchi * and pstore filesystem will continue reading subsequent entries. 218e0d59733SSeiji Aguchi * size == 0: Entry was not logged via efi_pstore_write(), 219e0d59733SSeiji Aguchi * and efi_pstore driver will continue reading subsequent entries. 220e0d59733SSeiji Aguchi * size < 0: Failed to get data of entry logging via efi_pstore_write(), 221e0d59733SSeiji Aguchi * and pstore will stop reading entry. 222e0d59733SSeiji Aguchi */ 223125cc42bSKees Cook static ssize_t efi_pstore_read(struct pstore_record *record) 22404851772SMatt Fleming { 225e0d59733SSeiji Aguchi ssize_t size; 22604851772SMatt Fleming 227125cc42bSKees Cook record->buf = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL); 228125cc42bSKees Cook if (!record->buf) 229e0d59733SSeiji Aguchi return -ENOMEM; 230e0d59733SSeiji Aguchi 23121b3ddd3SSylvain Chouleur if (efivar_entry_iter_begin()) { 232125cc42bSKees Cook size = -EINTR; 233125cc42bSKees Cook goto out; 23421b3ddd3SSylvain Chouleur } 2356f61dd3aSKees Cook size = efi_pstore_sysfs_entry_iter(record); 236e0d59733SSeiji Aguchi efivar_entry_iter_end(); 237125cc42bSKees Cook 238125cc42bSKees Cook out: 239125cc42bSKees Cook if (size <= 0) { 240125cc42bSKees Cook kfree(record->buf); 241125cc42bSKees Cook record->buf = NULL; 242125cc42bSKees Cook } 243e0d59733SSeiji Aguchi return size; 24404851772SMatt Fleming } 24504851772SMatt Fleming 24676cc9580SKees Cook static int efi_pstore_write(struct pstore_record *record) 24704851772SMatt Fleming { 24804851772SMatt Fleming char name[DUMP_NAME_LEN]; 24904851772SMatt Fleming efi_char16_t efi_name[DUMP_NAME_LEN]; 25004851772SMatt Fleming efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 25104851772SMatt Fleming int i, ret = 0; 25204851772SMatt Fleming 253c10e8031SKees Cook record->id = generic_id(record->time.tv_sec, record->part, 254c10e8031SKees Cook record->count); 255c10e8031SKees Cook 256efb74e4bSKees Cook /* Since we copy the entire length of name, make sure it is wiped. */ 257efb74e4bSKees Cook memset(name, 0, sizeof(name)); 258efb74e4bSKees Cook 2597aaa822eSKees Cook snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lld-%c", 26076cc9580SKees Cook record->type, record->part, record->count, 2617aaa822eSKees Cook (long long)record->time.tv_sec, 2627aaa822eSKees Cook record->compressed ? 'C' : 'D'); 26304851772SMatt Fleming 26404851772SMatt Fleming for (i = 0; i < DUMP_NAME_LEN; i++) 26504851772SMatt Fleming efi_name[i] = name[i]; 26604851772SMatt Fleming 267fee929baSEvgeny Kalugin ret = efivar_entry_set_safe(efi_name, vendor, PSTORE_EFI_ATTRIBUTES, 268ea84b580SKees Cook preemptible(), record->size, record->psi->buf); 26904851772SMatt Fleming 27076cc9580SKees Cook if (record->reason == KMSG_DUMP_OOPS) 27104851772SMatt Fleming efivar_run_worker(); 27204851772SMatt Fleming 27304851772SMatt Fleming return ret; 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 { 281efb74e4bSKees Cook efi_char16_t *efi_name = data; 28204851772SMatt Fleming efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 283efb74e4bSKees Cook unsigned long ucs2_len = ucs2_strlen(efi_name); 28404851772SMatt Fleming 28504851772SMatt Fleming if (efi_guidcmp(entry->var.VendorGuid, vendor)) 28604851772SMatt Fleming return 0; 28704851772SMatt Fleming 288efb74e4bSKees Cook if (ucs2_strncmp(entry->var.VariableName, efi_name, (size_t)ucs2_len)) 28904851772SMatt Fleming return 0; 29004851772SMatt Fleming 291e0d59733SSeiji Aguchi if (entry->scanning) { 292e0d59733SSeiji Aguchi /* 293e0d59733SSeiji Aguchi * Skip deletion because this entry will be deleted 294e0d59733SSeiji Aguchi * after scanning is completed. 295e0d59733SSeiji Aguchi */ 296e0d59733SSeiji Aguchi entry->deleting = true; 297e0d59733SSeiji Aguchi } else 298e0d59733SSeiji Aguchi list_del(&entry->list); 299e0d59733SSeiji Aguchi 30004851772SMatt Fleming /* found */ 30104851772SMatt Fleming __efivar_entry_delete(entry); 30212abcfdeSMatt Fleming 30304851772SMatt Fleming return 1; 30404851772SMatt Fleming } 30504851772SMatt Fleming 306efb74e4bSKees Cook static int efi_pstore_erase_name(const char *name) 30704851772SMatt Fleming { 3084ee39e97SMatt Fleming struct efivar_entry *entry = NULL; 30904851772SMatt Fleming efi_char16_t efi_name[DUMP_NAME_LEN]; 31004851772SMatt Fleming int found, i; 31104851772SMatt Fleming 312efb74e4bSKees Cook for (i = 0; i < DUMP_NAME_LEN; i++) { 313efb74e4bSKees Cook efi_name[i] = name[i]; 314efb74e4bSKees Cook if (name[i] == '\0') 315efb74e4bSKees Cook break; 316efb74e4bSKees Cook } 317efb74e4bSKees Cook 318efb74e4bSKees Cook if (efivar_entry_iter_begin()) 319efb74e4bSKees Cook return -EINTR; 320efb74e4bSKees Cook 321*232f4eb6SArd Biesheuvel found = __efivar_entry_iter(efi_pstore_erase_func, &efi_pstore_list, 322efb74e4bSKees Cook efi_name, &entry); 323efb74e4bSKees Cook efivar_entry_iter_end(); 324efb74e4bSKees Cook 325efb74e4bSKees Cook if (found && !entry->scanning) 326*232f4eb6SArd Biesheuvel kfree(entry); 327efb74e4bSKees Cook 328efb74e4bSKees Cook return found ? 0 : -ENOENT; 329efb74e4bSKees Cook } 330efb74e4bSKees Cook 331efb74e4bSKees Cook static int efi_pstore_erase(struct pstore_record *record) 332efb74e4bSKees Cook { 333efb74e4bSKees Cook char name[DUMP_NAME_LEN]; 334efb74e4bSKees Cook int ret; 335efb74e4bSKees Cook 3367aaa822eSKees Cook snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lld", 337a61072aaSKees Cook record->type, record->part, record->count, 3387aaa822eSKees Cook (long long)record->time.tv_sec); 339efb74e4bSKees Cook ret = efi_pstore_erase_name(name); 340efb74e4bSKees Cook if (ret != -ENOENT) 341efb74e4bSKees Cook return ret; 34204851772SMatt Fleming 3437aaa822eSKees Cook snprintf(name, sizeof(name), "dump-type%u-%u-%lld", 3447aaa822eSKees Cook record->type, record->part, (long long)record->time.tv_sec); 345efb74e4bSKees Cook ret = efi_pstore_erase_name(name); 34604851772SMatt Fleming 347efb74e4bSKees Cook return ret; 34804851772SMatt Fleming } 34904851772SMatt Fleming 35004851772SMatt Fleming static struct pstore_info efi_pstore_info = { 35104851772SMatt Fleming .owner = THIS_MODULE, 35204851772SMatt Fleming .name = "efi", 353c950fd6fSNamhyung Kim .flags = PSTORE_FLAGS_DMESG, 35404851772SMatt Fleming .open = efi_pstore_open, 35504851772SMatt Fleming .close = efi_pstore_close, 35604851772SMatt Fleming .read = efi_pstore_read, 35704851772SMatt Fleming .write = efi_pstore_write, 35804851772SMatt Fleming .erase = efi_pstore_erase, 35904851772SMatt Fleming }; 36004851772SMatt Fleming 361*232f4eb6SArd Biesheuvel static int efi_pstore_callback(efi_char16_t *name, efi_guid_t vendor, 362*232f4eb6SArd Biesheuvel unsigned long name_size, void *data) 363*232f4eb6SArd Biesheuvel { 364*232f4eb6SArd Biesheuvel struct efivar_entry *entry; 365*232f4eb6SArd Biesheuvel int ret; 366*232f4eb6SArd Biesheuvel 367*232f4eb6SArd Biesheuvel entry = kzalloc(sizeof(*entry), GFP_KERNEL); 368*232f4eb6SArd Biesheuvel if (!entry) 369*232f4eb6SArd Biesheuvel return -ENOMEM; 370*232f4eb6SArd Biesheuvel 371*232f4eb6SArd Biesheuvel memcpy(entry->var.VariableName, name, name_size); 372*232f4eb6SArd Biesheuvel entry->var.VendorGuid = vendor; 373*232f4eb6SArd Biesheuvel 374*232f4eb6SArd Biesheuvel ret = efivar_entry_add(entry, &efi_pstore_list); 375*232f4eb6SArd Biesheuvel if (ret) 376*232f4eb6SArd Biesheuvel kfree(entry); 377*232f4eb6SArd Biesheuvel 378*232f4eb6SArd Biesheuvel return ret; 379*232f4eb6SArd Biesheuvel } 380*232f4eb6SArd Biesheuvel 381*232f4eb6SArd Biesheuvel static int efi_pstore_update_entry(efi_char16_t *name, efi_guid_t vendor, 382*232f4eb6SArd Biesheuvel unsigned long name_size, void *data) 383*232f4eb6SArd Biesheuvel { 384*232f4eb6SArd Biesheuvel struct efivar_entry *entry = data; 385*232f4eb6SArd Biesheuvel 386*232f4eb6SArd Biesheuvel if (efivar_entry_find(name, vendor, &efi_pstore_list, false)) 387*232f4eb6SArd Biesheuvel return 0; 388*232f4eb6SArd Biesheuvel 389*232f4eb6SArd Biesheuvel memcpy(entry->var.VariableName, name, name_size); 390*232f4eb6SArd Biesheuvel memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t)); 391*232f4eb6SArd Biesheuvel 392*232f4eb6SArd Biesheuvel return 1; 393*232f4eb6SArd Biesheuvel } 394*232f4eb6SArd Biesheuvel 395*232f4eb6SArd Biesheuvel static void efi_pstore_update_entries(struct work_struct *work) 396*232f4eb6SArd Biesheuvel { 397*232f4eb6SArd Biesheuvel struct efivar_entry *entry; 398*232f4eb6SArd Biesheuvel int err; 399*232f4eb6SArd Biesheuvel 400*232f4eb6SArd Biesheuvel /* Add new sysfs entries */ 401*232f4eb6SArd Biesheuvel while (1) { 402*232f4eb6SArd Biesheuvel entry = kzalloc(sizeof(*entry), GFP_KERNEL); 403*232f4eb6SArd Biesheuvel if (!entry) 404*232f4eb6SArd Biesheuvel return; 405*232f4eb6SArd Biesheuvel 406*232f4eb6SArd Biesheuvel err = efivar_init(efi_pstore_update_entry, entry, 407*232f4eb6SArd Biesheuvel false, &efi_pstore_list); 408*232f4eb6SArd Biesheuvel if (!err) 409*232f4eb6SArd Biesheuvel break; 410*232f4eb6SArd Biesheuvel 411*232f4eb6SArd Biesheuvel efivar_entry_add(entry, &efi_pstore_list); 412*232f4eb6SArd Biesheuvel } 413*232f4eb6SArd Biesheuvel 414*232f4eb6SArd Biesheuvel kfree(entry); 415*232f4eb6SArd Biesheuvel } 416*232f4eb6SArd Biesheuvel 41704851772SMatt Fleming static __init int efivars_pstore_init(void) 41804851772SMatt Fleming { 419*232f4eb6SArd Biesheuvel int ret; 420*232f4eb6SArd Biesheuvel 421f88814ccSArd Biesheuvel if (!efivars_kobject() || !efivar_supports_writes()) 42204851772SMatt Fleming return 0; 42304851772SMatt Fleming 42404851772SMatt Fleming if (efivars_pstore_disable) 42504851772SMatt Fleming return 0; 42604851772SMatt Fleming 427*232f4eb6SArd Biesheuvel ret = efivar_init(efi_pstore_callback, NULL, true, &efi_pstore_list); 428*232f4eb6SArd Biesheuvel if (ret) 429*232f4eb6SArd Biesheuvel return ret; 430*232f4eb6SArd Biesheuvel 43104851772SMatt Fleming efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL); 43204851772SMatt Fleming if (!efi_pstore_info.buf) 43304851772SMatt Fleming return -ENOMEM; 43404851772SMatt Fleming 43504851772SMatt Fleming efi_pstore_info.bufsize = 1024; 43604851772SMatt Fleming 4370d838347SLenny Szubowicz if (pstore_register(&efi_pstore_info)) { 4380d838347SLenny Szubowicz kfree(efi_pstore_info.buf); 4390d838347SLenny Szubowicz efi_pstore_info.buf = NULL; 4400d838347SLenny Szubowicz efi_pstore_info.bufsize = 0; 4410d838347SLenny Szubowicz } 44204851772SMatt Fleming 443*232f4eb6SArd Biesheuvel INIT_WORK(&efivar_work, efi_pstore_update_entries); 444*232f4eb6SArd Biesheuvel 44504851772SMatt Fleming return 0; 44604851772SMatt Fleming } 44704851772SMatt Fleming 44804851772SMatt Fleming static __exit void efivars_pstore_exit(void) 44904851772SMatt Fleming { 450cae73167SGeliang Tang if (!efi_pstore_info.bufsize) 451cae73167SGeliang Tang return; 452cae73167SGeliang Tang 453cae73167SGeliang Tang pstore_unregister(&efi_pstore_info); 454cae73167SGeliang Tang kfree(efi_pstore_info.buf); 455cae73167SGeliang Tang efi_pstore_info.buf = NULL; 456cae73167SGeliang Tang efi_pstore_info.bufsize = 0; 45704851772SMatt Fleming } 45804851772SMatt Fleming 45904851772SMatt Fleming module_init(efivars_pstore_init); 46004851772SMatt Fleming module_exit(efivars_pstore_exit); 46104851772SMatt Fleming 46204851772SMatt Fleming MODULE_DESCRIPTION("EFI variable backend for pstore"); 46304851772SMatt Fleming MODULE_LICENSE("GPL"); 4649ac4d5abSBen Hutchings MODULE_ALIAS("platform:efivars"); 465