11fff234dSArd Biesheuvel // SPDX-License-Identifier: GPL-2.0
21fff234dSArd Biesheuvel /*
31fff234dSArd Biesheuvel  * Copyright (C) 2013 Red Hat, Inc., Dave Young <dyoung@redhat.com>
41fff234dSArd Biesheuvel  */
51fff234dSArd Biesheuvel 
61fff234dSArd Biesheuvel #include <linux/string.h>
71fff234dSArd Biesheuvel #include <linux/kernel.h>
81fff234dSArd Biesheuvel #include <linux/module.h>
91fff234dSArd Biesheuvel #include <linux/types.h>
101fff234dSArd Biesheuvel #include <linux/efi.h>
111fff234dSArd Biesheuvel #include <linux/slab.h>
121fff234dSArd Biesheuvel 
131fff234dSArd Biesheuvel #include <asm/efi.h>
141fff234dSArd Biesheuvel #include <asm/setup.h>
151fff234dSArd Biesheuvel 
161fff234dSArd Biesheuvel struct efi_runtime_map_entry {
171fff234dSArd Biesheuvel 	efi_memory_desc_t md;
181fff234dSArd Biesheuvel 	struct kobject kobj;   /* kobject for each entry */
191fff234dSArd Biesheuvel };
201fff234dSArd Biesheuvel 
211fff234dSArd Biesheuvel static struct efi_runtime_map_entry **map_entries;
221fff234dSArd Biesheuvel 
231fff234dSArd Biesheuvel struct map_attribute {
241fff234dSArd Biesheuvel 	struct attribute attr;
251fff234dSArd Biesheuvel 	ssize_t (*show)(struct efi_runtime_map_entry *entry, char *buf);
261fff234dSArd Biesheuvel };
271fff234dSArd Biesheuvel 
to_map_attr(struct attribute * attr)281fff234dSArd Biesheuvel static inline struct map_attribute *to_map_attr(struct attribute *attr)
291fff234dSArd Biesheuvel {
301fff234dSArd Biesheuvel 	return container_of(attr, struct map_attribute, attr);
311fff234dSArd Biesheuvel }
321fff234dSArd Biesheuvel 
type_show(struct efi_runtime_map_entry * entry,char * buf)331fff234dSArd Biesheuvel static ssize_t type_show(struct efi_runtime_map_entry *entry, char *buf)
341fff234dSArd Biesheuvel {
351fff234dSArd Biesheuvel 	return snprintf(buf, PAGE_SIZE, "0x%x\n", entry->md.type);
361fff234dSArd Biesheuvel }
371fff234dSArd Biesheuvel 
381fff234dSArd Biesheuvel #define EFI_RUNTIME_FIELD(var) entry->md.var
391fff234dSArd Biesheuvel 
401fff234dSArd Biesheuvel #define EFI_RUNTIME_U64_ATTR_SHOW(name) \
411fff234dSArd Biesheuvel static ssize_t name##_show(struct efi_runtime_map_entry *entry, char *buf) \
421fff234dSArd Biesheuvel { \
431fff234dSArd Biesheuvel 	return snprintf(buf, PAGE_SIZE, "0x%llx\n", EFI_RUNTIME_FIELD(name)); \
441fff234dSArd Biesheuvel }
451fff234dSArd Biesheuvel 
461fff234dSArd Biesheuvel EFI_RUNTIME_U64_ATTR_SHOW(phys_addr);
471fff234dSArd Biesheuvel EFI_RUNTIME_U64_ATTR_SHOW(virt_addr);
481fff234dSArd Biesheuvel EFI_RUNTIME_U64_ATTR_SHOW(num_pages);
491fff234dSArd Biesheuvel EFI_RUNTIME_U64_ATTR_SHOW(attribute);
501fff234dSArd Biesheuvel 
to_map_entry(struct kobject * kobj)511fff234dSArd Biesheuvel static inline struct efi_runtime_map_entry *to_map_entry(struct kobject *kobj)
521fff234dSArd Biesheuvel {
531fff234dSArd Biesheuvel 	return container_of(kobj, struct efi_runtime_map_entry, kobj);
541fff234dSArd Biesheuvel }
551fff234dSArd Biesheuvel 
map_attr_show(struct kobject * kobj,struct attribute * attr,char * buf)561fff234dSArd Biesheuvel static ssize_t map_attr_show(struct kobject *kobj, struct attribute *attr,
571fff234dSArd Biesheuvel 			      char *buf)
581fff234dSArd Biesheuvel {
591fff234dSArd Biesheuvel 	struct efi_runtime_map_entry *entry = to_map_entry(kobj);
601fff234dSArd Biesheuvel 	struct map_attribute *map_attr = to_map_attr(attr);
611fff234dSArd Biesheuvel 
621fff234dSArd Biesheuvel 	return map_attr->show(entry, buf);
631fff234dSArd Biesheuvel }
641fff234dSArd Biesheuvel 
651fff234dSArd Biesheuvel static struct map_attribute map_type_attr = __ATTR_RO_MODE(type, 0400);
661fff234dSArd Biesheuvel static struct map_attribute map_phys_addr_attr = __ATTR_RO_MODE(phys_addr, 0400);
671fff234dSArd Biesheuvel static struct map_attribute map_virt_addr_attr = __ATTR_RO_MODE(virt_addr, 0400);
681fff234dSArd Biesheuvel static struct map_attribute map_num_pages_attr = __ATTR_RO_MODE(num_pages, 0400);
691fff234dSArd Biesheuvel static struct map_attribute map_attribute_attr = __ATTR_RO_MODE(attribute, 0400);
701fff234dSArd Biesheuvel 
711fff234dSArd Biesheuvel /*
721fff234dSArd Biesheuvel  * These are default attributes that are added for every memmap entry.
731fff234dSArd Biesheuvel  */
741fff234dSArd Biesheuvel static struct attribute *def_attrs[] = {
751fff234dSArd Biesheuvel 	&map_type_attr.attr,
761fff234dSArd Biesheuvel 	&map_phys_addr_attr.attr,
771fff234dSArd Biesheuvel 	&map_virt_addr_attr.attr,
781fff234dSArd Biesheuvel 	&map_num_pages_attr.attr,
791fff234dSArd Biesheuvel 	&map_attribute_attr.attr,
801fff234dSArd Biesheuvel 	NULL
811fff234dSArd Biesheuvel };
821fff234dSArd Biesheuvel ATTRIBUTE_GROUPS(def);
831fff234dSArd Biesheuvel 
841fff234dSArd Biesheuvel static const struct sysfs_ops map_attr_ops = {
851fff234dSArd Biesheuvel 	.show = map_attr_show,
861fff234dSArd Biesheuvel };
871fff234dSArd Biesheuvel 
map_release(struct kobject * kobj)881fff234dSArd Biesheuvel static void map_release(struct kobject *kobj)
891fff234dSArd Biesheuvel {
901fff234dSArd Biesheuvel 	struct efi_runtime_map_entry *entry;
911fff234dSArd Biesheuvel 
921fff234dSArd Biesheuvel 	entry = to_map_entry(kobj);
931fff234dSArd Biesheuvel 	kfree(entry);
941fff234dSArd Biesheuvel }
951fff234dSArd Biesheuvel 
96*42a8af0fSThomas Weißschuh static const struct kobj_type __refconst map_ktype = {
971fff234dSArd Biesheuvel 	.sysfs_ops	= &map_attr_ops,
981fff234dSArd Biesheuvel 	.default_groups	= def_groups,
991fff234dSArd Biesheuvel 	.release	= map_release,
1001fff234dSArd Biesheuvel };
1011fff234dSArd Biesheuvel 
1021fff234dSArd Biesheuvel static struct kset *map_kset;
1031fff234dSArd Biesheuvel 
1041fff234dSArd Biesheuvel static struct efi_runtime_map_entry *
add_sysfs_runtime_map_entry(struct kobject * kobj,int nr,efi_memory_desc_t * md)1051fff234dSArd Biesheuvel add_sysfs_runtime_map_entry(struct kobject *kobj, int nr,
1061fff234dSArd Biesheuvel 			    efi_memory_desc_t *md)
1071fff234dSArd Biesheuvel {
1081fff234dSArd Biesheuvel 	int ret;
1091fff234dSArd Biesheuvel 	struct efi_runtime_map_entry *entry;
1101fff234dSArd Biesheuvel 
1111fff234dSArd Biesheuvel 	if (!map_kset) {
1121fff234dSArd Biesheuvel 		map_kset = kset_create_and_add("runtime-map", NULL, kobj);
1131fff234dSArd Biesheuvel 		if (!map_kset)
1141fff234dSArd Biesheuvel 			return ERR_PTR(-ENOMEM);
1151fff234dSArd Biesheuvel 	}
1161fff234dSArd Biesheuvel 
1171fff234dSArd Biesheuvel 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
1181fff234dSArd Biesheuvel 	if (!entry) {
1191fff234dSArd Biesheuvel 		kset_unregister(map_kset);
1201fff234dSArd Biesheuvel 		map_kset = NULL;
1211fff234dSArd Biesheuvel 		return ERR_PTR(-ENOMEM);
1221fff234dSArd Biesheuvel 	}
1231fff234dSArd Biesheuvel 
1241fff234dSArd Biesheuvel 	memcpy(&entry->md, md, sizeof(efi_memory_desc_t));
1251fff234dSArd Biesheuvel 
1261fff234dSArd Biesheuvel 	kobject_init(&entry->kobj, &map_ktype);
1271fff234dSArd Biesheuvel 	entry->kobj.kset = map_kset;
1281fff234dSArd Biesheuvel 	ret = kobject_add(&entry->kobj, NULL, "%d", nr);
1291fff234dSArd Biesheuvel 	if (ret) {
1301fff234dSArd Biesheuvel 		kobject_put(&entry->kobj);
1311fff234dSArd Biesheuvel 		kset_unregister(map_kset);
1321fff234dSArd Biesheuvel 		map_kset = NULL;
1331fff234dSArd Biesheuvel 		return ERR_PTR(ret);
1341fff234dSArd Biesheuvel 	}
1351fff234dSArd Biesheuvel 
1361fff234dSArd Biesheuvel 	return entry;
1371fff234dSArd Biesheuvel }
1381fff234dSArd Biesheuvel 
efi_get_runtime_map_size(void)1391fff234dSArd Biesheuvel int efi_get_runtime_map_size(void)
1401fff234dSArd Biesheuvel {
1411fff234dSArd Biesheuvel 	return efi.memmap.nr_map * efi.memmap.desc_size;
1421fff234dSArd Biesheuvel }
1431fff234dSArd Biesheuvel 
efi_get_runtime_map_desc_size(void)1441fff234dSArd Biesheuvel int efi_get_runtime_map_desc_size(void)
1451fff234dSArd Biesheuvel {
1461fff234dSArd Biesheuvel 	return efi.memmap.desc_size;
1471fff234dSArd Biesheuvel }
1481fff234dSArd Biesheuvel 
efi_runtime_map_copy(void * buf,size_t bufsz)1491fff234dSArd Biesheuvel int efi_runtime_map_copy(void *buf, size_t bufsz)
1501fff234dSArd Biesheuvel {
1511fff234dSArd Biesheuvel 	size_t sz = efi_get_runtime_map_size();
1521fff234dSArd Biesheuvel 
1531fff234dSArd Biesheuvel 	if (sz > bufsz)
1541fff234dSArd Biesheuvel 		sz = bufsz;
1551fff234dSArd Biesheuvel 
1561fff234dSArd Biesheuvel 	memcpy(buf, efi.memmap.map, sz);
1571fff234dSArd Biesheuvel 	return 0;
1581fff234dSArd Biesheuvel }
1591fff234dSArd Biesheuvel 
efi_runtime_map_init(void)1601fff234dSArd Biesheuvel static int __init efi_runtime_map_init(void)
1611fff234dSArd Biesheuvel {
1621fff234dSArd Biesheuvel 	int i, j, ret = 0;
1631fff234dSArd Biesheuvel 	struct efi_runtime_map_entry *entry;
1641fff234dSArd Biesheuvel 	efi_memory_desc_t *md;
1651fff234dSArd Biesheuvel 
1661fff234dSArd Biesheuvel 	if (!efi_enabled(EFI_MEMMAP) || !efi_kobj)
1671fff234dSArd Biesheuvel 		return 0;
1681fff234dSArd Biesheuvel 
1691fff234dSArd Biesheuvel 	map_entries = kcalloc(efi.memmap.nr_map, sizeof(entry), GFP_KERNEL);
1701fff234dSArd Biesheuvel 	if (!map_entries) {
1711fff234dSArd Biesheuvel 		ret = -ENOMEM;
1721fff234dSArd Biesheuvel 		goto out;
1731fff234dSArd Biesheuvel 	}
1741fff234dSArd Biesheuvel 
1751fff234dSArd Biesheuvel 	i = 0;
1761fff234dSArd Biesheuvel 	for_each_efi_memory_desc(md) {
1771fff234dSArd Biesheuvel 		entry = add_sysfs_runtime_map_entry(efi_kobj, i, md);
1781fff234dSArd Biesheuvel 		if (IS_ERR(entry)) {
1791fff234dSArd Biesheuvel 			ret = PTR_ERR(entry);
1801fff234dSArd Biesheuvel 			goto out_add_entry;
1811fff234dSArd Biesheuvel 		}
1821fff234dSArd Biesheuvel 		*(map_entries + i++) = entry;
1831fff234dSArd Biesheuvel 	}
1841fff234dSArd Biesheuvel 
1851fff234dSArd Biesheuvel 	return 0;
1861fff234dSArd Biesheuvel out_add_entry:
1871fff234dSArd Biesheuvel 	for (j = i - 1; j >= 0; j--) {
1881fff234dSArd Biesheuvel 		entry = *(map_entries + j);
1891fff234dSArd Biesheuvel 		kobject_put(&entry->kobj);
1901fff234dSArd Biesheuvel 	}
1911fff234dSArd Biesheuvel out:
1921fff234dSArd Biesheuvel 	return ret;
1931fff234dSArd Biesheuvel }
1941fff234dSArd Biesheuvel subsys_initcall_sync(efi_runtime_map_init);
195