* [RFC PATCH] Add esrt support.
@ 2014-11-25 19:28 Peter Jones
[not found] ` <1416943684-11246-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
0 siblings, 1 reply; 18+ messages in thread
From: Peter Jones @ 2014-11-25 19:28 UTC (permalink / raw)
To: linux-efi-u79uwXL29TY76Z2rM5mHXA; +Cc: Peter Jones
Add sysfs files for EFI System Resource Table under
/sys/firmware/efi/esrt and for each EFI System Resource Entry under
entries/ as a subdir.
---
drivers/firmware/efi/Makefile | 2 +-
drivers/firmware/efi/efi.c | 37 +++-
drivers/firmware/efi/esrt.c | 402 ++++++++++++++++++++++++++++++++++++++++++
include/linux/efi.h | 6 +
4 files changed, 445 insertions(+), 2 deletions(-)
create mode 100644 drivers/firmware/efi/esrt.c
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
index aef6a95..0d61089 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -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
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 8590099..3314522 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -38,6 +38,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);
@@ -63,7 +64,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;
/*
@@ -92,6 +93,8 @@ static ssize_t systab_show(struct kobject *kobj,
str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info);
if (efi.uga != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "UGA=0x%lx\n", efi.uga);
+ if (efi.esrt != EFI_INVALID_TABLE_ADDR)
+ str += sprintf(str, "ESRT=0x%lx\n", efi.esrt);
return str - buf;
}
@@ -220,6 +223,37 @@ err_put:
subsys_initcall(efisubsys_init);
+u64 efi_mem_max_reasonable_size(u64 phys_addr)
+{
+ struct efi_memory_map *map = efi.memmap;
+ void *p, *e;
+
+ if (!map)
+ return 0;
+ if (WARN_ON(!map->phys_map))
+ return 0;
+ if (WARN_ON(map->nr_map == 0) || WARN_ON(map->desc_size == 0))
+ return 0;
+
+ e = map->phys_map + map->nr_map * map->desc_size;
+ for (p = map->phys_map; p < e; p += map->desc_size) {
+ /* If a driver calls this after efi_free_boot_services,
+ * ->map will be NULL. So just always get our own virtual
+ * map on the CPU. */
+ efi_memory_desc_t *md = phys_to_virt((phys_addr_t)p);
+ u64 size = md->num_pages << EFI_PAGE_SHIFT;
+ u64 end = md->phys_addr + size;
+ if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
+ md->type != EFI_BOOT_SERVICES_CODE &&
+ md->type != EFI_BOOT_SERVICES_DATA)
+ continue;
+ if (!md->virt_addr)
+ continue;
+ if (phys_addr >= md->phys_addr && phys_addr < end)
+ return end - phys_addr;
+ }
+ return 0;
+}
/*
* We can't ioremap data in EFI boot services RAM, because we've already mapped
@@ -261,6 +295,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
{SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab},
{SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
{UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
+ {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
{NULL_GUID, NULL, NULL},
};
diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c
new file mode 100644
index 0000000..8c6b238
--- /dev/null
+++ b/drivers/firmware/efi/esrt.c
@@ -0,0 +1,402 @@
+/*
+ * esrt-sysfs.c
+ *
+ * This module exports ESRT entries read-only into userspace through the
+ * sysfs file system.
+ *
+ * Data is currently found below /sys/firmware/efi/esrt/...
+ *
+ */
+#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/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+typedef struct {
+ 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;
+} efi_system_resource_entry_t;
+
+typedef struct {
+ u32 fw_resource_count;
+ u32 fw_resource_count_max;
+ u64 fw_resource_version;
+ efi_system_resource_entry_t entries[];
+} efi_system_resource_table_t;
+
+static efi_system_resource_table_t *esrt;
+
+struct esre_sysfs_entry {
+ efi_system_resource_entry_t esre;
+
+ struct kobject kobj;
+ struct list_head list;
+};
+
+/* global list of esre_sysfs_entry. */
+static LIST_HEAD(entry_list);
+
+/* entry attribute */
+struct esre_sysfs_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct esre_sysfs_entry *entry, char *buf);
+};
+
+static struct esre_sysfs_entry *to_entry(struct kobject *kobj)
+{
+ return container_of(kobj, struct esre_sysfs_entry, kobj);
+}
+
+static struct esre_sysfs_attribute *to_attr(struct attribute *attr)
+{
+ return container_of(attr, struct esre_sysfs_attribute, attr);
+}
+
+static ssize_t esre_sysfs_attr_show(struct kobject *kobj,
+ struct attribute *_attr, char *buf)
+{
+ struct esre_sysfs_entry *entry = to_entry(kobj);
+ struct esre_sysfs_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_sysfs_attr_ops = {
+ .show = esre_sysfs_attr_show,
+};
+
+/* Generic ESRT Entry ("ESRE") support. */
+static ssize_t esre_sysfs_fw_class(struct esre_sysfs_entry *entry, char *buf)
+{
+ char *str = buf;
+ efi_guid_unparse(&entry->esre.fw_class, str);
+ str += strlen(str);
+ str += sprintf(str, "\n");
+
+ return str - buf;
+}
+
+static ssize_t esre_sysfs_fw_type(struct esre_sysfs_entry *entry, char *buf)
+{
+ return sprintf(buf, "%u\n", entry->esre.fw_type);
+}
+
+static ssize_t esre_sysfs_fw_version(struct esre_sysfs_entry *entry, char *buf)
+{
+ return sprintf(buf, "%u\n", entry->esre.fw_version);
+}
+
+static ssize_t esre_sysfs_lowest_supported_fw_version(struct esre_sysfs_entry *entry, char *buf)
+{
+ return sprintf(buf, "%u\n", entry->esre.lowest_supported_fw_version);
+}
+
+static ssize_t esre_sysfs_capsule_flags(struct esre_sysfs_entry *entry, char *buf)
+{
+ return sprintf(buf, "0x%x\n", entry->esre.capsule_flags);
+}
+
+static ssize_t esre_sysfs_last_attempt_version(struct esre_sysfs_entry *entry, char *buf)
+{
+ return sprintf(buf, "%u\n", entry->esre.last_attempt_version);
+}
+
+static ssize_t esre_sysfs_last_attempt_status(struct esre_sysfs_entry *entry, char *buf)
+{
+ return sprintf(buf, "%u\n", entry->esre.last_attempt_status);
+}
+
+#define ESRE_SYSFS_ATTR(_name) \
+struct esre_sysfs_attribute esre_sysfs_attr_##_name = { \
+ .attr = {.name = __stringify(_name), .mode = 0400}, \
+ .show = esre_sysfs_##_name, \
+}
+
+static ESRE_SYSFS_ATTR(fw_class);
+static ESRE_SYSFS_ATTR(fw_type);
+static ESRE_SYSFS_ATTR(fw_version);
+static ESRE_SYSFS_ATTR(lowest_supported_fw_version);
+static ESRE_SYSFS_ATTR(capsule_flags);
+static ESRE_SYSFS_ATTR(last_attempt_version);
+static ESRE_SYSFS_ATTR(last_attempt_status);
+
+static struct attribute *esre_sysfs_attrs[] = {
+ &esre_sysfs_attr_fw_class.attr,
+ &esre_sysfs_attr_fw_type.attr,
+ &esre_sysfs_attr_fw_version.attr,
+ &esre_sysfs_attr_lowest_supported_fw_version.attr,
+ &esre_sysfs_attr_capsule_flags.attr,
+ &esre_sysfs_attr_last_attempt_version.attr,
+ &esre_sysfs_attr_last_attempt_status.attr,
+ NULL
+};
+
+static void esre_sysfs_release(struct kobject *kobj)
+{
+ struct esre_sysfs_entry *entry = to_entry(kobj);
+
+ list_del(&entry->list);
+ kfree(entry);
+}
+
+static struct kobj_type esre_sysfs_ktype = {
+ .release = esre_sysfs_release,
+ .sysfs_ops = &esre_sysfs_attr_ops,
+ .default_attrs = esre_sysfs_attrs,
+};
+
+static struct kobject *esrt_kobj;
+static struct kset *esrt_kset;
+
+static int esre_sysfs_create_sysfs_entry(efi_system_resource_entry_t *esre)
+{
+ int rc;
+ struct esre_sysfs_entry *entry;
+ char *name;
+ int name_size;
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ name_size = EFI_VARIABLE_GUID_LEN + 1;
+ name = kzalloc(name_size, GFP_KERNEL);
+ if (!name) {
+ kfree(entry);
+ return -ENOMEM;
+ }
+ efi_guid_unparse(&esre->fw_class, name);
+
+ memcpy(&entry->esre, esre, sizeof(*esre));
+ entry->kobj.kset = esrt_kset;
+ rc = kobject_init_and_add(&entry->kobj, &esre_sysfs_ktype, NULL,
+ "%s", name);
+ if (rc) {
+ kfree(name);
+ kfree(entry);
+ return rc;
+ }
+ kfree(name);
+
+ list_add_tail(&entry->list, &entry_list);
+ return 0;
+}
+
+/* support for displaying ESRT fields at the top level */
+#define ESRT_FIELD(name) ((esrt)->name)
+
+#define ESRT_ATTR_SHOW(name, size, fmt) \
+static ssize_t esrt_attr_##name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+{ \
+ return sprintf(buf, fmt, le##size##_to_cpu(ESRT_FIELD(name))); \
+} \
+\
+static struct kobj_attribute esrt_attr_##name = __ATTR(name, 0400, \
+ esrt_attr_##name##_show, NULL); \
+
+ESRT_ATTR_SHOW(fw_resource_count, 32, "%u\n");
+ESRT_ATTR_SHOW(fw_resource_count_max, 32, "%u\n");
+ESRT_ATTR_SHOW(fw_resource_version, 64, "%llu\n");
+
+static struct attribute *esrt_attrs[] = {
+ &esrt_attr_fw_resource_count.attr,
+ &esrt_attr_fw_resource_count_max.attr,
+ &esrt_attr_fw_resource_version.attr,
+ NULL,
+};
+
+static umode_t esrt_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ if (!efi_enabled(EFI_CONFIG_TABLES))
+ return 0;
+ if (efi.esrt == EFI_INVALID_TABLE_ADDR)
+ return 0;
+ return attr->mode;
+}
+
+static struct attribute_group esrt_attr_group = {
+ .attrs = esrt_attrs,
+ .is_visible = esrt_attr_is_visible,
+};
+
+/* ioremap the table, copy it to kmalloced pages, and unmap it */
+static int esrt_duplicate_pages(void)
+{
+ efi_system_resource_table_t *tmpesrt;
+ efi_system_resource_entry_t *entries;
+ size_t size, max;
+
+ if (!efi_enabled(EFI_CONFIG_TABLES))
+ return 0;
+ if (efi.esrt == EFI_INVALID_TABLE_ADDR)
+ return 0;
+
+ max = efi_mem_max_reasonable_size(efi.esrt);
+ size = sizeof(*esrt);
+
+ if (max < size) {
+ pr_err("ESRT does not fit on single memory map entry.\n");
+ return -EINVAL;
+ }
+
+ tmpesrt = ioremap(efi.esrt, size);
+ if (!tmpesrt) {
+ pr_err("ioremap failed.\n");
+ return -ENOMEM;
+ }
+
+ size += tmpesrt->fw_resource_count * sizeof (*entries);
+ if (max < size) {
+ pr_err("ESRT does not fit on single memory map entry.\n");
+ return -EINVAL;
+ }
+
+ iounmap(tmpesrt);
+ tmpesrt = ioremap(efi.esrt, size);
+ if (!tmpesrt) {
+ pr_err("ioremap failed.\n");
+ return -ENOMEM;
+ }
+
+ if (tmpesrt->fw_resource_count > tmpesrt->fw_resource_count_max) {
+ pr_err("ESRT has invalid fw_resource_count:"
+ "fw_resource_count_max::%d:%d.\n",
+ tmpesrt->fw_resource_count,
+ tmpesrt->fw_resource_count_max);
+ iounmap(tmpesrt);
+ return -EINVAL;
+ }
+
+ /* attempt to test /some/ boundary... 128 should be pretty excessive */
+ if (tmpesrt->fw_resource_count > 128) {
+ pr_err("ESRT says fw_resource_count has very large value %d.\n",
+ tmpesrt->fw_resource_count);
+ iounmap(tmpesrt);
+ return -EINVAL;
+ }
+
+ esrt = kmalloc(size, GFP_KERNEL);
+ if (!esrt) {
+ iounmap(tmpesrt);
+ pr_err("kmalloc failed.\n");
+ return -ENOMEM;
+ }
+
+ memcpy(esrt, tmpesrt, size);
+ iounmap(tmpesrt);
+ return 0;
+}
+
+static int register_entries(void)
+{
+ efi_system_resource_entry_t *entries = esrt->entries;
+ int i, rc;
+
+ if (!efi_enabled(EFI_CONFIG_TABLES))
+ return 0;
+ if (efi.esrt == EFI_INVALID_TABLE_ADDR)
+ return 0;
+
+ for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
+ rc = esre_sysfs_create_sysfs_entry(&entries[i]);
+ if (rc < 0) {
+ pr_err("EFI System Resource Entry creation "
+ "failed with error %d.\n", rc);
+ return rc;
+ }
+ }
+ return 0;
+}
+
+static void cleanup_entry_list(void)
+{
+ struct esre_sysfs_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 = -ENOMEM;
+
+ esrt_kobj = kobject_create_and_add("esrt", efi_kobj);
+ if (!esrt_kobj) {
+ pr_err("esrt: Firmware table registration failed.\n");
+ return -ENOMEM;
+ }
+
+ error = esrt_duplicate_pages();
+ if (error)
+ goto err_remove_esrt;
+
+ error = sysfs_create_group(esrt_kobj, &esrt_attr_group);
+ if (error) {
+ pr_err("esrt: Sysfs attribute export failed with error %d.\n",
+ error);
+ goto err_free_esrt;
+ }
+
+ esrt_kset = kset_create_and_add("entries", NULL, esrt_kobj);
+ if (!esrt_kset) {
+ pr_err("esrt: kset creation failed.\n");
+ error = -ENOMEM;
+ goto err_remove_group;
+ }
+
+ error = register_entries();
+ if (error)
+ goto err_cleanup_list;
+
+ 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_free_esrt:
+ kfree(esrt);
+ esrt = NULL;
+err_remove_esrt:
+ kobject_put(esrt_kobj);
+ 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-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>");
+MODULE_DESCRIPTION("EFI System Resource Table support");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 0949f9c..5b663a7 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -562,6 +562,9 @@ void efi_native_runtime_setup(void);
#define UV_SYSTEM_TABLE_GUID \
EFI_GUID( 0x3b13a7d4, 0x633e, 0x11dd, 0x93, 0xec, 0xda, 0x25, 0x56, 0xd8, 0x95, 0x93 )
+#define EFI_SYSTEM_RESOURCE_TABLE_GUID \
+ EFI_GUID( 0xb122a263, 0x3661, 0x4f68, 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80 )
+
#define LINUX_EFI_CRASH_GUID \
EFI_GUID( 0xcfc8fc79, 0xbe2e, 0x4ddc, 0x97, 0xf0, 0x9f, 0x98, 0xbf, 0xe2, 0x98, 0xa0 )
@@ -819,6 +822,7 @@ extern struct efi {
unsigned long fw_vendor; /* fw_vendor */
unsigned long runtime; /* runtime table */
unsigned long config_table; /* config tables */
+ unsigned long esrt; /* EFI System Resource Table */
efi_get_time_t *get_time;
efi_set_time_t *set_time;
efi_get_wakeup_time_t *get_wakeup_time;
@@ -875,6 +879,7 @@ extern u64 efi_get_iobase (void);
extern u32 efi_mem_type (unsigned long phys_addr);
extern u64 efi_mem_attributes (unsigned long phys_addr);
extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size);
+extern u64 efi_mem_max_reasonable_size(u64 phys_addr);
extern int __init efi_uart_console_only (void);
extern void efi_initialize_iomem_resources(struct resource *code_resource,
struct resource *data_resource, struct resource *bss_resource);
@@ -882,6 +887,7 @@ extern void efi_get_time(struct timespec *now);
extern void efi_reserve_boot_services(void);
extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose);
extern struct efi_memory_map memmap;
+extern struct kobject *efi_kobj;
extern int efi_reboot_quirk_mode;
extern bool efi_poweroff_required(void);
--
2.1.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [RFC PATCH] Add esrt support.
[not found] ` <1416943684-11246-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
@ 2014-12-08 18:57 ` Borislav Petkov
[not found] ` <20141208185720.GA11013-fF5Pk5pvG8Y@public.gmane.org>
0 siblings, 1 reply; 18+ messages in thread
From: Borislav Petkov @ 2014-12-08 18:57 UTC (permalink / raw)
To: Peter Jones; +Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA
On Tue, Nov 25, 2014 at 02:28:04PM -0500, Peter Jones wrote:
> Add sysfs files for EFI System Resource Table under
> /sys/firmware/efi/esrt and for each EFI System Resource Entry under
> entries/ as a subdir.
Ok, let me make sure I know what I'm looking at:
This is a table which is supposed to describe a bunch of firmware blobs
somewhere, correct?
I've found this doc here: http://download.microsoft.com/download/5/F/5/5F5D16CD-2530-4289-8019-94C6A20BED3C/windows-uefi-firmware-update-platform.docx
The next question is: why do we need it exposed in sysfs? So that we
know what firmware blobs the UEFI carries with it?
> ---
> drivers/firmware/efi/Makefile | 2 +-
> drivers/firmware/efi/efi.c | 37 +++-
> drivers/firmware/efi/esrt.c | 402 ++++++++++++++++++++++++++++++++++++++++++
> include/linux/efi.h | 6 +
> 4 files changed, 445 insertions(+), 2 deletions(-)
> create mode 100644 drivers/firmware/efi/esrt.c
Please run it through checkpatch - some of the warnings actually make
sense.
> diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
> index aef6a95..0d61089 100644
> --- a/drivers/firmware/efi/Makefile
> +++ b/drivers/firmware/efi/Makefile
> @@ -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
> diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
> index 8590099..3314522 100644
> --- a/drivers/firmware/efi/efi.c
> +++ b/drivers/firmware/efi/efi.c
> @@ -38,6 +38,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);
>
> @@ -63,7 +64,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;
>
> /*
> @@ -92,6 +93,8 @@ static ssize_t systab_show(struct kobject *kobj,
> str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info);
> if (efi.uga != EFI_INVALID_TABLE_ADDR)
> str += sprintf(str, "UGA=0x%lx\n", efi.uga);
> + if (efi.esrt != EFI_INVALID_TABLE_ADDR)
> + str += sprintf(str, "ESRT=0x%lx\n", efi.esrt);
>
> return str - buf;
> }
> @@ -220,6 +223,37 @@ err_put:
>
> subsys_initcall(efisubsys_init);
>
> +u64 efi_mem_max_reasonable_size(u64 phys_addr)
How am I to understand the function name? Find a reasonable size or is
the size reasonable?
> +{
> + struct efi_memory_map *map = efi.memmap;
> + void *p, *e;
> +
> + if (!map)
> + return 0;
> + if (WARN_ON(!map->phys_map))
> + return 0;
> + if (WARN_ON(map->nr_map == 0) || WARN_ON(map->desc_size == 0))
If this is going to be called pretty often maybe use WARN_ON_ONCE().
Also, you probably want to return a negative value here so that caller
can stop further processing. I mean, it does so now too but a negative
value makes it more explicit.
> + return 0;
> +
> + e = map->phys_map + map->nr_map * map->desc_size;
> + for (p = map->phys_map; p < e; p += map->desc_size) {
> + /* If a driver calls this after efi_free_boot_services,
> + * ->map will be NULL. So just always get our own virtual
> + * map on the CPU. */
Kernel comment style:
/*
* Blabla.
* Second sentence blabla.
*/
> + efi_memory_desc_t *md = phys_to_virt((phys_addr_t)p);
Maybe be more defensive here:
if (!md)
continue;
Can this even happen, btw?
> + u64 size = md->num_pages << EFI_PAGE_SHIFT;
> + u64 end = md->phys_addr + size;
> + if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
> + md->type != EFI_BOOT_SERVICES_CODE &&
> + md->type != EFI_BOOT_SERVICES_DATA)
> + continue;
This test appears pretty often - maybe we need an inline function for
it. It is unrelated to your patch, of course, I'm just mentioning it
while we're here :-)
> + if (!md->virt_addr)
> + continue;
> + if (phys_addr >= md->phys_addr && phys_addr < end)
> + return end - phys_addr;
> + }
> + return 0;
> +}
>
> /*
> * We can't ioremap data in EFI boot services RAM, because we've already mapped
> @@ -261,6 +295,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
> {SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab},
> {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
> {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
> + {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
> {NULL_GUID, NULL, NULL},
> };
>
> diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c
> new file mode 100644
> index 0000000..8c6b238
> --- /dev/null
> +++ b/drivers/firmware/efi/esrt.c
> @@ -0,0 +1,402 @@
> +/*
> + * esrt-sysfs.c
Either this should be esrt.c or the file should be called esrt-sysfs.c.
I say we create drivers/firmware/efi/sysfs/ and put all the
sysfs-related stuff in there.
> + *
> + * This module exports ESRT entries read-only into userspace through the
> + * sysfs file system.
> + *
> + * Data is currently found below /sys/firmware/efi/esrt/...
> + *
> + */
> +#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/module.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +
> +typedef struct {
> + 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;
> +} efi_system_resource_entry_t;
> +
> +typedef struct {
> + u32 fw_resource_count;
> + u32 fw_resource_count_max;
> + u64 fw_resource_version;
> + efi_system_resource_entry_t entries[];
> +} efi_system_resource_table_t;
> +
> +static efi_system_resource_table_t *esrt;
> +
> +struct esre_sysfs_entry {
> + efi_system_resource_entry_t esre;
> +
> + struct kobject kobj;
> + struct list_head list;
> +};
> +
> +/* global list of esre_sysfs_entry. */
> +static LIST_HEAD(entry_list);
> +
> +/* entry attribute */
> +struct esre_sysfs_attribute {
I'd say no need for the "sysfs" in the name as the whole file is dealing
implicitly with sysfs machinery.
Ditto for other names in here.
> + struct attribute attr;
> + ssize_t (*show)(struct esre_sysfs_entry *entry, char *buf);
> +};
> +
> +static struct esre_sysfs_entry *to_entry(struct kobject *kobj)
> +{
> + return container_of(kobj, struct esre_sysfs_entry, kobj);
> +}
> +
> +static struct esre_sysfs_attribute *to_attr(struct attribute *attr)
> +{
> + return container_of(attr, struct esre_sysfs_attribute, attr);
> +}
> +
> +static ssize_t esre_sysfs_attr_show(struct kobject *kobj,
Right, "esre_attr_show" should be perfectly fine IMO.
> + struct attribute *_attr, char *buf)
> +{
> + struct esre_sysfs_entry *entry = to_entry(kobj);
> + struct esre_sysfs_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_sysfs_attr_ops = {
> + .show = esre_sysfs_attr_show,
> +};
> +
> +/* Generic ESRT Entry ("ESRE") support. */
> +static ssize_t esre_sysfs_fw_class(struct esre_sysfs_entry *entry, char *buf)
> +{
> + char *str = buf;
> + efi_guid_unparse(&entry->esre.fw_class, str);
Can someone enlighten me please why is this thing called "unparse"? It
looks like a
efi_guit_to_str()
to me...
> + str += strlen(str);
> + str += sprintf(str, "\n");
> +
> + return str - buf;
> +}
> +
> +static ssize_t esre_sysfs_fw_type(struct esre_sysfs_entry *entry, char *buf)
> +{
> + return sprintf(buf, "%u\n", entry->esre.fw_type);
> +}
> +
> +static ssize_t esre_sysfs_fw_version(struct esre_sysfs_entry *entry, char *buf)
> +{
> + return sprintf(buf, "%u\n", entry->esre.fw_version);
> +}
> +
> +static ssize_t esre_sysfs_lowest_supported_fw_version(struct esre_sysfs_entry *entry, char *buf)
> +{
> + return sprintf(buf, "%u\n", entry->esre.lowest_supported_fw_version);
> +}
> +
> +static ssize_t esre_sysfs_capsule_flags(struct esre_sysfs_entry *entry, char *buf)
> +{
> + return sprintf(buf, "0x%x\n", entry->esre.capsule_flags);
> +}
> +
> +static ssize_t esre_sysfs_last_attempt_version(struct esre_sysfs_entry *entry, char *buf)
> +{
> + return sprintf(buf, "%u\n", entry->esre.last_attempt_version);
> +}
> +
> +static ssize_t esre_sysfs_last_attempt_status(struct esre_sysfs_entry *entry, char *buf)
> +{
> + return sprintf(buf, "%u\n", entry->esre.last_attempt_status);
> +}
You could define those with a macro too - at least the common printing
%u ones, like you've done with that ESRT_FIELD() macro below.
> +
> +#define ESRE_SYSFS_ATTR(_name) \
> +struct esre_sysfs_attribute esre_sysfs_attr_##_name = { \
> + .attr = {.name = __stringify(_name), .mode = 0400}, \
Ok, so those all are RO, so the purpose of this thing is purely
informational so root can be able to tell what firmware is there in the
UEFI.
> + .show = esre_sysfs_##_name, \
> +}
> +
> +static ESRE_SYSFS_ATTR(fw_class);
> +static ESRE_SYSFS_ATTR(fw_type);
> +static ESRE_SYSFS_ATTR(fw_version);
> +static ESRE_SYSFS_ATTR(lowest_supported_fw_version);
> +static ESRE_SYSFS_ATTR(capsule_flags);
> +static ESRE_SYSFS_ATTR(last_attempt_version);
> +static ESRE_SYSFS_ATTR(last_attempt_status);
> +
> +static struct attribute *esre_sysfs_attrs[] = {
> + &esre_sysfs_attr_fw_class.attr,
> + &esre_sysfs_attr_fw_type.attr,
> + &esre_sysfs_attr_fw_version.attr,
> + &esre_sysfs_attr_lowest_supported_fw_version.attr,
> + &esre_sysfs_attr_capsule_flags.attr,
> + &esre_sysfs_attr_last_attempt_version.attr,
> + &esre_sysfs_attr_last_attempt_status.attr,
> + NULL
> +};
> +
> +static void esre_sysfs_release(struct kobject *kobj)
> +{
> + struct esre_sysfs_entry *entry = to_entry(kobj);
> +
> + list_del(&entry->list);
> + kfree(entry);
> +}
> +
> +static struct kobj_type esre_sysfs_ktype = {
> + .release = esre_sysfs_release,
> + .sysfs_ops = &esre_sysfs_attr_ops,
> + .default_attrs = esre_sysfs_attrs,
> +};
> +
> +static struct kobject *esrt_kobj;
> +static struct kset *esrt_kset;
> +
> +static int esre_sysfs_create_sysfs_entry(efi_system_resource_entry_t *esre)
One "sysfs" too many.
> +{
> + int rc;
> + struct esre_sysfs_entry *entry;
> + char *name;
> + int name_size;
> +
> + entry = kzalloc(sizeof(*entry), GFP_KERNEL);
> + if (!entry)
> + return -ENOMEM;
> +
> + name_size = EFI_VARIABLE_GUID_LEN + 1;
AFAICT this is 37 bytes. It would be probably simple to allocate it on
the stack of register_entries and hand it down to this function for
using by zeroing it out each time. This way you can save yourself the
second kzalloc here.
> + name = kzalloc(name_size, GFP_KERNEL);
> + if (!name) {
> + kfree(entry);
You can solve that with goto and labels. The kernel is full of
examples...
> + return -ENOMEM;
> + }
> + efi_guid_unparse(&esre->fw_class, name);
> +
> + memcpy(&entry->esre, esre, sizeof(*esre));
> + entry->kobj.kset = esrt_kset;
> + rc = kobject_init_and_add(&entry->kobj, &esre_sysfs_ktype, NULL,
> + "%s", name);
> + if (rc) {
> + kfree(name);
> + kfree(entry);
... this too.
> + return rc;
> + }
> + kfree(name);
> +
> + list_add_tail(&entry->list, &entry_list);
> + return 0;
> +}
> +
> +/* support for displaying ESRT fields at the top level */
> +#define ESRT_FIELD(name) ((esrt)->name)
> +
> +#define ESRT_ATTR_SHOW(name, size, fmt) \
> +static ssize_t esrt_attr_##name##_show(struct kobject *kobj, \
> + struct kobj_attribute *attr, char *buf) \
> +{ \
> + return sprintf(buf, fmt, le##size##_to_cpu(ESRT_FIELD(name))); \
> +} \
> +\
> +static struct kobj_attribute esrt_attr_##name = __ATTR(name, 0400, \
> + esrt_attr_##name##_show, NULL); \
> +
> +ESRT_ATTR_SHOW(fw_resource_count, 32, "%u\n");
> +ESRT_ATTR_SHOW(fw_resource_count_max, 32, "%u\n");
> +ESRT_ATTR_SHOW(fw_resource_version, 64, "%llu\n");
> +
> +static struct attribute *esrt_attrs[] = {
> + &esrt_attr_fw_resource_count.attr,
> + &esrt_attr_fw_resource_count_max.attr,
> + &esrt_attr_fw_resource_version.attr,
> + NULL,
> +};
> +
> +static umode_t esrt_attr_is_visible(struct kobject *kobj,
> + struct attribute *attr, int n)
Arg alignment should be under the opening brace, i.e.:
static umode_t esrt_attr_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
so that it is more perrty :-P
> +{
> + if (!efi_enabled(EFI_CONFIG_TABLES))
> + return 0;
> + if (efi.esrt == EFI_INVALID_TABLE_ADDR)
> + return 0;
> + return attr->mode;
> +}
> +
> +static struct attribute_group esrt_attr_group = {
> + .attrs = esrt_attrs,
> + .is_visible = esrt_attr_is_visible,
> +};
> +
> +/* ioremap the table, copy it to kmalloced pages, and unmap it */
> +static int esrt_duplicate_pages(void)
> +{
> + efi_system_resource_table_t *tmpesrt;
> + efi_system_resource_entry_t *entries;
> + size_t size, max;
> +
> + if (!efi_enabled(EFI_CONFIG_TABLES))
> + return 0;
> + if (efi.esrt == EFI_INVALID_TABLE_ADDR)
> + return 0;
This double-test is repeated from above, maybe it should be an inline
function. I see it used again below also...
> +
> + max = efi_mem_max_reasonable_size(efi.esrt);
> + size = sizeof(*esrt);
> +
> + if (max < size) {
> + pr_err("ESRT does not fit on single memory map entry.\n");
> + return -EINVAL;
> + }
> +
> + tmpesrt = ioremap(efi.esrt, size);
> + if (!tmpesrt) {
> + pr_err("ioremap failed.\n");
> + return -ENOMEM;
> + }
> +
> + size += tmpesrt->fw_resource_count * sizeof (*entries);
> + if (max < size) {
> + pr_err("ESRT does not fit on single memory map entry.\n");
> + return -EINVAL;
> + }
We probably should protect ourselves on the upper end too in case
->fw_resource_count is nuts...
> +
> + iounmap(tmpesrt);
> + tmpesrt = ioremap(efi.esrt, size);
> + if (!tmpesrt) {
> + pr_err("ioremap failed.\n");
> + return -ENOMEM;
> + }
> +
> + if (tmpesrt->fw_resource_count > tmpesrt->fw_resource_count_max) {
> + pr_err("ESRT has invalid fw_resource_count:"
> + "fw_resource_count_max::%d:%d.\n",
> + tmpesrt->fw_resource_count,
> + tmpesrt->fw_resource_count_max);
> + iounmap(tmpesrt);
> + return -EINVAL;
> + }
Ditto.
> +
> + /* attempt to test /some/ boundary... 128 should be pretty excessive */
> + if (tmpesrt->fw_resource_count > 128) {
> + pr_err("ESRT says fw_resource_count has very large value %d.\n",
> + tmpesrt->fw_resource_count);
> + iounmap(tmpesrt);
> + return -EINVAL;
> + }
... ah, here it is, ->fw_resource_count_max might be nuts too. You
probably should pull those checks up, before you use the values. And
test against sane defaults and not trust the FW. 128 looks like an
arbitrary chosen value, isn't this spec-ed somewhere?
I'm not saying the spec knows what it is doing either but at least it is
there and people can point fingers at it.
> +
> + esrt = kmalloc(size, GFP_KERNEL);
> + if (!esrt) {
> + iounmap(tmpesrt);
> + pr_err("kmalloc failed.\n");
> + return -ENOMEM;
> + }
> +
> + memcpy(esrt, tmpesrt, size);
> + iounmap(tmpesrt);
> + return 0;
Also, I'd suggest using goto with labels here too.
> +}
> +
> +static int register_entries(void)
> +{
> + efi_system_resource_entry_t *entries = esrt->entries;
> + int i, rc;
> +
> + if (!efi_enabled(EFI_CONFIG_TABLES))
> + return 0;
> + if (efi.esrt == EFI_INVALID_TABLE_ADDR)
> + return 0;
> +
> + for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
I wouldn't trust what I'm getting from the fw and sanity-check it first,
before iterating over it.
> + rc = esre_sysfs_create_sysfs_entry(&entries[i]);
> + if (rc < 0) {
> + pr_err("EFI System Resource Entry creation "
> + "failed with error %d.\n", rc);
> + return rc;
You need to properly unwind here and destroy the successfully created
sysfs entries if you encounter an error at some later entry which isn't
the first.
> + }
> + }
> + return 0;
> +}
> +
> +static void cleanup_entry_list(void)
> +{
> + struct esre_sysfs_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 = -ENOMEM;
Useless variable initialization.
> +
> + esrt_kobj = kobject_create_and_add("esrt", efi_kobj);
> + if (!esrt_kobj) {
> + pr_err("esrt: Firmware table registration failed.\n");
"esrt" prefix is done with pr_fmt, grep the kernel sources for examples.
> + return -ENOMEM;
> + }
> +
> + error = esrt_duplicate_pages();
> + if (error)
> + goto err_remove_esrt;
Move that call up, before the kobject_create_and_add(). It'll simplify
the error path too.
> +
> + error = sysfs_create_group(esrt_kobj, &esrt_attr_group);
> + if (error) {
> + pr_err("esrt: Sysfs attribute export failed with error %d.\n",
> + error);
> + goto err_free_esrt;
> + }
> +
> + esrt_kset = kset_create_and_add("entries", NULL, esrt_kobj);
> + if (!esrt_kset) {
> + pr_err("esrt: kset creation failed.\n");
> + error = -ENOMEM;
> + goto err_remove_group;
> + }
> +
> + error = register_entries();
> + if (error)
> + goto err_cleanup_list;
> +
> + pr_debug("esrt-sysfs: loaded.\n");
Debugging left-over?
> +
> + return 0;
> +err_cleanup_list:
> + cleanup_entry_list();
> + kset_unregister(esrt_kset);
> +err_remove_group:
> + sysfs_remove_group(esrt_kobj, &esrt_attr_group);
> +err_free_esrt:
> + kfree(esrt);
> + esrt = NULL;
> +err_remove_esrt:
> + kobject_put(esrt_kobj);
> + return error;
Yep, this is what I mean with goto and labels.
> +}
> +
> +static void __exit esrt_sysfs_exit(void)
> +{
> + pr_debug("esrt-sysfs: unloading.\n");
More debugging leftovers.
> + 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-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>");
> +MODULE_DESCRIPTION("EFI System Resource Table support");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index 0949f9c..5b663a7 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -562,6 +562,9 @@ void efi_native_runtime_setup(void);
> #define UV_SYSTEM_TABLE_GUID \
> EFI_GUID( 0x3b13a7d4, 0x633e, 0x11dd, 0x93, 0xec, 0xda, 0x25, 0x56, 0xd8, 0x95, 0x93 )
>
> +#define EFI_SYSTEM_RESOURCE_TABLE_GUID \
> + EFI_GUID( 0xb122a263, 0x3661, 0x4f68, 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80 )
> +
> #define LINUX_EFI_CRASH_GUID \
> EFI_GUID( 0xcfc8fc79, 0xbe2e, 0x4ddc, 0x97, 0xf0, 0x9f, 0x98, 0xbf, 0xe2, 0x98, 0xa0 )
>
> @@ -819,6 +822,7 @@ extern struct efi {
> unsigned long fw_vendor; /* fw_vendor */
> unsigned long runtime; /* runtime table */
> unsigned long config_table; /* config tables */
> + unsigned long esrt; /* EFI System Resource Table */
> efi_get_time_t *get_time;
> efi_set_time_t *set_time;
> efi_get_wakeup_time_t *get_wakeup_time;
> @@ -875,6 +879,7 @@ extern u64 efi_get_iobase (void);
> extern u32 efi_mem_type (unsigned long phys_addr);
> extern u64 efi_mem_attributes (unsigned long phys_addr);
> extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size);
> +extern u64 efi_mem_max_reasonable_size(u64 phys_addr);
> extern int __init efi_uart_console_only (void);
> extern void efi_initialize_iomem_resources(struct resource *code_resource,
> struct resource *data_resource, struct resource *bss_resource);
> @@ -882,6 +887,7 @@ extern void efi_get_time(struct timespec *now);
> extern void efi_reserve_boot_services(void);
> extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose);
> extern struct efi_memory_map memmap;
> +extern struct kobject *efi_kobj;
>
> extern int efi_reboot_quirk_mode;
> extern bool efi_poweroff_required(void);
> --
> 2.1.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-efi" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
Regards/Gruss,
Boris.
Sent from a fat crate under my desk. Formatting is fine.
--
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC PATCH] Add esrt support.
[not found] ` <20141208185720.GA11013-fF5Pk5pvG8Y@public.gmane.org>
@ 2014-12-09 21:38 ` Peter Jones
[not found] ` <20141209213847.GB1159-FS9oOTXHwv9t4tGkRPVz9tcb/sdHg95EuydrBrBl+0sAvxtiuMwx3w@public.gmane.org>
0 siblings, 1 reply; 18+ messages in thread
From: Peter Jones @ 2014-12-09 21:38 UTC (permalink / raw)
To: Borislav Petkov; +Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA
On Mon, Dec 08, 2014 at 07:57:20PM +0100, Borislav Petkov wrote:
> On Tue, Nov 25, 2014 at 02:28:04PM -0500, Peter Jones wrote:
> > Add sysfs files for EFI System Resource Table under
> > /sys/firmware/efi/esrt and for each EFI System Resource Entry under
> > entries/ as a subdir.
>
> Ok, let me make sure I know what I'm looking at:
>
> This is a table which is supposed to describe a bunch of firmware blobs
> somewhere, correct?
Yes. I'll add a comment explaining that.
> I've found this doc here: http://download.microsoft.com/download/5/F/5/5F5D16CD-2530-4289-8019-94C6A20BED3C/windows-uefi-firmware-update-platform.docx
It started life there, and there's also UEFI ECR 1090, and as such it'll
be formally defined in UEFI 2.5, but I can't provide a URL for either of
those at this time.
Unfortunately, there are a couple of bizarre things about the table, but
basically it got created and used in a fair number of platforms before
it got standardized, and I think they didn't want to make a second rev
of the table so early. I'm not really sure I know why it landed as it
did, but there are some things I wish were different in the format.
That's life.
> The next question is: why do we need it exposed in sysfs? So that we
> know what firmware blobs the UEFI carries with it?
The point is to allow userland utilities to see which firmware
updates are /applicable/, and arrange (through external means) to have
them applied. In particular, the plan is to read this data in userland,
and also to parse data files supplied with firmware images in userland,
determine what firmwares can be applied, and set up a UEFI binary to do
the update during the next reboot.
I'll add a comment somewhere to this effect.
>
> > ---
> > drivers/firmware/efi/Makefile | 2 +-
> > drivers/firmware/efi/efi.c | 37 +++-
> > drivers/firmware/efi/esrt.c | 402 ++++++++++++++++++++++++++++++++++++++++++
> > include/linux/efi.h | 6 +
> > 4 files changed, 445 insertions(+), 2 deletions(-)
> > create mode 100644 drivers/firmware/efi/esrt.c
>
> Please run it through checkpatch - some of the warnings actually make
> sense.
Oh, sorry about that. Will do.
>
[snip]
> >
> > subsys_initcall(efisubsys_init);
> >
> > +u64 efi_mem_max_reasonable_size(u64 phys_addr)
>
> How am I to understand the function name? Find a reasonable size or is
> the size reasonable?
It is attempting to find the firmware's idea of the memory region the
table is in, and use that to tell us the maximum address the table may
occupy. That is, since there's not very good data in the table to
verify its correctness, I want to enforce an upper bound to what we're
trying /somehow/. EFI's memory map seems like the way to go.
Maybe I should go with "efi_mem_get_map_size()"? Except as written it
does the subtraction internally, so it's really just what's /left/ of a
map after the provided address. Suggestions welcome. In the meantime,
I'll add a comment explaining this.
>
> > +{
> > + struct efi_memory_map *map = efi.memmap;
> > + void *p, *e;
> > +
> > + if (!map)
> > + return 0;
> > + if (WARN_ON(!map->phys_map))
> > + return 0;
> > + if (WARN_ON(map->nr_map == 0) || WARN_ON(map->desc_size == 0))
>
> If this is going to be called pretty often maybe use WARN_ON_ONCE().
> Also, you probably want to return a negative value here so that caller
> can stop further processing. I mean, it does so now too but a negative
> value makes it more explicit.
Yeah, the negative number is a good point. Right now I think, if I make
it stop iterating when that happens, then the WARN_ON won't be triggered
more than once per piece of code using it. Right now that's once piece
of code. I think even if it becomes more, that's probably the right thing,
because it indicates the calling code has done something wrong with the
data in the first place.
> > + return 0;
> > +
> > + e = map->phys_map + map->nr_map * map->desc_size;
> > + for (p = map->phys_map; p < e; p += map->desc_size) {
> > + /* If a driver calls this after efi_free_boot_services,
> > + * ->map will be NULL. So just always get our own virtual
> > + * map on the CPU. */
>
> Kernel comment style:
>
> /*
> * Blabla.
> * Second sentence blabla.
> */
Okay, I'll fix that.
>
> > + efi_memory_desc_t *md = phys_to_virt((phys_addr_t)p);
>
> Maybe be more defensive here:
>
> if (!md)
> continue;
>
> Can this even happen, btw?
I don't think so? I can't find an abundance of other code error
checking phys_to_virt(). AIUI it does nothing but temporarily
populate a TLB entry, and there's code /all over/ the kernel that
doesn't test its output, so I think it's reasonably safe.
>
> > + u64 size = md->num_pages << EFI_PAGE_SHIFT;
> > + u64 end = md->phys_addr + size;
> > + if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
> > + md->type != EFI_BOOT_SERVICES_CODE &&
> > + md->type != EFI_BOOT_SERVICES_DATA)
> > + continue;
>
> This test appears pretty often - maybe we need an inline function for
> it. It is unrelated to your patch, of course, I'm just mentioning it
> while we're here :-)
A worthwhile point, yeah.
>
[trim]
> > +++ b/drivers/firmware/efi/esrt.c
> > @@ -0,0 +1,402 @@
> > +/*
> > + * esrt-sysfs.c
>
> Either this should be esrt.c or the file should be called esrt-sysfs.c.
Yep, sorry for that. Will fix.
> I say we create drivers/firmware/efi/sysfs/ and put all the
> sysfs-related stuff in there.
Sounds reasonable enough to me, but I think it should be a separate
patch from one that adds specific functionality.
>
[lots of trimming I've done here]
> > +/* entry attribute */
> > +struct esre_sysfs_attribute {
>
> I'd say no need for the "sysfs" in the name as the whole file is dealing
> implicitly with sysfs machinery.
>
> Ditto for other names in here.
Okay.
> > + struct attribute attr;
> > + ssize_t (*show)(struct esre_sysfs_entry *entry, char *buf);
> > +};
> > +
> > +static struct esre_sysfs_entry *to_entry(struct kobject *kobj)
> > +{
> > + return container_of(kobj, struct esre_sysfs_entry, kobj);
> > +}
> > +
> > +static struct esre_sysfs_attribute *to_attr(struct attribute *attr)
> > +{
> > + return container_of(attr, struct esre_sysfs_attribute, attr);
> > +}
> > +
> > +static ssize_t esre_sysfs_attr_show(struct kobject *kobj,
>
> Right, "esre_attr_show" should be perfectly fine IMO.
>
> > + struct attribute *_attr, char *buf)
> > +{
> > + struct esre_sysfs_entry *entry = to_entry(kobj);
> > + struct esre_sysfs_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_sysfs_attr_ops = {
> > + .show = esre_sysfs_attr_show,
> > +};
> > +
> > +/* Generic ESRT Entry ("ESRE") support. */
> > +static ssize_t esre_sysfs_fw_class(struct esre_sysfs_entry *entry, char *buf)
> > +{
> > + char *str = buf;
> > + efi_guid_unparse(&entry->esre.fw_class, str);
>
> Can someone enlighten me please why is this thing called "unparse"? It
> looks like a
>
> efi_guit_to_str()
>
> to me...
I couldn't agree more.
>
> > + str += strlen(str);
> > + str += sprintf(str, "\n");
> > +
> > + return str - buf;
> > +}
> > +
> > +static ssize_t esre_sysfs_fw_type(struct esre_sysfs_entry *entry, char *buf)
> > +{
> > + return sprintf(buf, "%u\n", entry->esre.fw_type);
> > +}
> > +
> > +static ssize_t esre_sysfs_fw_version(struct esre_sysfs_entry *entry, char *buf)
> > +{
> > + return sprintf(buf, "%u\n", entry->esre.fw_version);
> > +}
> > +
> > +static ssize_t esre_sysfs_lowest_supported_fw_version(struct esre_sysfs_entry *entry, char *buf)
> > +{
> > + return sprintf(buf, "%u\n", entry->esre.lowest_supported_fw_version);
> > +}
> > +
> > +static ssize_t esre_sysfs_capsule_flags(struct esre_sysfs_entry *entry, char *buf)
> > +{
> > + return sprintf(buf, "0x%x\n", entry->esre.capsule_flags);
> > +}
> > +
> > +static ssize_t esre_sysfs_last_attempt_version(struct esre_sysfs_entry *entry, char *buf)
> > +{
> > + return sprintf(buf, "%u\n", entry->esre.last_attempt_version);
> > +}
> > +
> > +static ssize_t esre_sysfs_last_attempt_status(struct esre_sysfs_entry *entry, char *buf)
> > +{
> > + return sprintf(buf, "%u\n", entry->esre.last_attempt_status);
> > +}
>
> You could define those with a macro too - at least the common printing
> %u ones, like you've done with that ESRT_FIELD() macro below.
Honestly I'm not sure why there's not a higher level macro to do this -
other people have to dump %u and %x data into sysfs, don't they? Is
there some radically different way I should be doing this altogether,
maybe?
Anyway, I've made all this macros in my next revision.
>
> > +
> > +#define ESRE_SYSFS_ATTR(_name) \
> > +struct esre_sysfs_attribute esre_sysfs_attr_##_name = { \
> > + .attr = {.name = __stringify(_name), .mode = 0400}, \
>
> Ok, so those all are RO, so the purpose of this thing is purely
> informational so root can be able to tell what firmware is there in the
> UEFI.
Yes.
>
>
> > + .show = esre_sysfs_##_name, \
> > +}
> > +
> > +static ESRE_SYSFS_ATTR(fw_class);
> > +static ESRE_SYSFS_ATTR(fw_type);
> > +static ESRE_SYSFS_ATTR(fw_version);
> > +static ESRE_SYSFS_ATTR(lowest_supported_fw_version);
> > +static ESRE_SYSFS_ATTR(capsule_flags);
> > +static ESRE_SYSFS_ATTR(last_attempt_version);
> > +static ESRE_SYSFS_ATTR(last_attempt_status);
> > +
> > +static struct attribute *esre_sysfs_attrs[] = {
> > + &esre_sysfs_attr_fw_class.attr,
> > + &esre_sysfs_attr_fw_type.attr,
> > + &esre_sysfs_attr_fw_version.attr,
> > + &esre_sysfs_attr_lowest_supported_fw_version.attr,
> > + &esre_sysfs_attr_capsule_flags.attr,
> > + &esre_sysfs_attr_last_attempt_version.attr,
> > + &esre_sysfs_attr_last_attempt_status.attr,
> > + NULL
> > +};
> > +
> > +static void esre_sysfs_release(struct kobject *kobj)
> > +{
> > + struct esre_sysfs_entry *entry = to_entry(kobj);
> > +
> > + list_del(&entry->list);
> > + kfree(entry);
> > +}
> > +
> > +static struct kobj_type esre_sysfs_ktype = {
> > + .release = esre_sysfs_release,
> > + .sysfs_ops = &esre_sysfs_attr_ops,
> > + .default_attrs = esre_sysfs_attrs,
> > +};
> > +
> > +static struct kobject *esrt_kobj;
> > +static struct kset *esrt_kset;
> > +
> > +static int esre_sysfs_create_sysfs_entry(efi_system_resource_entry_t *esre)
>
> One "sysfs" too many.
Okay.
>
> > +{
> > + int rc;
> > + struct esre_sysfs_entry *entry;
> > + char *name;
> > + int name_size;
> > +
> > + entry = kzalloc(sizeof(*entry), GFP_KERNEL);
> > + if (!entry)
> > + return -ENOMEM;
> > +
> > + name_size = EFI_VARIABLE_GUID_LEN + 1;
>
> AFAICT this is 37 bytes. It would be probably simple to allocate it on
> the stack of register_entries and hand it down to this function for
> using by zeroing it out each time. This way you can save yourself the
> second kzalloc here.
I get why you're saying it's small and thus I should put it on the
stack, but is there any good reason not to use the local stack here?
I'm thinking that's better, and I'll do it in the upcoming patch. If
you do have a good reason, I'm happy to product another revision.
>
> > + name = kzalloc(name_size, GFP_KERNEL);
> > + if (!name) {
> > + kfree(entry);
>
> You can solve that with goto and labels. The kernel is full of
> examples...
Yeah, I know how that works; having only 2 things to free, it didn't
seem like it made much difference. Now that I'm down to one, that's
even more the case. :)
>
> > + return -ENOMEM;
> > + }
> > + efi_guid_unparse(&esre->fw_class, name);
> > +
> > + memcpy(&entry->esre, esre, sizeof(*esre));
> > + entry->kobj.kset = esrt_kset;
> > + rc = kobject_init_and_add(&entry->kobj, &esre_sysfs_ktype, NULL,
> > + "%s", name);
> > + if (rc) {
> > + kfree(name);
> > + kfree(entry);
>
> ... this too.
>
> > + return rc;
> > + }
> > + kfree(name);
> > +
> > + list_add_tail(&entry->list, &entry_list);
> > + return 0;
> > +}
> > +
> > +/* support for displaying ESRT fields at the top level */
> > +#define ESRT_FIELD(name) ((esrt)->name)
> > +
> > +#define ESRT_ATTR_SHOW(name, size, fmt) \
> > +static ssize_t esrt_attr_##name##_show(struct kobject *kobj, \
> > + struct kobj_attribute *attr, char *buf) \
> > +{ \
> > + return sprintf(buf, fmt, le##size##_to_cpu(ESRT_FIELD(name))); \
> > +} \
> > +\
> > +static struct kobj_attribute esrt_attr_##name = __ATTR(name, 0400, \
> > + esrt_attr_##name##_show, NULL); \
> > +
> > +ESRT_ATTR_SHOW(fw_resource_count, 32, "%u\n");
> > +ESRT_ATTR_SHOW(fw_resource_count_max, 32, "%u\n");
> > +ESRT_ATTR_SHOW(fw_resource_version, 64, "%llu\n");
> > +
> > +static struct attribute *esrt_attrs[] = {
> > + &esrt_attr_fw_resource_count.attr,
> > + &esrt_attr_fw_resource_count_max.attr,
> > + &esrt_attr_fw_resource_version.attr,
> > + NULL,
> > +};
> > +
> > +static umode_t esrt_attr_is_visible(struct kobject *kobj,
> > + struct attribute *attr, int n)
>
> Arg alignment should be under the opening brace, i.e.:
>
> static umode_t esrt_attr_is_visible(struct kobject *kobj,
> struct attribute *attr, int n)
>
> so that it is more perrty :-P
Awesomely that failed to align properly in email, but I take your point
and I'll fix it up.
>
> > +{
> > + if (!efi_enabled(EFI_CONFIG_TABLES))
> > + return 0;
> > + if (efi.esrt == EFI_INVALID_TABLE_ADDR)
> > + return 0;
> > + return attr->mode;
> > +}
> > +
> > +static struct attribute_group esrt_attr_group = {
> > + .attrs = esrt_attrs,
> > + .is_visible = esrt_attr_is_visible,
> > +};
> > +
> > +/* ioremap the table, copy it to kmalloced pages, and unmap it */
> > +static int esrt_duplicate_pages(void)
> > +{
> > + efi_system_resource_table_t *tmpesrt;
> > + efi_system_resource_entry_t *entries;
> > + size_t size, max;
> > +
> > + if (!efi_enabled(EFI_CONFIG_TABLES))
> > + return 0;
> > + if (efi.esrt == EFI_INVALID_TABLE_ADDR)
> > + return 0;
>
> This double-test is repeated from above, maybe it should be an inline
> function. I see it used again below also...
Okay.
>
> > +
> > + max = efi_mem_max_reasonable_size(efi.esrt);
> > + size = sizeof(*esrt);
> > +
> > + if (max < size) {
> > + pr_err("ESRT does not fit on single memory map entry.\n");
> > + return -EINVAL;
> > + }
> > +
> > + tmpesrt = ioremap(efi.esrt, size);
> > + if (!tmpesrt) {
> > + pr_err("ioremap failed.\n");
> > + return -ENOMEM;
> > + }
> > +
> > + size += tmpesrt->fw_resource_count * sizeof (*entries);
> > + if (max < size) {
> > + pr_err("ESRT does not fit on single memory map entry.\n");
> > + return -EINVAL;
> > + }
>
> We probably should protect ourselves on the upper end too in case
> ->fw_resource_count is nuts...
_max isn't actually a meaningful value at all (to us), and its naming is
quite unfortunate. I've reworked this a bit to try and handle this
suggestion and those below, though.
>
> > +
> > + iounmap(tmpesrt);
> > + tmpesrt = ioremap(efi.esrt, size);
> > + if (!tmpesrt) {
> > + pr_err("ioremap failed.\n");
> > + return -ENOMEM;
> > + }
> > +
> > + if (tmpesrt->fw_resource_count > tmpesrt->fw_resource_count_max) {
> > + pr_err("ESRT has invalid fw_resource_count:"
> > + "fw_resource_count_max::%d:%d.\n",
> > + tmpesrt->fw_resource_count,
> > + tmpesrt->fw_resource_count_max);
> > + iounmap(tmpesrt);
> > + return -EINVAL;
> > + }
>
> Ditto.
Oh, I've made a serious error here; _max here isn't actually what it
seems like, and obviously I need a comment to explain that and also to
fix this code, because it's wrong and I tricked myself with the name
from the spec as well.
>
> > +
> > + /* attempt to test /some/ boundary... 128 should be pretty excessive */
> > + if (tmpesrt->fw_resource_count > 128) {
> > + pr_err("ESRT says fw_resource_count has very large value %d.\n",
> > + tmpesrt->fw_resource_count);
> > + iounmap(tmpesrt);
> > + return -EINVAL;
> > + }
>
> ... ah, here it is, ->fw_resource_count_max might be nuts too. You
> probably should pull those checks up, before you use the values. And
> test against sane defaults and not trust the FW. 128 looks like an
> arbitrary chosen value, isn't this spec-ed somewhere?
>
> I'm not saying the spec knows what it is doing either but at least it is
> there and people can point fingers at it.
No, this is apparently something neither I nor anybody else who read the
spec thought about at the time. I count it as a personal failing :/
>
> > +
> > + esrt = kmalloc(size, GFP_KERNEL);
> > + if (!esrt) {
> > + iounmap(tmpesrt);
> > + pr_err("kmalloc failed.\n");
> > + return -ENOMEM;
> > + }
> > +
> > + memcpy(esrt, tmpesrt, size);
> > + iounmap(tmpesrt);
> > + return 0;
>
> Also, I'd suggest using goto with labels here too.
Okay.
>
> > +}
> > +
> > +static int register_entries(void)
> > +{
> > + efi_system_resource_entry_t *entries = esrt->entries;
> > + int i, rc;
> > +
> > + if (!efi_enabled(EFI_CONFIG_TABLES))
> > + return 0;
> > + if (efi.esrt == EFI_INVALID_TABLE_ADDR)
> > + return 0;
> > +
> > + for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
>
> I wouldn't trust what I'm getting from the fw and sanity-check it first,
> before iterating over it.
We actually already checked this. register_entries() is operating on
the generated output from esrt_duplicate_pages(), which would have
returned error in the cases we don't believe it's sane. In that case,
an error has been returned before this function is ever called.
>
> > + rc = esre_sysfs_create_sysfs_entry(&entries[i]);
> > + if (rc < 0) {
> > + pr_err("EFI System Resource Entry creation "
> > + "failed with error %d.\n", rc);
> > + return rc;
>
> You need to properly unwind here and destroy the successfully created
> sysfs entries if you encounter an error at some later entry which isn't
> the first.
Do I? I'm returning to esrt_sysfs_init() here, and if this function
fails /that/ function will call cleanup_entry_list(), which does a
kobject_put() on each entry. AFAICS, that's going to call
esre_release(), and that's all the cleanup that's needed.
Have I missed something?
>
> > + }
> > + }
> > + return 0;
> > +}
> > +
> > +static void cleanup_entry_list(void)
> > +{
> > + struct esre_sysfs_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 = -ENOMEM;
>
> Useless variable initialization.
Okay.
>
> > +
> > + esrt_kobj = kobject_create_and_add("esrt", efi_kobj);
> > + if (!esrt_kobj) {
> > + pr_err("esrt: Firmware table registration failed.\n");
>
> "esrt" prefix is done with pr_fmt, grep the kernel sources for examples.
Okay.
> > + return -ENOMEM;
> > + }
> > +
> > + error = esrt_duplicate_pages();
> > + if (error)
> > + goto err_remove_esrt;
>
> Move that call up, before the kobject_create_and_add(). It'll simplify
> the error path too.
Yep.
>
> > +
> > + error = sysfs_create_group(esrt_kobj, &esrt_attr_group);
> > + if (error) {
> > + pr_err("esrt: Sysfs attribute export failed with error %d.\n",
> > + error);
> > + goto err_free_esrt;
> > + }
> > +
> > + esrt_kset = kset_create_and_add("entries", NULL, esrt_kobj);
> > + if (!esrt_kset) {
> > + pr_err("esrt: kset creation failed.\n");
> > + error = -ENOMEM;
> > + goto err_remove_group;
> > + }
> > +
> > + error = register_entries();
> > + if (error)
> > + goto err_cleanup_list;
> > +
> > + pr_debug("esrt-sysfs: loaded.\n");
>
> Debugging left-over?
Isn't that the definition of pr_debug()? In theory if there's a bug
someday due to some bizarre table value, I'd like to be able to boot
with debugging and see that the message doesn't get printed...
>
> > +
> > + return 0;
> > +err_cleanup_list:
> > + cleanup_entry_list();
> > + kset_unregister(esrt_kset);
> > +err_remove_group:
> > + sysfs_remove_group(esrt_kobj, &esrt_attr_group);
> > +err_free_esrt:
> > + kfree(esrt);
> > + esrt = NULL;
> > +err_remove_esrt:
> > + kobject_put(esrt_kobj);
> > + return error;
>
> Yep, this is what I mean with goto and labels.
Right - apparently you and I have different thresholds for how much
cleanup you're doing before it's worthwhile. I'm willing to accept your
evaluation of that.
>
> > +}
> > +
> > +static void __exit esrt_sysfs_exit(void)
> > +{
> > + pr_debug("esrt-sysfs: unloading.\n");
>
> More debugging leftovers.
Same question/point above applies, though. Seems like if something is
going wrong out in the real world, these would be worthwhile.
[cut the rest out as it had no more comments]
Thanks for the feedback, I'll incorporate it and send out a new version
soon.
--
Peter
^ permalink raw reply [flat|nested] 18+ messages in thread
* [RFC PATCH] Add esrt support.
[not found] ` <20141209213847.GB1159-FS9oOTXHwv9t4tGkRPVz9tcb/sdHg95EuydrBrBl+0sAvxtiuMwx3w@public.gmane.org>
@ 2014-12-09 22:18 ` Peter Jones
[not found] ` <1418163514-22977-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
0 siblings, 1 reply; 18+ messages in thread
From: Peter Jones @ 2014-12-09 22:18 UTC (permalink / raw)
To: linux-efi-u79uwXL29TY76Z2rM5mHXA; +Cc: Peter Jones
Add sysfs files for EFI System Resource Table under
/sys/firmware/efi/esrt and for each EFI System Resource Entry under
entries/ as a subdir.
v2 with suggestions from bpetkov.
Signed-off-by: Peter Jones <pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
drivers/firmware/efi/Makefile | 2 +-
drivers/firmware/efi/efi.c | 45 ++++-
drivers/firmware/efi/esrt.c | 392 ++++++++++++++++++++++++++++++++++++++++++
include/linux/efi.h | 6 +
4 files changed, 443 insertions(+), 2 deletions(-)
create mode 100644 drivers/firmware/efi/esrt.c
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
index aef6a95..0d61089 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -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
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 8590099..bc7eaca 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -38,6 +38,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);
@@ -63,7 +64,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;
/*
@@ -92,6 +93,8 @@ static ssize_t systab_show(struct kobject *kobj,
str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info);
if (efi.uga != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "UGA=0x%lx\n", efi.uga);
+ if (efi.esrt != EFI_INVALID_TABLE_ADDR)
+ str += sprintf(str, "ESRT=0x%lx\n", efi.esrt);
return str - buf;
}
@@ -220,6 +223,45 @@ err_put:
subsys_initcall(efisubsys_init);
+/*
+ * Given a physicall address, determine if it exists within an EFI Memory Map
+ * entry, and if so, how much of that map exists at a higher address. That
+ * is, if this is the address of something in an EFI map, what's the highest
+ * address at which it's likely to end.
+ */
+u64 efi_mem_max_reasonable_size(u64 phys_addr)
+{
+ struct efi_memory_map *map = efi.memmap;
+ void *p, *e;
+
+ if (!map)
+ return -1;
+ if (WARN_ON(!map->phys_map))
+ return -1;
+ if (WARN_ON(map->nr_map == 0) || WARN_ON(map->desc_size == 0))
+ return -1;
+
+ e = map->phys_map + map->nr_map * map->desc_size;
+ for (p = map->phys_map; p < e; p += map->desc_size) {
+ /*
+ * If a driver calls this after efi_free_boot_services,
+ * ->map will be NULL.
+ * So just always get our own virtual map on the CPU.
+ */
+ efi_memory_desc_t *md = phys_to_virt((phys_addr_t)p);
+ u64 size = md->num_pages << EFI_PAGE_SHIFT;
+ u64 end = md->phys_addr + size;
+ if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
+ md->type != EFI_BOOT_SERVICES_CODE &&
+ md->type != EFI_BOOT_SERVICES_DATA)
+ continue;
+ if (!md->virt_addr)
+ continue;
+ if (phys_addr >= md->phys_addr && phys_addr < end)
+ return end - phys_addr;
+ }
+ return -1;
+}
/*
* We can't ioremap data in EFI boot services RAM, because we've already mapped
@@ -261,6 +303,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
{SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab},
{SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
{UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
+ {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
{NULL_GUID, NULL, NULL},
};
diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c
new file mode 100644
index 0000000..ffa2144
--- /dev/null
+++ b/drivers/firmware/efi/esrt.c
@@ -0,0 +1,392 @@
+/*
+ * 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 utilties 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/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+typedef struct {
+ 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;
+} efi_system_resource_entry_t;
+
+/*
+ * _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.
+ */
+typedef struct {
+ u32 fw_resource_count;
+ u32 fw_resource_count_max;
+ u64 fw_resource_version;
+ efi_system_resource_entry_t entries[];
+} efi_system_resource_table_t;
+
+static efi_system_resource_table_t *esrt;
+
+struct esre_entry {
+ efi_system_resource_entry_t *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_unparse(&entry->esre->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->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 *esre_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 esre_ktype = {
+ .release = esre_release,
+ .sysfs_ops = &esre_attr_ops,
+ .default_attrs = esre_attrs,
+};
+
+static struct kobject *esrt_kobj;
+static struct kset *esrt_kset;
+
+static int esre_create_sysfs_entry(efi_system_resource_entry_t *esre)
+{
+ int rc;
+ struct esre_entry *entry;
+ char name[EFI_VARIABLE_GUID_LEN + 1];
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ efi_guid_unparse(&esre->fw_class, name);
+
+ entry->esre = esre;
+ entry->kobj.kset = esrt_kset;
+ rc = kobject_init_and_add(&entry->kobj, &esre_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,
+};
+
+/*
+ * ioremap the table, copy it to kmalloced pages, and unmap it.
+ */
+static int esrt_duplicate_pages(void)
+{
+ efi_system_resource_table_t *tmpesrt;
+ efi_system_resource_entry_t *entries;
+ size_t size, max;
+ int err = -EINVAL;
+
+ if (!esrt_table_exists())
+ return err;
+
+ max = efi_mem_max_reasonable_size(efi.esrt);
+ if (max < 0) {
+ pr_err("ESRT header is not in the memory map.\n");
+ return err;
+ }
+ size = sizeof(*esrt);
+
+ if (max < size) {
+ pr_err("ESRT header doen't fit on single memory map entry.\n");
+ return err;
+ }
+
+ tmpesrt = ioremap(efi.esrt, size);
+ if (!tmpesrt) {
+ pr_err("ioremap failed.\n");
+ return -ENOMEM;
+ }
+
+ if (tmpesrt->fw_resource_count > 0 && max - size < sizeof (*entries)) {
+ pr_err("ESRT memory map entry can only hold the header.\n");
+ goto err_iounmap;
+ }
+
+ /*
+ * 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_iounmap;
+ }
+
+ /*
+ * 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.
+ */
+ size += tmpesrt->fw_resource_count * sizeof (*entries);
+ if (max < size) {
+ pr_err("ESRT does not fit on single memory map entry.\n");
+ goto err_iounmap;
+ }
+
+ esrt = kmalloc(size, GFP_KERNEL);
+ if (!esrt) {
+ pr_err("kmalloc failed.\n");
+ err = -ENOMEM;
+ goto err_iounmap;
+ }
+
+ memcpy(esrt, tmpesrt, size);
+ err = 0;
+err_iounmap:
+ iounmap(tmpesrt);
+ return err;
+}
+
+static int register_entries(void)
+{
+ efi_system_resource_entry_t *entries = esrt->entries;
+ int i, rc;
+
+ if (!esrt_table_exists())
+ return 0;
+
+ for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
+ rc = esre_create_sysfs_entry(&entries[i]);
+ if (rc < 0) {
+ pr_err("EFI System Resource 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;
+
+ error = esrt_duplicate_pages();
+ if (error)
+ return error;
+
+ 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;
+
+ 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-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>");
+MODULE_DESCRIPTION("EFI System Resource Table support");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 0949f9c..5b663a7 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -562,6 +562,9 @@ void efi_native_runtime_setup(void);
#define UV_SYSTEM_TABLE_GUID \
EFI_GUID( 0x3b13a7d4, 0x633e, 0x11dd, 0x93, 0xec, 0xda, 0x25, 0x56, 0xd8, 0x95, 0x93 )
+#define EFI_SYSTEM_RESOURCE_TABLE_GUID \
+ EFI_GUID( 0xb122a263, 0x3661, 0x4f68, 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80 )
+
#define LINUX_EFI_CRASH_GUID \
EFI_GUID( 0xcfc8fc79, 0xbe2e, 0x4ddc, 0x97, 0xf0, 0x9f, 0x98, 0xbf, 0xe2, 0x98, 0xa0 )
@@ -819,6 +822,7 @@ extern struct efi {
unsigned long fw_vendor; /* fw_vendor */
unsigned long runtime; /* runtime table */
unsigned long config_table; /* config tables */
+ unsigned long esrt; /* EFI System Resource Table */
efi_get_time_t *get_time;
efi_set_time_t *set_time;
efi_get_wakeup_time_t *get_wakeup_time;
@@ -875,6 +879,7 @@ extern u64 efi_get_iobase (void);
extern u32 efi_mem_type (unsigned long phys_addr);
extern u64 efi_mem_attributes (unsigned long phys_addr);
extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size);
+extern u64 efi_mem_max_reasonable_size(u64 phys_addr);
extern int __init efi_uart_console_only (void);
extern void efi_initialize_iomem_resources(struct resource *code_resource,
struct resource *data_resource, struct resource *bss_resource);
@@ -882,6 +887,7 @@ extern void efi_get_time(struct timespec *now);
extern void efi_reserve_boot_services(void);
extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose);
extern struct efi_memory_map memmap;
+extern struct kobject *efi_kobj;
extern int efi_reboot_quirk_mode;
extern bool efi_poweroff_required(void);
--
2.1.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [RFC PATCH] Add esrt support.
[not found] ` <1418163514-22977-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
@ 2014-12-09 22:28 ` Peter Jones
[not found] ` <1418164082-23534-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
0 siblings, 1 reply; 18+ messages in thread
From: Peter Jones @ 2014-12-09 22:28 UTC (permalink / raw)
To: linux-efi-u79uwXL29TY76Z2rM5mHXA; +Cc: Peter Jones
Add sysfs files for EFI System Resource Table under
/sys/firmware/efi/esrt and for each EFI System Resource Entry under
entries/ as a subdir.
v2 with suggestions from bpetkov.
v3 with me remembering checkpatch.
Signed-off-by: Peter Jones <pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
drivers/firmware/efi/Makefile | 2 +-
drivers/firmware/efi/efi.c | 46 ++++-
drivers/firmware/efi/esrt.c | 393 ++++++++++++++++++++++++++++++++++++++++++
include/linux/efi.h | 6 +
4 files changed, 445 insertions(+), 2 deletions(-)
create mode 100644 drivers/firmware/efi/esrt.c
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
index aef6a95..0d61089 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -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
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 8590099..68002d8 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -38,6 +38,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);
@@ -63,7 +64,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;
/*
@@ -92,6 +93,8 @@ static ssize_t systab_show(struct kobject *kobj,
str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info);
if (efi.uga != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "UGA=0x%lx\n", efi.uga);
+ if (efi.esrt != EFI_INVALID_TABLE_ADDR)
+ str += sprintf(str, "ESRT=0x%lx\n", efi.esrt);
return str - buf;
}
@@ -220,6 +223,46 @@ err_put:
subsys_initcall(efisubsys_init);
+/*
+ * Given a physicall address, determine if it exists within an EFI Memory Map
+ * entry, and if so, how much of that map exists at a higher address. That
+ * is, if this is the address of something in an EFI map, what's the highest
+ * address at which it's likely to end.
+ */
+u64 efi_mem_max_reasonable_size(u64 phys_addr)
+{
+ struct efi_memory_map *map = efi.memmap;
+ void *p, *e;
+
+ if (!map)
+ return -1;
+ if (WARN_ON(!map->phys_map))
+ return -1;
+ if (WARN_ON(map->nr_map == 0) || WARN_ON(map->desc_size == 0))
+ return -1;
+
+ e = map->phys_map + map->nr_map * map->desc_size;
+ for (p = map->phys_map; p < e; p += map->desc_size) {
+ /*
+ * If a driver calls this after efi_free_boot_services,
+ * ->map will be NULL.
+ * So just always get our own virtual map on the CPU.
+ */
+ efi_memory_desc_t *md = phys_to_virt((phys_addr_t)p);
+ u64 size = md->num_pages << EFI_PAGE_SHIFT;
+ u64 end = md->phys_addr + size;
+
+ if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
+ md->type != EFI_BOOT_SERVICES_CODE &&
+ md->type != EFI_BOOT_SERVICES_DATA)
+ continue;
+ if (!md->virt_addr)
+ continue;
+ if (phys_addr >= md->phys_addr && phys_addr < end)
+ return end - phys_addr;
+ }
+ return -1;
+}
/*
* We can't ioremap data in EFI boot services RAM, because we've already mapped
@@ -261,6 +304,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
{SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab},
{SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
{UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
+ {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
{NULL_GUID, NULL, NULL},
};
diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c
new file mode 100644
index 0000000..71eb77c
--- /dev/null
+++ b/drivers/firmware/efi/esrt.c
@@ -0,0 +1,393 @@
+/*
+ * 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/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+struct {
+ 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;
+} efi_system_resource_entry;
+
+/*
+ * _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 {
+ u32 fw_resource_count;
+ u32 fw_resource_count_max;
+ u64 fw_resource_version;
+ struct efi_system_resource_entry entries[];
+} efi_system_resource_table;
+
+static struct efi_system_resource_table *esrt;
+
+struct esre_entry {
+ struct efi_system_resource_entry *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_unparse(&entry->esre->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->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 *esre_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 esre_ktype = {
+ .release = esre_release,
+ .sysfs_ops = &esre_attr_ops,
+ .default_attrs = esre_attrs,
+};
+
+static struct kobject *esrt_kobj;
+static struct kset *esrt_kset;
+
+static int esre_create_sysfs_entry(struct efi_system_resource_entry *esre)
+{
+ int rc;
+ struct esre_entry *entry;
+ char name[EFI_VARIABLE_GUID_LEN + 1];
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ efi_guid_unparse(&esre->fw_class, name);
+
+ entry->esre = esre;
+ entry->kobj.kset = esrt_kset;
+ rc = kobject_init_and_add(&entry->kobj, &esre_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,
+};
+
+/*
+ * ioremap the table, copy it to kmalloced pages, and unmap it.
+ */
+static int esrt_duplicate_pages(void)
+{
+ struct efi_system_resource_table *tmpesrt;
+ struct efi_system_resource_entry *entries;
+ size_t size, max;
+ int err = -EINVAL;
+
+ if (!esrt_table_exists())
+ return err;
+
+ max = efi_mem_max_reasonable_size(efi.esrt);
+ if (max < 0) {
+ pr_err("ESRT header is not in the memory map.\n");
+ return err;
+ }
+ size = sizeof(*esrt);
+
+ if (max < size) {
+ pr_err("ESRT header doen't fit on single memory map entry.\n");
+ return err;
+ }
+
+ tmpesrt = ioremap(efi.esrt, size);
+ if (!tmpesrt) {
+ pr_err("ioremap failed.\n");
+ return -ENOMEM;
+ }
+
+ if (tmpesrt->fw_resource_count > 0 && max - size < sizeof(*entries)) {
+ pr_err("ESRT memory map entry can only hold the header.\n");
+ goto err_iounmap;
+ }
+
+ /*
+ * 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_iounmap;
+ }
+
+ /*
+ * 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.
+ */
+ size += tmpesrt->fw_resource_count * sizeof(*entries);
+ if (max < size) {
+ pr_err("ESRT does not fit on single memory map entry.\n");
+ goto err_iounmap;
+ }
+
+ esrt = kmalloc(size, GFP_KERNEL);
+ if (!esrt) {
+ err = -ENOMEM;
+ goto err_iounmap;
+ }
+
+ memcpy(esrt, tmpesrt, size);
+ err = 0;
+err_iounmap:
+ iounmap(tmpesrt);
+ return err;
+}
+
+static int register_entries(void)
+{
+ struct efi_system_resource_entry *entries = esrt->entries;
+ int i, rc;
+
+ if (!esrt_table_exists())
+ return 0;
+
+ for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
+ rc = esre_create_sysfs_entry(&entries[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;
+
+ error = esrt_duplicate_pages();
+ if (error)
+ return error;
+
+ 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;
+
+ 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-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>");
+MODULE_DESCRIPTION("EFI System Resource Table support");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 0949f9c..5b663a7 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -562,6 +562,9 @@ void efi_native_runtime_setup(void);
#define UV_SYSTEM_TABLE_GUID \
EFI_GUID( 0x3b13a7d4, 0x633e, 0x11dd, 0x93, 0xec, 0xda, 0x25, 0x56, 0xd8, 0x95, 0x93 )
+#define EFI_SYSTEM_RESOURCE_TABLE_GUID \
+ EFI_GUID( 0xb122a263, 0x3661, 0x4f68, 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80 )
+
#define LINUX_EFI_CRASH_GUID \
EFI_GUID( 0xcfc8fc79, 0xbe2e, 0x4ddc, 0x97, 0xf0, 0x9f, 0x98, 0xbf, 0xe2, 0x98, 0xa0 )
@@ -819,6 +822,7 @@ extern struct efi {
unsigned long fw_vendor; /* fw_vendor */
unsigned long runtime; /* runtime table */
unsigned long config_table; /* config tables */
+ unsigned long esrt; /* EFI System Resource Table */
efi_get_time_t *get_time;
efi_set_time_t *set_time;
efi_get_wakeup_time_t *get_wakeup_time;
@@ -875,6 +879,7 @@ extern u64 efi_get_iobase (void);
extern u32 efi_mem_type (unsigned long phys_addr);
extern u64 efi_mem_attributes (unsigned long phys_addr);
extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size);
+extern u64 efi_mem_max_reasonable_size(u64 phys_addr);
extern int __init efi_uart_console_only (void);
extern void efi_initialize_iomem_resources(struct resource *code_resource,
struct resource *data_resource, struct resource *bss_resource);
@@ -882,6 +887,7 @@ extern void efi_get_time(struct timespec *now);
extern void efi_reserve_boot_services(void);
extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose);
extern struct efi_memory_map memmap;
+extern struct kobject *efi_kobj;
extern int efi_reboot_quirk_mode;
extern bool efi_poweroff_required(void);
--
2.1.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* RE: [RFC PATCH] Add esrt support.
[not found] ` <1418164082-23534-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
@ 2014-12-10 10:42 ` Parmeshwr_Prasad-DYMqY+WieiM
[not found] ` <EAEC0BCD44614F499E7CCE37C3B71F113793F9A460-6n1rj5y50trWnfa2oNyE5AVY21JRvFwKV6yJEvX+wlw@public.gmane.org>
2014-12-11 0:19 ` Neri, Ricardo
1 sibling, 1 reply; 18+ messages in thread
From: Parmeshwr_Prasad-DYMqY+WieiM @ 2014-12-10 10:42 UTC (permalink / raw)
To: pjones-H+wXaHxf7aLQT0dZR+AlfA, linux-efi-u79uwXL29TY76Z2rM5mHXA
Peter I have checked you patch with linux-3.18-rc2 got few compilation error.
drivers/firmware/efi/esrt.c:196:83: error: stray '##' in program
static struct kobj_attribute esrt_attr_##name = __ATTR(name, 0400,esrt_attr_##name##_show, NULL);
^
include/linux/sysfs.h:76:10: note: in definition of macro '__ATTR'
.show = _show, \
^
drivers/firmware/efi/esrt.c:203:3: error: 'esrt_attr_fw_resource_count' undeclared here (not in a function)
&esrt_attr_fw_resource_count.attr,
^
drivers/firmware/efi/esrt.c:204:3: error: 'esrt_attr_fw_resource_count_max' undeclared here (not in a function)
&esrt_attr_fw_resource_count_max.attr,
^
drivers/firmware/efi/esrt.c:205:3: error: 'esrt_attr_fw_resource_version' undeclared here (not in a function)
&esrt_attr_fw_resource_version.attr,
Is these are really error or I am making mistake.
I will be happy to test you patch.
-Param
-----Original Message-----
From: linux-efi-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org [mailto:linux-efi-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org] On Behalf Of Peter Jones
Sent: Wednesday, December 10, 2014 3:58 AM
To: linux-efi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: Peter Jones
Subject: [RFC PATCH] Add esrt support.
Add sysfs files for EFI System Resource Table under /sys/firmware/efi/esrt and for each EFI System Resource Entry under entries/ as a subdir.
v2 with suggestions from bpetkov.
v3 with me remembering checkpatch.
Signed-off-by: Peter Jones <pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
drivers/firmware/efi/Makefile | 2 +-
drivers/firmware/efi/efi.c | 46 ++++-
drivers/firmware/efi/esrt.c | 393 ++++++++++++++++++++++++++++++++++++++++++
include/linux/efi.h | 6 +
4 files changed, 445 insertions(+), 2 deletions(-) create mode 100644 drivers/firmware/efi/esrt.c
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index aef6a95..0d61089 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -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
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 8590099..68002d8 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -38,6 +38,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);
@@ -63,7 +64,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;
/*
@@ -92,6 +93,8 @@ static ssize_t systab_show(struct kobject *kobj,
str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info);
if (efi.uga != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "UGA=0x%lx\n", efi.uga);
+ if (efi.esrt != EFI_INVALID_TABLE_ADDR)
+ str += sprintf(str, "ESRT=0x%lx\n", efi.esrt);
return str - buf;
}
@@ -220,6 +223,46 @@ err_put:
subsys_initcall(efisubsys_init);
+/*
+ * Given a physicall address, determine if it exists within an EFI
+Memory Map
+ * entry, and if so, how much of that map exists at a higher address.
+That
+ * is, if this is the address of something in an EFI map, what's the
+highest
+ * address at which it's likely to end.
+ */
+u64 efi_mem_max_reasonable_size(u64 phys_addr) {
+ struct efi_memory_map *map = efi.memmap;
+ void *p, *e;
+
+ if (!map)
+ return -1;
+ if (WARN_ON(!map->phys_map))
+ return -1;
+ if (WARN_ON(map->nr_map == 0) || WARN_ON(map->desc_size == 0))
+ return -1;
+
+ e = map->phys_map + map->nr_map * map->desc_size;
+ for (p = map->phys_map; p < e; p += map->desc_size) {
+ /*
+ * If a driver calls this after efi_free_boot_services,
+ * ->map will be NULL.
+ * So just always get our own virtual map on the CPU.
+ */
+ efi_memory_desc_t *md = phys_to_virt((phys_addr_t)p);
+ u64 size = md->num_pages << EFI_PAGE_SHIFT;
+ u64 end = md->phys_addr + size;
+
+ if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
+ md->type != EFI_BOOT_SERVICES_CODE &&
+ md->type != EFI_BOOT_SERVICES_DATA)
+ continue;
+ if (!md->virt_addr)
+ continue;
+ if (phys_addr >= md->phys_addr && phys_addr < end)
+ return end - phys_addr;
+ }
+ return -1;
+}
/*
* We can't ioremap data in EFI boot services RAM, because we've already mapped @@ -261,6 +304,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
{SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab},
{SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
{UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
+ {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
{NULL_GUID, NULL, NULL},
};
diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c new file mode 100644 index 0000000..71eb77c
--- /dev/null
+++ b/drivers/firmware/efi/esrt.c
@@ -0,0 +1,393 @@
+/*
+ * 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/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+struct {
+ 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;
+} efi_system_resource_entry;
+
+/*
+ * _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 {
+ u32 fw_resource_count;
+ u32 fw_resource_count_max;
+ u64 fw_resource_version;
+ struct efi_system_resource_entry entries[]; }
+efi_system_resource_table;
+
+static struct efi_system_resource_table *esrt;
+
+struct esre_entry {
+ struct efi_system_resource_entry *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_unparse(&entry->esre->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->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 *esre_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 esre_ktype = {
+ .release = esre_release,
+ .sysfs_ops = &esre_attr_ops,
+ .default_attrs = esre_attrs,
+};
+
+static struct kobject *esrt_kobj;
+static struct kset *esrt_kset;
+
+static int esre_create_sysfs_entry(struct efi_system_resource_entry
+*esre) {
+ int rc;
+ struct esre_entry *entry;
+ char name[EFI_VARIABLE_GUID_LEN + 1];
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ efi_guid_unparse(&esre->fw_class, name);
+
+ entry->esre = esre;
+ entry->kobj.kset = esrt_kset;
+ rc = kobject_init_and_add(&entry->kobj, &esre_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,
+};
+
+/*
+ * ioremap the table, copy it to kmalloced pages, and unmap it.
+ */
+static int esrt_duplicate_pages(void)
+{
+ struct efi_system_resource_table *tmpesrt;
+ struct efi_system_resource_entry *entries;
+ size_t size, max;
+ int err = -EINVAL;
+
+ if (!esrt_table_exists())
+ return err;
+
+ max = efi_mem_max_reasonable_size(efi.esrt);
+ if (max < 0) {
+ pr_err("ESRT header is not in the memory map.\n");
+ return err;
+ }
+ size = sizeof(*esrt);
+
+ if (max < size) {
+ pr_err("ESRT header doen't fit on single memory map entry.\n");
+ return err;
+ }
+
+ tmpesrt = ioremap(efi.esrt, size);
+ if (!tmpesrt) {
+ pr_err("ioremap failed.\n");
+ return -ENOMEM;
+ }
+
+ if (tmpesrt->fw_resource_count > 0 && max - size < sizeof(*entries)) {
+ pr_err("ESRT memory map entry can only hold the header.\n");
+ goto err_iounmap;
+ }
+
+ /*
+ * 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_iounmap;
+ }
+
+ /*
+ * 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.
+ */
+ size += tmpesrt->fw_resource_count * sizeof(*entries);
+ if (max < size) {
+ pr_err("ESRT does not fit on single memory map entry.\n");
+ goto err_iounmap;
+ }
+
+ esrt = kmalloc(size, GFP_KERNEL);
+ if (!esrt) {
+ err = -ENOMEM;
+ goto err_iounmap;
+ }
+
+ memcpy(esrt, tmpesrt, size);
+ err = 0;
+err_iounmap:
+ iounmap(tmpesrt);
+ return err;
+}
+
+static int register_entries(void)
+{
+ struct efi_system_resource_entry *entries = esrt->entries;
+ int i, rc;
+
+ if (!esrt_table_exists())
+ return 0;
+
+ for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
+ rc = esre_create_sysfs_entry(&entries[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;
+
+ error = esrt_duplicate_pages();
+ if (error)
+ return error;
+
+ 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;
+
+ 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-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>");
+MODULE_DESCRIPTION("EFI System Resource Table support");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/efi.h b/include/linux/efi.h index 0949f9c..5b663a7 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -562,6 +562,9 @@ void efi_native_runtime_setup(void); #define UV_SYSTEM_TABLE_GUID \
EFI_GUID( 0x3b13a7d4, 0x633e, 0x11dd, 0x93, 0xec, 0xda, 0x25, 0x56, 0xd8, 0x95, 0x93 )
+#define EFI_SYSTEM_RESOURCE_TABLE_GUID \
+ EFI_GUID( 0xb122a263, 0x3661, 0x4f68, 0x99, 0x29, 0x78, 0xf8,
+0xb0, 0xd6, 0x21, 0x80 )
+
#define LINUX_EFI_CRASH_GUID \
EFI_GUID( 0xcfc8fc79, 0xbe2e, 0x4ddc, 0x97, 0xf0, 0x9f, 0x98, 0xbf, 0xe2, 0x98, 0xa0 )
@@ -819,6 +822,7 @@ extern struct efi {
unsigned long fw_vendor; /* fw_vendor */
unsigned long runtime; /* runtime table */
unsigned long config_table; /* config tables */
+ unsigned long esrt; /* EFI System Resource Table */
efi_get_time_t *get_time;
efi_set_time_t *set_time;
efi_get_wakeup_time_t *get_wakeup_time; @@ -875,6 +879,7 @@ extern u64 efi_get_iobase (void); extern u32 efi_mem_type (unsigned long phys_addr); extern u64 efi_mem_attributes (unsigned long phys_addr); extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size);
+extern u64 efi_mem_max_reasonable_size(u64 phys_addr);
extern int __init efi_uart_console_only (void); extern void efi_initialize_iomem_resources(struct resource *code_resource,
struct resource *data_resource, struct resource *bss_resource); @@ -882,6 +887,7 @@ extern void efi_get_time(struct timespec *now); extern void efi_reserve_boot_services(void); extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose); extern struct efi_memory_map memmap;
+extern struct kobject *efi_kobj;
extern int efi_reboot_quirk_mode;
extern bool efi_poweroff_required(void);
--
2.1.0
--
To unsubscribe from this list: send the line "unsubscribe linux-efi" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC PATCH] Add esrt support.
[not found] ` <EAEC0BCD44614F499E7CCE37C3B71F113793F9A460-6n1rj5y50trWnfa2oNyE5AVY21JRvFwKV6yJEvX+wlw@public.gmane.org>
@ 2014-12-10 14:41 ` Peter Jones
[not found] ` <20141210144148.GA24234-FS9oOTXHwv9t4tGkRPVz9tcb/sdHg95EuydrBrBl+0sAvxtiuMwx3w@public.gmane.org>
0 siblings, 1 reply; 18+ messages in thread
From: Peter Jones @ 2014-12-10 14:41 UTC (permalink / raw)
To: Parmeshwr_Prasad-8PEkshWhKlo; +Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA
On Wed, Dec 10, 2014 at 04:12:33PM +0530, Parmeshwr_Prasad-8PEkshWhKlo@public.gmane.org wrote:
> Peter I have checked you patch with linux-3.18-rc2 got few compilation error.
>
> drivers/firmware/efi/esrt.c:196:83: error: stray '##' in program
> static struct kobj_attribute esrt_attr_##name = __ATTR(name, 0400,esrt_attr_##name##_show, NULL);
What gcc version are you using? This builds without this problem with
gcc-4.9.2-1.fc21.x86_64 . But I don't offhand see any reason it
/shouldn't/ work, and we use this construct elsewhere in the kernel.
Is this the first error you get? I.e. you don't get the same error from
the esre_ versions of that same thing around line 123?
> ^
> include/linux/sysfs.h:76:10: note: in definition of macro '__ATTR'
> .show = _show, \
> ^
> drivers/firmware/efi/esrt.c:203:3: error: 'esrt_attr_fw_resource_count' undeclared here (not in a function)
> &esrt_attr_fw_resource_count.attr,
> ^
> drivers/firmware/efi/esrt.c:204:3: error: 'esrt_attr_fw_resource_count_max' undeclared here (not in a function)
> &esrt_attr_fw_resource_count_max.attr,
> ^
> drivers/firmware/efi/esrt.c:205:3: error: 'esrt_attr_fw_resource_version' undeclared here (not in a function)
> &esrt_attr_fw_resource_version.attr,
These are pretty clearly the result of the first thing going wrong.
>
> Is these are really error or I am making mistake.
>
> I will be happy to test you patch.
>
> -Param
>
> -----Original Message-----
> From: linux-efi-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org [mailto:linux-efi-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org] On Behalf Of Peter Jones
> Sent: Wednesday, December 10, 2014 3:58 AM
> To: linux-efi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Cc: Peter Jones
> Subject: [RFC PATCH] Add esrt support.
>
> Add sysfs files for EFI System Resource Table under /sys/firmware/efi/esrt and for each EFI System Resource Entry under entries/ as a subdir.
>
> v2 with suggestions from bpetkov.
> v3 with me remembering checkpatch.
>
> Signed-off-by: Peter Jones <pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> ---
> drivers/firmware/efi/Makefile | 2 +-
> drivers/firmware/efi/efi.c | 46 ++++-
> drivers/firmware/efi/esrt.c | 393 ++++++++++++++++++++++++++++++++++++++++++
> include/linux/efi.h | 6 +
> 4 files changed, 445 insertions(+), 2 deletions(-) create mode 100644 drivers/firmware/efi/esrt.c
>
> diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index aef6a95..0d61089 100644
> --- a/drivers/firmware/efi/Makefile
> +++ b/drivers/firmware/efi/Makefile
> @@ -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
> diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 8590099..68002d8 100644
> --- a/drivers/firmware/efi/efi.c
> +++ b/drivers/firmware/efi/efi.c
> @@ -38,6 +38,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);
>
> @@ -63,7 +64,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;
>
> /*
> @@ -92,6 +93,8 @@ static ssize_t systab_show(struct kobject *kobj,
> str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info);
> if (efi.uga != EFI_INVALID_TABLE_ADDR)
> str += sprintf(str, "UGA=0x%lx\n", efi.uga);
> + if (efi.esrt != EFI_INVALID_TABLE_ADDR)
> + str += sprintf(str, "ESRT=0x%lx\n", efi.esrt);
>
> return str - buf;
> }
> @@ -220,6 +223,46 @@ err_put:
>
> subsys_initcall(efisubsys_init);
>
> +/*
> + * Given a physicall address, determine if it exists within an EFI
> +Memory Map
> + * entry, and if so, how much of that map exists at a higher address.
> +That
> + * is, if this is the address of something in an EFI map, what's the
> +highest
> + * address at which it's likely to end.
> + */
> +u64 efi_mem_max_reasonable_size(u64 phys_addr) {
> + struct efi_memory_map *map = efi.memmap;
> + void *p, *e;
> +
> + if (!map)
> + return -1;
> + if (WARN_ON(!map->phys_map))
> + return -1;
> + if (WARN_ON(map->nr_map == 0) || WARN_ON(map->desc_size == 0))
> + return -1;
> +
> + e = map->phys_map + map->nr_map * map->desc_size;
> + for (p = map->phys_map; p < e; p += map->desc_size) {
> + /*
> + * If a driver calls this after efi_free_boot_services,
> + * ->map will be NULL.
> + * So just always get our own virtual map on the CPU.
> + */
> + efi_memory_desc_t *md = phys_to_virt((phys_addr_t)p);
> + u64 size = md->num_pages << EFI_PAGE_SHIFT;
> + u64 end = md->phys_addr + size;
> +
> + if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
> + md->type != EFI_BOOT_SERVICES_CODE &&
> + md->type != EFI_BOOT_SERVICES_DATA)
> + continue;
> + if (!md->virt_addr)
> + continue;
> + if (phys_addr >= md->phys_addr && phys_addr < end)
> + return end - phys_addr;
> + }
> + return -1;
> +}
>
> /*
> * We can't ioremap data in EFI boot services RAM, because we've already mapped @@ -261,6 +304,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
> {SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab},
> {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
> {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
> + {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
> {NULL_GUID, NULL, NULL},
> };
>
> diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c new file mode 100644 index 0000000..71eb77c
> --- /dev/null
> +++ b/drivers/firmware/efi/esrt.c
> @@ -0,0 +1,393 @@
> +/*
> + * 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/module.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +
> +struct {
> + 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;
> +} efi_system_resource_entry;
> +
> +/*
> + * _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 {
> + u32 fw_resource_count;
> + u32 fw_resource_count_max;
> + u64 fw_resource_version;
> + struct efi_system_resource_entry entries[]; }
> +efi_system_resource_table;
> +
> +static struct efi_system_resource_table *esrt;
> +
> +struct esre_entry {
> + struct efi_system_resource_entry *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_unparse(&entry->esre->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->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 *esre_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 esre_ktype = {
> + .release = esre_release,
> + .sysfs_ops = &esre_attr_ops,
> + .default_attrs = esre_attrs,
> +};
> +
> +static struct kobject *esrt_kobj;
> +static struct kset *esrt_kset;
> +
> +static int esre_create_sysfs_entry(struct efi_system_resource_entry
> +*esre) {
> + int rc;
> + struct esre_entry *entry;
> + char name[EFI_VARIABLE_GUID_LEN + 1];
> +
> + entry = kzalloc(sizeof(*entry), GFP_KERNEL);
> + if (!entry)
> + return -ENOMEM;
> +
> + efi_guid_unparse(&esre->fw_class, name);
> +
> + entry->esre = esre;
> + entry->kobj.kset = esrt_kset;
> + rc = kobject_init_and_add(&entry->kobj, &esre_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,
> +};
> +
> +/*
> + * ioremap the table, copy it to kmalloced pages, and unmap it.
> + */
> +static int esrt_duplicate_pages(void)
> +{
> + struct efi_system_resource_table *tmpesrt;
> + struct efi_system_resource_entry *entries;
> + size_t size, max;
> + int err = -EINVAL;
> +
> + if (!esrt_table_exists())
> + return err;
> +
> + max = efi_mem_max_reasonable_size(efi.esrt);
> + if (max < 0) {
> + pr_err("ESRT header is not in the memory map.\n");
> + return err;
> + }
> + size = sizeof(*esrt);
> +
> + if (max < size) {
> + pr_err("ESRT header doen't fit on single memory map entry.\n");
> + return err;
> + }
> +
> + tmpesrt = ioremap(efi.esrt, size);
> + if (!tmpesrt) {
> + pr_err("ioremap failed.\n");
> + return -ENOMEM;
> + }
> +
> + if (tmpesrt->fw_resource_count > 0 && max - size < sizeof(*entries)) {
> + pr_err("ESRT memory map entry can only hold the header.\n");
> + goto err_iounmap;
> + }
> +
> + /*
> + * 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_iounmap;
> + }
> +
> + /*
> + * 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.
> + */
> + size += tmpesrt->fw_resource_count * sizeof(*entries);
> + if (max < size) {
> + pr_err("ESRT does not fit on single memory map entry.\n");
> + goto err_iounmap;
> + }
> +
> + esrt = kmalloc(size, GFP_KERNEL);
> + if (!esrt) {
> + err = -ENOMEM;
> + goto err_iounmap;
> + }
> +
> + memcpy(esrt, tmpesrt, size);
> + err = 0;
> +err_iounmap:
> + iounmap(tmpesrt);
> + return err;
> +}
> +
> +static int register_entries(void)
> +{
> + struct efi_system_resource_entry *entries = esrt->entries;
> + int i, rc;
> +
> + if (!esrt_table_exists())
> + return 0;
> +
> + for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
> + rc = esre_create_sysfs_entry(&entries[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;
> +
> + error = esrt_duplicate_pages();
> + if (error)
> + return error;
> +
> + 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;
> +
> + 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-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>");
> +MODULE_DESCRIPTION("EFI System Resource Table support");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/efi.h b/include/linux/efi.h index 0949f9c..5b663a7 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -562,6 +562,9 @@ void efi_native_runtime_setup(void); #define UV_SYSTEM_TABLE_GUID \
> EFI_GUID( 0x3b13a7d4, 0x633e, 0x11dd, 0x93, 0xec, 0xda, 0x25, 0x56, 0xd8, 0x95, 0x93 )
>
> +#define EFI_SYSTEM_RESOURCE_TABLE_GUID \
> + EFI_GUID( 0xb122a263, 0x3661, 0x4f68, 0x99, 0x29, 0x78, 0xf8,
> +0xb0, 0xd6, 0x21, 0x80 )
> +
> #define LINUX_EFI_CRASH_GUID \
> EFI_GUID( 0xcfc8fc79, 0xbe2e, 0x4ddc, 0x97, 0xf0, 0x9f, 0x98, 0xbf, 0xe2, 0x98, 0xa0 )
>
> @@ -819,6 +822,7 @@ extern struct efi {
> unsigned long fw_vendor; /* fw_vendor */
> unsigned long runtime; /* runtime table */
> unsigned long config_table; /* config tables */
> + unsigned long esrt; /* EFI System Resource Table */
> efi_get_time_t *get_time;
> efi_set_time_t *set_time;
> efi_get_wakeup_time_t *get_wakeup_time; @@ -875,6 +879,7 @@ extern u64 efi_get_iobase (void); extern u32 efi_mem_type (unsigned long phys_addr); extern u64 efi_mem_attributes (unsigned long phys_addr); extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size);
> +extern u64 efi_mem_max_reasonable_size(u64 phys_addr);
> extern int __init efi_uart_console_only (void); extern void efi_initialize_iomem_resources(struct resource *code_resource,
> struct resource *data_resource, struct resource *bss_resource); @@ -882,6 +887,7 @@ extern void efi_get_time(struct timespec *now); extern void efi_reserve_boot_services(void); extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose); extern struct efi_memory_map memmap;
> +extern struct kobject *efi_kobj;
>
> extern int efi_reboot_quirk_mode;
> extern bool efi_poweroff_required(void);
> --
> 2.1.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-efi" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Peter
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC PATCH] Add esrt support.
[not found] ` <1418164082-23534-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2014-12-10 10:42 ` Parmeshwr_Prasad-DYMqY+WieiM
@ 2014-12-11 0:19 ` Neri, Ricardo
2014-12-11 14:08 ` Peter Jones
` (2 more replies)
1 sibling, 3 replies; 18+ messages in thread
From: Neri, Ricardo @ 2014-12-11 0:19 UTC (permalink / raw)
To: pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org
Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Parmeshwr_Prasad-DYMqY+WieiM@public.gmane.org
On Tue, 2014-12-09 at 17:28 -0500, Peter Jones wrote:
> Add sysfs files for EFI System Resource Table under
> /sys/firmware/efi/esrt and for each EFI System Resource Entry under
> entries/ as a subdir.
>
> v2 with suggestions from bpetkov.
> v3 with me remembering checkpatch.
Hi Peter,
I also see compilations errors after applying this patch to v3.18. I am
using gcc ver gcc version 4.9.2 (Ubuntu 4.9.2-0ubuntu1~14.04) and doing
make x86_64_defconfig && make:
drivers/firmware/efi/esrt.c:49:35: error: array type has incomplete
element type
struct efi_system_resource_entry entries[];
^
drivers/firmware/efi/esrt.c: In function ‘esre_fw_class_show’:
drivers/firmware/efi/esrt.c:104:31: error: dereferencing pointer to
incomplete type
efi_guid_unparse(&entry->esre->fw_class, str);
^
In file included from include/linux/byteorder/little_endian.h:4:0,
from ./arch/x86/include/uapi/asm/byteorder.h:4,
from include/asm-generic/bitops/le.h:5,
from ./arch/x86/include/asm/bitops.h:504,
from include/linux/bitops.h:36,
from include/linux/kernel.h:10,
from include/linux/list.h:8,
from include/linux/kobject.h:20,
from include/linux/device.h:17,
from drivers/firmware/efi/esrt.c:16:
drivers/firmware/efi/esrt.c: In function ‘esre_fw_type_show’:
drivers/firmware/efi/esrt.c:117:61: error: dereferencing pointer to
incomplete type
return sprintf(buf, fmt "\n", le##size##_to_cpu(entry->esre->name)); \
^
include/uapi/linux/byteorder/little_endian.h:33:51: note: in definition
of macro ‘__le32_to_cpu’
#define __le32_to_cpu(x) ((__force __u32)(__le32)(x))
^
drivers/firmware/efi/esrt.c:123:1: note: in expansion of macro
‘esre_attr_decl’
esre_attr_decl(fw_type, 32, "%u");
^
drivers/firmware/efi/esrt.c: In function ‘esre_fw_version_show’:
drivers/firmware/efi/esrt.c:117:61: error: dereferencing pointer to
incomplete type
return sprintf(buf, fmt "\n", le##size##_to_cpu(entry->esre->name)); \
^
include/uapi/linux/byteorder/little_endian.h:33:51: note: in definition
of macro ‘__le32_to_cpu’
#define __le32_to_cpu(x) ((__force __u32)(__le32)(x))
^
drivers/firmware/efi/esrt.c:124:1: note: in expansion of macro
‘esre_attr_decl’
esre_attr_decl(fw_version, 32, "%u");
^
drivers/firmware/efi/esrt.c: In function
‘esre_lowest_supported_fw_version_show’:
drivers/firmware/efi/esrt.c:117:61: error: dereferencing pointer to
incomplete type
return sprintf(buf, fmt "\n", le##size##_to_cpu(entry->esre->name)); \
^
include/uapi/linux/byteorder/little_endian.h:33:51: note: in definition
of macro ‘__le32_to_cpu’
#define __le32_to_cpu(x) ((__force __u32)(__le32)(x))
^
drivers/firmware/efi/esrt.c:125:1: note: in expansion of macro
‘esre_attr_decl’
esre_attr_decl(lowest_supported_fw_version, 32, "%u");
^
drivers/firmware/efi/esrt.c: In function ‘esre_capsule_flags_show’:
drivers/firmware/efi/esrt.c:117:61: error: dereferencing pointer to
incomplete type
return sprintf(buf, fmt "\n", le##size##_to_cpu(entry->esre->name)); \
^
include/uapi/linux/byteorder/little_endian.h:33:51: note: in definition
of macro ‘__le32_to_cpu’
#define __le32_to_cpu(x) ((__force __u32)(__le32)(x))
^
drivers/firmware/efi/esrt.c:126:1: note: in expansion of macro
‘esre_attr_decl’
esre_attr_decl(capsule_flags, 32, "0x%x");
^
drivers/firmware/efi/esrt.c: In function
‘esre_last_attempt_version_show’:
drivers/firmware/efi/esrt.c:117:61: error: dereferencing pointer to
incomplete type
return sprintf(buf, fmt "\n", le##size##_to_cpu(entry->esre->name)); \
^
include/uapi/linux/byteorder/little_endian.h:33:51: note: in definition
of macro ‘__le32_to_cpu’
#define __le32_to_cpu(x) ((__force __u32)(__le32)(x))
^
drivers/firmware/efi/esrt.c:127:1: note: in expansion of macro
‘esre_attr_decl’
esre_attr_decl(last_attempt_version, 32, "%u");
^
drivers/firmware/efi/esrt.c: In function
‘esre_last_attempt_status_show’:
drivers/firmware/efi/esrt.c:117:61: error: dereferencing pointer to
incomplete type
return sprintf(buf, fmt "\n", le##size##_to_cpu(entry->esre->name)); \
^
include/uapi/linux/byteorder/little_endian.h:33:51: note: in definition
of macro ‘__le32_to_cpu’
#define __le32_to_cpu(x) ((__force __u32)(__le32)(x))
^
drivers/firmware/efi/esrt.c:128:1: note: in expansion of macro
‘esre_attr_decl’
esre_attr_decl(last_attempt_status, 32, "%u");
^
drivers/firmware/efi/esrt.c: In function ‘esre_create_sysfs_entry’:
drivers/firmware/efi/esrt.c:168:24: error: dereferencing pointer to
incomplete type
efi_guid_unparse(&esre->fw_class, name);
^
In file included from include/linux/byteorder/little_endian.h:4:0,
from ./arch/x86/include/uapi/asm/byteorder.h:4,
from include/asm-generic/bitops/le.h:5,
from ./arch/x86/include/asm/bitops.h:504,
from include/linux/bitops.h:36,
from include/linux/kernel.h:10,
from include/linux/list.h:8,
from include/linux/kobject.h:20,
from include/linux/device.h:17,
from drivers/firmware/efi/esrt.c:16:
drivers/firmware/efi/esrt.c: In function ‘esrt_fw_resource_count_show’:
drivers/firmware/efi/esrt.c:188:54: error: dereferencing pointer to
incomplete type
return sprintf(buf, fmt "\n", le##size##_to_cpu(esrt->name)); \
^
include/uapi/linux/byteorder/little_endian.h:33:51: note: in definition
of macro ‘__le32_to_cpu’
#define __le32_to_cpu(x) ((__force __u32)(__le32)(x))
^
drivers/firmware/efi/esrt.c:194:1: note: in expansion of macro
‘esrt_attr_decl’
esrt_attr_decl(fw_resource_count, 32, "%u");
^
drivers/firmware/efi/esrt.c: In function
‘esrt_fw_resource_count_max_show’:
drivers/firmware/efi/esrt.c:188:54: error: dereferencing pointer to
incomplete type
return sprintf(buf, fmt "\n", le##size##_to_cpu(esrt->name)); \
^
include/uapi/linux/byteorder/little_endian.h:33:51: note: in definition
of macro ‘__le32_to_cpu’
#define __le32_to_cpu(x) ((__force __u32)(__le32)(x))
^
drivers/firmware/efi/esrt.c:195:1: note: in expansion of macro
‘esrt_attr_decl’
esrt_attr_decl(fw_resource_count_max, 32, "%u");
^
drivers/firmware/efi/esrt.c: In function
‘esrt_fw_resource_version_show’:
drivers/firmware/efi/esrt.c:188:54: error: dereferencing pointer to
incomplete type
return sprintf(buf, fmt "\n", le##size##_to_cpu(esrt->name)); \
^
include/uapi/linux/byteorder/little_endian.h:31:51: note: in definition
of macro ‘__le64_to_cpu’
#define __le64_to_cpu(x) ((__force __u64)(__le64)(x))
^
drivers/firmware/efi/esrt.c:196:1: note: in expansion of macro
‘esrt_attr_decl’
esrt_attr_decl(fw_resource_version, 64, "%llu");
^
drivers/firmware/efi/esrt.c: In function ‘esrt_duplicate_pages’:
drivers/firmware/efi/esrt.c:245:16: error: dereferencing pointer to
incomplete type
size = sizeof(*esrt);
^
drivers/firmware/efi/esrt.c:258:13: error: dereferencing pointer to
incomplete type
if (tmpesrt->fw_resource_count > 0 && max - size < sizeof(*entries)) {
^
drivers/firmware/efi/esrt.c:258:60: error: dereferencing pointer to
incomplete type
if (tmpesrt->fw_resource_count > 0 && max - size < sizeof(*entries)) {
^
drivers/firmware/efi/esrt.c:270:13: error: dereferencing pointer to
incomplete type
if (tmpesrt->fw_resource_count > 128) {
^
In file included from include/linux/kernel.h:13:0,
from include/linux/list.h:8,
from include/linux/kobject.h:20,
from include/linux/device.h:17,
from drivers/firmware/efi/esrt.c:16:
drivers/firmware/efi/esrt.c:272:17: error: dereferencing pointer to
incomplete type
tmpesrt->fw_resource_count);
^
include/linux/printk.h:240:33: note: in definition of macro ‘pr_err’
printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
^
drivers/firmware/efi/esrt.c:280:17: error: dereferencing pointer to
incomplete type
size += tmpesrt->fw_resource_count * sizeof(*entries);
^
drivers/firmware/efi/esrt.c:280:46: error: dereferencing pointer to
incomplete type
size += tmpesrt->fw_resource_count * sizeof(*entries);
^
drivers/firmware/efi/esrt.c: In function ‘register_entries’:
drivers/firmware/efi/esrt.c:301:50: error: dereferencing pointer to
incomplete type
struct efi_system_resource_entry *entries = esrt->entries;
^
In file included from include/linux/byteorder/little_endian.h:4:0,
from ./arch/x86/include/uapi/asm/byteorder.h:4,
from include/asm-generic/bitops/le.h:5,
from ./arch/x86/include/asm/bitops.h:504,
from include/linux/bitops.h:36,
from include/linux/kernel.h:10,
from include/linux/list.h:8,
from include/linux/kobject.h:20,
from include/linux/device.h:17,
from drivers/firmware/efi/esrt.c:16:
drivers/firmware/efi/esrt.c:307:34: error: dereferencing pointer to
incomplete type
for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
^
include/uapi/linux/byteorder/little_endian.h:33:51: note: in definition
of macro ‘__le32_to_cpu’
#define __le32_to_cpu(x) ((__force __u32)(__le32)(x))
^
drivers/firmware/efi/esrt.c:307:18: note: in expansion of macro
‘le32_to_cpu’
for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
^
drivers/firmware/efi/esrt.c:308:3: error: invalid use of undefined type
‘struct efi_system_resource_entry’
rc = esre_create_sysfs_entry(&entries[i]);
^
drivers/firmware/efi/esrt.c:308:40: error: dereferencing pointer to
incomplete type
rc = esre_create_sysfs_entry(&entries[i]);
^
drivers/firmware/efi/esrt.c: In function
‘esrt_fw_resource_version_show’:
drivers/firmware/efi/esrt.c:186:14: warning: control reaches end of
non-void function [-Wreturn-type]
struct kobj_attribute *attr, char *buf)\
^
drivers/firmware/efi/esrt.c:196:1: note: in expansion of macro
‘esrt_attr_decl’
esrt_attr_decl(fw_resource_version, 64, "%llu");
^
drivers/firmware/efi/esrt.c: In function
‘esrt_fw_resource_count_max_show’:
drivers/firmware/efi/esrt.c:186:14: warning: control reaches end of
non-void function [-Wreturn-type]
struct kobj_attribute *attr, char *buf)\
^
drivers/firmware/efi/esrt.c:195:1: note: in expansion of macro
‘esrt_attr_decl’
esrt_attr_decl(fw_resource_count_max, 32, "%u");
^
drivers/firmware/efi/esrt.c: In function ‘esrt_fw_resource_count_show’:
drivers/firmware/efi/esrt.c:186:14: warning: control reaches end of
non-void function [-Wreturn-type]
struct kobj_attribute *attr, char *buf)\
^
drivers/firmware/efi/esrt.c:194:1: note: in expansion of macro
‘esrt_attr_decl’
esrt_attr_decl(fw_resource_count, 32, "%u");
^
>
> Signed-off-by: Peter Jones <pjones@redhat.com>
> ---
> drivers/firmware/efi/Makefile | 2 +-
> drivers/firmware/efi/efi.c | 46 ++++-
> drivers/firmware/efi/esrt.c | 393 ++++++++++++++++++++++++++++++++++++++++++
> include/linux/efi.h | 6 +
> 4 files changed, 445 insertions(+), 2 deletions(-)
> create mode 100644 drivers/firmware/efi/esrt.c
>
> diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
> index aef6a95..0d61089 100644
> --- a/drivers/firmware/efi/Makefile
> +++ b/drivers/firmware/efi/Makefile
> @@ -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
> diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
> index 8590099..68002d8 100644
> --- a/drivers/firmware/efi/efi.c
> +++ b/drivers/firmware/efi/efi.c
> @@ -38,6 +38,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);
>
> @@ -63,7 +64,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;
>
> /*
> @@ -92,6 +93,8 @@ static ssize_t systab_show(struct kobject *kobj,
> str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info);
> if (efi.uga != EFI_INVALID_TABLE_ADDR)
> str += sprintf(str, "UGA=0x%lx\n", efi.uga);
> + if (efi.esrt != EFI_INVALID_TABLE_ADDR)
> + str += sprintf(str, "ESRT=0x%lx\n", efi.esrt);
>
> return str - buf;
> }
> @@ -220,6 +223,46 @@ err_put:
>
> subsys_initcall(efisubsys_init);
>
> +/*
> + * Given a physicall address, determine if it exists within an EFI Memory Map
> + * entry, and if so, how much of that map exists at a higher address. That
> + * is, if this is the address of something in an EFI map, what's the highest
> + * address at which it's likely to end.
> + */
> +u64 efi_mem_max_reasonable_size(u64 phys_addr)
> +{
> + struct efi_memory_map *map = efi.memmap;
> + void *p, *e;
> +
> + if (!map)
> + return -1;
> + if (WARN_ON(!map->phys_map))
> + return -1;
> + if (WARN_ON(map->nr_map == 0) || WARN_ON(map->desc_size == 0))
> + return -1;
> +
> + e = map->phys_map + map->nr_map * map->desc_size;
> + for (p = map->phys_map; p < e; p += map->desc_size) {
> + /*
> + * If a driver calls this after efi_free_boot_services,
> + * ->map will be NULL.
> + * So just always get our own virtual map on the CPU.
> + */
> + efi_memory_desc_t *md = phys_to_virt((phys_addr_t)p);
> + u64 size = md->num_pages << EFI_PAGE_SHIFT;
> + u64 end = md->phys_addr + size;
> +
> + if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
> + md->type != EFI_BOOT_SERVICES_CODE &&
> + md->type != EFI_BOOT_SERVICES_DATA)
> + continue;
> + if (!md->virt_addr)
> + continue;
> + if (phys_addr >= md->phys_addr && phys_addr < end)
> + return end - phys_addr;
> + }
> + return -1;
> +}
>
> /*
> * We can't ioremap data in EFI boot services RAM, because we've already mapped
> @@ -261,6 +304,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
> {SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab},
> {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
> {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
> + {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
> {NULL_GUID, NULL, NULL},
> };
>
> diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c
> new file mode 100644
> index 0000000..71eb77c
> --- /dev/null
> +++ b/drivers/firmware/efi/esrt.c
> @@ -0,0 +1,393 @@
> +/*
> + * 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/module.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +
> +struct {
> + 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;
> +} efi_system_resource_entry;
> +
> +/*
> + * _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 {
> + u32 fw_resource_count;
> + u32 fw_resource_count_max;
> + u64 fw_resource_version;
> + struct efi_system_resource_entry entries[];
> +} efi_system_resource_table;
> +
> +static struct efi_system_resource_table *esrt;
> +
> +struct esre_entry {
> + struct efi_system_resource_entry *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_unparse(&entry->esre->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->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 *esre_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 esre_ktype = {
> + .release = esre_release,
> + .sysfs_ops = &esre_attr_ops,
> + .default_attrs = esre_attrs,
> +};
> +
> +static struct kobject *esrt_kobj;
> +static struct kset *esrt_kset;
> +
> +static int esre_create_sysfs_entry(struct efi_system_resource_entry *esre)
> +{
> + int rc;
> + struct esre_entry *entry;
> + char name[EFI_VARIABLE_GUID_LEN + 1];
> +
> + entry = kzalloc(sizeof(*entry), GFP_KERNEL);
> + if (!entry)
> + return -ENOMEM;
> +
> + efi_guid_unparse(&esre->fw_class, name);
> +
> + entry->esre = esre;
> + entry->kobj.kset = esrt_kset;
> + rc = kobject_init_and_add(&entry->kobj, &esre_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,
> +};
> +
> +/*
> + * ioremap the table, copy it to kmalloced pages, and unmap it.
> + */
> +static int esrt_duplicate_pages(void)
> +{
> + struct efi_system_resource_table *tmpesrt;
> + struct efi_system_resource_entry *entries;
> + size_t size, max;
> + int err = -EINVAL;
> +
> + if (!esrt_table_exists())
> + return err;
> +
> + max = efi_mem_max_reasonable_size(efi.esrt);
> + if (max < 0) {
> + pr_err("ESRT header is not in the memory map.\n");
> + return err;
> + }
> + size = sizeof(*esrt);
> +
> + if (max < size) {
> + pr_err("ESRT header doen't fit on single memory map entry.\n");
> + return err;
> + }
> +
> + tmpesrt = ioremap(efi.esrt, size);
> + if (!tmpesrt) {
> + pr_err("ioremap failed.\n");
> + return -ENOMEM;
> + }
> +
> + if (tmpesrt->fw_resource_count > 0 && max - size < sizeof(*entries)) {
> + pr_err("ESRT memory map entry can only hold the header.\n");
> + goto err_iounmap;
> + }
> +
> + /*
> + * 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_iounmap;
> + }
> +
> + /*
> + * 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.
> + */
> + size += tmpesrt->fw_resource_count * sizeof(*entries);
> + if (max < size) {
> + pr_err("ESRT does not fit on single memory map entry.\n");
> + goto err_iounmap;
> + }
> +
> + esrt = kmalloc(size, GFP_KERNEL);
> + if (!esrt) {
> + err = -ENOMEM;
> + goto err_iounmap;
> + }
> +
> + memcpy(esrt, tmpesrt, size);
> + err = 0;
> +err_iounmap:
> + iounmap(tmpesrt);
> + return err;
> +}
> +
> +static int register_entries(void)
> +{
> + struct efi_system_resource_entry *entries = esrt->entries;
> + int i, rc;
> +
> + if (!esrt_table_exists())
> + return 0;
> +
> + for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
> + rc = esre_create_sysfs_entry(&entries[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;
> +
> + error = esrt_duplicate_pages();
> + if (error)
> + return error;
> +
> + 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;
> +
> + 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");
> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index 0949f9c..5b663a7 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -562,6 +562,9 @@ void efi_native_runtime_setup(void);
> #define UV_SYSTEM_TABLE_GUID \
> EFI_GUID( 0x3b13a7d4, 0x633e, 0x11dd, 0x93, 0xec, 0xda, 0x25, 0x56, 0xd8, 0x95, 0x93 )
>
> +#define EFI_SYSTEM_RESOURCE_TABLE_GUID \
> + EFI_GUID( 0xb122a263, 0x3661, 0x4f68, 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80 )
> +
> #define LINUX_EFI_CRASH_GUID \
> EFI_GUID( 0xcfc8fc79, 0xbe2e, 0x4ddc, 0x97, 0xf0, 0x9f, 0x98, 0xbf, 0xe2, 0x98, 0xa0 )
>
> @@ -819,6 +822,7 @@ extern struct efi {
> unsigned long fw_vendor; /* fw_vendor */
> unsigned long runtime; /* runtime table */
> unsigned long config_table; /* config tables */
> + unsigned long esrt; /* EFI System Resource Table */
> efi_get_time_t *get_time;
> efi_set_time_t *set_time;
> efi_get_wakeup_time_t *get_wakeup_time;
> @@ -875,6 +879,7 @@ extern u64 efi_get_iobase (void);
> extern u32 efi_mem_type (unsigned long phys_addr);
> extern u64 efi_mem_attributes (unsigned long phys_addr);
> extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size);
> +extern u64 efi_mem_max_reasonable_size(u64 phys_addr);
> extern int __init efi_uart_console_only (void);
> extern void efi_initialize_iomem_resources(struct resource *code_resource,
> struct resource *data_resource, struct resource *bss_resource);
> @@ -882,6 +887,7 @@ extern void efi_get_time(struct timespec *now);
> extern void efi_reserve_boot_services(void);
> extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose);
> extern struct efi_memory_map memmap;
> +extern struct kobject *efi_kobj;
>
> extern int efi_reboot_quirk_mode;
> extern bool efi_poweroff_required(void);
^ permalink raw reply [flat|nested] 18+ messages in thread
* RE: [RFC PATCH] Add esrt support.
[not found] ` <20141210144148.GA24234-FS9oOTXHwv9t4tGkRPVz9tcb/sdHg95EuydrBrBl+0sAvxtiuMwx3w@public.gmane.org>
@ 2014-12-11 9:32 ` Parmeshwr_Prasad-DYMqY+WieiM
0 siblings, 0 replies; 18+ messages in thread
From: Parmeshwr_Prasad-DYMqY+WieiM @ 2014-12-11 9:32 UTC (permalink / raw)
To: pjones-H+wXaHxf7aLQT0dZR+AlfA; +Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA
-----Original Message-----
From: Peter Jones [mailto:pjones@redhat.com]
Sent: Wednesday, December 10, 2014 8:12 PM
To: Prasad, Parmeshwr
Cc: linux-efi@vger.kernel.org
Subject: Re: [RFC PATCH] Add esrt support.
On Wed, Dec 10, 2014 at 04:12:33PM +0530, Parmeshwr_Prasad@dell.com wrote:
> Peter I have checked you patch with linux-3.18-rc2 got few compilation error.
>
> drivers/firmware/efi/esrt.c:196:83: error: stray '##' in program
> static struct kobj_attribute esrt_attr_##name = __ATTR(name,
> 0400,esrt_attr_##name##_show, NULL);
What gcc version are you using? This builds without this problem with
gcc-4.9.2-1.fc21.x86_64 . But I don't offhand see any reason it /shouldn't/ work, and we use this construct elsewhere in the kernel.
Is this the first error you get? I.e. you don't get the same error from the esre_ versions of that same thing around line 123?
My problem got solved, it was a windows problem :)
>
> ^
> include/linux/sysfs.h:76:10: note: in definition of macro '__ATTR'
> .show = _show, \
> ^
> drivers/firmware/efi/esrt.c:203:3: error: 'esrt_attr_fw_resource_count' undeclared here (not in a function)
> &esrt_attr_fw_resource_count.attr,
> ^
> drivers/firmware/efi/esrt.c:204:3: error: 'esrt_attr_fw_resource_count_max' undeclared here (not in a function)
> &esrt_attr_fw_resource_count_max.attr,
> ^
> drivers/firmware/efi/esrt.c:205:3: error: 'esrt_attr_fw_resource_version' undeclared here (not in a function)
> &esrt_attr_fw_resource_version.attr,
These are pretty clearly the result of the first thing going wrong.
>
> Is these are really error or I am making mistake.
>
> I will be happy to test you patch.
>
> -Param
>
> -----Original Message-----
> From: linux-efi-owner@vger.kernel.org
> [mailto:linux-efi-owner@vger.kernel.org] On Behalf Of Peter Jones
> Sent: Wednesday, December 10, 2014 3:58 AM
> To: linux-efi@vger.kernel.org
> Cc: Peter Jones
> Subject: [RFC PATCH] Add esrt support.
>
> Add sysfs files for EFI System Resource Table under /sys/firmware/efi/esrt and for each EFI System Resource Entry under entries/ as a subdir.
>
> v2 with suggestions from bpetkov.
> v3 with me remembering checkpatch.
>
> Signed-off-by: Peter Jones <pjones@redhat.com>
> ---
> drivers/firmware/efi/Makefile | 2 +-
> drivers/firmware/efi/efi.c | 46 ++++-
> drivers/firmware/efi/esrt.c | 393 ++++++++++++++++++++++++++++++++++++++++++
> include/linux/efi.h | 6 +
> 4 files changed, 445 insertions(+), 2 deletions(-) create mode
> 100644 drivers/firmware/efi/esrt.c
>
> diff --git a/drivers/firmware/efi/Makefile
> b/drivers/firmware/efi/Makefile index aef6a95..0d61089 100644
> --- a/drivers/firmware/efi/Makefile
> +++ b/drivers/firmware/efi/Makefile
> @@ -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
> diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
> index 8590099..68002d8 100644
> --- a/drivers/firmware/efi/efi.c
> +++ b/drivers/firmware/efi/efi.c
> @@ -38,6 +38,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);
>
> @@ -63,7 +64,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;
>
> /*
> @@ -92,6 +93,8 @@ static ssize_t systab_show(struct kobject *kobj,
> str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info);
> if (efi.uga != EFI_INVALID_TABLE_ADDR)
> str += sprintf(str, "UGA=0x%lx\n", efi.uga);
> + if (efi.esrt != EFI_INVALID_TABLE_ADDR)
> + str += sprintf(str, "ESRT=0x%lx\n", efi.esrt);
>
> return str - buf;
> }
> @@ -220,6 +223,46 @@ err_put:
>
> subsys_initcall(efisubsys_init);
>
> +/*
> + * Given a physicall address, determine if it exists within an EFI
> +Memory Map
> + * entry, and if so, how much of that map exists at a higher address.
> +That
> + * is, if this is the address of something in an EFI map, what's the
> +highest
> + * address at which it's likely to end.
> + */
> +u64 efi_mem_max_reasonable_size(u64 phys_addr) {
> + struct efi_memory_map *map = efi.memmap;
> + void *p, *e;
> +
> + if (!map)
> + return -1;
> + if (WARN_ON(!map->phys_map))
> + return -1;
> + if (WARN_ON(map->nr_map == 0) || WARN_ON(map->desc_size == 0))
> + return -1;
> +
> + e = map->phys_map + map->nr_map * map->desc_size;
> + for (p = map->phys_map; p < e; p += map->desc_size) {
> + /*
> + * If a driver calls this after efi_free_boot_services,
> + * ->map will be NULL.
> + * So just always get our own virtual map on the CPU.
> + */
> + efi_memory_desc_t *md = phys_to_virt((phys_addr_t)p);
> + u64 size = md->num_pages << EFI_PAGE_SHIFT;
> + u64 end = md->phys_addr + size;
> +
> + if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
> + md->type != EFI_BOOT_SERVICES_CODE &&
> + md->type != EFI_BOOT_SERVICES_DATA)
> + continue;
> + if (!md->virt_addr)
> + continue;
> + if (phys_addr >= md->phys_addr && phys_addr < end)
> + return end - phys_addr;
> + }
> + return -1;
> +}
>
> /*
> * We can't ioremap data in EFI boot services RAM, because we've already mapped @@ -261,6 +304,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
> {SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab},
> {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
> {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
> + {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
> {NULL_GUID, NULL, NULL},
> };
>
> diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c
> new file mode 100644 index 0000000..71eb77c
> --- /dev/null
> +++ b/drivers/firmware/efi/esrt.c
> @@ -0,0 +1,393 @@
> +/*
> + * 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/module.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +
> +struct {
> + 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;
> +} efi_system_resource_entry;
> +
> +/*
> + * _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 {
> + u32 fw_resource_count;
> + u32 fw_resource_count_max;
> + u64 fw_resource_version;
> + struct efi_system_resource_entry entries[]; }
> +efi_system_resource_table;
> +
> +static struct efi_system_resource_table *esrt;
> +
> +struct esre_entry {
> + struct efi_system_resource_entry *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_unparse(&entry->esre->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->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 *esre_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 esre_ktype = {
> + .release = esre_release,
> + .sysfs_ops = &esre_attr_ops,
> + .default_attrs = esre_attrs,
> +};
> +
> +static struct kobject *esrt_kobj;
> +static struct kset *esrt_kset;
> +
> +static int esre_create_sysfs_entry(struct efi_system_resource_entry
> +*esre) {
> + int rc;
> + struct esre_entry *entry;
> + char name[EFI_VARIABLE_GUID_LEN + 1];
> +
> + entry = kzalloc(sizeof(*entry), GFP_KERNEL);
> + if (!entry)
> + return -ENOMEM;
> +
> + efi_guid_unparse(&esre->fw_class, name);
> +
> + entry->esre = esre;
> + entry->kobj.kset = esrt_kset;
> + rc = kobject_init_and_add(&entry->kobj, &esre_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, };
> +
> +/*
> + * ioremap the table, copy it to kmalloced pages, and unmap it.
> + */
> +static int esrt_duplicate_pages(void) {
> + struct efi_system_resource_table *tmpesrt;
> + struct efi_system_resource_entry *entries;
> + size_t size, max;
> + int err = -EINVAL;
> +
> + if (!esrt_table_exists())
> + return err;
> +
> + max = efi_mem_max_reasonable_size(efi.esrt);
> + if (max < 0) {
> + pr_err("ESRT header is not in the memory map.\n");
> + return err;
> + }
> + size = sizeof(*esrt);
> +
> + if (max < size) {
> + pr_err("ESRT header doen't fit on single memory map entry.\n");
> + return err;
> + }
> +
> + tmpesrt = ioremap(efi.esrt, size);
> + if (!tmpesrt) {
> + pr_err("ioremap failed.\n");
> + return -ENOMEM;
> + }
> +
> + if (tmpesrt->fw_resource_count > 0 && max - size < sizeof(*entries)) {
> + pr_err("ESRT memory map entry can only hold the header.\n");
> + goto err_iounmap;
> + }
> +
> + /*
> + * 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_iounmap;
> + }
> +
> + /*
> + * 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.
> + */
> + size += tmpesrt->fw_resource_count * sizeof(*entries);
> + if (max < size) {
> + pr_err("ESRT does not fit on single memory map entry.\n");
> + goto err_iounmap;
> + }
> +
> + esrt = kmalloc(size, GFP_KERNEL);
> + if (!esrt) {
> + err = -ENOMEM;
> + goto err_iounmap;
> + }
> +
> + memcpy(esrt, tmpesrt, size);
> + err = 0;
> +err_iounmap:
> + iounmap(tmpesrt);
> + return err;
> +}
> +
> +static int register_entries(void)
> +{
> + struct efi_system_resource_entry *entries = esrt->entries;
> + int i, rc;
> +
> + if (!esrt_table_exists())
> + return 0;
> +
> + for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
> + rc = esre_create_sysfs_entry(&entries[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;
> +
> + error = esrt_duplicate_pages();
> + if (error)
> + return error;
> +
> + 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;
> +
> + 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");
> diff --git a/include/linux/efi.h b/include/linux/efi.h index
> 0949f9c..5b663a7 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -562,6 +562,9 @@ void efi_native_runtime_setup(void); #define UV_SYSTEM_TABLE_GUID \
> EFI_GUID( 0x3b13a7d4, 0x633e, 0x11dd, 0x93, 0xec, 0xda, 0x25,
> 0x56, 0xd8, 0x95, 0x93 )
>
> +#define EFI_SYSTEM_RESOURCE_TABLE_GUID \
> + EFI_GUID( 0xb122a263, 0x3661, 0x4f68, 0x99, 0x29, 0x78, 0xf8,
> +0xb0, 0xd6, 0x21, 0x80 )
> +
> #define LINUX_EFI_CRASH_GUID \
> EFI_GUID( 0xcfc8fc79, 0xbe2e, 0x4ddc, 0x97, 0xf0, 0x9f, 0x98,
> 0xbf, 0xe2, 0x98, 0xa0 )
>
> @@ -819,6 +822,7 @@ extern struct efi {
> unsigned long fw_vendor; /* fw_vendor */
> unsigned long runtime; /* runtime table */
> unsigned long config_table; /* config tables */
> + unsigned long esrt; /* EFI System Resource Table */
> efi_get_time_t *get_time;
> efi_set_time_t *set_time;
> efi_get_wakeup_time_t *get_wakeup_time; @@ -875,6 +879,7 @@
> extern u64 efi_get_iobase (void); extern u32 efi_mem_type (unsigned
> long phys_addr); extern u64 efi_mem_attributes (unsigned long
> phys_addr); extern u64 efi_mem_attribute (unsigned long phys_addr,
> unsigned long size);
> +extern u64 efi_mem_max_reasonable_size(u64 phys_addr);
> extern int __init efi_uart_console_only (void); extern void efi_initialize_iomem_resources(struct resource *code_resource,
> struct resource *data_resource, struct resource
> *bss_resource); @@ -882,6 +887,7 @@ extern void efi_get_time(struct
> timespec *now); extern void efi_reserve_boot_services(void); extern
> int efi_get_fdt_params(struct efi_fdt_params *params, int verbose);
> extern struct efi_memory_map memmap;
> +extern struct kobject *efi_kobj;
>
> extern int efi_reboot_quirk_mode;
> extern bool efi_poweroff_required(void);
> --
> 2.1.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-efi"
> in the body of a message to majordomo@vger.kernel.org More majordomo
> info at http://vger.kernel.org/majordomo-info.html
--
Peter
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC PATCH] Add esrt support.
2014-12-11 0:19 ` Neri, Ricardo
@ 2014-12-11 14:08 ` Peter Jones
2014-12-11 20:17 ` Peter Jones
2014-12-11 20:18 ` Peter Jones
2 siblings, 0 replies; 18+ messages in thread
From: Peter Jones @ 2014-12-11 14:08 UTC (permalink / raw)
To: Neri, Ricardo
Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Parmeshwr_Prasad-DYMqY+WieiM@public.gmane.org
On Thu, Dec 11, 2014 at 12:19:23AM +0000, Neri, Ricardo wrote:
> On Tue, 2014-12-09 at 17:28 -0500, Peter Jones wrote:
> > Add sysfs files for EFI System Resource Table under
> > /sys/firmware/efi/esrt and for each EFI System Resource Entry under
> > entries/ as a subdir.
> >
> > v2 with suggestions from bpetkov.
> > v3 with me remembering checkpatch.
>
> Hi Peter,
>
> I also see compilations errors after applying this patch to v3.18. I am
> using gcc ver gcc version 4.9.2 (Ubuntu 4.9.2-0ubuntu1~14.04) and doing
> make x86_64_defconfig && make:
Alright, then clearly something has gone wrong on my end. I'll start
with a clean tree and see if I can't reproduce these problems and fix
them.
Thanks.
--
Peter
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC PATCH] Add esrt support.
2014-12-11 0:19 ` Neri, Ricardo
2014-12-11 14:08 ` Peter Jones
@ 2014-12-11 20:17 ` Peter Jones
2014-12-11 20:18 ` Peter Jones
2 siblings, 0 replies; 18+ messages in thread
From: Peter Jones @ 2014-12-11 20:17 UTC (permalink / raw)
To: Neri, Ricardo
Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Parmeshwr_Prasad-DYMqY+WieiM@public.gmane.org
On Thu, Dec 11, 2014 at 12:19:23AM +0000, Neri, Ricardo wrote:
> On Tue, 2014-12-09 at 17:28 -0500, Peter Jones wrote:
> > Add sysfs files for EFI System Resource Table under
> > /sys/firmware/efi/esrt and for each EFI System Resource Entry under
> > entries/ as a subdir.
> >
> > v2 with suggestions from bpetkov.
> > v3 with me remembering checkpatch.
>
> Hi Peter,
>
> I also see compilations errors after applying this patch to v3.18. I am
> using gcc ver gcc version 4.9.2 (Ubuntu 4.9.2-0ubuntu1~14.04) and doing
> make x86_64_defconfig && make:
>
> drivers/firmware/efi/esrt.c:49:35: error: array type has incomplete
> element type
> struct efi_system_resource_entry entries[];
So this was a pretty straightforward error on my part, and I'm not sure
why I didn't notice it building, but when I've tried it in a clean tree
it shows up clear as day. My follow-up email here will have a fixed
version.
--
Peter
^ permalink raw reply [flat|nested] 18+ messages in thread
* [RFC PATCH] Add esrt support.
2014-12-11 0:19 ` Neri, Ricardo
2014-12-11 14:08 ` Peter Jones
2014-12-11 20:17 ` Peter Jones
@ 2014-12-11 20:18 ` Peter Jones
[not found] ` <1418329096-8675-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2 siblings, 1 reply; 18+ messages in thread
From: Peter Jones @ 2014-12-11 20:18 UTC (permalink / raw)
To: linux-efi-u79uwXL29TY76Z2rM5mHXA; +Cc: Peter Jones
Add sysfs files for EFI System Resource Table under
/sys/firmware/efi/esrt and for each EFI System Resource Entry under
entries/ as a subdir.
v2 with suggestions from bpetkov.
v3 with me remembering checkpatch.
v4 without me typing struct decls completely wrong somehow.
Signed-off-by: Peter Jones <pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
drivers/firmware/efi/Makefile | 2 +-
drivers/firmware/efi/efi.c | 46 ++++-
drivers/firmware/efi/esrt.c | 393 ++++++++++++++++++++++++++++++++++++++++++
include/linux/efi.h | 6 +
4 files changed, 445 insertions(+), 2 deletions(-)
create mode 100644 drivers/firmware/efi/esrt.c
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
index aef6a95..0d61089 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -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
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 8590099..68002d8 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -38,6 +38,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);
@@ -63,7 +64,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;
/*
@@ -92,6 +93,8 @@ static ssize_t systab_show(struct kobject *kobj,
str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info);
if (efi.uga != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "UGA=0x%lx\n", efi.uga);
+ if (efi.esrt != EFI_INVALID_TABLE_ADDR)
+ str += sprintf(str, "ESRT=0x%lx\n", efi.esrt);
return str - buf;
}
@@ -220,6 +223,46 @@ err_put:
subsys_initcall(efisubsys_init);
+/*
+ * Given a physicall address, determine if it exists within an EFI Memory Map
+ * entry, and if so, how much of that map exists at a higher address. That
+ * is, if this is the address of something in an EFI map, what's the highest
+ * address at which it's likely to end.
+ */
+u64 efi_mem_max_reasonable_size(u64 phys_addr)
+{
+ struct efi_memory_map *map = efi.memmap;
+ void *p, *e;
+
+ if (!map)
+ return -1;
+ if (WARN_ON(!map->phys_map))
+ return -1;
+ if (WARN_ON(map->nr_map == 0) || WARN_ON(map->desc_size == 0))
+ return -1;
+
+ e = map->phys_map + map->nr_map * map->desc_size;
+ for (p = map->phys_map; p < e; p += map->desc_size) {
+ /*
+ * If a driver calls this after efi_free_boot_services,
+ * ->map will be NULL.
+ * So just always get our own virtual map on the CPU.
+ */
+ efi_memory_desc_t *md = phys_to_virt((phys_addr_t)p);
+ u64 size = md->num_pages << EFI_PAGE_SHIFT;
+ u64 end = md->phys_addr + size;
+
+ if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
+ md->type != EFI_BOOT_SERVICES_CODE &&
+ md->type != EFI_BOOT_SERVICES_DATA)
+ continue;
+ if (!md->virt_addr)
+ continue;
+ if (phys_addr >= md->phys_addr && phys_addr < end)
+ return end - phys_addr;
+ }
+ return -1;
+}
/*
* We can't ioremap data in EFI boot services RAM, because we've already mapped
@@ -261,6 +304,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
{SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab},
{SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
{UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
+ {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
{NULL_GUID, NULL, NULL},
};
diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c
new file mode 100644
index 0000000..71eb77c
--- /dev/null
+++ b/drivers/firmware/efi/esrt.c
@@ -0,0 +1,393 @@
+/*
+ * 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/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+struct {
+ 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;
+} efi_system_resource_entry;
+
+/*
+ * _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 {
+ u32 fw_resource_count;
+ u32 fw_resource_count_max;
+ u64 fw_resource_version;
+ struct efi_system_resource_entry entries[];
+} efi_system_resource_table;
+
+static struct efi_system_resource_table *esrt;
+
+struct esre_entry {
+ struct efi_system_resource_entry *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_unparse(&entry->esre->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->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 *esre_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 esre_ktype = {
+ .release = esre_release,
+ .sysfs_ops = &esre_attr_ops,
+ .default_attrs = esre_attrs,
+};
+
+static struct kobject *esrt_kobj;
+static struct kset *esrt_kset;
+
+static int esre_create_sysfs_entry(struct efi_system_resource_entry *esre)
+{
+ int rc;
+ struct esre_entry *entry;
+ char name[EFI_VARIABLE_GUID_LEN + 1];
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ efi_guid_unparse(&esre->fw_class, name);
+
+ entry->esre = esre;
+ entry->kobj.kset = esrt_kset;
+ rc = kobject_init_and_add(&entry->kobj, &esre_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,
+};
+
+/*
+ * ioremap the table, copy it to kmalloced pages, and unmap it.
+ */
+static int esrt_duplicate_pages(void)
+{
+ struct efi_system_resource_table *tmpesrt;
+ struct efi_system_resource_entry *entries;
+ size_t size, max;
+ int err = -EINVAL;
+
+ if (!esrt_table_exists())
+ return err;
+
+ max = efi_mem_max_reasonable_size(efi.esrt);
+ if (max < 0) {
+ pr_err("ESRT header is not in the memory map.\n");
+ return err;
+ }
+ size = sizeof(*esrt);
+
+ if (max < size) {
+ pr_err("ESRT header doen't fit on single memory map entry.\n");
+ return err;
+ }
+
+ tmpesrt = ioremap(efi.esrt, size);
+ if (!tmpesrt) {
+ pr_err("ioremap failed.\n");
+ return -ENOMEM;
+ }
+
+ if (tmpesrt->fw_resource_count > 0 && max - size < sizeof(*entries)) {
+ pr_err("ESRT memory map entry can only hold the header.\n");
+ goto err_iounmap;
+ }
+
+ /*
+ * 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_iounmap;
+ }
+
+ /*
+ * 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.
+ */
+ size += tmpesrt->fw_resource_count * sizeof(*entries);
+ if (max < size) {
+ pr_err("ESRT does not fit on single memory map entry.\n");
+ goto err_iounmap;
+ }
+
+ esrt = kmalloc(size, GFP_KERNEL);
+ if (!esrt) {
+ err = -ENOMEM;
+ goto err_iounmap;
+ }
+
+ memcpy(esrt, tmpesrt, size);
+ err = 0;
+err_iounmap:
+ iounmap(tmpesrt);
+ return err;
+}
+
+static int register_entries(void)
+{
+ struct efi_system_resource_entry *entries = esrt->entries;
+ int i, rc;
+
+ if (!esrt_table_exists())
+ return 0;
+
+ for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
+ rc = esre_create_sysfs_entry(&entries[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;
+
+ error = esrt_duplicate_pages();
+ if (error)
+ return error;
+
+ 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;
+
+ 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-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>");
+MODULE_DESCRIPTION("EFI System Resource Table support");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 0949f9c..5b663a7 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -562,6 +562,9 @@ void efi_native_runtime_setup(void);
#define UV_SYSTEM_TABLE_GUID \
EFI_GUID( 0x3b13a7d4, 0x633e, 0x11dd, 0x93, 0xec, 0xda, 0x25, 0x56, 0xd8, 0x95, 0x93 )
+#define EFI_SYSTEM_RESOURCE_TABLE_GUID \
+ EFI_GUID( 0xb122a263, 0x3661, 0x4f68, 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80 )
+
#define LINUX_EFI_CRASH_GUID \
EFI_GUID( 0xcfc8fc79, 0xbe2e, 0x4ddc, 0x97, 0xf0, 0x9f, 0x98, 0xbf, 0xe2, 0x98, 0xa0 )
@@ -819,6 +822,7 @@ extern struct efi {
unsigned long fw_vendor; /* fw_vendor */
unsigned long runtime; /* runtime table */
unsigned long config_table; /* config tables */
+ unsigned long esrt; /* EFI System Resource Table */
efi_get_time_t *get_time;
efi_set_time_t *set_time;
efi_get_wakeup_time_t *get_wakeup_time;
@@ -875,6 +879,7 @@ extern u64 efi_get_iobase (void);
extern u32 efi_mem_type (unsigned long phys_addr);
extern u64 efi_mem_attributes (unsigned long phys_addr);
extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size);
+extern u64 efi_mem_max_reasonable_size(u64 phys_addr);
extern int __init efi_uart_console_only (void);
extern void efi_initialize_iomem_resources(struct resource *code_resource,
struct resource *data_resource, struct resource *bss_resource);
@@ -882,6 +887,7 @@ extern void efi_get_time(struct timespec *now);
extern void efi_reserve_boot_services(void);
extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose);
extern struct efi_memory_map memmap;
+extern struct kobject *efi_kobj;
extern int efi_reboot_quirk_mode;
extern bool efi_poweroff_required(void);
--
2.1.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [RFC PATCH] Add esrt support.
[not found] ` <1418329096-8675-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
@ 2014-12-11 20:21 ` Peter Jones
[not found] ` <20141211202119.GG17171-FS9oOTXHwv9t4tGkRPVz9tcb/sdHg95EuydrBrBl+0sAvxtiuMwx3w@public.gmane.org>
0 siblings, 1 reply; 18+ messages in thread
From: Peter Jones @ 2014-12-11 20:21 UTC (permalink / raw)
To: linux-efi-u79uwXL29TY76Z2rM5mHXA
On Thu, Dec 11, 2014 at 03:18:16PM -0500, Peter Jones wrote:
> Add sysfs files for EFI System Resource Table under
> /sys/firmware/efi/esrt and for each EFI System Resource Entry under
> entries/ as a subdir.
>
> v2 with suggestions from bpetkov.
> v3 with me remembering checkpatch.
> v4 without me typing struct decls completely wrong somehow.
Except it isn't. Apparently I'm really having a rough week with
invoking git correctly.
--
Peter
^ permalink raw reply [flat|nested] 18+ messages in thread
* [RFC PATCH] Add esrt support.
[not found] ` <20141211202119.GG17171-FS9oOTXHwv9t4tGkRPVz9tcb/sdHg95EuydrBrBl+0sAvxtiuMwx3w@public.gmane.org>
@ 2014-12-11 20:22 ` Peter Jones
[not found] ` <1418329324-8975-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
0 siblings, 1 reply; 18+ messages in thread
From: Peter Jones @ 2014-12-11 20:22 UTC (permalink / raw)
To: linux-efi-u79uwXL29TY76Z2rM5mHXA; +Cc: Peter Jones
Add sysfs files for EFI System Resource Table under
/sys/firmware/efi/esrt and for each EFI System Resource Entry under
entries/ as a subdir.
v2 with suggestions from bpetkov.
v3 with me remembering checkpatch.
v4 without me typing struct decls completely wrong somehow.
Signed-off-by: Peter Jones <pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
drivers/firmware/efi/Makefile | 2 +-
drivers/firmware/efi/efi.c | 46 ++++-
drivers/firmware/efi/esrt.c | 393 ++++++++++++++++++++++++++++++++++++++++++
include/linux/efi.h | 6 +
4 files changed, 445 insertions(+), 2 deletions(-)
create mode 100644 drivers/firmware/efi/esrt.c
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
index aef6a95..0d61089 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -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
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 8590099..68002d8 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -38,6 +38,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);
@@ -63,7 +64,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;
/*
@@ -92,6 +93,8 @@ static ssize_t systab_show(struct kobject *kobj,
str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info);
if (efi.uga != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "UGA=0x%lx\n", efi.uga);
+ if (efi.esrt != EFI_INVALID_TABLE_ADDR)
+ str += sprintf(str, "ESRT=0x%lx\n", efi.esrt);
return str - buf;
}
@@ -220,6 +223,46 @@ err_put:
subsys_initcall(efisubsys_init);
+/*
+ * Given a physicall address, determine if it exists within an EFI Memory Map
+ * entry, and if so, how much of that map exists at a higher address. That
+ * is, if this is the address of something in an EFI map, what's the highest
+ * address at which it's likely to end.
+ */
+u64 efi_mem_max_reasonable_size(u64 phys_addr)
+{
+ struct efi_memory_map *map = efi.memmap;
+ void *p, *e;
+
+ if (!map)
+ return -1;
+ if (WARN_ON(!map->phys_map))
+ return -1;
+ if (WARN_ON(map->nr_map == 0) || WARN_ON(map->desc_size == 0))
+ return -1;
+
+ e = map->phys_map + map->nr_map * map->desc_size;
+ for (p = map->phys_map; p < e; p += map->desc_size) {
+ /*
+ * If a driver calls this after efi_free_boot_services,
+ * ->map will be NULL.
+ * So just always get our own virtual map on the CPU.
+ */
+ efi_memory_desc_t *md = phys_to_virt((phys_addr_t)p);
+ u64 size = md->num_pages << EFI_PAGE_SHIFT;
+ u64 end = md->phys_addr + size;
+
+ if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
+ md->type != EFI_BOOT_SERVICES_CODE &&
+ md->type != EFI_BOOT_SERVICES_DATA)
+ continue;
+ if (!md->virt_addr)
+ continue;
+ if (phys_addr >= md->phys_addr && phys_addr < end)
+ return end - phys_addr;
+ }
+ return -1;
+}
/*
* We can't ioremap data in EFI boot services RAM, because we've already mapped
@@ -261,6 +304,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
{SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab},
{SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
{UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
+ {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
{NULL_GUID, NULL, NULL},
};
diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c
new file mode 100644
index 0000000..a58065b
--- /dev/null
+++ b/drivers/firmware/efi/esrt.c
@@ -0,0 +1,393 @@
+/*
+ * 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/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+struct efi_system_resource_entry {
+ 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;
+ struct efi_system_resource_entry entries[];
+};
+
+static struct efi_system_resource_table *esrt;
+
+struct esre_entry {
+ struct efi_system_resource_entry *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_unparse(&entry->esre->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->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 *esre_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 esre_ktype = {
+ .release = esre_release,
+ .sysfs_ops = &esre_attr_ops,
+ .default_attrs = esre_attrs,
+};
+
+static struct kobject *esrt_kobj;
+static struct kset *esrt_kset;
+
+static int esre_create_sysfs_entry(struct efi_system_resource_entry *esre)
+{
+ int rc;
+ struct esre_entry *entry;
+ char name[EFI_VARIABLE_GUID_LEN + 1];
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ efi_guid_unparse(&esre->fw_class, name);
+
+ entry->esre = esre;
+ entry->kobj.kset = esrt_kset;
+ rc = kobject_init_and_add(&entry->kobj, &esre_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,
+};
+
+/*
+ * ioremap the table, copy it to kmalloced pages, and unmap it.
+ */
+static int esrt_duplicate_pages(void)
+{
+ struct efi_system_resource_table *tmpesrt;
+ struct efi_system_resource_entry *entries;
+ size_t size, max;
+ int err = -EINVAL;
+
+ if (!esrt_table_exists())
+ return err;
+
+ max = efi_mem_max_reasonable_size(efi.esrt);
+ if (max < 0) {
+ pr_err("ESRT header is not in the memory map.\n");
+ return err;
+ }
+ size = sizeof(*esrt);
+
+ if (max < size) {
+ pr_err("ESRT header doen't fit on single memory map entry.\n");
+ return err;
+ }
+
+ tmpesrt = ioremap(efi.esrt, size);
+ if (!tmpesrt) {
+ pr_err("ioremap failed.\n");
+ return -ENOMEM;
+ }
+
+ if (tmpesrt->fw_resource_count > 0 && max - size < sizeof(*entries)) {
+ pr_err("ESRT memory map entry can only hold the header.\n");
+ goto err_iounmap;
+ }
+
+ /*
+ * 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_iounmap;
+ }
+
+ /*
+ * 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.
+ */
+ size += tmpesrt->fw_resource_count * sizeof(*entries);
+ if (max < size) {
+ pr_err("ESRT does not fit on single memory map entry.\n");
+ goto err_iounmap;
+ }
+
+ esrt = kmalloc(size, GFP_KERNEL);
+ if (!esrt) {
+ err = -ENOMEM;
+ goto err_iounmap;
+ }
+
+ memcpy(esrt, tmpesrt, size);
+ err = 0;
+err_iounmap:
+ iounmap(tmpesrt);
+ return err;
+}
+
+static int register_entries(void)
+{
+ struct efi_system_resource_entry *entries = esrt->entries;
+ int i, rc;
+
+ if (!esrt_table_exists())
+ return 0;
+
+ for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
+ rc = esre_create_sysfs_entry(&entries[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;
+
+ error = esrt_duplicate_pages();
+ if (error)
+ return error;
+
+ 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;
+
+ 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-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>");
+MODULE_DESCRIPTION("EFI System Resource Table support");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 0949f9c..5b663a7 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -562,6 +562,9 @@ void efi_native_runtime_setup(void);
#define UV_SYSTEM_TABLE_GUID \
EFI_GUID( 0x3b13a7d4, 0x633e, 0x11dd, 0x93, 0xec, 0xda, 0x25, 0x56, 0xd8, 0x95, 0x93 )
+#define EFI_SYSTEM_RESOURCE_TABLE_GUID \
+ EFI_GUID( 0xb122a263, 0x3661, 0x4f68, 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80 )
+
#define LINUX_EFI_CRASH_GUID \
EFI_GUID( 0xcfc8fc79, 0xbe2e, 0x4ddc, 0x97, 0xf0, 0x9f, 0x98, 0xbf, 0xe2, 0x98, 0xa0 )
@@ -819,6 +822,7 @@ extern struct efi {
unsigned long fw_vendor; /* fw_vendor */
unsigned long runtime; /* runtime table */
unsigned long config_table; /* config tables */
+ unsigned long esrt; /* EFI System Resource Table */
efi_get_time_t *get_time;
efi_set_time_t *set_time;
efi_get_wakeup_time_t *get_wakeup_time;
@@ -875,6 +879,7 @@ extern u64 efi_get_iobase (void);
extern u32 efi_mem_type (unsigned long phys_addr);
extern u64 efi_mem_attributes (unsigned long phys_addr);
extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size);
+extern u64 efi_mem_max_reasonable_size(u64 phys_addr);
extern int __init efi_uart_console_only (void);
extern void efi_initialize_iomem_resources(struct resource *code_resource,
struct resource *data_resource, struct resource *bss_resource);
@@ -882,6 +887,7 @@ extern void efi_get_time(struct timespec *now);
extern void efi_reserve_boot_services(void);
extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose);
extern struct efi_memory_map memmap;
+extern struct kobject *efi_kobj;
extern int efi_reboot_quirk_mode;
extern bool efi_poweroff_required(void);
--
2.1.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* RE: [RFC PATCH] Add esrt support.
[not found] ` <1418329324-8975-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
@ 2014-12-16 12:58 ` Parmeshwr_Prasad-DYMqY+WieiM
2014-12-19 15:27 ` Borislav Petkov
2015-01-05 13:51 ` Matt Fleming
2 siblings, 0 replies; 18+ messages in thread
From: Parmeshwr_Prasad-DYMqY+WieiM @ 2014-12-16 12:58 UTC (permalink / raw)
To: pjones-H+wXaHxf7aLQT0dZR+AlfA, linux-efi-u79uwXL29TY76Z2rM5mHXA
Peter, I don't see this patch working in my system.
+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;
+}
Validation is failing because of efi.esrt == EFI_INVALID_TABLE_ADDR
What is the dependency to make this patch work ? how it will get valid address for esrt ?
Or soothing else is required in this patch ?
-Param
-----Original Message-----
From: linux-efi-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org [mailto:linux-efi-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org] On Behalf Of Peter Jones
Sent: Friday, December 12, 2014 1:52 AM
To: linux-efi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: Peter Jones
Subject: [RFC PATCH] Add esrt support.
Add sysfs files for EFI System Resource Table under /sys/firmware/efi/esrt and for each EFI System Resource Entry under entries/ as a subdir.
v2 with suggestions from bpetkov.
v3 with me remembering checkpatch.
v4 without me typing struct decls completely wrong somehow.
Signed-off-by: Peter Jones <pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
drivers/firmware/efi/Makefile | 2 +-
drivers/firmware/efi/efi.c | 46 ++++-
drivers/firmware/efi/esrt.c | 393 ++++++++++++++++++++++++++++++++++++++++++
include/linux/efi.h | 6 +
4 files changed, 445 insertions(+), 2 deletions(-) create mode 100644 drivers/firmware/efi/esrt.c
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index aef6a95..0d61089 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -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
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 8590099..68002d8 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -38,6 +38,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);
@@ -63,7 +64,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;
/*
@@ -92,6 +93,8 @@ static ssize_t systab_show(struct kobject *kobj,
str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info);
if (efi.uga != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "UGA=0x%lx\n", efi.uga);
+ if (efi.esrt != EFI_INVALID_TABLE_ADDR)
+ str += sprintf(str, "ESRT=0x%lx\n", efi.esrt);
return str - buf;
}
@@ -220,6 +223,46 @@ err_put:
subsys_initcall(efisubsys_init);
+/*
+ * Given a physicall address, determine if it exists within an EFI
+Memory Map
+ * entry, and if so, how much of that map exists at a higher address.
+That
+ * is, if this is the address of something in an EFI map, what's the
+highest
+ * address at which it's likely to end.
+ */
+u64 efi_mem_max_reasonable_size(u64 phys_addr) {
+ struct efi_memory_map *map = efi.memmap;
+ void *p, *e;
+
+ if (!map)
+ return -1;
+ if (WARN_ON(!map->phys_map))
+ return -1;
+ if (WARN_ON(map->nr_map == 0) || WARN_ON(map->desc_size == 0))
+ return -1;
+
+ e = map->phys_map + map->nr_map * map->desc_size;
+ for (p = map->phys_map; p < e; p += map->desc_size) {
+ /*
+ * If a driver calls this after efi_free_boot_services,
+ * ->map will be NULL.
+ * So just always get our own virtual map on the CPU.
+ */
+ efi_memory_desc_t *md = phys_to_virt((phys_addr_t)p);
+ u64 size = md->num_pages << EFI_PAGE_SHIFT;
+ u64 end = md->phys_addr + size;
+
+ if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
+ md->type != EFI_BOOT_SERVICES_CODE &&
+ md->type != EFI_BOOT_SERVICES_DATA)
+ continue;
+ if (!md->virt_addr)
+ continue;
+ if (phys_addr >= md->phys_addr && phys_addr < end)
+ return end - phys_addr;
+ }
+ return -1;
+}
/*
* We can't ioremap data in EFI boot services RAM, because we've already mapped @@ -261,6 +304,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
{SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab},
{SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
{UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
+ {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
{NULL_GUID, NULL, NULL},
};
diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c new file mode 100644 index 0000000..a58065b
--- /dev/null
+++ b/drivers/firmware/efi/esrt.c
@@ -0,0 +1,393 @@
+/*
+ * 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/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+struct efi_system_resource_entry {
+ 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;
+ struct efi_system_resource_entry entries[]; };
+
+static struct efi_system_resource_table *esrt;
+
+struct esre_entry {
+ struct efi_system_resource_entry *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_unparse(&entry->esre->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->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 *esre_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 esre_ktype = {
+ .release = esre_release,
+ .sysfs_ops = &esre_attr_ops,
+ .default_attrs = esre_attrs,
+};
+
+static struct kobject *esrt_kobj;
+static struct kset *esrt_kset;
+
+static int esre_create_sysfs_entry(struct efi_system_resource_entry
+*esre) {
+ int rc;
+ struct esre_entry *entry;
+ char name[EFI_VARIABLE_GUID_LEN + 1];
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ efi_guid_unparse(&esre->fw_class, name);
+
+ entry->esre = esre;
+ entry->kobj.kset = esrt_kset;
+ rc = kobject_init_and_add(&entry->kobj, &esre_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,
+};
+
+/*
+ * ioremap the table, copy it to kmalloced pages, and unmap it.
+ */
+static int esrt_duplicate_pages(void)
+{
+ struct efi_system_resource_table *tmpesrt;
+ struct efi_system_resource_entry *entries;
+ size_t size, max;
+ int err = -EINVAL;
+
+ if (!esrt_table_exists())
+ return err;
+
+ max = efi_mem_max_reasonable_size(efi.esrt);
+ if (max < 0) {
+ pr_err("ESRT header is not in the memory map.\n");
+ return err;
+ }
+ size = sizeof(*esrt);
+
+ if (max < size) {
+ pr_err("ESRT header doen't fit on single memory map entry.\n");
+ return err;
+ }
+
+ tmpesrt = ioremap(efi.esrt, size);
+ if (!tmpesrt) {
+ pr_err("ioremap failed.\n");
+ return -ENOMEM;
+ }
+
+ if (tmpesrt->fw_resource_count > 0 && max - size < sizeof(*entries)) {
+ pr_err("ESRT memory map entry can only hold the header.\n");
+ goto err_iounmap;
+ }
+
+ /*
+ * 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_iounmap;
+ }
+
+ /*
+ * 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.
+ */
+ size += tmpesrt->fw_resource_count * sizeof(*entries);
+ if (max < size) {
+ pr_err("ESRT does not fit on single memory map entry.\n");
+ goto err_iounmap;
+ }
+
+ esrt = kmalloc(size, GFP_KERNEL);
+ if (!esrt) {
+ err = -ENOMEM;
+ goto err_iounmap;
+ }
+
+ memcpy(esrt, tmpesrt, size);
+ err = 0;
+err_iounmap:
+ iounmap(tmpesrt);
+ return err;
+}
+
+static int register_entries(void)
+{
+ struct efi_system_resource_entry *entries = esrt->entries;
+ int i, rc;
+
+ if (!esrt_table_exists())
+ return 0;
+
+ for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
+ rc = esre_create_sysfs_entry(&entries[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;
+
+ error = esrt_duplicate_pages();
+ if (error)
+ return error;
+
+ 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;
+
+ 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-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>");
+MODULE_DESCRIPTION("EFI System Resource Table support");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/efi.h b/include/linux/efi.h index 0949f9c..5b663a7 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -562,6 +562,9 @@ void efi_native_runtime_setup(void); #define UV_SYSTEM_TABLE_GUID \
EFI_GUID( 0x3b13a7d4, 0x633e, 0x11dd, 0x93, 0xec, 0xda, 0x25, 0x56, 0xd8, 0x95, 0x93 )
+#define EFI_SYSTEM_RESOURCE_TABLE_GUID \
+ EFI_GUID( 0xb122a263, 0x3661, 0x4f68, 0x99, 0x29, 0x78, 0xf8,
+0xb0, 0xd6, 0x21, 0x80 )
+
#define LINUX_EFI_CRASH_GUID \
EFI_GUID( 0xcfc8fc79, 0xbe2e, 0x4ddc, 0x97, 0xf0, 0x9f, 0x98, 0xbf, 0xe2, 0x98, 0xa0 )
@@ -819,6 +822,7 @@ extern struct efi {
unsigned long fw_vendor; /* fw_vendor */
unsigned long runtime; /* runtime table */
unsigned long config_table; /* config tables */
+ unsigned long esrt; /* EFI System Resource Table */
efi_get_time_t *get_time;
efi_set_time_t *set_time;
efi_get_wakeup_time_t *get_wakeup_time; @@ -875,6 +879,7 @@ extern u64 efi_get_iobase (void); extern u32 efi_mem_type (unsigned long phys_addr); extern u64 efi_mem_attributes (unsigned long phys_addr); extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size);
+extern u64 efi_mem_max_reasonable_size(u64 phys_addr);
extern int __init efi_uart_console_only (void); extern void efi_initialize_iomem_resources(struct resource *code_resource,
struct resource *data_resource, struct resource *bss_resource); @@ -882,6 +887,7 @@ extern void efi_get_time(struct timespec *now); extern void efi_reserve_boot_services(void); extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose); extern struct efi_memory_map memmap;
+extern struct kobject *efi_kobj;
extern int efi_reboot_quirk_mode;
extern bool efi_poweroff_required(void);
--
2.1.0
--
To unsubscribe from this list: send the line "unsubscribe linux-efi" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC PATCH] Add esrt support.
[not found] ` <1418329324-8975-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2014-12-16 12:58 ` Parmeshwr_Prasad-DYMqY+WieiM
@ 2014-12-19 15:27 ` Borislav Petkov
2015-01-05 13:51 ` Matt Fleming
2 siblings, 0 replies; 18+ messages in thread
From: Borislav Petkov @ 2014-12-19 15:27 UTC (permalink / raw)
To: Peter Jones; +Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA
On Thu, Dec 11, 2014 at 03:22:04PM -0500, Peter Jones wrote:
> Add sysfs files for EFI System Resource Table under
> /sys/firmware/efi/esrt and for each EFI System Resource Entry under
> entries/ as a subdir.
>
> v2 with suggestions from bpetkov.
> v3 with me remembering checkpatch.
> v4 without me typing struct decls completely wrong somehow.
>
> Signed-off-by: Peter Jones <pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Looks good, just minor nitpicks and some big-picture questions:
> ---
> drivers/firmware/efi/Makefile | 2 +-
> drivers/firmware/efi/efi.c | 46 ++++-
> drivers/firmware/efi/esrt.c | 393 ++++++++++++++++++++++++++++++++++++++++++
> include/linux/efi.h | 6 +
> 4 files changed, 445 insertions(+), 2 deletions(-)
> create mode 100644 drivers/firmware/efi/esrt.c
>
> diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
> index aef6a95..0d61089 100644
> --- a/drivers/firmware/efi/Makefile
> +++ b/drivers/firmware/efi/Makefile
> @@ -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
Ok, so this enables it by default on CONFIG_EFI.
Since this is an addition to the spec and there are UEFIs without it,
maybe we should make it a default y Kconfig option...
Distros will enable it by default but we might still want to be able to
turn it off, if not required.
> obj-$(CONFIG_EFI_VARS) += efivars.o
> obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o
> obj-$(CONFIG_UEFI_CPER) += cper.o
> diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
> index 8590099..68002d8 100644
> --- a/drivers/firmware/efi/efi.c
> +++ b/drivers/firmware/efi/efi.c
> @@ -38,6 +38,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);
>
> @@ -63,7 +64,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;
>
> /*
> @@ -92,6 +93,8 @@ static ssize_t systab_show(struct kobject *kobj,
> str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info);
> if (efi.uga != EFI_INVALID_TABLE_ADDR)
> str += sprintf(str, "UGA=0x%lx\n", efi.uga);
> + if (efi.esrt != EFI_INVALID_TABLE_ADDR)
> + str += sprintf(str, "ESRT=0x%lx\n", efi.esrt);
>
> return str - buf;
> }
> @@ -220,6 +223,46 @@ err_put:
>
> subsys_initcall(efisubsys_init);
>
> +/*
> + * Given a physicall address, determine if it exists within an EFI Memory Map
> + * entry, and if so, how much of that map exists at a higher address. That
> + * is, if this is the address of something in an EFI map, what's the highest
> + * address at which it's likely to end.
> + */
> +u64 efi_mem_max_reasonable_size(u64 phys_addr)
Ok, I'm reading your other explanation in the reply to my review and I
see what you mean. How about:
efi_sanity_check_table_region()
or so? A negative value means the table region is insane. :)
> +{
> + struct efi_memory_map *map = efi.memmap;
> + void *p, *e;
> +
> + if (!map)
> + return -1;
> + if (WARN_ON(!map->phys_map))
> + return -1;
> + if (WARN_ON(map->nr_map == 0) || WARN_ON(map->desc_size == 0))
> + return -1;
Pasting here the previous discussion:
>> If this is going to be called pretty often maybe use WARN_ON_ONCE().
>> Also, you probably want to return a negative value here so that caller
>> can stop further processing. I mean, it does so now too but a negative
>> value makes it more explicit.
>
> Yeah, the negative number is a good point. Right now I think, if I make
> it stop iterating when that happens, then the WARN_ON won't be triggered
> more than once per piece of code using it. Right now that's once piece
> of code. I think even if it becomes more, that's probably the right thing,
> because it indicates the calling code has done something wrong with the
> data in the first place.
Just be careful so that this doesn't start spewing too much and filling
up logs. If you want to be able to debug this, you probably should turn
those checks in normal pr_warning() calls with the required information
needed for debugging. It'll be a lot less output than the WARN_ON splats.
> + e = map->phys_map + map->nr_map * map->desc_size;
> + for (p = map->phys_map; p < e; p += map->desc_size) {
> + /*
> + * If a driver calls this after efi_free_boot_services,
> + * ->map will be NULL.
> + * So just always get our own virtual map on the CPU.
> + */
> + efi_memory_desc_t *md = phys_to_virt((phys_addr_t)p);
> + u64 size = md->num_pages << EFI_PAGE_SHIFT;
> + u64 end = md->phys_addr + size;
> +
> + if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
> + md->type != EFI_BOOT_SERVICES_CODE &&
> + md->type != EFI_BOOT_SERVICES_DATA)
> + continue;
> + if (!md->virt_addr)
> + continue;
> + if (phys_addr >= md->phys_addr && phys_addr < end)
> + return end - phys_addr;
> + }
> + return -1;
> +}
>
> /*
> * We can't ioremap data in EFI boot services RAM, because we've already mapped
> @@ -261,6 +304,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
> {SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab},
> {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
> {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
> + {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
> {NULL_GUID, NULL, NULL},
> };
>
> diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c
> new file mode 100644
> index 0000000..a58065b
> --- /dev/null
> +++ b/drivers/firmware/efi/esrt.c
> @@ -0,0 +1,393 @@
> +/*
> + * 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.
Btw, another stupid question: are we going to use this interface to
upload capsules through too or how are we going to do UpdateCapsule()?
Or are the different subsystems/drivers going to provide their own
methods to upload capsules and we'll have a generic UEFI mechanism
for that which will do UpdateCapsule() in the end, similar to what
request_firmware* does nowadays.
You said something about preparing an UEFI binary to do the fw update on
the next reboot but have we thought about updating fw during runtime,
the way we do microcode updates?
Or this is not the use case and we're talking only about updating the
UEFI image itself?
> + *
> + * Data is currently found below /sys/firmware/efi/esrt/...
> + */
> +#define pr_fmt(fmt) "esrt: " fmt
...
> +/*
> + * ioremap the table, copy it to kmalloced pages, and unmap it.
> + */
> +static int esrt_duplicate_pages(void)
> +{
> + struct efi_system_resource_table *tmpesrt;
> + struct efi_system_resource_entry *entries;
> + size_t size, max;
> + int err = -EINVAL;
> +
> + if (!esrt_table_exists())
> + return err;
> +
> + max = efi_mem_max_reasonable_size(efi.esrt);
> + if (max < 0) {
> + pr_err("ESRT header is not in the memory map.\n");
> + return err;
> + }
> + size = sizeof(*esrt);
> +
> + if (max < size) {
> + pr_err("ESRT header doen't fit on single memory map entry.\n");
> + return err;
> + }
> +
> + tmpesrt = ioremap(efi.esrt, size);
> + if (!tmpesrt) {
> + pr_err("ioremap failed.\n");
> + return -ENOMEM;
> + }
> +
> + if (tmpesrt->fw_resource_count > 0 && max - size < sizeof(*entries)) {
> + pr_err("ESRT memory map entry can only hold the header.\n");
> + goto err_iounmap;
> + }
> +
> + /*
> + * 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) {
I guess we want that magic number 128 as a macro along with the comment
somewhere towards the beginning of this file. Something like
#define ERST_MAX_RESOURCES
or so.
> + pr_err("ESRT says fw_resource_count has very large value %d.\n",
"Resource count overflow" ?
> + tmpesrt->fw_resource_count);
> + goto err_iounmap;
> + }
> +
> + /*
> + * 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.
> + */
> + size += tmpesrt->fw_resource_count * sizeof(*entries);
> + if (max < size) {
> + pr_err("ESRT does not fit on single memory map entry.\n");
> + goto err_iounmap;
> + }
> +
> + esrt = kmalloc(size, GFP_KERNEL);
> + if (!esrt) {
> + err = -ENOMEM;
> + goto err_iounmap;
> + }
> +
> + memcpy(esrt, tmpesrt, size);
> + err = 0;
> +err_iounmap:
> + iounmap(tmpesrt);
> + return err;
> +}
> +
> +static int register_entries(void)
> +{
> + struct efi_system_resource_entry *entries = esrt->entries;
> + int i, rc;
> +
> + if (!esrt_table_exists())
> + return 0;
> +
> + for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
> + rc = esre_create_sysfs_entry(&entries[i]);
> + if (rc < 0) {
> + pr_err("ESRT entry creation failed with error %d.\n",
> + rc);
No need for the linebreak here, just let it stick outta 80 cols.
> + 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;
> +
> + error = esrt_duplicate_pages();
> + if (error)
> + return error;
> +
> + 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);
No need to break the line.
> + 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;
> +
> + pr_debug("esrt-sysfs: loaded.\n");
> +
> + return 0;
newline here please.
> +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;
> +}
--
Regards/Gruss,
Boris.
Sent from a fat crate under my desk. Formatting is fine.
--
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC PATCH] Add esrt support.
[not found] ` <1418329324-8975-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2014-12-16 12:58 ` Parmeshwr_Prasad-DYMqY+WieiM
2014-12-19 15:27 ` Borislav Petkov
@ 2015-01-05 13:51 ` Matt Fleming
[not found] ` <20150105135113.GA3163-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
2 siblings, 1 reply; 18+ messages in thread
From: Matt Fleming @ 2015-01-05 13:51 UTC (permalink / raw)
To: Peter Jones; +Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA
On Thu, 11 Dec, at 03:22:04PM, Peter Jones wrote:
> Add sysfs files for EFI System Resource Table under
> /sys/firmware/efi/esrt and for each EFI System Resource Entry under
> entries/ as a subdir.
>
> v2 with suggestions from bpetkov.
> v3 with me remembering checkpatch.
> v4 without me typing struct decls completely wrong somehow.
>
> Signed-off-by: Peter Jones <pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> ---
> drivers/firmware/efi/Makefile | 2 +-
> drivers/firmware/efi/efi.c | 46 ++++-
> drivers/firmware/efi/esrt.c | 393 ++++++++++++++++++++++++++++++++++++++++++
> include/linux/efi.h | 6 +
> 4 files changed, 445 insertions(+), 2 deletions(-)
> create mode 100644 drivers/firmware/efi/esrt.c
When this patch moves out of RFC I'd ideally like to see a much more
expansive commit message.
Why do we want this functionality, what's it going to be used for? What
does the patch actaully do? Where can we go looking for more
information about ERST? You'll probably end up copying and pasting
things from the top of erst.c, but that's fine.
> @@ -220,6 +223,46 @@ err_put:
>
> subsys_initcall(efisubsys_init);
>
> +/*
> + * Given a physicall address, determine if it exists within an EFI Memory Map
> + * entry, and if so, how much of that map exists at a higher address. That
> + * is, if this is the address of something in an EFI map, what's the highest
> + * address at which it's likely to end.
> + */
> +u64 efi_mem_max_reasonable_size(u64 phys_addr)
Confused. I think I know what you're saying here, but I'm also doing a
fair bit of a guesswork.
Instead, how about,
extern struct efi_memory_desc_t *efi_mem_desc_lookup(u64 phys_addr);
/*
* Return the end physical address of a EFI memory map region
*/
static inline u64 efi_mem_desc_end(struct efi_memory_desc_t *md)
{
u64 size = md->num_pages << EFI_PAGE_SHIFT;
return md->phys_addr + size;
}
The caller can then do,
md = efi_mem_desc_lookup(phys_addr)
if (!md)
return -1;
bytes = efi_mem_desc_end(md) - phys_addr;
ioremap(phys_addr, bytes);
I think leaving this "remaining bytes" logic in the ioremap() caller is
the most robust solution because it's a quirky thing to want to do,
whereas the other functions (efi_mem_desc_lookup() and
efi_mem_desc_end()) are more general.
Thoughts?
[...]
> +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;
> +}
Minor detail: this should be using 'bool'.
--
Matt Fleming, Intel Open Source Technology Center
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC PATCH] Add esrt support.
[not found] ` <20150105135113.GA3163-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
@ 2015-01-05 15:10 ` Peter Jones
0 siblings, 0 replies; 18+ messages in thread
From: Peter Jones @ 2015-01-05 15:10 UTC (permalink / raw)
To: Matt Fleming; +Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA
On Mon, Jan 05, 2015 at 01:51:13PM +0000, Matt Fleming wrote:
> On Thu, 11 Dec, at 03:22:04PM, Peter Jones wrote:
> > Add sysfs files for EFI System Resource Table under
> > /sys/firmware/efi/esrt and for each EFI System Resource Entry under
> > entries/ as a subdir.
> >
> > v2 with suggestions from bpetkov.
> > v3 with me remembering checkpatch.
> > v4 without me typing struct decls completely wrong somehow.
> >
> > Signed-off-by: Peter Jones <pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> > ---
> > drivers/firmware/efi/Makefile | 2 +-
> > drivers/firmware/efi/efi.c | 46 ++++-
> > drivers/firmware/efi/esrt.c | 393 ++++++++++++++++++++++++++++++++++++++++++
> > include/linux/efi.h | 6 +
> > 4 files changed, 445 insertions(+), 2 deletions(-)
> > create mode 100644 drivers/firmware/efi/esrt.c
>
> When this patch moves out of RFC I'd ideally like to see a much more
> expansive commit message.
>
> Why do we want this functionality, what's it going to be used for? What
> does the patch actaully do? Where can we go looking for more
> information about ERST? You'll probably end up copying and pasting
> things from the top of erst.c, but that's fine.
Yeah, fair enough.
>
> > @@ -220,6 +223,46 @@ err_put:
> >
> > subsys_initcall(efisubsys_init);
> >
> > +/*
> > + * Given a physicall address, determine if it exists within an EFI Memory Map
> > + * entry, and if so, how much of that map exists at a higher address. That
> > + * is, if this is the address of something in an EFI map, what's the highest
> > + * address at which it's likely to end.
> > + */
> > +u64 efi_mem_max_reasonable_size(u64 phys_addr)
>
> Confused. I think I know what you're saying here, but I'm also doing a
> fair bit of a guesswork.
>
> Instead, how about,
>
> extern struct efi_memory_desc_t *efi_mem_desc_lookup(u64 phys_addr);
>
> /*
> * Return the end physical address of a EFI memory map region
> */
> static inline u64 efi_mem_desc_end(struct efi_memory_desc_t *md)
> {
> u64 size = md->num_pages << EFI_PAGE_SHIFT;
>
> return md->phys_addr + size;
> }
>
> The caller can then do,
>
> md = efi_mem_desc_lookup(phys_addr)
> if (!md)
> return -1;
>
> bytes = efi_mem_desc_end(md) - phys_addr;
> ioremap(phys_addr, bytes);
>
> I think leaving this "remaining bytes" logic in the ioremap() caller is
> the most robust solution because it's a quirky thing to want to do,
> whereas the other functions (efi_mem_desc_lookup() and
> efi_mem_desc_end()) are more general.
>
> Thoughts?
I can be okay with this.
>
> [...]
>
> > +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;
> > +}
>
> Minor detail: this should be using 'bool'.
Thanks. I'll send a new patch soon.
--
Peter
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2015-01-05 15:10 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-11-25 19:28 [RFC PATCH] Add esrt support Peter Jones
[not found] ` <1416943684-11246-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2014-12-08 18:57 ` Borislav Petkov
[not found] ` <20141208185720.GA11013-fF5Pk5pvG8Y@public.gmane.org>
2014-12-09 21:38 ` Peter Jones
[not found] ` <20141209213847.GB1159-FS9oOTXHwv9t4tGkRPVz9tcb/sdHg95EuydrBrBl+0sAvxtiuMwx3w@public.gmane.org>
2014-12-09 22:18 ` Peter Jones
[not found] ` <1418163514-22977-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2014-12-09 22:28 ` Peter Jones
[not found] ` <1418164082-23534-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2014-12-10 10:42 ` Parmeshwr_Prasad-DYMqY+WieiM
[not found] ` <EAEC0BCD44614F499E7CCE37C3B71F113793F9A460-6n1rj5y50trWnfa2oNyE5AVY21JRvFwKV6yJEvX+wlw@public.gmane.org>
2014-12-10 14:41 ` Peter Jones
[not found] ` <20141210144148.GA24234-FS9oOTXHwv9t4tGkRPVz9tcb/sdHg95EuydrBrBl+0sAvxtiuMwx3w@public.gmane.org>
2014-12-11 9:32 ` Parmeshwr_Prasad-DYMqY+WieiM
2014-12-11 0:19 ` Neri, Ricardo
2014-12-11 14:08 ` Peter Jones
2014-12-11 20:17 ` Peter Jones
2014-12-11 20:18 ` Peter Jones
[not found] ` <1418329096-8675-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2014-12-11 20:21 ` Peter Jones
[not found] ` <20141211202119.GG17171-FS9oOTXHwv9t4tGkRPVz9tcb/sdHg95EuydrBrBl+0sAvxtiuMwx3w@public.gmane.org>
2014-12-11 20:22 ` Peter Jones
[not found] ` <1418329324-8975-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2014-12-16 12:58 ` Parmeshwr_Prasad-DYMqY+WieiM
2014-12-19 15:27 ` Borislav Petkov
2015-01-05 13:51 ` Matt Fleming
[not found] ` <20150105135113.GA3163-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
2015-01-05 15:10 ` Peter Jones
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox