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 /* This gets called just before system reboots */ 307f2748bdfSNicholas Piggin void opal_flash_update_print_message(void) 3082196c6f1SVasant Hegde { 3092196c6f1SVasant Hegde if (update_flash_data.status != FLASH_IMG_READY) 3102196c6f1SVasant Hegde return; 3112196c6f1SVasant Hegde 3122196c6f1SVasant Hegde pr_alert("FLASH: Flashing new firmware\n"); 3132196c6f1SVasant Hegde pr_alert("FLASH: Image is %u bytes\n", image_data.size); 3142196c6f1SVasant Hegde pr_alert("FLASH: Performing flash and reboot/shutdown\n"); 3152196c6f1SVasant Hegde pr_alert("FLASH: This will take several minutes. Do not power off!\n"); 3162196c6f1SVasant Hegde 3172196c6f1SVasant Hegde /* Small delay to help getting the above message out */ 3182196c6f1SVasant Hegde msleep(500); 3192196c6f1SVasant Hegde } 3202196c6f1SVasant Hegde 32150bd6153SVasant Hegde /* 32250bd6153SVasant Hegde * Show candidate image status 32350bd6153SVasant Hegde */ 32450bd6153SVasant Hegde static ssize_t update_show(struct kobject *kobj, 32550bd6153SVasant Hegde struct kobj_attribute *attr, char *buf) 32650bd6153SVasant Hegde { 32750bd6153SVasant Hegde struct update_flash_t *const args_buf = &update_flash_data; 32850bd6153SVasant Hegde return sprintf(buf, "%d\n", args_buf->status); 32950bd6153SVasant Hegde } 33050bd6153SVasant Hegde 33150bd6153SVasant Hegde /* 33250bd6153SVasant Hegde * Set update image flag 33350bd6153SVasant Hegde * 1 - Flash new image 33450bd6153SVasant Hegde * 0 - Cancel flash request 33550bd6153SVasant Hegde */ 33650bd6153SVasant Hegde static ssize_t update_store(struct kobject *kobj, 33750bd6153SVasant Hegde struct kobj_attribute *attr, 33850bd6153SVasant Hegde const char *buf, size_t count) 33950bd6153SVasant Hegde { 34050bd6153SVasant Hegde struct update_flash_t *const args_buf = &update_flash_data; 34150bd6153SVasant Hegde int rc = count; 34250bd6153SVasant Hegde 34350bd6153SVasant Hegde mutex_lock(&image_data_mutex); 34450bd6153SVasant Hegde 34550bd6153SVasant Hegde switch (buf[0]) { 34650bd6153SVasant Hegde case '0': 34750bd6153SVasant Hegde if (args_buf->status == FLASH_IMG_READY) 34850bd6153SVasant Hegde opal_flash_update(FLASH_UPDATE_CANCEL); 34950bd6153SVasant Hegde args_buf->status = FLASH_NO_OP; 35050bd6153SVasant Hegde break; 35150bd6153SVasant Hegde case '1': 35250bd6153SVasant Hegde /* Image is loaded? */ 35350bd6153SVasant Hegde if (image_data.status == IMAGE_READY) 35450bd6153SVasant Hegde args_buf->status = 35550bd6153SVasant Hegde opal_flash_update(FLASH_UPDATE_INIT); 35650bd6153SVasant Hegde else 35750bd6153SVasant Hegde args_buf->status = FLASH_INVALID_IMG; 35850bd6153SVasant Hegde break; 35950bd6153SVasant Hegde default: 36050bd6153SVasant Hegde rc = -EINVAL; 36150bd6153SVasant Hegde } 36250bd6153SVasant Hegde 36350bd6153SVasant Hegde mutex_unlock(&image_data_mutex); 36450bd6153SVasant Hegde return rc; 36550bd6153SVasant Hegde } 36650bd6153SVasant Hegde 36750bd6153SVasant Hegde /* 36850bd6153SVasant Hegde * Free image buffer 36950bd6153SVasant Hegde */ 37050bd6153SVasant Hegde static void free_image_buf(void) 37150bd6153SVasant Hegde { 37250bd6153SVasant Hegde void *addr; 37350bd6153SVasant Hegde int size; 37450bd6153SVasant Hegde 37550bd6153SVasant Hegde addr = image_data.data; 37650bd6153SVasant Hegde size = PAGE_ALIGN(image_data.size); 37750bd6153SVasant Hegde while (size > 0) { 37850bd6153SVasant Hegde ClearPageReserved(vmalloc_to_page(addr)); 37950bd6153SVasant Hegde addr += PAGE_SIZE; 38050bd6153SVasant Hegde size -= PAGE_SIZE; 38150bd6153SVasant Hegde } 38250bd6153SVasant Hegde vfree(image_data.data); 38350bd6153SVasant Hegde image_data.data = NULL; 38450bd6153SVasant Hegde image_data.status = IMAGE_INVALID; 38550bd6153SVasant Hegde } 38650bd6153SVasant Hegde 38750bd6153SVasant Hegde /* 38850bd6153SVasant Hegde * Allocate image buffer. 38950bd6153SVasant Hegde */ 39050bd6153SVasant Hegde static int alloc_image_buf(char *buffer, size_t count) 39150bd6153SVasant Hegde { 39250bd6153SVasant Hegde void *addr; 39350bd6153SVasant Hegde int size; 39450bd6153SVasant Hegde 395a0828cf5SMarkus Elfring if (count < sizeof(image_header)) { 39650bd6153SVasant Hegde pr_warn("FLASH: Invalid candidate image\n"); 39750bd6153SVasant Hegde return -EINVAL; 39850bd6153SVasant Hegde } 39950bd6153SVasant Hegde 400a0828cf5SMarkus Elfring memcpy(&image_header, (void *)buffer, sizeof(image_header)); 40150bd6153SVasant Hegde image_data.size = be32_to_cpu(image_header.size); 4028faaaeadSMasanari Iida pr_debug("FLASH: Candidate image size = %u\n", image_data.size); 40350bd6153SVasant Hegde 40450bd6153SVasant Hegde if (image_data.size > MAX_IMAGE_SIZE) { 40550bd6153SVasant Hegde pr_warn("FLASH: Too large image\n"); 40650bd6153SVasant Hegde return -EINVAL; 40750bd6153SVasant Hegde } 40850bd6153SVasant Hegde if (image_data.size < VALIDATE_BUF_SIZE) { 40950bd6153SVasant Hegde pr_warn("FLASH: Image is shorter than expected\n"); 41050bd6153SVasant Hegde return -EINVAL; 41150bd6153SVasant Hegde } 41250bd6153SVasant Hegde 41350bd6153SVasant Hegde image_data.data = vzalloc(PAGE_ALIGN(image_data.size)); 41450bd6153SVasant Hegde if (!image_data.data) { 41550bd6153SVasant Hegde pr_err("%s : Failed to allocate memory\n", __func__); 41650bd6153SVasant Hegde return -ENOMEM; 41750bd6153SVasant Hegde } 41850bd6153SVasant Hegde 41950bd6153SVasant Hegde /* Pin memory */ 42050bd6153SVasant Hegde addr = image_data.data; 42150bd6153SVasant Hegde size = PAGE_ALIGN(image_data.size); 42250bd6153SVasant Hegde while (size > 0) { 42350bd6153SVasant Hegde SetPageReserved(vmalloc_to_page(addr)); 42450bd6153SVasant Hegde addr += PAGE_SIZE; 42550bd6153SVasant Hegde size -= PAGE_SIZE; 42650bd6153SVasant Hegde } 42750bd6153SVasant Hegde 42850bd6153SVasant Hegde image_data.status = IMAGE_LOADING; 42950bd6153SVasant Hegde return 0; 43050bd6153SVasant Hegde } 43150bd6153SVasant Hegde 43250bd6153SVasant Hegde /* 43350bd6153SVasant Hegde * Copy candidate image 43450bd6153SVasant Hegde * 43550bd6153SVasant Hegde * Parse candidate image header to get total image size 43650bd6153SVasant Hegde * and pre-allocate required memory. 43750bd6153SVasant Hegde */ 43850bd6153SVasant Hegde static ssize_t image_data_write(struct file *filp, struct kobject *kobj, 43950bd6153SVasant Hegde struct bin_attribute *bin_attr, 44050bd6153SVasant Hegde char *buffer, loff_t pos, size_t count) 44150bd6153SVasant Hegde { 44250bd6153SVasant Hegde int rc; 44350bd6153SVasant Hegde 44450bd6153SVasant Hegde mutex_lock(&image_data_mutex); 44550bd6153SVasant Hegde 44650bd6153SVasant Hegde /* New image ? */ 44750bd6153SVasant Hegde if (pos == 0) { 44850bd6153SVasant Hegde /* Free memory, if already allocated */ 44950bd6153SVasant Hegde if (image_data.data) 45050bd6153SVasant Hegde free_image_buf(); 45150bd6153SVasant Hegde 45250bd6153SVasant Hegde /* Cancel outstanding image update request */ 45350bd6153SVasant Hegde if (update_flash_data.status == FLASH_IMG_READY) 45450bd6153SVasant Hegde opal_flash_update(FLASH_UPDATE_CANCEL); 45550bd6153SVasant Hegde 45650bd6153SVasant Hegde /* Allocate memory */ 45750bd6153SVasant Hegde rc = alloc_image_buf(buffer, count); 45850bd6153SVasant Hegde if (rc) 45950bd6153SVasant Hegde goto out; 46050bd6153SVasant Hegde } 46150bd6153SVasant Hegde 46250bd6153SVasant Hegde if (image_data.status != IMAGE_LOADING) { 46350bd6153SVasant Hegde rc = -ENOMEM; 46450bd6153SVasant Hegde goto out; 46550bd6153SVasant Hegde } 46650bd6153SVasant Hegde 46750bd6153SVasant Hegde if ((pos + count) > image_data.size) { 46850bd6153SVasant Hegde rc = -EINVAL; 46950bd6153SVasant Hegde goto out; 47050bd6153SVasant Hegde } 47150bd6153SVasant Hegde 47250bd6153SVasant Hegde memcpy(image_data.data + pos, (void *)buffer, count); 47350bd6153SVasant Hegde rc = count; 47450bd6153SVasant Hegde 47550bd6153SVasant Hegde /* Set image status */ 47650bd6153SVasant Hegde if ((pos + count) == image_data.size) { 47750bd6153SVasant Hegde pr_debug("FLASH: Candidate image loaded....\n"); 47850bd6153SVasant Hegde image_data.status = IMAGE_READY; 47950bd6153SVasant Hegde } 48050bd6153SVasant Hegde 48150bd6153SVasant Hegde out: 48250bd6153SVasant Hegde mutex_unlock(&image_data_mutex); 48350bd6153SVasant Hegde return rc; 48450bd6153SVasant Hegde } 48550bd6153SVasant Hegde 48650bd6153SVasant Hegde /* 48750bd6153SVasant Hegde * sysfs interface : 48850bd6153SVasant Hegde * OPAL uses below sysfs files for code update. 48950bd6153SVasant Hegde * We create these files under /sys/firmware/opal. 49050bd6153SVasant Hegde * 49150bd6153SVasant Hegde * image : Interface to load candidate firmware image 49250bd6153SVasant Hegde * validate_flash : Validate firmware image 49350bd6153SVasant Hegde * manage_flash : Commit/Reject firmware image 49450bd6153SVasant Hegde * update_flash : Flash new firmware image 49550bd6153SVasant Hegde * 49650bd6153SVasant Hegde */ 4978bfa42abSBhumika Goyal static const struct bin_attribute image_data_attr = { 49850bd6153SVasant Hegde .attr = {.name = "image", .mode = 0200}, 49950bd6153SVasant Hegde .size = MAX_IMAGE_SIZE, /* Limit image size */ 50050bd6153SVasant Hegde .write = image_data_write, 50150bd6153SVasant Hegde }; 50250bd6153SVasant Hegde 50350bd6153SVasant Hegde static struct kobj_attribute validate_attribute = 50450bd6153SVasant Hegde __ATTR(validate_flash, 0600, validate_show, validate_store); 50550bd6153SVasant Hegde 50650bd6153SVasant Hegde static struct kobj_attribute manage_attribute = 50750bd6153SVasant Hegde __ATTR(manage_flash, 0600, manage_show, manage_store); 50850bd6153SVasant Hegde 50950bd6153SVasant Hegde static struct kobj_attribute update_attribute = 51050bd6153SVasant Hegde __ATTR(update_flash, 0600, update_show, update_store); 51150bd6153SVasant Hegde 51250bd6153SVasant Hegde static struct attribute *image_op_attrs[] = { 51350bd6153SVasant Hegde &validate_attribute.attr, 51450bd6153SVasant Hegde &manage_attribute.attr, 51550bd6153SVasant Hegde &update_attribute.attr, 51650bd6153SVasant Hegde NULL /* need to NULL terminate the list of attributes */ 51750bd6153SVasant Hegde }; 51850bd6153SVasant Hegde 51950bd6153SVasant Hegde static struct attribute_group image_op_attr_group = { 52050bd6153SVasant Hegde .attrs = image_op_attrs, 52150bd6153SVasant Hegde }; 52250bd6153SVasant Hegde 523ed59190eSCyril Bur void __init opal_flash_update_init(void) 52450bd6153SVasant Hegde { 52550bd6153SVasant Hegde int ret; 52650bd6153SVasant Hegde 52750bd6153SVasant Hegde /* Allocate validate image buffer */ 52850bd6153SVasant Hegde validate_flash_data.buf = kzalloc(VALIDATE_BUF_SIZE, GFP_KERNEL); 52950bd6153SVasant Hegde if (!validate_flash_data.buf) { 53050bd6153SVasant Hegde pr_err("%s : Failed to allocate memory\n", __func__); 53150bd6153SVasant Hegde return; 53250bd6153SVasant Hegde } 53350bd6153SVasant Hegde 53450bd6153SVasant Hegde /* Make sure /sys/firmware/opal directory is created */ 53550bd6153SVasant Hegde if (!opal_kobj) { 53650bd6153SVasant Hegde pr_warn("FLASH: opal kobject is not available\n"); 53750bd6153SVasant Hegde goto nokobj; 53850bd6153SVasant Hegde } 53950bd6153SVasant Hegde 54050bd6153SVasant Hegde /* Create the sysfs files */ 54150bd6153SVasant Hegde ret = sysfs_create_group(opal_kobj, &image_op_attr_group); 54250bd6153SVasant Hegde if (ret) { 54350bd6153SVasant Hegde pr_warn("FLASH: Failed to create sysfs files\n"); 54450bd6153SVasant Hegde goto nokobj; 54550bd6153SVasant Hegde } 54650bd6153SVasant Hegde 54750bd6153SVasant Hegde ret = sysfs_create_bin_file(opal_kobj, &image_data_attr); 54850bd6153SVasant Hegde if (ret) { 54950bd6153SVasant Hegde pr_warn("FLASH: Failed to create sysfs files\n"); 55050bd6153SVasant Hegde goto nosysfs_file; 55150bd6153SVasant Hegde } 55250bd6153SVasant Hegde 55350bd6153SVasant Hegde /* Set default status */ 55450bd6153SVasant Hegde validate_flash_data.status = FLASH_NO_OP; 55550bd6153SVasant Hegde manage_flash_data.status = FLASH_NO_OP; 55650bd6153SVasant Hegde update_flash_data.status = FLASH_NO_OP; 55750bd6153SVasant Hegde image_data.status = IMAGE_INVALID; 55850bd6153SVasant Hegde return; 55950bd6153SVasant Hegde 56050bd6153SVasant Hegde nosysfs_file: 56150bd6153SVasant Hegde sysfs_remove_group(opal_kobj, &image_op_attr_group); 56250bd6153SVasant Hegde 56350bd6153SVasant Hegde nokobj: 56450bd6153SVasant Hegde kfree(validate_flash_data.buf); 56550bd6153SVasant Hegde return; 56650bd6153SVasant Hegde } 567