]> Git Repo - linux.git/blob - drivers/firmware/efi/efi-pstore.c
Merge tag 'audit-pr-20221003' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoor...
[linux.git] / drivers / firmware / efi / efi-pstore.c
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
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
40 static int efi_pstore_close(struct pstore_info *psi)
41 {
42         efivar_unlock();
43         kfree(psi->data);
44         return 0;
45 }
46
47 static inline u64 generic_id(u64 timestamp, unsigned int part, int count)
48 {
49         return (timestamp * 100 + part) * 1000 + count;
50 }
51
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
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
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
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
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
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");
This page took 0.049172 seconds and 4 git commands to generate.