xref: /linux/drivers/firmware/efi/efi-pstore.c (revision 048517722cde2595a7366d0c3c72b8b1ec142a9c)
1*04851772SMatt Fleming #include <linux/efi.h>
2*04851772SMatt Fleming #include <linux/module.h>
3*04851772SMatt Fleming #include <linux/pstore.h>
4*04851772SMatt Fleming 
5*04851772SMatt Fleming #define DUMP_NAME_LEN 52
6*04851772SMatt Fleming 
7*04851772SMatt Fleming static bool efivars_pstore_disable =
8*04851772SMatt Fleming 	IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE);
9*04851772SMatt Fleming 
10*04851772SMatt Fleming module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644);
11*04851772SMatt Fleming 
12*04851772SMatt Fleming #define PSTORE_EFI_ATTRIBUTES \
13*04851772SMatt Fleming 	(EFI_VARIABLE_NON_VOLATILE | \
14*04851772SMatt Fleming 	 EFI_VARIABLE_BOOTSERVICE_ACCESS | \
15*04851772SMatt Fleming 	 EFI_VARIABLE_RUNTIME_ACCESS)
16*04851772SMatt Fleming 
17*04851772SMatt Fleming static int efi_pstore_open(struct pstore_info *psi)
18*04851772SMatt Fleming {
19*04851772SMatt Fleming 	efivar_entry_iter_begin();
20*04851772SMatt Fleming 	psi->data = NULL;
21*04851772SMatt Fleming 	return 0;
22*04851772SMatt Fleming }
23*04851772SMatt Fleming 
24*04851772SMatt Fleming static int efi_pstore_close(struct pstore_info *psi)
25*04851772SMatt Fleming {
26*04851772SMatt Fleming 	efivar_entry_iter_end();
27*04851772SMatt Fleming 	psi->data = NULL;
28*04851772SMatt Fleming 	return 0;
29*04851772SMatt Fleming }
30*04851772SMatt Fleming 
31*04851772SMatt Fleming struct pstore_read_data {
32*04851772SMatt Fleming 	u64 *id;
33*04851772SMatt Fleming 	enum pstore_type_id *type;
34*04851772SMatt Fleming 	int *count;
35*04851772SMatt Fleming 	struct timespec *timespec;
36*04851772SMatt Fleming 	char **buf;
37*04851772SMatt Fleming };
38*04851772SMatt Fleming 
39*04851772SMatt Fleming static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
40*04851772SMatt Fleming {
41*04851772SMatt Fleming 	efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
42*04851772SMatt Fleming 	struct pstore_read_data *cb_data = data;
43*04851772SMatt Fleming 	char name[DUMP_NAME_LEN];
44*04851772SMatt Fleming 	int i;
45*04851772SMatt Fleming 	int cnt;
46*04851772SMatt Fleming 	unsigned int part;
47*04851772SMatt Fleming 	unsigned long time, size;
48*04851772SMatt Fleming 
49*04851772SMatt Fleming 	if (efi_guidcmp(entry->var.VendorGuid, vendor))
50*04851772SMatt Fleming 		return 0;
51*04851772SMatt Fleming 
52*04851772SMatt Fleming 	for (i = 0; i < DUMP_NAME_LEN; i++)
53*04851772SMatt Fleming 		name[i] = entry->var.VariableName[i];
54*04851772SMatt Fleming 
55*04851772SMatt Fleming 	if (sscanf(name, "dump-type%u-%u-%d-%lu",
56*04851772SMatt Fleming 		   cb_data->type, &part, &cnt, &time) == 4) {
57*04851772SMatt Fleming 		*cb_data->id = part;
58*04851772SMatt Fleming 		*cb_data->count = cnt;
59*04851772SMatt Fleming 		cb_data->timespec->tv_sec = time;
60*04851772SMatt Fleming 		cb_data->timespec->tv_nsec = 0;
61*04851772SMatt Fleming 	} else if (sscanf(name, "dump-type%u-%u-%lu",
62*04851772SMatt Fleming 			  cb_data->type, &part, &time) == 3) {
63*04851772SMatt Fleming 		/*
64*04851772SMatt Fleming 		 * Check if an old format,
65*04851772SMatt Fleming 		 * which doesn't support holding
66*04851772SMatt Fleming 		 * multiple logs, remains.
67*04851772SMatt Fleming 		 */
68*04851772SMatt Fleming 		*cb_data->id = part;
69*04851772SMatt Fleming 		*cb_data->count = 0;
70*04851772SMatt Fleming 		cb_data->timespec->tv_sec = time;
71*04851772SMatt Fleming 		cb_data->timespec->tv_nsec = 0;
72*04851772SMatt Fleming 	} else
73*04851772SMatt Fleming 		return 0;
74*04851772SMatt Fleming 
75*04851772SMatt Fleming 	__efivar_entry_size(entry, &size);
76*04851772SMatt Fleming 	*cb_data->buf = kmalloc(size, GFP_KERNEL);
77*04851772SMatt Fleming 	if (*cb_data->buf == NULL)
78*04851772SMatt Fleming 		return -ENOMEM;
79*04851772SMatt Fleming 	memcpy(*cb_data->buf, entry->var.Data, size);
80*04851772SMatt Fleming 	return size;
81*04851772SMatt Fleming }
82*04851772SMatt Fleming 
83*04851772SMatt Fleming static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
84*04851772SMatt Fleming 			       int *count, struct timespec *timespec,
85*04851772SMatt Fleming 			       char **buf, struct pstore_info *psi)
86*04851772SMatt Fleming {
87*04851772SMatt Fleming 	struct pstore_read_data data;
88*04851772SMatt Fleming 
89*04851772SMatt Fleming 	data.id = id;
90*04851772SMatt Fleming 	data.type = type;
91*04851772SMatt Fleming 	data.count = count;
92*04851772SMatt Fleming 	data.timespec = timespec;
93*04851772SMatt Fleming 	data.buf = buf;
94*04851772SMatt Fleming 
95*04851772SMatt Fleming 	return __efivar_entry_iter(efi_pstore_read_func, &efivar_sysfs_list, &data,
96*04851772SMatt Fleming 				   (struct efivar_entry **)&psi->data);
97*04851772SMatt Fleming }
98*04851772SMatt Fleming 
99*04851772SMatt Fleming static int efi_pstore_write(enum pstore_type_id type,
100*04851772SMatt Fleming 		enum kmsg_dump_reason reason, u64 *id,
101*04851772SMatt Fleming 		unsigned int part, int count, size_t size,
102*04851772SMatt Fleming 		struct pstore_info *psi)
103*04851772SMatt Fleming {
104*04851772SMatt Fleming 	char name[DUMP_NAME_LEN];
105*04851772SMatt Fleming 	efi_char16_t efi_name[DUMP_NAME_LEN];
106*04851772SMatt Fleming 	efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
107*04851772SMatt Fleming 	int i, ret = 0;
108*04851772SMatt Fleming 
109*04851772SMatt Fleming 	sprintf(name, "dump-type%u-%u-%d-%lu", type, part, count,
110*04851772SMatt Fleming 		get_seconds());
111*04851772SMatt Fleming 
112*04851772SMatt Fleming 	for (i = 0; i < DUMP_NAME_LEN; i++)
113*04851772SMatt Fleming 		efi_name[i] = name[i];
114*04851772SMatt Fleming 
115*04851772SMatt Fleming 	efivar_entry_set_safe(efi_name, vendor, PSTORE_EFI_ATTRIBUTES,
116*04851772SMatt Fleming 			      !pstore_cannot_block_path(reason),
117*04851772SMatt Fleming 			      size, psi->buf);
118*04851772SMatt Fleming 
119*04851772SMatt Fleming 	if (reason == KMSG_DUMP_OOPS)
120*04851772SMatt Fleming 		efivar_run_worker();
121*04851772SMatt Fleming 
122*04851772SMatt Fleming 	*id = part;
123*04851772SMatt Fleming 	return ret;
124*04851772SMatt Fleming };
125*04851772SMatt Fleming 
126*04851772SMatt Fleming struct pstore_erase_data {
127*04851772SMatt Fleming 	u64 id;
128*04851772SMatt Fleming 	enum pstore_type_id type;
129*04851772SMatt Fleming 	int count;
130*04851772SMatt Fleming 	struct timespec time;
131*04851772SMatt Fleming 	efi_char16_t *name;
132*04851772SMatt Fleming };
133*04851772SMatt Fleming 
134*04851772SMatt Fleming /*
135*04851772SMatt Fleming  * Clean up an entry with the same name
136*04851772SMatt Fleming  */
137*04851772SMatt Fleming static int efi_pstore_erase_func(struct efivar_entry *entry, void *data)
138*04851772SMatt Fleming {
139*04851772SMatt Fleming 	struct pstore_erase_data *ed = data;
140*04851772SMatt Fleming 	efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
141*04851772SMatt Fleming 	efi_char16_t efi_name_old[DUMP_NAME_LEN];
142*04851772SMatt Fleming 	efi_char16_t *efi_name = ed->name;
143*04851772SMatt Fleming 	unsigned long utf16_len = utf16_strlen(ed->name);
144*04851772SMatt Fleming 	char name_old[DUMP_NAME_LEN];
145*04851772SMatt Fleming 	int i;
146*04851772SMatt Fleming 
147*04851772SMatt Fleming 	if (efi_guidcmp(entry->var.VendorGuid, vendor))
148*04851772SMatt Fleming 		return 0;
149*04851772SMatt Fleming 
150*04851772SMatt Fleming 	if (utf16_strncmp(entry->var.VariableName,
151*04851772SMatt Fleming 			  efi_name, (size_t)utf16_len)) {
152*04851772SMatt Fleming 		/*
153*04851772SMatt Fleming 		 * Check if an old format, which doesn't support
154*04851772SMatt Fleming 		 * holding multiple logs, remains.
155*04851772SMatt Fleming 		 */
156*04851772SMatt Fleming 		sprintf(name_old, "dump-type%u-%u-%lu", ed->type,
157*04851772SMatt Fleming 			(unsigned int)ed->id, ed->time.tv_sec);
158*04851772SMatt Fleming 
159*04851772SMatt Fleming 		for (i = 0; i < DUMP_NAME_LEN; i++)
160*04851772SMatt Fleming 			efi_name_old[i] = name_old[i];
161*04851772SMatt Fleming 
162*04851772SMatt Fleming 		if (utf16_strncmp(entry->var.VariableName, efi_name_old,
163*04851772SMatt Fleming 				  utf16_strlen(efi_name_old)))
164*04851772SMatt Fleming 			return 0;
165*04851772SMatt Fleming 	}
166*04851772SMatt Fleming 
167*04851772SMatt Fleming 	/* found */
168*04851772SMatt Fleming 	__efivar_entry_delete(entry);
169*04851772SMatt Fleming 	return 1;
170*04851772SMatt Fleming }
171*04851772SMatt Fleming 
172*04851772SMatt Fleming static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
173*04851772SMatt Fleming 			    struct timespec time, struct pstore_info *psi)
174*04851772SMatt Fleming {
175*04851772SMatt Fleming 	struct pstore_erase_data edata;
176*04851772SMatt Fleming 	struct efivar_entry *entry;
177*04851772SMatt Fleming 	char name[DUMP_NAME_LEN];
178*04851772SMatt Fleming 	efi_char16_t efi_name[DUMP_NAME_LEN];
179*04851772SMatt Fleming 	int found, i;
180*04851772SMatt Fleming 
181*04851772SMatt Fleming 	sprintf(name, "dump-type%u-%u-%d-%lu", type, (unsigned int)id, count,
182*04851772SMatt Fleming 		time.tv_sec);
183*04851772SMatt Fleming 
184*04851772SMatt Fleming 	for (i = 0; i < DUMP_NAME_LEN; i++)
185*04851772SMatt Fleming 		efi_name[i] = name[i];
186*04851772SMatt Fleming 
187*04851772SMatt Fleming 	edata.id = id;
188*04851772SMatt Fleming 	edata.type = type;
189*04851772SMatt Fleming 	edata.count = count;
190*04851772SMatt Fleming 	edata.time = time;
191*04851772SMatt Fleming 	edata.name = efi_name;
192*04851772SMatt Fleming 
193*04851772SMatt Fleming 	efivar_entry_iter_begin();
194*04851772SMatt Fleming 	found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry);
195*04851772SMatt Fleming 	efivar_entry_iter_end();
196*04851772SMatt Fleming 
197*04851772SMatt Fleming 	if (found)
198*04851772SMatt Fleming 		efivar_unregister(entry);
199*04851772SMatt Fleming 
200*04851772SMatt Fleming 	return 0;
201*04851772SMatt Fleming }
202*04851772SMatt Fleming 
203*04851772SMatt Fleming static struct pstore_info efi_pstore_info = {
204*04851772SMatt Fleming 	.owner		= THIS_MODULE,
205*04851772SMatt Fleming 	.name		= "efi",
206*04851772SMatt Fleming 	.open		= efi_pstore_open,
207*04851772SMatt Fleming 	.close		= efi_pstore_close,
208*04851772SMatt Fleming 	.read		= efi_pstore_read,
209*04851772SMatt Fleming 	.write		= efi_pstore_write,
210*04851772SMatt Fleming 	.erase		= efi_pstore_erase,
211*04851772SMatt Fleming };
212*04851772SMatt Fleming 
213*04851772SMatt Fleming static __init int efivars_pstore_init(void)
214*04851772SMatt Fleming {
215*04851772SMatt Fleming 	if (!efi_enabled(EFI_RUNTIME_SERVICES))
216*04851772SMatt Fleming 		return 0;
217*04851772SMatt Fleming 
218*04851772SMatt Fleming 	if (!efivars_kobject())
219*04851772SMatt Fleming 		return 0;
220*04851772SMatt Fleming 
221*04851772SMatt Fleming 	if (efivars_pstore_disable)
222*04851772SMatt Fleming 		return 0;
223*04851772SMatt Fleming 
224*04851772SMatt Fleming 	efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL);
225*04851772SMatt Fleming 	if (!efi_pstore_info.buf)
226*04851772SMatt Fleming 		return -ENOMEM;
227*04851772SMatt Fleming 
228*04851772SMatt Fleming 	efi_pstore_info.bufsize = 1024;
229*04851772SMatt Fleming 	spin_lock_init(&efi_pstore_info.buf_lock);
230*04851772SMatt Fleming 
231*04851772SMatt Fleming 	pstore_register(&efi_pstore_info);
232*04851772SMatt Fleming 
233*04851772SMatt Fleming 	return 0;
234*04851772SMatt Fleming }
235*04851772SMatt Fleming 
236*04851772SMatt Fleming static __exit void efivars_pstore_exit(void)
237*04851772SMatt Fleming {
238*04851772SMatt Fleming }
239*04851772SMatt Fleming 
240*04851772SMatt Fleming module_init(efivars_pstore_init);
241*04851772SMatt Fleming module_exit(efivars_pstore_exit);
242*04851772SMatt Fleming 
243*04851772SMatt Fleming MODULE_DESCRIPTION("EFI variable backend for pstore");
244*04851772SMatt Fleming MODULE_LICENSE("GPL");
245