efi: Export EFI runtime memory mapping to sysfs
kexec kernel will need exactly same mapping for EFI runtime memory ranges. Thus here export the runtime ranges mapping to sysfs, kexec-tools will assemble them and pass to 2nd kernel via setup_data. Introducing a new directory /sys/firmware/efi/runtime-map just like /sys/firmware/memmap. Containing below attribute in each file of that directory: attribute num_pages phys_addr type virt_addr Signed-off-by: Dave Young <dyoung@redhat.com> Tested-by: Toshi Kani <toshi.kani@hp.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com>
This commit is contained in:
parent
a0998eb15a
commit
926172d460
34
Documentation/ABI/testing/sysfs-firmware-efi-runtime-map
Normal file
34
Documentation/ABI/testing/sysfs-firmware-efi-runtime-map
Normal file
@ -0,0 +1,34 @@
|
||||
What: /sys/firmware/efi/runtime-map/
|
||||
Date: December 2013
|
||||
Contact: Dave Young <dyoung@redhat.com>
|
||||
Description: Switching efi runtime services to virtual mode requires
|
||||
that all efi memory ranges which have the runtime attribute
|
||||
bit set to be mapped to virtual addresses.
|
||||
|
||||
The efi runtime services can only be switched to virtual
|
||||
mode once without rebooting. The kexec kernel must maintain
|
||||
the same physical to virtual address mappings as the first
|
||||
kernel. The mappings are exported to sysfs so userspace tools
|
||||
can reassemble them and pass them into the kexec kernel.
|
||||
|
||||
/sys/firmware/efi/runtime-map/ is the directory the kernel
|
||||
exports that information in.
|
||||
|
||||
subdirectories are named with the number of the memory range:
|
||||
|
||||
/sys/firmware/efi/runtime-map/0
|
||||
/sys/firmware/efi/runtime-map/1
|
||||
/sys/firmware/efi/runtime-map/2
|
||||
/sys/firmware/efi/runtime-map/3
|
||||
...
|
||||
|
||||
Each subdirectory contains five files:
|
||||
|
||||
attribute : The attributes of the memory range.
|
||||
num_pages : The size of the memory range in pages.
|
||||
phys_addr : The physical address of the memory range.
|
||||
type : The type of the memory range.
|
||||
virt_addr : The virtual address of the memory range.
|
||||
|
||||
Above values are all hexadecimal numbers with the '0x' prefix.
|
||||
Users: Kexec
|
@ -76,6 +76,9 @@ static __initdata efi_config_table_type_t arch_tables[] = {
|
||||
{NULL_GUID, NULL, NULL},
|
||||
};
|
||||
|
||||
static void *efi_runtime_map;
|
||||
static int nr_efi_runtime_map;
|
||||
|
||||
/*
|
||||
* Returns 1 if 'facility' is enabled, 0 otherwise.
|
||||
*/
|
||||
@ -824,6 +827,39 @@ static void __init get_systab_virt_addr(efi_memory_desc_t *md)
|
||||
}
|
||||
}
|
||||
|
||||
static int __init save_runtime_map(void)
|
||||
{
|
||||
efi_memory_desc_t *md;
|
||||
void *tmp, *p, *q = NULL;
|
||||
int count = 0;
|
||||
|
||||
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
|
||||
md = p;
|
||||
|
||||
if (!(md->attribute & EFI_MEMORY_RUNTIME) ||
|
||||
(md->type == EFI_BOOT_SERVICES_CODE) ||
|
||||
(md->type == EFI_BOOT_SERVICES_DATA))
|
||||
continue;
|
||||
tmp = krealloc(q, (count + 1) * memmap.desc_size, GFP_KERNEL);
|
||||
if (!tmp)
|
||||
goto out;
|
||||
q = tmp;
|
||||
|
||||
memcpy(q + count * memmap.desc_size, md, memmap.desc_size);
|
||||
count++;
|
||||
}
|
||||
|
||||
efi_runtime_map = q;
|
||||
nr_efi_runtime_map = count;
|
||||
efi_runtime_map_setup(efi_runtime_map, nr_efi_runtime_map,
|
||||
boot_params.efi_info.efi_memdesc_size);
|
||||
|
||||
return 0;
|
||||
out:
|
||||
kfree(q);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* Map efi memory ranges for runtime serivce and update new_memmap with virtual
|
||||
* addresses.
|
||||
@ -849,7 +885,7 @@ static void * __init efi_map_regions(int *count)
|
||||
tmp = krealloc(new_memmap, (*count + 1) * memmap.desc_size,
|
||||
GFP_KERNEL);
|
||||
if (!tmp)
|
||||
goto out_krealloc;
|
||||
goto out;
|
||||
new_memmap = tmp;
|
||||
memcpy(new_memmap + (*count * memmap.desc_size), md,
|
||||
memmap.desc_size);
|
||||
@ -857,7 +893,7 @@ static void * __init efi_map_regions(int *count)
|
||||
}
|
||||
|
||||
return new_memmap;
|
||||
out_krealloc:
|
||||
out:
|
||||
kfree(new_memmap);
|
||||
return NULL;
|
||||
}
|
||||
@ -883,7 +919,7 @@ void __init efi_enter_virtual_mode(void)
|
||||
{
|
||||
efi_status_t status;
|
||||
void *new_memmap = NULL;
|
||||
int count = 0;
|
||||
int err, count = 0;
|
||||
|
||||
efi.systab = NULL;
|
||||
|
||||
@ -904,6 +940,10 @@ void __init efi_enter_virtual_mode(void)
|
||||
return;
|
||||
}
|
||||
|
||||
err = save_runtime_map();
|
||||
if (err)
|
||||
pr_err("Error saving runtime map, efi runtime on kexec non-functional!!\n");
|
||||
|
||||
BUG_ON(!efi.systab);
|
||||
|
||||
efi_setup_page_tables();
|
||||
|
@ -39,4 +39,15 @@ config EFI_VARS_PSTORE_DEFAULT_DISABLE
|
||||
config UEFI_CPER
|
||||
def_bool n
|
||||
|
||||
config EFI_RUNTIME_MAP
|
||||
bool "Export efi runtime maps to sysfs"
|
||||
depends on X86 && EFI && KEXEC
|
||||
default y
|
||||
help
|
||||
Export efi runtime memory maps to /sys/firmware/efi/runtime-map.
|
||||
That memory map is used for example by kexec to set up efi virtual
|
||||
mapping the 2nd kernel, but can also be used for debugging purposes.
|
||||
|
||||
See also Documentation/ABI/testing/sysfs-firmware-efi-runtime-map.
|
||||
|
||||
endmenu
|
||||
|
@ -5,3 +5,4 @@ obj-y += efi.o vars.o
|
||||
obj-$(CONFIG_EFI_VARS) += efivars.o
|
||||
obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o
|
||||
obj-$(CONFIG_UEFI_CPER) += cper.o
|
||||
obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o
|
||||
|
@ -167,6 +167,10 @@ static int __init efisubsys_init(void)
|
||||
goto err_unregister;
|
||||
}
|
||||
|
||||
error = efi_runtime_map_init(efi_kobj);
|
||||
if (error)
|
||||
goto err_remove_group;
|
||||
|
||||
/* and the standard mountpoint for efivarfs */
|
||||
efivars_kobj = kobject_create_and_add("efivars", efi_kobj);
|
||||
if (!efivars_kobj) {
|
||||
|
181
drivers/firmware/efi/runtime-map.c
Normal file
181
drivers/firmware/efi/runtime-map.c
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* linux/drivers/efi/runtime-map.c
|
||||
* Copyright (C) 2013 Red Hat, Inc., Dave Young <dyoung@redhat.com>
|
||||
*
|
||||
* This file is released under the GPLv2.
|
||||
*/
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/efi.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/setup.h>
|
||||
|
||||
static void *efi_runtime_map;
|
||||
static int nr_efi_runtime_map;
|
||||
static u32 efi_memdesc_size;
|
||||
|
||||
struct efi_runtime_map_entry {
|
||||
efi_memory_desc_t md;
|
||||
struct kobject kobj; /* kobject for each entry */
|
||||
};
|
||||
|
||||
static struct efi_runtime_map_entry **map_entries;
|
||||
|
||||
struct map_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct efi_runtime_map_entry *entry, char *buf);
|
||||
};
|
||||
|
||||
static inline struct map_attribute *to_map_attr(struct attribute *attr)
|
||||
{
|
||||
return container_of(attr, struct map_attribute, attr);
|
||||
}
|
||||
|
||||
static ssize_t type_show(struct efi_runtime_map_entry *entry, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "0x%x\n", entry->md.type);
|
||||
}
|
||||
|
||||
#define EFI_RUNTIME_FIELD(var) entry->md.var
|
||||
|
||||
#define EFI_RUNTIME_U64_ATTR_SHOW(name) \
|
||||
static ssize_t name##_show(struct efi_runtime_map_entry *entry, char *buf) \
|
||||
{ \
|
||||
return snprintf(buf, PAGE_SIZE, "0x%llx\n", EFI_RUNTIME_FIELD(name)); \
|
||||
}
|
||||
|
||||
EFI_RUNTIME_U64_ATTR_SHOW(phys_addr);
|
||||
EFI_RUNTIME_U64_ATTR_SHOW(virt_addr);
|
||||
EFI_RUNTIME_U64_ATTR_SHOW(num_pages);
|
||||
EFI_RUNTIME_U64_ATTR_SHOW(attribute);
|
||||
|
||||
static inline struct efi_runtime_map_entry *to_map_entry(struct kobject *kobj)
|
||||
{
|
||||
return container_of(kobj, struct efi_runtime_map_entry, kobj);
|
||||
}
|
||||
|
||||
static ssize_t map_attr_show(struct kobject *kobj, struct attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct efi_runtime_map_entry *entry = to_map_entry(kobj);
|
||||
struct map_attribute *map_attr = to_map_attr(attr);
|
||||
|
||||
return map_attr->show(entry, buf);
|
||||
}
|
||||
|
||||
static struct map_attribute map_type_attr = __ATTR_RO(type);
|
||||
static struct map_attribute map_phys_addr_attr = __ATTR_RO(phys_addr);
|
||||
static struct map_attribute map_virt_addr_attr = __ATTR_RO(virt_addr);
|
||||
static struct map_attribute map_num_pages_attr = __ATTR_RO(num_pages);
|
||||
static struct map_attribute map_attribute_attr = __ATTR_RO(attribute);
|
||||
|
||||
/*
|
||||
* These are default attributes that are added for every memmap entry.
|
||||
*/
|
||||
static struct attribute *def_attrs[] = {
|
||||
&map_type_attr.attr,
|
||||
&map_phys_addr_attr.attr,
|
||||
&map_virt_addr_attr.attr,
|
||||
&map_num_pages_attr.attr,
|
||||
&map_attribute_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct sysfs_ops map_attr_ops = {
|
||||
.show = map_attr_show,
|
||||
};
|
||||
|
||||
static void map_release(struct kobject *kobj)
|
||||
{
|
||||
struct efi_runtime_map_entry *entry;
|
||||
|
||||
entry = to_map_entry(kobj);
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
static struct kobj_type __refdata map_ktype = {
|
||||
.sysfs_ops = &map_attr_ops,
|
||||
.default_attrs = def_attrs,
|
||||
.release = map_release,
|
||||
};
|
||||
|
||||
static struct kset *map_kset;
|
||||
|
||||
static struct efi_runtime_map_entry *
|
||||
add_sysfs_runtime_map_entry(struct kobject *kobj, int nr)
|
||||
{
|
||||
int ret;
|
||||
struct efi_runtime_map_entry *entry;
|
||||
|
||||
if (!map_kset) {
|
||||
map_kset = kset_create_and_add("runtime-map", NULL, kobj);
|
||||
if (!map_kset)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry) {
|
||||
kset_unregister(map_kset);
|
||||
return entry;
|
||||
}
|
||||
|
||||
memcpy(&entry->md, efi_runtime_map + nr * efi_memdesc_size,
|
||||
sizeof(efi_memory_desc_t));
|
||||
|
||||
kobject_init(&entry->kobj, &map_ktype);
|
||||
entry->kobj.kset = map_kset;
|
||||
ret = kobject_add(&entry->kobj, NULL, "%d", nr);
|
||||
if (ret) {
|
||||
kobject_put(&entry->kobj);
|
||||
kset_unregister(map_kset);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
void efi_runtime_map_setup(void *map, int nr_entries, u32 desc_size)
|
||||
{
|
||||
efi_runtime_map = map;
|
||||
nr_efi_runtime_map = nr_entries;
|
||||
efi_memdesc_size = desc_size;
|
||||
}
|
||||
|
||||
int __init efi_runtime_map_init(struct kobject *efi_kobj)
|
||||
{
|
||||
int i, j, ret = 0;
|
||||
struct efi_runtime_map_entry *entry;
|
||||
|
||||
if (!efi_runtime_map)
|
||||
return 0;
|
||||
|
||||
map_entries = kzalloc(nr_efi_runtime_map * sizeof(entry), GFP_KERNEL);
|
||||
if (!map_entries) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_efi_runtime_map; i++) {
|
||||
entry = add_sysfs_runtime_map_entry(efi_kobj, i);
|
||||
if (IS_ERR(entry)) {
|
||||
ret = PTR_ERR(entry);
|
||||
goto out_add_entry;
|
||||
}
|
||||
*(map_entries + i) = entry;
|
||||
}
|
||||
|
||||
return 0;
|
||||
out_add_entry:
|
||||
for (j = i - 1; j > 0; j--) {
|
||||
entry = *(map_entries + j);
|
||||
kobject_put(&entry->kobj);
|
||||
}
|
||||
if (map_kset)
|
||||
kset_unregister(map_kset);
|
||||
out:
|
||||
return ret;
|
||||
}
|
@ -872,4 +872,17 @@ int efivars_sysfs_init(void);
|
||||
|
||||
#endif /* CONFIG_EFI_VARS */
|
||||
|
||||
#ifdef CONFIG_EFI_RUNTIME_MAP
|
||||
int efi_runtime_map_init(struct kobject *);
|
||||
void efi_runtime_map_setup(void *, int, u32);
|
||||
#else
|
||||
static inline int efi_runtime_map_init(struct kobject *kobj)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
efi_runtime_map_setup(void *map, int nr_entries, u32 desc_size) {}
|
||||
#endif
|
||||
|
||||
#endif /* _LINUX_EFI_H */
|
||||
|
Loading…
Reference in New Issue
Block a user