162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PowerNV OPAL Firmware Update Interface 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2013 IBM Corp. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define DEBUG 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/reboot.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/kobject.h> 1462306a36Sopenharmony_ci#include <linux/sysfs.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/mm.h> 1762306a36Sopenharmony_ci#include <linux/vmalloc.h> 1862306a36Sopenharmony_ci#include <linux/pagemap.h> 1962306a36Sopenharmony_ci#include <linux/delay.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <asm/opal.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* FLASH status codes */ 2462306a36Sopenharmony_ci#define FLASH_NO_OP -1099 /* No operation initiated by user */ 2562306a36Sopenharmony_ci#define FLASH_NO_AUTH -9002 /* Not a service authority partition */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* Validate image status values */ 2862306a36Sopenharmony_ci#define VALIDATE_IMG_READY -1001 /* Image ready for validation */ 2962306a36Sopenharmony_ci#define VALIDATE_IMG_INCOMPLETE -1002 /* User copied < VALIDATE_BUF_SIZE */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* Manage image status values */ 3262306a36Sopenharmony_ci#define MANAGE_ACTIVE_ERR -9001 /* Cannot overwrite active img */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* Flash image status values */ 3562306a36Sopenharmony_ci#define FLASH_IMG_READY 0 /* Img ready for flash on reboot */ 3662306a36Sopenharmony_ci#define FLASH_INVALID_IMG -1003 /* Flash image shorter than expected */ 3762306a36Sopenharmony_ci#define FLASH_IMG_NULL_DATA -1004 /* Bad data in sg list entry */ 3862306a36Sopenharmony_ci#define FLASH_IMG_BAD_LEN -1005 /* Bad length in sg list entry */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* Manage operation tokens */ 4162306a36Sopenharmony_ci#define FLASH_REJECT_TMP_SIDE 0 /* Reject temporary fw image */ 4262306a36Sopenharmony_ci#define FLASH_COMMIT_TMP_SIDE 1 /* Commit temporary fw image */ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* Update tokens */ 4562306a36Sopenharmony_ci#define FLASH_UPDATE_CANCEL 0 /* Cancel update request */ 4662306a36Sopenharmony_ci#define FLASH_UPDATE_INIT 1 /* Initiate update */ 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* Validate image update result tokens */ 4962306a36Sopenharmony_ci#define VALIDATE_TMP_UPDATE 0 /* T side will be updated */ 5062306a36Sopenharmony_ci#define VALIDATE_FLASH_AUTH 1 /* Partition does not have authority */ 5162306a36Sopenharmony_ci#define VALIDATE_INVALID_IMG 2 /* Candidate image is not valid */ 5262306a36Sopenharmony_ci#define VALIDATE_CUR_UNKNOWN 3 /* Current fixpack level is unknown */ 5362306a36Sopenharmony_ci/* 5462306a36Sopenharmony_ci * Current T side will be committed to P side before being replace with new 5562306a36Sopenharmony_ci * image, and the new image is downlevel from current image 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci#define VALIDATE_TMP_COMMIT_DL 4 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * Current T side will be committed to P side before being replaced with new 6062306a36Sopenharmony_ci * image 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci#define VALIDATE_TMP_COMMIT 5 6362306a36Sopenharmony_ci/* 6462306a36Sopenharmony_ci * T side will be updated with a downlevel image 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_ci#define VALIDATE_TMP_UPDATE_DL 6 6762306a36Sopenharmony_ci/* 6862306a36Sopenharmony_ci * The candidate image's release date is later than the system's firmware 6962306a36Sopenharmony_ci * service entitlement date - service warranty period has expired 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci#define VALIDATE_OUT_OF_WRNTY 7 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* Validate buffer size */ 7462306a36Sopenharmony_ci#define VALIDATE_BUF_SIZE 4096 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* XXX: Assume candidate image size is <= 1GB */ 7762306a36Sopenharmony_ci#define MAX_IMAGE_SIZE 0x40000000 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* Image status */ 8062306a36Sopenharmony_cienum { 8162306a36Sopenharmony_ci IMAGE_INVALID, 8262306a36Sopenharmony_ci IMAGE_LOADING, 8362306a36Sopenharmony_ci IMAGE_READY, 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* Candidate image data */ 8762306a36Sopenharmony_cistruct image_data_t { 8862306a36Sopenharmony_ci int status; 8962306a36Sopenharmony_ci void *data; 9062306a36Sopenharmony_ci uint32_t size; 9162306a36Sopenharmony_ci}; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* Candidate image header */ 9462306a36Sopenharmony_cistruct image_header_t { 9562306a36Sopenharmony_ci uint16_t magic; 9662306a36Sopenharmony_ci uint16_t version; 9762306a36Sopenharmony_ci uint32_t size; 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistruct validate_flash_t { 10162306a36Sopenharmony_ci int status; /* Return status */ 10262306a36Sopenharmony_ci void *buf; /* Candidate image buffer */ 10362306a36Sopenharmony_ci uint32_t buf_size; /* Image size */ 10462306a36Sopenharmony_ci uint32_t result; /* Update results token */ 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistruct manage_flash_t { 10862306a36Sopenharmony_ci int status; /* Return status */ 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistruct update_flash_t { 11262306a36Sopenharmony_ci int status; /* Return status */ 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic struct image_header_t image_header; 11662306a36Sopenharmony_cistatic struct image_data_t image_data; 11762306a36Sopenharmony_cistatic struct validate_flash_t validate_flash_data; 11862306a36Sopenharmony_cistatic struct manage_flash_t manage_flash_data; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* Initialize update_flash_data status to No Operation */ 12162306a36Sopenharmony_cistatic struct update_flash_t update_flash_data = { 12262306a36Sopenharmony_ci .status = FLASH_NO_OP, 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic DEFINE_MUTEX(image_data_mutex); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* 12862306a36Sopenharmony_ci * Validate candidate image 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_cistatic inline void opal_flash_validate(void) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci long ret; 13362306a36Sopenharmony_ci void *buf = validate_flash_data.buf; 13462306a36Sopenharmony_ci __be32 size = cpu_to_be32(validate_flash_data.buf_size); 13562306a36Sopenharmony_ci __be32 result; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci ret = opal_validate_flash(__pa(buf), &size, &result); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci validate_flash_data.status = ret; 14062306a36Sopenharmony_ci validate_flash_data.buf_size = be32_to_cpu(size); 14162306a36Sopenharmony_ci validate_flash_data.result = be32_to_cpu(result); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* 14562306a36Sopenharmony_ci * Validate output format: 14662306a36Sopenharmony_ci * validate result token 14762306a36Sopenharmony_ci * current image version details 14862306a36Sopenharmony_ci * new image version details 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_cistatic ssize_t validate_show(struct kobject *kobj, 15162306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct validate_flash_t *args_buf = &validate_flash_data; 15462306a36Sopenharmony_ci int len; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Candidate image is not validated */ 15762306a36Sopenharmony_ci if (args_buf->status < VALIDATE_TMP_UPDATE) { 15862306a36Sopenharmony_ci len = sprintf(buf, "%d\n", args_buf->status); 15962306a36Sopenharmony_ci goto out; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* Result token */ 16362306a36Sopenharmony_ci len = sprintf(buf, "%d\n", args_buf->result); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* Current and candidate image version details */ 16662306a36Sopenharmony_ci if ((args_buf->result != VALIDATE_TMP_UPDATE) && 16762306a36Sopenharmony_ci (args_buf->result < VALIDATE_CUR_UNKNOWN)) 16862306a36Sopenharmony_ci goto out; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (args_buf->buf_size > (VALIDATE_BUF_SIZE - len)) { 17162306a36Sopenharmony_ci memcpy(buf + len, args_buf->buf, VALIDATE_BUF_SIZE - len); 17262306a36Sopenharmony_ci len = VALIDATE_BUF_SIZE; 17362306a36Sopenharmony_ci } else { 17462306a36Sopenharmony_ci memcpy(buf + len, args_buf->buf, args_buf->buf_size); 17562306a36Sopenharmony_ci len += args_buf->buf_size; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ciout: 17862306a36Sopenharmony_ci /* Set status to default */ 17962306a36Sopenharmony_ci args_buf->status = FLASH_NO_OP; 18062306a36Sopenharmony_ci return len; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/* 18462306a36Sopenharmony_ci * Validate candidate firmware image 18562306a36Sopenharmony_ci * 18662306a36Sopenharmony_ci * Note: 18762306a36Sopenharmony_ci * We are only interested in first 4K bytes of the 18862306a36Sopenharmony_ci * candidate image. 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_cistatic ssize_t validate_store(struct kobject *kobj, 19162306a36Sopenharmony_ci struct kobj_attribute *attr, 19262306a36Sopenharmony_ci const char *buf, size_t count) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct validate_flash_t *args_buf = &validate_flash_data; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (buf[0] != '1') 19762306a36Sopenharmony_ci return -EINVAL; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci mutex_lock(&image_data_mutex); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (image_data.status != IMAGE_READY || 20262306a36Sopenharmony_ci image_data.size < VALIDATE_BUF_SIZE) { 20362306a36Sopenharmony_ci args_buf->result = VALIDATE_INVALID_IMG; 20462306a36Sopenharmony_ci args_buf->status = VALIDATE_IMG_INCOMPLETE; 20562306a36Sopenharmony_ci goto out; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* Copy first 4k bytes of candidate image */ 20962306a36Sopenharmony_ci memcpy(args_buf->buf, image_data.data, VALIDATE_BUF_SIZE); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci args_buf->status = VALIDATE_IMG_READY; 21262306a36Sopenharmony_ci args_buf->buf_size = VALIDATE_BUF_SIZE; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* Validate candidate image */ 21562306a36Sopenharmony_ci opal_flash_validate(); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ciout: 21862306a36Sopenharmony_ci mutex_unlock(&image_data_mutex); 21962306a36Sopenharmony_ci return count; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci/* 22362306a36Sopenharmony_ci * Manage flash routine 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_cistatic inline void opal_flash_manage(uint8_t op) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct manage_flash_t *const args_buf = &manage_flash_data; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci args_buf->status = opal_manage_flash(op); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/* 23362306a36Sopenharmony_ci * Show manage flash status 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_cistatic ssize_t manage_show(struct kobject *kobj, 23662306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct manage_flash_t *const args_buf = &manage_flash_data; 23962306a36Sopenharmony_ci int rc; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci rc = sprintf(buf, "%d\n", args_buf->status); 24262306a36Sopenharmony_ci /* Set status to default*/ 24362306a36Sopenharmony_ci args_buf->status = FLASH_NO_OP; 24462306a36Sopenharmony_ci return rc; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/* 24862306a36Sopenharmony_ci * Manage operations: 24962306a36Sopenharmony_ci * 0 - Reject 25062306a36Sopenharmony_ci * 1 - Commit 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_cistatic ssize_t manage_store(struct kobject *kobj, 25362306a36Sopenharmony_ci struct kobj_attribute *attr, 25462306a36Sopenharmony_ci const char *buf, size_t count) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci uint8_t op; 25762306a36Sopenharmony_ci switch (buf[0]) { 25862306a36Sopenharmony_ci case '0': 25962306a36Sopenharmony_ci op = FLASH_REJECT_TMP_SIDE; 26062306a36Sopenharmony_ci break; 26162306a36Sopenharmony_ci case '1': 26262306a36Sopenharmony_ci op = FLASH_COMMIT_TMP_SIDE; 26362306a36Sopenharmony_ci break; 26462306a36Sopenharmony_ci default: 26562306a36Sopenharmony_ci return -EINVAL; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* commit/reject temporary image */ 26962306a36Sopenharmony_ci opal_flash_manage(op); 27062306a36Sopenharmony_ci return count; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci/* 27462306a36Sopenharmony_ci * OPAL update flash 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_cistatic int opal_flash_update(int op) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct opal_sg_list *list; 27962306a36Sopenharmony_ci unsigned long addr; 28062306a36Sopenharmony_ci int64_t rc = OPAL_PARAMETER; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (op == FLASH_UPDATE_CANCEL) { 28362306a36Sopenharmony_ci pr_alert("FLASH: Image update cancelled\n"); 28462306a36Sopenharmony_ci addr = '\0'; 28562306a36Sopenharmony_ci goto flash; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci list = opal_vmalloc_to_sg_list(image_data.data, image_data.size); 28962306a36Sopenharmony_ci if (!list) 29062306a36Sopenharmony_ci goto invalid_img; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* First entry address */ 29362306a36Sopenharmony_ci addr = __pa(list); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ciflash: 29662306a36Sopenharmony_ci rc = opal_update_flash(addr); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ciinvalid_img: 29962306a36Sopenharmony_ci return rc; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci/* This gets called just before system reboots */ 30362306a36Sopenharmony_civoid opal_flash_update_print_message(void) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci if (update_flash_data.status != FLASH_IMG_READY) 30662306a36Sopenharmony_ci return; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci pr_alert("FLASH: Flashing new firmware\n"); 30962306a36Sopenharmony_ci pr_alert("FLASH: Image is %u bytes\n", image_data.size); 31062306a36Sopenharmony_ci pr_alert("FLASH: Performing flash and reboot/shutdown\n"); 31162306a36Sopenharmony_ci pr_alert("FLASH: This will take several minutes. Do not power off!\n"); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* Small delay to help getting the above message out */ 31462306a36Sopenharmony_ci msleep(500); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci/* 31862306a36Sopenharmony_ci * Show candidate image status 31962306a36Sopenharmony_ci */ 32062306a36Sopenharmony_cistatic ssize_t update_show(struct kobject *kobj, 32162306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct update_flash_t *const args_buf = &update_flash_data; 32462306a36Sopenharmony_ci return sprintf(buf, "%d\n", args_buf->status); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci/* 32862306a36Sopenharmony_ci * Set update image flag 32962306a36Sopenharmony_ci * 1 - Flash new image 33062306a36Sopenharmony_ci * 0 - Cancel flash request 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_cistatic ssize_t update_store(struct kobject *kobj, 33362306a36Sopenharmony_ci struct kobj_attribute *attr, 33462306a36Sopenharmony_ci const char *buf, size_t count) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct update_flash_t *const args_buf = &update_flash_data; 33762306a36Sopenharmony_ci int rc = count; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci mutex_lock(&image_data_mutex); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci switch (buf[0]) { 34262306a36Sopenharmony_ci case '0': 34362306a36Sopenharmony_ci if (args_buf->status == FLASH_IMG_READY) 34462306a36Sopenharmony_ci opal_flash_update(FLASH_UPDATE_CANCEL); 34562306a36Sopenharmony_ci args_buf->status = FLASH_NO_OP; 34662306a36Sopenharmony_ci break; 34762306a36Sopenharmony_ci case '1': 34862306a36Sopenharmony_ci /* Image is loaded? */ 34962306a36Sopenharmony_ci if (image_data.status == IMAGE_READY) 35062306a36Sopenharmony_ci args_buf->status = 35162306a36Sopenharmony_ci opal_flash_update(FLASH_UPDATE_INIT); 35262306a36Sopenharmony_ci else 35362306a36Sopenharmony_ci args_buf->status = FLASH_INVALID_IMG; 35462306a36Sopenharmony_ci break; 35562306a36Sopenharmony_ci default: 35662306a36Sopenharmony_ci rc = -EINVAL; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci mutex_unlock(&image_data_mutex); 36062306a36Sopenharmony_ci return rc; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci/* 36462306a36Sopenharmony_ci * Free image buffer 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_cistatic void free_image_buf(void) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci void *addr; 36962306a36Sopenharmony_ci int size; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci addr = image_data.data; 37262306a36Sopenharmony_ci size = PAGE_ALIGN(image_data.size); 37362306a36Sopenharmony_ci while (size > 0) { 37462306a36Sopenharmony_ci ClearPageReserved(vmalloc_to_page(addr)); 37562306a36Sopenharmony_ci addr += PAGE_SIZE; 37662306a36Sopenharmony_ci size -= PAGE_SIZE; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci vfree(image_data.data); 37962306a36Sopenharmony_ci image_data.data = NULL; 38062306a36Sopenharmony_ci image_data.status = IMAGE_INVALID; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/* 38462306a36Sopenharmony_ci * Allocate image buffer. 38562306a36Sopenharmony_ci */ 38662306a36Sopenharmony_cistatic int alloc_image_buf(char *buffer, size_t count) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci void *addr; 38962306a36Sopenharmony_ci int size; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (count < sizeof(image_header)) { 39262306a36Sopenharmony_ci pr_warn("FLASH: Invalid candidate image\n"); 39362306a36Sopenharmony_ci return -EINVAL; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci memcpy(&image_header, (void *)buffer, sizeof(image_header)); 39762306a36Sopenharmony_ci image_data.size = be32_to_cpu(image_header.size); 39862306a36Sopenharmony_ci pr_debug("FLASH: Candidate image size = %u\n", image_data.size); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (image_data.size > MAX_IMAGE_SIZE) { 40162306a36Sopenharmony_ci pr_warn("FLASH: Too large image\n"); 40262306a36Sopenharmony_ci return -EINVAL; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci if (image_data.size < VALIDATE_BUF_SIZE) { 40562306a36Sopenharmony_ci pr_warn("FLASH: Image is shorter than expected\n"); 40662306a36Sopenharmony_ci return -EINVAL; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci image_data.data = vzalloc(PAGE_ALIGN(image_data.size)); 41062306a36Sopenharmony_ci if (!image_data.data) { 41162306a36Sopenharmony_ci pr_err("%s : Failed to allocate memory\n", __func__); 41262306a36Sopenharmony_ci return -ENOMEM; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* Pin memory */ 41662306a36Sopenharmony_ci addr = image_data.data; 41762306a36Sopenharmony_ci size = PAGE_ALIGN(image_data.size); 41862306a36Sopenharmony_ci while (size > 0) { 41962306a36Sopenharmony_ci SetPageReserved(vmalloc_to_page(addr)); 42062306a36Sopenharmony_ci addr += PAGE_SIZE; 42162306a36Sopenharmony_ci size -= PAGE_SIZE; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci image_data.status = IMAGE_LOADING; 42562306a36Sopenharmony_ci return 0; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci/* 42962306a36Sopenharmony_ci * Copy candidate image 43062306a36Sopenharmony_ci * 43162306a36Sopenharmony_ci * Parse candidate image header to get total image size 43262306a36Sopenharmony_ci * and pre-allocate required memory. 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_cistatic ssize_t image_data_write(struct file *filp, struct kobject *kobj, 43562306a36Sopenharmony_ci struct bin_attribute *bin_attr, 43662306a36Sopenharmony_ci char *buffer, loff_t pos, size_t count) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci int rc; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci mutex_lock(&image_data_mutex); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* New image ? */ 44362306a36Sopenharmony_ci if (pos == 0) { 44462306a36Sopenharmony_ci /* Free memory, if already allocated */ 44562306a36Sopenharmony_ci if (image_data.data) 44662306a36Sopenharmony_ci free_image_buf(); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* Cancel outstanding image update request */ 44962306a36Sopenharmony_ci if (update_flash_data.status == FLASH_IMG_READY) 45062306a36Sopenharmony_ci opal_flash_update(FLASH_UPDATE_CANCEL); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* Allocate memory */ 45362306a36Sopenharmony_ci rc = alloc_image_buf(buffer, count); 45462306a36Sopenharmony_ci if (rc) 45562306a36Sopenharmony_ci goto out; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (image_data.status != IMAGE_LOADING) { 45962306a36Sopenharmony_ci rc = -ENOMEM; 46062306a36Sopenharmony_ci goto out; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if ((pos + count) > image_data.size) { 46462306a36Sopenharmony_ci rc = -EINVAL; 46562306a36Sopenharmony_ci goto out; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci memcpy(image_data.data + pos, (void *)buffer, count); 46962306a36Sopenharmony_ci rc = count; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* Set image status */ 47262306a36Sopenharmony_ci if ((pos + count) == image_data.size) { 47362306a36Sopenharmony_ci pr_debug("FLASH: Candidate image loaded....\n"); 47462306a36Sopenharmony_ci image_data.status = IMAGE_READY; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ciout: 47862306a36Sopenharmony_ci mutex_unlock(&image_data_mutex); 47962306a36Sopenharmony_ci return rc; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci/* 48362306a36Sopenharmony_ci * sysfs interface : 48462306a36Sopenharmony_ci * OPAL uses below sysfs files for code update. 48562306a36Sopenharmony_ci * We create these files under /sys/firmware/opal. 48662306a36Sopenharmony_ci * 48762306a36Sopenharmony_ci * image : Interface to load candidate firmware image 48862306a36Sopenharmony_ci * validate_flash : Validate firmware image 48962306a36Sopenharmony_ci * manage_flash : Commit/Reject firmware image 49062306a36Sopenharmony_ci * update_flash : Flash new firmware image 49162306a36Sopenharmony_ci * 49262306a36Sopenharmony_ci */ 49362306a36Sopenharmony_cistatic const struct bin_attribute image_data_attr = { 49462306a36Sopenharmony_ci .attr = {.name = "image", .mode = 0200}, 49562306a36Sopenharmony_ci .size = MAX_IMAGE_SIZE, /* Limit image size */ 49662306a36Sopenharmony_ci .write = image_data_write, 49762306a36Sopenharmony_ci}; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic struct kobj_attribute validate_attribute = 50062306a36Sopenharmony_ci __ATTR(validate_flash, 0600, validate_show, validate_store); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic struct kobj_attribute manage_attribute = 50362306a36Sopenharmony_ci __ATTR(manage_flash, 0600, manage_show, manage_store); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic struct kobj_attribute update_attribute = 50662306a36Sopenharmony_ci __ATTR(update_flash, 0600, update_show, update_store); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic struct attribute *image_op_attrs[] = { 50962306a36Sopenharmony_ci &validate_attribute.attr, 51062306a36Sopenharmony_ci &manage_attribute.attr, 51162306a36Sopenharmony_ci &update_attribute.attr, 51262306a36Sopenharmony_ci NULL /* need to NULL terminate the list of attributes */ 51362306a36Sopenharmony_ci}; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic const struct attribute_group image_op_attr_group = { 51662306a36Sopenharmony_ci .attrs = image_op_attrs, 51762306a36Sopenharmony_ci}; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_civoid __init opal_flash_update_init(void) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci int ret; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Firmware update is not supported by firmware */ 52462306a36Sopenharmony_ci if (!opal_check_token(OPAL_FLASH_VALIDATE)) 52562306a36Sopenharmony_ci return; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* Allocate validate image buffer */ 52862306a36Sopenharmony_ci validate_flash_data.buf = kzalloc(VALIDATE_BUF_SIZE, GFP_KERNEL); 52962306a36Sopenharmony_ci if (!validate_flash_data.buf) { 53062306a36Sopenharmony_ci pr_err("%s : Failed to allocate memory\n", __func__); 53162306a36Sopenharmony_ci return; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* Make sure /sys/firmware/opal directory is created */ 53562306a36Sopenharmony_ci if (!opal_kobj) { 53662306a36Sopenharmony_ci pr_warn("FLASH: opal kobject is not available\n"); 53762306a36Sopenharmony_ci goto nokobj; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* Create the sysfs files */ 54162306a36Sopenharmony_ci ret = sysfs_create_group(opal_kobj, &image_op_attr_group); 54262306a36Sopenharmony_ci if (ret) { 54362306a36Sopenharmony_ci pr_warn("FLASH: Failed to create sysfs files\n"); 54462306a36Sopenharmony_ci goto nokobj; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci ret = sysfs_create_bin_file(opal_kobj, &image_data_attr); 54862306a36Sopenharmony_ci if (ret) { 54962306a36Sopenharmony_ci pr_warn("FLASH: Failed to create sysfs files\n"); 55062306a36Sopenharmony_ci goto nosysfs_file; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* Set default status */ 55462306a36Sopenharmony_ci validate_flash_data.status = FLASH_NO_OP; 55562306a36Sopenharmony_ci manage_flash_data.status = FLASH_NO_OP; 55662306a36Sopenharmony_ci update_flash_data.status = FLASH_NO_OP; 55762306a36Sopenharmony_ci image_data.status = IMAGE_INVALID; 55862306a36Sopenharmony_ci return; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cinosysfs_file: 56162306a36Sopenharmony_ci sysfs_remove_group(opal_kobj, &image_op_attr_group); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cinokobj: 56462306a36Sopenharmony_ci kfree(validate_flash_data.buf); 56562306a36Sopenharmony_ci return; 56662306a36Sopenharmony_ci} 567