18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * esrt.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This module exports EFI System Resource Table (ESRT) entries into userspace
68c2ecf20Sopenharmony_ci * through the sysfs file system. The ESRT provides a read-only catalog of
78c2ecf20Sopenharmony_ci * system components for which the system accepts firmware upgrades via UEFI's
88c2ecf20Sopenharmony_ci * "Capsule Update" feature. This module allows userland utilities to evaluate
98c2ecf20Sopenharmony_ci * what firmware updates can be applied to this system, and potentially arrange
108c2ecf20Sopenharmony_ci * for those updates to occur.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Data is currently found below /sys/firmware/efi/esrt/...
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "esrt: " fmt
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/capability.h>
178c2ecf20Sopenharmony_ci#include <linux/device.h>
188c2ecf20Sopenharmony_ci#include <linux/efi.h>
198c2ecf20Sopenharmony_ci#include <linux/init.h>
208c2ecf20Sopenharmony_ci#include <linux/io.h>
218c2ecf20Sopenharmony_ci#include <linux/kernel.h>
228c2ecf20Sopenharmony_ci#include <linux/kobject.h>
238c2ecf20Sopenharmony_ci#include <linux/list.h>
248c2ecf20Sopenharmony_ci#include <linux/memblock.h>
258c2ecf20Sopenharmony_ci#include <linux/slab.h>
268c2ecf20Sopenharmony_ci#include <linux/types.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <asm/io.h>
298c2ecf20Sopenharmony_ci#include <asm/early_ioremap.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistruct efi_system_resource_entry_v1 {
328c2ecf20Sopenharmony_ci	efi_guid_t	fw_class;
338c2ecf20Sopenharmony_ci	u32		fw_type;
348c2ecf20Sopenharmony_ci	u32		fw_version;
358c2ecf20Sopenharmony_ci	u32		lowest_supported_fw_version;
368c2ecf20Sopenharmony_ci	u32		capsule_flags;
378c2ecf20Sopenharmony_ci	u32		last_attempt_version;
388c2ecf20Sopenharmony_ci	u32		last_attempt_status;
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/*
428c2ecf20Sopenharmony_ci * _count and _version are what they seem like.  _max is actually just
438c2ecf20Sopenharmony_ci * accounting info for the firmware when creating the table; it should never
448c2ecf20Sopenharmony_ci * have been exposed to us.  To wit, the spec says:
458c2ecf20Sopenharmony_ci * The maximum number of resource array entries that can be within the
468c2ecf20Sopenharmony_ci * table without reallocating the table, must not be zero.
478c2ecf20Sopenharmony_ci * Since there's no guidance about what that means in terms of memory layout,
488c2ecf20Sopenharmony_ci * it means nothing to us.
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_cistruct efi_system_resource_table {
518c2ecf20Sopenharmony_ci	u32	fw_resource_count;
528c2ecf20Sopenharmony_ci	u32	fw_resource_count_max;
538c2ecf20Sopenharmony_ci	u64	fw_resource_version;
548c2ecf20Sopenharmony_ci	u8	entries[];
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic phys_addr_t esrt_data;
588c2ecf20Sopenharmony_cistatic size_t esrt_data_size;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic struct efi_system_resource_table *esrt;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistruct esre_entry {
638c2ecf20Sopenharmony_ci	union {
648c2ecf20Sopenharmony_ci		struct efi_system_resource_entry_v1 *esre1;
658c2ecf20Sopenharmony_ci	} esre;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	struct kobject kobj;
688c2ecf20Sopenharmony_ci	struct list_head list;
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/* global list of esre_entry. */
728c2ecf20Sopenharmony_cistatic LIST_HEAD(entry_list);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/* entry attribute */
758c2ecf20Sopenharmony_cistruct esre_attribute {
768c2ecf20Sopenharmony_ci	struct attribute attr;
778c2ecf20Sopenharmony_ci	ssize_t (*show)(struct esre_entry *entry, char *buf);
788c2ecf20Sopenharmony_ci	ssize_t (*store)(struct esre_entry *entry,
798c2ecf20Sopenharmony_ci			 const char *buf, size_t count);
808c2ecf20Sopenharmony_ci};
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic struct esre_entry *to_entry(struct kobject *kobj)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	return container_of(kobj, struct esre_entry, kobj);
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic struct esre_attribute *to_attr(struct attribute *attr)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	return container_of(attr, struct esre_attribute, attr);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic ssize_t esre_attr_show(struct kobject *kobj,
938c2ecf20Sopenharmony_ci			      struct attribute *_attr, char *buf)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	struct esre_entry *entry = to_entry(kobj);
968c2ecf20Sopenharmony_ci	struct esre_attribute *attr = to_attr(_attr);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	/* Don't tell normal users what firmware versions we've got... */
998c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
1008c2ecf20Sopenharmony_ci		return -EACCES;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return attr->show(entry, buf);
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic const struct sysfs_ops esre_attr_ops = {
1068c2ecf20Sopenharmony_ci	.show = esre_attr_show,
1078c2ecf20Sopenharmony_ci};
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/* Generic ESRT Entry ("ESRE") support. */
1108c2ecf20Sopenharmony_cistatic ssize_t fw_class_show(struct esre_entry *entry, char *buf)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	char *str = buf;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	efi_guid_to_str(&entry->esre.esre1->fw_class, str);
1158c2ecf20Sopenharmony_ci	str += strlen(str);
1168c2ecf20Sopenharmony_ci	str += sprintf(str, "\n");
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return str - buf;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic struct esre_attribute esre_fw_class = __ATTR_RO_MODE(fw_class, 0400);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci#define esre_attr_decl(name, size, fmt) \
1248c2ecf20Sopenharmony_cistatic ssize_t name##_show(struct esre_entry *entry, char *buf) \
1258c2ecf20Sopenharmony_ci{ \
1268c2ecf20Sopenharmony_ci	return sprintf(buf, fmt "\n", \
1278c2ecf20Sopenharmony_ci		       le##size##_to_cpu(entry->esre.esre1->name)); \
1288c2ecf20Sopenharmony_ci} \
1298c2ecf20Sopenharmony_ci\
1308c2ecf20Sopenharmony_cistatic struct esre_attribute esre_##name = __ATTR_RO_MODE(name, 0400)
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ciesre_attr_decl(fw_type, 32, "%u");
1338c2ecf20Sopenharmony_ciesre_attr_decl(fw_version, 32, "%u");
1348c2ecf20Sopenharmony_ciesre_attr_decl(lowest_supported_fw_version, 32, "%u");
1358c2ecf20Sopenharmony_ciesre_attr_decl(capsule_flags, 32, "0x%x");
1368c2ecf20Sopenharmony_ciesre_attr_decl(last_attempt_version, 32, "%u");
1378c2ecf20Sopenharmony_ciesre_attr_decl(last_attempt_status, 32, "%u");
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic struct attribute *esre1_attrs[] = {
1408c2ecf20Sopenharmony_ci	&esre_fw_class.attr,
1418c2ecf20Sopenharmony_ci	&esre_fw_type.attr,
1428c2ecf20Sopenharmony_ci	&esre_fw_version.attr,
1438c2ecf20Sopenharmony_ci	&esre_lowest_supported_fw_version.attr,
1448c2ecf20Sopenharmony_ci	&esre_capsule_flags.attr,
1458c2ecf20Sopenharmony_ci	&esre_last_attempt_version.attr,
1468c2ecf20Sopenharmony_ci	&esre_last_attempt_status.attr,
1478c2ecf20Sopenharmony_ci	NULL
1488c2ecf20Sopenharmony_ci};
1498c2ecf20Sopenharmony_cistatic void esre_release(struct kobject *kobj)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct esre_entry *entry = to_entry(kobj);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	list_del(&entry->list);
1548c2ecf20Sopenharmony_ci	kfree(entry);
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic struct kobj_type esre1_ktype = {
1588c2ecf20Sopenharmony_ci	.release = esre_release,
1598c2ecf20Sopenharmony_ci	.sysfs_ops = &esre_attr_ops,
1608c2ecf20Sopenharmony_ci	.default_attrs = esre1_attrs,
1618c2ecf20Sopenharmony_ci};
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic struct kobject *esrt_kobj;
1658c2ecf20Sopenharmony_cistatic struct kset *esrt_kset;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic int esre_create_sysfs_entry(void *esre, int entry_num)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct esre_entry *entry;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
1728c2ecf20Sopenharmony_ci	if (!entry)
1738c2ecf20Sopenharmony_ci		return -ENOMEM;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	entry->kobj.kset = esrt_kset;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (esrt->fw_resource_version == 1) {
1788c2ecf20Sopenharmony_ci		int rc = 0;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci		entry->esre.esre1 = esre;
1818c2ecf20Sopenharmony_ci		rc = kobject_init_and_add(&entry->kobj, &esre1_ktype, NULL,
1828c2ecf20Sopenharmony_ci					  "entry%d", entry_num);
1838c2ecf20Sopenharmony_ci		if (rc) {
1848c2ecf20Sopenharmony_ci			kobject_put(&entry->kobj);
1858c2ecf20Sopenharmony_ci			return rc;
1868c2ecf20Sopenharmony_ci		}
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	list_add_tail(&entry->list, &entry_list);
1908c2ecf20Sopenharmony_ci	return 0;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci/* support for displaying ESRT fields at the top level */
1948c2ecf20Sopenharmony_ci#define esrt_attr_decl(name, size, fmt) \
1958c2ecf20Sopenharmony_cistatic ssize_t name##_show(struct kobject *kobj, \
1968c2ecf20Sopenharmony_ci				  struct kobj_attribute *attr, char *buf)\
1978c2ecf20Sopenharmony_ci{ \
1988c2ecf20Sopenharmony_ci	return sprintf(buf, fmt "\n", le##size##_to_cpu(esrt->name)); \
1998c2ecf20Sopenharmony_ci} \
2008c2ecf20Sopenharmony_ci\
2018c2ecf20Sopenharmony_cistatic struct kobj_attribute esrt_##name = __ATTR_RO_MODE(name, 0400)
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ciesrt_attr_decl(fw_resource_count, 32, "%u");
2048c2ecf20Sopenharmony_ciesrt_attr_decl(fw_resource_count_max, 32, "%u");
2058c2ecf20Sopenharmony_ciesrt_attr_decl(fw_resource_version, 64, "%llu");
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic struct attribute *esrt_attrs[] = {
2088c2ecf20Sopenharmony_ci	&esrt_fw_resource_count.attr,
2098c2ecf20Sopenharmony_ci	&esrt_fw_resource_count_max.attr,
2108c2ecf20Sopenharmony_ci	&esrt_fw_resource_version.attr,
2118c2ecf20Sopenharmony_ci	NULL,
2128c2ecf20Sopenharmony_ci};
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic inline int esrt_table_exists(void)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	if (!efi_enabled(EFI_CONFIG_TABLES))
2178c2ecf20Sopenharmony_ci		return 0;
2188c2ecf20Sopenharmony_ci	if (efi.esrt == EFI_INVALID_TABLE_ADDR)
2198c2ecf20Sopenharmony_ci		return 0;
2208c2ecf20Sopenharmony_ci	return 1;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic umode_t esrt_attr_is_visible(struct kobject *kobj,
2248c2ecf20Sopenharmony_ci				    struct attribute *attr, int n)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	if (!esrt_table_exists())
2278c2ecf20Sopenharmony_ci		return 0;
2288c2ecf20Sopenharmony_ci	return attr->mode;
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic const struct attribute_group esrt_attr_group = {
2328c2ecf20Sopenharmony_ci	.attrs = esrt_attrs,
2338c2ecf20Sopenharmony_ci	.is_visible = esrt_attr_is_visible,
2348c2ecf20Sopenharmony_ci};
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci/*
2378c2ecf20Sopenharmony_ci * remap the table, validate it, mark it reserved and unmap it.
2388c2ecf20Sopenharmony_ci */
2398c2ecf20Sopenharmony_civoid __init efi_esrt_init(void)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	void *va;
2428c2ecf20Sopenharmony_ci	struct efi_system_resource_table tmpesrt;
2438c2ecf20Sopenharmony_ci	size_t size, max, entry_size, entries_size;
2448c2ecf20Sopenharmony_ci	efi_memory_desc_t md;
2458c2ecf20Sopenharmony_ci	int rc;
2468c2ecf20Sopenharmony_ci	phys_addr_t end;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (!efi_enabled(EFI_MEMMAP))
2498c2ecf20Sopenharmony_ci		return;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	pr_debug("esrt-init: loading.\n");
2528c2ecf20Sopenharmony_ci	if (!esrt_table_exists())
2538c2ecf20Sopenharmony_ci		return;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	rc = efi_mem_desc_lookup(efi.esrt, &md);
2568c2ecf20Sopenharmony_ci	if (rc < 0 ||
2578c2ecf20Sopenharmony_ci	    (!(md.attribute & EFI_MEMORY_RUNTIME) &&
2588c2ecf20Sopenharmony_ci	     md.type != EFI_BOOT_SERVICES_DATA &&
2598c2ecf20Sopenharmony_ci	     md.type != EFI_RUNTIME_SERVICES_DATA)) {
2608c2ecf20Sopenharmony_ci		pr_warn("ESRT header is not in the memory map.\n");
2618c2ecf20Sopenharmony_ci		return;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	max = efi_mem_desc_end(&md);
2658c2ecf20Sopenharmony_ci	if (max < efi.esrt) {
2668c2ecf20Sopenharmony_ci		pr_err("EFI memory descriptor is invalid. (esrt: %p max: %p)\n",
2678c2ecf20Sopenharmony_ci		       (void *)efi.esrt, (void *)max);
2688c2ecf20Sopenharmony_ci		return;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	size = sizeof(*esrt);
2728c2ecf20Sopenharmony_ci	max -= efi.esrt;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (max < size) {
2758c2ecf20Sopenharmony_ci		pr_err("ESRT header doesn't fit on single memory map entry. (size: %zu max: %zu)\n",
2768c2ecf20Sopenharmony_ci		       size, max);
2778c2ecf20Sopenharmony_ci		return;
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	va = early_memremap(efi.esrt, size);
2818c2ecf20Sopenharmony_ci	if (!va) {
2828c2ecf20Sopenharmony_ci		pr_err("early_memremap(%p, %zu) failed.\n", (void *)efi.esrt,
2838c2ecf20Sopenharmony_ci		       size);
2848c2ecf20Sopenharmony_ci		return;
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	memcpy(&tmpesrt, va, sizeof(tmpesrt));
2888c2ecf20Sopenharmony_ci	early_memunmap(va, size);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if (tmpesrt.fw_resource_version != 1) {
2918c2ecf20Sopenharmony_ci		pr_err("Unsupported ESRT version %lld.\n",
2928c2ecf20Sopenharmony_ci		       tmpesrt.fw_resource_version);
2938c2ecf20Sopenharmony_ci		return;
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	entry_size = sizeof(struct efi_system_resource_entry_v1);
2978c2ecf20Sopenharmony_ci	if (tmpesrt.fw_resource_count > 0 && max - size < entry_size) {
2988c2ecf20Sopenharmony_ci		pr_err("ESRT memory map entry can only hold the header. (max: %zu size: %zu)\n",
2998c2ecf20Sopenharmony_ci		       max - size, entry_size);
3008c2ecf20Sopenharmony_ci		return;
3018c2ecf20Sopenharmony_ci	}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	/*
3048c2ecf20Sopenharmony_ci	 * The format doesn't really give us any boundary to test here,
3058c2ecf20Sopenharmony_ci	 * so I'm making up 128 as the max number of individually updatable
3068c2ecf20Sopenharmony_ci	 * components we support.
3078c2ecf20Sopenharmony_ci	 * 128 should be pretty excessive, but there's still some chance
3088c2ecf20Sopenharmony_ci	 * somebody will do that someday and we'll need to raise this.
3098c2ecf20Sopenharmony_ci	 */
3108c2ecf20Sopenharmony_ci	if (tmpesrt.fw_resource_count > 128) {
3118c2ecf20Sopenharmony_ci		pr_err("ESRT says fw_resource_count has very large value %d.\n",
3128c2ecf20Sopenharmony_ci		       tmpesrt.fw_resource_count);
3138c2ecf20Sopenharmony_ci		return;
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/*
3178c2ecf20Sopenharmony_ci	 * We know it can't be larger than N * sizeof() here, and N is limited
3188c2ecf20Sopenharmony_ci	 * by the previous test to a small number, so there's no overflow.
3198c2ecf20Sopenharmony_ci	 */
3208c2ecf20Sopenharmony_ci	entries_size = tmpesrt.fw_resource_count * entry_size;
3218c2ecf20Sopenharmony_ci	if (max < size + entries_size) {
3228c2ecf20Sopenharmony_ci		pr_err("ESRT does not fit on single memory map entry (size: %zu max: %zu)\n",
3238c2ecf20Sopenharmony_ci		       size, max);
3248c2ecf20Sopenharmony_ci		return;
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	size += entries_size;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	esrt_data = (phys_addr_t)efi.esrt;
3308c2ecf20Sopenharmony_ci	esrt_data_size = size;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	end = esrt_data + size;
3338c2ecf20Sopenharmony_ci	pr_info("Reserving ESRT space from %pa to %pa.\n", &esrt_data, &end);
3348c2ecf20Sopenharmony_ci	if (md.type == EFI_BOOT_SERVICES_DATA)
3358c2ecf20Sopenharmony_ci		efi_mem_reserve(esrt_data, esrt_data_size);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	pr_debug("esrt-init: loaded.\n");
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic int __init register_entries(void)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct efi_system_resource_entry_v1 *v1_entries = (void *)esrt->entries;
3438c2ecf20Sopenharmony_ci	int i, rc;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	if (!esrt_table_exists())
3468c2ecf20Sopenharmony_ci		return 0;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
3498c2ecf20Sopenharmony_ci		void *esre = NULL;
3508c2ecf20Sopenharmony_ci		if (esrt->fw_resource_version == 1) {
3518c2ecf20Sopenharmony_ci			esre = &v1_entries[i];
3528c2ecf20Sopenharmony_ci		} else {
3538c2ecf20Sopenharmony_ci			pr_err("Unsupported ESRT version %lld.\n",
3548c2ecf20Sopenharmony_ci			       esrt->fw_resource_version);
3558c2ecf20Sopenharmony_ci			return -EINVAL;
3568c2ecf20Sopenharmony_ci		}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci		rc = esre_create_sysfs_entry(esre, i);
3598c2ecf20Sopenharmony_ci		if (rc < 0) {
3608c2ecf20Sopenharmony_ci			pr_err("ESRT entry creation failed with error %d.\n",
3618c2ecf20Sopenharmony_ci			       rc);
3628c2ecf20Sopenharmony_ci			return rc;
3638c2ecf20Sopenharmony_ci		}
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci	return 0;
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic void cleanup_entry_list(void)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct esre_entry *entry, *next;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	list_for_each_entry_safe(entry, next, &entry_list, list) {
3738c2ecf20Sopenharmony_ci		kobject_put(&entry->kobj);
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic int __init esrt_sysfs_init(void)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	int error;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	pr_debug("esrt-sysfs: loading.\n");
3828c2ecf20Sopenharmony_ci	if (!esrt_data || !esrt_data_size)
3838c2ecf20Sopenharmony_ci		return -ENOSYS;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	esrt = memremap(esrt_data, esrt_data_size, MEMREMAP_WB);
3868c2ecf20Sopenharmony_ci	if (!esrt) {
3878c2ecf20Sopenharmony_ci		pr_err("memremap(%pa, %zu) failed.\n", &esrt_data,
3888c2ecf20Sopenharmony_ci		       esrt_data_size);
3898c2ecf20Sopenharmony_ci		return -ENOMEM;
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	esrt_kobj = kobject_create_and_add("esrt", efi_kobj);
3938c2ecf20Sopenharmony_ci	if (!esrt_kobj) {
3948c2ecf20Sopenharmony_ci		pr_err("Firmware table registration failed.\n");
3958c2ecf20Sopenharmony_ci		error = -ENOMEM;
3968c2ecf20Sopenharmony_ci		goto err;
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	error = sysfs_create_group(esrt_kobj, &esrt_attr_group);
4008c2ecf20Sopenharmony_ci	if (error) {
4018c2ecf20Sopenharmony_ci		pr_err("Sysfs attribute export failed with error %d.\n",
4028c2ecf20Sopenharmony_ci		       error);
4038c2ecf20Sopenharmony_ci		goto err_remove_esrt;
4048c2ecf20Sopenharmony_ci	}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	esrt_kset = kset_create_and_add("entries", NULL, esrt_kobj);
4078c2ecf20Sopenharmony_ci	if (!esrt_kset) {
4088c2ecf20Sopenharmony_ci		pr_err("kset creation failed.\n");
4098c2ecf20Sopenharmony_ci		error = -ENOMEM;
4108c2ecf20Sopenharmony_ci		goto err_remove_group;
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	error = register_entries();
4148c2ecf20Sopenharmony_ci	if (error)
4158c2ecf20Sopenharmony_ci		goto err_cleanup_list;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	pr_debug("esrt-sysfs: loaded.\n");
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	return 0;
4208c2ecf20Sopenharmony_cierr_cleanup_list:
4218c2ecf20Sopenharmony_ci	cleanup_entry_list();
4228c2ecf20Sopenharmony_ci	kset_unregister(esrt_kset);
4238c2ecf20Sopenharmony_cierr_remove_group:
4248c2ecf20Sopenharmony_ci	sysfs_remove_group(esrt_kobj, &esrt_attr_group);
4258c2ecf20Sopenharmony_cierr_remove_esrt:
4268c2ecf20Sopenharmony_ci	kobject_put(esrt_kobj);
4278c2ecf20Sopenharmony_cierr:
4288c2ecf20Sopenharmony_ci	memunmap(esrt);
4298c2ecf20Sopenharmony_ci	esrt = NULL;
4308c2ecf20Sopenharmony_ci	return error;
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_cidevice_initcall(esrt_sysfs_init);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci/*
4358c2ecf20Sopenharmony_ciMODULE_AUTHOR("Peter Jones <pjones@redhat.com>");
4368c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("EFI System Resource Table support");
4378c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
4388c2ecf20Sopenharmony_ci*/
439