1 // SPDX-License-Identifier: GPL-2.0+
2 
3 #include <linux/efi.h>
4 #include <linux/module.h>
5 #include <linux/pstore.h>
6 #include <linux/slab.h>
7 #include <linux/ucs2_string.h>
8 
9 MODULE_IMPORT_NS(EFIVAR);
10 
11 #define DUMP_NAME_LEN 66
12 
13 #define EFIVARS_DATA_SIZE_MAX 1024
14 
15 static bool efivars_pstore_disable =
16 	IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE);
17 
18 module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644);
19 
20 #define PSTORE_EFI_ATTRIBUTES \
21 	(EFI_VARIABLE_NON_VOLATILE | \
22 	 EFI_VARIABLE_BOOTSERVICE_ACCESS | \
23 	 EFI_VARIABLE_RUNTIME_ACCESS)
24 
efi_pstore_open(struct pstore_info * psi)25 static int efi_pstore_open(struct pstore_info *psi)
26 {
27 	int err;
28 
29 	err = efivar_lock();
30 	if (err)
31 		return err;
32 
33 	psi->data = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL);
34 	if (!psi->data)
35 		return -ENOMEM;
36 
37 	return 0;
38 }
39 
efi_pstore_close(struct pstore_info * psi)40 static int efi_pstore_close(struct pstore_info *psi)
41 {
42 	efivar_unlock();
43 	kfree(psi->data);
44 	return 0;
45 }
46 
generic_id(u64 timestamp,unsigned int part,int count)47 static inline u64 generic_id(u64 timestamp, unsigned int part, int count)
48 {
49 	return (timestamp * 100 + part) * 1000 + count;
50 }
51 
efi_pstore_read_func(struct pstore_record * record,efi_char16_t * varname)52 static int efi_pstore_read_func(struct pstore_record *record,
53 				efi_char16_t *varname)
54 {
55 	unsigned long wlen, size = EFIVARS_DATA_SIZE_MAX;
56 	char name[DUMP_NAME_LEN], data_type;
57 	efi_status_t status;
58 	int cnt;
59 	unsigned int part;
60 	u64 time;
61 
62 	ucs2_as_utf8(name, varname, DUMP_NAME_LEN);
63 
64 	if (sscanf(name, "dump-type%u-%u-%d-%llu-%c",
65 		   &record->type, &part, &cnt, &time, &data_type) == 5) {
66 		record->id = generic_id(time, part, cnt);
67 		record->part = part;
68 		record->count = cnt;
69 		record->time.tv_sec = time;
70 		record->time.tv_nsec = 0;
71 		if (data_type == 'C')
72 			record->compressed = true;
73 		else
74 			record->compressed = false;
75 		record->ecc_notice_size = 0;
76 	} else if (sscanf(name, "dump-type%u-%u-%d-%llu",
77 		   &record->type, &part, &cnt, &time) == 4) {
78 		record->id = generic_id(time, part, cnt);
79 		record->part = part;
80 		record->count = cnt;
81 		record->time.tv_sec = time;
82 		record->time.tv_nsec = 0;
83 		record->compressed = false;
84 		record->ecc_notice_size = 0;
85 	} else if (sscanf(name, "dump-type%u-%u-%llu",
86 			  &record->type, &part, &time) == 3) {
87 		/*
88 		 * Check if an old format,
89 		 * which doesn't support holding
90 		 * multiple logs, remains.
91 		 */
92 		record->id = generic_id(time, part, 0);
93 		record->part = part;
94 		record->count = 0;
95 		record->time.tv_sec = time;
96 		record->time.tv_nsec = 0;
97 		record->compressed = false;
98 		record->ecc_notice_size = 0;
99 	} else
100 		return 0;
101 
102 	record->buf = kmalloc(size, GFP_KERNEL);
103 	if (!record->buf)
104 		return -ENOMEM;
105 
106 	status = efivar_get_variable(varname, &LINUX_EFI_CRASH_GUID, NULL,
107 				     &size, record->buf);
108 	if (status != EFI_SUCCESS) {
109 		kfree(record->buf);
110 		return -EIO;
111 	}
112 
113 	/*
114 	 * Store the name of the variable in the pstore_record priv field, so
115 	 * we can reuse it later if we need to delete the EFI variable from the
116 	 * variable store.
117 	 */
118 	wlen = (ucs2_strnlen(varname, DUMP_NAME_LEN) + 1) * sizeof(efi_char16_t);
119 	record->priv = kmemdup(varname, wlen, GFP_KERNEL);
120 	if (!record->priv) {
121 		kfree(record->buf);
122 		return -ENOMEM;
123 	}
124 
125 	return size;
126 }
127 
efi_pstore_read(struct pstore_record * record)128 static ssize_t efi_pstore_read(struct pstore_record *record)
129 {
130 	efi_char16_t *varname = record->psi->data;
131 	efi_guid_t guid = LINUX_EFI_CRASH_GUID;
132 	unsigned long varname_size;
133 	efi_status_t status;
134 
135 	for (;;) {
136 		varname_size = EFIVARS_DATA_SIZE_MAX;
137 
138 		/*
139 		 * If this is the first read() call in the pstore enumeration,
140 		 * varname will be the empty string, and the GetNextVariable()
141 		 * runtime service call will return the first EFI variable in
142 		 * its own enumeration order, ignoring the guid argument.
143 		 *
144 		 * Subsequent calls to GetNextVariable() must pass the name and
145 		 * guid values returned by the previous call, which is why we
146 		 * store varname in record->psi->data. Given that we only
147 		 * enumerate variables with the efi-pstore GUID, there is no
148 		 * need to record the guid return value.
149 		 */
150 		status = efivar_get_next_variable(&varname_size, varname, &guid);
151 		if (status == EFI_NOT_FOUND)
152 			return 0;
153 
154 		if (status != EFI_SUCCESS)
155 			return -EIO;
156 
157 		/* skip variables that don't concern us */
158 		if (efi_guidcmp(guid, LINUX_EFI_CRASH_GUID))
159 			continue;
160 
161 		return efi_pstore_read_func(record, varname);
162 	}
163 }
164 
efi_pstore_write(struct pstore_record * record)165 static int efi_pstore_write(struct pstore_record *record)
166 {
167 	char name[DUMP_NAME_LEN];
168 	efi_char16_t efi_name[DUMP_NAME_LEN];
169 	efi_status_t status;
170 	int i;
171 
172 	record->id = generic_id(record->time.tv_sec, record->part,
173 				record->count);
174 
175 	/* Since we copy the entire length of name, make sure it is wiped. */
176 	memset(name, 0, sizeof(name));
177 
178 	snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lld-%c",
179 		 record->type, record->part, record->count,
180 		 (long long)record->time.tv_sec,
181 		 record->compressed ? 'C' : 'D');
182 
183 	for (i = 0; i < DUMP_NAME_LEN; i++)
184 		efi_name[i] = name[i];
185 
186 	if (efivar_trylock())
187 		return -EBUSY;
188 	status = efivar_set_variable_locked(efi_name, &LINUX_EFI_CRASH_GUID,
189 					    PSTORE_EFI_ATTRIBUTES,
190 					    record->size, record->psi->buf,
191 					    true);
192 	efivar_unlock();
193 	return status == EFI_SUCCESS ? 0 : -EIO;
194 };
195 
efi_pstore_erase(struct pstore_record * record)196 static int efi_pstore_erase(struct pstore_record *record)
197 {
198 	efi_status_t status;
199 
200 	status = efivar_set_variable(record->priv, &LINUX_EFI_CRASH_GUID,
201 				     PSTORE_EFI_ATTRIBUTES, 0, NULL);
202 
203 	if (status != EFI_SUCCESS && status != EFI_NOT_FOUND)
204 		return -EIO;
205 	return 0;
206 }
207 
208 static struct pstore_info efi_pstore_info = {
209 	.owner		= THIS_MODULE,
210 	.name		= "efi",
211 	.flags		= PSTORE_FLAGS_DMESG,
212 	.open		= efi_pstore_open,
213 	.close		= efi_pstore_close,
214 	.read		= efi_pstore_read,
215 	.write		= efi_pstore_write,
216 	.erase		= efi_pstore_erase,
217 };
218 
efivars_pstore_init(void)219 static __init int efivars_pstore_init(void)
220 {
221 	if (!efivar_supports_writes())
222 		return 0;
223 
224 	if (efivars_pstore_disable)
225 		return 0;
226 
227 	efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL);
228 	if (!efi_pstore_info.buf)
229 		return -ENOMEM;
230 
231 	efi_pstore_info.bufsize = 1024;
232 
233 	if (pstore_register(&efi_pstore_info)) {
234 		kfree(efi_pstore_info.buf);
235 		efi_pstore_info.buf = NULL;
236 		efi_pstore_info.bufsize = 0;
237 	}
238 
239 	return 0;
240 }
241 
efivars_pstore_exit(void)242 static __exit void efivars_pstore_exit(void)
243 {
244 	if (!efi_pstore_info.bufsize)
245 		return;
246 
247 	pstore_unregister(&efi_pstore_info);
248 	kfree(efi_pstore_info.buf);
249 	efi_pstore_info.buf = NULL;
250 	efi_pstore_info.bufsize = 0;
251 }
252 
253 module_init(efivars_pstore_init);
254 module_exit(efivars_pstore_exit);
255 
256 MODULE_DESCRIPTION("EFI variable backend for pstore");
257 MODULE_LICENSE("GPL");
258 MODULE_ALIAS("platform:efivars");
259