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; 12350bd6153SVasant Hegde static struct update_flash_t update_flash_data; 12450bd6153SVasant Hegde 12550bd6153SVasant Hegde static DEFINE_MUTEX(image_data_mutex); 12650bd6153SVasant Hegde 12750bd6153SVasant Hegde /* 12850bd6153SVasant Hegde * Validate candidate image 12950bd6153SVasant Hegde */ 13050bd6153SVasant Hegde static inline void opal_flash_validate(void) 13150bd6153SVasant Hegde { 132cc146d1dSAnton Blanchard long ret; 133cc146d1dSAnton Blanchard void *buf = validate_flash_data.buf; 1348b8f7bf4SVasant Hegde __be32 size = cpu_to_be32(validate_flash_data.buf_size); 1358b8f7bf4SVasant Hegde __be32 result; 13650bd6153SVasant Hegde 137cc146d1dSAnton Blanchard ret = opal_validate_flash(__pa(buf), &size, &result); 138cc146d1dSAnton Blanchard 139cc146d1dSAnton Blanchard validate_flash_data.status = ret; 140cc146d1dSAnton Blanchard validate_flash_data.buf_size = be32_to_cpu(size); 141cc146d1dSAnton Blanchard validate_flash_data.result = be32_to_cpu(result); 14250bd6153SVasant Hegde } 14350bd6153SVasant Hegde 14450bd6153SVasant Hegde /* 14550bd6153SVasant Hegde * Validate output format: 14650bd6153SVasant Hegde * validate result token 14750bd6153SVasant Hegde * current image version details 14850bd6153SVasant Hegde * new image version details 14950bd6153SVasant Hegde */ 15050bd6153SVasant Hegde static ssize_t validate_show(struct kobject *kobj, 15150bd6153SVasant Hegde struct kobj_attribute *attr, char *buf) 15250bd6153SVasant Hegde { 15350bd6153SVasant Hegde struct validate_flash_t *args_buf = &validate_flash_data; 15450bd6153SVasant Hegde int len; 15550bd6153SVasant Hegde 15650bd6153SVasant Hegde /* Candidate image is not validated */ 15750bd6153SVasant Hegde if (args_buf->status < VALIDATE_TMP_UPDATE) { 15850bd6153SVasant Hegde len = sprintf(buf, "%d\n", args_buf->status); 15950bd6153SVasant Hegde goto out; 16050bd6153SVasant Hegde } 16150bd6153SVasant Hegde 16250bd6153SVasant Hegde /* Result token */ 16350bd6153SVasant Hegde len = sprintf(buf, "%d\n", args_buf->result); 16450bd6153SVasant Hegde 16550bd6153SVasant Hegde /* Current and candidate image version details */ 16650bd6153SVasant Hegde if ((args_buf->result != VALIDATE_TMP_UPDATE) && 16750bd6153SVasant Hegde (args_buf->result < VALIDATE_CUR_UNKNOWN)) 16850bd6153SVasant Hegde goto out; 16950bd6153SVasant Hegde 17050bd6153SVasant Hegde if (args_buf->buf_size > (VALIDATE_BUF_SIZE - len)) { 17150bd6153SVasant Hegde memcpy(buf + len, args_buf->buf, VALIDATE_BUF_SIZE - len); 17250bd6153SVasant Hegde len = VALIDATE_BUF_SIZE; 17350bd6153SVasant Hegde } else { 17450bd6153SVasant Hegde memcpy(buf + len, args_buf->buf, args_buf->buf_size); 17550bd6153SVasant Hegde len += args_buf->buf_size; 17650bd6153SVasant Hegde } 17750bd6153SVasant Hegde out: 17850bd6153SVasant Hegde /* Set status to default */ 17950bd6153SVasant Hegde args_buf->status = FLASH_NO_OP; 18050bd6153SVasant Hegde return len; 18150bd6153SVasant Hegde } 18250bd6153SVasant Hegde 18350bd6153SVasant Hegde /* 18450bd6153SVasant Hegde * Validate candidate firmware image 18550bd6153SVasant Hegde * 18650bd6153SVasant Hegde * Note: 18750bd6153SVasant Hegde * We are only interested in first 4K bytes of the 18850bd6153SVasant Hegde * candidate image. 18950bd6153SVasant Hegde */ 19050bd6153SVasant Hegde static ssize_t validate_store(struct kobject *kobj, 19150bd6153SVasant Hegde struct kobj_attribute *attr, 19250bd6153SVasant Hegde const char *buf, size_t count) 19350bd6153SVasant Hegde { 19450bd6153SVasant Hegde struct validate_flash_t *args_buf = &validate_flash_data; 19550bd6153SVasant Hegde 19650bd6153SVasant Hegde if (buf[0] != '1') 19750bd6153SVasant Hegde return -EINVAL; 19850bd6153SVasant Hegde 19950bd6153SVasant Hegde mutex_lock(&image_data_mutex); 20050bd6153SVasant Hegde 20150bd6153SVasant Hegde if (image_data.status != IMAGE_READY || 20250bd6153SVasant Hegde image_data.size < VALIDATE_BUF_SIZE) { 20350bd6153SVasant Hegde args_buf->result = VALIDATE_INVALID_IMG; 20450bd6153SVasant Hegde args_buf->status = VALIDATE_IMG_INCOMPLETE; 20550bd6153SVasant Hegde goto out; 20650bd6153SVasant Hegde } 20750bd6153SVasant Hegde 20850bd6153SVasant Hegde /* Copy first 4k bytes of candidate image */ 20950bd6153SVasant Hegde memcpy(args_buf->buf, image_data.data, VALIDATE_BUF_SIZE); 21050bd6153SVasant Hegde 21150bd6153SVasant Hegde args_buf->status = VALIDATE_IMG_READY; 21250bd6153SVasant Hegde args_buf->buf_size = VALIDATE_BUF_SIZE; 21350bd6153SVasant Hegde 21450bd6153SVasant Hegde /* Validate candidate image */ 21550bd6153SVasant Hegde opal_flash_validate(); 21650bd6153SVasant Hegde 21750bd6153SVasant Hegde out: 21850bd6153SVasant Hegde mutex_unlock(&image_data_mutex); 21950bd6153SVasant Hegde return count; 22050bd6153SVasant Hegde } 22150bd6153SVasant Hegde 22250bd6153SVasant Hegde /* 22350bd6153SVasant Hegde * Manage flash routine 22450bd6153SVasant Hegde */ 22550bd6153SVasant Hegde static inline void opal_flash_manage(uint8_t op) 22650bd6153SVasant Hegde { 22750bd6153SVasant Hegde struct manage_flash_t *const args_buf = &manage_flash_data; 22850bd6153SVasant Hegde 22950bd6153SVasant Hegde args_buf->status = opal_manage_flash(op); 23050bd6153SVasant Hegde } 23150bd6153SVasant Hegde 23250bd6153SVasant Hegde /* 23350bd6153SVasant Hegde * Show manage flash status 23450bd6153SVasant Hegde */ 23550bd6153SVasant Hegde static ssize_t manage_show(struct kobject *kobj, 23650bd6153SVasant Hegde struct kobj_attribute *attr, char *buf) 23750bd6153SVasant Hegde { 23850bd6153SVasant Hegde struct manage_flash_t *const args_buf = &manage_flash_data; 23950bd6153SVasant Hegde int rc; 24050bd6153SVasant Hegde 24150bd6153SVasant Hegde rc = sprintf(buf, "%d\n", args_buf->status); 24250bd6153SVasant Hegde /* Set status to default*/ 24350bd6153SVasant Hegde args_buf->status = FLASH_NO_OP; 24450bd6153SVasant Hegde return rc; 24550bd6153SVasant Hegde } 24650bd6153SVasant Hegde 24750bd6153SVasant Hegde /* 24850bd6153SVasant Hegde * Manage operations: 24950bd6153SVasant Hegde * 0 - Reject 25050bd6153SVasant Hegde * 1 - Commit 25150bd6153SVasant Hegde */ 25250bd6153SVasant Hegde static ssize_t manage_store(struct kobject *kobj, 25350bd6153SVasant Hegde struct kobj_attribute *attr, 25450bd6153SVasant Hegde const char *buf, size_t count) 25550bd6153SVasant Hegde { 25650bd6153SVasant Hegde uint8_t op; 25750bd6153SVasant Hegde switch (buf[0]) { 25850bd6153SVasant Hegde case '0': 25950bd6153SVasant Hegde op = FLASH_REJECT_TMP_SIDE; 26050bd6153SVasant Hegde break; 26150bd6153SVasant Hegde case '1': 26250bd6153SVasant Hegde op = FLASH_COMMIT_TMP_SIDE; 26350bd6153SVasant Hegde break; 26450bd6153SVasant Hegde default: 26550bd6153SVasant Hegde return -EINVAL; 26650bd6153SVasant Hegde } 26750bd6153SVasant Hegde 26850bd6153SVasant Hegde /* commit/reject temporary image */ 26950bd6153SVasant Hegde opal_flash_manage(op); 27050bd6153SVasant Hegde return count; 27150bd6153SVasant Hegde } 27250bd6153SVasant Hegde 27350bd6153SVasant Hegde /* 27450bd6153SVasant Hegde * OPAL update flash 27550bd6153SVasant Hegde */ 27650bd6153SVasant Hegde static int opal_flash_update(int op) 27750bd6153SVasant Hegde { 2783441f04bSAnton Blanchard struct opal_sg_list *list; 27950bd6153SVasant Hegde unsigned long addr; 28050bd6153SVasant Hegde int64_t rc = OPAL_PARAMETER; 28150bd6153SVasant Hegde 28250bd6153SVasant Hegde if (op == FLASH_UPDATE_CANCEL) { 28350bd6153SVasant Hegde pr_alert("FLASH: Image update cancelled\n"); 28450bd6153SVasant Hegde addr = '\0'; 28550bd6153SVasant Hegde goto flash; 28650bd6153SVasant Hegde } 28750bd6153SVasant Hegde 2883441f04bSAnton Blanchard list = opal_vmalloc_to_sg_list(image_data.data, image_data.size); 28950bd6153SVasant Hegde if (!list) 29050bd6153SVasant Hegde goto invalid_img; 29150bd6153SVasant Hegde 29250bd6153SVasant Hegde /* First entry address */ 29350bd6153SVasant Hegde addr = __pa(list); 29450bd6153SVasant Hegde 29550bd6153SVasant Hegde flash: 29650bd6153SVasant Hegde rc = opal_update_flash(addr); 29750bd6153SVasant Hegde 29850bd6153SVasant Hegde invalid_img: 29950bd6153SVasant Hegde return rc; 30050bd6153SVasant Hegde } 30150bd6153SVasant Hegde 3022196c6f1SVasant Hegde /* Return CPUs to OPAL before starting FW update */ 3032196c6f1SVasant Hegde static void flash_return_cpu(void *info) 3042196c6f1SVasant Hegde { 3052196c6f1SVasant Hegde int cpu = smp_processor_id(); 3062196c6f1SVasant Hegde 3072196c6f1SVasant Hegde if (!cpu_online(cpu)) 3082196c6f1SVasant Hegde return; 3092196c6f1SVasant Hegde 3102196c6f1SVasant Hegde /* Disable IRQ */ 3112196c6f1SVasant Hegde hard_irq_disable(); 3122196c6f1SVasant Hegde 3132196c6f1SVasant Hegde /* Return the CPU to OPAL */ 3142196c6f1SVasant Hegde opal_return_cpu(); 3152196c6f1SVasant Hegde } 3162196c6f1SVasant Hegde 3172196c6f1SVasant Hegde /* This gets called just before system reboots */ 3182196c6f1SVasant Hegde void opal_flash_term_callback(void) 3192196c6f1SVasant Hegde { 3202196c6f1SVasant Hegde struct cpumask mask; 3212196c6f1SVasant Hegde 3222196c6f1SVasant Hegde if (update_flash_data.status != FLASH_IMG_READY) 3232196c6f1SVasant Hegde return; 3242196c6f1SVasant Hegde 3252196c6f1SVasant Hegde pr_alert("FLASH: Flashing new firmware\n"); 3262196c6f1SVasant Hegde pr_alert("FLASH: Image is %u bytes\n", image_data.size); 3272196c6f1SVasant Hegde pr_alert("FLASH: Performing flash and reboot/shutdown\n"); 3282196c6f1SVasant Hegde pr_alert("FLASH: This will take several minutes. Do not power off!\n"); 3292196c6f1SVasant Hegde 3302196c6f1SVasant Hegde /* Small delay to help getting the above message out */ 3312196c6f1SVasant Hegde msleep(500); 3322196c6f1SVasant Hegde 3332196c6f1SVasant Hegde /* Return secondary CPUs to firmware */ 3342196c6f1SVasant Hegde cpumask_copy(&mask, cpu_online_mask); 3352196c6f1SVasant Hegde cpumask_clear_cpu(smp_processor_id(), &mask); 3362196c6f1SVasant Hegde if (!cpumask_empty(&mask)) 3372196c6f1SVasant Hegde smp_call_function_many(&mask, 3382196c6f1SVasant Hegde flash_return_cpu, NULL, false); 3392196c6f1SVasant Hegde /* Hard disable interrupts */ 3402196c6f1SVasant Hegde hard_irq_disable(); 3412196c6f1SVasant Hegde } 3422196c6f1SVasant Hegde 34350bd6153SVasant Hegde /* 34450bd6153SVasant Hegde * Show candidate image status 34550bd6153SVasant Hegde */ 34650bd6153SVasant Hegde static ssize_t update_show(struct kobject *kobj, 34750bd6153SVasant Hegde struct kobj_attribute *attr, char *buf) 34850bd6153SVasant Hegde { 34950bd6153SVasant Hegde struct update_flash_t *const args_buf = &update_flash_data; 35050bd6153SVasant Hegde return sprintf(buf, "%d\n", args_buf->status); 35150bd6153SVasant Hegde } 35250bd6153SVasant Hegde 35350bd6153SVasant Hegde /* 35450bd6153SVasant Hegde * Set update image flag 35550bd6153SVasant Hegde * 1 - Flash new image 35650bd6153SVasant Hegde * 0 - Cancel flash request 35750bd6153SVasant Hegde */ 35850bd6153SVasant Hegde static ssize_t update_store(struct kobject *kobj, 35950bd6153SVasant Hegde struct kobj_attribute *attr, 36050bd6153SVasant Hegde const char *buf, size_t count) 36150bd6153SVasant Hegde { 36250bd6153SVasant Hegde struct update_flash_t *const args_buf = &update_flash_data; 36350bd6153SVasant Hegde int rc = count; 36450bd6153SVasant Hegde 36550bd6153SVasant Hegde mutex_lock(&image_data_mutex); 36650bd6153SVasant Hegde 36750bd6153SVasant Hegde switch (buf[0]) { 36850bd6153SVasant Hegde case '0': 36950bd6153SVasant Hegde if (args_buf->status == FLASH_IMG_READY) 37050bd6153SVasant Hegde opal_flash_update(FLASH_UPDATE_CANCEL); 37150bd6153SVasant Hegde args_buf->status = FLASH_NO_OP; 37250bd6153SVasant Hegde break; 37350bd6153SVasant Hegde case '1': 37450bd6153SVasant Hegde /* Image is loaded? */ 37550bd6153SVasant Hegde if (image_data.status == IMAGE_READY) 37650bd6153SVasant Hegde args_buf->status = 37750bd6153SVasant Hegde opal_flash_update(FLASH_UPDATE_INIT); 37850bd6153SVasant Hegde else 37950bd6153SVasant Hegde args_buf->status = FLASH_INVALID_IMG; 38050bd6153SVasant Hegde break; 38150bd6153SVasant Hegde default: 38250bd6153SVasant Hegde rc = -EINVAL; 38350bd6153SVasant Hegde } 38450bd6153SVasant Hegde 38550bd6153SVasant Hegde mutex_unlock(&image_data_mutex); 38650bd6153SVasant Hegde return rc; 38750bd6153SVasant Hegde } 38850bd6153SVasant Hegde 38950bd6153SVasant Hegde /* 39050bd6153SVasant Hegde * Free image buffer 39150bd6153SVasant Hegde */ 39250bd6153SVasant Hegde static void free_image_buf(void) 39350bd6153SVasant Hegde { 39450bd6153SVasant Hegde void *addr; 39550bd6153SVasant Hegde int size; 39650bd6153SVasant Hegde 39750bd6153SVasant Hegde addr = image_data.data; 39850bd6153SVasant Hegde size = PAGE_ALIGN(image_data.size); 39950bd6153SVasant Hegde while (size > 0) { 40050bd6153SVasant Hegde ClearPageReserved(vmalloc_to_page(addr)); 40150bd6153SVasant Hegde addr += PAGE_SIZE; 40250bd6153SVasant Hegde size -= PAGE_SIZE; 40350bd6153SVasant Hegde } 40450bd6153SVasant Hegde vfree(image_data.data); 40550bd6153SVasant Hegde image_data.data = NULL; 40650bd6153SVasant Hegde image_data.status = IMAGE_INVALID; 40750bd6153SVasant Hegde } 40850bd6153SVasant Hegde 40950bd6153SVasant Hegde /* 41050bd6153SVasant Hegde * Allocate image buffer. 41150bd6153SVasant Hegde */ 41250bd6153SVasant Hegde static int alloc_image_buf(char *buffer, size_t count) 41350bd6153SVasant Hegde { 41450bd6153SVasant Hegde void *addr; 41550bd6153SVasant Hegde int size; 41650bd6153SVasant Hegde 41750bd6153SVasant Hegde if (count < sizeof(struct image_header_t)) { 41850bd6153SVasant Hegde pr_warn("FLASH: Invalid candidate image\n"); 41950bd6153SVasant Hegde return -EINVAL; 42050bd6153SVasant Hegde } 42150bd6153SVasant Hegde 42250bd6153SVasant Hegde memcpy(&image_header, (void *)buffer, sizeof(struct image_header_t)); 42350bd6153SVasant Hegde image_data.size = be32_to_cpu(image_header.size); 4248faaaeadSMasanari Iida pr_debug("FLASH: Candidate image size = %u\n", image_data.size); 42550bd6153SVasant Hegde 42650bd6153SVasant Hegde if (image_data.size > MAX_IMAGE_SIZE) { 42750bd6153SVasant Hegde pr_warn("FLASH: Too large image\n"); 42850bd6153SVasant Hegde return -EINVAL; 42950bd6153SVasant Hegde } 43050bd6153SVasant Hegde if (image_data.size < VALIDATE_BUF_SIZE) { 43150bd6153SVasant Hegde pr_warn("FLASH: Image is shorter than expected\n"); 43250bd6153SVasant Hegde return -EINVAL; 43350bd6153SVasant Hegde } 43450bd6153SVasant Hegde 43550bd6153SVasant Hegde image_data.data = vzalloc(PAGE_ALIGN(image_data.size)); 43650bd6153SVasant Hegde if (!image_data.data) { 43750bd6153SVasant Hegde pr_err("%s : Failed to allocate memory\n", __func__); 43850bd6153SVasant Hegde return -ENOMEM; 43950bd6153SVasant Hegde } 44050bd6153SVasant Hegde 44150bd6153SVasant Hegde /* Pin memory */ 44250bd6153SVasant Hegde addr = image_data.data; 44350bd6153SVasant Hegde size = PAGE_ALIGN(image_data.size); 44450bd6153SVasant Hegde while (size > 0) { 44550bd6153SVasant Hegde SetPageReserved(vmalloc_to_page(addr)); 44650bd6153SVasant Hegde addr += PAGE_SIZE; 44750bd6153SVasant Hegde size -= PAGE_SIZE; 44850bd6153SVasant Hegde } 44950bd6153SVasant Hegde 45050bd6153SVasant Hegde image_data.status = IMAGE_LOADING; 45150bd6153SVasant Hegde return 0; 45250bd6153SVasant Hegde } 45350bd6153SVasant Hegde 45450bd6153SVasant Hegde /* 45550bd6153SVasant Hegde * Copy candidate image 45650bd6153SVasant Hegde * 45750bd6153SVasant Hegde * Parse candidate image header to get total image size 45850bd6153SVasant Hegde * and pre-allocate required memory. 45950bd6153SVasant Hegde */ 46050bd6153SVasant Hegde static ssize_t image_data_write(struct file *filp, struct kobject *kobj, 46150bd6153SVasant Hegde struct bin_attribute *bin_attr, 46250bd6153SVasant Hegde char *buffer, loff_t pos, size_t count) 46350bd6153SVasant Hegde { 46450bd6153SVasant Hegde int rc; 46550bd6153SVasant Hegde 46650bd6153SVasant Hegde mutex_lock(&image_data_mutex); 46750bd6153SVasant Hegde 46850bd6153SVasant Hegde /* New image ? */ 46950bd6153SVasant Hegde if (pos == 0) { 47050bd6153SVasant Hegde /* Free memory, if already allocated */ 47150bd6153SVasant Hegde if (image_data.data) 47250bd6153SVasant Hegde free_image_buf(); 47350bd6153SVasant Hegde 47450bd6153SVasant Hegde /* Cancel outstanding image update request */ 47550bd6153SVasant Hegde if (update_flash_data.status == FLASH_IMG_READY) 47650bd6153SVasant Hegde opal_flash_update(FLASH_UPDATE_CANCEL); 47750bd6153SVasant Hegde 47850bd6153SVasant Hegde /* Allocate memory */ 47950bd6153SVasant Hegde rc = alloc_image_buf(buffer, count); 48050bd6153SVasant Hegde if (rc) 48150bd6153SVasant Hegde goto out; 48250bd6153SVasant Hegde } 48350bd6153SVasant Hegde 48450bd6153SVasant Hegde if (image_data.status != IMAGE_LOADING) { 48550bd6153SVasant Hegde rc = -ENOMEM; 48650bd6153SVasant Hegde goto out; 48750bd6153SVasant Hegde } 48850bd6153SVasant Hegde 48950bd6153SVasant Hegde if ((pos + count) > image_data.size) { 49050bd6153SVasant Hegde rc = -EINVAL; 49150bd6153SVasant Hegde goto out; 49250bd6153SVasant Hegde } 49350bd6153SVasant Hegde 49450bd6153SVasant Hegde memcpy(image_data.data + pos, (void *)buffer, count); 49550bd6153SVasant Hegde rc = count; 49650bd6153SVasant Hegde 49750bd6153SVasant Hegde /* Set image status */ 49850bd6153SVasant Hegde if ((pos + count) == image_data.size) { 49950bd6153SVasant Hegde pr_debug("FLASH: Candidate image loaded....\n"); 50050bd6153SVasant Hegde image_data.status = IMAGE_READY; 50150bd6153SVasant Hegde } 50250bd6153SVasant Hegde 50350bd6153SVasant Hegde out: 50450bd6153SVasant Hegde mutex_unlock(&image_data_mutex); 50550bd6153SVasant Hegde return rc; 50650bd6153SVasant Hegde } 50750bd6153SVasant Hegde 50850bd6153SVasant Hegde /* 50950bd6153SVasant Hegde * sysfs interface : 51050bd6153SVasant Hegde * OPAL uses below sysfs files for code update. 51150bd6153SVasant Hegde * We create these files under /sys/firmware/opal. 51250bd6153SVasant Hegde * 51350bd6153SVasant Hegde * image : Interface to load candidate firmware image 51450bd6153SVasant Hegde * validate_flash : Validate firmware image 51550bd6153SVasant Hegde * manage_flash : Commit/Reject firmware image 51650bd6153SVasant Hegde * update_flash : Flash new firmware image 51750bd6153SVasant Hegde * 51850bd6153SVasant Hegde */ 51950bd6153SVasant Hegde static struct bin_attribute image_data_attr = { 52050bd6153SVasant Hegde .attr = {.name = "image", .mode = 0200}, 52150bd6153SVasant Hegde .size = MAX_IMAGE_SIZE, /* Limit image size */ 52250bd6153SVasant Hegde .write = image_data_write, 52350bd6153SVasant Hegde }; 52450bd6153SVasant Hegde 52550bd6153SVasant Hegde static struct kobj_attribute validate_attribute = 52650bd6153SVasant Hegde __ATTR(validate_flash, 0600, validate_show, validate_store); 52750bd6153SVasant Hegde 52850bd6153SVasant Hegde static struct kobj_attribute manage_attribute = 52950bd6153SVasant Hegde __ATTR(manage_flash, 0600, manage_show, manage_store); 53050bd6153SVasant Hegde 53150bd6153SVasant Hegde static struct kobj_attribute update_attribute = 53250bd6153SVasant Hegde __ATTR(update_flash, 0600, update_show, update_store); 53350bd6153SVasant Hegde 53450bd6153SVasant Hegde static struct attribute *image_op_attrs[] = { 53550bd6153SVasant Hegde &validate_attribute.attr, 53650bd6153SVasant Hegde &manage_attribute.attr, 53750bd6153SVasant Hegde &update_attribute.attr, 53850bd6153SVasant Hegde NULL /* need to NULL terminate the list of attributes */ 53950bd6153SVasant Hegde }; 54050bd6153SVasant Hegde 54150bd6153SVasant Hegde static struct attribute_group image_op_attr_group = { 54250bd6153SVasant Hegde .attrs = image_op_attrs, 54350bd6153SVasant Hegde }; 54450bd6153SVasant Hegde 54550bd6153SVasant Hegde void __init opal_flash_init(void) 54650bd6153SVasant Hegde { 54750bd6153SVasant Hegde int ret; 54850bd6153SVasant Hegde 54950bd6153SVasant Hegde /* Allocate validate image buffer */ 55050bd6153SVasant Hegde validate_flash_data.buf = kzalloc(VALIDATE_BUF_SIZE, GFP_KERNEL); 55150bd6153SVasant Hegde if (!validate_flash_data.buf) { 55250bd6153SVasant Hegde pr_err("%s : Failed to allocate memory\n", __func__); 55350bd6153SVasant Hegde return; 55450bd6153SVasant Hegde } 55550bd6153SVasant Hegde 55650bd6153SVasant Hegde /* Make sure /sys/firmware/opal directory is created */ 55750bd6153SVasant Hegde if (!opal_kobj) { 55850bd6153SVasant Hegde pr_warn("FLASH: opal kobject is not available\n"); 55950bd6153SVasant Hegde goto nokobj; 56050bd6153SVasant Hegde } 56150bd6153SVasant Hegde 56250bd6153SVasant Hegde /* Create the sysfs files */ 56350bd6153SVasant Hegde ret = sysfs_create_group(opal_kobj, &image_op_attr_group); 56450bd6153SVasant Hegde if (ret) { 56550bd6153SVasant Hegde pr_warn("FLASH: Failed to create sysfs files\n"); 56650bd6153SVasant Hegde goto nokobj; 56750bd6153SVasant Hegde } 56850bd6153SVasant Hegde 56950bd6153SVasant Hegde ret = sysfs_create_bin_file(opal_kobj, &image_data_attr); 57050bd6153SVasant Hegde if (ret) { 57150bd6153SVasant Hegde pr_warn("FLASH: Failed to create sysfs files\n"); 57250bd6153SVasant Hegde goto nosysfs_file; 57350bd6153SVasant Hegde } 57450bd6153SVasant Hegde 57550bd6153SVasant Hegde /* Set default status */ 57650bd6153SVasant Hegde validate_flash_data.status = FLASH_NO_OP; 57750bd6153SVasant Hegde manage_flash_data.status = FLASH_NO_OP; 57850bd6153SVasant Hegde update_flash_data.status = FLASH_NO_OP; 57950bd6153SVasant Hegde image_data.status = IMAGE_INVALID; 58050bd6153SVasant Hegde return; 58150bd6153SVasant Hegde 58250bd6153SVasant Hegde nosysfs_file: 58350bd6153SVasant Hegde sysfs_remove_group(opal_kobj, &image_op_attr_group); 58450bd6153SVasant Hegde 58550bd6153SVasant Hegde nokobj: 58650bd6153SVasant Hegde kfree(validate_flash_data.buf); 58750bd6153SVasant Hegde return; 58850bd6153SVasant Hegde } 589