Merge tag 'efi-next' of git://git.kernel.org/pub/scm/linux/kernel/git/mfleming/efi into x86/efi
Pull EFI changes from Matt Fleming: - Use idiomatic negative error values in efivar_create_sysfs_entry() instead of returning '1' to indicate error. (Dan Carpenter) - Implement new support to expose the EFI System Resource Tables in sysfs, which provides information for performing firmware updates. (Peter Jones) - Documentation cleanup in the EFI handover protocol section which falsely claimed that 'cmdline_size' needed to be filled out by the boot loader. (Alex Smith) - Align the order of SMBIOS tables in /sys/firmware/efi/systab to match the way that we do things for ACPI and add documentation to Documentation/ABI. (Jean Delvare) Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# Makefile for linux kernel
|
||||
#
|
||||
obj-$(CONFIG_EFI) += efi.o vars.o reboot.o
|
||||
obj-$(CONFIG_EFI) += efi.o esrt.o vars.o reboot.o
|
||||
obj-$(CONFIG_EFI_VARS) += efivars.o
|
||||
obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o
|
||||
obj-$(CONFIG_UEFI_CPER) += cper.o
|
||||
|
@@ -39,6 +39,7 @@ struct efi __read_mostly efi = {
|
||||
.fw_vendor = EFI_INVALID_TABLE_ADDR,
|
||||
.runtime = EFI_INVALID_TABLE_ADDR,
|
||||
.config_table = EFI_INVALID_TABLE_ADDR,
|
||||
.esrt = EFI_INVALID_TABLE_ADDR,
|
||||
};
|
||||
EXPORT_SYMBOL(efi);
|
||||
|
||||
@@ -64,7 +65,7 @@ static int __init parse_efi_cmdline(char *str)
|
||||
}
|
||||
early_param("efi", parse_efi_cmdline);
|
||||
|
||||
static struct kobject *efi_kobj;
|
||||
struct kobject *efi_kobj;
|
||||
static struct kobject *efivars_kobj;
|
||||
|
||||
/*
|
||||
@@ -85,10 +86,15 @@ static ssize_t systab_show(struct kobject *kobj,
|
||||
str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20);
|
||||
if (efi.acpi != EFI_INVALID_TABLE_ADDR)
|
||||
str += sprintf(str, "ACPI=0x%lx\n", efi.acpi);
|
||||
if (efi.smbios != EFI_INVALID_TABLE_ADDR)
|
||||
str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios);
|
||||
/*
|
||||
* If both SMBIOS and SMBIOS3 entry points are implemented, the
|
||||
* SMBIOS3 entry point shall be preferred, so we list it first to
|
||||
* let applications stop parsing after the first match.
|
||||
*/
|
||||
if (efi.smbios3 != EFI_INVALID_TABLE_ADDR)
|
||||
str += sprintf(str, "SMBIOS3=0x%lx\n", efi.smbios3);
|
||||
if (efi.smbios != EFI_INVALID_TABLE_ADDR)
|
||||
str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios);
|
||||
if (efi.hcdp != EFI_INVALID_TABLE_ADDR)
|
||||
str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp);
|
||||
if (efi.boot_info != EFI_INVALID_TABLE_ADDR)
|
||||
@@ -232,6 +238,84 @@ err_put:
|
||||
|
||||
subsys_initcall(efisubsys_init);
|
||||
|
||||
/*
|
||||
* Find the efi memory descriptor for a given physical address. Given a
|
||||
* physicall address, determine if it exists within an EFI Memory Map entry,
|
||||
* and if so, populate the supplied memory descriptor with the appropriate
|
||||
* data.
|
||||
*/
|
||||
int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
|
||||
{
|
||||
struct efi_memory_map *map = efi.memmap;
|
||||
void *p, *e;
|
||||
|
||||
if (!efi_enabled(EFI_MEMMAP)) {
|
||||
pr_err_once("EFI_MEMMAP is not enabled.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!map) {
|
||||
pr_err_once("efi.memmap is not set.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!out_md) {
|
||||
pr_err_once("out_md is null.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (WARN_ON_ONCE(!map->phys_map))
|
||||
return -EINVAL;
|
||||
if (WARN_ON_ONCE(map->nr_map == 0) || WARN_ON_ONCE(map->desc_size == 0))
|
||||
return -EINVAL;
|
||||
|
||||
e = map->phys_map + map->nr_map * map->desc_size;
|
||||
for (p = map->phys_map; p < e; p += map->desc_size) {
|
||||
efi_memory_desc_t *md;
|
||||
u64 size;
|
||||
u64 end;
|
||||
|
||||
/*
|
||||
* If a driver calls this after efi_free_boot_services,
|
||||
* ->map will be NULL, and the target may also not be mapped.
|
||||
* So just always get our own virtual map on the CPU.
|
||||
*
|
||||
*/
|
||||
md = early_memremap((phys_addr_t)p, sizeof (*md));
|
||||
if (!md) {
|
||||
pr_err_once("early_memremap(%p, %zu) failed.\n",
|
||||
p, sizeof (*md));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
|
||||
md->type != EFI_BOOT_SERVICES_DATA &&
|
||||
md->type != EFI_RUNTIME_SERVICES_DATA) {
|
||||
early_memunmap(md, sizeof (*md));
|
||||
continue;
|
||||
}
|
||||
|
||||
size = md->num_pages << EFI_PAGE_SHIFT;
|
||||
end = md->phys_addr + size;
|
||||
if (phys_addr >= md->phys_addr && phys_addr < end) {
|
||||
memcpy(out_md, md, sizeof(*out_md));
|
||||
early_memunmap(md, sizeof (*md));
|
||||
return 0;
|
||||
}
|
||||
|
||||
early_memunmap(md, sizeof (*md));
|
||||
}
|
||||
pr_err_once("requested map not found.\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the highest address of an efi memory descriptor.
|
||||
*/
|
||||
u64 __init efi_mem_desc_end(efi_memory_desc_t *md)
|
||||
{
|
||||
u64 size = md->num_pages << EFI_PAGE_SHIFT;
|
||||
u64 end = md->phys_addr + size;
|
||||
return end;
|
||||
}
|
||||
|
||||
/*
|
||||
* We can't ioremap data in EFI boot services RAM, because we've already mapped
|
||||
@@ -274,6 +358,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
|
||||
{SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
|
||||
{SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3},
|
||||
{UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
|
||||
{EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
|
||||
{NULL_GUID, NULL, NULL},
|
||||
};
|
||||
|
||||
|
@@ -535,7 +535,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
|
||||
* efivar_create_sysfs_entry - create a new entry in sysfs
|
||||
* @new_var: efivar entry to create
|
||||
*
|
||||
* Returns 1 on failure, 0 on success
|
||||
* Returns 0 on success, negative error code on failure
|
||||
*/
|
||||
static int
|
||||
efivar_create_sysfs_entry(struct efivar_entry *new_var)
|
||||
@@ -544,6 +544,7 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)
|
||||
char *short_name;
|
||||
unsigned long variable_name_size;
|
||||
efi_char16_t *variable_name;
|
||||
int ret;
|
||||
|
||||
variable_name = new_var->var.VariableName;
|
||||
variable_name_size = ucs2_strlen(variable_name) * sizeof(efi_char16_t);
|
||||
@@ -558,7 +559,7 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)
|
||||
short_name = kzalloc(short_name_size, GFP_KERNEL);
|
||||
|
||||
if (!short_name)
|
||||
return 1;
|
||||
return -ENOMEM;
|
||||
|
||||
/* Convert Unicode to normal chars (assume top bits are 0),
|
||||
ala UTF-8 */
|
||||
@@ -574,11 +575,11 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)
|
||||
|
||||
new_var->kobj.kset = efivars_kset;
|
||||
|
||||
i = kobject_init_and_add(&new_var->kobj, &efivar_ktype,
|
||||
ret = kobject_init_and_add(&new_var->kobj, &efivar_ktype,
|
||||
NULL, "%s", short_name);
|
||||
kfree(short_name);
|
||||
if (i)
|
||||
return 1;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
kobject_uevent(&new_var->kobj, KOBJ_ADD);
|
||||
efivar_entry_add(new_var, &efivar_sysfs_list);
|
||||
|
471
drivers/firmware/efi/esrt.c
Normal file
471
drivers/firmware/efi/esrt.c
Normal file
@@ -0,0 +1,471 @@
|
||||
/*
|
||||
* esrt.c
|
||||
*
|
||||
* This module exports EFI System Resource Table (ESRT) entries into userspace
|
||||
* through the sysfs file system. The ESRT provides a read-only catalog of
|
||||
* system components for which the system accepts firmware upgrades via UEFI's
|
||||
* "Capsule Update" feature. This module allows userland utilities to evaluate
|
||||
* what firmware updates can be applied to this system, and potentially arrange
|
||||
* for those updates to occur.
|
||||
*
|
||||
* Data is currently found below /sys/firmware/efi/esrt/...
|
||||
*/
|
||||
#define pr_fmt(fmt) "esrt: " fmt
|
||||
|
||||
#include <linux/capability.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/efi.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/early_ioremap.h>
|
||||
|
||||
struct efi_system_resource_entry_v1 {
|
||||
efi_guid_t fw_class;
|
||||
u32 fw_type;
|
||||
u32 fw_version;
|
||||
u32 lowest_supported_fw_version;
|
||||
u32 capsule_flags;
|
||||
u32 last_attempt_version;
|
||||
u32 last_attempt_status;
|
||||
};
|
||||
|
||||
/*
|
||||
* _count and _version are what they seem like. _max is actually just
|
||||
* accounting info for the firmware when creating the table; it should never
|
||||
* have been exposed to us. To wit, the spec says:
|
||||
* The maximum number of resource array entries that can be within the
|
||||
* table without reallocating the table, must not be zero.
|
||||
* Since there's no guidance about what that means in terms of memory layout,
|
||||
* it means nothing to us.
|
||||
*/
|
||||
struct efi_system_resource_table {
|
||||
u32 fw_resource_count;
|
||||
u32 fw_resource_count_max;
|
||||
u64 fw_resource_version;
|
||||
u8 entries[];
|
||||
};
|
||||
|
||||
static phys_addr_t esrt_data;
|
||||
static size_t esrt_data_size;
|
||||
|
||||
static struct efi_system_resource_table *esrt;
|
||||
|
||||
struct esre_entry {
|
||||
union {
|
||||
struct efi_system_resource_entry_v1 *esre1;
|
||||
} esre;
|
||||
|
||||
struct kobject kobj;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/* global list of esre_entry. */
|
||||
static LIST_HEAD(entry_list);
|
||||
|
||||
/* entry attribute */
|
||||
struct esre_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct esre_entry *entry, char *buf);
|
||||
ssize_t (*store)(struct esre_entry *entry,
|
||||
const char *buf, size_t count);
|
||||
};
|
||||
|
||||
static struct esre_entry *to_entry(struct kobject *kobj)
|
||||
{
|
||||
return container_of(kobj, struct esre_entry, kobj);
|
||||
}
|
||||
|
||||
static struct esre_attribute *to_attr(struct attribute *attr)
|
||||
{
|
||||
return container_of(attr, struct esre_attribute, attr);
|
||||
}
|
||||
|
||||
static ssize_t esre_attr_show(struct kobject *kobj,
|
||||
struct attribute *_attr, char *buf)
|
||||
{
|
||||
struct esre_entry *entry = to_entry(kobj);
|
||||
struct esre_attribute *attr = to_attr(_attr);
|
||||
|
||||
/* Don't tell normal users what firmware versions we've got... */
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
|
||||
return attr->show(entry, buf);
|
||||
}
|
||||
|
||||
static const struct sysfs_ops esre_attr_ops = {
|
||||
.show = esre_attr_show,
|
||||
};
|
||||
|
||||
/* Generic ESRT Entry ("ESRE") support. */
|
||||
static ssize_t esre_fw_class_show(struct esre_entry *entry, char *buf)
|
||||
{
|
||||
char *str = buf;
|
||||
|
||||
efi_guid_to_str(&entry->esre.esre1->fw_class, str);
|
||||
str += strlen(str);
|
||||
str += sprintf(str, "\n");
|
||||
|
||||
return str - buf;
|
||||
}
|
||||
|
||||
static struct esre_attribute esre_fw_class = __ATTR(fw_class, 0400,
|
||||
esre_fw_class_show, NULL);
|
||||
|
||||
#define esre_attr_decl(name, size, fmt) \
|
||||
static ssize_t esre_##name##_show(struct esre_entry *entry, char *buf) \
|
||||
{ \
|
||||
return sprintf(buf, fmt "\n", \
|
||||
le##size##_to_cpu(entry->esre.esre1->name)); \
|
||||
} \
|
||||
\
|
||||
static struct esre_attribute esre_##name = __ATTR(name, 0400, \
|
||||
esre_##name##_show, NULL)
|
||||
|
||||
esre_attr_decl(fw_type, 32, "%u");
|
||||
esre_attr_decl(fw_version, 32, "%u");
|
||||
esre_attr_decl(lowest_supported_fw_version, 32, "%u");
|
||||
esre_attr_decl(capsule_flags, 32, "0x%x");
|
||||
esre_attr_decl(last_attempt_version, 32, "%u");
|
||||
esre_attr_decl(last_attempt_status, 32, "%u");
|
||||
|
||||
static struct attribute *esre1_attrs[] = {
|
||||
&esre_fw_class.attr,
|
||||
&esre_fw_type.attr,
|
||||
&esre_fw_version.attr,
|
||||
&esre_lowest_supported_fw_version.attr,
|
||||
&esre_capsule_flags.attr,
|
||||
&esre_last_attempt_version.attr,
|
||||
&esre_last_attempt_status.attr,
|
||||
NULL
|
||||
};
|
||||
static void esre_release(struct kobject *kobj)
|
||||
{
|
||||
struct esre_entry *entry = to_entry(kobj);
|
||||
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
static struct kobj_type esre1_ktype = {
|
||||
.release = esre_release,
|
||||
.sysfs_ops = &esre_attr_ops,
|
||||
.default_attrs = esre1_attrs,
|
||||
};
|
||||
|
||||
|
||||
static struct kobject *esrt_kobj;
|
||||
static struct kset *esrt_kset;
|
||||
|
||||
static int esre_create_sysfs_entry(void *esre, int entry_num)
|
||||
{
|
||||
struct esre_entry *entry;
|
||||
char name[20];
|
||||
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
|
||||
sprintf(name, "entry%d", entry_num);
|
||||
|
||||
entry->kobj.kset = esrt_kset;
|
||||
|
||||
if (esrt->fw_resource_version == 1) {
|
||||
int rc = 0;
|
||||
|
||||
entry->esre.esre1 = esre;
|
||||
rc = kobject_init_and_add(&entry->kobj, &esre1_ktype, NULL,
|
||||
"%s", name);
|
||||
if (rc) {
|
||||
kfree(entry);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
list_add_tail(&entry->list, &entry_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* support for displaying ESRT fields at the top level */
|
||||
#define esrt_attr_decl(name, size, fmt) \
|
||||
static ssize_t esrt_##name##_show(struct kobject *kobj, \
|
||||
struct kobj_attribute *attr, char *buf)\
|
||||
{ \
|
||||
return sprintf(buf, fmt "\n", le##size##_to_cpu(esrt->name)); \
|
||||
} \
|
||||
\
|
||||
static struct kobj_attribute esrt_##name = __ATTR(name, 0400, \
|
||||
esrt_##name##_show, NULL)
|
||||
|
||||
esrt_attr_decl(fw_resource_count, 32, "%u");
|
||||
esrt_attr_decl(fw_resource_count_max, 32, "%u");
|
||||
esrt_attr_decl(fw_resource_version, 64, "%llu");
|
||||
|
||||
static struct attribute *esrt_attrs[] = {
|
||||
&esrt_fw_resource_count.attr,
|
||||
&esrt_fw_resource_count_max.attr,
|
||||
&esrt_fw_resource_version.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static inline int esrt_table_exists(void)
|
||||
{
|
||||
if (!efi_enabled(EFI_CONFIG_TABLES))
|
||||
return 0;
|
||||
if (efi.esrt == EFI_INVALID_TABLE_ADDR)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static umode_t esrt_attr_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int n)
|
||||
{
|
||||
if (!esrt_table_exists())
|
||||
return 0;
|
||||
return attr->mode;
|
||||
}
|
||||
|
||||
static struct attribute_group esrt_attr_group = {
|
||||
.attrs = esrt_attrs,
|
||||
.is_visible = esrt_attr_is_visible,
|
||||
};
|
||||
|
||||
/*
|
||||
* remap the table, copy it to kmalloced pages, and unmap it.
|
||||
*/
|
||||
void __init efi_esrt_init(void)
|
||||
{
|
||||
void *va;
|
||||
struct efi_system_resource_table tmpesrt;
|
||||
struct efi_system_resource_entry_v1 *v1_entries;
|
||||
size_t size, max, entry_size, entries_size;
|
||||
efi_memory_desc_t md;
|
||||
int rc;
|
||||
phys_addr_t end;
|
||||
|
||||
pr_debug("esrt-init: loading.\n");
|
||||
if (!esrt_table_exists())
|
||||
return;
|
||||
|
||||
rc = efi_mem_desc_lookup(efi.esrt, &md);
|
||||
if (rc < 0) {
|
||||
pr_err("ESRT header is not in the memory map.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
max = efi_mem_desc_end(&md);
|
||||
if (max < efi.esrt) {
|
||||
pr_err("EFI memory descriptor is invalid. (esrt: %p max: %p)\n",
|
||||
(void *)efi.esrt, (void *)max);
|
||||
return;
|
||||
}
|
||||
|
||||
size = sizeof(*esrt);
|
||||
max -= efi.esrt;
|
||||
|
||||
if (max < size) {
|
||||
pr_err("ESRT header doen't fit on single memory map entry. (size: %zu max: %zu)\n",
|
||||
size, max);
|
||||
return;
|
||||
}
|
||||
|
||||
va = early_memremap(efi.esrt, size);
|
||||
if (!va) {
|
||||
pr_err("early_memremap(%p, %zu) failed.\n", (void *)efi.esrt,
|
||||
size);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&tmpesrt, va, sizeof(tmpesrt));
|
||||
|
||||
if (tmpesrt.fw_resource_version == 1) {
|
||||
entry_size = sizeof (*v1_entries);
|
||||
} else {
|
||||
pr_err("Unsupported ESRT version %lld.\n",
|
||||
tmpesrt.fw_resource_version);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tmpesrt.fw_resource_count > 0 && max - size < entry_size) {
|
||||
pr_err("ESRT memory map entry can only hold the header. (max: %zu size: %zu)\n",
|
||||
max - size, entry_size);
|
||||
goto err_memunmap;
|
||||
}
|
||||
|
||||
/*
|
||||
* The format doesn't really give us any boundary to test here,
|
||||
* so I'm making up 128 as the max number of individually updatable
|
||||
* components we support.
|
||||
* 128 should be pretty excessive, but there's still some chance
|
||||
* somebody will do that someday and we'll need to raise this.
|
||||
*/
|
||||
if (tmpesrt.fw_resource_count > 128) {
|
||||
pr_err("ESRT says fw_resource_count has very large value %d.\n",
|
||||
tmpesrt.fw_resource_count);
|
||||
goto err_memunmap;
|
||||
}
|
||||
|
||||
/*
|
||||
* We know it can't be larger than N * sizeof() here, and N is limited
|
||||
* by the previous test to a small number, so there's no overflow.
|
||||
*/
|
||||
entries_size = tmpesrt.fw_resource_count * entry_size;
|
||||
if (max < size + entries_size) {
|
||||
pr_err("ESRT does not fit on single memory map entry (size: %zu max: %zu)\n",
|
||||
size, max);
|
||||
goto err_memunmap;
|
||||
}
|
||||
|
||||
/* remap it with our (plausible) new pages */
|
||||
early_memunmap(va, size);
|
||||
size += entries_size;
|
||||
va = early_memremap(efi.esrt, size);
|
||||
if (!va) {
|
||||
pr_err("early_memremap(%p, %zu) failed.\n", (void *)efi.esrt,
|
||||
size);
|
||||
return;
|
||||
}
|
||||
|
||||
esrt_data = (phys_addr_t)efi.esrt;
|
||||
esrt_data_size = size;
|
||||
|
||||
end = esrt_data + size;
|
||||
pr_info("Reserving ESRT space from %pa to %pa.\n", &esrt_data, &end);
|
||||
memblock_reserve(esrt_data, esrt_data_size);
|
||||
|
||||
pr_debug("esrt-init: loaded.\n");
|
||||
err_memunmap:
|
||||
early_memunmap(va, size);
|
||||
}
|
||||
|
||||
static int __init register_entries(void)
|
||||
{
|
||||
struct efi_system_resource_entry_v1 *v1_entries = (void *)esrt->entries;
|
||||
int i, rc;
|
||||
|
||||
if (!esrt_table_exists())
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
|
||||
void *esre = NULL;
|
||||
if (esrt->fw_resource_version == 1) {
|
||||
esre = &v1_entries[i];
|
||||
} else {
|
||||
pr_err("Unsupported ESRT version %lld.\n",
|
||||
esrt->fw_resource_version);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = esre_create_sysfs_entry(esre, i);
|
||||
if (rc < 0) {
|
||||
pr_err("ESRT entry creation failed with error %d.\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cleanup_entry_list(void)
|
||||
{
|
||||
struct esre_entry *entry, *next;
|
||||
|
||||
list_for_each_entry_safe(entry, next, &entry_list, list) {
|
||||
kobject_put(&entry->kobj);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init esrt_sysfs_init(void)
|
||||
{
|
||||
int error;
|
||||
struct efi_system_resource_table __iomem *ioesrt;
|
||||
|
||||
pr_debug("esrt-sysfs: loading.\n");
|
||||
if (!esrt_data || !esrt_data_size)
|
||||
return -ENOSYS;
|
||||
|
||||
ioesrt = ioremap(esrt_data, esrt_data_size);
|
||||
if (!ioesrt) {
|
||||
pr_err("ioremap(%pa, %zu) failed.\n", &esrt_data,
|
||||
esrt_data_size);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
esrt = kmalloc(esrt_data_size, GFP_KERNEL);
|
||||
if (!esrt) {
|
||||
pr_err("kmalloc failed. (wanted %zu bytes)\n", esrt_data_size);
|
||||
iounmap(ioesrt);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy_fromio(esrt, ioesrt, esrt_data_size);
|
||||
|
||||
esrt_kobj = kobject_create_and_add("esrt", efi_kobj);
|
||||
if (!esrt_kobj) {
|
||||
pr_err("Firmware table registration failed.\n");
|
||||
error = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
error = sysfs_create_group(esrt_kobj, &esrt_attr_group);
|
||||
if (error) {
|
||||
pr_err("Sysfs attribute export failed with error %d.\n",
|
||||
error);
|
||||
goto err_remove_esrt;
|
||||
}
|
||||
|
||||
esrt_kset = kset_create_and_add("entries", NULL, esrt_kobj);
|
||||
if (!esrt_kset) {
|
||||
pr_err("kset creation failed.\n");
|
||||
error = -ENOMEM;
|
||||
goto err_remove_group;
|
||||
}
|
||||
|
||||
error = register_entries();
|
||||
if (error)
|
||||
goto err_cleanup_list;
|
||||
|
||||
memblock_remove(esrt_data, esrt_data_size);
|
||||
|
||||
pr_debug("esrt-sysfs: loaded.\n");
|
||||
|
||||
return 0;
|
||||
err_cleanup_list:
|
||||
cleanup_entry_list();
|
||||
kset_unregister(esrt_kset);
|
||||
err_remove_group:
|
||||
sysfs_remove_group(esrt_kobj, &esrt_attr_group);
|
||||
err_remove_esrt:
|
||||
kobject_put(esrt_kobj);
|
||||
err:
|
||||
kfree(esrt);
|
||||
esrt = NULL;
|
||||
return error;
|
||||
}
|
||||
|
||||
static void __exit esrt_sysfs_exit(void)
|
||||
{
|
||||
pr_debug("esrt-sysfs: unloading.\n");
|
||||
cleanup_entry_list();
|
||||
kset_unregister(esrt_kset);
|
||||
sysfs_remove_group(esrt_kobj, &esrt_attr_group);
|
||||
kfree(esrt);
|
||||
esrt = NULL;
|
||||
kobject_del(esrt_kobj);
|
||||
kobject_put(esrt_kobj);
|
||||
}
|
||||
|
||||
module_init(esrt_sysfs_init);
|
||||
module_exit(esrt_sysfs_exit);
|
||||
|
||||
MODULE_AUTHOR("Peter Jones <pjones@redhat.com>");
|
||||
MODULE_DESCRIPTION("EFI System Resource Table support");
|
||||
MODULE_LICENSE("GPL");
|
Reference in New Issue
Block a user