Skip to contentSkip to conversation

Add esrt support.

RFC PATCH In review Peter Jones 5 participants 18 messages Nov 25, 2014 – Jan 5, 2015 11 messages not in the archive EFI

lore
Share & tools
lore
#0 lore raw

RFC PATCH 1/1 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.

---
 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
drivers/firmware/efi/Makefile +1 -1
index aef6a95..0d61089 100644
@@ -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
drivers/firmware/efi/efi.c +36 -1
index 8590099..3314522 100644
@@ -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},
 };
 
drivers/firmware/efi/esrt.c new file +402 -0

402 lines not on this page · raw message

include/linux/efi.h +6 -0
index 0949f9c..5b663a7 100644
@@ -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);
Signature · 1 line
2.1.0

A message that is not in the archive (sent off-list or not yet received): 1416943684-11246-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org

#2 lore raw
Quoted 3 lines · 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.

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?

Quoted 7 lines
---
 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.

Quoted 44 lines
drivers/firmware/efi/Makefile +1 -1
index aef6a95..0d61089 100644
@@ -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
drivers/firmware/efi/efi.c +4 -1
index 8590099..3314522 100644
@@ -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;
 }

drivers/firmware/efi/efi.c err_put: · line 226

Quoted 4 lines
drivers/firmware/efi/efi.c +1 -0
@@ -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?

drivers/firmware/efi/efi.c err_put: · line 235

Quoted 9 lines
drivers/firmware/efi/efi.c +9 -0
+{
+	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.

drivers/firmware/efi/efi.c err_put: · line 242

Quoted 7 lines
drivers/firmware/efi/efi.c +7 -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. */

Kernel comment style:

	/*
	 * Blabla.
	 * Second sentence blabla.
	 */

drivers/firmware/efi/efi.c err_put: · line 243

Quoted 1 line
drivers/firmware/efi/efi.c +1 -0
+		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?

drivers/firmware/efi/efi.c err_put: · line 249

Quoted 6 lines
drivers/firmware/efi/efi.c +6 -0
+		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 :-)

Quoted 24 lines
drivers/firmware/efi/efi.c +8 -0
+		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},
 };
 
drivers/firmware/efi/esrt.c new file +0 -0
new file mode 100644
index 0000000..8c6b238

drivers/firmware/efi/esrt.c line 2

Quoted 2 lines
drivers/firmware/efi/esrt.c new file +2 -0
@@ -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.

Quoted 39 lines
drivers/firmware/efi/esrt.c new file +39 -0
+ *
+ * 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;

drivers/firmware/efi/esrt.c line 51

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+
+	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.

Quoted 5 lines
drivers/firmware/efi/esrt.c new file +5 -0
+	struct attribute attr;
+	ssize_t (*show)(struct esre_sysfs_entry *entry, char *buf);
+};
+
+static struct esre_sysfs_entry *to_entry(struct kobject *kobj)

drivers/firmware/efi/esrt.c line 66

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+{
+	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.

Quoted 11 lines
drivers/firmware/efi/esrt.c new file +11 -0
+				    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);
+}

drivers/firmware/efi/esrt.c line 87

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+
+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...

Quoted 25 lines
drivers/firmware/efi/esrt.c new file +25 -0
+	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);
+}

drivers/firmware/efi/esrt.c line 122

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+
+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.

drivers/firmware/efi/esrt.c line 126

Quoted 4 lines
drivers/firmware/efi/esrt.c new file +4 -0
+
+#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.

Quoted 30 lines
drivers/firmware/efi/esrt.c new file +30 -0
+	        .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);
+}
+

drivers/firmware/efi/esrt.c line 166

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+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.

drivers/firmware/efi/esrt.c line 177

Quoted 11 lines
drivers/firmware/efi/esrt.c new file +11 -0
+{
+	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.

drivers/firmware/efi/esrt.c line 180

Quoted 3 lines
drivers/firmware/efi/esrt.c new file +3 -0
+	name = kzalloc(name_size, GFP_KERNEL);
+	if (!name) {
+		kfree(entry);

You can solve that with goto and labels. The kernel is full of examples...

drivers/firmware/efi/esrt.c line 191

Quoted 11 lines
drivers/firmware/efi/esrt.c new file +11 -0
+		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.

Quoted 24 lines
drivers/firmware/efi/esrt.c new file +24 -0
+		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");

drivers/firmware/efi/esrt.c line 225

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+
+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

Quoted 14 lines
drivers/firmware/efi/esrt.c new file +14 -0
+{
+	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 */

drivers/firmware/efi/esrt.c line 249

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+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...

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -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);

drivers/firmware/efi/esrt.c line 269

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+	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...

Quoted 6 lines
drivers/firmware/efi/esrt.c new file +6 -0
+
+	iounmap(tmpesrt);
+	tmpesrt = ioremap(efi.esrt, size);
+	if (!tmpesrt) {
+		pr_err("ioremap failed.\n");
+		return -ENOMEM;

drivers/firmware/efi/esrt.c line 285

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+	}
+
+	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.

drivers/firmware/efi/esrt.c line 293

Quoted 8 lines
drivers/firmware/efi/esrt.c new file +8 -0
+
+	/* 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.

drivers/firmware/efi/esrt.c line 304

Quoted 11 lines
drivers/firmware/efi/esrt.c new file +11 -0
+
+	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.

drivers/firmware/efi/esrt.c line 317

Quoted 13 lines
drivers/firmware/efi/esrt.c new file +13 -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++) {

I wouldn't trust what I'm getting from the fw and sanity-check it first, before iterating over it.

drivers/firmware/efi/esrt.c line 322

Quoted 5 lines
drivers/firmware/efi/esrt.c new file +5 -0
+		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.

Quoted 7 lines
drivers/firmware/efi/esrt.c new file +7 -0
+		}
+	}
+	return 0;
+}
+
+static void cleanup_entry_list(void)
+{

drivers/firmware/efi/esrt.c line 339

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+	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.

drivers/firmware/efi/esrt.c line 343

Quoted 4 lines
drivers/firmware/efi/esrt.c new file +4 -0
+
+	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.

drivers/firmware/efi/esrt.c line 349

Quoted 6 lines
drivers/firmware/efi/esrt.c new file +6 -0
+		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.

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+
+	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) {

drivers/firmware/efi/esrt.c line 369

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+		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?

drivers/firmware/efi/esrt.c line 382

Quoted 13 lines
drivers/firmware/efi/esrt.c new file +13 -0
+
+	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.

drivers/firmware/efi/esrt.c line 387

Quoted 5 lines
drivers/firmware/efi/esrt.c new file +5 -0
+}
+
+static void __exit esrt_sysfs_exit(void)
+{
+	pr_debug("esrt-sysfs: unloading.\n");

More debugging leftovers.

Quoted 61 lines
drivers/firmware/efi/esrt.c new file +15 -0
+	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 <[email protected]>");
+MODULE_DESCRIPTION("EFI System Resource Table support");
+MODULE_LICENSE("GPL");
include/linux/efi.h +6 -0
index 0949f9c..5b663a7 100644
@@ -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);
Signature · 6 lines
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe linux-efi" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Signature · 5 lines
Regards/Gruss,
    Boris.

Sent from a fat crate under my desk. Formatting is fine.
--

A message that is not in the archive (sent off-list or not yet received): [email protected]

#4 lore raw
Quoted 9 lines · Borislav Petkov
Quoted 3 lines · 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.

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.

Quoted 1 line

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.

Quoted 2 lines

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.

Quoted 11 lines
Quoted 8 lines
---
 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.

Quoted 1 line

[snip]

Quoted 7 lines
Quoted 4 lines

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.

Quoted 15 lines

drivers/firmware/efi/efi.c err_put: · line 235

Quoted 10 lines
drivers/firmware/efi/efi.c +9 -0
+{
+	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.

Quoted 10 lines

drivers/firmware/efi/efi.c err_put: · line 242

Quoted 7 lines
drivers/firmware/efi/efi.c +7 -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. */

Kernel comment style:

Quoted 4 lines
	/*
	 * Blabla.
	 * Second sentence blabla.
	 */

Okay, I'll fix that.

Quoted 9 lines

drivers/firmware/efi/efi.c err_put: · line 243

Quoted 2 lines
drivers/firmware/efi/efi.c +1 -0
+		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.

Quoted 11 lines

drivers/firmware/efi/efi.c err_put: · line 249

Quoted 7 lines
drivers/firmware/efi/efi.c +6 -0
+		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.

Quoted 1 line

[trim]

Quoted 6 lines

drivers/firmware/efi/esrt.c line 2

Quoted 4 lines

+++ b/drivers/firmware/efi/esrt.c

drivers/firmware/efi/esrt.c new file +2 -0
@@ -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.

Quoted 2 lines

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.

Quoted 1 line

[lots of trimming I've done here]

Quoted 7 lines

drivers/firmware/efi/esrt.c line 51

Quoted 2 lines
drivers/firmware/efi/esrt.c new file +2 -0
+/* 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.

Quoted 43 lines
Quoted 5 lines
drivers/firmware/efi/esrt.c new file +5 -0
+	struct attribute attr;
+	ssize_t (*show)(struct esre_sysfs_entry *entry, char *buf);
+};
+
+static struct esre_sysfs_entry *to_entry(struct kobject *kobj)

drivers/firmware/efi/esrt.c line 66

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+{
+	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.

Quoted 11 lines
drivers/firmware/efi/esrt.c new file +11 -0
+				    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);
+}

drivers/firmware/efi/esrt.c line 87

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+
+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

Quoted 3 lines
	efi_guit_to_str()

to me...

I couldn't agree more.

Quoted 31 lines
Quoted 31 lines
drivers/firmware/efi/esrt.c new file +30 -0
+	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);
+}
Quoted 8 lines

drivers/firmware/efi/esrt.c line 122

Quoted 5 lines
drivers/firmware/efi/esrt.c new file +5 -0
+
+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.

Quoted 9 lines

drivers/firmware/efi/esrt.c line 126

Quoted 5 lines
drivers/firmware/efi/esrt.c new file +4 -0
+
+#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.

Quoted 36 lines
Quoted 36 lines
drivers/firmware/efi/esrt.c new file +34 -0
+	        .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,
Quoted 8 lines

drivers/firmware/efi/esrt.c line 166

Quoted 6 lines
drivers/firmware/efi/esrt.c new file +6 -0
+};
+
+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.

Quoted 17 lines

drivers/firmware/efi/esrt.c line 177

Quoted 12 lines
drivers/firmware/efi/esrt.c new file +11 -0
+{
+	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.

Quoted 7 lines

drivers/firmware/efi/esrt.c line 180

Quoted 4 lines
drivers/firmware/efi/esrt.c new file +3 -0
+	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. :)

Quoted 52 lines

drivers/firmware/efi/esrt.c line 191

Quoted 12 lines
drivers/firmware/efi/esrt.c new file +11 -0
+		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.

Quoted 24 lines
drivers/firmware/efi/esrt.c new file +24 -0
+		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");

drivers/firmware/efi/esrt.c line 225

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+
+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.:

Quoted 4 lines

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.

Quoted 20 lines
Quoted 20 lines
drivers/firmware/efi/esrt.c new file +19 -0
+{
+	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;
Quoted 8 lines

drivers/firmware/efi/esrt.c line 230

Quoted 5 lines
drivers/firmware/efi/esrt.c new file +5 -0
+
+	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.

Quoted 16 lines
Quoted 16 lines
drivers/firmware/efi/esrt.c new file +15 -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;
+	}
+
Quoted 8 lines

drivers/firmware/efi/esrt.c line 269

Quoted 5 lines
drivers/firmware/efi/esrt.c new file +5 -0
+	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.

Quoted 11 lines
Quoted 11 lines
drivers/firmware/efi/esrt.c new file +10 -0
+
+	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:"
Quoted 8 lines

drivers/firmware/efi/esrt.c line 285

Quoted 6 lines
drivers/firmware/efi/esrt.c new file +6 -0
+		       "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.

Quoted 15 lines

drivers/firmware/efi/esrt.c line 293

Quoted 9 lines
drivers/firmware/efi/esrt.c new file +8 -0
+
+	/* 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?

Quoted 2 lines

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 :/

Quoted 14 lines

drivers/firmware/efi/esrt.c line 304

Quoted 12 lines
drivers/firmware/efi/esrt.c new file +11 -0
+
+	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.

Quoted 17 lines
Quoted 4 lines
drivers/firmware/efi/esrt.c new file +3 -0
+}
+
+static int register_entries(void)

drivers/firmware/efi/esrt.c line 317

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+{
+	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.

Quoted 10 lines

drivers/firmware/efi/esrt.c line 322

Quoted 6 lines
drivers/firmware/efi/esrt.c new file +5 -0
+		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?

Quoted 12 lines
Quoted 12 lines
drivers/firmware/efi/esrt.c new file +11 -0
+		}
+	}
+	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);
Quoted 8 lines

drivers/firmware/efi/esrt.c line 339

Quoted 6 lines
drivers/firmware/efi/esrt.c new file +6 -0
+	}
+}
+
+static int __init esrt_sysfs_init(void)
+{
+	int error = -ENOMEM;

Useless variable initialization.

Okay.

Quoted 7 lines

drivers/firmware/efi/esrt.c line 343

Quoted 5 lines
drivers/firmware/efi/esrt.c new file +4 -0
+
+	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.

Quoted 9 lines

drivers/firmware/efi/esrt.c line 349

Quoted 6 lines
drivers/firmware/efi/esrt.c new file +6 -0
+		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.

Quoted 15 lines
Quoted 15 lines
drivers/firmware/efi/esrt.c new file +14 -0
+
+	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;
+	}
Quoted 8 lines

drivers/firmware/efi/esrt.c line 369

Quoted 6 lines
drivers/firmware/efi/esrt.c new file +6 -0
+
+	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...

Quoted 16 lines
Quoted 4 lines
drivers/firmware/efi/esrt.c new file +3 -0
+
+	return 0;
+err_cleanup_list:

drivers/firmware/efi/esrt.c line 382

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+	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.

Quoted 8 lines

drivers/firmware/efi/esrt.c line 387

Quoted 6 lines
drivers/firmware/efi/esrt.c new file +5 -0
+}
+
+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.

Signature · 1 line
        Peter

A message that is not in the archive (sent off-list or not yet received): 20141209213847.GB1159-FS9oOTXHwv9t4tGkRPVz9tcb/[email protected]

#6 lore raw

RFC PATCH 1/1 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.

---
 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
drivers/firmware/efi/Makefile +1 -1
index aef6a95..0d61089 100644
@@ -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
drivers/firmware/efi/efi.c +44 -1

75 lines not on this page · raw message

drivers/firmware/efi/esrt.c new file +392 -0

392 lines not on this page · raw message

include/linux/efi.h +6 -0
index 0949f9c..5b663a7 100644
@@ -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);
Signature · 1 line
2.1.0

A message that is not in the archive (sent off-list or not yet received): 1418163514-22977-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org

#8 lore raw

RFC PATCH 1/1 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.

---
 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
drivers/firmware/efi/Makefile +1 -1
index aef6a95..0d61089 100644
@@ -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
drivers/firmware/efi/efi.c +45 -1

76 lines not on this page · raw message

drivers/firmware/efi/esrt.c new file +393 -0

393 lines not on this page · raw message

include/linux/efi.h +6 -0
index 0949f9c..5b663a7 100644
@@ -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);
Signature · 1 line
2.1.0

A message that is not in the archive (sent off-list or not yet received): 1418164082-23534-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org

#10 lore raw

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: [email protected] [mailto:[email protected]] On Behalf Of Peter Jones
Sent: Wednesday, December 10, 2014 3:58 AM
To: [email protected]
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.

---
 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
drivers/firmware/efi/Makefile +1 -1
@@ -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
drivers/firmware/efi/efi.c +46 -1

67 lines not on this page · raw message

/*

  * 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},
};

drivers/firmware/efi/esrt.c new file +384 -0

384 lines not on this page · raw message

include/linux/efi.h +5 -0
@@ -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 [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

A message that is not in the archive (sent off-list or not yet received): EAEC0BCD44614F499E7CCE37C3B71F113793F9A460-6n1rj5y50trWnfa2oNyE5AVY21JRvFwKV6yJEvX+wlw@public.gmane.org

#12 lore raw
Quoted 4 lines · On Wed, Dec 10, 2014 at 04:12:33PM +0530…

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?

Quoted 4 lines
                                                                                   ^
include/linux/sysfs.h:76:10: note: in definition of macro '__ATTR'
  .show = _show,      \
          ^
Quoted 8 lines

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.

Quoted 543 lines

Is these are really error or I am making mistake.

I will be happy to test you patch.

-Param

-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of Peter Jones
Sent: Wednesday, December 10, 2014 3:58 AM
To: [email protected]
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.

---
 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
drivers/firmware/efi/Makefile +1 -1
@@ -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
drivers/firmware/efi/efi.c +47 -1
@@ -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},
 };
drivers/firmware/efi/esrt.c new file +384 -0
@@ -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 <[email protected]>");
+MODULE_DESCRIPTION("EFI System Resource Table support");
+MODULE_LICENSE("GPL");
include/linux/efi.h +7 -1
@@ -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 [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

Signature · 1 line
        Peter

A message that is not in the archive (sent off-list or not yet received): 20141210144148.GA24234-FS9oOTXHwv9t4tGkRPVz9tcb/[email protected]

#14 lore raw

-----Original Message-----
From: Peter Jones [mailto:[email protected]]
Sent: Wednesday, December 10, 2014 8:12 PM
To: Prasad, Parmeshwr
Cc: [email protected]
Subject: Re: [RFC PATCH] Add esrt support.

Quoted 5 lines · On Wed, Dec 10, 2014 at 04:12:33PM +0530, [email protected] 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 :)

Quoted 5 lines

^
include/linux/sysfs.h:76:10: note: in definition of macro '__ATTR'

  .show = _show,      \
          ^
Quoted 8 lines

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.

Quoted 555 lines

Is these are really error or I am making mistake.

I will be happy to test you patch.

-Param

-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of Peter Jones
Sent: Wednesday, December 10, 2014 3:58 AM
To: [email protected]
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.

---
 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

(quoted diff) +0 -0

b/drivers/firmware/efi/Makefile index aef6a95..0d61089 100644

drivers/firmware/efi/Makefile +1 -1
@@ -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
drivers/firmware/efi/efi.c +47 -1
index 8590099..68002d8 100644
@@ -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},
 };
drivers/firmware/efi/esrt.c new file +381 -0
new file mode 100644 index 0000000..71eb77c
@@ -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 <[email protected]>");
+MODULE_DESCRIPTION("EFI System Resource Table support");
+MODULE_LICENSE("GPL");
include/linux/efi.h index renamed from include/linux/efi.h +0 -0

0949f9c..5b663a7 100644

include/linux/efi.h +4 -0
@@ -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 )

include/linux/efi.h +1 -0
@@ -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

Quoted 4 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-efi" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

--
Peter

#15 lore raw
In reply to a message not in the archive
Quoted 6 lines · 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.

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");
^

Quoted 545 lines
---
 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
drivers/firmware/efi/Makefile +1 -1
index aef6a95..0d61089 100644
@@ -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
drivers/firmware/efi/efi.c +45 -1
index 8590099..68002d8 100644
@@ -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},
 };
 
drivers/firmware/efi/esrt.c new file +393 -0
new file mode 100644
index 0000000..71eb77c
@@ -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 <[email protected]>");
+MODULE_DESCRIPTION("EFI System Resource Table support");
+MODULE_LICENSE("GPL");
include/linux/efi.h +6 -0
index 0949f9c..5b663a7 100644
@@ -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);
#16 lore raw
Quoted 5 lines · Ricardo
Quoted 4 lines · 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.

Quoted 8 lines
Quoted 2 lines

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.

Signature · 1 line
        Peter
#17 lore raw
In reply to Neri, Ricardo
Quoted 10 lines · Ricardo
Quoted 6 lines · 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.

Hi Peter,

Quoted 7 lines

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.

Signature · 1 line
        Peter
#18 lore raw

RFC PATCH 1/1 Add esrt support.

In reply to Neri, Ricardo

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.

---
 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
drivers/firmware/efi/Makefile +1 -1
index aef6a95..0d61089 100644
@@ -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
drivers/firmware/efi/efi.c +45 -1

76 lines not on this page · raw message

drivers/firmware/efi/esrt.c new file +393 -0

393 lines not on this page · raw message

include/linux/efi.h +6 -0
index 0949f9c..5b663a7 100644
@@ -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);
Signature · 1 line
2.1.0

A message that is not in the archive (sent off-list or not yet received): 1418329096-8675-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org

#20 lore raw
Quoted 7 lines · 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.

Except it isn't. Apparently I'm really having a rough week with invoking git correctly.

Signature · 1 line
        Peter

A message that is not in the archive (sent off-list or not yet received): 20141211202119.GG17171-FS9oOTXHwv9t4tGkRPVz9tcb/[email protected]

#22 lore raw

RFC PATCH 1/1 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.

---
 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
drivers/firmware/efi/Makefile +1 -1
index aef6a95..0d61089 100644
@@ -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
drivers/firmware/efi/efi.c +45 -1

76 lines not on this page · raw message

drivers/firmware/efi/esrt.c new file +393 -0

393 lines not on this page · raw message

include/linux/efi.h +6 -0
index 0949f9c..5b663a7 100644
@@ -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);
Signature · 1 line
2.1.0

A message that is not in the archive (sent off-list or not yet received): 1418329324-8975-1-git-send-email-pjones-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org

#24 lore raw

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: [email protected] [mailto:[email protected]] On Behalf Of Peter Jones
Sent: Friday, December 12, 2014 1:52 AM
To: [email protected]
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.

---
 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
drivers/firmware/efi/Makefile +1 -1
@@ -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
drivers/firmware/efi/efi.c +46 -1

67 lines not on this page · raw message

/*

  * 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},
};

drivers/firmware/efi/esrt.c new file +383 -0

383 lines not on this page · raw message

include/linux/efi.h +5 -0
@@ -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 [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

#25 lore raw
In reply to a message not in the archive
Quoted 9 lines · 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.

Looks good, just minor nitpicks and some big-picture questions:

Quoted 13 lines
---
 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
drivers/firmware/efi/Makefile +0 -0
index aef6a95..0d61089 100644

drivers/firmware/efi/Makefile line 4

Quoted 5 lines
drivers/firmware/efi/Makefile +1 -1
@@ -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.

Quoted 34 lines
drivers/firmware/efi/Makefile +0 -0
 obj-$(CONFIG_EFI_VARS)			+= efivars.o
 obj-$(CONFIG_EFI_VARS_PSTORE)		+= efi-pstore.o
 obj-$(CONFIG_UEFI_CPER)			+= cper.o
drivers/firmware/efi/efi.c +4 -1
index 8590099..68002d8 100644
@@ -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;
 }

drivers/firmware/efi/efi.c err_put: · line 232

Quoted 10 lines
drivers/firmware/efi/efi.c +7 -0
@@ -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. :)

drivers/firmware/efi/efi.c err_put: · line 242

Quoted 10 lines
drivers/firmware/efi/efi.c +10 -0
+{
+	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:

Quoted 11 lines
Quoted 4 lines

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.

Quoted 39 lines
drivers/firmware/efi/efi.c +23 -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 -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},
 };
 
drivers/firmware/efi/esrt.c new file +0 -0
new file mode 100644
index 0000000..a58065b

drivers/firmware/efi/esrt.c line 9

Quoted 9 lines
drivers/firmware/efi/esrt.c new file +9 -0
@@ -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?

Quoted 4 lines
drivers/firmware/efi/esrt.c new file +4 -0
+ *
+ * Data is currently found below /sys/firmware/efi/esrt/...
+ */
+#define pr_fmt(fmt) "esrt: " fmt

...

Quoted 34 lines
drivers/firmware/efi/esrt.c new file +34 -0
+/*
+ * 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;

drivers/firmware/efi/esrt.c line 270

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+	}
+
+	/*
+	 * 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.

drivers/firmware/efi/esrt.c line 271

Quoted 1 line
drivers/firmware/efi/esrt.c new file +1 -0
+		pr_err("ESRT says fw_resource_count has very large value %d.\n",

"Resource count overflow" ?

Quoted 30 lines
drivers/firmware/efi/esrt.c new file +30 -0
+		       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;

drivers/firmware/efi/esrt.c line 311

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+	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.

Quoted 24 lines
drivers/firmware/efi/esrt.c new file +24 -0
+			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);

drivers/firmware/efi/esrt.c line 345

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+	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.

Quoted 7 lines
drivers/firmware/efi/esrt.c new file +7 -0
+		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;

drivers/firmware/efi/esrt.c line 362

Quoted 10 lines
drivers/firmware/efi/esrt.c new file +10 -0
+		goto err_remove_group;
+	}
+
+	error = register_entries();
+	if (error)
+		goto err_cleanup_list;
+
+	pr_debug("esrt-sysfs: loaded.\n");
+
+	return 0;

newline here please.

Quoted 12 lines
drivers/firmware/efi/esrt.c new file +12 -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;
+}
Signature · 5 lines
Regards/Gruss,
    Boris.

Sent from a fat crate under my desk. Formatting is fine.
--
#26 lore raw
In reply to a message not in the archive
Quoted 8 lines · 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.

Quoted 8 lines
---
 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.

drivers/firmware/efi/efi.c err_put: · line 232

Quoted 11 lines
drivers/firmware/efi/efi.c +7 -0
@@ -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?

[...]

drivers/firmware/efi/esrt.c line 212

Quoted 8 lines
drivers/firmware/efi/esrt.c new file +8 -0
+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'.

Signature · 1 line
Matt Fleming, Intel Open Source Technology Center

A message that is not in the archive (sent off-list or not yet received): 20150105135113.GA3163-HNK1S37rvNbeXh+fF434Mdi2O/[email protected]

#28 lore raw
Quoted 18 lines · Matt Fleming
Quoted 8 lines · 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.

Quoted 8 lines
---
 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
Quoted 7 lines

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.

Quoted 39 lines

drivers/firmware/efi/efi.c err_put: · line 232

Quoted 12 lines
drivers/firmware/efi/efi.c +7 -0
@@ -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);
Quoted 6 lines

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.

Quoted 13 lines

[...]

drivers/firmware/efi/esrt.c line 212

Quoted 8 lines
drivers/firmware/efi/esrt.c new file +8 -0
+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.

Signature · 1 line
        Peter