Add ESRT patch here.
Signed-off-by: Peter Jones <pjones@redhat.com>
This commit is contained in:
		
							parent
							
								
									283bd0665f
								
							
						
					
					
						commit
						bbf3f54706
					
				
							
								
								
									
										764
									
								
								efi-Add-esrt-support.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										764
									
								
								efi-Add-esrt-support.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,764 @@ | ||||
| From: Peter Jones <pjones@redhat.com> | ||||
| Date: Tue, 18 Nov 2014 10:18:22 -0500 | ||||
| Subject: [PATCH] efi: Add esrt support. | ||||
| 
 | ||||
| Add sysfs files for the EFI System Resource Table (ESRT) under | ||||
| /sys/firmware/efi/esrt and for each EFI System Resource Entry under | ||||
| entries/ as a subdir. | ||||
| 
 | ||||
| The EFI System Resource Table (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. | ||||
| 
 | ||||
| The ESRT is described as part of the UEFI specification, in version 2.5 | ||||
| which should be available from http://uefi.org/specifications in early | ||||
| 2015.  If you're a member of the UEFI Forum, information about its | ||||
| addition to the standard is available as UEFI Mantis 1090. | ||||
| 
 | ||||
| For some hardware platforms, additional restrictions may be found at | ||||
| http://msdn.microsoft.com/en-us/library/windows/hardware/jj128256.aspx , | ||||
| and additional documentation may be found at | ||||
| http://download.microsoft.com/download/5/F/5/5F5D16CD-2530-4289-8019-94C6A20BED3C/windows-uefi-firmware-update-platform.docx | ||||
| . | ||||
| 
 | ||||
| Signed-off-by: Peter Jones <pjones@redhat.com> | ||||
| ---
 | ||||
|  Documentation/ABI/testing/sysfs-firmware-efi-esrt |  81 ++++ | ||||
|  arch/x86/platform/efi/efi.c                       |   2 + | ||||
|  drivers/firmware/efi/Makefile                     |   2 +- | ||||
|  drivers/firmware/efi/efi.c                        |  66 +++- | ||||
|  drivers/firmware/efi/esrt.c                       | 461 ++++++++++++++++++++++ | ||||
|  include/linux/efi.h                               |   8 + | ||||
|  6 files changed, 618 insertions(+), 2 deletions(-) | ||||
|  create mode 100644 Documentation/ABI/testing/sysfs-firmware-efi-esrt | ||||
|  create mode 100644 drivers/firmware/efi/esrt.c | ||||
| 
 | ||||
| diff --git a/Documentation/ABI/testing/sysfs-firmware-efi-esrt b/Documentation/ABI/testing/sysfs-firmware-efi-esrt
 | ||||
| new file mode 100644 | ||||
| index 000000000000..6e431d1a4e79
 | ||||
| --- /dev/null
 | ||||
| +++ b/Documentation/ABI/testing/sysfs-firmware-efi-esrt
 | ||||
| @@ -0,0 +1,81 @@
 | ||||
| +What:		/sys/firmware/efi/esrt/
 | ||||
| +Date:		February 2015
 | ||||
| +Contact:	Peter Jones <pjones@redhat.com>
 | ||||
| +Description:	Provides userland access to read the EFI System Resource Table
 | ||||
| +		(ESRT), a catalog of firmware for which can be updated with
 | ||||
| +		the UEFI UpdateCapsule mechanism described in section 7.5 of
 | ||||
| +		the UEFI Standard.
 | ||||
| +Users:		fwupdate - https://github.com/rhinstaller/fwupdate
 | ||||
| +
 | ||||
| +What:		/sys/firmware/efi/esrt/fw_resource_count
 | ||||
| +Date:		February 2015
 | ||||
| +Contact:	Peter Jones <pjones@redhat.com>
 | ||||
| +Description:	The number of entries in the ESRT
 | ||||
| +
 | ||||
| +What:		/sys/firmware/efi/esrt/fw_resource_count_max
 | ||||
| +Date:		February 2015
 | ||||
| +Contact:	Peter Jones <pjones@redhat.com>
 | ||||
| +Description:	The maximum number of entries that /could/ be registered
 | ||||
| +		in the allocation the table is currently in.  This is
 | ||||
| +		really only useful to the system firmware itself.
 | ||||
| +
 | ||||
| +What:		/sys/firmware/efi/esrt/fw_resource_version
 | ||||
| +Date:		February 2015
 | ||||
| +Contact:	Peter Jones <pjones@redhat.com>
 | ||||
| +Description:	The version of the ESRT structure provided by the firmware.
 | ||||
| +
 | ||||
| +What:		/sys/firmware/efi/esrt/entries/entry$N/
 | ||||
| +Date:		February 2015
 | ||||
| +Contact:	Peter Jones <pjones@redhat.com>
 | ||||
| +Description:	Each ESRT entry is identified by a GUID, and each gets a
 | ||||
| +		subdirectory under entries/ .
 | ||||
| +		example: /sys/firmware/efi/esrt/entries/entry0/
 | ||||
| +
 | ||||
| +What:		/sys/firmware/efi/esrt/entries/entry$N/fw_type
 | ||||
| +Date:		February 2015
 | ||||
| +Contact:	Peter Jones <pjones@redhat.com>
 | ||||
| +Description:	What kind of firmware entry this is:
 | ||||
| +		0 - Unknown
 | ||||
| +		1 - System Firmware
 | ||||
| +		2 - Device Firmware
 | ||||
| +		3 - UEFI Driver
 | ||||
| +
 | ||||
| +What:		/sys/firmware/efi/esrt/entries/entry$N/fw_class
 | ||||
| +Date:		February 2015
 | ||||
| +Contact:	Peter Jones <pjones@redhat.com>
 | ||||
| +Description:	This is the entry's guid, and will match the directory name.
 | ||||
| +
 | ||||
| +What:		/sys/firmware/efi/esrt/entries/entry$N/fw_version
 | ||||
| +Date:		February 2015
 | ||||
| +Contact:	Peter Jones <pjones@redhat.com>
 | ||||
| +Description:	The version of the firmware currently installed.  This is a
 | ||||
| +		32-bit unsigned integer.
 | ||||
| +
 | ||||
| +What:		/sys/firmware/efi/esrt/entries/entry$N/lowest_supported_fw_version
 | ||||
| +Date:		February 2015
 | ||||
| +Contact:	Peter Jones <pjones@redhat.com>
 | ||||
| +Description:	The lowest version of the firmware that can be installed.
 | ||||
| +
 | ||||
| +What:		/sys/firmware/efi/esrt/entries/entry$N/capsule_flags
 | ||||
| +Date:		February 2015
 | ||||
| +Contact:	Peter Jones <pjones@redhat.com>
 | ||||
| +Description:	Flags that must be passed to UpdateCapsule()
 | ||||
| +
 | ||||
| +What:		/sys/firmware/efi/esrt/entries/entry$N/last_attempt_version
 | ||||
| +Date:		February 2015
 | ||||
| +Contact:	Peter Jones <pjones@redhat.com>
 | ||||
| +Description:	The last firmware version for which an update was attempted.
 | ||||
| +
 | ||||
| +What:		/sys/firmware/efi/esrt/entries/entry$N/last_attempt_status
 | ||||
| +Date:		February 2015
 | ||||
| +Contact:	Peter Jones <pjones@redhat.com>
 | ||||
| +Description:	The result of the last firmware update attempt for the
 | ||||
| +		firmware resource entry.
 | ||||
| +		0 - Success
 | ||||
| +		1 - Insufficient resources
 | ||||
| +		2 - Incorrect version
 | ||||
| +		3 - Invalid format
 | ||||
| +		4 - Authentication error
 | ||||
| +		5 - AC power event
 | ||||
| +		6 - Battery power event
 | ||||
| +
 | ||||
| diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
 | ||||
| index dbc8627a5cdf..b4c9fbbc9270 100644
 | ||||
| --- a/arch/x86/platform/efi/efi.c
 | ||||
| +++ b/arch/x86/platform/efi/efi.c
 | ||||
| @@ -492,6 +492,8 @@ void __init efi_init(void)
 | ||||
|  		return; | ||||
|   | ||||
|  	print_efi_memmap(); | ||||
| +
 | ||||
| +	efi_esrt_init();
 | ||||
|  } | ||||
|   | ||||
|  void __init efi_late_init(void) | ||||
| diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
 | ||||
| index d8be608a9f3b..26eabbc55341 100644
 | ||||
| --- a/drivers/firmware/efi/Makefile
 | ||||
| +++ b/drivers/firmware/efi/Makefile
 | ||||
| @@ -1,7 +1,7 @@
 | ||||
|  # | ||||
|  # Makefile for linux kernel | ||||
|  # | ||||
| -obj-$(CONFIG_EFI)			+= efi.o vars.o reboot.o
 | ||||
| +obj-$(CONFIG_EFI)			+= efi.o esrt.o vars.o reboot.o
 | ||||
|  obj-$(CONFIG_EFI_VARS)			+= efivars.o | ||||
|  obj-$(CONFIG_EFI_VARS_PSTORE)		+= efi-pstore.o | ||||
|  obj-$(CONFIG_UEFI_CPER)			+= cper.o | ||||
| diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
 | ||||
| index 3061bb8629dc..e54ea20dfb42 100644
 | ||||
| --- a/drivers/firmware/efi/efi.c
 | ||||
| +++ b/drivers/firmware/efi/efi.c
 | ||||
| @@ -39,6 +39,7 @@ struct efi __read_mostly efi = {
 | ||||
|  	.fw_vendor  = EFI_INVALID_TABLE_ADDR, | ||||
|  	.runtime    = EFI_INVALID_TABLE_ADDR, | ||||
|  	.config_table  = EFI_INVALID_TABLE_ADDR, | ||||
| +	.esrt       = EFI_INVALID_TABLE_ADDR,
 | ||||
|  }; | ||||
|  EXPORT_SYMBOL(efi); | ||||
|   | ||||
| @@ -64,7 +65,7 @@ static int __init parse_efi_cmdline(char *str)
 | ||||
|  } | ||||
|  early_param("efi", parse_efi_cmdline); | ||||
|   | ||||
| -static struct kobject *efi_kobj;
 | ||||
| +struct kobject *efi_kobj;
 | ||||
|  static struct kobject *efivars_kobj; | ||||
|   | ||||
|  /* | ||||
| @@ -232,6 +233,68 @@ err_put:
 | ||||
|   | ||||
|  subsys_initcall(efisubsys_init); | ||||
|   | ||||
| +/*
 | ||||
| + * Find the efi memory descriptor for a given physical address.  Given a
 | ||||
| + * physicall address, determine if it exists within an EFI Memory Map entry,
 | ||||
| + * and if so, populate the supplied memory descriptor with the appropriate
 | ||||
| + * data.
 | ||||
| + */
 | ||||
| +int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
 | ||||
| +{
 | ||||
| +	struct efi_memory_map *map = efi.memmap;
 | ||||
| +	void *p, *e;
 | ||||
| +
 | ||||
| +	if (!efi_enabled(EFI_MEMMAP)) {
 | ||||
| +		pr_err_once("EFI_MEMMAP is not enabled.\n");
 | ||||
| +		return -EINVAL;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	if (!map) {
 | ||||
| +		pr_err_once("efi.memmap is not set.\n");
 | ||||
| +		return -EINVAL;
 | ||||
| +	}
 | ||||
| +	if (!out_md) {
 | ||||
| +		pr_err_once("out_md is null.\n");
 | ||||
| +		return -EINVAL;
 | ||||
| +        }
 | ||||
| +	if (WARN_ON_ONCE(!map->phys_map))
 | ||||
| +		return -EINVAL;
 | ||||
| +	if (WARN_ON_ONCE(map->nr_map == 0) || WARN_ON_ONCE(map->desc_size == 0))
 | ||||
| +		return -EINVAL;
 | ||||
| +
 | ||||
| +	e = map->phys_map + map->nr_map * map->desc_size;
 | ||||
| +	for (p = map->phys_map; p < e; p += map->desc_size) {
 | ||||
| +		/*
 | ||||
| +		 * 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_DATA &&
 | ||||
| +		    md->type != EFI_RUNTIME_SERVICES_DATA)
 | ||||
| +			continue;
 | ||||
| +		if (phys_addr >= md->phys_addr && phys_addr < end) {
 | ||||
| +			memcpy(out_md, md, sizeof(*out_md));
 | ||||
| +			return 0;
 | ||||
| +		}
 | ||||
| +	}
 | ||||
| +	pr_err_once("requested map not found.\n");
 | ||||
| +	return -ENOENT;
 | ||||
| +}
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * Calculate the highest address of an efi memory descriptor.
 | ||||
| + */
 | ||||
| +u64 __init efi_mem_desc_end(efi_memory_desc_t *md)
 | ||||
| +{
 | ||||
| +	u64 size = md->num_pages << EFI_PAGE_SHIFT;
 | ||||
| +	u64 end = md->phys_addr + size;
 | ||||
| +	return end;
 | ||||
| +}
 | ||||
|   | ||||
|  /* | ||||
|   * We can't ioremap data in EFI boot services RAM, because we've already mapped | ||||
| @@ -274,6 +337,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
 | ||||
|  	{SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, | ||||
|  	{SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3}, | ||||
|  	{UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga}, | ||||
| +	{EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
 | ||||
|  	{NULL_GUID, NULL, NULL}, | ||||
|  }; | ||||
|   | ||||
| diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c
 | ||||
| new file mode 100644 | ||||
| index 000000000000..1053b38f7e46
 | ||||
| --- /dev/null
 | ||||
| +++ b/drivers/firmware/efi/esrt.c
 | ||||
| @@ -0,0 +1,461 @@
 | ||||
| +/*
 | ||||
| + * esrt.c
 | ||||
| + *
 | ||||
| + * This module exports EFI System Resource Table (ESRT) entries into userspace
 | ||||
| + * through the sysfs file system. The ESRT provides a read-only catalog of
 | ||||
| + * system components for which the system accepts firmware upgrades via UEFI's
 | ||||
| + * "Capsule Update" feature. This module allows userland utilities to evaluate
 | ||||
| + * what firmware updates can be applied to this system, and potentially arrange
 | ||||
| + * for those updates to occur.
 | ||||
| + *
 | ||||
| + * Data is currently found below /sys/firmware/efi/esrt/...
 | ||||
| + */
 | ||||
| +#define pr_fmt(fmt) "esrt: " fmt
 | ||||
| +
 | ||||
| +#include <linux/capability.h>
 | ||||
| +#include <linux/device.h>
 | ||||
| +#include <linux/efi.h>
 | ||||
| +#include <linux/init.h>
 | ||||
| +#include <linux/kernel.h>
 | ||||
| +#include <linux/kobject.h>
 | ||||
| +#include <linux/list.h>
 | ||||
| +#include <linux/memblock.h>
 | ||||
| +#include <linux/module.h>
 | ||||
| +#include <linux/slab.h>
 | ||||
| +#include <linux/types.h>
 | ||||
| +
 | ||||
| +struct efi_system_resource_entry_v1 {
 | ||||
| +	efi_guid_t	fw_class;
 | ||||
| +	u32		fw_type;
 | ||||
| +	u32		fw_version;
 | ||||
| +	u32		lowest_supported_fw_version;
 | ||||
| +	u32		capsule_flags;
 | ||||
| +	u32		last_attempt_version;
 | ||||
| +	u32		last_attempt_status;
 | ||||
| +};
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * _count and _version are what they seem like.  _max is actually just
 | ||||
| + * accounting info for the firmware when creating the table; it should never
 | ||||
| + * have been exposed to us.  To wit, the spec says:
 | ||||
| + * The maximum number of resource array entries that can be within the
 | ||||
| + * table without reallocating the table, must not be zero.
 | ||||
| + * Since there's no guidance about what that means in terms of memory layout,
 | ||||
| + * it means nothing to us.
 | ||||
| + */
 | ||||
| +struct efi_system_resource_table {
 | ||||
| +	u32	fw_resource_count;
 | ||||
| +	u32	fw_resource_count_max;
 | ||||
| +	u64	fw_resource_version;
 | ||||
| +	u8	entries[];
 | ||||
| +};
 | ||||
| +
 | ||||
| +static phys_addr_t esrt_data;
 | ||||
| +static size_t esrt_data_size;
 | ||||
| +
 | ||||
| +static struct efi_system_resource_table *esrt;
 | ||||
| +
 | ||||
| +struct esre_entry {
 | ||||
| +	union {
 | ||||
| +		struct efi_system_resource_entry_v1 *esre1;
 | ||||
| +	} esre;
 | ||||
| +
 | ||||
| +	struct kobject kobj;
 | ||||
| +	struct list_head list;
 | ||||
| +};
 | ||||
| +
 | ||||
| +/* global list of esre_entry. */
 | ||||
| +static LIST_HEAD(entry_list);
 | ||||
| +
 | ||||
| +/* entry attribute */
 | ||||
| +struct esre_attribute {
 | ||||
| +	struct attribute attr;
 | ||||
| +	ssize_t (*show)(struct esre_entry *entry, char *buf);
 | ||||
| +	ssize_t (*store)(struct esre_entry *entry,
 | ||||
| +			 const char *buf, size_t count);
 | ||||
| +};
 | ||||
| +
 | ||||
| +static struct esre_entry *to_entry(struct kobject *kobj)
 | ||||
| +{
 | ||||
| +	return container_of(kobj, struct esre_entry, kobj);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static struct esre_attribute *to_attr(struct attribute *attr)
 | ||||
| +{
 | ||||
| +	return container_of(attr, struct esre_attribute, attr);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static ssize_t esre_attr_show(struct kobject *kobj,
 | ||||
| +			      struct attribute *_attr, char *buf)
 | ||||
| +{
 | ||||
| +	struct esre_entry *entry = to_entry(kobj);
 | ||||
| +	struct esre_attribute *attr = to_attr(_attr);
 | ||||
| +
 | ||||
| +	/* Don't tell normal users what firmware versions we've got... */
 | ||||
| +	if (!capable(CAP_SYS_ADMIN))
 | ||||
| +		return -EACCES;
 | ||||
| +
 | ||||
| +	return attr->show(entry, buf);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static const struct sysfs_ops esre_attr_ops = {
 | ||||
| +	.show = esre_attr_show,
 | ||||
| +};
 | ||||
| +
 | ||||
| +/* Generic ESRT Entry ("ESRE") support. */
 | ||||
| +static ssize_t esre_fw_class_show(struct esre_entry *entry, char *buf)
 | ||||
| +{
 | ||||
| +	char *str = buf;
 | ||||
| +
 | ||||
| +	efi_guid_to_str(&entry->esre.esre1->fw_class, str);
 | ||||
| +	str += strlen(str);
 | ||||
| +	str += sprintf(str, "\n");
 | ||||
| +
 | ||||
| +	return str - buf;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static struct esre_attribute esre_fw_class = __ATTR(fw_class, 0400,
 | ||||
| +	esre_fw_class_show, NULL);
 | ||||
| +
 | ||||
| +#define esre_attr_decl(name, size, fmt) \
 | ||||
| +static ssize_t esre_##name##_show(struct esre_entry *entry, char *buf) \
 | ||||
| +{ \
 | ||||
| +	return sprintf(buf, fmt "\n", \
 | ||||
| +		       le##size##_to_cpu(entry->esre.esre1->name)); \
 | ||||
| +} \
 | ||||
| +\
 | ||||
| +static struct esre_attribute esre_##name = __ATTR(name, 0400, \
 | ||||
| +	esre_##name##_show, NULL)
 | ||||
| +
 | ||||
| +esre_attr_decl(fw_type, 32, "%u");
 | ||||
| +esre_attr_decl(fw_version, 32, "%u");
 | ||||
| +esre_attr_decl(lowest_supported_fw_version, 32, "%u");
 | ||||
| +esre_attr_decl(capsule_flags, 32, "0x%x");
 | ||||
| +esre_attr_decl(last_attempt_version, 32, "%u");
 | ||||
| +esre_attr_decl(last_attempt_status, 32, "%u");
 | ||||
| +
 | ||||
| +static struct attribute *esre1_attrs[] = {
 | ||||
| +	&esre_fw_class.attr,
 | ||||
| +	&esre_fw_type.attr,
 | ||||
| +	&esre_fw_version.attr,
 | ||||
| +	&esre_lowest_supported_fw_version.attr,
 | ||||
| +	&esre_capsule_flags.attr,
 | ||||
| +	&esre_last_attempt_version.attr,
 | ||||
| +	&esre_last_attempt_status.attr,
 | ||||
| +	NULL
 | ||||
| +};
 | ||||
| +static void esre_release(struct kobject *kobj)
 | ||||
| +{
 | ||||
| +	struct esre_entry *entry = to_entry(kobj);
 | ||||
| +
 | ||||
| +	list_del(&entry->list);
 | ||||
| +	kfree(entry);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static struct kobj_type esre1_ktype = {
 | ||||
| +	.release = esre_release,
 | ||||
| +	.sysfs_ops = &esre_attr_ops,
 | ||||
| +	.default_attrs = esre1_attrs,
 | ||||
| +};
 | ||||
| +
 | ||||
| +
 | ||||
| +static struct kobject *esrt_kobj;
 | ||||
| +static struct kset *esrt_kset;
 | ||||
| +
 | ||||
| +static int esre_create_sysfs_entry(void *esre, int entry_num)
 | ||||
| +{
 | ||||
| +	int rc = 0;
 | ||||
| +	struct esre_entry *entry;
 | ||||
| +	char name[20];
 | ||||
| +
 | ||||
| +	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
 | ||||
| +	if (!entry)
 | ||||
| +		return -ENOMEM;
 | ||||
| +
 | ||||
| +	sprintf(name, "entry%d", entry_num);
 | ||||
| +
 | ||||
| +	entry->kobj.kset = esrt_kset;
 | ||||
| +
 | ||||
| +	if (esrt->fw_resource_version == 1) {
 | ||||
| +		entry->esre.esre1 = esre;
 | ||||
| +		rc = kobject_init_and_add(&entry->kobj, &esre1_ktype, NULL,
 | ||||
| +					  "%s", name);
 | ||||
| +	}
 | ||||
| +	if (rc) {
 | ||||
| +		kfree(entry);
 | ||||
| +		return rc;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	list_add_tail(&entry->list, &entry_list);
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* support for displaying ESRT fields at the top level */
 | ||||
| +#define esrt_attr_decl(name, size, fmt) \
 | ||||
| +static ssize_t esrt_##name##_show(struct kobject *kobj, \
 | ||||
| +				  struct kobj_attribute *attr, char *buf)\
 | ||||
| +{ \
 | ||||
| +	return sprintf(buf, fmt "\n", le##size##_to_cpu(esrt->name)); \
 | ||||
| +} \
 | ||||
| +\
 | ||||
| +static struct kobj_attribute esrt_##name = __ATTR(name, 0400, \
 | ||||
| +	esrt_##name##_show, NULL)
 | ||||
| +
 | ||||
| +esrt_attr_decl(fw_resource_count, 32, "%u");
 | ||||
| +esrt_attr_decl(fw_resource_count_max, 32, "%u");
 | ||||
| +esrt_attr_decl(fw_resource_version, 64, "%llu");
 | ||||
| +
 | ||||
| +static struct attribute *esrt_attrs[] = {
 | ||||
| +	&esrt_fw_resource_count.attr,
 | ||||
| +	&esrt_fw_resource_count_max.attr,
 | ||||
| +	&esrt_fw_resource_version.attr,
 | ||||
| +	NULL,
 | ||||
| +};
 | ||||
| +
 | ||||
| +static inline int esrt_table_exists(void)
 | ||||
| +{
 | ||||
| +	if (!efi_enabled(EFI_CONFIG_TABLES))
 | ||||
| +		return 0;
 | ||||
| +	if (efi.esrt == EFI_INVALID_TABLE_ADDR)
 | ||||
| +		return 0;
 | ||||
| +	return 1;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static umode_t esrt_attr_is_visible(struct kobject *kobj,
 | ||||
| +				    struct attribute *attr, int n)
 | ||||
| +{
 | ||||
| +	if (!esrt_table_exists())
 | ||||
| +		return 0;
 | ||||
| +	return attr->mode;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static struct attribute_group esrt_attr_group = {
 | ||||
| +	.attrs = esrt_attrs,
 | ||||
| +	.is_visible = esrt_attr_is_visible,
 | ||||
| +};
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * remap the table, copy it to kmalloced pages, and unmap it.
 | ||||
| + */
 | ||||
| +void __init efi_esrt_init(void)
 | ||||
| +{
 | ||||
| +	void *va;
 | ||||
| +	struct efi_system_resource_table tmpesrt;
 | ||||
| +	struct efi_system_resource_entry_v1 *v1_entries;
 | ||||
| +	size_t size, max, entry_size, entries_size;
 | ||||
| +	efi_memory_desc_t md;
 | ||||
| +	int rc;
 | ||||
| +
 | ||||
| +	pr_debug("esrt-init: loading.\n");
 | ||||
| +	if (!esrt_table_exists())
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	rc = efi_mem_desc_lookup(efi.esrt, &md);
 | ||||
| +	if (rc < 0) {
 | ||||
| +		pr_err("ESRT header is not in the memory map.\n");
 | ||||
| +		return;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	max = efi_mem_desc_end(&md);
 | ||||
| +	if (max < efi.esrt) {
 | ||||
| +		pr_err("EFI memory descriptor is invalid. (esrt: %p max: %p)\n",
 | ||||
| +		       (void *)efi.esrt, (void *)max);
 | ||||
| +		return;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	size = sizeof(*esrt);
 | ||||
| +	max -= efi.esrt;
 | ||||
| +
 | ||||
| +	if (max < size) {
 | ||||
| +		pr_err("ESRT header doen't fit on single memory map entry. (size: %zu max: %zu)\n",
 | ||||
| +		       size, max);
 | ||||
| +		return;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	va = early_memremap(efi.esrt, size);
 | ||||
| +	if (!va) {
 | ||||
| +		pr_err("early_memremap(%p, %zu) failed.\n", (void *)efi.esrt,
 | ||||
| +		       size);
 | ||||
| +		return;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	memcpy(&tmpesrt, va, sizeof(tmpesrt));
 | ||||
| +
 | ||||
| +	if (tmpesrt.fw_resource_version == 1) {
 | ||||
| +		entry_size = sizeof (*v1_entries);
 | ||||
| +	} else {
 | ||||
| +		pr_err("Unsupported ESRT version %lld.\n",
 | ||||
| +		       tmpesrt.fw_resource_version);
 | ||||
| +		return;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	if (tmpesrt.fw_resource_count > 0 && max - size < entry_size) {
 | ||||
| +		pr_err("ESRT memory map entry can only hold the header. (max: %zu size: %zu)\n",
 | ||||
| +		       max - size, entry_size);
 | ||||
| +		goto err_memunmap;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	/*
 | ||||
| +	 * The format doesn't really give us any boundary to test here,
 | ||||
| +	 * so I'm making up 128 as the max number of individually updatable
 | ||||
| +	 * components we support.
 | ||||
| +	 * 128 should be pretty excessive, but there's still some chance
 | ||||
| +	 * somebody will do that someday and we'll need to raise this.
 | ||||
| +	 */
 | ||||
| +	if (tmpesrt.fw_resource_count > 128) {
 | ||||
| +		pr_err("ESRT says fw_resource_count has very large value %d.\n",
 | ||||
| +		       tmpesrt.fw_resource_count);
 | ||||
| +		goto err_memunmap;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	/*
 | ||||
| +	 * We know it can't be larger than N * sizeof() here, and N is limited
 | ||||
| +	 * by the previous test to a small number, so there's no overflow.
 | ||||
| +	 */
 | ||||
| +	entries_size = tmpesrt.fw_resource_count * entry_size;
 | ||||
| +	if (max < size + entries_size) {
 | ||||
| +		pr_err("ESRT does not fit on single memory map entry (size: %zu max: %zu)\n",
 | ||||
| +		       size, max);
 | ||||
| +		goto err_memunmap;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	/* remap it with our (plausible) new pages */
 | ||||
| +	early_memunmap(va, size);
 | ||||
| +	size += entries_size;
 | ||||
| +	va = early_memremap(efi.esrt, size);
 | ||||
| +	if (!va) {
 | ||||
| +		pr_err("early_memremap(%p, %zu) failed.\n", (void *)efi.esrt,
 | ||||
| +		       size);
 | ||||
| +		return;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	esrt_data = (phys_addr_t)efi.esrt;
 | ||||
| +	esrt_data_size = size;
 | ||||
| +
 | ||||
| +	pr_info("Reserving ESRT space from %p to %p.\n", (void *)esrt_data,
 | ||||
| +		(char *)esrt_data + size);
 | ||||
| +	memblock_reserve(esrt_data, esrt_data_size);
 | ||||
| +
 | ||||
| +	pr_debug("esrt-init: loaded.\n");
 | ||||
| +err_memunmap:
 | ||||
| +	early_memunmap(va, size);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static int __init register_entries(void)
 | ||||
| +{
 | ||||
| +	struct efi_system_resource_entry_v1 *v1_entries = (void *)esrt->entries;
 | ||||
| +	int i, rc;
 | ||||
| +
 | ||||
| +	if (!esrt_table_exists())
 | ||||
| +		return 0;
 | ||||
| +
 | ||||
| +	for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
 | ||||
| +		void *entry;
 | ||||
| +		if (esrt->fw_resource_version == 1) {
 | ||||
| +			entry = &v1_entries[i];
 | ||||
| +		}
 | ||||
| +		rc = esre_create_sysfs_entry(entry, i);
 | ||||
| +		if (rc < 0) {
 | ||||
| +			pr_err("ESRT entry creation failed with error %d.\n",
 | ||||
| +			       rc);
 | ||||
| +			return rc;
 | ||||
| +		}
 | ||||
| +	}
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void cleanup_entry_list(void)
 | ||||
| +{
 | ||||
| +	struct esre_entry *entry, *next;
 | ||||
| +
 | ||||
| +	list_for_each_entry_safe(entry, next, &entry_list, list) {
 | ||||
| +		kobject_put(&entry->kobj);
 | ||||
| +	}
 | ||||
| +}
 | ||||
| +
 | ||||
| +static int __init esrt_sysfs_init(void)
 | ||||
| +{
 | ||||
| +	int error;
 | ||||
| +	struct efi_system_resource_table __iomem *ioesrt;
 | ||||
| +
 | ||||
| +	pr_debug("esrt-sysfs: loading.\n");
 | ||||
| +	if (!esrt_data || !esrt_data_size)
 | ||||
| +		return -ENOSYS;
 | ||||
| +
 | ||||
| +	ioesrt = ioremap(esrt_data, esrt_data_size);
 | ||||
| +	if (!ioesrt) {
 | ||||
| +		pr_err("ioremap(%p, %zu) failed.\n", (void *)esrt_data,
 | ||||
| +		       esrt_data_size);
 | ||||
| +		return -ENOMEM;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	esrt = kmalloc(esrt_data_size, GFP_KERNEL);
 | ||||
| +	if (!esrt) {
 | ||||
| +		pr_err("kmalloc failed. (wanted %zu bytes)\n", esrt_data_size);
 | ||||
| +		iounmap(ioesrt);
 | ||||
| +		return -ENOMEM;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	memcpy_fromio(esrt, ioesrt, esrt_data_size);
 | ||||
| +
 | ||||
| +	esrt_kobj = kobject_create_and_add("esrt", efi_kobj);
 | ||||
| +	if (!esrt_kobj) {
 | ||||
| +		pr_err("Firmware table registration failed.\n");
 | ||||
| +		error = -ENOMEM;
 | ||||
| +		goto err;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	error = sysfs_create_group(esrt_kobj, &esrt_attr_group);
 | ||||
| +	if (error) {
 | ||||
| +		pr_err("Sysfs attribute export failed with error %d.\n",
 | ||||
| +		       error);
 | ||||
| +		goto err_remove_esrt;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	esrt_kset = kset_create_and_add("entries", NULL, esrt_kobj);
 | ||||
| +	if (!esrt_kset) {
 | ||||
| +		pr_err("kset creation failed.\n");
 | ||||
| +		error = -ENOMEM;
 | ||||
| +		goto err_remove_group;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	error = register_entries();
 | ||||
| +	if (error)
 | ||||
| +		goto err_cleanup_list;
 | ||||
| +
 | ||||
| +	memblock_remove(esrt_data, esrt_data_size);
 | ||||
| +
 | ||||
| +	pr_debug("esrt-sysfs: loaded.\n");
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +err_cleanup_list:
 | ||||
| +	cleanup_entry_list();
 | ||||
| +	kset_unregister(esrt_kset);
 | ||||
| +err_remove_group:
 | ||||
| +	sysfs_remove_group(esrt_kobj, &esrt_attr_group);
 | ||||
| +err_remove_esrt:
 | ||||
| +	kobject_put(esrt_kobj);
 | ||||
| +err:
 | ||||
| +	kfree(esrt);
 | ||||
| +	esrt = NULL;
 | ||||
| +	return error;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void __exit esrt_sysfs_exit(void)
 | ||||
| +{
 | ||||
| +	pr_debug("esrt-sysfs: unloading.\n");
 | ||||
| +	cleanup_entry_list();
 | ||||
| +	kset_unregister(esrt_kset);
 | ||||
| +	sysfs_remove_group(esrt_kobj, &esrt_attr_group);
 | ||||
| +	kfree(esrt);
 | ||||
| +	esrt = NULL;
 | ||||
| +	kobject_del(esrt_kobj);
 | ||||
| +	kobject_put(esrt_kobj);
 | ||||
| +}
 | ||||
| +
 | ||||
| +module_init(esrt_sysfs_init);
 | ||||
| +module_exit(esrt_sysfs_exit);
 | ||||
| +
 | ||||
| +MODULE_AUTHOR("Peter Jones <pjones@redhat.com>");
 | ||||
| +MODULE_DESCRIPTION("EFI System Resource Table support");
 | ||||
| +MODULE_LICENSE("GPL");
 | ||||
| diff --git a/include/linux/efi.h b/include/linux/efi.h
 | ||||
| index 470e8dfcb517..ed0d0c09d45c 100644
 | ||||
| --- a/include/linux/efi.h
 | ||||
| +++ b/include/linux/efi.h
 | ||||
| @@ -589,6 +589,9 @@ void efi_native_runtime_setup(void);
 | ||||
|  #define DEVICE_TREE_GUID \ | ||||
|      EFI_GUID(  0xb1b621d5, 0xf19c, 0x41a5, 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0 ) | ||||
|   | ||||
| +#define EFI_SYSTEM_RESOURCE_TABLE_GUID \
 | ||||
| +    EFI_GUID(  0xb122a263, 0x3661, 0x4f68, 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80 )
 | ||||
| +
 | ||||
|  #define EFI_CERT_SHA256_GUID \ | ||||
|      EFI_GUID(  0xc1c41626, 0x504c, 0x4092, 0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28 ) | ||||
|   | ||||
| @@ -849,6 +852,7 @@ extern struct efi {
 | ||||
|  	unsigned long fw_vendor;	/* fw_vendor */ | ||||
|  	unsigned long runtime;		/* runtime table */ | ||||
|  	unsigned long config_table;	/* config tables */ | ||||
| +	unsigned long esrt;		/* ESRT table */
 | ||||
|  	efi_get_time_t *get_time; | ||||
|  	efi_set_time_t *set_time; | ||||
|  	efi_get_wakeup_time_t *get_wakeup_time; | ||||
| @@ -901,6 +905,7 @@ static inline efi_status_t efi_query_variable_store(u32 attributes, unsigned lon
 | ||||
|  #endif | ||||
|  extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr); | ||||
|  extern int efi_config_init(efi_config_table_type_t *arch_tables); | ||||
| +extern void __init efi_esrt_init(void);
 | ||||
|  extern int efi_config_parse_tables(void *config_tables, int count, int sz, | ||||
|  				   efi_config_table_type_t *arch_tables); | ||||
|  extern u64 efi_get_iobase (void); | ||||
| @@ -908,12 +913,15 @@ 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 int __init efi_uart_console_only (void); | ||||
| +extern u64 efi_mem_desc_end(efi_memory_desc_t *md);
 | ||||
| +extern int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md);
 | ||||
|  extern void efi_initialize_iomem_resources(struct resource *code_resource, | ||||
|  		struct resource *data_resource, struct resource *bss_resource); | ||||
|  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 | ||||
| 
 | ||||
| @ -554,6 +554,9 @@ Patch1018: MODSIGN-Support-not-importing-certs-from-db.patch | ||||
| 
 | ||||
| Patch1019: Add-sysrq-option-to-disable-secure-boot-mode.patch | ||||
| 
 | ||||
| # esrt | ||||
| Patch1020: efi-Add-esrt-support.patch | ||||
| 
 | ||||
| # virt + ksm patches | ||||
| 
 | ||||
| # DRM | ||||
| @ -1329,6 +1332,8 @@ ApplyPatch MODSIGN-Support-not-importing-certs-from-db.patch | ||||
| 
 | ||||
| ApplyPatch Add-sysrq-option-to-disable-secure-boot-mode.patch | ||||
| 
 | ||||
| ApplyPatch efi-Add-esrt-support.patch | ||||
| 
 | ||||
| # Assorted Virt Fixes | ||||
| 
 | ||||
| # DRM core | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user