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 7efb74e4bSKees Cook #define DUMP_NAME_LEN 66 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 31*7aaa822eSKees Cook static inline u64 generic_id(u64 timestamp, unsigned int part, int count) 32fdeadb43SMadper Xie { 33*7aaa822eSKees Cook return (timestamp * 100 + part) * 1000 + count; 34fdeadb43SMadper Xie } 35fdeadb43SMadper Xie 36125cc42bSKees Cook static int efi_pstore_read_func(struct efivar_entry *entry, 37125cc42bSKees Cook struct pstore_record *record) 3804851772SMatt Fleming { 3904851772SMatt Fleming efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 40f8c62f34SAruna Balakrishnaiah char name[DUMP_NAME_LEN], data_type; 4104851772SMatt Fleming int i; 4204851772SMatt Fleming int cnt; 4304851772SMatt Fleming unsigned int part; 44*7aaa822eSKees Cook unsigned long size; 45*7aaa822eSKees Cook u64 time; 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 53*7aaa822eSKees Cook if (sscanf(name, "dump-type%u-%u-%d-%llu-%c", 54125cc42bSKees Cook &record->type, &part, &cnt, &time, &data_type) == 5) { 55125cc42bSKees Cook record->id = generic_id(time, part, cnt); 56c10e8031SKees 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; 65*7aaa822eSKees Cook } else if (sscanf(name, "dump-type%u-%u-%d-%llu", 66125cc42bSKees Cook &record->type, &part, &cnt, &time) == 4) { 67125cc42bSKees Cook record->id = generic_id(time, part, cnt); 68c10e8031SKees 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; 74*7aaa822eSKees Cook } else if (sscanf(name, "dump-type%u-%u-%llu", 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); 82c10e8031SKees 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 247c10e8031SKees Cook record->id = generic_id(record->time.tv_sec, record->part, 248c10e8031SKees Cook record->count); 249c10e8031SKees Cook 250efb74e4bSKees Cook /* Since we copy the entire length of name, make sure it is wiped. */ 251efb74e4bSKees Cook memset(name, 0, sizeof(name)); 252efb74e4bSKees Cook 253*7aaa822eSKees Cook snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lld-%c", 25476cc9580SKees Cook record->type, record->part, record->count, 255*7aaa822eSKees Cook (long long)record->time.tv_sec, 256*7aaa822eSKees Cook record->compressed ? 'C' : 'D'); 25704851772SMatt Fleming 25804851772SMatt Fleming for (i = 0; i < DUMP_NAME_LEN; i++) 25904851772SMatt Fleming efi_name[i] = name[i]; 26004851772SMatt Fleming 261fee929baSEvgeny Kalugin ret = efivar_entry_set_safe(efi_name, vendor, PSTORE_EFI_ATTRIBUTES, 26276cc9580SKees Cook !pstore_cannot_block_path(record->reason), 26376cc9580SKees Cook record->size, record->psi->buf); 26404851772SMatt Fleming 26576cc9580SKees Cook if (record->reason == KMSG_DUMP_OOPS) 26604851772SMatt Fleming efivar_run_worker(); 26704851772SMatt Fleming 26804851772SMatt Fleming return ret; 26904851772SMatt Fleming }; 27004851772SMatt Fleming 27104851772SMatt Fleming /* 27204851772SMatt Fleming * Clean up an entry with the same name 27304851772SMatt Fleming */ 27404851772SMatt Fleming static int efi_pstore_erase_func(struct efivar_entry *entry, void *data) 27504851772SMatt Fleming { 276efb74e4bSKees Cook efi_char16_t *efi_name = data; 27704851772SMatt Fleming efi_guid_t vendor = LINUX_EFI_CRASH_GUID; 278efb74e4bSKees Cook unsigned long ucs2_len = ucs2_strlen(efi_name); 27904851772SMatt Fleming 28004851772SMatt Fleming if (efi_guidcmp(entry->var.VendorGuid, vendor)) 28104851772SMatt Fleming return 0; 28204851772SMatt Fleming 283efb74e4bSKees Cook if (ucs2_strncmp(entry->var.VariableName, efi_name, (size_t)ucs2_len)) 28404851772SMatt Fleming return 0; 28504851772SMatt Fleming 286e0d59733SSeiji Aguchi if (entry->scanning) { 287e0d59733SSeiji Aguchi /* 288e0d59733SSeiji Aguchi * Skip deletion because this entry will be deleted 289e0d59733SSeiji Aguchi * after scanning is completed. 290e0d59733SSeiji Aguchi */ 291e0d59733SSeiji Aguchi entry->deleting = true; 292e0d59733SSeiji Aguchi } else 293e0d59733SSeiji Aguchi list_del(&entry->list); 294e0d59733SSeiji Aguchi 29504851772SMatt Fleming /* found */ 29604851772SMatt Fleming __efivar_entry_delete(entry); 29712abcfdeSMatt Fleming 29804851772SMatt Fleming return 1; 29904851772SMatt Fleming } 30004851772SMatt Fleming 301efb74e4bSKees Cook static int efi_pstore_erase_name(const char *name) 30204851772SMatt Fleming { 3034ee39e97SMatt Fleming struct efivar_entry *entry = NULL; 30404851772SMatt Fleming efi_char16_t efi_name[DUMP_NAME_LEN]; 30504851772SMatt Fleming int found, i; 30604851772SMatt Fleming 307efb74e4bSKees Cook for (i = 0; i < DUMP_NAME_LEN; i++) { 308efb74e4bSKees Cook efi_name[i] = name[i]; 309efb74e4bSKees Cook if (name[i] == '\0') 310efb74e4bSKees Cook break; 311efb74e4bSKees Cook } 312efb74e4bSKees Cook 313efb74e4bSKees Cook if (efivar_entry_iter_begin()) 314efb74e4bSKees Cook return -EINTR; 315efb74e4bSKees Cook 316efb74e4bSKees Cook found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, 317efb74e4bSKees Cook efi_name, &entry); 318efb74e4bSKees Cook efivar_entry_iter_end(); 319efb74e4bSKees Cook 320efb74e4bSKees Cook if (found && !entry->scanning) 321efb74e4bSKees Cook efivar_unregister(entry); 322efb74e4bSKees Cook 323efb74e4bSKees Cook return found ? 0 : -ENOENT; 324efb74e4bSKees Cook } 325efb74e4bSKees Cook 326efb74e4bSKees Cook static int efi_pstore_erase(struct pstore_record *record) 327efb74e4bSKees Cook { 328efb74e4bSKees Cook char name[DUMP_NAME_LEN]; 329efb74e4bSKees Cook int ret; 330efb74e4bSKees Cook 331*7aaa822eSKees Cook snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lld", 332a61072aaSKees Cook record->type, record->part, record->count, 333*7aaa822eSKees Cook (long long)record->time.tv_sec); 334efb74e4bSKees Cook ret = efi_pstore_erase_name(name); 335efb74e4bSKees Cook if (ret != -ENOENT) 336efb74e4bSKees Cook return ret; 33704851772SMatt Fleming 338*7aaa822eSKees Cook snprintf(name, sizeof(name), "dump-type%u-%u-%lld", 339*7aaa822eSKees Cook record->type, record->part, (long long)record->time.tv_sec); 340efb74e4bSKees Cook ret = efi_pstore_erase_name(name); 34104851772SMatt Fleming 342efb74e4bSKees Cook return ret; 34304851772SMatt Fleming } 34404851772SMatt Fleming 34504851772SMatt Fleming static struct pstore_info efi_pstore_info = { 34604851772SMatt Fleming .owner = THIS_MODULE, 34704851772SMatt Fleming .name = "efi", 348c950fd6fSNamhyung Kim .flags = PSTORE_FLAGS_DMESG, 34904851772SMatt Fleming .open = efi_pstore_open, 35004851772SMatt Fleming .close = efi_pstore_close, 35104851772SMatt Fleming .read = efi_pstore_read, 35204851772SMatt Fleming .write = efi_pstore_write, 35304851772SMatt Fleming .erase = efi_pstore_erase, 35404851772SMatt Fleming }; 35504851772SMatt Fleming 35604851772SMatt Fleming static __init int efivars_pstore_init(void) 35704851772SMatt Fleming { 35804851772SMatt Fleming if (!efi_enabled(EFI_RUNTIME_SERVICES)) 35904851772SMatt Fleming return 0; 36004851772SMatt Fleming 36104851772SMatt Fleming if (!efivars_kobject()) 36204851772SMatt Fleming return 0; 36304851772SMatt Fleming 36404851772SMatt Fleming if (efivars_pstore_disable) 36504851772SMatt Fleming return 0; 36604851772SMatt Fleming 36704851772SMatt Fleming efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL); 36804851772SMatt Fleming if (!efi_pstore_info.buf) 36904851772SMatt Fleming return -ENOMEM; 37004851772SMatt Fleming 37104851772SMatt Fleming efi_pstore_info.bufsize = 1024; 37204851772SMatt Fleming spin_lock_init(&efi_pstore_info.buf_lock); 37304851772SMatt Fleming 3740d838347SLenny Szubowicz if (pstore_register(&efi_pstore_info)) { 3750d838347SLenny Szubowicz kfree(efi_pstore_info.buf); 3760d838347SLenny Szubowicz efi_pstore_info.buf = NULL; 3770d838347SLenny Szubowicz efi_pstore_info.bufsize = 0; 3780d838347SLenny Szubowicz } 37904851772SMatt Fleming 38004851772SMatt Fleming return 0; 38104851772SMatt Fleming } 38204851772SMatt Fleming 38304851772SMatt Fleming static __exit void efivars_pstore_exit(void) 38404851772SMatt Fleming { 385cae73167SGeliang Tang if (!efi_pstore_info.bufsize) 386cae73167SGeliang Tang return; 387cae73167SGeliang Tang 388cae73167SGeliang Tang pstore_unregister(&efi_pstore_info); 389cae73167SGeliang Tang kfree(efi_pstore_info.buf); 390cae73167SGeliang Tang efi_pstore_info.buf = NULL; 391cae73167SGeliang Tang efi_pstore_info.bufsize = 0; 39204851772SMatt Fleming } 39304851772SMatt Fleming 39404851772SMatt Fleming module_init(efivars_pstore_init); 39504851772SMatt Fleming module_exit(efivars_pstore_exit); 39604851772SMatt Fleming 39704851772SMatt Fleming MODULE_DESCRIPTION("EFI variable backend for pstore"); 39804851772SMatt Fleming MODULE_LICENSE("GPL"); 3999ac4d5abSBen Hutchings MODULE_ALIAS("platform:efivars"); 400