162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * esrt.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This module exports EFI System Resource Table (ESRT) entries into userspace
662306a36Sopenharmony_ci * through the sysfs file system. The ESRT provides a read-only catalog of
762306a36Sopenharmony_ci * system components for which the system accepts firmware upgrades via UEFI's
862306a36Sopenharmony_ci * "Capsule Update" feature. This module allows userland utilities to evaluate
962306a36Sopenharmony_ci * what firmware updates can be applied to this system, and potentially arrange
1062306a36Sopenharmony_ci * for those updates to occur.
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Data is currently found below /sys/firmware/efi/esrt/...
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci#define pr_fmt(fmt) "esrt: " fmt
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/capability.h>
1762306a36Sopenharmony_ci#include <linux/device.h>
1862306a36Sopenharmony_ci#include <linux/efi.h>
1962306a36Sopenharmony_ci#include <linux/init.h>
2062306a36Sopenharmony_ci#include <linux/io.h>
2162306a36Sopenharmony_ci#include <linux/kernel.h>
2262306a36Sopenharmony_ci#include <linux/kobject.h>
2362306a36Sopenharmony_ci#include <linux/list.h>
2462306a36Sopenharmony_ci#include <linux/memblock.h>
2562306a36Sopenharmony_ci#include <linux/slab.h>
2662306a36Sopenharmony_ci#include <linux/types.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <asm/io.h>
2962306a36Sopenharmony_ci#include <asm/early_ioremap.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistruct efi_system_resource_entry_v1 {
3262306a36Sopenharmony_ci	efi_guid_t	fw_class;
3362306a36Sopenharmony_ci	u32		fw_type;
3462306a36Sopenharmony_ci	u32		fw_version;
3562306a36Sopenharmony_ci	u32		lowest_supported_fw_version;
3662306a36Sopenharmony_ci	u32		capsule_flags;
3762306a36Sopenharmony_ci	u32		last_attempt_version;
3862306a36Sopenharmony_ci	u32		last_attempt_status;
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/*
4262306a36Sopenharmony_ci * _count and _version are what they seem like.  _max is actually just
4362306a36Sopenharmony_ci * accounting info for the firmware when creating the table; it should never
4462306a36Sopenharmony_ci * have been exposed to us.  To wit, the spec says:
4562306a36Sopenharmony_ci * The maximum number of resource array entries that can be within the
4662306a36Sopenharmony_ci * table without reallocating the table, must not be zero.
4762306a36Sopenharmony_ci * Since there's no guidance about what that means in terms of memory layout,
4862306a36Sopenharmony_ci * it means nothing to us.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_cistruct efi_system_resource_table {
5162306a36Sopenharmony_ci	u32	fw_resource_count;
5262306a36Sopenharmony_ci	u32	fw_resource_count_max;
5362306a36Sopenharmony_ci	u64	fw_resource_version;
5462306a36Sopenharmony_ci	u8	entries[];
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic phys_addr_t esrt_data;
5862306a36Sopenharmony_cistatic size_t esrt_data_size;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic struct efi_system_resource_table *esrt;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistruct esre_entry {
6362306a36Sopenharmony_ci	union {
6462306a36Sopenharmony_ci		struct efi_system_resource_entry_v1 *esre1;
6562306a36Sopenharmony_ci	} esre;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	struct kobject kobj;
6862306a36Sopenharmony_ci	struct list_head list;
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* global list of esre_entry. */
7262306a36Sopenharmony_cistatic LIST_HEAD(entry_list);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/* entry attribute */
7562306a36Sopenharmony_cistruct esre_attribute {
7662306a36Sopenharmony_ci	struct attribute attr;
7762306a36Sopenharmony_ci	ssize_t (*show)(struct esre_entry *entry, char *buf);
7862306a36Sopenharmony_ci	ssize_t (*store)(struct esre_entry *entry,
7962306a36Sopenharmony_ci			 const char *buf, size_t count);
8062306a36Sopenharmony_ci};
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic struct esre_entry *to_entry(struct kobject *kobj)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	return container_of(kobj, struct esre_entry, kobj);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic struct esre_attribute *to_attr(struct attribute *attr)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	return container_of(attr, struct esre_attribute, attr);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic ssize_t esre_attr_show(struct kobject *kobj,
9362306a36Sopenharmony_ci			      struct attribute *_attr, char *buf)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct esre_entry *entry = to_entry(kobj);
9662306a36Sopenharmony_ci	struct esre_attribute *attr = to_attr(_attr);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return attr->show(entry, buf);
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic const struct sysfs_ops esre_attr_ops = {
10262306a36Sopenharmony_ci	.show = esre_attr_show,
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/* Generic ESRT Entry ("ESRE") support. */
10662306a36Sopenharmony_cistatic ssize_t fw_class_show(struct esre_entry *entry, char *buf)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	char *str = buf;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	efi_guid_to_str(&entry->esre.esre1->fw_class, str);
11162306a36Sopenharmony_ci	str += strlen(str);
11262306a36Sopenharmony_ci	str += sprintf(str, "\n");
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return str - buf;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic struct esre_attribute esre_fw_class = __ATTR_RO_MODE(fw_class, 0400);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci#define esre_attr_decl(name, size, fmt) \
12062306a36Sopenharmony_cistatic ssize_t name##_show(struct esre_entry *entry, char *buf) \
12162306a36Sopenharmony_ci{ \
12262306a36Sopenharmony_ci	return sprintf(buf, fmt "\n", \
12362306a36Sopenharmony_ci		       le##size##_to_cpu(entry->esre.esre1->name)); \
12462306a36Sopenharmony_ci} \
12562306a36Sopenharmony_ci\
12662306a36Sopenharmony_cistatic struct esre_attribute esre_##name = __ATTR_RO_MODE(name, 0400)
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ciesre_attr_decl(fw_type, 32, "%u");
12962306a36Sopenharmony_ciesre_attr_decl(fw_version, 32, "%u");
13062306a36Sopenharmony_ciesre_attr_decl(lowest_supported_fw_version, 32, "%u");
13162306a36Sopenharmony_ciesre_attr_decl(capsule_flags, 32, "0x%x");
13262306a36Sopenharmony_ciesre_attr_decl(last_attempt_version, 32, "%u");
13362306a36Sopenharmony_ciesre_attr_decl(last_attempt_status, 32, "%u");
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic struct attribute *esre1_attrs[] = {
13662306a36Sopenharmony_ci	&esre_fw_class.attr,
13762306a36Sopenharmony_ci	&esre_fw_type.attr,
13862306a36Sopenharmony_ci	&esre_fw_version.attr,
13962306a36Sopenharmony_ci	&esre_lowest_supported_fw_version.attr,
14062306a36Sopenharmony_ci	&esre_capsule_flags.attr,
14162306a36Sopenharmony_ci	&esre_last_attempt_version.attr,
14262306a36Sopenharmony_ci	&esre_last_attempt_status.attr,
14362306a36Sopenharmony_ci	NULL
14462306a36Sopenharmony_ci};
14562306a36Sopenharmony_ciATTRIBUTE_GROUPS(esre1);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic void esre_release(struct kobject *kobj)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct esre_entry *entry = to_entry(kobj);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	list_del(&entry->list);
15262306a36Sopenharmony_ci	kfree(entry);
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic const struct kobj_type esre1_ktype = {
15662306a36Sopenharmony_ci	.release = esre_release,
15762306a36Sopenharmony_ci	.sysfs_ops = &esre_attr_ops,
15862306a36Sopenharmony_ci	.default_groups = esre1_groups,
15962306a36Sopenharmony_ci};
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic struct kobject *esrt_kobj;
16362306a36Sopenharmony_cistatic struct kset *esrt_kset;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic int esre_create_sysfs_entry(void *esre, int entry_num)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct esre_entry *entry;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
17062306a36Sopenharmony_ci	if (!entry)
17162306a36Sopenharmony_ci		return -ENOMEM;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	entry->kobj.kset = esrt_kset;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (esrt->fw_resource_version == 1) {
17662306a36Sopenharmony_ci		int rc = 0;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci		entry->esre.esre1 = esre;
17962306a36Sopenharmony_ci		rc = kobject_init_and_add(&entry->kobj, &esre1_ktype, NULL,
18062306a36Sopenharmony_ci					  "entry%d", entry_num);
18162306a36Sopenharmony_ci		if (rc) {
18262306a36Sopenharmony_ci			kobject_put(&entry->kobj);
18362306a36Sopenharmony_ci			return rc;
18462306a36Sopenharmony_ci		}
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	list_add_tail(&entry->list, &entry_list);
18862306a36Sopenharmony_ci	return 0;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci/* support for displaying ESRT fields at the top level */
19262306a36Sopenharmony_ci#define esrt_attr_decl(name, size, fmt) \
19362306a36Sopenharmony_cistatic ssize_t name##_show(struct kobject *kobj, \
19462306a36Sopenharmony_ci				  struct kobj_attribute *attr, char *buf)\
19562306a36Sopenharmony_ci{ \
19662306a36Sopenharmony_ci	return sprintf(buf, fmt "\n", le##size##_to_cpu(esrt->name)); \
19762306a36Sopenharmony_ci} \
19862306a36Sopenharmony_ci\
19962306a36Sopenharmony_cistatic struct kobj_attribute esrt_##name = __ATTR_RO_MODE(name, 0400)
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ciesrt_attr_decl(fw_resource_count, 32, "%u");
20262306a36Sopenharmony_ciesrt_attr_decl(fw_resource_count_max, 32, "%u");
20362306a36Sopenharmony_ciesrt_attr_decl(fw_resource_version, 64, "%llu");
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic struct attribute *esrt_attrs[] = {
20662306a36Sopenharmony_ci	&esrt_fw_resource_count.attr,
20762306a36Sopenharmony_ci	&esrt_fw_resource_count_max.attr,
20862306a36Sopenharmony_ci	&esrt_fw_resource_version.attr,
20962306a36Sopenharmony_ci	NULL,
21062306a36Sopenharmony_ci};
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic inline int esrt_table_exists(void)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	if (!efi_enabled(EFI_CONFIG_TABLES))
21562306a36Sopenharmony_ci		return 0;
21662306a36Sopenharmony_ci	if (efi.esrt == EFI_INVALID_TABLE_ADDR)
21762306a36Sopenharmony_ci		return 0;
21862306a36Sopenharmony_ci	return 1;
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic umode_t esrt_attr_is_visible(struct kobject *kobj,
22262306a36Sopenharmony_ci				    struct attribute *attr, int n)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	if (!esrt_table_exists())
22562306a36Sopenharmony_ci		return 0;
22662306a36Sopenharmony_ci	return attr->mode;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic const struct attribute_group esrt_attr_group = {
23062306a36Sopenharmony_ci	.attrs = esrt_attrs,
23162306a36Sopenharmony_ci	.is_visible = esrt_attr_is_visible,
23262306a36Sopenharmony_ci};
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/*
23562306a36Sopenharmony_ci * remap the table, validate it, mark it reserved and unmap it.
23662306a36Sopenharmony_ci */
23762306a36Sopenharmony_civoid __init efi_esrt_init(void)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	void *va;
24062306a36Sopenharmony_ci	struct efi_system_resource_table tmpesrt;
24162306a36Sopenharmony_ci	size_t size, max, entry_size, entries_size;
24262306a36Sopenharmony_ci	efi_memory_desc_t md;
24362306a36Sopenharmony_ci	int rc;
24462306a36Sopenharmony_ci	phys_addr_t end;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (!efi_enabled(EFI_MEMMAP) && !efi_enabled(EFI_PARAVIRT))
24762306a36Sopenharmony_ci		return;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	pr_debug("esrt-init: loading.\n");
25062306a36Sopenharmony_ci	if (!esrt_table_exists())
25162306a36Sopenharmony_ci		return;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	rc = efi_mem_desc_lookup(efi.esrt, &md);
25462306a36Sopenharmony_ci	if (rc < 0 ||
25562306a36Sopenharmony_ci	    (!(md.attribute & EFI_MEMORY_RUNTIME) &&
25662306a36Sopenharmony_ci	     md.type != EFI_BOOT_SERVICES_DATA &&
25762306a36Sopenharmony_ci	     md.type != EFI_RUNTIME_SERVICES_DATA &&
25862306a36Sopenharmony_ci	     md.type != EFI_ACPI_RECLAIM_MEMORY &&
25962306a36Sopenharmony_ci	     md.type != EFI_ACPI_MEMORY_NVS)) {
26062306a36Sopenharmony_ci		pr_warn("ESRT header is not in the memory map.\n");
26162306a36Sopenharmony_ci		return;
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	max = efi_mem_desc_end(&md) - efi.esrt;
26562306a36Sopenharmony_ci	size = sizeof(*esrt);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (max < size) {
26862306a36Sopenharmony_ci		pr_err("ESRT header doesn't fit on single memory map entry. (size: %zu max: %zu)\n",
26962306a36Sopenharmony_ci		       size, max);
27062306a36Sopenharmony_ci		return;
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	va = early_memremap(efi.esrt, size);
27462306a36Sopenharmony_ci	if (!va) {
27562306a36Sopenharmony_ci		pr_err("early_memremap(%p, %zu) failed.\n", (void *)efi.esrt,
27662306a36Sopenharmony_ci		       size);
27762306a36Sopenharmony_ci		return;
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	memcpy(&tmpesrt, va, sizeof(tmpesrt));
28162306a36Sopenharmony_ci	early_memunmap(va, size);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (tmpesrt.fw_resource_version != 1) {
28462306a36Sopenharmony_ci		pr_err("Unsupported ESRT version %lld.\n",
28562306a36Sopenharmony_ci		       tmpesrt.fw_resource_version);
28662306a36Sopenharmony_ci		return;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	entry_size = sizeof(struct efi_system_resource_entry_v1);
29062306a36Sopenharmony_ci	if (tmpesrt.fw_resource_count > 0 && max - size < entry_size) {
29162306a36Sopenharmony_ci		pr_err("ESRT memory map entry can only hold the header. (max: %zu size: %zu)\n",
29262306a36Sopenharmony_ci		       max - size, entry_size);
29362306a36Sopenharmony_ci		return;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/*
29762306a36Sopenharmony_ci	 * The format doesn't really give us any boundary to test here,
29862306a36Sopenharmony_ci	 * so I'm making up 128 as the max number of individually updatable
29962306a36Sopenharmony_ci	 * components we support.
30062306a36Sopenharmony_ci	 * 128 should be pretty excessive, but there's still some chance
30162306a36Sopenharmony_ci	 * somebody will do that someday and we'll need to raise this.
30262306a36Sopenharmony_ci	 */
30362306a36Sopenharmony_ci	if (tmpesrt.fw_resource_count > 128) {
30462306a36Sopenharmony_ci		pr_err("ESRT says fw_resource_count has very large value %d.\n",
30562306a36Sopenharmony_ci		       tmpesrt.fw_resource_count);
30662306a36Sopenharmony_ci		return;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/*
31062306a36Sopenharmony_ci	 * We know it can't be larger than N * sizeof() here, and N is limited
31162306a36Sopenharmony_ci	 * by the previous test to a small number, so there's no overflow.
31262306a36Sopenharmony_ci	 */
31362306a36Sopenharmony_ci	entries_size = tmpesrt.fw_resource_count * entry_size;
31462306a36Sopenharmony_ci	if (max < size + entries_size) {
31562306a36Sopenharmony_ci		pr_err("ESRT does not fit on single memory map entry (size: %zu max: %zu)\n",
31662306a36Sopenharmony_ci		       size, max);
31762306a36Sopenharmony_ci		return;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	size += entries_size;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	esrt_data = (phys_addr_t)efi.esrt;
32362306a36Sopenharmony_ci	esrt_data_size = size;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	end = esrt_data + size;
32662306a36Sopenharmony_ci	pr_info("Reserving ESRT space from %pa to %pa.\n", &esrt_data, &end);
32762306a36Sopenharmony_ci	if (md.type == EFI_BOOT_SERVICES_DATA)
32862306a36Sopenharmony_ci		efi_mem_reserve(esrt_data, esrt_data_size);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	pr_debug("esrt-init: loaded.\n");
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic int __init register_entries(void)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct efi_system_resource_entry_v1 *v1_entries = (void *)esrt->entries;
33662306a36Sopenharmony_ci	int i, rc;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (!esrt_table_exists())
33962306a36Sopenharmony_ci		return 0;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
34262306a36Sopenharmony_ci		void *esre = NULL;
34362306a36Sopenharmony_ci		if (esrt->fw_resource_version == 1) {
34462306a36Sopenharmony_ci			esre = &v1_entries[i];
34562306a36Sopenharmony_ci		} else {
34662306a36Sopenharmony_ci			pr_err("Unsupported ESRT version %lld.\n",
34762306a36Sopenharmony_ci			       esrt->fw_resource_version);
34862306a36Sopenharmony_ci			return -EINVAL;
34962306a36Sopenharmony_ci		}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci		rc = esre_create_sysfs_entry(esre, i);
35262306a36Sopenharmony_ci		if (rc < 0) {
35362306a36Sopenharmony_ci			pr_err("ESRT entry creation failed with error %d.\n",
35462306a36Sopenharmony_ci			       rc);
35562306a36Sopenharmony_ci			return rc;
35662306a36Sopenharmony_ci		}
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci	return 0;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic void cleanup_entry_list(void)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct esre_entry *entry, *next;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	list_for_each_entry_safe(entry, next, &entry_list, list) {
36662306a36Sopenharmony_ci		kobject_put(&entry->kobj);
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic int __init esrt_sysfs_init(void)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	int error;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	pr_debug("esrt-sysfs: loading.\n");
37562306a36Sopenharmony_ci	if (!esrt_data || !esrt_data_size)
37662306a36Sopenharmony_ci		return -ENOSYS;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	esrt = memremap(esrt_data, esrt_data_size, MEMREMAP_WB);
37962306a36Sopenharmony_ci	if (!esrt) {
38062306a36Sopenharmony_ci		pr_err("memremap(%pa, %zu) failed.\n", &esrt_data,
38162306a36Sopenharmony_ci		       esrt_data_size);
38262306a36Sopenharmony_ci		return -ENOMEM;
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	esrt_kobj = kobject_create_and_add("esrt", efi_kobj);
38662306a36Sopenharmony_ci	if (!esrt_kobj) {
38762306a36Sopenharmony_ci		pr_err("Firmware table registration failed.\n");
38862306a36Sopenharmony_ci		error = -ENOMEM;
38962306a36Sopenharmony_ci		goto err;
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	error = sysfs_create_group(esrt_kobj, &esrt_attr_group);
39362306a36Sopenharmony_ci	if (error) {
39462306a36Sopenharmony_ci		pr_err("Sysfs attribute export failed with error %d.\n",
39562306a36Sopenharmony_ci		       error);
39662306a36Sopenharmony_ci		goto err_remove_esrt;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	esrt_kset = kset_create_and_add("entries", NULL, esrt_kobj);
40062306a36Sopenharmony_ci	if (!esrt_kset) {
40162306a36Sopenharmony_ci		pr_err("kset creation failed.\n");
40262306a36Sopenharmony_ci		error = -ENOMEM;
40362306a36Sopenharmony_ci		goto err_remove_group;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	error = register_entries();
40762306a36Sopenharmony_ci	if (error)
40862306a36Sopenharmony_ci		goto err_cleanup_list;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	pr_debug("esrt-sysfs: loaded.\n");
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	return 0;
41362306a36Sopenharmony_cierr_cleanup_list:
41462306a36Sopenharmony_ci	cleanup_entry_list();
41562306a36Sopenharmony_ci	kset_unregister(esrt_kset);
41662306a36Sopenharmony_cierr_remove_group:
41762306a36Sopenharmony_ci	sysfs_remove_group(esrt_kobj, &esrt_attr_group);
41862306a36Sopenharmony_cierr_remove_esrt:
41962306a36Sopenharmony_ci	kobject_put(esrt_kobj);
42062306a36Sopenharmony_cierr:
42162306a36Sopenharmony_ci	memunmap(esrt);
42262306a36Sopenharmony_ci	esrt = NULL;
42362306a36Sopenharmony_ci	return error;
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_cidevice_initcall(esrt_sysfs_init);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci/*
42862306a36Sopenharmony_ciMODULE_AUTHOR("Peter Jones <pjones@redhat.com>");
42962306a36Sopenharmony_ciMODULE_DESCRIPTION("EFI System Resource Table support");
43062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
43162306a36Sopenharmony_ci*/
432