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