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