powerpc/powernv: Code update interface
authorVasant Hegde <hegdevasant@linux.vnet.ibm.com>
Thu, 24 Oct 2013 10:34:58 +0000 (16:04 +0530)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Wed, 30 Oct 2013 05:12:02 +0000 (16:12 +1100)
Code update interface for powernv platform. This provides
sysfs interface to pass new image, validate, update and
commit images.

This patch includes:
  - Below OPAL APIs for code update
    - opal_validate_flash()
    - opal_manage_flash()
    - opal_update_flash()

  - Create below sysfs files under /sys/firmware/opal
    - image : Interface to pass new FW image
    - validate_flash : Validate candidate image
    - manage_flash : Commit/Reject operations
    - update_flash : Flash new candidate image

Updating Image:
  "update_flash" is an interface to indicate flash new FW.
It just passes image SG list to FW. Actual flashing is done
during system reboot time.

Note:
  - SG entry format:
    I have kept version number to keep this list similar to what
    PAPR is defined.

Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/include/asm/opal.h
arch/powerpc/platforms/powernv/Makefile
arch/powerpc/platforms/powernv/opal-flash.c [new file with mode: 0644]
arch/powerpc/platforms/powernv/opal-wrappers.S
arch/powerpc/platforms/powernv/opal.c

index ee0efd2f2abc13395d3286a0ee0030a811c85510..033c06be1d840da02423d596121940d0aa11adb5 100644 (file)
@@ -129,6 +129,9 @@ extern int opal_enter_rtas(struct rtas_args *args,
 #define OPAL_LPC_READ                          67
 #define OPAL_LPC_WRITE                         68
 #define OPAL_RETURN_CPU                                69
+#define OPAL_FLASH_VALIDATE                    76
+#define OPAL_FLASH_MANAGE                      77
+#define OPAL_FLASH_UPDATE                      78
 
 #ifndef __ASSEMBLY__
 
@@ -724,6 +727,9 @@ int64_t opal_lpc_write(uint32_t chip_id, enum OpalLPCAddressType addr_type,
                       uint32_t addr, uint32_t data, uint32_t sz);
 int64_t opal_lpc_read(uint32_t chip_id, enum OpalLPCAddressType addr_type,
                      uint32_t addr, uint32_t *data, uint32_t sz);
+int64_t opal_validate_flash(uint64_t buffer, uint32_t *size, uint32_t *result);
+int64_t opal_manage_flash(uint8_t op);
+int64_t opal_update_flash(uint64_t blk_list);
 
 /* Internal functions */
 extern int early_init_dt_scan_opal(unsigned long node, const char *uname, int depth, void *data);
@@ -752,6 +758,7 @@ extern int opal_set_rtc_time(struct rtc_time *tm);
 extern void opal_get_rtc_time(struct rtc_time *tm);
 extern unsigned long opal_get_boot_time(void);
 extern void opal_nvram_init(void);
+extern void opal_flash_init(void);
 
 extern int opal_machine_check(struct pt_regs *regs);
 
index 050d57e0c78811e2c1276ec90ead89a827d1fc8c..873fa1370dc44c1b0b3994e7555c4fac450adcbd 100644 (file)
@@ -1,5 +1,6 @@
 obj-y                  += setup.o opal-takeover.o opal-wrappers.o opal.o
-obj-y                  += opal-rtc.o opal-nvram.o opal-lpc.o rng.o
+obj-y                  += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o
+obj-y                  += rng.o
 
 obj-$(CONFIG_SMP)      += smp.o
 obj-$(CONFIG_PCI)      += pci.o pci-p5ioc2.o pci-ioda.o
diff --git a/arch/powerpc/platforms/powernv/opal-flash.c b/arch/powerpc/platforms/powernv/opal-flash.c
new file mode 100644 (file)
index 0000000..6ffa6b1
--- /dev/null
@@ -0,0 +1,667 @@
+/*
+ * PowerNV OPAL Firmware Update Interface
+ *
+ * Copyright 2013 IBM Corp.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/pagemap.h>
+
+#include <asm/opal.h>
+
+/* FLASH status codes */
+#define FLASH_NO_OP            -1099   /* No operation initiated by user */
+#define FLASH_NO_AUTH          -9002   /* Not a service authority partition */
+
+/* Validate image status values */
+#define VALIDATE_IMG_READY     -1001   /* Image ready for validation */
+#define VALIDATE_IMG_INCOMPLETE        -1002   /* User copied < VALIDATE_BUF_SIZE */
+
+/* Manage image status values */
+#define MANAGE_ACTIVE_ERR      -9001   /* Cannot overwrite active img */
+
+/* Flash image status values */
+#define FLASH_IMG_READY                0       /* Img ready for flash on reboot */
+#define FLASH_INVALID_IMG      -1003   /* Flash image shorter than expected */
+#define FLASH_IMG_NULL_DATA    -1004   /* Bad data in sg list entry */
+#define FLASH_IMG_BAD_LEN      -1005   /* Bad length in sg list entry */
+
+/* Manage operation tokens */
+#define FLASH_REJECT_TMP_SIDE  0       /* Reject temporary fw image */
+#define FLASH_COMMIT_TMP_SIDE  1       /* Commit temporary fw image */
+
+/* Update tokens */
+#define FLASH_UPDATE_CANCEL    0       /* Cancel update request */
+#define FLASH_UPDATE_INIT      1       /* Initiate update */
+
+/* Validate image update result tokens */
+#define VALIDATE_TMP_UPDATE    0     /* T side will be updated */
+#define VALIDATE_FLASH_AUTH    1     /* Partition does not have authority */
+#define VALIDATE_INVALID_IMG   2     /* Candidate image is not valid */
+#define VALIDATE_CUR_UNKNOWN   3     /* Current fixpack level is unknown */
+/*
+ * Current T side will be committed to P side before being replace with new
+ * image, and the new image is downlevel from current image
+ */
+#define VALIDATE_TMP_COMMIT_DL 4
+/*
+ * Current T side will be committed to P side before being replaced with new
+ * image
+ */
+#define VALIDATE_TMP_COMMIT    5
+/*
+ * T side will be updated with a downlevel image
+ */
+#define VALIDATE_TMP_UPDATE_DL 6
+/*
+ * The candidate image's release date is later than the system's firmware
+ * service entitlement date - service warranty period has expired
+ */
+#define VALIDATE_OUT_OF_WRNTY  7
+
+/* Validate buffer size */
+#define VALIDATE_BUF_SIZE      4096
+
+/* XXX: Assume candidate image size is <= 256MB */
+#define MAX_IMAGE_SIZE 0x10000000
+
+/* Flash sg list version */
+#define SG_LIST_VERSION (1UL)
+
+/* Image status */
+enum {
+       IMAGE_INVALID,
+       IMAGE_LOADING,
+       IMAGE_READY,
+};
+
+/* Candidate image data */
+struct image_data_t {
+       int             status;
+       void            *data;
+       uint32_t        size;
+};
+
+/* Candidate image header */
+struct image_header_t {
+       uint16_t        magic;
+       uint16_t        version;
+       uint32_t        size;
+};
+
+/* Scatter/gather entry */
+struct opal_sg_entry {
+       void    *data;
+       long    length;
+};
+
+/* We calculate number of entries based on PAGE_SIZE */
+#define SG_ENTRIES_PER_NODE ((PAGE_SIZE - 16) / sizeof(struct opal_sg_entry))
+
+/*
+ * This struct is very similar but not identical to that
+ * needed by the opal flash update. All we need to do for
+ * opal is rewrite num_entries into a version/length and
+ * translate the pointers to absolute.
+ */
+struct opal_sg_list {
+       unsigned long num_entries;
+       struct opal_sg_list *next;
+       struct opal_sg_entry entry[SG_ENTRIES_PER_NODE];
+};
+
+struct validate_flash_t {
+       int             status;         /* Return status */
+       void            *buf;           /* Candiate image buffer */
+       uint32_t        buf_size;       /* Image size */
+       uint32_t        result;         /* Update results token */
+};
+
+struct manage_flash_t {
+       int status;             /* Return status */
+};
+
+struct update_flash_t {
+       int status;             /* Return status */
+};
+
+static struct image_header_t   image_header;
+static struct image_data_t     image_data;
+static struct validate_flash_t validate_flash_data;
+static struct manage_flash_t   manage_flash_data;
+static struct update_flash_t   update_flash_data;
+
+static DEFINE_MUTEX(image_data_mutex);
+
+/*
+ * Validate candidate image
+ */
+static inline void opal_flash_validate(void)
+{
+       struct validate_flash_t *args_buf = &validate_flash_data;
+
+       args_buf->status = opal_validate_flash(__pa(args_buf->buf),
+                                              &(args_buf->buf_size),
+                                              &(args_buf->result));
+}
+
+/*
+ * Validate output format:
+ *     validate result token
+ *     current image version details
+ *     new image version details
+ */
+static ssize_t validate_show(struct kobject *kobj,
+                            struct kobj_attribute *attr, char *buf)
+{
+       struct validate_flash_t *args_buf = &validate_flash_data;
+       int len;
+
+       /* Candidate image is not validated */
+       if (args_buf->status < VALIDATE_TMP_UPDATE) {
+               len = sprintf(buf, "%d\n", args_buf->status);
+               goto out;
+       }
+
+       /* Result token */
+       len = sprintf(buf, "%d\n", args_buf->result);
+
+       /* Current and candidate image version details */
+       if ((args_buf->result != VALIDATE_TMP_UPDATE) &&
+           (args_buf->result < VALIDATE_CUR_UNKNOWN))
+               goto out;
+
+       if (args_buf->buf_size > (VALIDATE_BUF_SIZE - len)) {
+               memcpy(buf + len, args_buf->buf, VALIDATE_BUF_SIZE - len);
+               len = VALIDATE_BUF_SIZE;
+       } else {
+               memcpy(buf + len, args_buf->buf, args_buf->buf_size);
+               len += args_buf->buf_size;
+       }
+out:
+       /* Set status to default */
+       args_buf->status = FLASH_NO_OP;
+       return len;
+}
+
+/*
+ * Validate candidate firmware image
+ *
+ * Note:
+ *   We are only interested in first 4K bytes of the
+ *   candidate image.
+ */
+static ssize_t validate_store(struct kobject *kobj,
+                             struct kobj_attribute *attr,
+                             const char *buf, size_t count)
+{
+       struct validate_flash_t *args_buf = &validate_flash_data;
+
+       if (buf[0] != '1')
+               return -EINVAL;
+
+       mutex_lock(&image_data_mutex);
+
+       if (image_data.status != IMAGE_READY ||
+           image_data.size < VALIDATE_BUF_SIZE) {
+               args_buf->result = VALIDATE_INVALID_IMG;
+               args_buf->status = VALIDATE_IMG_INCOMPLETE;
+               goto out;
+       }
+
+       /* Copy first 4k bytes of candidate image */
+       memcpy(args_buf->buf, image_data.data, VALIDATE_BUF_SIZE);
+
+       args_buf->status = VALIDATE_IMG_READY;
+       args_buf->buf_size = VALIDATE_BUF_SIZE;
+
+       /* Validate candidate image */
+       opal_flash_validate();
+
+out:
+       mutex_unlock(&image_data_mutex);
+       return count;
+}
+
+/*
+ * Manage flash routine
+ */
+static inline void opal_flash_manage(uint8_t op)
+{
+       struct manage_flash_t *const args_buf = &manage_flash_data;
+
+       args_buf->status = opal_manage_flash(op);
+}
+
+/*
+ * Show manage flash status
+ */
+static ssize_t manage_show(struct kobject *kobj,
+                          struct kobj_attribute *attr, char *buf)
+{
+       struct manage_flash_t *const args_buf = &manage_flash_data;
+       int rc;
+
+       rc = sprintf(buf, "%d\n", args_buf->status);
+       /* Set status to default*/
+       args_buf->status = FLASH_NO_OP;
+       return rc;
+}
+
+/*
+ * Manage operations:
+ *   0 - Reject
+ *   1 - Commit
+ */
+static ssize_t manage_store(struct kobject *kobj,
+                           struct kobj_attribute *attr,
+                           const char *buf, size_t count)
+{
+       uint8_t op;
+       switch (buf[0]) {
+       case '0':
+               op = FLASH_REJECT_TMP_SIDE;
+               break;
+       case '1':
+               op = FLASH_COMMIT_TMP_SIDE;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* commit/reject temporary image */
+       opal_flash_manage(op);
+       return count;
+}
+
+/*
+ * Free sg list
+ */
+static void free_sg_list(struct opal_sg_list *list)
+{
+       struct opal_sg_list *sg1;
+       while (list) {
+               sg1 = list->next;
+               kfree(list);
+               list = sg1;
+       }
+       list = NULL;
+}
+
+/*
+ * Build candidate image scatter gather list
+ *
+ * list format:
+ *   -----------------------------------
+ *  |  VER (8) | Entry length in bytes  |
+ *   -----------------------------------
+ *  |  Pointer to next entry            |
+ *   -----------------------------------
+ *  |  Address of memory area 1         |
+ *   -----------------------------------
+ *  |  Length of memory area 1          |
+ *   -----------------------------------
+ *  |   .........                       |
+ *   -----------------------------------
+ *  |   .........                       |
+ *   -----------------------------------
+ *  |  Address of memory area N         |
+ *   -----------------------------------
+ *  |  Length of memory area N          |
+ *   -----------------------------------
+ */
+static struct opal_sg_list *image_data_to_sglist(void)
+{
+       struct opal_sg_list *sg1, *list = NULL;
+       void *addr;
+       int size;
+
+       addr = image_data.data;
+       size = image_data.size;
+
+       sg1 = kzalloc((sizeof(struct opal_sg_list)), GFP_KERNEL);
+       if (!sg1)
+               return NULL;
+
+       list = sg1;
+       sg1->num_entries = 0;
+       while (size > 0) {
+               /* Translate virtual address to physical address */
+               sg1->entry[sg1->num_entries].data =
+                       (void *)(vmalloc_to_pfn(addr) << PAGE_SHIFT);
+
+               if (size > PAGE_SIZE)
+                       sg1->entry[sg1->num_entries].length = PAGE_SIZE;
+               else
+                       sg1->entry[sg1->num_entries].length = size;
+
+               sg1->num_entries++;
+               if (sg1->num_entries >= SG_ENTRIES_PER_NODE) {
+                       sg1->next = kzalloc((sizeof(struct opal_sg_list)),
+                                           GFP_KERNEL);
+                       if (!sg1->next) {
+                               pr_err("%s : Failed to allocate memory\n",
+                                      __func__);
+                               goto nomem;
+                       }
+
+                       sg1 = sg1->next;
+                       sg1->num_entries = 0;
+               }
+               addr += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+       return list;
+nomem:
+       free_sg_list(list);
+       return NULL;
+}
+
+/*
+ * OPAL update flash
+ */
+static int opal_flash_update(int op)
+{
+       struct opal_sg_list *sg, *list, *next;
+       unsigned long addr;
+       int64_t rc = OPAL_PARAMETER;
+
+       if (op == FLASH_UPDATE_CANCEL) {
+               pr_alert("FLASH: Image update cancelled\n");
+               addr = '\0';
+               goto flash;
+       }
+
+       list = image_data_to_sglist();
+       if (!list)
+               goto invalid_img;
+
+       /* First entry address */
+       addr = __pa(list);
+
+       /* Translate sg list address to absolute */
+       for (sg = list; sg; sg = next) {
+               next = sg->next;
+               /* Don't translate NULL pointer for last entry */
+               if (sg->next)
+                       sg->next = (struct opal_sg_list *)__pa(sg->next);
+               else
+                       sg->next = NULL;
+
+               /* Make num_entries into the version/length field */
+               sg->num_entries = (SG_LIST_VERSION << 56) |
+                       (sg->num_entries * sizeof(struct opal_sg_entry) + 16);
+       }
+
+       pr_alert("FLASH: Image is %u bytes\n", image_data.size);
+       pr_alert("FLASH: Image update requested\n");
+       pr_alert("FLASH: Image will be updated during system reboot\n");
+       pr_alert("FLASH: This will take several minutes. Do not power off!\n");
+
+flash:
+       rc = opal_update_flash(addr);
+
+invalid_img:
+       return rc;
+}
+
+/*
+ * Show candidate image status
+ */
+static ssize_t update_show(struct kobject *kobj,
+                          struct kobj_attribute *attr, char *buf)
+{
+       struct update_flash_t *const args_buf = &update_flash_data;
+       return sprintf(buf, "%d\n", args_buf->status);
+}
+
+/*
+ * Set update image flag
+ *  1 - Flash new image
+ *  0 - Cancel flash request
+ */
+static ssize_t update_store(struct kobject *kobj,
+                           struct kobj_attribute *attr,
+                           const char *buf, size_t count)
+{
+       struct update_flash_t *const args_buf = &update_flash_data;
+       int rc = count;
+
+       mutex_lock(&image_data_mutex);
+
+       switch (buf[0]) {
+       case '0':
+               if (args_buf->status == FLASH_IMG_READY)
+                       opal_flash_update(FLASH_UPDATE_CANCEL);
+               args_buf->status = FLASH_NO_OP;
+               break;
+       case '1':
+               /* Image is loaded? */
+               if (image_data.status == IMAGE_READY)
+                       args_buf->status =
+                               opal_flash_update(FLASH_UPDATE_INIT);
+               else
+                       args_buf->status = FLASH_INVALID_IMG;
+               break;
+       default:
+               rc = -EINVAL;
+       }
+
+       mutex_unlock(&image_data_mutex);
+       return rc;
+}
+
+/*
+ * Free image buffer
+ */
+static void free_image_buf(void)
+{
+       void *addr;
+       int size;
+
+       addr = image_data.data;
+       size = PAGE_ALIGN(image_data.size);
+       while (size > 0) {
+               ClearPageReserved(vmalloc_to_page(addr));
+               addr += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+       vfree(image_data.data);
+       image_data.data = NULL;
+       image_data.status = IMAGE_INVALID;
+}
+
+/*
+ * Allocate image buffer.
+ */
+static int alloc_image_buf(char *buffer, size_t count)
+{
+       void *addr;
+       int size;
+
+       if (count < sizeof(struct image_header_t)) {
+               pr_warn("FLASH: Invalid candidate image\n");
+               return -EINVAL;
+       }
+
+       memcpy(&image_header, (void *)buffer, sizeof(struct image_header_t));
+       image_data.size = be32_to_cpu(image_header.size);
+       pr_debug("FLASH: Candiate image size = %u\n", image_data.size);
+
+       if (image_data.size > MAX_IMAGE_SIZE) {
+               pr_warn("FLASH: Too large image\n");
+               return -EINVAL;
+       }
+       if (image_data.size < VALIDATE_BUF_SIZE) {
+               pr_warn("FLASH: Image is shorter than expected\n");
+               return -EINVAL;
+       }
+
+       image_data.data = vzalloc(PAGE_ALIGN(image_data.size));
+       if (!image_data.data) {
+               pr_err("%s : Failed to allocate memory\n", __func__);
+               return -ENOMEM;
+       }
+
+       /* Pin memory */
+       addr = image_data.data;
+       size = PAGE_ALIGN(image_data.size);
+       while (size > 0) {
+               SetPageReserved(vmalloc_to_page(addr));
+               addr += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+
+       image_data.status = IMAGE_LOADING;
+       return 0;
+}
+
+/*
+ * Copy candidate image
+ *
+ * Parse candidate image header to get total image size
+ * and pre-allocate required memory.
+ */
+static ssize_t image_data_write(struct file *filp, struct kobject *kobj,
+                               struct bin_attribute *bin_attr,
+                               char *buffer, loff_t pos, size_t count)
+{
+       int rc;
+
+       mutex_lock(&image_data_mutex);
+
+       /* New image ? */
+       if (pos == 0) {
+               /* Free memory, if already allocated */
+               if (image_data.data)
+                       free_image_buf();
+
+               /* Cancel outstanding image update request */
+               if (update_flash_data.status == FLASH_IMG_READY)
+                       opal_flash_update(FLASH_UPDATE_CANCEL);
+
+               /* Allocate memory */
+               rc = alloc_image_buf(buffer, count);
+               if (rc)
+                       goto out;
+       }
+
+       if (image_data.status != IMAGE_LOADING) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       if ((pos + count) > image_data.size) {
+               rc = -EINVAL;
+               goto out;
+       }
+
+       memcpy(image_data.data + pos, (void *)buffer, count);
+       rc = count;
+
+       /* Set image status */
+       if ((pos + count) == image_data.size) {
+               pr_debug("FLASH: Candidate image loaded....\n");
+               image_data.status = IMAGE_READY;
+       }
+
+out:
+       mutex_unlock(&image_data_mutex);
+       return rc;
+}
+
+/*
+ * sysfs interface :
+ *  OPAL uses below sysfs files for code update.
+ *  We create these files under /sys/firmware/opal.
+ *
+ *   image             : Interface to load candidate firmware image
+ *   validate_flash    : Validate firmware image
+ *   manage_flash      : Commit/Reject firmware image
+ *   update_flash      : Flash new firmware image
+ *
+ */
+static struct bin_attribute image_data_attr = {
+       .attr = {.name = "image", .mode = 0200},
+       .size = MAX_IMAGE_SIZE, /* Limit image size */
+       .write = image_data_write,
+};
+
+static struct kobj_attribute validate_attribute =
+       __ATTR(validate_flash, 0600, validate_show, validate_store);
+
+static struct kobj_attribute manage_attribute =
+       __ATTR(manage_flash, 0600, manage_show, manage_store);
+
+static struct kobj_attribute update_attribute =
+       __ATTR(update_flash, 0600, update_show, update_store);
+
+static struct attribute *image_op_attrs[] = {
+       &validate_attribute.attr,
+       &manage_attribute.attr,
+       &update_attribute.attr,
+       NULL    /* need to NULL terminate the list of attributes */
+};
+
+static struct attribute_group image_op_attr_group = {
+       .attrs = image_op_attrs,
+};
+
+void __init opal_flash_init(void)
+{
+       int ret;
+
+       /* Allocate validate image buffer */
+       validate_flash_data.buf = kzalloc(VALIDATE_BUF_SIZE, GFP_KERNEL);
+       if (!validate_flash_data.buf) {
+               pr_err("%s : Failed to allocate memory\n", __func__);
+               return;
+       }
+
+       /* Make sure /sys/firmware/opal directory is created */
+       if (!opal_kobj) {
+               pr_warn("FLASH: opal kobject is not available\n");
+               goto nokobj;
+       }
+
+       /* Create the sysfs files */
+       ret = sysfs_create_group(opal_kobj, &image_op_attr_group);
+       if (ret) {
+               pr_warn("FLASH: Failed to create sysfs files\n");
+               goto nokobj;
+       }
+
+       ret = sysfs_create_bin_file(opal_kobj, &image_data_attr);
+       if (ret) {
+               pr_warn("FLASH: Failed to create sysfs files\n");
+               goto nosysfs_file;
+       }
+
+       /* Set default status */
+       validate_flash_data.status = FLASH_NO_OP;
+       manage_flash_data.status = FLASH_NO_OP;
+       update_flash_data.status = FLASH_NO_OP;
+       image_data.status = IMAGE_INVALID;
+       return;
+
+nosysfs_file:
+       sysfs_remove_group(opal_kobj, &image_op_attr_group);
+
+nokobj:
+       kfree(validate_flash_data.buf);
+       return;
+}
index 403d05840625e578b6f68ab728d73e8214d4425c..e7806504e9769162f921e704cf55d0b3e210cf6c 100644 (file)
@@ -123,3 +123,6 @@ OPAL_CALL(opal_xscom_write,                 OPAL_XSCOM_WRITE);
 OPAL_CALL(opal_lpc_read,                       OPAL_LPC_READ);
 OPAL_CALL(opal_lpc_write,                      OPAL_LPC_WRITE);
 OPAL_CALL(opal_return_cpu,                     OPAL_RETURN_CPU);
+OPAL_CALL(opal_validate_flash,                 OPAL_FLASH_VALIDATE);
+OPAL_CALL(opal_manage_flash,                   OPAL_FLASH_MANAGE);
+OPAL_CALL(opal_update_flash,                   OPAL_FLASH_UPDATE);
index 37f06580709a426c7632d924ac7bd2076568b689..b56c243aaee93907741d59aee9090dcd7e000ec7 100644 (file)
@@ -438,6 +438,10 @@ static int __init opal_init(void)
 
        /* Create "opal" kobject under /sys/firmware */
        rc = opal_sysfs_init();
+       if (rc == 0) {
+               /* Setup code update interface */
+               opal_flash_init();
+       }
 
        return 0;
 }