150bd6153SVasant Hegde /* 250bd6153SVasant Hegde * PowerNV OPAL Firmware Update Interface 350bd6153SVasant Hegde * 450bd6153SVasant Hegde * Copyright 2013 IBM Corp. 550bd6153SVasant Hegde * 650bd6153SVasant Hegde * This program is free software; you can redistribute it and/or 750bd6153SVasant Hegde * modify it under the terms of the GNU General Public License 850bd6153SVasant Hegde * as published by the Free Software Foundation; either version 950bd6153SVasant Hegde * 2 of the License, or (at your option) any later version. 1050bd6153SVasant Hegde */ 1150bd6153SVasant Hegde 1250bd6153SVasant Hegde #define DEBUG 1350bd6153SVasant Hegde 1450bd6153SVasant Hegde #include <linux/kernel.h> 1550bd6153SVasant Hegde #include <linux/reboot.h> 1650bd6153SVasant Hegde #include <linux/init.h> 1750bd6153SVasant Hegde #include <linux/kobject.h> 1850bd6153SVasant Hegde #include <linux/sysfs.h> 1950bd6153SVasant Hegde #include <linux/slab.h> 2050bd6153SVasant Hegde #include <linux/mm.h> 2150bd6153SVasant Hegde #include <linux/vmalloc.h> 2250bd6153SVasant Hegde #include <linux/pagemap.h> 232196c6f1SVasant Hegde #include <linux/delay.h> 2450bd6153SVasant Hegde 2550bd6153SVasant Hegde #include <asm/opal.h> 2650bd6153SVasant Hegde 2750bd6153SVasant Hegde /* FLASH status codes */ 2850bd6153SVasant Hegde #define FLASH_NO_OP -1099 /* No operation initiated by user */ 2950bd6153SVasant Hegde #define FLASH_NO_AUTH -9002 /* Not a service authority partition */ 3050bd6153SVasant Hegde 3150bd6153SVasant Hegde /* Validate image status values */ 3250bd6153SVasant Hegde #define VALIDATE_IMG_READY -1001 /* Image ready for validation */ 3350bd6153SVasant Hegde #define VALIDATE_IMG_INCOMPLETE -1002 /* User copied < VALIDATE_BUF_SIZE */ 3450bd6153SVasant Hegde 3550bd6153SVasant Hegde /* Manage image status values */ 3650bd6153SVasant Hegde #define MANAGE_ACTIVE_ERR -9001 /* Cannot overwrite active img */ 3750bd6153SVasant Hegde 3850bd6153SVasant Hegde /* Flash image status values */ 3950bd6153SVasant Hegde #define FLASH_IMG_READY 0 /* Img ready for flash on reboot */ 4050bd6153SVasant Hegde #define FLASH_INVALID_IMG -1003 /* Flash image shorter than expected */ 4150bd6153SVasant Hegde #define FLASH_IMG_NULL_DATA -1004 /* Bad data in sg list entry */ 4250bd6153SVasant Hegde #define FLASH_IMG_BAD_LEN -1005 /* Bad length in sg list entry */ 4350bd6153SVasant Hegde 4450bd6153SVasant Hegde /* Manage operation tokens */ 4550bd6153SVasant Hegde #define FLASH_REJECT_TMP_SIDE 0 /* Reject temporary fw image */ 4650bd6153SVasant Hegde #define FLASH_COMMIT_TMP_SIDE 1 /* Commit temporary fw image */ 4750bd6153SVasant Hegde 4850bd6153SVasant Hegde /* Update tokens */ 4950bd6153SVasant Hegde #define FLASH_UPDATE_CANCEL 0 /* Cancel update request */ 5050bd6153SVasant Hegde #define FLASH_UPDATE_INIT 1 /* Initiate update */ 5150bd6153SVasant Hegde 5250bd6153SVasant Hegde /* Validate image update result tokens */ 5350bd6153SVasant Hegde #define VALIDATE_TMP_UPDATE 0 /* T side will be updated */ 5450bd6153SVasant Hegde #define VALIDATE_FLASH_AUTH 1 /* Partition does not have authority */ 5550bd6153SVasant Hegde #define VALIDATE_INVALID_IMG 2 /* Candidate image is not valid */ 5650bd6153SVasant Hegde #define VALIDATE_CUR_UNKNOWN 3 /* Current fixpack level is unknown */ 5750bd6153SVasant Hegde /* 5850bd6153SVasant Hegde * Current T side will be committed to P side before being replace with new 5950bd6153SVasant Hegde * image, and the new image is downlevel from current image 6050bd6153SVasant Hegde */ 6150bd6153SVasant Hegde #define VALIDATE_TMP_COMMIT_DL 4 6250bd6153SVasant Hegde /* 6350bd6153SVasant Hegde * Current T side will be committed to P side before being replaced with new 6450bd6153SVasant Hegde * image 6550bd6153SVasant Hegde */ 6650bd6153SVasant Hegde #define VALIDATE_TMP_COMMIT 5 6750bd6153SVasant Hegde /* 6850bd6153SVasant Hegde * T side will be updated with a downlevel image 6950bd6153SVasant Hegde */ 7050bd6153SVasant Hegde #define VALIDATE_TMP_UPDATE_DL 6 7150bd6153SVasant Hegde /* 7250bd6153SVasant Hegde * The candidate image's release date is later than the system's firmware 7350bd6153SVasant Hegde * service entitlement date - service warranty period has expired 7450bd6153SVasant Hegde */ 7550bd6153SVasant Hegde #define VALIDATE_OUT_OF_WRNTY 7 7650bd6153SVasant Hegde 7750bd6153SVasant Hegde /* Validate buffer size */ 7850bd6153SVasant Hegde #define VALIDATE_BUF_SIZE 4096 7950bd6153SVasant Hegde 80bf16a4c2SVasant Hegde /* XXX: Assume candidate image size is <= 1GB */ 81bf16a4c2SVasant Hegde #define MAX_IMAGE_SIZE 0x40000000 8250bd6153SVasant Hegde 8350bd6153SVasant Hegde /* Image status */ 8450bd6153SVasant Hegde enum { 8550bd6153SVasant Hegde IMAGE_INVALID, 8650bd6153SVasant Hegde IMAGE_LOADING, 8750bd6153SVasant Hegde IMAGE_READY, 8850bd6153SVasant Hegde }; 8950bd6153SVasant Hegde 9050bd6153SVasant Hegde /* Candidate image data */ 9150bd6153SVasant Hegde struct image_data_t { 9250bd6153SVasant Hegde int status; 9350bd6153SVasant Hegde void *data; 9450bd6153SVasant Hegde uint32_t size; 9550bd6153SVasant Hegde }; 9650bd6153SVasant Hegde 9750bd6153SVasant Hegde /* Candidate image header */ 9850bd6153SVasant Hegde struct image_header_t { 9950bd6153SVasant Hegde uint16_t magic; 10050bd6153SVasant Hegde uint16_t version; 10150bd6153SVasant Hegde uint32_t size; 10250bd6153SVasant Hegde }; 10350bd6153SVasant Hegde 10450bd6153SVasant Hegde struct validate_flash_t { 10550bd6153SVasant Hegde int status; /* Return status */ 1068faaaeadSMasanari Iida void *buf; /* Candidate image buffer */ 10750bd6153SVasant Hegde uint32_t buf_size; /* Image size */ 10850bd6153SVasant Hegde uint32_t result; /* Update results token */ 10950bd6153SVasant Hegde }; 11050bd6153SVasant Hegde 11150bd6153SVasant Hegde struct manage_flash_t { 11250bd6153SVasant Hegde int status; /* Return status */ 11350bd6153SVasant Hegde }; 11450bd6153SVasant Hegde 11550bd6153SVasant Hegde struct update_flash_t { 11650bd6153SVasant Hegde int status; /* Return status */ 11750bd6153SVasant Hegde }; 11850bd6153SVasant Hegde 11950bd6153SVasant Hegde static struct image_header_t image_header; 12050bd6153SVasant Hegde static struct image_data_t image_data; 12150bd6153SVasant Hegde static struct validate_flash_t validate_flash_data; 12250bd6153SVasant Hegde static struct manage_flash_t manage_flash_data; 1233f77df7fSVasant Hegde 1243f77df7fSVasant Hegde /* Initialize update_flash_data status to No Operation */ 1253f77df7fSVasant Hegde static struct update_flash_t update_flash_data = { 1263f77df7fSVasant Hegde .status = FLASH_NO_OP, 1273f77df7fSVasant Hegde }; 12850bd6153SVasant Hegde 12950bd6153SVasant Hegde static DEFINE_MUTEX(image_data_mutex); 13050bd6153SVasant Hegde 13150bd6153SVasant Hegde /* 13250bd6153SVasant Hegde * Validate candidate image 13350bd6153SVasant Hegde */ 13450bd6153SVasant Hegde static inline void opal_flash_validate(void) 13550bd6153SVasant Hegde { 136cc146d1dSAnton Blanchard long ret; 137cc146d1dSAnton Blanchard void *buf = validate_flash_data.buf; 1388b8f7bf4SVasant Hegde __be32 size = cpu_to_be32(validate_flash_data.buf_size); 1398b8f7bf4SVasant Hegde __be32 result; 14050bd6153SVasant Hegde 141cc146d1dSAnton Blanchard ret = opal_validate_flash(__pa(buf), &size, &result); 142cc146d1dSAnton Blanchard 143cc146d1dSAnton Blanchard validate_flash_data.status = ret; 144cc146d1dSAnton Blanchard validate_flash_data.buf_size = be32_to_cpu(size); 145cc146d1dSAnton Blanchard validate_flash_data.result = be32_to_cpu(result); 14650bd6153SVasant Hegde } 14750bd6153SVasant Hegde 14850bd6153SVasant Hegde /* 14950bd6153SVasant Hegde * Validate output format: 15050bd6153SVasant Hegde * validate result token 15150bd6153SVasant Hegde * current image version details 15250bd6153SVasant Hegde * new image version details 15350bd6153SVasant Hegde */ 15450bd6153SVasant Hegde static ssize_t validate_show(struct kobject *kobj, 15550bd6153SVasant Hegde struct kobj_attribute *attr, char *buf) 15650bd6153SVasant Hegde { 15750bd6153SVasant Hegde struct validate_flash_t *args_buf = &validate_flash_data; 15850bd6153SVasant Hegde int len; 15950bd6153SVasant Hegde 16050bd6153SVasant Hegde /* Candidate image is not validated */ 16150bd6153SVasant Hegde if (args_buf->status < VALIDATE_TMP_UPDATE) { 16250bd6153SVasant Hegde len = sprintf(buf, "%d\n", args_buf->status); 16350bd6153SVasant Hegde goto out; 16450bd6153SVasant Hegde } 16550bd6153SVasant Hegde 16650bd6153SVasant Hegde /* Result token */ 16750bd6153SVasant Hegde len = sprintf(buf, "%d\n", args_buf->result); 16850bd6153SVasant Hegde 16950bd6153SVasant Hegde /* Current and candidate image version details */ 17050bd6153SVasant Hegde if ((args_buf->result != VALIDATE_TMP_UPDATE) && 17150bd6153SVasant Hegde (args_buf->result < VALIDATE_CUR_UNKNOWN)) 17250bd6153SVasant Hegde goto out; 17350bd6153SVasant Hegde 17450bd6153SVasant Hegde if (args_buf->buf_size > (VALIDATE_BUF_SIZE - len)) { 17550bd6153SVasant Hegde memcpy(buf + len, args_buf->buf, VALIDATE_BUF_SIZE - len); 17650bd6153SVasant Hegde len = VALIDATE_BUF_SIZE; 17750bd6153SVasant Hegde } else { 17850bd6153SVasant Hegde memcpy(buf + len, args_buf->buf, args_buf->buf_size); 17950bd6153SVasant Hegde len += args_buf->buf_size; 18050bd6153SVasant Hegde } 18150bd6153SVasant Hegde out: 18250bd6153SVasant Hegde /* Set status to default */ 18350bd6153SVasant Hegde args_buf->status = FLASH_NO_OP; 18450bd6153SVasant Hegde return len; 18550bd6153SVasant Hegde } 18650bd6153SVasant Hegde 18750bd6153SVasant Hegde /* 18850bd6153SVasant Hegde * Validate candidate firmware image 18950bd6153SVasant Hegde * 19050bd6153SVasant Hegde * Note: 19150bd6153SVasant Hegde * We are only interested in first 4K bytes of the 19250bd6153SVasant Hegde * candidate image. 19350bd6153SVasant Hegde */ 19450bd6153SVasant Hegde static ssize_t validate_store(struct kobject *kobj, 19550bd6153SVasant Hegde struct kobj_attribute *attr, 19650bd6153SVasant Hegde const char *buf, size_t count) 19750bd6153SVasant Hegde { 19850bd6153SVasant Hegde struct validate_flash_t *args_buf = &validate_flash_data; 19950bd6153SVasant Hegde 20050bd6153SVasant Hegde if (buf[0] != '1') 20150bd6153SVasant Hegde return -EINVAL; 20250bd6153SVasant Hegde 20350bd6153SVasant Hegde mutex_lock(&image_data_mutex); 20450bd6153SVasant Hegde 20550bd6153SVasant Hegde if (image_data.status != IMAGE_READY || 20650bd6153SVasant Hegde image_data.size < VALIDATE_BUF_SIZE) { 20750bd6153SVasant Hegde args_buf->result = VALIDATE_INVALID_IMG; 20850bd6153SVasant Hegde args_buf->status = VALIDATE_IMG_INCOMPLETE; 20950bd6153SVasant Hegde goto out; 21050bd6153SVasant Hegde } 21150bd6153SVasant Hegde 21250bd6153SVasant Hegde /* Copy first 4k bytes of candidate image */ 21350bd6153SVasant Hegde memcpy(args_buf->buf, image_data.data, VALIDATE_BUF_SIZE); 21450bd6153SVasant Hegde 21550bd6153SVasant Hegde args_buf->status = VALIDATE_IMG_READY; 21650bd6153SVasant Hegde args_buf->buf_size = VALIDATE_BUF_SIZE; 21750bd6153SVasant Hegde 21850bd6153SVasant Hegde /* Validate candidate image */ 21950bd6153SVasant Hegde opal_flash_validate(); 22050bd6153SVasant Hegde 22150bd6153SVasant Hegde out: 22250bd6153SVasant Hegde mutex_unlock(&image_data_mutex); 22350bd6153SVasant Hegde return count; 22450bd6153SVasant Hegde } 22550bd6153SVasant Hegde 22650bd6153SVasant Hegde /* 22750bd6153SVasant Hegde * Manage flash routine 22850bd6153SVasant Hegde */ 22950bd6153SVasant Hegde static inline void opal_flash_manage(uint8_t op) 23050bd6153SVasant Hegde { 23150bd6153SVasant Hegde struct manage_flash_t *const args_buf = &manage_flash_data; 23250bd6153SVasant Hegde 23350bd6153SVasant Hegde args_buf->status = opal_manage_flash(op); 23450bd6153SVasant Hegde } 23550bd6153SVasant Hegde 23650bd6153SVasant Hegde /* 23750bd6153SVasant Hegde * Show manage flash status 23850bd6153SVasant Hegde */ 23950bd6153SVasant Hegde static ssize_t manage_show(struct kobject *kobj, 24050bd6153SVasant Hegde struct kobj_attribute *attr, char *buf) 24150bd6153SVasant Hegde { 24250bd6153SVasant Hegde struct manage_flash_t *const args_buf = &manage_flash_data; 24350bd6153SVasant Hegde int rc; 24450bd6153SVasant Hegde 24550bd6153SVasant Hegde rc = sprintf(buf, "%d\n", args_buf->status); 24650bd6153SVasant Hegde /* Set status to default*/ 24750bd6153SVasant Hegde args_buf->status = FLASH_NO_OP; 24850bd6153SVasant Hegde return rc; 24950bd6153SVasant Hegde } 25050bd6153SVasant Hegde 25150bd6153SVasant Hegde /* 25250bd6153SVasant Hegde * Manage operations: 25350bd6153SVasant Hegde * 0 - Reject 25450bd6153SVasant Hegde * 1 - Commit 25550bd6153SVasant Hegde */ 25650bd6153SVasant Hegde static ssize_t manage_store(struct kobject *kobj, 25750bd6153SVasant Hegde struct kobj_attribute *attr, 25850bd6153SVasant Hegde const char *buf, size_t count) 25950bd6153SVasant Hegde { 26050bd6153SVasant Hegde uint8_t op; 26150bd6153SVasant Hegde switch (buf[0]) { 26250bd6153SVasant Hegde case '0': 26350bd6153SVasant Hegde op = FLASH_REJECT_TMP_SIDE; 26450bd6153SVasant Hegde break; 26550bd6153SVasant Hegde case '1': 26650bd6153SVasant Hegde op = FLASH_COMMIT_TMP_SIDE; 26750bd6153SVasant Hegde break; 26850bd6153SVasant Hegde default: 26950bd6153SVasant Hegde return -EINVAL; 27050bd6153SVasant Hegde } 27150bd6153SVasant Hegde 27250bd6153SVasant Hegde /* commit/reject temporary image */ 27350bd6153SVasant Hegde opal_flash_manage(op); 27450bd6153SVasant Hegde return count; 27550bd6153SVasant Hegde } 27650bd6153SVasant Hegde 27750bd6153SVasant Hegde /* 27850bd6153SVasant Hegde * OPAL update flash 27950bd6153SVasant Hegde */ 28050bd6153SVasant Hegde static int opal_flash_update(int op) 28150bd6153SVasant Hegde { 2823441f04bSAnton Blanchard struct opal_sg_list *list; 28350bd6153SVasant Hegde unsigned long addr; 28450bd6153SVasant Hegde int64_t rc = OPAL_PARAMETER; 28550bd6153SVasant Hegde 28650bd6153SVasant Hegde if (op == FLASH_UPDATE_CANCEL) { 28750bd6153SVasant Hegde pr_alert("FLASH: Image update cancelled\n"); 28850bd6153SVasant Hegde addr = '\0'; 28950bd6153SVasant Hegde goto flash; 29050bd6153SVasant Hegde } 29150bd6153SVasant Hegde 2923441f04bSAnton Blanchard list = opal_vmalloc_to_sg_list(image_data.data, image_data.size); 29350bd6153SVasant Hegde if (!list) 29450bd6153SVasant Hegde goto invalid_img; 29550bd6153SVasant Hegde 29650bd6153SVasant Hegde /* First entry address */ 29750bd6153SVasant Hegde addr = __pa(list); 29850bd6153SVasant Hegde 29950bd6153SVasant Hegde flash: 30050bd6153SVasant Hegde rc = opal_update_flash(addr); 30150bd6153SVasant Hegde 30250bd6153SVasant Hegde invalid_img: 30350bd6153SVasant Hegde return rc; 30450bd6153SVasant Hegde } 30550bd6153SVasant Hegde 3062196c6f1SVasant Hegde /* Return CPUs to OPAL before starting FW update */ 3072196c6f1SVasant Hegde static void flash_return_cpu(void *info) 3082196c6f1SVasant Hegde { 3092196c6f1SVasant Hegde int cpu = smp_processor_id(); 3102196c6f1SVasant Hegde 3112196c6f1SVasant Hegde if (!cpu_online(cpu)) 3122196c6f1SVasant Hegde return; 3132196c6f1SVasant Hegde 3142196c6f1SVasant Hegde /* Disable IRQ */ 3152196c6f1SVasant Hegde hard_irq_disable(); 3162196c6f1SVasant Hegde 3172196c6f1SVasant Hegde /* Return the CPU to OPAL */ 3182196c6f1SVasant Hegde opal_return_cpu(); 3192196c6f1SVasant Hegde } 3202196c6f1SVasant Hegde 3212196c6f1SVasant Hegde /* This gets called just before system reboots */ 3222196c6f1SVasant Hegde void opal_flash_term_callback(void) 3232196c6f1SVasant Hegde { 3242196c6f1SVasant Hegde struct cpumask mask; 3252196c6f1SVasant Hegde 3262196c6f1SVasant Hegde if (update_flash_data.status != FLASH_IMG_READY) 3272196c6f1SVasant Hegde return; 3282196c6f1SVasant Hegde 3292196c6f1SVasant Hegde pr_alert("FLASH: Flashing new firmware\n"); 3302196c6f1SVasant Hegde pr_alert("FLASH: Image is %u bytes\n", image_data.size); 3312196c6f1SVasant Hegde pr_alert("FLASH: Performing flash and reboot/shutdown\n"); 3322196c6f1SVasant Hegde pr_alert("FLASH: This will take several minutes. Do not power off!\n"); 3332196c6f1SVasant Hegde 3342196c6f1SVasant Hegde /* Small delay to help getting the above message out */ 3352196c6f1SVasant Hegde msleep(500); 3362196c6f1SVasant Hegde 3372196c6f1SVasant Hegde /* Return secondary CPUs to firmware */ 3382196c6f1SVasant Hegde cpumask_copy(&mask, cpu_online_mask); 3392196c6f1SVasant Hegde cpumask_clear_cpu(smp_processor_id(), &mask); 3402196c6f1SVasant Hegde if (!cpumask_empty(&mask)) 3412196c6f1SVasant Hegde smp_call_function_many(&mask, 3422196c6f1SVasant Hegde flash_return_cpu, NULL, false); 3432196c6f1SVasant Hegde /* Hard disable interrupts */ 3442196c6f1SVasant Hegde hard_irq_disable(); 3452196c6f1SVasant Hegde } 3462196c6f1SVasant Hegde 34750bd6153SVasant Hegde /* 34850bd6153SVasant Hegde * Show candidate image status 34950bd6153SVasant Hegde */ 35050bd6153SVasant Hegde static ssize_t update_show(struct kobject *kobj, 35150bd6153SVasant Hegde struct kobj_attribute *attr, char *buf) 35250bd6153SVasant Hegde { 35350bd6153SVasant Hegde struct update_flash_t *const args_buf = &update_flash_data; 35450bd6153SVasant Hegde return sprintf(buf, "%d\n", args_buf->status); 35550bd6153SVasant Hegde } 35650bd6153SVasant Hegde 35750bd6153SVasant Hegde /* 35850bd6153SVasant Hegde * Set update image flag 35950bd6153SVasant Hegde * 1 - Flash new image 36050bd6153SVasant Hegde * 0 - Cancel flash request 36150bd6153SVasant Hegde */ 36250bd6153SVasant Hegde static ssize_t update_store(struct kobject *kobj, 36350bd6153SVasant Hegde struct kobj_attribute *attr, 36450bd6153SVasant Hegde const char *buf, size_t count) 36550bd6153SVasant Hegde { 36650bd6153SVasant Hegde struct update_flash_t *const args_buf = &update_flash_data; 36750bd6153SVasant Hegde int rc = count; 36850bd6153SVasant Hegde 36950bd6153SVasant Hegde mutex_lock(&image_data_mutex); 37050bd6153SVasant Hegde 37150bd6153SVasant Hegde switch (buf[0]) { 37250bd6153SVasant Hegde case '0': 37350bd6153SVasant Hegde if (args_buf->status == FLASH_IMG_READY) 37450bd6153SVasant Hegde opal_flash_update(FLASH_UPDATE_CANCEL); 37550bd6153SVasant Hegde args_buf->status = FLASH_NO_OP; 37650bd6153SVasant Hegde break; 37750bd6153SVasant Hegde case '1': 37850bd6153SVasant Hegde /* Image is loaded? */ 37950bd6153SVasant Hegde if (image_data.status == IMAGE_READY) 38050bd6153SVasant Hegde args_buf->status = 38150bd6153SVasant Hegde opal_flash_update(FLASH_UPDATE_INIT); 38250bd6153SVasant Hegde else 38350bd6153SVasant Hegde args_buf->status = FLASH_INVALID_IMG; 38450bd6153SVasant Hegde break; 38550bd6153SVasant Hegde default: 38650bd6153SVasant Hegde rc = -EINVAL; 38750bd6153SVasant Hegde } 38850bd6153SVasant Hegde 38950bd6153SVasant Hegde mutex_unlock(&image_data_mutex); 39050bd6153SVasant Hegde return rc; 39150bd6153SVasant Hegde } 39250bd6153SVasant Hegde 39350bd6153SVasant Hegde /* 39450bd6153SVasant Hegde * Free image buffer 39550bd6153SVasant Hegde */ 39650bd6153SVasant Hegde static void free_image_buf(void) 39750bd6153SVasant Hegde { 39850bd6153SVasant Hegde void *addr; 39950bd6153SVasant Hegde int size; 40050bd6153SVasant Hegde 40150bd6153SVasant Hegde addr = image_data.data; 40250bd6153SVasant Hegde size = PAGE_ALIGN(image_data.size); 40350bd6153SVasant Hegde while (size > 0) { 40450bd6153SVasant Hegde ClearPageReserved(vmalloc_to_page(addr)); 40550bd6153SVasant Hegde addr += PAGE_SIZE; 40650bd6153SVasant Hegde size -= PAGE_SIZE; 40750bd6153SVasant Hegde } 40850bd6153SVasant Hegde vfree(image_data.data); 40950bd6153SVasant Hegde image_data.data = NULL; 41050bd6153SVasant Hegde image_data.status = IMAGE_INVALID; 41150bd6153SVasant Hegde } 41250bd6153SVasant Hegde 41350bd6153SVasant Hegde /* 41450bd6153SVasant Hegde * Allocate image buffer. 41550bd6153SVasant Hegde */ 41650bd6153SVasant Hegde static int alloc_image_buf(char *buffer, size_t count) 41750bd6153SVasant Hegde { 41850bd6153SVasant Hegde void *addr; 41950bd6153SVasant Hegde int size; 42050bd6153SVasant Hegde 42150bd6153SVasant Hegde if (count < sizeof(struct image_header_t)) { 42250bd6153SVasant Hegde pr_warn("FLASH: Invalid candidate image\n"); 42350bd6153SVasant Hegde return -EINVAL; 42450bd6153SVasant Hegde } 42550bd6153SVasant Hegde 42650bd6153SVasant Hegde memcpy(&image_header, (void *)buffer, sizeof(struct image_header_t)); 42750bd6153SVasant Hegde image_data.size = be32_to_cpu(image_header.size); 4288faaaeadSMasanari Iida pr_debug("FLASH: Candidate image size = %u\n", image_data.size); 42950bd6153SVasant Hegde 43050bd6153SVasant Hegde if (image_data.size > MAX_IMAGE_SIZE) { 43150bd6153SVasant Hegde pr_warn("FLASH: Too large image\n"); 43250bd6153SVasant Hegde return -EINVAL; 43350bd6153SVasant Hegde } 43450bd6153SVasant Hegde if (image_data.size < VALIDATE_BUF_SIZE) { 43550bd6153SVasant Hegde pr_warn("FLASH: Image is shorter than expected\n"); 43650bd6153SVasant Hegde return -EINVAL; 43750bd6153SVasant Hegde } 43850bd6153SVasant Hegde 43950bd6153SVasant Hegde image_data.data = vzalloc(PAGE_ALIGN(image_data.size)); 44050bd6153SVasant Hegde if (!image_data.data) { 44150bd6153SVasant Hegde pr_err("%s : Failed to allocate memory\n", __func__); 44250bd6153SVasant Hegde return -ENOMEM; 44350bd6153SVasant Hegde } 44450bd6153SVasant Hegde 44550bd6153SVasant Hegde /* Pin memory */ 44650bd6153SVasant Hegde addr = image_data.data; 44750bd6153SVasant Hegde size = PAGE_ALIGN(image_data.size); 44850bd6153SVasant Hegde while (size > 0) { 44950bd6153SVasant Hegde SetPageReserved(vmalloc_to_page(addr)); 45050bd6153SVasant Hegde addr += PAGE_SIZE; 45150bd6153SVasant Hegde size -= PAGE_SIZE; 45250bd6153SVasant Hegde } 45350bd6153SVasant Hegde 45450bd6153SVasant Hegde image_data.status = IMAGE_LOADING; 45550bd6153SVasant Hegde return 0; 45650bd6153SVasant Hegde } 45750bd6153SVasant Hegde 45850bd6153SVasant Hegde /* 45950bd6153SVasant Hegde * Copy candidate image 46050bd6153SVasant Hegde * 46150bd6153SVasant Hegde * Parse candidate image header to get total image size 46250bd6153SVasant Hegde * and pre-allocate required memory. 46350bd6153SVasant Hegde */ 46450bd6153SVasant Hegde static ssize_t image_data_write(struct file *filp, struct kobject *kobj, 46550bd6153SVasant Hegde struct bin_attribute *bin_attr, 46650bd6153SVasant Hegde char *buffer, loff_t pos, size_t count) 46750bd6153SVasant Hegde { 46850bd6153SVasant Hegde int rc; 46950bd6153SVasant Hegde 47050bd6153SVasant Hegde mutex_lock(&image_data_mutex); 47150bd6153SVasant Hegde 47250bd6153SVasant Hegde /* New image ? */ 47350bd6153SVasant Hegde if (pos == 0) { 47450bd6153SVasant Hegde /* Free memory, if already allocated */ 47550bd6153SVasant Hegde if (image_data.data) 47650bd6153SVasant Hegde free_image_buf(); 47750bd6153SVasant Hegde 47850bd6153SVasant Hegde /* Cancel outstanding image update request */ 47950bd6153SVasant Hegde if (update_flash_data.status == FLASH_IMG_READY) 48050bd6153SVasant Hegde opal_flash_update(FLASH_UPDATE_CANCEL); 48150bd6153SVasant Hegde 48250bd6153SVasant Hegde /* Allocate memory */ 48350bd6153SVasant Hegde rc = alloc_image_buf(buffer, count); 48450bd6153SVasant Hegde if (rc) 48550bd6153SVasant Hegde goto out; 48650bd6153SVasant Hegde } 48750bd6153SVasant Hegde 48850bd6153SVasant Hegde if (image_data.status != IMAGE_LOADING) { 48950bd6153SVasant Hegde rc = -ENOMEM; 49050bd6153SVasant Hegde goto out; 49150bd6153SVasant Hegde } 49250bd6153SVasant Hegde 49350bd6153SVasant Hegde if ((pos + count) > image_data.size) { 49450bd6153SVasant Hegde rc = -EINVAL; 49550bd6153SVasant Hegde goto out; 49650bd6153SVasant Hegde } 49750bd6153SVasant Hegde 49850bd6153SVasant Hegde memcpy(image_data.data + pos, (void *)buffer, count); 49950bd6153SVasant Hegde rc = count; 50050bd6153SVasant Hegde 50150bd6153SVasant Hegde /* Set image status */ 50250bd6153SVasant Hegde if ((pos + count) == image_data.size) { 50350bd6153SVasant Hegde pr_debug("FLASH: Candidate image loaded....\n"); 50450bd6153SVasant Hegde image_data.status = IMAGE_READY; 50550bd6153SVasant Hegde } 50650bd6153SVasant Hegde 50750bd6153SVasant Hegde out: 50850bd6153SVasant Hegde mutex_unlock(&image_data_mutex); 50950bd6153SVasant Hegde return rc; 51050bd6153SVasant Hegde } 51150bd6153SVasant Hegde 51250bd6153SVasant Hegde /* 51350bd6153SVasant Hegde * sysfs interface : 51450bd6153SVasant Hegde * OPAL uses below sysfs files for code update. 51550bd6153SVasant Hegde * We create these files under /sys/firmware/opal. 51650bd6153SVasant Hegde * 51750bd6153SVasant Hegde * image : Interface to load candidate firmware image 51850bd6153SVasant Hegde * validate_flash : Validate firmware image 51950bd6153SVasant Hegde * manage_flash : Commit/Reject firmware image 52050bd6153SVasant Hegde * update_flash : Flash new firmware image 52150bd6153SVasant Hegde * 52250bd6153SVasant Hegde */ 5238bfa42abSBhumika Goyal static const struct bin_attribute image_data_attr = { 52450bd6153SVasant Hegde .attr = {.name = "image", .mode = 0200}, 52550bd6153SVasant Hegde .size = MAX_IMAGE_SIZE, /* Limit image size */ 52650bd6153SVasant Hegde .write = image_data_write, 52750bd6153SVasant Hegde }; 52850bd6153SVasant Hegde 52950bd6153SVasant Hegde static struct kobj_attribute validate_attribute = 53050bd6153SVasant Hegde __ATTR(validate_flash, 0600, validate_show, validate_store); 53150bd6153SVasant Hegde 53250bd6153SVasant Hegde static struct kobj_attribute manage_attribute = 53350bd6153SVasant Hegde __ATTR(manage_flash, 0600, manage_show, manage_store); 53450bd6153SVasant Hegde 53550bd6153SVasant Hegde static struct kobj_attribute update_attribute = 53650bd6153SVasant Hegde __ATTR(update_flash, 0600, update_show, update_store); 53750bd6153SVasant Hegde 53850bd6153SVasant Hegde static struct attribute *image_op_attrs[] = { 53950bd6153SVasant Hegde &validate_attribute.attr, 54050bd6153SVasant Hegde &manage_attribute.attr, 54150bd6153SVasant Hegde &update_attribute.attr, 54250bd6153SVasant Hegde NULL /* need to NULL terminate the list of attributes */ 54350bd6153SVasant Hegde }; 54450bd6153SVasant Hegde 54550bd6153SVasant Hegde static struct attribute_group image_op_attr_group = { 54650bd6153SVasant Hegde .attrs = image_op_attrs, 54750bd6153SVasant Hegde }; 54850bd6153SVasant Hegde 549ed59190eSCyril Bur void __init opal_flash_update_init(void) 55050bd6153SVasant Hegde { 55150bd6153SVasant Hegde int ret; 55250bd6153SVasant Hegde 55350bd6153SVasant Hegde /* Allocate validate image buffer */ 55450bd6153SVasant Hegde validate_flash_data.buf = kzalloc(VALIDATE_BUF_SIZE, GFP_KERNEL); 55550bd6153SVasant Hegde if (!validate_flash_data.buf) { 55650bd6153SVasant Hegde pr_err("%s : Failed to allocate memory\n", __func__); 55750bd6153SVasant Hegde return; 55850bd6153SVasant Hegde } 55950bd6153SVasant Hegde 56050bd6153SVasant Hegde /* Make sure /sys/firmware/opal directory is created */ 56150bd6153SVasant Hegde if (!opal_kobj) { 56250bd6153SVasant Hegde pr_warn("FLASH: opal kobject is not available\n"); 56350bd6153SVasant Hegde goto nokobj; 56450bd6153SVasant Hegde } 56550bd6153SVasant Hegde 56650bd6153SVasant Hegde /* Create the sysfs files */ 56750bd6153SVasant Hegde ret = sysfs_create_group(opal_kobj, &image_op_attr_group); 56850bd6153SVasant Hegde if (ret) { 56950bd6153SVasant Hegde pr_warn("FLASH: Failed to create sysfs files\n"); 57050bd6153SVasant Hegde goto nokobj; 57150bd6153SVasant Hegde } 57250bd6153SVasant Hegde 57350bd6153SVasant Hegde ret = sysfs_create_bin_file(opal_kobj, &image_data_attr); 57450bd6153SVasant Hegde if (ret) { 57550bd6153SVasant Hegde pr_warn("FLASH: Failed to create sysfs files\n"); 57650bd6153SVasant Hegde goto nosysfs_file; 57750bd6153SVasant Hegde } 57850bd6153SVasant Hegde 57950bd6153SVasant Hegde /* Set default status */ 58050bd6153SVasant Hegde validate_flash_data.status = FLASH_NO_OP; 58150bd6153SVasant Hegde manage_flash_data.status = FLASH_NO_OP; 58250bd6153SVasant Hegde update_flash_data.status = FLASH_NO_OP; 58350bd6153SVasant Hegde image_data.status = IMAGE_INVALID; 58450bd6153SVasant Hegde return; 58550bd6153SVasant Hegde 58650bd6153SVasant Hegde nosysfs_file: 58750bd6153SVasant Hegde sysfs_remove_group(opal_kobj, &image_op_attr_group); 58850bd6153SVasant Hegde 58950bd6153SVasant Hegde nokobj: 59050bd6153SVasant Hegde kfree(validate_flash_data.buf); 59150bd6153SVasant Hegde return; 59250bd6153SVasant Hegde } 593