2016-03-17 12:57:17

by Matt Fleming

[permalink] [raw]
Subject: [PATCH 0/4] EFI capsule update support

This is the reincarnation of the EFI capsule patches I originally
posted in 2013, plus Hock's capsule loader driver from earlier this
year. Together they provide a way for users to send capsule images to
firmware.

EFI capsules are binary blobs passed from the OS to the firmware. The
firmware then parses them and makes some decision based upon their
contents. The most common use case is to bundle a flashable firmware
image into a capsule that the firmware can use to upgrade the existing
version in the flash.

The series has been tested on the Intel Quark Galileo.

I plan on queuing this up for v4.7.

Kweh, Hock Leong (1):
efi: A misc char interface to update EFI firmware

Matt Fleming (3):
efi: Move efi_status_to_err() to drivers/firmware/efi/
efi: Capsule update support
x86/efi: Force EFI reboot to process pending capsules

arch/x86/kernel/reboot.c | 9 +
drivers/firmware/efi/Kconfig | 10 +
drivers/firmware/efi/Makefile | 3 +-
drivers/firmware/efi/capsule-loader.c | 343 ++++++++++++++++++++++++++++++++++
drivers/firmware/efi/capsule.c | 286 ++++++++++++++++++++++++++++
drivers/firmware/efi/efi.c | 33 ++++
drivers/firmware/efi/reboot.c | 12 +-
drivers/firmware/efi/vars.c | 33 ----
include/linux/efi.h | 22 +++
9 files changed, 716 insertions(+), 35 deletions(-)
create mode 100644 drivers/firmware/efi/capsule-loader.c
create mode 100644 drivers/firmware/efi/capsule.c

--
2.6.2


2016-03-17 12:57:26

by Matt Fleming

[permalink] [raw]
Subject: [PATCH 3/4] x86/efi: Force EFI reboot to process pending capsules

If an EFI capsule has been sent to the firmware we must match the type
of EFI reset against that required by the capsule to ensure it is
processed correctly.

Force an EFI reboot if a capsule is pending for the next reset.

Cc: Kweh Hock Leong <[email protected]>
Cc: Borislav Petkov <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: "H. Peter Anvin" <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Signed-off-by: Matt Fleming <[email protected]>
---
arch/x86/kernel/reboot.c | 9 +++++++++
include/linux/efi.h | 6 ++++++
2 files changed, 15 insertions(+)

diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index ab0adc0fa5db..a9b31eb815f2 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -535,6 +535,15 @@ static void native_machine_emergency_restart(void)
mode = reboot_mode == REBOOT_WARM ? 0x1234 : 0;
*((unsigned short *)__va(0x472)) = mode;

+ /*
+ * If an EFI capsule has been registered with the firmware then
+ * override the reboot= parameter.
+ */
+ if (efi_capsule_pending(NULL)) {
+ pr_info("EFI capsule is pending, forcing EFI reboot.\n");
+ reboot_type = BOOT_EFI;
+ }
+
for (;;) {
/* Could also try the reset bit in the Hammer NB */
switch (reboot_type) {
diff --git a/include/linux/efi.h b/include/linux/efi.h
index a28053e71e99..290aa5673119 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -1028,6 +1028,12 @@ static inline bool efi_enabled(int feature)
}
static inline void
efi_reboot(enum reboot_mode reboot_mode, const char *__unused) {}
+
+static inline bool
+efi_capsule_pending(int *reset_type)
+{
+ return false;
+}
#endif

extern int efi_status_to_err(efi_status_t status);
--
2.6.2

2016-03-17 12:57:28

by Matt Fleming

[permalink] [raw]
Subject: [PATCH 2/4] efi: Capsule update support

The EFI capsule mechanism allows data blobs to be passed to the EFI
firmware. A common use case is performing firmware updates. This patch
just introduces the main infrastructure for interacting with the
firmware, and a driver that allows users to upload capsules will come
in a later patch.

Once a capsule has been passed to the firmware, the next reboot must
be performed using the ResetSystem() EFI runtime service, which may
involve overriding the reboot type specified by reboot=. This ensures
the reset value returned by QueryCapsuleCapabilities() is used to
reset the system, which is required for the capsule to be processed.
efi_capsule_pending() is provided for this purpose.

At the moment we only allow a single capsule blob to be sent to the
firmware despite the fact that UpdateCapsule() takes a 'CapsuleCount'
parameter. This simplifies the API and shouldn't result in any
downside since it is still possible to send multiple capsules by
repeatedly calling UpdateCapsule().

Cc: Kweh Hock Leong <[email protected]>
Cc: Borislav Petkov <[email protected]>
Cc: Ard Biesheuvel <[email protected]>
Cc: Mark Salter <[email protected]>
Cc: Peter Jones <[email protected]>
Cc: joeyli <[email protected]>
Cc: Bryan O'Donoghue <[email protected]>
Signed-off-by: Matt Fleming <[email protected]>
---
drivers/firmware/efi/Makefile | 2 +-
drivers/firmware/efi/capsule.c | 286 +++++++++++++++++++++++++++++++++++++++++
drivers/firmware/efi/reboot.c | 12 +-
include/linux/efi.h | 14 ++
4 files changed, 312 insertions(+), 2 deletions(-)
create mode 100644 drivers/firmware/efi/capsule.c

diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
index 62e654f255f4..e4468c7c16b5 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -9,7 +9,7 @@
#
KASAN_SANITIZE_runtime-wrappers.o := n

-obj-$(CONFIG_EFI) += efi.o vars.o reboot.o
+obj-$(CONFIG_EFI) += efi.o vars.o reboot.o capsule.o
obj-$(CONFIG_EFI_VARS) += efivars.o
obj-$(CONFIG_EFI_ESRT) += esrt.o
obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o
diff --git a/drivers/firmware/efi/capsule.c b/drivers/firmware/efi/capsule.c
new file mode 100644
index 000000000000..dac25208ad5e
--- /dev/null
+++ b/drivers/firmware/efi/capsule.c
@@ -0,0 +1,286 @@
+/*
+ * EFI capsule support.
+ *
+ * Copyright 2013 Intel Corporation; author Matt Fleming
+ *
+ * This file is part of the Linux kernel, and is made available under
+ * the terms of the GNU General Public License version 2.
+ */
+
+#define pr_fmt(fmt) "efi: " fmt
+
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/highmem.h>
+#include <linux/efi.h>
+#include <linux/vmalloc.h>
+#include <asm/io.h>
+
+typedef struct {
+ u64 length;
+ u64 data;
+} efi_capsule_block_desc_t;
+
+static bool capsule_pending;
+static int efi_reset_type = -1;
+
+/*
+ * capsule_mutex serialises access to both capsule_pending and
+ * efi_reset_type.
+ */
+static DEFINE_MUTEX(capsule_mutex);
+
+/**
+ * efi_capsule_pending - has a capsule been passed to the firmware?
+ * @reset_type: store the type of EFI reset if capsule is pending
+ *
+ * To ensure that the registered capsule is processed correctly by the
+ * firmware we need to perform a specific type of reset. If a capsule is
+ * pending return the reset type in @reset_type.
+ *
+ * This function will race with callers of efi_capsule_update(), for
+ * example, calling this function while somebody else is in
+ * efi_capsule_update() but hasn't reached efi_capsue_update_locked()
+ * will miss the updates to capsule_pending and efi_reset_type after
+ * efi_capsule_update_locked() completes.
+ *
+ * A non-racy use is from platform reboot code because we use
+ * system_state to ensure no capsules can be sent to the firmware once
+ * we're at SYSTEM_RESTART. See efi_capsule_update_locked().
+ */
+bool efi_capsule_pending(int *reset_type)
+{
+ bool rv = false;
+
+ mutex_lock(&capsule_mutex);
+ if (!capsule_pending)
+ goto out;
+
+ if (reset_type)
+ *reset_type = efi_reset_type;
+ rv = true;
+out:
+ mutex_unlock(&capsule_mutex);
+ return rv;
+}
+
+/**
+ * efi_capsule_supported - does the firmware support the capsule?
+ * @guid: vendor guid of capsule
+ * @flags: capsule flags
+ * @size: size of capsule data
+ * @reset: the reset type required for this capsule
+ *
+ * Check whether a capsule with @flags is supported by the firmware
+ * and that @size doesn't exceed the maximum size for a capsule.
+ *
+ * No attempt is made to check @reset against the reset type required
+ * by any pending capsules because of the races involved.
+ */
+int efi_capsule_supported(efi_guid_t guid, u32 flags, size_t size, int *reset)
+{
+ efi_capsule_header_t *capsule;
+ efi_status_t status;
+ u64 max_size;
+ int rv = 0;
+
+ capsule = kmalloc(sizeof(*capsule), GFP_KERNEL);
+ if (!capsule)
+ return -ENOMEM;
+
+ capsule->headersize = capsule->imagesize = sizeof(*capsule);
+ memcpy(&capsule->guid, &guid, sizeof(efi_guid_t));
+ capsule->flags = flags;
+
+ status = efi.query_capsule_caps(&capsule, 1, &max_size, reset);
+ if (status != EFI_SUCCESS) {
+ rv = efi_status_to_err(status);
+ goto out;
+ }
+
+ if (size > max_size)
+ rv = -ENOSPC;
+out:
+ kfree(capsule);
+ return rv;
+}
+EXPORT_SYMBOL_GPL(efi_capsule_supported);
+
+/*
+ * Every scatter gather list (block descriptor) page must end with a
+ * continuation pointer. The last continuation pointer of the last
+ * page must be zero to mark the end of the chain.
+ */
+#define SGLIST_PER_PAGE ((PAGE_SIZE / sizeof(efi_capsule_block_desc_t)) - 1)
+
+/*
+ * How many scatter gather list (block descriptor) pages do we need
+ * to map @count pages?
+ */
+static inline unsigned int sg_pages_num(unsigned int count)
+{
+ return DIV_ROUND_UP(count, SGLIST_PER_PAGE);
+}
+
+/**
+ * efi_capsule_update_locked - pass a single capsule to the firmware
+ * @capsule: capsule to send to the firmware
+ * @sg_pages: array of scatter gather (block descriptor) pages
+ * @reset: the reset type required for @capsule
+ *
+ * Since this function must be called under capsule_mutex check
+ * whether efi_reset_type will conflict with @reset, and atomically
+ * set it and capsule_pending if a capsule was successfully sent to
+ * the firmware.
+ *
+ * We also check to see if the system is about to restart, and if so,
+ * abort. This avoids races between efi_capsule_update() and
+ * efi_capsule_pending().
+ */
+static int
+efi_capsule_update_locked(efi_capsule_header_t *capsule,
+ struct page **sg_pages, int reset)
+{
+ efi_physical_addr_t sglist_phys;
+ efi_status_t status;
+
+ lockdep_assert_held(&capsule_mutex);
+
+ /*
+ * If someone has already registered a capsule that requires a
+ * different reset type, we're out of luck and must abort.
+ */
+ if (efi_reset_type >= 0 && efi_reset_type != reset) {
+ pr_err("Conflicting capsule reset type %d (%d).\n",
+ reset, efi_reset_type);
+ return -EINVAL;
+ }
+
+ /*
+ * If the system is getting ready to restart it may have
+ * called efi_capsule_pending() to make decisions (such as
+ * whether to force an EFI reboot), and we're racing against
+ * that call. Abort in that case.
+ */
+ if (unlikely(system_state == SYSTEM_RESTART)) {
+ pr_warn("Capsule update raced with reboot, aborting.\n");
+ return -EINVAL;
+ }
+
+ sglist_phys = page_to_phys(sg_pages[0]);
+
+ status = efi.update_capsule(&capsule, 1, sglist_phys);
+ if (status == EFI_SUCCESS) {
+ capsule_pending = true;
+ efi_reset_type = reset;
+ }
+
+ return efi_status_to_err(status);
+}
+
+/**
+ * efi_capsule_update - send a capsule to the firmware
+ * @capsule: capsule to send to firmware
+ * @pages: an array of capsule data pages
+ *
+ * Build a scatter gather list with EFI capsule block descriptors to
+ * map the capsule described by @capsule with its data in @pages and
+ * send it to the firmware via the UpdateCapsule() runtime service.
+ *
+ * @capsule must be a virtual mapping of the first page in @pages
+ * (@pages[0]) in the kernel address space. That is, a
+ * capsule_header_t that describes the entire contents of the capsule
+ * must be at the start of the first data page.
+ *
+ * Even though this function will validate that the firmware supports
+ * the capsule guid, users will likely want to check that
+ * efi_capsule_supported() returns true before calling this function
+ * because it makes it easier to print helpful error messages.
+ *
+ * If the capsule is successfully submitted to the firmware, any
+ * subsequent calls to efi_capsule_pending() will return true. @pages
+ * must not be released or modified if this function returns
+ * successfully.
+ *
+ * Callers must be prepared for this function to fail, which can
+ * happen if we raced with system reboot or if there is already a
+ * pending capsule that has a reset type that conflicts with the one
+ * required by @capsule. Do NOT use efi_capsule_pending() to detect
+ * this conflict since that would be racy. Instead, submit the capsule
+ * to efi_capsule_update() and check the return value.
+ *
+ * Return 0 on success, a converted EFI status code on failure.
+ */
+int efi_capsule_update(efi_capsule_header_t *capsule, struct page **pages)
+{
+ u32 imagesize = capsule->imagesize;
+ efi_guid_t guid = capsule->guid;
+ unsigned int count, sg_count;
+ u32 flags = capsule->flags;
+ struct page **sg_pages;
+ int rv, reset_type;
+ int i, j;
+
+ rv = efi_capsule_supported(guid, flags, imagesize, &reset_type);
+ if (rv)
+ return rv;
+
+ count = DIV_ROUND_UP(imagesize, PAGE_SIZE);
+ sg_count = sg_pages_num(count);
+
+ sg_pages = kzalloc(sg_count * sizeof(*sg_pages), GFP_KERNEL);
+ if (!sg_pages)
+ return -ENOMEM;
+
+ for (i = 0; i < sg_count; i++) {
+ sg_pages[i] = alloc_page(GFP_KERNEL);
+ if (!sg_pages[i]) {
+ rv = -ENOMEM;
+ goto out;
+ }
+ }
+
+ for (i = 0; i < sg_count; i++) {
+ efi_capsule_block_desc_t *sglist;
+
+ sglist = kmap(sg_pages[i]);
+ if (!sglist) {
+ rv = -ENOMEM;
+ goto out;
+ }
+
+ for (j = 0; j < SGLIST_PER_PAGE && count > 0; j++) {
+ u64 sz = min_t(u64, imagesize, PAGE_SIZE);
+
+ sglist[j].length = sz;
+ sglist[j].data = page_to_phys(*pages++);
+
+ imagesize -= sz;
+ count--;
+ }
+
+ /* Continuation pointer */
+ sglist[j].length = 0;
+
+ if (i + 1 == sg_count)
+ sglist[j].data = 0;
+ else
+ sglist[j].data = page_to_phys(sg_pages[i + 1]);
+
+ kunmap(sg_pages[i]);
+ }
+
+ mutex_lock(&capsule_mutex);
+ rv = efi_capsule_update_locked(capsule, sg_pages, reset_type);
+ mutex_unlock(&capsule_mutex);
+
+out:
+ for (i = 0; rv && i < sg_count; i++) {
+ if (sg_pages[i])
+ __free_page(sg_pages[i]);
+ }
+
+ kfree(sg_pages);
+ return rv;
+}
+EXPORT_SYMBOL_GPL(efi_capsule_update);
diff --git a/drivers/firmware/efi/reboot.c b/drivers/firmware/efi/reboot.c
index 9c59d1c795d1..62ead9b9d871 100644
--- a/drivers/firmware/efi/reboot.c
+++ b/drivers/firmware/efi/reboot.c
@@ -9,7 +9,8 @@ int efi_reboot_quirk_mode = -1;

void efi_reboot(enum reboot_mode reboot_mode, const char *__unused)
{
- int efi_mode;
+ const char *str[] = { "cold", "warm", "shutdown", "platform" };
+ int efi_mode, cap_reset_mode;

if (!efi_enabled(EFI_RUNTIME_SERVICES))
return;
@@ -30,6 +31,15 @@ void efi_reboot(enum reboot_mode reboot_mode, const char *__unused)
if (efi_reboot_quirk_mode != -1)
efi_mode = efi_reboot_quirk_mode;

+ if (efi_capsule_pending(&cap_reset_mode)) {
+ if (efi_mode != cap_reset_mode)
+ printk(KERN_CRIT "efi: %s reset requested but pending "
+ "capsule update requires %s reset... Performing "
+ "%s reset.\n", str[efi_mode], str[cap_reset_mode],
+ str[cap_reset_mode]);
+ efi_mode = cap_reset_mode;
+ }
+
efi.reset_system(efi_mode, EFI_SUCCESS, 0, NULL);
}

diff --git a/include/linux/efi.h b/include/linux/efi.h
index 9c8aae0e3b51..a28053e71e99 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -124,6 +124,13 @@ typedef struct {
} efi_capsule_header_t;

/*
+ * EFI capsule flags
+ */
+#define EFI_CAPSULE_PERSIST_ACROSS_RESET 0x00010000
+#define EFI_CAPSULE_POPULATE_SYSTEM_TABLE 0x00020000
+#define EFI_CAPSULE_INITIATE_RESET 0x00040000
+
+/*
* Allocation types for calls to boottime->allocate_pages.
*/
#define EFI_ALLOCATE_ANY_PAGES 0
@@ -1239,6 +1246,13 @@ int efivars_sysfs_init(void);
#define EFIVARS_DATA_SIZE_MAX 1024

#endif /* CONFIG_EFI_VARS */
+extern bool efi_capsule_pending(int *reset_type);
+
+extern int efi_capsule_supported(efi_guid_t guid, u32 flags,
+ size_t size, int *reset);
+
+extern int efi_capsule_update(efi_capsule_header_t *capsule,
+ struct page **pages);

#ifdef CONFIG_EFI_RUNTIME_MAP
int efi_runtime_map_init(struct kobject *);
--
2.6.2

2016-03-17 12:57:49

by Matt Fleming

[permalink] [raw]
Subject: [PATCH 4/4] efi: A misc char interface to update EFI firmware

From: "Kweh, Hock Leong" <[email protected]>

This patch introduces a kernel module to expose a capsule loader
interface (misc char device file note) for users to upload capsule
binaries.

Example:
cat firmware.bin > /dev/efi_capsule_loader

Any upload error will be returned while doing "cat" through file
operation write() function call.

Tested with Intel Quark Galileo GEN1 platform.

Signed-off-by: "Kweh, Hock Leong" <[email protected]>
Acked-by: Greg Kroah-Hartman <[email protected]>
Cc: Bryan O'Donoghue <[email protected]>
Cc: Borislav Petkov <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Sam Protsenko <[email protected]>
Cc: Peter Jones <[email protected]>
Cc: Ard Biesheuvel <[email protected]>
[ Update comments and Kconfig text ]
Signed-off-by: Matt Fleming <[email protected]>
---
drivers/firmware/efi/Kconfig | 10 +
drivers/firmware/efi/Makefile | 1 +
drivers/firmware/efi/capsule-loader.c | 343 ++++++++++++++++++++++++++++++++++
3 files changed, 354 insertions(+)
create mode 100644 drivers/firmware/efi/capsule-loader.c

diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
index e1670d533f97..de221bbde9c9 100644
--- a/drivers/firmware/efi/Kconfig
+++ b/drivers/firmware/efi/Kconfig
@@ -87,6 +87,16 @@ config EFI_RUNTIME_WRAPPERS
config EFI_ARMSTUB
bool

+config EFI_CAPSULE_LOADER
+ tristate "EFI capsule loader"
+ depends on EFI
+ help
+ This option exposes a loader interface "/dev/efi_capsule_loader" for
+ users to load EFI capsules. This driver requires working runtime
+ capsule support in the firmware, which many OEMs do not provide.
+
+ Most users should say N.
+
endmenu

config UEFI_CPER
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
index e4468c7c16b5..a0f99f51626f 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -22,3 +22,4 @@ obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o
arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o
obj-$(CONFIG_ARM) += $(arm-obj-y)
obj-$(CONFIG_ARM64) += $(arm-obj-y)
+obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o
diff --git a/drivers/firmware/efi/capsule-loader.c b/drivers/firmware/efi/capsule-loader.c
new file mode 100644
index 000000000000..c99c24bc79b0
--- /dev/null
+++ b/drivers/firmware/efi/capsule-loader.c
@@ -0,0 +1,343 @@
+/*
+ * EFI capsule loader driver.
+ *
+ * Copyright 2015 Intel Corporation
+ *
+ * This file is part of the Linux kernel, and is made available under
+ * the terms of the GNU General Public License version 2.
+ */
+
+#define pr_fmt(fmt) "efi: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/highmem.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/efi.h>
+
+#define NO_FURTHER_WRITE_ACTION -1
+
+struct capsule_info {
+ bool header_obtained;
+ int reset_type;
+ long index;
+ size_t count;
+ size_t total_size;
+ struct page **pages;
+ size_t page_bytes_remain;
+};
+
+/**
+ * efi_free_all_buff_pages - free all previous allocated buffer pages
+ * @cap_info: pointer to current instance of capsule_info structure
+ *
+ * In addition to freeing buffer pages, it flags NO_FURTHER_WRITE_ACTION
+ * to cease processing data in subsequent write(2) calls until close(2)
+ * is called.
+ **/
+static void efi_free_all_buff_pages(struct capsule_info *cap_info)
+{
+ while (cap_info->index > 0)
+ __free_page(cap_info->pages[--cap_info->index]);
+
+ cap_info->index = NO_FURTHER_WRITE_ACTION;
+}
+
+/**
+ * efi_capsule_setup_info - obtain the efi capsule header in the binary and
+ * setup capsule_info structure
+ * @cap_info: pointer to current instance of capsule_info structure
+ * @kbuff: a mapped first page buffer pointer
+ * @hdr_bytes: the total received number of bytes for efi header
+ **/
+static ssize_t efi_capsule_setup_info(struct capsule_info *cap_info,
+ void *kbuff, size_t hdr_bytes)
+{
+ efi_capsule_header_t *cap_hdr;
+ size_t pages_needed;
+ int ret;
+ void *temp_page;
+
+ /* Only process data block that is larger than efi header size */
+ if (hdr_bytes < sizeof(efi_capsule_header_t))
+ return 0;
+
+ /* Reset back to the correct offset of header */
+ cap_hdr = kbuff - cap_info->count;
+ pages_needed = ALIGN(cap_hdr->imagesize, PAGE_SIZE) >> PAGE_SHIFT;
+
+ if (pages_needed == 0) {
+ pr_err("%s: pages count invalid\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Check if the capsule binary supported */
+ ret = efi_capsule_supported(cap_hdr->guid, cap_hdr->flags,
+ cap_hdr->imagesize,
+ &cap_info->reset_type);
+ if (ret) {
+ pr_err("%s: efi_capsule_supported() failed\n",
+ __func__);
+ return ret;
+ }
+
+ cap_info->total_size = cap_hdr->imagesize;
+ temp_page = krealloc(cap_info->pages,
+ pages_needed * sizeof(void *),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!temp_page) {
+ pr_debug("%s: krealloc() failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ cap_info->pages = temp_page;
+ cap_info->header_obtained = true;
+
+ return 0;
+}
+
+/**
+ * efi_capsule_submit_update - invoke the efi_capsule_update API once binary
+ * upload done
+ * @cap_info: pointer to current instance of capsule_info structure
+ **/
+static ssize_t efi_capsule_submit_update(struct capsule_info *cap_info)
+{
+ int ret;
+ void *cap_hdr_temp;
+
+ cap_hdr_temp = kmap(cap_info->pages[0]);
+ if (!cap_hdr_temp) {
+ pr_debug("%s: kmap() failed\n", __func__);
+ return -EFAULT;
+ }
+
+ ret = efi_capsule_update(cap_hdr_temp, cap_info->pages);
+ kunmap(cap_info->pages[0]);
+ if (ret) {
+ pr_err("%s: efi_capsule_update() failed\n", __func__);
+ return ret;
+ }
+
+ /* Indicate capsule binary uploading is done */
+ cap_info->index = NO_FURTHER_WRITE_ACTION;
+ pr_info("%s: Successfully upload capsule file with reboot type '%s'\n",
+ __func__, !cap_info->reset_type ? "RESET_COLD" :
+ cap_info->reset_type == 1 ? "RESET_WARM" :
+ "RESET_SHUTDOWN");
+ return 0;
+}
+
+/**
+ * efi_capsule_write - store the capsule binary and pass it to
+ * efi_capsule_update() API
+ * @file: file pointer
+ * @buff: buffer pointer
+ * @count: number of bytes in @buff
+ * @offp: not used
+ *
+ * Expectation:
+ * - A user space tool should start at the beginning of capsule binary and
+ * pass data in sequentially.
+ * - Users should close and re-open this file note in order to upload more
+ * capsules.
+ * - After an error returned, user should close the file and restart the
+ * operation for the next try otherwise -EIO will be returned until the
+ * file is closed.
+ * - An EFI capsule header must be located at the beginning of capsule
+ * binary file and passed in as first block data of write operation.
+ **/
+static ssize_t efi_capsule_write(struct file *file, const char __user *buff,
+ size_t count, loff_t *offp)
+{
+ int ret = 0;
+ struct capsule_info *cap_info = file->private_data;
+ struct page *page;
+ void *kbuff = NULL;
+ size_t write_byte;
+
+ if (count == 0)
+ return 0;
+
+ /* Return error while NO_FURTHER_WRITE_ACTION is flagged */
+ if (cap_info->index < 0)
+ return -EIO;
+
+ /* Only alloc a new page when previous page is full */
+ if (!cap_info->page_bytes_remain) {
+ page = alloc_page(GFP_KERNEL);
+ if (!page) {
+ pr_debug("%s: alloc_page() failed\n", __func__);
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ cap_info->pages[cap_info->index++] = page;
+ cap_info->page_bytes_remain = PAGE_SIZE;
+ }
+
+ page = cap_info->pages[cap_info->index - 1];
+
+ kbuff = kmap(page);
+ if (!kbuff) {
+ pr_debug("%s: kmap() failed\n", __func__);
+ ret = -EFAULT;
+ goto failed;
+ }
+ kbuff += PAGE_SIZE - cap_info->page_bytes_remain;
+
+ /* Copy capsule binary data from user space to kernel space buffer */
+ write_byte = min_t(size_t, count, cap_info->page_bytes_remain);
+ if (copy_from_user(kbuff, buff, write_byte)) {
+ pr_debug("%s: copy_from_user() failed\n", __func__);
+ ret = -EFAULT;
+ goto fail_unmap;
+ }
+ cap_info->page_bytes_remain -= write_byte;
+
+ /* Setup capsule binary info structure */
+ if (!cap_info->header_obtained) {
+ ret = efi_capsule_setup_info(cap_info, kbuff,
+ cap_info->count + write_byte);
+ if (ret)
+ goto fail_unmap;
+ }
+
+ cap_info->count += write_byte;
+ kunmap(page);
+
+ /* Submit the full binary to efi_capsule_update() API */
+ if (cap_info->header_obtained &&
+ cap_info->count >= cap_info->total_size) {
+ if (cap_info->count > cap_info->total_size) {
+ pr_err("%s: upload size exceeded header defined size\n",
+ __func__);
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ ret = efi_capsule_submit_update(cap_info);
+ if (ret)
+ goto failed;
+ }
+
+ return write_byte;
+
+fail_unmap:
+ kunmap(page);
+failed:
+ efi_free_all_buff_pages(cap_info);
+ return ret;
+}
+
+/**
+ * efi_capsule_flush - called by file close or file flush
+ * @file: file pointer
+ * @id: not used
+ *
+ * If a capsule is being partially uploaded then calling this function
+ * will be treated as upload termination and will free those completed
+ * buffer pages and -ECANCELED will be returned.
+ **/
+static int efi_capsule_flush(struct file *file, fl_owner_t id)
+{
+ int ret = 0;
+ struct capsule_info *cap_info = file->private_data;
+
+ if (cap_info->index > 0) {
+ pr_err("%s: capsule upload not complete\n", __func__);
+ efi_free_all_buff_pages(cap_info);
+ ret = -ECANCELED;
+ }
+
+ return ret;
+}
+
+/**
+ * efi_capsule_release - called by file close
+ * @inode: not used
+ * @file: file pointer
+ *
+ * We will not free successfully submitted pages since efi update
+ * requires data to be maintained across system reboot.
+ **/
+static int efi_capsule_release(struct inode *inode, struct file *file)
+{
+ struct capsule_info *cap_info = file->private_data;
+
+ kfree(cap_info->pages);
+ kfree(file->private_data);
+ file->private_data = NULL;
+ return 0;
+}
+
+/**
+ * efi_capsule_open - called by file open
+ * @inode: not used
+ * @file: file pointer
+ *
+ * Will allocate each capsule_info memory for each file open call.
+ * This provided the capability to support multiple file open feature
+ * where user is not needed to wait for others to finish in order to
+ * upload their capsule binary.
+ **/
+static int efi_capsule_open(struct inode *inode, struct file *file)
+{
+ struct capsule_info *cap_info;
+
+ cap_info = kzalloc(sizeof(*cap_info), GFP_KERNEL);
+ if (!cap_info)
+ return -ENOMEM;
+
+ cap_info->pages = kzalloc(sizeof(void *), GFP_KERNEL);
+ if (!cap_info->pages) {
+ kfree(cap_info);
+ return -ENOMEM;
+ }
+
+ file->private_data = cap_info;
+
+ return 0;
+}
+
+static const struct file_operations efi_capsule_fops = {
+ .owner = THIS_MODULE,
+ .open = efi_capsule_open,
+ .write = efi_capsule_write,
+ .flush = efi_capsule_flush,
+ .release = efi_capsule_release,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice efi_capsule_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "efi_capsule_loader",
+ .fops = &efi_capsule_fops,
+};
+
+static int __init efi_capsule_loader_init(void)
+{
+ int ret;
+
+ if (!efi_enabled(EFI_RUNTIME_SERVICES))
+ return -ENODEV;
+
+ ret = misc_register(&efi_capsule_misc);
+ if (ret)
+ pr_err("%s: Failed to register misc char file note\n",
+ __func__);
+
+ return ret;
+}
+module_init(efi_capsule_loader_init);
+
+static void __exit efi_capsule_loader_exit(void)
+{
+ misc_deregister(&efi_capsule_misc);
+}
+module_exit(efi_capsule_loader_exit);
+
+MODULE_DESCRIPTION("EFI capsule firmware binary loader");
+MODULE_LICENSE("GPL v2");
--
2.6.2

2016-03-17 12:57:22

by Matt Fleming

[permalink] [raw]
Subject: [PATCH 1/4] efi: Move efi_status_to_err() to drivers/firmware/efi/

Move efi_status_to_err() to the architecture independent code as it's
generally useful in all bits of EFI code where there is a need to
convert an efi_status_t to a kernel error value.

Cc: Kweh Hock Leong <[email protected]>
Cc: Borislav Petkov <[email protected]>
Cc: Ard Biesheuvel <[email protected]>
Signed-off-by: Matt Fleming <[email protected]>
---
drivers/firmware/efi/efi.c | 33 +++++++++++++++++++++++++++++++++
drivers/firmware/efi/vars.c | 33 ---------------------------------
include/linux/efi.h | 2 ++
3 files changed, 35 insertions(+), 33 deletions(-)

diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 3a69ed5ecfcb..b73222a52a9d 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -637,3 +637,36 @@ u64 __weak efi_mem_attributes(unsigned long phys_addr)
}
return 0;
}
+
+int efi_status_to_err(efi_status_t status)
+{
+ int err;
+
+ switch (status) {
+ case EFI_SUCCESS:
+ err = 0;
+ break;
+ case EFI_INVALID_PARAMETER:
+ err = -EINVAL;
+ break;
+ case EFI_OUT_OF_RESOURCES:
+ err = -ENOSPC;
+ break;
+ case EFI_DEVICE_ERROR:
+ err = -EIO;
+ break;
+ case EFI_WRITE_PROTECTED:
+ err = -EROFS;
+ break;
+ case EFI_SECURITY_VIOLATION:
+ err = -EACCES;
+ break;
+ case EFI_NOT_FOUND:
+ err = -ENOENT;
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ return err;
+}
diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
index 0ac594c0a234..4911d5697844 100644
--- a/drivers/firmware/efi/vars.c
+++ b/drivers/firmware/efi/vars.c
@@ -314,39 +314,6 @@ check_var_size_nonblocking(u32 attributes, unsigned long size)
return fops->query_variable_store(attributes, size, true);
}

-static int efi_status_to_err(efi_status_t status)
-{
- int err;
-
- switch (status) {
- case EFI_SUCCESS:
- err = 0;
- break;
- case EFI_INVALID_PARAMETER:
- err = -EINVAL;
- break;
- case EFI_OUT_OF_RESOURCES:
- err = -ENOSPC;
- break;
- case EFI_DEVICE_ERROR:
- err = -EIO;
- break;
- case EFI_WRITE_PROTECTED:
- err = -EROFS;
- break;
- case EFI_SECURITY_VIOLATION:
- err = -EACCES;
- break;
- case EFI_NOT_FOUND:
- err = -ENOENT;
- break;
- default:
- err = -EINVAL;
- }
-
- return err;
-}
-
static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor,
struct list_head *head)
{
diff --git a/include/linux/efi.h b/include/linux/efi.h
index c55c452b991a..9c8aae0e3b51 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -1023,6 +1023,8 @@ static inline void
efi_reboot(enum reboot_mode reboot_mode, const char *__unused) {}
#endif

+extern int efi_status_to_err(efi_status_t status);
+
/*
* Variable Attributes
*/
--
2.6.2

2016-03-21 09:43:20

by Ard Biesheuvel

[permalink] [raw]
Subject: Re: [PATCH 1/4] efi: Move efi_status_to_err() to drivers/firmware/efi/

On 17 March 2016 at 13:57, Matt Fleming <[email protected]> wrote:
> Move efi_status_to_err() to the architecture independent code as it's
> generally useful in all bits of EFI code where there is a need to
> convert an efi_status_t to a kernel error value.
>
> Cc: Kweh Hock Leong <[email protected]>
> Cc: Borislav Petkov <[email protected]>
> Cc: Ard Biesheuvel <[email protected]>
> Signed-off-by: Matt Fleming <[email protected]>

Acked-by: Ard Biesheuvel <[email protected]>

> ---
> drivers/firmware/efi/efi.c | 33 +++++++++++++++++++++++++++++++++
> drivers/firmware/efi/vars.c | 33 ---------------------------------
> include/linux/efi.h | 2 ++
> 3 files changed, 35 insertions(+), 33 deletions(-)
>
> diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
> index 3a69ed5ecfcb..b73222a52a9d 100644
> --- a/drivers/firmware/efi/efi.c
> +++ b/drivers/firmware/efi/efi.c
> @@ -637,3 +637,36 @@ u64 __weak efi_mem_attributes(unsigned long phys_addr)
> }
> return 0;
> }
> +
> +int efi_status_to_err(efi_status_t status)
> +{
> + int err;
> +
> + switch (status) {
> + case EFI_SUCCESS:
> + err = 0;
> + break;
> + case EFI_INVALID_PARAMETER:
> + err = -EINVAL;
> + break;
> + case EFI_OUT_OF_RESOURCES:
> + err = -ENOSPC;
> + break;
> + case EFI_DEVICE_ERROR:
> + err = -EIO;
> + break;
> + case EFI_WRITE_PROTECTED:
> + err = -EROFS;
> + break;
> + case EFI_SECURITY_VIOLATION:
> + err = -EACCES;
> + break;
> + case EFI_NOT_FOUND:
> + err = -ENOENT;
> + break;
> + default:
> + err = -EINVAL;
> + }
> +
> + return err;
> +}
> diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
> index 0ac594c0a234..4911d5697844 100644
> --- a/drivers/firmware/efi/vars.c
> +++ b/drivers/firmware/efi/vars.c
> @@ -314,39 +314,6 @@ check_var_size_nonblocking(u32 attributes, unsigned long size)
> return fops->query_variable_store(attributes, size, true);
> }
>
> -static int efi_status_to_err(efi_status_t status)
> -{
> - int err;
> -
> - switch (status) {
> - case EFI_SUCCESS:
> - err = 0;
> - break;
> - case EFI_INVALID_PARAMETER:
> - err = -EINVAL;
> - break;
> - case EFI_OUT_OF_RESOURCES:
> - err = -ENOSPC;
> - break;
> - case EFI_DEVICE_ERROR:
> - err = -EIO;
> - break;
> - case EFI_WRITE_PROTECTED:
> - err = -EROFS;
> - break;
> - case EFI_SECURITY_VIOLATION:
> - err = -EACCES;
> - break;
> - case EFI_NOT_FOUND:
> - err = -ENOENT;
> - break;
> - default:
> - err = -EINVAL;
> - }
> -
> - return err;
> -}
> -
> static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor,
> struct list_head *head)
> {
> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index c55c452b991a..9c8aae0e3b51 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -1023,6 +1023,8 @@ static inline void
> efi_reboot(enum reboot_mode reboot_mode, const char *__unused) {}
> #endif
>
> +extern int efi_status_to_err(efi_status_t status);
> +
> /*
> * Variable Attributes
> */
> --
> 2.6.2
>

2016-03-21 10:20:02

by Ard Biesheuvel

[permalink] [raw]
Subject: Re: [PATCH 2/4] efi: Capsule update support

On 17 March 2016 at 13:57, Matt Fleming <[email protected]> wrote:
> The EFI capsule mechanism allows data blobs to be passed to the EFI
> firmware. A common use case is performing firmware updates. This patch
> just introduces the main infrastructure for interacting with the
> firmware, and a driver that allows users to upload capsules will come
> in a later patch.
>
> Once a capsule has been passed to the firmware, the next reboot must
> be performed using the ResetSystem() EFI runtime service, which may
> involve overriding the reboot type specified by reboot=. This ensures
> the reset value returned by QueryCapsuleCapabilities() is used to
> reset the system, which is required for the capsule to be processed.
> efi_capsule_pending() is provided for this purpose.
>
> At the moment we only allow a single capsule blob to be sent to the
> firmware despite the fact that UpdateCapsule() takes a 'CapsuleCount'
> parameter. This simplifies the API and shouldn't result in any
> downside since it is still possible to send multiple capsules by
> repeatedly calling UpdateCapsule().
>
> Cc: Kweh Hock Leong <[email protected]>
> Cc: Borislav Petkov <[email protected]>
> Cc: Ard Biesheuvel <[email protected]>
> Cc: Mark Salter <[email protected]>
> Cc: Peter Jones <[email protected]>
> Cc: joeyli <[email protected]>
> Cc: Bryan O'Donoghue <[email protected]>
> Signed-off-by: Matt Fleming <[email protected]>

How are capsules with the CAPSULE_FLAGS_INITIATE_RESET flag handled?
The runtime service will never return in that case, so I suppose we
need some explicit handling somewhere?

> ---
> drivers/firmware/efi/Makefile | 2 +-
> drivers/firmware/efi/capsule.c | 286 +++++++++++++++++++++++++++++++++++++++++
> drivers/firmware/efi/reboot.c | 12 +-
> include/linux/efi.h | 14 ++
> 4 files changed, 312 insertions(+), 2 deletions(-)
> create mode 100644 drivers/firmware/efi/capsule.c
>
> diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
> index 62e654f255f4..e4468c7c16b5 100644
> --- a/drivers/firmware/efi/Makefile
> +++ b/drivers/firmware/efi/Makefile
> @@ -9,7 +9,7 @@
> #
> KASAN_SANITIZE_runtime-wrappers.o := n
>
> -obj-$(CONFIG_EFI) += efi.o vars.o reboot.o
> +obj-$(CONFIG_EFI) += efi.o vars.o reboot.o capsule.o
> obj-$(CONFIG_EFI_VARS) += efivars.o
> obj-$(CONFIG_EFI_ESRT) += esrt.o
> obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o
> diff --git a/drivers/firmware/efi/capsule.c b/drivers/firmware/efi/capsule.c
> new file mode 100644
> index 000000000000..dac25208ad5e
> --- /dev/null
> +++ b/drivers/firmware/efi/capsule.c
> @@ -0,0 +1,286 @@
> +/*
> + * EFI capsule support.
> + *
> + * Copyright 2013 Intel Corporation; author Matt Fleming
> + *
> + * This file is part of the Linux kernel, and is made available under
> + * the terms of the GNU General Public License version 2.
> + */
> +
> +#define pr_fmt(fmt) "efi: " fmt
> +
> +#include <linux/slab.h>
> +#include <linux/mutex.h>
> +#include <linux/highmem.h>
> +#include <linux/efi.h>
> +#include <linux/vmalloc.h>
> +#include <asm/io.h>
> +
> +typedef struct {
> + u64 length;
> + u64 data;
> +} efi_capsule_block_desc_t;
> +
> +static bool capsule_pending;
> +static int efi_reset_type = -1;
> +
> +/*
> + * capsule_mutex serialises access to both capsule_pending and
> + * efi_reset_type.
> + */
> +static DEFINE_MUTEX(capsule_mutex);
> +
> +/**
> + * efi_capsule_pending - has a capsule been passed to the firmware?
> + * @reset_type: store the type of EFI reset if capsule is pending
> + *
> + * To ensure that the registered capsule is processed correctly by the
> + * firmware we need to perform a specific type of reset. If a capsule is
> + * pending return the reset type in @reset_type.
> + *
> + * This function will race with callers of efi_capsule_update(), for
> + * example, calling this function while somebody else is in
> + * efi_capsule_update() but hasn't reached efi_capsue_update_locked()
> + * will miss the updates to capsule_pending and efi_reset_type after
> + * efi_capsule_update_locked() completes.
> + *
> + * A non-racy use is from platform reboot code because we use
> + * system_state to ensure no capsules can be sent to the firmware once
> + * we're at SYSTEM_RESTART. See efi_capsule_update_locked().
> + */
> +bool efi_capsule_pending(int *reset_type)
> +{
> + bool rv = false;
> +
> + mutex_lock(&capsule_mutex);
> + if (!capsule_pending)
> + goto out;
> +
> + if (reset_type)
> + *reset_type = efi_reset_type;
> + rv = true;
> +out:
> + mutex_unlock(&capsule_mutex);
> + return rv;
> +}
> +
> +/**
> + * efi_capsule_supported - does the firmware support the capsule?
> + * @guid: vendor guid of capsule
> + * @flags: capsule flags
> + * @size: size of capsule data
> + * @reset: the reset type required for this capsule
> + *
> + * Check whether a capsule with @flags is supported by the firmware
> + * and that @size doesn't exceed the maximum size for a capsule.
> + *
> + * No attempt is made to check @reset against the reset type required
> + * by any pending capsules because of the races involved.
> + */
> +int efi_capsule_supported(efi_guid_t guid, u32 flags, size_t size, int *reset)
> +{
> + efi_capsule_header_t *capsule;
> + efi_status_t status;
> + u64 max_size;
> + int rv = 0;
> +
> + capsule = kmalloc(sizeof(*capsule), GFP_KERNEL);
> + if (!capsule)
> + return -ENOMEM;
> +
> + capsule->headersize = capsule->imagesize = sizeof(*capsule);
> + memcpy(&capsule->guid, &guid, sizeof(efi_guid_t));
> + capsule->flags = flags;
> +
> + status = efi.query_capsule_caps(&capsule, 1, &max_size, reset);
> + if (status != EFI_SUCCESS) {
> + rv = efi_status_to_err(status);
> + goto out;
> + }
> +
> + if (size > max_size)
> + rv = -ENOSPC;
> +out:
> + kfree(capsule);
> + return rv;
> +}
> +EXPORT_SYMBOL_GPL(efi_capsule_supported);
> +
> +/*
> + * Every scatter gather list (block descriptor) page must end with a
> + * continuation pointer. The last continuation pointer of the last
> + * page must be zero to mark the end of the chain.
> + */
> +#define SGLIST_PER_PAGE ((PAGE_SIZE / sizeof(efi_capsule_block_desc_t)) - 1)
> +
> +/*
> + * How many scatter gather list (block descriptor) pages do we need
> + * to map @count pages?
> + */
> +static inline unsigned int sg_pages_num(unsigned int count)
> +{
> + return DIV_ROUND_UP(count, SGLIST_PER_PAGE);
> +}
> +
> +/**
> + * efi_capsule_update_locked - pass a single capsule to the firmware
> + * @capsule: capsule to send to the firmware
> + * @sg_pages: array of scatter gather (block descriptor) pages
> + * @reset: the reset type required for @capsule
> + *
> + * Since this function must be called under capsule_mutex check
> + * whether efi_reset_type will conflict with @reset, and atomically
> + * set it and capsule_pending if a capsule was successfully sent to
> + * the firmware.
> + *
> + * We also check to see if the system is about to restart, and if so,
> + * abort. This avoids races between efi_capsule_update() and
> + * efi_capsule_pending().
> + */
> +static int
> +efi_capsule_update_locked(efi_capsule_header_t *capsule,
> + struct page **sg_pages, int reset)
> +{
> + efi_physical_addr_t sglist_phys;
> + efi_status_t status;
> +
> + lockdep_assert_held(&capsule_mutex);
> +
> + /*
> + * If someone has already registered a capsule that requires a
> + * different reset type, we're out of luck and must abort.
> + */
> + if (efi_reset_type >= 0 && efi_reset_type != reset) {
> + pr_err("Conflicting capsule reset type %d (%d).\n",
> + reset, efi_reset_type);
> + return -EINVAL;
> + }
> +
> + /*
> + * If the system is getting ready to restart it may have
> + * called efi_capsule_pending() to make decisions (such as
> + * whether to force an EFI reboot), and we're racing against
> + * that call. Abort in that case.
> + */
> + if (unlikely(system_state == SYSTEM_RESTART)) {
> + pr_warn("Capsule update raced with reboot, aborting.\n");
> + return -EINVAL;
> + }
> +
> + sglist_phys = page_to_phys(sg_pages[0]);
> +
> + status = efi.update_capsule(&capsule, 1, sglist_phys);
> + if (status == EFI_SUCCESS) {
> + capsule_pending = true;
> + efi_reset_type = reset;
> + }
> +
> + return efi_status_to_err(status);
> +}
> +
> +/**
> + * efi_capsule_update - send a capsule to the firmware
> + * @capsule: capsule to send to firmware
> + * @pages: an array of capsule data pages
> + *
> + * Build a scatter gather list with EFI capsule block descriptors to
> + * map the capsule described by @capsule with its data in @pages and
> + * send it to the firmware via the UpdateCapsule() runtime service.
> + *
> + * @capsule must be a virtual mapping of the first page in @pages
> + * (@pages[0]) in the kernel address space. That is, a
> + * capsule_header_t that describes the entire contents of the capsule
> + * must be at the start of the first data page.
> + *
> + * Even though this function will validate that the firmware supports
> + * the capsule guid, users will likely want to check that
> + * efi_capsule_supported() returns true before calling this function
> + * because it makes it easier to print helpful error messages.
> + *
> + * If the capsule is successfully submitted to the firmware, any
> + * subsequent calls to efi_capsule_pending() will return true. @pages
> + * must not be released or modified if this function returns
> + * successfully.
> + *
> + * Callers must be prepared for this function to fail, which can
> + * happen if we raced with system reboot or if there is already a
> + * pending capsule that has a reset type that conflicts with the one
> + * required by @capsule. Do NOT use efi_capsule_pending() to detect
> + * this conflict since that would be racy. Instead, submit the capsule
> + * to efi_capsule_update() and check the return value.
> + *
> + * Return 0 on success, a converted EFI status code on failure.
> + */
> +int efi_capsule_update(efi_capsule_header_t *capsule, struct page **pages)
> +{
> + u32 imagesize = capsule->imagesize;
> + efi_guid_t guid = capsule->guid;
> + unsigned int count, sg_count;
> + u32 flags = capsule->flags;
> + struct page **sg_pages;
> + int rv, reset_type;
> + int i, j;
> +
> + rv = efi_capsule_supported(guid, flags, imagesize, &reset_type);
> + if (rv)
> + return rv;
> +
> + count = DIV_ROUND_UP(imagesize, PAGE_SIZE);
> + sg_count = sg_pages_num(count);
> +
> + sg_pages = kzalloc(sg_count * sizeof(*sg_pages), GFP_KERNEL);
> + if (!sg_pages)
> + return -ENOMEM;
> +
> + for (i = 0; i < sg_count; i++) {
> + sg_pages[i] = alloc_page(GFP_KERNEL);
> + if (!sg_pages[i]) {
> + rv = -ENOMEM;
> + goto out;
> + }
> + }
> +
> + for (i = 0; i < sg_count; i++) {
> + efi_capsule_block_desc_t *sglist;
> +
> + sglist = kmap(sg_pages[i]);
> + if (!sglist) {
> + rv = -ENOMEM;
> + goto out;
> + }
> +
> + for (j = 0; j < SGLIST_PER_PAGE && count > 0; j++) {
> + u64 sz = min_t(u64, imagesize, PAGE_SIZE);
> +
> + sglist[j].length = sz;
> + sglist[j].data = page_to_phys(*pages++);
> +
> + imagesize -= sz;
> + count--;
> + }
> +
> + /* Continuation pointer */
> + sglist[j].length = 0;
> +
> + if (i + 1 == sg_count)
> + sglist[j].data = 0;
> + else
> + sglist[j].data = page_to_phys(sg_pages[i + 1]);
> +
> + kunmap(sg_pages[i]);
> + }
> +
> + mutex_lock(&capsule_mutex);
> + rv = efi_capsule_update_locked(capsule, sg_pages, reset_type);
> + mutex_unlock(&capsule_mutex);
> +
> +out:
> + for (i = 0; rv && i < sg_count; i++) {
> + if (sg_pages[i])
> + __free_page(sg_pages[i]);
> + }
> +
> + kfree(sg_pages);
> + return rv;
> +}
> +EXPORT_SYMBOL_GPL(efi_capsule_update);
> diff --git a/drivers/firmware/efi/reboot.c b/drivers/firmware/efi/reboot.c
> index 9c59d1c795d1..62ead9b9d871 100644
> --- a/drivers/firmware/efi/reboot.c
> +++ b/drivers/firmware/efi/reboot.c
> @@ -9,7 +9,8 @@ int efi_reboot_quirk_mode = -1;
>
> void efi_reboot(enum reboot_mode reboot_mode, const char *__unused)
> {
> - int efi_mode;
> + const char *str[] = { "cold", "warm", "shutdown", "platform" };
> + int efi_mode, cap_reset_mode;
>
> if (!efi_enabled(EFI_RUNTIME_SERVICES))
> return;
> @@ -30,6 +31,15 @@ void efi_reboot(enum reboot_mode reboot_mode, const char *__unused)
> if (efi_reboot_quirk_mode != -1)
> efi_mode = efi_reboot_quirk_mode;
>
> + if (efi_capsule_pending(&cap_reset_mode)) {
> + if (efi_mode != cap_reset_mode)
> + printk(KERN_CRIT "efi: %s reset requested but pending "
> + "capsule update requires %s reset... Performing "
> + "%s reset.\n", str[efi_mode], str[cap_reset_mode],
> + str[cap_reset_mode]);
> + efi_mode = cap_reset_mode;
> + }
> +
> efi.reset_system(efi_mode, EFI_SUCCESS, 0, NULL);
> }
>
> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index 9c8aae0e3b51..a28053e71e99 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -124,6 +124,13 @@ typedef struct {
> } efi_capsule_header_t;
>
> /*
> + * EFI capsule flags
> + */
> +#define EFI_CAPSULE_PERSIST_ACROSS_RESET 0x00010000
> +#define EFI_CAPSULE_POPULATE_SYSTEM_TABLE 0x00020000
> +#define EFI_CAPSULE_INITIATE_RESET 0x00040000
> +
> +/*
> * Allocation types for calls to boottime->allocate_pages.
> */
> #define EFI_ALLOCATE_ANY_PAGES 0
> @@ -1239,6 +1246,13 @@ int efivars_sysfs_init(void);
> #define EFIVARS_DATA_SIZE_MAX 1024
>
> #endif /* CONFIG_EFI_VARS */
> +extern bool efi_capsule_pending(int *reset_type);
> +
> +extern int efi_capsule_supported(efi_guid_t guid, u32 flags,
> + size_t size, int *reset);
> +
> +extern int efi_capsule_update(efi_capsule_header_t *capsule,
> + struct page **pages);
>
> #ifdef CONFIG_EFI_RUNTIME_MAP
> int efi_runtime_map_init(struct kobject *);
> --
> 2.6.2
>

2016-03-21 20:32:06

by Matt Fleming

[permalink] [raw]
Subject: Re: [PATCH 2/4] efi: Capsule update support

On Mon, 21 Mar, at 11:19:50AM, Ard Biesheuvel wrote:
>
> How are capsules with the CAPSULE_FLAGS_INITIATE_RESET flag handled?
> The runtime service will never return in that case, so I suppose we
> need some explicit handling somewhere?

Good question. They're not handled in any special way with this patch
series, so the firmware will just initiate its own reset inside of
UpdateCapsule().

That's probably not what we want, because things like on-disk
consistency are not guaranteed if the machine spontaneously reboots
without assistance from the kernel.

The simplest thing to do is to refuse to pass such capsules to the
firmware, since it's likely not going to be a common use case. But
maybe that's overly restrictive.

Let me have a think about that one.

2016-03-29 12:27:05

by Matt Fleming

[permalink] [raw]
Subject: Re: [PATCH 2/4] efi: Capsule update support

On Mon, 21 Mar, at 08:31:59PM, Matt Fleming wrote:
>
> Good question. They're not handled in any special way with this patch
> series, so the firmware will just initiate its own reset inside of
> UpdateCapsule().
>
> That's probably not what we want, because things like on-disk
> consistency are not guaranteed if the machine spontaneously reboots
> without assistance from the kernel.
>
> The simplest thing to do is to refuse to pass such capsules to the
> firmware, since it's likely not going to be a common use case. But
> maybe that's overly restrictive.
>
> Let me have a think about that one.

OK, I did think about this, and until someone actually requests the
ability to handle CAPSULE_FLAGS_INITIATE_RESET, I'm happy to just punt
on the problem. Anyone got any objections?

---

diff --git a/drivers/firmware/efi/capsule.c b/drivers/firmware/efi/capsule.c
index dac25208ad5e..84450e9cdf41 100644
--- a/drivers/firmware/efi/capsule.c
+++ b/drivers/firmware/efi/capsule.c
@@ -84,6 +84,14 @@ int efi_capsule_supported(efi_guid_t guid, u32 flags, size_t size, int *reset)
u64 max_size;
int rv = 0;

+ /*
+ * We do not handle firmware-initiated reset because that
+ * would require us to prepare the kernel for reboot. Refuse
+ * to load any capsules with that flag.
+ */
+ if (flags & EFI_CAPSULE_INITIATE_RESET)
+ return -EINVAL;
+
capsule = kmalloc(sizeof(*capsule), GFP_KERNEL);
if (!capsule)
return -ENOMEM;

2016-03-29 13:50:42

by Ard Biesheuvel

[permalink] [raw]
Subject: Re: [PATCH 2/4] efi: Capsule update support

On 29 March 2016 at 14:26, Matt Fleming <[email protected]> wrote:
> On Mon, 21 Mar, at 08:31:59PM, Matt Fleming wrote:
>>
>> Good question. They're not handled in any special way with this patch
>> series, so the firmware will just initiate its own reset inside of
>> UpdateCapsule().
>>
>> That's probably not what we want, because things like on-disk
>> consistency are not guaranteed if the machine spontaneously reboots
>> without assistance from the kernel.
>>
>> The simplest thing to do is to refuse to pass such capsules to the
>> firmware, since it's likely not going to be a common use case. But
>> maybe that's overly restrictive.
>>
>> Let me have a think about that one.
>
> OK, I did think about this, and until someone actually requests the
> ability to handle CAPSULE_FLAGS_INITIATE_RESET, I'm happy to just punt
> on the problem. Anyone got any objections?
>

Nope

> ---
>
> diff --git a/drivers/firmware/efi/capsule.c b/drivers/firmware/efi/capsule.c
> index dac25208ad5e..84450e9cdf41 100644
> --- a/drivers/firmware/efi/capsule.c
> +++ b/drivers/firmware/efi/capsule.c
> @@ -84,6 +84,14 @@ int efi_capsule_supported(efi_guid_t guid, u32 flags, size_t size, int *reset)
> u64 max_size;
> int rv = 0;
>
> + /*
> + * We do not handle firmware-initiated reset because that
> + * would require us to prepare the kernel for reboot. Refuse
> + * to load any capsules with that flag.
> + */
> + if (flags & EFI_CAPSULE_INITIATE_RESET)
> + return -EINVAL;
> +

Should we perhaps whitelist rather than blacklist these flags? If a
'EFI_CAPSULE_INITIATE_RESET_TOO' surfaces at some point, or flags that
do other nasty things, at least we won't be caught off guard.

> capsule = kmalloc(sizeof(*capsule), GFP_KERNEL);
> if (!capsule)
> return -ENOMEM;

2016-04-01 02:48:55

by Bryan O'Donoghue

[permalink] [raw]
Subject: Re: [PATCH 4/4] efi: A misc char interface to update EFI firmware

On Thu, 2016-03-17 at 12:57 +0000, Matt Fleming wrote:
> Tested with Intel Quark Galileo GEN1 platform.

+ * If the capsule is successfully submitted to the firmware, any
+ * subsequent calls to efi_capsule_pending() will return true. @pages
+ * must not be released or modified if this function returns
+ * successfully.

The only minor nit here is that you can't actually test this on a
Galileo GEN1 since there's a non-standard header required to update the
firmware on that platform !

So I suggest dropping that particular comment in the git log, since you
can't actually *do* that on a Galileo without doing something about the
required CSH (Clanton Secure Header) prefixed to the standard Capsule
header.

Otherwise.

Reviewed-by: Bryan O'Donoghue <[email protected]>

2016-04-05 09:48:17

by Matt Fleming

[permalink] [raw]
Subject: Re: [PATCH 4/4] efi: A misc char interface to update EFI firmware

On Fri, 01 Apr, at 03:48:50AM, Bryan O'Donoghue wrote:
> On Thu, 2016-03-17 at 12:57 +0000, Matt Fleming wrote:
> > Tested with Intel Quark Galileo GEN1 platform.
>
> + * If the capsule is successfully submitted to the firmware, any
> + * subsequent calls to efi_capsule_pending() will return true. @pages
> + * must not be released or modified if this function returns
> + * successfully.
>
> The only minor nit here is that you can't actually test this on a
> Galileo GEN1 since there's a non-standard header required to update the
> firmware on that platform !
>
> So I suggest dropping that particular comment in the git log, since you
> can't actually *do* that on a Galileo without doing something about the
> required CSH (Clanton Secure Header) prefixed to the standard Capsule
> header.
>
> Otherwise.
>
> Reviewed-by: Bryan O'Donoghue <[email protected]>

Thanks Bryan. I've updated the commit log and added your Reviewed-by.

2016-04-05 10:09:35

by Matt Fleming

[permalink] [raw]
Subject: Re: [PATCH 2/4] efi: Capsule update support

On Tue, 29 Mar, at 03:50:39PM, Ard Biesheuvel wrote:
>
> Should we perhaps whitelist rather than blacklist these flags? If a
> 'EFI_CAPSULE_INITIATE_RESET_TOO' surfaces at some point, or flags that
> do other nasty things, at least we won't be caught off guard.

I spent a while thinking about this and was originally going to go
with the blacklist. The idea being that we wouldn't need to update the
kernel to allow new capsule flags to be passed through to the
firmware, even when the kernel doesn't care about them.

But then the thought of having to apply patches to stable to disallow
new capsule flags that don't work correctly with the current patches
left me feeling a cold chill.

So yes, it's a good suggestion Ard. Let's go with the whitelist, which
gives us the power to opt-in to any new capsule flags, whatever they
may be.

---

diff --git a/drivers/firmware/efi/capsule.c b/drivers/firmware/efi/capsule.c
index dac25208ad5e..0de55944ac0b 100644
--- a/drivers/firmware/efi/capsule.c
+++ b/drivers/firmware/efi/capsule.c
@@ -64,6 +64,17 @@ out:
return rv;
}

+/*
+ * Whitelist of EFI capsule flags that we support.
+ *
+ * We do not handle EFI_CAPSULE_INITIATE_RESET because that would
+ * require us to prepare the kernel for reboot. Refuse to load any
+ * capsules with that flag and any other flags that we do not know how
+ * to handle.
+ */
+#define EFI_CAPSULE_SUPPORTED_FLAG_MASK \
+ (EFI_CAPSULE_PERSIST_ACROSS_RESET | EFI_CAPSULE_POPULATE_SYSTEM_TABLE)
+
/**
* efi_capsule_supported - does the firmware support the capsule?
* @guid: vendor guid of capsule
@@ -84,6 +95,9 @@ int efi_capsule_supported(efi_guid_t guid, u32 flags, size_t size, int *reset)
u64 max_size;
int rv = 0;

+ if (flags & ~EFI_CAPSULE_SUPPORTED_FLAG_MASK)
+ return -EINVAL;
+
capsule = kmalloc(sizeof(*capsule), GFP_KERNEL);
if (!capsule)
return -ENOMEM;