Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/firmware/efi/esrt.c
26428 views
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
};
79
80
static struct esre_entry *to_entry(struct kobject *kobj)
81
{
82
return container_of(kobj, struct esre_entry, kobj);
83
}
84
85
static struct esre_attribute *to_attr(struct attribute *attr)
86
{
87
return container_of(attr, struct esre_attribute, attr);
88
}
89
90
static ssize_t esre_attr_show(struct kobject *kobj,
91
struct attribute *_attr, char *buf)
92
{
93
struct esre_entry *entry = to_entry(kobj);
94
struct esre_attribute *attr = to_attr(_attr);
95
96
return attr->show(entry, buf);
97
}
98
99
static const struct sysfs_ops esre_attr_ops = {
100
.show = esre_attr_show,
101
};
102
103
/* Generic ESRT Entry ("ESRE") support. */
104
static ssize_t fw_class_show(struct esre_entry *entry, char *buf)
105
{
106
char *str = buf;
107
108
efi_guid_to_str(&entry->esre.esre1->fw_class, str);
109
str += strlen(str);
110
str += sprintf(str, "\n");
111
112
return str - buf;
113
}
114
115
static struct esre_attribute esre_fw_class = __ATTR_RO_MODE(fw_class, 0400);
116
117
#define esre_attr_decl(name, size, fmt) \
118
static ssize_t name##_show(struct esre_entry *entry, char *buf) \
119
{ \
120
return sprintf(buf, fmt "\n", \
121
le##size##_to_cpu(entry->esre.esre1->name)); \
122
} \
123
\
124
static struct esre_attribute esre_##name = __ATTR_RO_MODE(name, 0400)
125
126
esre_attr_decl(fw_type, 32, "%u");
127
esre_attr_decl(fw_version, 32, "%u");
128
esre_attr_decl(lowest_supported_fw_version, 32, "%u");
129
esre_attr_decl(capsule_flags, 32, "0x%x");
130
esre_attr_decl(last_attempt_version, 32, "%u");
131
esre_attr_decl(last_attempt_status, 32, "%u");
132
133
static struct attribute *esre1_attrs[] = {
134
&esre_fw_class.attr,
135
&esre_fw_type.attr,
136
&esre_fw_version.attr,
137
&esre_lowest_supported_fw_version.attr,
138
&esre_capsule_flags.attr,
139
&esre_last_attempt_version.attr,
140
&esre_last_attempt_status.attr,
141
NULL
142
};
143
ATTRIBUTE_GROUPS(esre1);
144
145
static void esre_release(struct kobject *kobj)
146
{
147
struct esre_entry *entry = to_entry(kobj);
148
149
list_del(&entry->list);
150
kfree(entry);
151
}
152
153
static const struct kobj_type esre1_ktype = {
154
.release = esre_release,
155
.sysfs_ops = &esre_attr_ops,
156
.default_groups = esre1_groups,
157
};
158
159
160
static struct kobject *esrt_kobj;
161
static struct kset *esrt_kset;
162
163
static int esre_create_sysfs_entry(void *esre, int entry_num)
164
{
165
struct esre_entry *entry;
166
167
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
168
if (!entry)
169
return -ENOMEM;
170
171
entry->kobj.kset = esrt_kset;
172
173
if (esrt->fw_resource_version == 1) {
174
int rc = 0;
175
176
entry->esre.esre1 = esre;
177
rc = kobject_init_and_add(&entry->kobj, &esre1_ktype, NULL,
178
"entry%d", entry_num);
179
if (rc) {
180
kobject_put(&entry->kobj);
181
return rc;
182
}
183
}
184
185
list_add_tail(&entry->list, &entry_list);
186
return 0;
187
}
188
189
/* support for displaying ESRT fields at the top level */
190
#define esrt_attr_decl(name, size, fmt) \
191
static ssize_t name##_show(struct kobject *kobj, \
192
struct kobj_attribute *attr, char *buf)\
193
{ \
194
return sprintf(buf, fmt "\n", le##size##_to_cpu(esrt->name)); \
195
} \
196
\
197
static struct kobj_attribute esrt_##name = __ATTR_RO_MODE(name, 0400)
198
199
esrt_attr_decl(fw_resource_count, 32, "%u");
200
esrt_attr_decl(fw_resource_count_max, 32, "%u");
201
esrt_attr_decl(fw_resource_version, 64, "%llu");
202
203
static struct attribute *esrt_attrs[] = {
204
&esrt_fw_resource_count.attr,
205
&esrt_fw_resource_count_max.attr,
206
&esrt_fw_resource_version.attr,
207
NULL,
208
};
209
210
static inline int esrt_table_exists(void)
211
{
212
if (!efi_enabled(EFI_CONFIG_TABLES))
213
return 0;
214
if (efi.esrt == EFI_INVALID_TABLE_ADDR)
215
return 0;
216
return 1;
217
}
218
219
static umode_t esrt_attr_is_visible(struct kobject *kobj,
220
struct attribute *attr, int n)
221
{
222
if (!esrt_table_exists())
223
return 0;
224
return attr->mode;
225
}
226
227
static const struct attribute_group esrt_attr_group = {
228
.attrs = esrt_attrs,
229
.is_visible = esrt_attr_is_visible,
230
};
231
232
/*
233
* remap the table, validate it, mark it reserved and unmap it.
234
*/
235
void __init efi_esrt_init(void)
236
{
237
void *va;
238
struct efi_system_resource_table tmpesrt;
239
size_t size, max, entry_size, entries_size;
240
efi_memory_desc_t md;
241
int rc;
242
phys_addr_t end;
243
244
if (!efi_enabled(EFI_MEMMAP) && !efi_enabled(EFI_PARAVIRT))
245
return;
246
247
pr_debug("esrt-init: loading.\n");
248
if (!esrt_table_exists())
249
return;
250
251
rc = efi_mem_desc_lookup(efi.esrt, &md);
252
if (rc < 0 ||
253
(!(md.attribute & EFI_MEMORY_RUNTIME) &&
254
md.type != EFI_BOOT_SERVICES_DATA &&
255
md.type != EFI_RUNTIME_SERVICES_DATA &&
256
md.type != EFI_ACPI_RECLAIM_MEMORY &&
257
md.type != EFI_ACPI_MEMORY_NVS)) {
258
pr_warn("ESRT header is not in the memory map.\n");
259
return;
260
}
261
262
max = efi_mem_desc_end(&md) - efi.esrt;
263
size = sizeof(*esrt);
264
265
if (max < size) {
266
pr_err("ESRT header doesn't fit on single memory map entry. (size: %zu max: %zu)\n",
267
size, max);
268
return;
269
}
270
271
va = early_memremap(efi.esrt, size);
272
if (!va) {
273
pr_err("early_memremap(%p, %zu) failed.\n", (void *)efi.esrt,
274
size);
275
return;
276
}
277
278
memcpy(&tmpesrt, va, sizeof(tmpesrt));
279
early_memunmap(va, size);
280
281
if (tmpesrt.fw_resource_version != 1) {
282
pr_err("Unsupported ESRT version %lld.\n",
283
tmpesrt.fw_resource_version);
284
return;
285
}
286
287
entry_size = sizeof(struct efi_system_resource_entry_v1);
288
if (tmpesrt.fw_resource_count > 0 && max - size < entry_size) {
289
pr_err("ESRT memory map entry can only hold the header. (max: %zu size: %zu)\n",
290
max - size, entry_size);
291
return;
292
}
293
294
/*
295
* The format doesn't really give us any boundary to test here,
296
* so I'm making up 128 as the max number of individually updatable
297
* components we support.
298
* 128 should be pretty excessive, but there's still some chance
299
* somebody will do that someday and we'll need to raise this.
300
*/
301
if (tmpesrt.fw_resource_count > 128) {
302
pr_err("ESRT says fw_resource_count has very large value %d.\n",
303
tmpesrt.fw_resource_count);
304
return;
305
}
306
307
/*
308
* We know it can't be larger than N * sizeof() here, and N is limited
309
* by the previous test to a small number, so there's no overflow.
310
*/
311
entries_size = tmpesrt.fw_resource_count * entry_size;
312
if (max < size + entries_size) {
313
pr_err("ESRT does not fit on single memory map entry (size: %zu max: %zu)\n",
314
size, max);
315
return;
316
}
317
318
size += entries_size;
319
320
esrt_data = (phys_addr_t)efi.esrt;
321
esrt_data_size = size;
322
323
end = esrt_data + size;
324
pr_info("Reserving ESRT space from %pa to %pa.\n", &esrt_data, &end);
325
if (md.type == EFI_BOOT_SERVICES_DATA)
326
efi_mem_reserve(esrt_data, esrt_data_size);
327
328
pr_debug("esrt-init: loaded.\n");
329
}
330
331
static int __init register_entries(void)
332
{
333
struct efi_system_resource_entry_v1 *v1_entries = (void *)esrt->entries;
334
int i, rc;
335
336
if (!esrt_table_exists())
337
return 0;
338
339
for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
340
void *esre = NULL;
341
if (esrt->fw_resource_version == 1) {
342
esre = &v1_entries[i];
343
} else {
344
pr_err("Unsupported ESRT version %lld.\n",
345
esrt->fw_resource_version);
346
return -EINVAL;
347
}
348
349
rc = esre_create_sysfs_entry(esre, i);
350
if (rc < 0) {
351
pr_err("ESRT entry creation failed with error %d.\n",
352
rc);
353
return rc;
354
}
355
}
356
return 0;
357
}
358
359
static void cleanup_entry_list(void)
360
{
361
struct esre_entry *entry, *next;
362
363
list_for_each_entry_safe(entry, next, &entry_list, list) {
364
kobject_put(&entry->kobj);
365
}
366
}
367
368
static int __init esrt_sysfs_init(void)
369
{
370
int error;
371
372
pr_debug("esrt-sysfs: loading.\n");
373
if (!esrt_data || !esrt_data_size)
374
return -ENOSYS;
375
376
esrt = memremap(esrt_data, esrt_data_size, MEMREMAP_WB);
377
if (!esrt) {
378
pr_err("memremap(%pa, %zu) failed.\n", &esrt_data,
379
esrt_data_size);
380
return -ENOMEM;
381
}
382
383
esrt_kobj = kobject_create_and_add("esrt", efi_kobj);
384
if (!esrt_kobj) {
385
pr_err("Firmware table registration failed.\n");
386
error = -ENOMEM;
387
goto err;
388
}
389
390
error = sysfs_create_group(esrt_kobj, &esrt_attr_group);
391
if (error) {
392
pr_err("Sysfs attribute export failed with error %d.\n",
393
error);
394
goto err_remove_esrt;
395
}
396
397
esrt_kset = kset_create_and_add("entries", NULL, esrt_kobj);
398
if (!esrt_kset) {
399
pr_err("kset creation failed.\n");
400
error = -ENOMEM;
401
goto err_remove_group;
402
}
403
404
error = register_entries();
405
if (error)
406
goto err_cleanup_list;
407
408
pr_debug("esrt-sysfs: loaded.\n");
409
410
return 0;
411
err_cleanup_list:
412
cleanup_entry_list();
413
kset_unregister(esrt_kset);
414
err_remove_group:
415
sysfs_remove_group(esrt_kobj, &esrt_attr_group);
416
err_remove_esrt:
417
kobject_put(esrt_kobj);
418
err:
419
memunmap(esrt);
420
esrt = NULL;
421
return error;
422
}
423
device_initcall(esrt_sysfs_init);
424
425
/*
426
MODULE_AUTHOR("Peter Jones <[email protected]>");
427
MODULE_DESCRIPTION("EFI System Resource Table support");
428
MODULE_LICENSE("GPL");
429
*/
430
431