aboutsummaryrefslogtreecommitdiffstats
path: root/recipes-kernel/linux/files/0009-EFI-capsule-update-quark.patch
diff options
context:
space:
mode:
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.patch398
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
+