1b17366d69
Pull powerpc updates from Ben Herrenschmidt: "So here's my next branch for powerpc. A bit late as I was on vacation last week. It's mostly the same stuff that was in next already, I just added two patches today which are the wiring up of lockref for powerpc, which for some reason fell through the cracks last time and is trivial. The highlights are, in addition to a bunch of bug fixes: - Reworked Machine Check handling on kernels running without a hypervisor (or acting as a hypervisor). Provides hooks to handle some errors in real mode such as TLB errors, handle SLB errors, etc... - Support for retrieving memory error information from the service processor on IBM servers running without a hypervisor and routing them to the memory poison infrastructure. - _PAGE_NUMA support on server processors - 32-bit BookE relocatable kernel support - FSL e6500 hardware tablewalk support - A bunch of new/revived board support - FSL e6500 deeper idle states and altivec powerdown support You'll notice a generic mm change here, it has been acked by the relevant authorities and is a pre-req for our _PAGE_NUMA support" * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc: (121 commits) powerpc: Implement arch_spin_is_locked() using arch_spin_value_unlocked() powerpc: Add support for the optimised lockref implementation powerpc/powernv: Call OPAL sync before kexec'ing powerpc/eeh: Escalate error on non-existing PE powerpc/eeh: Handle multiple EEH errors powerpc: Fix transactional FP/VMX/VSX unavailable handlers powerpc: Don't corrupt transactional state when using FP/VMX in kernel powerpc: Reclaim two unused thread_info flag bits powerpc: Fix races with irq_work Move precessing of MCE queued event out from syscall exit path. pseries/cpuidle: Remove redundant call to ppc64_runlatch_off() in cpu idle routines powerpc: Make add_system_ram_resources() __init powerpc: add SATA_MV to ppc64_defconfig powerpc/powernv: Increase candidate fw image size powerpc: Add debug checks to catch invalid cpu-to-node mappings powerpc: Fix the setup of CPU-to-Node mappings during CPU online powerpc/iommu: Don't detach device without IOMMU group powerpc/eeh: Hotplug improvement powerpc/eeh: Call opal_pci_reinit() on powernv for restoring config space powerpc/eeh: Add restore_config operation ...
649 lines
15 KiB
C
649 lines
15 KiB
C
/*
|
|
* 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 <= 1GB */
|
|
#define MAX_IMAGE_SIZE 0x40000000
|
|
|
|
/* 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;
|
|
};
|
|
|
|
struct validate_flash_t {
|
|
int status; /* Return status */
|
|
void *buf; /* Candidate 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(PAGE_SIZE, 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(PAGE_SIZE, 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;
|
|
|
|
/*
|
|
* Convert num_entries to version/length format
|
|
* to satisfy OPAL.
|
|
*/
|
|
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: Candidate 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;
|
|
}
|