xref: /OK3568_Linux_fs/kernel/drivers/firmware/efi/efi-pstore.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun 
3*4882a593Smuzhiyun #include <linux/efi.h>
4*4882a593Smuzhiyun #include <linux/module.h>
5*4882a593Smuzhiyun #include <linux/pstore.h>
6*4882a593Smuzhiyun #include <linux/slab.h>
7*4882a593Smuzhiyun #include <linux/ucs2_string.h>
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #define DUMP_NAME_LEN 66
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #define EFIVARS_DATA_SIZE_MAX 1024
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun static bool efivars_pstore_disable =
14*4882a593Smuzhiyun 	IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE);
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644);
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #define PSTORE_EFI_ATTRIBUTES \
19*4882a593Smuzhiyun 	(EFI_VARIABLE_NON_VOLATILE | \
20*4882a593Smuzhiyun 	 EFI_VARIABLE_BOOTSERVICE_ACCESS | \
21*4882a593Smuzhiyun 	 EFI_VARIABLE_RUNTIME_ACCESS)
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun static LIST_HEAD(efi_pstore_list);
24*4882a593Smuzhiyun static DECLARE_WORK(efivar_work, NULL);
25*4882a593Smuzhiyun 
efi_pstore_open(struct pstore_info * psi)26*4882a593Smuzhiyun static int efi_pstore_open(struct pstore_info *psi)
27*4882a593Smuzhiyun {
28*4882a593Smuzhiyun 	psi->data = NULL;
29*4882a593Smuzhiyun 	return 0;
30*4882a593Smuzhiyun }
31*4882a593Smuzhiyun 
efi_pstore_close(struct pstore_info * psi)32*4882a593Smuzhiyun static int efi_pstore_close(struct pstore_info *psi)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun 	psi->data = NULL;
35*4882a593Smuzhiyun 	return 0;
36*4882a593Smuzhiyun }
37*4882a593Smuzhiyun 
generic_id(u64 timestamp,unsigned int part,int count)38*4882a593Smuzhiyun static inline u64 generic_id(u64 timestamp, unsigned int part, int count)
39*4882a593Smuzhiyun {
40*4882a593Smuzhiyun 	return (timestamp * 100 + part) * 1000 + count;
41*4882a593Smuzhiyun }
42*4882a593Smuzhiyun 
efi_pstore_read_func(struct efivar_entry * entry,struct pstore_record * record)43*4882a593Smuzhiyun static int efi_pstore_read_func(struct efivar_entry *entry,
44*4882a593Smuzhiyun 				struct pstore_record *record)
45*4882a593Smuzhiyun {
46*4882a593Smuzhiyun 	efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
47*4882a593Smuzhiyun 	char name[DUMP_NAME_LEN], data_type;
48*4882a593Smuzhiyun 	int i;
49*4882a593Smuzhiyun 	int cnt;
50*4882a593Smuzhiyun 	unsigned int part;
51*4882a593Smuzhiyun 	unsigned long size;
52*4882a593Smuzhiyun 	u64 time;
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun 	if (efi_guidcmp(entry->var.VendorGuid, vendor))
55*4882a593Smuzhiyun 		return 0;
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 	for (i = 0; i < DUMP_NAME_LEN; i++)
58*4882a593Smuzhiyun 		name[i] = entry->var.VariableName[i];
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	if (sscanf(name, "dump-type%u-%u-%d-%llu-%c",
61*4882a593Smuzhiyun 		   &record->type, &part, &cnt, &time, &data_type) == 5) {
62*4882a593Smuzhiyun 		record->id = generic_id(time, part, cnt);
63*4882a593Smuzhiyun 		record->part = part;
64*4882a593Smuzhiyun 		record->count = cnt;
65*4882a593Smuzhiyun 		record->time.tv_sec = time;
66*4882a593Smuzhiyun 		record->time.tv_nsec = 0;
67*4882a593Smuzhiyun 		if (data_type == 'C')
68*4882a593Smuzhiyun 			record->compressed = true;
69*4882a593Smuzhiyun 		else
70*4882a593Smuzhiyun 			record->compressed = false;
71*4882a593Smuzhiyun 		record->ecc_notice_size = 0;
72*4882a593Smuzhiyun 	} else if (sscanf(name, "dump-type%u-%u-%d-%llu",
73*4882a593Smuzhiyun 		   &record->type, &part, &cnt, &time) == 4) {
74*4882a593Smuzhiyun 		record->id = generic_id(time, part, cnt);
75*4882a593Smuzhiyun 		record->part = part;
76*4882a593Smuzhiyun 		record->count = cnt;
77*4882a593Smuzhiyun 		record->time.tv_sec = time;
78*4882a593Smuzhiyun 		record->time.tv_nsec = 0;
79*4882a593Smuzhiyun 		record->compressed = false;
80*4882a593Smuzhiyun 		record->ecc_notice_size = 0;
81*4882a593Smuzhiyun 	} else if (sscanf(name, "dump-type%u-%u-%llu",
82*4882a593Smuzhiyun 			  &record->type, &part, &time) == 3) {
83*4882a593Smuzhiyun 		/*
84*4882a593Smuzhiyun 		 * Check if an old format,
85*4882a593Smuzhiyun 		 * which doesn't support holding
86*4882a593Smuzhiyun 		 * multiple logs, remains.
87*4882a593Smuzhiyun 		 */
88*4882a593Smuzhiyun 		record->id = generic_id(time, part, 0);
89*4882a593Smuzhiyun 		record->part = part;
90*4882a593Smuzhiyun 		record->count = 0;
91*4882a593Smuzhiyun 		record->time.tv_sec = time;
92*4882a593Smuzhiyun 		record->time.tv_nsec = 0;
93*4882a593Smuzhiyun 		record->compressed = false;
94*4882a593Smuzhiyun 		record->ecc_notice_size = 0;
95*4882a593Smuzhiyun 	} else
96*4882a593Smuzhiyun 		return 0;
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	entry->var.DataSize = 1024;
99*4882a593Smuzhiyun 	__efivar_entry_get(entry, &entry->var.Attributes,
100*4882a593Smuzhiyun 			   &entry->var.DataSize, entry->var.Data);
101*4882a593Smuzhiyun 	size = entry->var.DataSize;
102*4882a593Smuzhiyun 	memcpy(record->buf, entry->var.Data,
103*4882a593Smuzhiyun 	       (size_t)min_t(unsigned long, EFIVARS_DATA_SIZE_MAX, size));
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	return size;
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun /**
109*4882a593Smuzhiyun  * efi_pstore_scan_sysfs_enter
110*4882a593Smuzhiyun  * @pos: scanning entry
111*4882a593Smuzhiyun  * @next: next entry
112*4882a593Smuzhiyun  * @head: list head
113*4882a593Smuzhiyun  */
efi_pstore_scan_sysfs_enter(struct efivar_entry * pos,struct efivar_entry * next,struct list_head * head)114*4882a593Smuzhiyun static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos,
115*4882a593Smuzhiyun 					struct efivar_entry *next,
116*4882a593Smuzhiyun 					struct list_head *head)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun 	pos->scanning = true;
119*4882a593Smuzhiyun 	if (&next->list != head)
120*4882a593Smuzhiyun 		next->scanning = true;
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun /**
124*4882a593Smuzhiyun  * __efi_pstore_scan_sysfs_exit
125*4882a593Smuzhiyun  * @entry: deleting entry
126*4882a593Smuzhiyun  * @turn_off_scanning: Check if a scanning flag should be turned off
127*4882a593Smuzhiyun  */
__efi_pstore_scan_sysfs_exit(struct efivar_entry * entry,bool turn_off_scanning)128*4882a593Smuzhiyun static inline int __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
129*4882a593Smuzhiyun 						bool turn_off_scanning)
130*4882a593Smuzhiyun {
131*4882a593Smuzhiyun 	if (entry->deleting) {
132*4882a593Smuzhiyun 		list_del(&entry->list);
133*4882a593Smuzhiyun 		efivar_entry_iter_end();
134*4882a593Smuzhiyun 		kfree(entry);
135*4882a593Smuzhiyun 		if (efivar_entry_iter_begin())
136*4882a593Smuzhiyun 			return -EINTR;
137*4882a593Smuzhiyun 	} else if (turn_off_scanning)
138*4882a593Smuzhiyun 		entry->scanning = false;
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	return 0;
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun /**
144*4882a593Smuzhiyun  * efi_pstore_scan_sysfs_exit
145*4882a593Smuzhiyun  * @pos: scanning entry
146*4882a593Smuzhiyun  * @next: next entry
147*4882a593Smuzhiyun  * @head: list head
148*4882a593Smuzhiyun  * @stop: a flag checking if scanning will stop
149*4882a593Smuzhiyun  */
efi_pstore_scan_sysfs_exit(struct efivar_entry * pos,struct efivar_entry * next,struct list_head * head,bool stop)150*4882a593Smuzhiyun static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
151*4882a593Smuzhiyun 				       struct efivar_entry *next,
152*4882a593Smuzhiyun 				       struct list_head *head, bool stop)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun 	int ret = __efi_pstore_scan_sysfs_exit(pos, true);
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	if (ret)
157*4882a593Smuzhiyun 		return ret;
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	if (stop)
160*4882a593Smuzhiyun 		ret = __efi_pstore_scan_sysfs_exit(next, &next->list != head);
161*4882a593Smuzhiyun 	return ret;
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun /**
165*4882a593Smuzhiyun  * efi_pstore_sysfs_entry_iter
166*4882a593Smuzhiyun  *
167*4882a593Smuzhiyun  * @record: pstore record to pass to callback
168*4882a593Smuzhiyun  *
169*4882a593Smuzhiyun  * You MUST call efivar_entry_iter_begin() before this function, and
170*4882a593Smuzhiyun  * efivar_entry_iter_end() afterwards.
171*4882a593Smuzhiyun  *
172*4882a593Smuzhiyun  */
efi_pstore_sysfs_entry_iter(struct pstore_record * record)173*4882a593Smuzhiyun static int efi_pstore_sysfs_entry_iter(struct pstore_record *record)
174*4882a593Smuzhiyun {
175*4882a593Smuzhiyun 	struct efivar_entry **pos = (struct efivar_entry **)&record->psi->data;
176*4882a593Smuzhiyun 	struct efivar_entry *entry, *n;
177*4882a593Smuzhiyun 	struct list_head *head = &efi_pstore_list;
178*4882a593Smuzhiyun 	int size = 0;
179*4882a593Smuzhiyun 	int ret;
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	if (!*pos) {
182*4882a593Smuzhiyun 		list_for_each_entry_safe(entry, n, head, list) {
183*4882a593Smuzhiyun 			efi_pstore_scan_sysfs_enter(entry, n, head);
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 			size = efi_pstore_read_func(entry, record);
186*4882a593Smuzhiyun 			ret = efi_pstore_scan_sysfs_exit(entry, n, head,
187*4882a593Smuzhiyun 							 size < 0);
188*4882a593Smuzhiyun 			if (ret)
189*4882a593Smuzhiyun 				return ret;
190*4882a593Smuzhiyun 			if (size)
191*4882a593Smuzhiyun 				break;
192*4882a593Smuzhiyun 		}
193*4882a593Smuzhiyun 		*pos = n;
194*4882a593Smuzhiyun 		return size;
195*4882a593Smuzhiyun 	}
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	list_for_each_entry_safe_from((*pos), n, head, list) {
198*4882a593Smuzhiyun 		efi_pstore_scan_sysfs_enter((*pos), n, head);
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 		size = efi_pstore_read_func((*pos), record);
201*4882a593Smuzhiyun 		ret = efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
202*4882a593Smuzhiyun 		if (ret)
203*4882a593Smuzhiyun 			return ret;
204*4882a593Smuzhiyun 		if (size)
205*4882a593Smuzhiyun 			break;
206*4882a593Smuzhiyun 	}
207*4882a593Smuzhiyun 	*pos = n;
208*4882a593Smuzhiyun 	return size;
209*4882a593Smuzhiyun }
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun /**
212*4882a593Smuzhiyun  * efi_pstore_read
213*4882a593Smuzhiyun  *
214*4882a593Smuzhiyun  * This function returns a size of NVRAM entry logged via efi_pstore_write().
215*4882a593Smuzhiyun  * The meaning and behavior of efi_pstore/pstore are as below.
216*4882a593Smuzhiyun  *
217*4882a593Smuzhiyun  * size > 0: Got data of an entry logged via efi_pstore_write() successfully,
218*4882a593Smuzhiyun  *           and pstore filesystem will continue reading subsequent entries.
219*4882a593Smuzhiyun  * size == 0: Entry was not logged via efi_pstore_write(),
220*4882a593Smuzhiyun  *            and efi_pstore driver will continue reading subsequent entries.
221*4882a593Smuzhiyun  * size < 0: Failed to get data of entry logging via efi_pstore_write(),
222*4882a593Smuzhiyun  *           and pstore will stop reading entry.
223*4882a593Smuzhiyun  */
efi_pstore_read(struct pstore_record * record)224*4882a593Smuzhiyun static ssize_t efi_pstore_read(struct pstore_record *record)
225*4882a593Smuzhiyun {
226*4882a593Smuzhiyun 	ssize_t size;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	record->buf = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL);
229*4882a593Smuzhiyun 	if (!record->buf)
230*4882a593Smuzhiyun 		return -ENOMEM;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	if (efivar_entry_iter_begin()) {
233*4882a593Smuzhiyun 		size = -EINTR;
234*4882a593Smuzhiyun 		goto out;
235*4882a593Smuzhiyun 	}
236*4882a593Smuzhiyun 	size = efi_pstore_sysfs_entry_iter(record);
237*4882a593Smuzhiyun 	efivar_entry_iter_end();
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun out:
240*4882a593Smuzhiyun 	if (size <= 0) {
241*4882a593Smuzhiyun 		kfree(record->buf);
242*4882a593Smuzhiyun 		record->buf = NULL;
243*4882a593Smuzhiyun 	}
244*4882a593Smuzhiyun 	return size;
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun 
efi_pstore_write(struct pstore_record * record)247*4882a593Smuzhiyun static int efi_pstore_write(struct pstore_record *record)
248*4882a593Smuzhiyun {
249*4882a593Smuzhiyun 	char name[DUMP_NAME_LEN];
250*4882a593Smuzhiyun 	efi_char16_t efi_name[DUMP_NAME_LEN];
251*4882a593Smuzhiyun 	efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
252*4882a593Smuzhiyun 	int i, ret = 0;
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	record->id = generic_id(record->time.tv_sec, record->part,
255*4882a593Smuzhiyun 				record->count);
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	/* Since we copy the entire length of name, make sure it is wiped. */
258*4882a593Smuzhiyun 	memset(name, 0, sizeof(name));
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 	snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lld-%c",
261*4882a593Smuzhiyun 		 record->type, record->part, record->count,
262*4882a593Smuzhiyun 		 (long long)record->time.tv_sec,
263*4882a593Smuzhiyun 		 record->compressed ? 'C' : 'D');
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	for (i = 0; i < DUMP_NAME_LEN; i++)
266*4882a593Smuzhiyun 		efi_name[i] = name[i];
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 	ret = efivar_entry_set_safe(efi_name, vendor, PSTORE_EFI_ATTRIBUTES,
269*4882a593Smuzhiyun 			      preemptible(), record->size, record->psi->buf);
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	if (record->reason == KMSG_DUMP_OOPS && try_module_get(THIS_MODULE))
272*4882a593Smuzhiyun 		if (!schedule_work(&efivar_work))
273*4882a593Smuzhiyun 			module_put(THIS_MODULE);
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	return ret;
276*4882a593Smuzhiyun };
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun /*
279*4882a593Smuzhiyun  * Clean up an entry with the same name
280*4882a593Smuzhiyun  */
efi_pstore_erase_func(struct efivar_entry * entry,void * data)281*4882a593Smuzhiyun static int efi_pstore_erase_func(struct efivar_entry *entry, void *data)
282*4882a593Smuzhiyun {
283*4882a593Smuzhiyun 	efi_char16_t *efi_name = data;
284*4882a593Smuzhiyun 	efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
285*4882a593Smuzhiyun 	unsigned long ucs2_len = ucs2_strlen(efi_name);
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 	if (efi_guidcmp(entry->var.VendorGuid, vendor))
288*4882a593Smuzhiyun 		return 0;
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	if (ucs2_strncmp(entry->var.VariableName, efi_name, (size_t)ucs2_len))
291*4882a593Smuzhiyun 		return 0;
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 	if (entry->scanning) {
294*4882a593Smuzhiyun 		/*
295*4882a593Smuzhiyun 		 * Skip deletion because this entry will be deleted
296*4882a593Smuzhiyun 		 * after scanning is completed.
297*4882a593Smuzhiyun 		 */
298*4882a593Smuzhiyun 		entry->deleting = true;
299*4882a593Smuzhiyun 	} else
300*4882a593Smuzhiyun 		list_del(&entry->list);
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	/* found */
303*4882a593Smuzhiyun 	__efivar_entry_delete(entry);
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	return 1;
306*4882a593Smuzhiyun }
307*4882a593Smuzhiyun 
efi_pstore_erase_name(const char * name)308*4882a593Smuzhiyun static int efi_pstore_erase_name(const char *name)
309*4882a593Smuzhiyun {
310*4882a593Smuzhiyun 	struct efivar_entry *entry = NULL;
311*4882a593Smuzhiyun 	efi_char16_t efi_name[DUMP_NAME_LEN];
312*4882a593Smuzhiyun 	int found, i;
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	for (i = 0; i < DUMP_NAME_LEN; i++) {
315*4882a593Smuzhiyun 		efi_name[i] = name[i];
316*4882a593Smuzhiyun 		if (name[i] == '\0')
317*4882a593Smuzhiyun 			break;
318*4882a593Smuzhiyun 	}
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	if (efivar_entry_iter_begin())
321*4882a593Smuzhiyun 		return -EINTR;
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 	found = __efivar_entry_iter(efi_pstore_erase_func, &efi_pstore_list,
324*4882a593Smuzhiyun 				    efi_name, &entry);
325*4882a593Smuzhiyun 	efivar_entry_iter_end();
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 	if (found && !entry->scanning)
328*4882a593Smuzhiyun 		kfree(entry);
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 	return found ? 0 : -ENOENT;
331*4882a593Smuzhiyun }
332*4882a593Smuzhiyun 
efi_pstore_erase(struct pstore_record * record)333*4882a593Smuzhiyun static int efi_pstore_erase(struct pstore_record *record)
334*4882a593Smuzhiyun {
335*4882a593Smuzhiyun 	char name[DUMP_NAME_LEN];
336*4882a593Smuzhiyun 	int ret;
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 	snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lld",
339*4882a593Smuzhiyun 		 record->type, record->part, record->count,
340*4882a593Smuzhiyun 		 (long long)record->time.tv_sec);
341*4882a593Smuzhiyun 	ret = efi_pstore_erase_name(name);
342*4882a593Smuzhiyun 	if (ret != -ENOENT)
343*4882a593Smuzhiyun 		return ret;
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	snprintf(name, sizeof(name), "dump-type%u-%u-%lld",
346*4882a593Smuzhiyun 		record->type, record->part, (long long)record->time.tv_sec);
347*4882a593Smuzhiyun 	ret = efi_pstore_erase_name(name);
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	return ret;
350*4882a593Smuzhiyun }
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun static struct pstore_info efi_pstore_info = {
353*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
354*4882a593Smuzhiyun 	.name		= "efi",
355*4882a593Smuzhiyun 	.flags		= PSTORE_FLAGS_DMESG,
356*4882a593Smuzhiyun 	.open		= efi_pstore_open,
357*4882a593Smuzhiyun 	.close		= efi_pstore_close,
358*4882a593Smuzhiyun 	.read		= efi_pstore_read,
359*4882a593Smuzhiyun 	.write		= efi_pstore_write,
360*4882a593Smuzhiyun 	.erase		= efi_pstore_erase,
361*4882a593Smuzhiyun };
362*4882a593Smuzhiyun 
efi_pstore_callback(efi_char16_t * name,efi_guid_t vendor,unsigned long name_size,void * data)363*4882a593Smuzhiyun static int efi_pstore_callback(efi_char16_t *name, efi_guid_t vendor,
364*4882a593Smuzhiyun 			       unsigned long name_size, void *data)
365*4882a593Smuzhiyun {
366*4882a593Smuzhiyun 	struct efivar_entry *entry;
367*4882a593Smuzhiyun 	int ret;
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
370*4882a593Smuzhiyun 	if (!entry)
371*4882a593Smuzhiyun 		return -ENOMEM;
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 	memcpy(entry->var.VariableName, name, name_size);
374*4882a593Smuzhiyun 	entry->var.VendorGuid = vendor;
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	ret = efivar_entry_add(entry, &efi_pstore_list);
377*4882a593Smuzhiyun 	if (ret)
378*4882a593Smuzhiyun 		kfree(entry);
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun 	return ret;
381*4882a593Smuzhiyun }
382*4882a593Smuzhiyun 
efi_pstore_update_entry(efi_char16_t * name,efi_guid_t vendor,unsigned long name_size,void * data)383*4882a593Smuzhiyun static int efi_pstore_update_entry(efi_char16_t *name, efi_guid_t vendor,
384*4882a593Smuzhiyun 				   unsigned long name_size, void *data)
385*4882a593Smuzhiyun {
386*4882a593Smuzhiyun 	struct efivar_entry *entry = data;
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 	if (efivar_entry_find(name, vendor, &efi_pstore_list, false))
389*4882a593Smuzhiyun 		return 0;
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 	memcpy(entry->var.VariableName, name, name_size);
392*4882a593Smuzhiyun 	memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t));
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun 	return 1;
395*4882a593Smuzhiyun }
396*4882a593Smuzhiyun 
efi_pstore_update_entries(struct work_struct * work)397*4882a593Smuzhiyun static void efi_pstore_update_entries(struct work_struct *work)
398*4882a593Smuzhiyun {
399*4882a593Smuzhiyun 	struct efivar_entry *entry;
400*4882a593Smuzhiyun 	int err;
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	/* Add new sysfs entries */
403*4882a593Smuzhiyun 	while (1) {
404*4882a593Smuzhiyun 		entry = kzalloc(sizeof(*entry), GFP_KERNEL);
405*4882a593Smuzhiyun 		if (!entry)
406*4882a593Smuzhiyun 			return;
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun 		err = efivar_init(efi_pstore_update_entry, entry,
409*4882a593Smuzhiyun 				  false, &efi_pstore_list);
410*4882a593Smuzhiyun 		if (!err)
411*4882a593Smuzhiyun 			break;
412*4882a593Smuzhiyun 
413*4882a593Smuzhiyun 		efivar_entry_add(entry, &efi_pstore_list);
414*4882a593Smuzhiyun 	}
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun 	kfree(entry);
417*4882a593Smuzhiyun 	module_put(THIS_MODULE);
418*4882a593Smuzhiyun }
419*4882a593Smuzhiyun 
efivars_pstore_init(void)420*4882a593Smuzhiyun static __init int efivars_pstore_init(void)
421*4882a593Smuzhiyun {
422*4882a593Smuzhiyun 	int ret;
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	if (!efivars_kobject() || !efivar_supports_writes())
425*4882a593Smuzhiyun 		return 0;
426*4882a593Smuzhiyun 
427*4882a593Smuzhiyun 	if (efivars_pstore_disable)
428*4882a593Smuzhiyun 		return 0;
429*4882a593Smuzhiyun 
430*4882a593Smuzhiyun 	ret = efivar_init(efi_pstore_callback, NULL, true, &efi_pstore_list);
431*4882a593Smuzhiyun 	if (ret)
432*4882a593Smuzhiyun 		return ret;
433*4882a593Smuzhiyun 
434*4882a593Smuzhiyun 	efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL);
435*4882a593Smuzhiyun 	if (!efi_pstore_info.buf)
436*4882a593Smuzhiyun 		return -ENOMEM;
437*4882a593Smuzhiyun 
438*4882a593Smuzhiyun 	efi_pstore_info.bufsize = 1024;
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 	if (pstore_register(&efi_pstore_info)) {
441*4882a593Smuzhiyun 		kfree(efi_pstore_info.buf);
442*4882a593Smuzhiyun 		efi_pstore_info.buf = NULL;
443*4882a593Smuzhiyun 		efi_pstore_info.bufsize = 0;
444*4882a593Smuzhiyun 	}
445*4882a593Smuzhiyun 
446*4882a593Smuzhiyun 	INIT_WORK(&efivar_work, efi_pstore_update_entries);
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 	return 0;
449*4882a593Smuzhiyun }
450*4882a593Smuzhiyun 
efivars_pstore_exit(void)451*4882a593Smuzhiyun static __exit void efivars_pstore_exit(void)
452*4882a593Smuzhiyun {
453*4882a593Smuzhiyun 	if (!efi_pstore_info.bufsize)
454*4882a593Smuzhiyun 		return;
455*4882a593Smuzhiyun 
456*4882a593Smuzhiyun 	pstore_unregister(&efi_pstore_info);
457*4882a593Smuzhiyun 	kfree(efi_pstore_info.buf);
458*4882a593Smuzhiyun 	efi_pstore_info.buf = NULL;
459*4882a593Smuzhiyun 	efi_pstore_info.bufsize = 0;
460*4882a593Smuzhiyun }
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun module_init(efivars_pstore_init);
463*4882a593Smuzhiyun module_exit(efivars_pstore_exit);
464*4882a593Smuzhiyun 
465*4882a593Smuzhiyun MODULE_DESCRIPTION("EFI variable backend for pstore");
466*4882a593Smuzhiyun MODULE_LICENSE("GPL");
467*4882a593Smuzhiyun MODULE_ALIAS("platform:efivars");
468