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