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