162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2013 Red Hat, Inc., Dave Young <dyoung@redhat.com>
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/string.h>
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/types.h>
1062306a36Sopenharmony_ci#include <linux/efi.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <asm/efi.h>
1462306a36Sopenharmony_ci#include <asm/setup.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistruct efi_runtime_map_entry {
1762306a36Sopenharmony_ci	efi_memory_desc_t md;
1862306a36Sopenharmony_ci	struct kobject kobj;   /* kobject for each entry */
1962306a36Sopenharmony_ci};
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic struct efi_runtime_map_entry **map_entries;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct map_attribute {
2462306a36Sopenharmony_ci	struct attribute attr;
2562306a36Sopenharmony_ci	ssize_t (*show)(struct efi_runtime_map_entry *entry, char *buf);
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic inline struct map_attribute *to_map_attr(struct attribute *attr)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	return container_of(attr, struct map_attribute, attr);
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic ssize_t type_show(struct efi_runtime_map_entry *entry, char *buf)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "0x%x\n", entry->md.type);
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define EFI_RUNTIME_FIELD(var) entry->md.var
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define EFI_RUNTIME_U64_ATTR_SHOW(name) \
4162306a36Sopenharmony_cistatic ssize_t name##_show(struct efi_runtime_map_entry *entry, char *buf) \
4262306a36Sopenharmony_ci{ \
4362306a36Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "0x%llx\n", EFI_RUNTIME_FIELD(name)); \
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ciEFI_RUNTIME_U64_ATTR_SHOW(phys_addr);
4762306a36Sopenharmony_ciEFI_RUNTIME_U64_ATTR_SHOW(virt_addr);
4862306a36Sopenharmony_ciEFI_RUNTIME_U64_ATTR_SHOW(num_pages);
4962306a36Sopenharmony_ciEFI_RUNTIME_U64_ATTR_SHOW(attribute);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic inline struct efi_runtime_map_entry *to_map_entry(struct kobject *kobj)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	return container_of(kobj, struct efi_runtime_map_entry, kobj);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic ssize_t map_attr_show(struct kobject *kobj, struct attribute *attr,
5762306a36Sopenharmony_ci			      char *buf)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct efi_runtime_map_entry *entry = to_map_entry(kobj);
6062306a36Sopenharmony_ci	struct map_attribute *map_attr = to_map_attr(attr);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return map_attr->show(entry, buf);
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic struct map_attribute map_type_attr = __ATTR_RO_MODE(type, 0400);
6662306a36Sopenharmony_cistatic struct map_attribute map_phys_addr_attr = __ATTR_RO_MODE(phys_addr, 0400);
6762306a36Sopenharmony_cistatic struct map_attribute map_virt_addr_attr = __ATTR_RO_MODE(virt_addr, 0400);
6862306a36Sopenharmony_cistatic struct map_attribute map_num_pages_attr = __ATTR_RO_MODE(num_pages, 0400);
6962306a36Sopenharmony_cistatic struct map_attribute map_attribute_attr = __ATTR_RO_MODE(attribute, 0400);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/*
7262306a36Sopenharmony_ci * These are default attributes that are added for every memmap entry.
7362306a36Sopenharmony_ci */
7462306a36Sopenharmony_cistatic struct attribute *def_attrs[] = {
7562306a36Sopenharmony_ci	&map_type_attr.attr,
7662306a36Sopenharmony_ci	&map_phys_addr_attr.attr,
7762306a36Sopenharmony_ci	&map_virt_addr_attr.attr,
7862306a36Sopenharmony_ci	&map_num_pages_attr.attr,
7962306a36Sopenharmony_ci	&map_attribute_attr.attr,
8062306a36Sopenharmony_ci	NULL
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ciATTRIBUTE_GROUPS(def);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic const struct sysfs_ops map_attr_ops = {
8562306a36Sopenharmony_ci	.show = map_attr_show,
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic void map_release(struct kobject *kobj)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct efi_runtime_map_entry *entry;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	entry = to_map_entry(kobj);
9362306a36Sopenharmony_ci	kfree(entry);
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic const struct kobj_type __refconst map_ktype = {
9762306a36Sopenharmony_ci	.sysfs_ops	= &map_attr_ops,
9862306a36Sopenharmony_ci	.default_groups	= def_groups,
9962306a36Sopenharmony_ci	.release	= map_release,
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic struct kset *map_kset;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic struct efi_runtime_map_entry *
10562306a36Sopenharmony_ciadd_sysfs_runtime_map_entry(struct kobject *kobj, int nr,
10662306a36Sopenharmony_ci			    efi_memory_desc_t *md)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	int ret;
10962306a36Sopenharmony_ci	struct efi_runtime_map_entry *entry;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (!map_kset) {
11262306a36Sopenharmony_ci		map_kset = kset_create_and_add("runtime-map", NULL, kobj);
11362306a36Sopenharmony_ci		if (!map_kset)
11462306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
11862306a36Sopenharmony_ci	if (!entry) {
11962306a36Sopenharmony_ci		kset_unregister(map_kset);
12062306a36Sopenharmony_ci		map_kset = NULL;
12162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	memcpy(&entry->md, md, sizeof(efi_memory_desc_t));
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	kobject_init(&entry->kobj, &map_ktype);
12762306a36Sopenharmony_ci	entry->kobj.kset = map_kset;
12862306a36Sopenharmony_ci	ret = kobject_add(&entry->kobj, NULL, "%d", nr);
12962306a36Sopenharmony_ci	if (ret) {
13062306a36Sopenharmony_ci		kobject_put(&entry->kobj);
13162306a36Sopenharmony_ci		kset_unregister(map_kset);
13262306a36Sopenharmony_ci		map_kset = NULL;
13362306a36Sopenharmony_ci		return ERR_PTR(ret);
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	return entry;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ciint efi_get_runtime_map_size(void)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	return efi.memmap.nr_map * efi.memmap.desc_size;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ciint efi_get_runtime_map_desc_size(void)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	return efi.memmap.desc_size;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ciint efi_runtime_map_copy(void *buf, size_t bufsz)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	size_t sz = efi_get_runtime_map_size();
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (sz > bufsz)
15462306a36Sopenharmony_ci		sz = bufsz;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	memcpy(buf, efi.memmap.map, sz);
15762306a36Sopenharmony_ci	return 0;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic int __init efi_runtime_map_init(void)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	int i, j, ret = 0;
16362306a36Sopenharmony_ci	struct efi_runtime_map_entry *entry;
16462306a36Sopenharmony_ci	efi_memory_desc_t *md;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	if (!efi_enabled(EFI_MEMMAP) || !efi_kobj)
16762306a36Sopenharmony_ci		return 0;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	map_entries = kcalloc(efi.memmap.nr_map, sizeof(entry), GFP_KERNEL);
17062306a36Sopenharmony_ci	if (!map_entries) {
17162306a36Sopenharmony_ci		ret = -ENOMEM;
17262306a36Sopenharmony_ci		goto out;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	i = 0;
17662306a36Sopenharmony_ci	for_each_efi_memory_desc(md) {
17762306a36Sopenharmony_ci		entry = add_sysfs_runtime_map_entry(efi_kobj, i, md);
17862306a36Sopenharmony_ci		if (IS_ERR(entry)) {
17962306a36Sopenharmony_ci			ret = PTR_ERR(entry);
18062306a36Sopenharmony_ci			goto out_add_entry;
18162306a36Sopenharmony_ci		}
18262306a36Sopenharmony_ci		*(map_entries + i++) = entry;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	return 0;
18662306a36Sopenharmony_ciout_add_entry:
18762306a36Sopenharmony_ci	for (j = i - 1; j >= 0; j--) {
18862306a36Sopenharmony_ci		entry = *(map_entries + j);
18962306a36Sopenharmony_ci		kobject_put(&entry->kobj);
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ciout:
19262306a36Sopenharmony_ci	return ret;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_cisubsys_initcall_sync(efi_runtime_map_init);
195