Path: blob/master/arch/powerpc/platforms/powernv/opal-flash.c
26481 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* PowerNV OPAL Firmware Update Interface3*4* Copyright 2013 IBM Corp.5*/67#define DEBUG89#include <linux/kernel.h>10#include <linux/reboot.h>11#include <linux/init.h>12#include <linux/kobject.h>13#include <linux/sysfs.h>14#include <linux/slab.h>15#include <linux/mm.h>16#include <linux/vmalloc.h>17#include <linux/pagemap.h>18#include <linux/delay.h>1920#include <asm/opal.h>2122/* FLASH status codes */23#define FLASH_NO_OP -1099 /* No operation initiated by user */24#define FLASH_NO_AUTH -9002 /* Not a service authority partition */2526/* Validate image status values */27#define VALIDATE_IMG_READY -1001 /* Image ready for validation */28#define VALIDATE_IMG_INCOMPLETE -1002 /* User copied < VALIDATE_BUF_SIZE */2930/* Manage image status values */31#define MANAGE_ACTIVE_ERR -9001 /* Cannot overwrite active img */3233/* Flash image status values */34#define FLASH_IMG_READY 0 /* Img ready for flash on reboot */35#define FLASH_INVALID_IMG -1003 /* Flash image shorter than expected */36#define FLASH_IMG_NULL_DATA -1004 /* Bad data in sg list entry */37#define FLASH_IMG_BAD_LEN -1005 /* Bad length in sg list entry */3839/* Manage operation tokens */40#define FLASH_REJECT_TMP_SIDE 0 /* Reject temporary fw image */41#define FLASH_COMMIT_TMP_SIDE 1 /* Commit temporary fw image */4243/* Update tokens */44#define FLASH_UPDATE_CANCEL 0 /* Cancel update request */45#define FLASH_UPDATE_INIT 1 /* Initiate update */4647/* Validate image update result tokens */48#define VALIDATE_TMP_UPDATE 0 /* T side will be updated */49#define VALIDATE_FLASH_AUTH 1 /* Partition does not have authority */50#define VALIDATE_INVALID_IMG 2 /* Candidate image is not valid */51#define VALIDATE_CUR_UNKNOWN 3 /* Current fixpack level is unknown */52/*53* Current T side will be committed to P side before being replace with new54* image, and the new image is downlevel from current image55*/56#define VALIDATE_TMP_COMMIT_DL 457/*58* Current T side will be committed to P side before being replaced with new59* image60*/61#define VALIDATE_TMP_COMMIT 562/*63* T side will be updated with a downlevel image64*/65#define VALIDATE_TMP_UPDATE_DL 666/*67* The candidate image's release date is later than the system's firmware68* service entitlement date - service warranty period has expired69*/70#define VALIDATE_OUT_OF_WRNTY 77172/* Validate buffer size */73#define VALIDATE_BUF_SIZE 40967475/* XXX: Assume candidate image size is <= 1GB */76#define MAX_IMAGE_SIZE 0x400000007778/* Image status */79enum {80IMAGE_INVALID,81IMAGE_LOADING,82IMAGE_READY,83};8485/* Candidate image data */86struct image_data_t {87int status;88void *data;89uint32_t size;90};9192/* Candidate image header */93struct image_header_t {94uint16_t magic;95uint16_t version;96uint32_t size;97};9899struct validate_flash_t {100int status; /* Return status */101void *buf; /* Candidate image buffer */102uint32_t buf_size; /* Image size */103uint32_t result; /* Update results token */104};105106struct manage_flash_t {107int status; /* Return status */108};109110struct update_flash_t {111int status; /* Return status */112};113114static struct image_header_t image_header;115static struct image_data_t image_data;116static struct validate_flash_t validate_flash_data;117static struct manage_flash_t manage_flash_data;118119/* Initialize update_flash_data status to No Operation */120static struct update_flash_t update_flash_data = {121.status = FLASH_NO_OP,122};123124static DEFINE_MUTEX(image_data_mutex);125126/*127* Validate candidate image128*/129static inline void opal_flash_validate(void)130{131long ret;132void *buf = validate_flash_data.buf;133__be32 size = cpu_to_be32(validate_flash_data.buf_size);134__be32 result;135136ret = opal_validate_flash(__pa(buf), &size, &result);137138validate_flash_data.status = ret;139validate_flash_data.buf_size = be32_to_cpu(size);140validate_flash_data.result = be32_to_cpu(result);141}142143/*144* Validate output format:145* validate result token146* current image version details147* new image version details148*/149static ssize_t validate_show(struct kobject *kobj,150struct kobj_attribute *attr, char *buf)151{152struct validate_flash_t *args_buf = &validate_flash_data;153int len;154155/* Candidate image is not validated */156if (args_buf->status < VALIDATE_TMP_UPDATE) {157len = sprintf(buf, "%d\n", args_buf->status);158goto out;159}160161/* Result token */162len = sprintf(buf, "%d\n", args_buf->result);163164/* Current and candidate image version details */165if ((args_buf->result != VALIDATE_TMP_UPDATE) &&166(args_buf->result < VALIDATE_CUR_UNKNOWN))167goto out;168169if (args_buf->buf_size > (VALIDATE_BUF_SIZE - len)) {170memcpy(buf + len, args_buf->buf, VALIDATE_BUF_SIZE - len);171len = VALIDATE_BUF_SIZE;172} else {173memcpy(buf + len, args_buf->buf, args_buf->buf_size);174len += args_buf->buf_size;175}176out:177/* Set status to default */178args_buf->status = FLASH_NO_OP;179return len;180}181182/*183* Validate candidate firmware image184*185* Note:186* We are only interested in first 4K bytes of the187* candidate image.188*/189static ssize_t validate_store(struct kobject *kobj,190struct kobj_attribute *attr,191const char *buf, size_t count)192{193struct validate_flash_t *args_buf = &validate_flash_data;194195if (buf[0] != '1')196return -EINVAL;197198mutex_lock(&image_data_mutex);199200if (image_data.status != IMAGE_READY ||201image_data.size < VALIDATE_BUF_SIZE) {202args_buf->result = VALIDATE_INVALID_IMG;203args_buf->status = VALIDATE_IMG_INCOMPLETE;204goto out;205}206207/* Copy first 4k bytes of candidate image */208memcpy(args_buf->buf, image_data.data, VALIDATE_BUF_SIZE);209210args_buf->status = VALIDATE_IMG_READY;211args_buf->buf_size = VALIDATE_BUF_SIZE;212213/* Validate candidate image */214opal_flash_validate();215216out:217mutex_unlock(&image_data_mutex);218return count;219}220221/*222* Manage flash routine223*/224static inline void opal_flash_manage(uint8_t op)225{226struct manage_flash_t *const args_buf = &manage_flash_data;227228args_buf->status = opal_manage_flash(op);229}230231/*232* Show manage flash status233*/234static ssize_t manage_show(struct kobject *kobj,235struct kobj_attribute *attr, char *buf)236{237struct manage_flash_t *const args_buf = &manage_flash_data;238int rc;239240rc = sprintf(buf, "%d\n", args_buf->status);241/* Set status to default*/242args_buf->status = FLASH_NO_OP;243return rc;244}245246/*247* Manage operations:248* 0 - Reject249* 1 - Commit250*/251static ssize_t manage_store(struct kobject *kobj,252struct kobj_attribute *attr,253const char *buf, size_t count)254{255uint8_t op;256switch (buf[0]) {257case '0':258op = FLASH_REJECT_TMP_SIDE;259break;260case '1':261op = FLASH_COMMIT_TMP_SIDE;262break;263default:264return -EINVAL;265}266267/* commit/reject temporary image */268opal_flash_manage(op);269return count;270}271272/*273* OPAL update flash274*/275static int opal_flash_update(int op)276{277struct opal_sg_list *list;278unsigned long addr;279int64_t rc = OPAL_PARAMETER;280281if (op == FLASH_UPDATE_CANCEL) {282pr_alert("FLASH: Image update cancelled\n");283addr = '\0';284goto flash;285}286287list = opal_vmalloc_to_sg_list(image_data.data, image_data.size);288if (!list)289goto invalid_img;290291/* First entry address */292addr = __pa(list);293294flash:295rc = opal_update_flash(addr);296297invalid_img:298return rc;299}300301/* This gets called just before system reboots */302void opal_flash_update_print_message(void)303{304if (update_flash_data.status != FLASH_IMG_READY)305return;306307pr_alert("FLASH: Flashing new firmware\n");308pr_alert("FLASH: Image is %u bytes\n", image_data.size);309pr_alert("FLASH: Performing flash and reboot/shutdown\n");310pr_alert("FLASH: This will take several minutes. Do not power off!\n");311312/* Small delay to help getting the above message out */313msleep(500);314}315316/*317* Show candidate image status318*/319static ssize_t update_show(struct kobject *kobj,320struct kobj_attribute *attr, char *buf)321{322struct update_flash_t *const args_buf = &update_flash_data;323return sprintf(buf, "%d\n", args_buf->status);324}325326/*327* Set update image flag328* 1 - Flash new image329* 0 - Cancel flash request330*/331static ssize_t update_store(struct kobject *kobj,332struct kobj_attribute *attr,333const char *buf, size_t count)334{335struct update_flash_t *const args_buf = &update_flash_data;336int rc = count;337338mutex_lock(&image_data_mutex);339340switch (buf[0]) {341case '0':342if (args_buf->status == FLASH_IMG_READY)343opal_flash_update(FLASH_UPDATE_CANCEL);344args_buf->status = FLASH_NO_OP;345break;346case '1':347/* Image is loaded? */348if (image_data.status == IMAGE_READY)349args_buf->status =350opal_flash_update(FLASH_UPDATE_INIT);351else352args_buf->status = FLASH_INVALID_IMG;353break;354default:355rc = -EINVAL;356}357358mutex_unlock(&image_data_mutex);359return rc;360}361362/*363* Free image buffer364*/365static void free_image_buf(void)366{367void *addr;368int size;369370addr = image_data.data;371size = PAGE_ALIGN(image_data.size);372while (size > 0) {373ClearPageReserved(vmalloc_to_page(addr));374addr += PAGE_SIZE;375size -= PAGE_SIZE;376}377vfree(image_data.data);378image_data.data = NULL;379image_data.status = IMAGE_INVALID;380}381382/*383* Allocate image buffer.384*/385static int alloc_image_buf(char *buffer, size_t count)386{387void *addr;388int size;389390if (count < sizeof(image_header)) {391pr_warn("FLASH: Invalid candidate image\n");392return -EINVAL;393}394395memcpy(&image_header, (void *)buffer, sizeof(image_header));396image_data.size = be32_to_cpu(image_header.size);397pr_debug("FLASH: Candidate image size = %u\n", image_data.size);398399if (image_data.size > MAX_IMAGE_SIZE) {400pr_warn("FLASH: Too large image\n");401return -EINVAL;402}403if (image_data.size < VALIDATE_BUF_SIZE) {404pr_warn("FLASH: Image is shorter than expected\n");405return -EINVAL;406}407408image_data.data = vzalloc(PAGE_ALIGN(image_data.size));409if (!image_data.data) {410pr_err("%s : Failed to allocate memory\n", __func__);411return -ENOMEM;412}413414/* Pin memory */415addr = image_data.data;416size = PAGE_ALIGN(image_data.size);417while (size > 0) {418SetPageReserved(vmalloc_to_page(addr));419addr += PAGE_SIZE;420size -= PAGE_SIZE;421}422423image_data.status = IMAGE_LOADING;424return 0;425}426427/*428* Copy candidate image429*430* Parse candidate image header to get total image size431* and pre-allocate required memory.432*/433static ssize_t image_data_write(struct file *filp, struct kobject *kobj,434const struct bin_attribute *bin_attr,435char *buffer, loff_t pos, size_t count)436{437int rc;438439mutex_lock(&image_data_mutex);440441/* New image ? */442if (pos == 0) {443/* Free memory, if already allocated */444if (image_data.data)445free_image_buf();446447/* Cancel outstanding image update request */448if (update_flash_data.status == FLASH_IMG_READY)449opal_flash_update(FLASH_UPDATE_CANCEL);450451/* Allocate memory */452rc = alloc_image_buf(buffer, count);453if (rc)454goto out;455}456457if (image_data.status != IMAGE_LOADING) {458rc = -ENOMEM;459goto out;460}461462if ((pos + count) > image_data.size) {463rc = -EINVAL;464goto out;465}466467memcpy(image_data.data + pos, (void *)buffer, count);468rc = count;469470/* Set image status */471if ((pos + count) == image_data.size) {472pr_debug("FLASH: Candidate image loaded....\n");473image_data.status = IMAGE_READY;474}475476out:477mutex_unlock(&image_data_mutex);478return rc;479}480481/*482* sysfs interface :483* OPAL uses below sysfs files for code update.484* We create these files under /sys/firmware/opal.485*486* image : Interface to load candidate firmware image487* validate_flash : Validate firmware image488* manage_flash : Commit/Reject firmware image489* update_flash : Flash new firmware image490*491*/492static const struct bin_attribute image_data_attr = {493.attr = {.name = "image", .mode = 0200},494.size = MAX_IMAGE_SIZE, /* Limit image size */495.write = image_data_write,496};497498static struct kobj_attribute validate_attribute =499__ATTR(validate_flash, 0600, validate_show, validate_store);500501static struct kobj_attribute manage_attribute =502__ATTR(manage_flash, 0600, manage_show, manage_store);503504static struct kobj_attribute update_attribute =505__ATTR(update_flash, 0600, update_show, update_store);506507static struct attribute *image_op_attrs[] = {508&validate_attribute.attr,509&manage_attribute.attr,510&update_attribute.attr,511NULL /* need to NULL terminate the list of attributes */512};513514static const struct attribute_group image_op_attr_group = {515.attrs = image_op_attrs,516};517518void __init opal_flash_update_init(void)519{520int ret;521522/* Firmware update is not supported by firmware */523if (!opal_check_token(OPAL_FLASH_VALIDATE))524return;525526/* Allocate validate image buffer */527validate_flash_data.buf = kzalloc(VALIDATE_BUF_SIZE, GFP_KERNEL);528if (!validate_flash_data.buf) {529pr_err("%s : Failed to allocate memory\n", __func__);530return;531}532533/* Make sure /sys/firmware/opal directory is created */534if (!opal_kobj) {535pr_warn("FLASH: opal kobject is not available\n");536goto nokobj;537}538539/* Create the sysfs files */540ret = sysfs_create_group(opal_kobj, &image_op_attr_group);541if (ret) {542pr_warn("FLASH: Failed to create sysfs files\n");543goto nokobj;544}545546ret = sysfs_create_bin_file(opal_kobj, &image_data_attr);547if (ret) {548pr_warn("FLASH: Failed to create sysfs files\n");549goto nosysfs_file;550}551552/* Set default status */553validate_flash_data.status = FLASH_NO_OP;554manage_flash_data.status = FLASH_NO_OP;555update_flash_data.status = FLASH_NO_OP;556image_data.status = IMAGE_INVALID;557return;558559nosysfs_file:560sysfs_remove_group(opal_kobj, &image_op_attr_group);561562nokobj:563kfree(validate_flash_data.buf);564return;565}566567568