1 // SPDX-License-Identifier: GPL-2.0+
4 #include <linux/module.h>
5 #include <linux/pstore.h>
6 #include <linux/slab.h>
7 #include <linux/ucs2_string.h>
9 MODULE_IMPORT_NS(EFIVAR);
11 #define DUMP_NAME_LEN 66
13 #define EFIVARS_DATA_SIZE_MAX 1024
15 static bool efivars_pstore_disable =
16 IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE);
18 module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644);
20 #define PSTORE_EFI_ATTRIBUTES \
21 (EFI_VARIABLE_NON_VOLATILE | \
22 EFI_VARIABLE_BOOTSERVICE_ACCESS | \
23 EFI_VARIABLE_RUNTIME_ACCESS)
25 static int efi_pstore_open(struct pstore_info *psi)
33 psi->data = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL);
40 static int efi_pstore_close(struct pstore_info *psi)
47 static inline u64 generic_id(u64 timestamp, unsigned int part, int count)
49 return (timestamp * 100 + part) * 1000 + count;
52 static int efi_pstore_read_func(struct pstore_record *record,
53 efi_char16_t *varname)
55 unsigned long wlen, size = EFIVARS_DATA_SIZE_MAX;
56 char name[DUMP_NAME_LEN], data_type;
62 ucs2_as_utf8(name, varname, DUMP_NAME_LEN);
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);
69 record->time.tv_sec = time;
70 record->time.tv_nsec = 0;
72 record->compressed = true;
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);
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) {
88 * Check if an old format,
89 * which doesn't support holding
90 * multiple logs, remains.
92 record->id = generic_id(time, part, 0);
95 record->time.tv_sec = time;
96 record->time.tv_nsec = 0;
97 record->compressed = false;
98 record->ecc_notice_size = 0;
102 record->buf = kmalloc(size, GFP_KERNEL);
106 status = efivar_get_variable(varname, &LINUX_EFI_CRASH_GUID, NULL,
108 if (status != EFI_SUCCESS) {
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
118 wlen = (ucs2_strnlen(varname, DUMP_NAME_LEN) + 1) * sizeof(efi_char16_t);
119 record->priv = kmemdup(varname, wlen, GFP_KERNEL);
128 static ssize_t efi_pstore_read(struct pstore_record *record)
130 efi_char16_t *varname = record->psi->data;
131 efi_guid_t guid = LINUX_EFI_CRASH_GUID;
132 unsigned long varname_size;
136 varname_size = EFIVARS_DATA_SIZE_MAX;
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.
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.
150 status = efivar_get_next_variable(&varname_size, varname, &guid);
151 if (status == EFI_NOT_FOUND)
154 if (status != EFI_SUCCESS)
157 /* skip variables that don't concern us */
158 if (efi_guidcmp(guid, LINUX_EFI_CRASH_GUID))
161 return efi_pstore_read_func(record, varname);
165 static int efi_pstore_write(struct pstore_record *record)
167 char name[DUMP_NAME_LEN];
168 efi_char16_t efi_name[DUMP_NAME_LEN];
172 record->id = generic_id(record->time.tv_sec, record->part,
175 /* Since we copy the entire length of name, make sure it is wiped. */
176 memset(name, 0, sizeof(name));
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');
183 for (i = 0; i < DUMP_NAME_LEN; i++)
184 efi_name[i] = name[i];
186 if (efivar_trylock())
188 status = efivar_set_variable_locked(efi_name, &LINUX_EFI_CRASH_GUID,
189 PSTORE_EFI_ATTRIBUTES,
190 record->size, record->psi->buf,
193 return status == EFI_SUCCESS ? 0 : -EIO;
196 static int efi_pstore_erase(struct pstore_record *record)
200 status = efivar_set_variable(record->priv, &LINUX_EFI_CRASH_GUID,
201 PSTORE_EFI_ATTRIBUTES, 0, NULL);
203 if (status != EFI_SUCCESS && status != EFI_NOT_FOUND)
208 static struct pstore_info efi_pstore_info = {
209 .owner = THIS_MODULE,
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,
219 static __init int efivars_pstore_init(void)
221 if (!efivar_supports_writes())
224 if (efivars_pstore_disable)
227 efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL);
228 if (!efi_pstore_info.buf)
231 efi_pstore_info.bufsize = 1024;
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;
242 static __exit void efivars_pstore_exit(void)
244 if (!efi_pstore_info.bufsize)
247 pstore_unregister(&efi_pstore_info);
248 kfree(efi_pstore_info.buf);
249 efi_pstore_info.buf = NULL;
250 efi_pstore_info.bufsize = 0;
253 module_init(efivars_pstore_init);
254 module_exit(efivars_pstore_exit);
256 MODULE_DESCRIPTION("EFI variable backend for pstore");
257 MODULE_LICENSE("GPL");
258 MODULE_ALIAS("platform:efivars");