powerpc/powernv: Code update interface
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>
This commit is contained in:
parent
6f68b5e2c6
commit
50bd6153d1
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
667
arch/powerpc/platforms/powernv/opal-flash.c
Normal file
667
arch/powerpc/platforms/powernv/opal-flash.c
Normal file
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue