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