Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0+
2 : : /*
3 : : * esrt.c
4 : : *
5 : : * This module exports EFI System Resource Table (ESRT) entries into userspace
6 : : * through the sysfs file system. The ESRT provides a read-only catalog of
7 : : * system components for which the system accepts firmware upgrades via UEFI's
8 : : * "Capsule Update" feature. This module allows userland utilities to evaluate
9 : : * what firmware updates can be applied to this system, and potentially arrange
10 : : * for those updates to occur.
11 : : *
12 : : * Data is currently found below /sys/firmware/efi/esrt/...
13 : : */
14 : : #define pr_fmt(fmt) "esrt: " fmt
15 : :
16 : : #include <linux/capability.h>
17 : : #include <linux/device.h>
18 : : #include <linux/efi.h>
19 : : #include <linux/init.h>
20 : : #include <linux/io.h>
21 : : #include <linux/kernel.h>
22 : : #include <linux/kobject.h>
23 : : #include <linux/list.h>
24 : : #include <linux/memblock.h>
25 : : #include <linux/slab.h>
26 : : #include <linux/types.h>
27 : :
28 : : #include <asm/io.h>
29 : : #include <asm/early_ioremap.h>
30 : :
31 : : struct efi_system_resource_entry_v1 {
32 : : efi_guid_t fw_class;
33 : : u32 fw_type;
34 : : u32 fw_version;
35 : : u32 lowest_supported_fw_version;
36 : : u32 capsule_flags;
37 : : u32 last_attempt_version;
38 : : u32 last_attempt_status;
39 : : };
40 : :
41 : : /*
42 : : * _count and _version are what they seem like. _max is actually just
43 : : * accounting info for the firmware when creating the table; it should never
44 : : * have been exposed to us. To wit, the spec says:
45 : : * The maximum number of resource array entries that can be within the
46 : : * table without reallocating the table, must not be zero.
47 : : * Since there's no guidance about what that means in terms of memory layout,
48 : : * it means nothing to us.
49 : : */
50 : : struct efi_system_resource_table {
51 : : u32 fw_resource_count;
52 : : u32 fw_resource_count_max;
53 : : u64 fw_resource_version;
54 : : u8 entries[];
55 : : };
56 : :
57 : : static phys_addr_t esrt_data;
58 : : static size_t esrt_data_size;
59 : :
60 : : static struct efi_system_resource_table *esrt;
61 : :
62 : : struct esre_entry {
63 : : union {
64 : : struct efi_system_resource_entry_v1 *esre1;
65 : : } esre;
66 : :
67 : : struct kobject kobj;
68 : : struct list_head list;
69 : : };
70 : :
71 : : /* global list of esre_entry. */
72 : : static LIST_HEAD(entry_list);
73 : :
74 : : /* entry attribute */
75 : : struct esre_attribute {
76 : : struct attribute attr;
77 : : ssize_t (*show)(struct esre_entry *entry, char *buf);
78 : : ssize_t (*store)(struct esre_entry *entry,
79 : : const char *buf, size_t count);
80 : : };
81 : :
82 : 0 : static struct esre_entry *to_entry(struct kobject *kobj)
83 : : {
84 : 0 : return container_of(kobj, struct esre_entry, kobj);
85 : : }
86 : :
87 : 0 : static struct esre_attribute *to_attr(struct attribute *attr)
88 : : {
89 : 0 : return container_of(attr, struct esre_attribute, attr);
90 : : }
91 : :
92 : 0 : static ssize_t esre_attr_show(struct kobject *kobj,
93 : : struct attribute *_attr, char *buf)
94 : : {
95 : 0 : struct esre_entry *entry = to_entry(kobj);
96 : 0 : struct esre_attribute *attr = to_attr(_attr);
97 : :
98 : : /* Don't tell normal users what firmware versions we've got... */
99 [ # # ]: 0 : if (!capable(CAP_SYS_ADMIN))
100 : : return -EACCES;
101 : :
102 : 0 : return attr->show(entry, buf);
103 : : }
104 : :
105 : : static const struct sysfs_ops esre_attr_ops = {
106 : : .show = esre_attr_show,
107 : : };
108 : :
109 : : /* Generic ESRT Entry ("ESRE") support. */
110 : 0 : static ssize_t fw_class_show(struct esre_entry *entry, char *buf)
111 : : {
112 : 0 : char *str = buf;
113 : :
114 : 0 : efi_guid_to_str(&entry->esre.esre1->fw_class, str);
115 : 0 : str += strlen(str);
116 : 0 : str += sprintf(str, "\n");
117 : :
118 : 0 : return str - buf;
119 : : }
120 : :
121 : : static struct esre_attribute esre_fw_class = __ATTR_RO_MODE(fw_class, 0400);
122 : :
123 : : #define esre_attr_decl(name, size, fmt) \
124 : : static ssize_t name##_show(struct esre_entry *entry, char *buf) \
125 : : { \
126 : : return sprintf(buf, fmt "\n", \
127 : : le##size##_to_cpu(entry->esre.esre1->name)); \
128 : : } \
129 : : \
130 : : static struct esre_attribute esre_##name = __ATTR_RO_MODE(name, 0400)
131 : :
132 : 0 : esre_attr_decl(fw_type, 32, "%u");
133 : 0 : esre_attr_decl(fw_version, 32, "%u");
134 : 0 : esre_attr_decl(lowest_supported_fw_version, 32, "%u");
135 : 0 : esre_attr_decl(capsule_flags, 32, "0x%x");
136 : 0 : esre_attr_decl(last_attempt_version, 32, "%u");
137 : 0 : esre_attr_decl(last_attempt_status, 32, "%u");
138 : :
139 : : static struct attribute *esre1_attrs[] = {
140 : : &esre_fw_class.attr,
141 : : &esre_fw_type.attr,
142 : : &esre_fw_version.attr,
143 : : &esre_lowest_supported_fw_version.attr,
144 : : &esre_capsule_flags.attr,
145 : : &esre_last_attempt_version.attr,
146 : : &esre_last_attempt_status.attr,
147 : : NULL
148 : : };
149 : 0 : static void esre_release(struct kobject *kobj)
150 : : {
151 : 0 : struct esre_entry *entry = to_entry(kobj);
152 : :
153 : 0 : list_del(&entry->list);
154 : 0 : kfree(entry);
155 : 0 : }
156 : :
157 : : static struct kobj_type esre1_ktype = {
158 : : .release = esre_release,
159 : : .sysfs_ops = &esre_attr_ops,
160 : : .default_attrs = esre1_attrs,
161 : : };
162 : :
163 : :
164 : : static struct kobject *esrt_kobj;
165 : : static struct kset *esrt_kset;
166 : :
167 : 0 : static int esre_create_sysfs_entry(void *esre, int entry_num)
168 : : {
169 : 0 : struct esre_entry *entry;
170 : :
171 : 0 : entry = kzalloc(sizeof(*entry), GFP_KERNEL);
172 [ # # ]: 0 : if (!entry)
173 : : return -ENOMEM;
174 : :
175 : 0 : entry->kobj.kset = esrt_kset;
176 : :
177 [ # # ]: 0 : if (esrt->fw_resource_version == 1) {
178 : 0 : int rc = 0;
179 : :
180 : 0 : entry->esre.esre1 = esre;
181 : 0 : rc = kobject_init_and_add(&entry->kobj, &esre1_ktype, NULL,
182 : : "entry%d", entry_num);
183 [ # # ]: 0 : if (rc) {
184 : 0 : kfree(entry);
185 : 0 : return rc;
186 : : }
187 : : }
188 : :
189 : 0 : list_add_tail(&entry->list, &entry_list);
190 : 0 : return 0;
191 : : }
192 : :
193 : : /* support for displaying ESRT fields at the top level */
194 : : #define esrt_attr_decl(name, size, fmt) \
195 : : static ssize_t name##_show(struct kobject *kobj, \
196 : : struct kobj_attribute *attr, char *buf)\
197 : : { \
198 : : return sprintf(buf, fmt "\n", le##size##_to_cpu(esrt->name)); \
199 : : } \
200 : : \
201 : : static struct kobj_attribute esrt_##name = __ATTR_RO_MODE(name, 0400)
202 : :
203 : 0 : esrt_attr_decl(fw_resource_count, 32, "%u");
204 : 0 : esrt_attr_decl(fw_resource_count_max, 32, "%u");
205 : 0 : esrt_attr_decl(fw_resource_version, 64, "%llu");
206 : :
207 : : static struct attribute *esrt_attrs[] = {
208 : : &esrt_fw_resource_count.attr,
209 : : &esrt_fw_resource_count_max.attr,
210 : : &esrt_fw_resource_version.attr,
211 : : NULL,
212 : : };
213 : :
214 : 0 : static inline int esrt_table_exists(void)
215 : : {
216 [ # # # # : 0 : if (!efi_enabled(EFI_CONFIG_TABLES))
# # ]
217 : : return 0;
218 [ # # # # : 0 : if (efi.esrt == EFI_INVALID_TABLE_ADDR)
# # ]
219 : : return 0;
220 : : return 1;
221 : : }
222 : :
223 : 0 : static umode_t esrt_attr_is_visible(struct kobject *kobj,
224 : : struct attribute *attr, int n)
225 : : {
226 : 0 : if (!esrt_table_exists())
227 : : return 0;
228 : 0 : return attr->mode;
229 : : }
230 : :
231 : : static const struct attribute_group esrt_attr_group = {
232 : : .attrs = esrt_attrs,
233 : : .is_visible = esrt_attr_is_visible,
234 : : };
235 : :
236 : : /*
237 : : * remap the table, validate it, mark it reserved and unmap it.
238 : : */
239 : 78 : void __init efi_esrt_init(void)
240 : : {
241 : 78 : void *va;
242 : 78 : struct efi_system_resource_table tmpesrt;
243 : 78 : struct efi_system_resource_entry_v1 *v1_entries;
244 : 78 : size_t size, max, entry_size, entries_size;
245 : 78 : efi_memory_desc_t md;
246 : 78 : int rc;
247 : 78 : phys_addr_t end;
248 : :
249 [ - + ]: 78 : if (!efi_enabled(EFI_MEMMAP))
250 : 78 : return;
251 : :
252 : 0 : pr_debug("esrt-init: loading.\n");
253 : 0 : if (!esrt_table_exists())
254 : : return;
255 : :
256 : 0 : rc = efi_mem_desc_lookup(efi.esrt, &md);
257 [ # # ]: 0 : if (rc < 0 ||
258 [ # # ]: 0 : (!(md.attribute & EFI_MEMORY_RUNTIME) &&
259 [ # # # # ]: 0 : md.type != EFI_BOOT_SERVICES_DATA &&
260 : : md.type != EFI_RUNTIME_SERVICES_DATA)) {
261 : 0 : pr_warn("ESRT header is not in the memory map.\n");
262 : 0 : return;
263 : : }
264 : :
265 : 0 : max = efi_mem_desc_end(&md);
266 [ # # ]: 0 : if (max < efi.esrt) {
267 : 0 : pr_err("EFI memory descriptor is invalid. (esrt: %p max: %p)\n",
268 : : (void *)efi.esrt, (void *)max);
269 : 0 : return;
270 : : }
271 : :
272 : 0 : size = sizeof(*esrt);
273 : 0 : max -= efi.esrt;
274 : :
275 [ # # ]: 0 : if (max < size) {
276 : 0 : pr_err("ESRT header doesn't fit on single memory map entry. (size: %zu max: %zu)\n",
277 : : size, max);
278 : 0 : return;
279 : : }
280 : :
281 : 0 : va = early_memremap(efi.esrt, size);
282 [ # # ]: 0 : if (!va) {
283 : 0 : pr_err("early_memremap(%p, %zu) failed.\n", (void *)efi.esrt,
284 : : size);
285 : 0 : return;
286 : : }
287 : :
288 : 0 : memcpy(&tmpesrt, va, sizeof(tmpesrt));
289 : 0 : early_memunmap(va, size);
290 : :
291 [ # # ]: 0 : if (tmpesrt.fw_resource_version == 1) {
292 : 0 : entry_size = sizeof (*v1_entries);
293 : : } else {
294 : 0 : pr_err("Unsupported ESRT version %lld.\n",
295 : : tmpesrt.fw_resource_version);
296 : 0 : return;
297 : : }
298 : :
299 [ # # # # ]: 0 : if (tmpesrt.fw_resource_count > 0 && max - size < entry_size) {
300 : 0 : pr_err("ESRT memory map entry can only hold the header. (max: %zu size: %zu)\n",
301 : : max - size, entry_size);
302 : 0 : return;
303 : : }
304 : :
305 : : /*
306 : : * The format doesn't really give us any boundary to test here,
307 : : * so I'm making up 128 as the max number of individually updatable
308 : : * components we support.
309 : : * 128 should be pretty excessive, but there's still some chance
310 : : * somebody will do that someday and we'll need to raise this.
311 : : */
312 [ # # ]: 0 : if (tmpesrt.fw_resource_count > 128) {
313 : 0 : pr_err("ESRT says fw_resource_count has very large value %d.\n",
314 : : tmpesrt.fw_resource_count);
315 : 0 : return;
316 : : }
317 : :
318 : : /*
319 : : * We know it can't be larger than N * sizeof() here, and N is limited
320 : : * by the previous test to a small number, so there's no overflow.
321 : : */
322 : 0 : entries_size = tmpesrt.fw_resource_count * entry_size;
323 [ # # ]: 0 : if (max < size + entries_size) {
324 : 0 : pr_err("ESRT does not fit on single memory map entry (size: %zu max: %zu)\n",
325 : : size, max);
326 : 0 : return;
327 : : }
328 : :
329 : 0 : size += entries_size;
330 : :
331 : 0 : esrt_data = (phys_addr_t)efi.esrt;
332 : 0 : esrt_data_size = size;
333 : :
334 : 0 : end = esrt_data + size;
335 : 0 : pr_info("Reserving ESRT space from %pa to %pa.\n", &esrt_data, &end);
336 [ # # ]: 0 : if (md.type == EFI_BOOT_SERVICES_DATA)
337 : 0 : efi_mem_reserve(esrt_data, esrt_data_size);
338 : :
339 : 0 : pr_debug("esrt-init: loaded.\n");
340 : : }
341 : :
342 : 0 : static int __init register_entries(void)
343 : : {
344 : 0 : struct efi_system_resource_entry_v1 *v1_entries = (void *)esrt->entries;
345 : 0 : int i, rc;
346 : :
347 : 0 : if (!esrt_table_exists())
348 : : return 0;
349 : :
350 [ # # ]: 0 : for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
351 : 0 : void *esre = NULL;
352 [ # # ]: 0 : if (esrt->fw_resource_version == 1) {
353 : 0 : esre = &v1_entries[i];
354 : : } else {
355 : 0 : pr_err("Unsupported ESRT version %lld.\n",
356 : : esrt->fw_resource_version);
357 : 0 : return -EINVAL;
358 : : }
359 : :
360 : 0 : rc = esre_create_sysfs_entry(esre, i);
361 [ # # ]: 0 : if (rc < 0) {
362 : 0 : pr_err("ESRT entry creation failed with error %d.\n",
363 : : rc);
364 : 0 : return rc;
365 : : }
366 : : }
367 : : return 0;
368 : : }
369 : :
370 : 0 : static void cleanup_entry_list(void)
371 : : {
372 : 0 : struct esre_entry *entry, *next;
373 : :
374 [ # # ]: 0 : list_for_each_entry_safe(entry, next, &entry_list, list) {
375 : 0 : kobject_put(&entry->kobj);
376 : : }
377 : 0 : }
378 : :
379 : 78 : static int __init esrt_sysfs_init(void)
380 : : {
381 : 78 : int error;
382 : :
383 : 78 : pr_debug("esrt-sysfs: loading.\n");
384 [ - + - - ]: 78 : if (!esrt_data || !esrt_data_size)
385 : : return -ENOSYS;
386 : :
387 : 0 : esrt = memremap(esrt_data, esrt_data_size, MEMREMAP_WB);
388 [ # # ]: 0 : if (!esrt) {
389 : 0 : pr_err("memremap(%pa, %zu) failed.\n", &esrt_data,
390 : : esrt_data_size);
391 : 0 : return -ENOMEM;
392 : : }
393 : :
394 : 0 : esrt_kobj = kobject_create_and_add("esrt", efi_kobj);
395 [ # # ]: 0 : if (!esrt_kobj) {
396 : 0 : pr_err("Firmware table registration failed.\n");
397 : 0 : error = -ENOMEM;
398 : 0 : goto err;
399 : : }
400 : :
401 : 0 : error = sysfs_create_group(esrt_kobj, &esrt_attr_group);
402 [ # # ]: 0 : if (error) {
403 : 0 : pr_err("Sysfs attribute export failed with error %d.\n",
404 : : error);
405 : 0 : goto err_remove_esrt;
406 : : }
407 : :
408 : 0 : esrt_kset = kset_create_and_add("entries", NULL, esrt_kobj);
409 [ # # ]: 0 : if (!esrt_kset) {
410 : 0 : pr_err("kset creation failed.\n");
411 : 0 : error = -ENOMEM;
412 : 0 : goto err_remove_group;
413 : : }
414 : :
415 : 0 : error = register_entries();
416 [ # # ]: 0 : if (error)
417 : 0 : goto err_cleanup_list;
418 : :
419 : : pr_debug("esrt-sysfs: loaded.\n");
420 : :
421 : : return 0;
422 : : err_cleanup_list:
423 : 0 : cleanup_entry_list();
424 : 0 : kset_unregister(esrt_kset);
425 : 0 : err_remove_group:
426 : 0 : sysfs_remove_group(esrt_kobj, &esrt_attr_group);
427 : 0 : err_remove_esrt:
428 : 0 : kobject_put(esrt_kobj);
429 : 0 : err:
430 : 0 : memunmap(esrt);
431 : 0 : esrt = NULL;
432 : 0 : return error;
433 : : }
434 : : device_initcall(esrt_sysfs_init);
435 : :
436 : : /*
437 : : MODULE_AUTHOR("Peter Jones <pjones@redhat.com>");
438 : : MODULE_DESCRIPTION("EFI System Resource Table support");
439 : : MODULE_LICENSE("GPL");
440 : : */
|