xref: /linux/drivers/firmware/efi/efi-pstore.c (revision 232f4eb6393f42f7f9418560ae9228e747fc6faf)
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