diff options
Diffstat (limited to 'recipes-kernel/linux/files/0009-EFI-capsule-update-quark.patch')
-rw-r--r-- | recipes-kernel/linux/files/0009-EFI-capsule-update-quark.patch | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/recipes-kernel/linux/files/0009-EFI-capsule-update-quark.patch b/recipes-kernel/linux/files/0009-EFI-capsule-update-quark.patch new file mode 100644 index 0000000..ff299ee --- /dev/null +++ b/recipes-kernel/linux/files/0009-EFI-capsule-update-quark.patch @@ -0,0 +1,398 @@ +From xxxx Mon Sep 17 00:00:00 2001 +From: Bryan O'Donoghue <bryan.odonoghue@intel.com> +Date: Mon, 24 Feb 2014 18:41:59 +0000 +Subject: [PATCH 09/21] EFI capsule update + +--- + arch/x86/platform/efi/Makefile | 3 +- + arch/x86/platform/efi/efi.c | 12 +- + arch/x86/platform/efi/efi_capsule_update.c | 331 ++++++++++++++++++++++++++++ + 3 files changed, 342 insertions(+), 4 deletions(-) + create mode 100644 arch/x86/platform/efi/efi_capsule_update.c + +diff --git a/arch/x86/platform/efi/Makefile b/arch/x86/platform/efi/Makefile +index 6db1cc4..03a4329 100644 +--- a/arch/x86/platform/efi/Makefile ++++ b/arch/x86/platform/efi/Makefile +@@ -1,2 +1,3 @@ + obj-$(CONFIG_EFI) += efi.o efi_$(BITS).o efi_stub_$(BITS).o +-obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o ++obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o ++obj-$(CONFIG_EFI_CAPSULE) += efi_capsule_update.o +diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c +index e2cd38f..0e22b5f4 100644 +--- a/arch/x86/platform/efi/efi.c ++++ b/arch/x86/platform/efi/efi.c +@@ -847,6 +847,7 @@ void __init efi_enter_virtual_mode(void) + u64 end, systab, end_pfn; + void *p, *va, *new_memmap = NULL; + int count = 0; ++ bool bgrt_map; + + efi.systab = NULL; + +@@ -860,6 +861,11 @@ void __init efi_enter_virtual_mode(void) + return; + } + ++ /* ++ * Determine if mapping EFI boot code/data is required for BGRT mapping ++ */ ++ bgrt_map = efi_bgrt_probe(); ++ + /* Merge contiguous regions of the same type and attribute */ + for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { + u64 prev_size; +@@ -889,9 +895,9 @@ void __init efi_enter_virtual_mode(void) + + for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { + md = p; +- if (!(md->attribute & EFI_MEMORY_RUNTIME) && +- md->type != EFI_BOOT_SERVICES_CODE && +- md->type != EFI_BOOT_SERVICES_DATA) ++ if (!((md->attribute & EFI_MEMORY_RUNTIME) || (bgrt_map && ++ (md->type == EFI_BOOT_SERVICES_CODE || ++ md->type == EFI_BOOT_SERVICES_DATA)))) + continue; + + size = md->num_pages << EFI_PAGE_SHIFT; +diff --git a/arch/x86/platform/efi/efi_capsule_update.c b/arch/x86/platform/efi/efi_capsule_update.c +new file mode 100644 +index 0000000..3420e35 +--- /dev/null ++++ b/arch/x86/platform/efi/efi_capsule_update.c +@@ -0,0 +1,331 @@ ++/* ++ * Copyright(c) 2013 Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++#define DEBUG ++#include <asm/qrk.h> ++#include <linux/errno.h> ++#include <linux/firmware.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/efi.h> ++ ++#define DRIVER_NAME "efi_capsule_update" ++#define PFX "efi-capsupdate: " ++#define MAX_PATH 256 ++#define MAX_CHUNK PAGE_SIZE ++#define CSH_HDR_SIZE 0x400 ++ ++typedef struct { ++ u64 length; ++ union { ++ u64 data_block; ++ u64 continuation_pointer; ++ }; ++} efi_blk_desc_t; ++ ++static struct kobject * efi_capsule_kobj; ++static struct device *dev; ++static struct list_head sg_list; ++static char fpath[MAX_PATH]; ++static bool path_set = false; ++static int csh_jump = CSH_HDR_SIZE; /* Quark EDK wants CSH jump */ ++ ++/** ++ * efi_capsule_trigger_update ++ * ++ * Trigger the EFI capsule update ++ */ ++static int efi_capsule_trigger_update(void) ++{ ++ const struct firmware *fw_entry; ++ int ret = 0; ++ u32 nblocks = 0, i = 0, total_size = 0, data_len = 0, offset = 0; ++ efi_capsule_header_t *chdr = NULL; ++ efi_blk_desc_t * desc_block = NULL; ++ u8 * data = NULL; ++ ++ if (path_set == false) ++ return -ENODEV; ++ ++ ret = request_firmware(&fw_entry, fpath, dev); ++ if (ret || fw_entry == NULL){ ++ pr_err(PFX"unable to load firmware %s\n", fpath); ++ return ret; ++ } ++ ++ /* Determine necessary sizes */ ++ nblocks = (fw_entry->size/MAX_CHUNK) + 2; ++ total_size = fw_entry->size; ++ ++ /* Allocate array of descriptor blocks + 1 for terminator */ ++ desc_block = (efi_blk_desc_t*)kzalloc(nblocks * sizeof(efi_blk_desc_t), GFP_KERNEL); ++ if (desc_block == NULL){ ++ pr_info(PFX"%s failed to allocate %d blocks\n", __func__, nblocks); ++ ret = -ENOMEM; ++ goto done_close; ++ } ++ ++ pr_info(PFX"File %s size %u descriptor blocks %u\n", ++ fpath, total_size, nblocks); ++ ++ /* Read in data */ ++ for (i = 0; i < nblocks && offset < total_size; i++){ ++ /* Determine read len */ ++ data_len = offset < total_size - MAX_CHUNK ? ++ MAX_CHUNK : total_size - offset; ++ data = kmalloc(MAX_CHUNK, GFP_KERNEL); ++ if (data == NULL){ ++ ret = -ENOMEM; ++ pr_info("Alloc fail %d bytes entry %d\n", ++ nblocks, i); ++ goto done; ++ } ++ memcpy(data, fw_entry->data + offset, data_len); ++ offset += data_len; ++ ++ /* Sanity check */ ++ if (i >= nblocks){ ++ pr_err(PFX"%s Driver bug line %d\n", __func__, __LINE__); ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ /* Validate header as appropriate */ ++ if (chdr == NULL){ ++ chdr = (efi_capsule_header_t*)&data[csh_jump]; ++ desc_block[i].data_block = __pa(&data[csh_jump]); ++ desc_block[i].length = data_len - csh_jump; ++ pr_debug(PFX"hdr offset in file %d bytes\n", csh_jump); ++ pr_debug(PFX"hdr size %u flags 0x%08x imagesize 0x%08x\n", ++ chdr->headersize, chdr->flags, chdr->imagesize); ++ ++ }else{ ++ desc_block[i].data_block = __pa(data); ++ desc_block[i].length = data_len; ++ } ++ pr_debug(PFX "block %d length %u data @ phys 0x%08x virt %x\n", ++ i, (int)desc_block[i].length, ++ (unsigned int)desc_block[i].data_block, (unsigned int)data); ++ } ++ ++ if (i > nblocks-1){ ++ pr_err(PFX"%s Used block %d expected %d !\n", __func__, i, nblocks-1); ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ pr_debug(PFX"submitting capsule to EDKII firmware\n"); ++ ++ ret = efi.update_capsule(&chdr, 1, __pa(desc_block)); ++ if(ret != EFI_SUCCESS) { ++ pr_err(PFX"submission fail err=0x%08x\n", ret); ++ }else{ ++ pr_debug(PFX"submission success\n"); ++ ret = 0; ++ } ++ ++ if (chdr != NULL && chdr->flags & 0x10000){ ++ pr_debug(PFX"capsule persist across S3 skipping capsule free\n"); ++ goto done_close; ++ } ++done: ++ ++ for (i = 0; i < nblocks; i++){ ++ if (desc_block[i].data_block != 0) ++ kfree(phys_to_virt((u32)desc_block[i].data_block)); ++ } ++ ++ if (desc_block != NULL) ++ kfree(desc_block); ++done_close: ++ release_firmware(fw_entry); ++ return ret; ++} ++ ++/** ++ * efi_capsule_csh_jump ++ * ++ * sysfs callback used to show current path ++ */ ++static ssize_t efi_capsule_csh_jump_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return snprintf(buf, sizeof(fpath), "%d\n", csh_jump > 0); ++} ++ ++/** ++ * efi_capsule_path_store ++ * ++ * sysfs callback used to set a new capsule path ++ */ ++static ssize_t efi_capsule_csh_jump_store(struct kobject *kobj, struct kobj_attribute *attr, ++ const char *buf, size_t count) ++{ ++ if (buf != NULL && buf[0] == '0') ++ csh_jump = 0; ++ else ++ csh_jump = CSH_HDR_SIZE; ++ return count; ++} ++ ++static struct kobj_attribute efi_capsule_csh_jump_attr = ++ __ATTR(csh_jump, 0644, efi_capsule_csh_jump_show, efi_capsule_csh_jump_store); ++ ++/** ++ * efi_capsule_path_show ++ * ++ * sysfs callback used to show current path ++ */ ++static ssize_t efi_capsule_path_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return snprintf(buf, sizeof(fpath), fpath); ++} ++ ++/** ++ * efi_capsule_path_store ++ * ++ * sysfs callback used to set a new capsule path ++ */ ++static ssize_t efi_capsule_path_store(struct kobject *kobj, struct kobj_attribute *attr, ++ const char *buf, size_t count) ++{ ++ if (count > MAX_PATH-1) ++ return -EINVAL; ++ ++ memset(fpath, 0x00, sizeof(fpath)); ++ memcpy(fpath, buf, count); ++ path_set = true; ++ ++ return count; ++} ++ ++static struct kobj_attribute efi_capsule_path_attr = ++ __ATTR(capsule_path, 0644, efi_capsule_path_show, efi_capsule_path_store); ++ ++/** ++ * efi_capsule_update_store ++ * ++ * sysfs callback used to initiate update ++ */ ++static ssize_t efi_capsule_update_store(struct kobject *kobj, struct kobj_attribute *attr, ++ const char *buf, size_t count) ++{ int ret = 0; ++ ++ ret = efi_capsule_trigger_update(); ++ return ret == 0 ? count : ret; ++} ++ ++static struct kobj_attribute efi_capsule_update_attr = ++ __ATTR(capsule_update, 0644, NULL, efi_capsule_update_store); ++ ++static void efi_capsule_device_release(struct device *dev) ++{ ++ kfree(dev); ++} ++ ++#define SYSFS_ERRTXT "Error adding sysfs entry!\n" ++/** ++ * intel_qrk_capsule_update_init ++ * ++ * @return 0 success < 0 failure ++ * ++ * Module entry point ++ */ ++static int __init efi_capsule_update_init(void) ++{ ++ int retval = 0; ++ extern struct kobject * firmware_kobj; ++ ++ INIT_LIST_HEAD(&sg_list); ++ ++ /* efi_capsule_kobj subordinate of firmware @ /sys/firmware/efi */ ++ efi_capsule_kobj = kobject_create_and_add("efi_capsule", firmware_kobj); ++ if (!efi_capsule_kobj) { ++ pr_err(PFX"kset create error\n"); ++ retval = -ENODEV; ++ goto err; ++ } ++ ++ dev = kzalloc(sizeof(struct device), GFP_KERNEL); ++ if (!dev) { ++ retval = -ENOMEM; ++ goto err_name; ++ } ++ ++ retval = dev_set_name(dev, "%s", DRIVER_NAME); ++ if (retval < 0){ ++ pr_err(PFX"dev_set_name err\n"); ++ goto err_dev_reg; ++ } ++ ++ dev->kobj.parent = efi_capsule_kobj; ++ dev->groups = NULL; ++ dev->release = efi_capsule_device_release; ++ ++ retval = device_register(dev); ++ if (retval < 0){ ++ pr_err(PFX"device_register error\n"); ++ goto err_dev_reg; ++ } ++ ++ if(sysfs_create_file(efi_capsule_kobj, &efi_capsule_path_attr.attr)) { ++ pr_err(PFX SYSFS_ERRTXT); ++ retval = -ENODEV; ++ goto err_dev_reg; ++ } ++ if(sysfs_create_file(efi_capsule_kobj, &efi_capsule_update_attr.attr)) { ++ pr_err(PFX SYSFS_ERRTXT); ++ retval = -ENODEV; ++ goto err_dev_reg; ++ ++ } ++ if(sysfs_create_file(efi_capsule_kobj, &efi_capsule_csh_jump_attr.attr)) { ++ pr_err(PFX SYSFS_ERRTXT); ++ retval = -ENODEV; ++ goto err_dev_reg; ++ ++ } ++ return 0; ++ ++err_dev_reg: ++ put_device(dev); ++ dev = NULL; ++err_name: ++ kfree(dev); ++err: ++ return retval; ++} ++ ++/** ++ * intel_qrk_esram_exit ++ * ++ * Module exit ++ */ ++static void __exit efi_capsule_update_exit(void) ++{ ++} ++ ++MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@intel.com>"); ++MODULE_DESCRIPTION("EFI Capsule Update driver"); ++MODULE_LICENSE("Dual BSD/GPL"); ++ ++module_init(efi_capsule_update_init); ++module_exit(efi_capsule_update_exit); +-- +1.7.4.1 + |