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> 2350bd6153SVasant Hegde 2450bd6153SVasant Hegde #include <asm/opal.h> 2550bd6153SVasant Hegde 2650bd6153SVasant Hegde /* FLASH status codes */ 2750bd6153SVasant Hegde #define FLASH_NO_OP -1099 /* No operation initiated by user */ 2850bd6153SVasant Hegde #define FLASH_NO_AUTH -9002 /* Not a service authority partition */ 2950bd6153SVasant Hegde 3050bd6153SVasant Hegde /* Validate image status values */ 3150bd6153SVasant Hegde #define VALIDATE_IMG_READY -1001 /* Image ready for validation */ 3250bd6153SVasant Hegde #define VALIDATE_IMG_INCOMPLETE -1002 /* User copied < VALIDATE_BUF_SIZE */ 3350bd6153SVasant Hegde 3450bd6153SVasant Hegde /* Manage image status values */ 3550bd6153SVasant Hegde #define MANAGE_ACTIVE_ERR -9001 /* Cannot overwrite active img */ 3650bd6153SVasant Hegde 3750bd6153SVasant Hegde /* Flash image status values */ 3850bd6153SVasant Hegde #define FLASH_IMG_READY 0 /* Img ready for flash on reboot */ 3950bd6153SVasant Hegde #define FLASH_INVALID_IMG -1003 /* Flash image shorter than expected */ 4050bd6153SVasant Hegde #define FLASH_IMG_NULL_DATA -1004 /* Bad data in sg list entry */ 4150bd6153SVasant Hegde #define FLASH_IMG_BAD_LEN -1005 /* Bad length in sg list entry */ 4250bd6153SVasant Hegde 4350bd6153SVasant Hegde /* Manage operation tokens */ 4450bd6153SVasant Hegde #define FLASH_REJECT_TMP_SIDE 0 /* Reject temporary fw image */ 4550bd6153SVasant Hegde #define FLASH_COMMIT_TMP_SIDE 1 /* Commit temporary fw image */ 4650bd6153SVasant Hegde 4750bd6153SVasant Hegde /* Update tokens */ 4850bd6153SVasant Hegde #define FLASH_UPDATE_CANCEL 0 /* Cancel update request */ 4950bd6153SVasant Hegde #define FLASH_UPDATE_INIT 1 /* Initiate update */ 5050bd6153SVasant Hegde 5150bd6153SVasant Hegde /* Validate image update result tokens */ 5250bd6153SVasant Hegde #define VALIDATE_TMP_UPDATE 0 /* T side will be updated */ 5350bd6153SVasant Hegde #define VALIDATE_FLASH_AUTH 1 /* Partition does not have authority */ 5450bd6153SVasant Hegde #define VALIDATE_INVALID_IMG 2 /* Candidate image is not valid */ 5550bd6153SVasant Hegde #define VALIDATE_CUR_UNKNOWN 3 /* Current fixpack level is unknown */ 5650bd6153SVasant Hegde /* 5750bd6153SVasant Hegde * Current T side will be committed to P side before being replace with new 5850bd6153SVasant Hegde * image, and the new image is downlevel from current image 5950bd6153SVasant Hegde */ 6050bd6153SVasant Hegde #define VALIDATE_TMP_COMMIT_DL 4 6150bd6153SVasant Hegde /* 6250bd6153SVasant Hegde * Current T side will be committed to P side before being replaced with new 6350bd6153SVasant Hegde * image 6450bd6153SVasant Hegde */ 6550bd6153SVasant Hegde #define VALIDATE_TMP_COMMIT 5 6650bd6153SVasant Hegde /* 6750bd6153SVasant Hegde * T side will be updated with a downlevel image 6850bd6153SVasant Hegde */ 6950bd6153SVasant Hegde #define VALIDATE_TMP_UPDATE_DL 6 7050bd6153SVasant Hegde /* 7150bd6153SVasant Hegde * The candidate image's release date is later than the system's firmware 7250bd6153SVasant Hegde * service entitlement date - service warranty period has expired 7350bd6153SVasant Hegde */ 7450bd6153SVasant Hegde #define VALIDATE_OUT_OF_WRNTY 7 7550bd6153SVasant Hegde 7650bd6153SVasant Hegde /* Validate buffer size */ 7750bd6153SVasant Hegde #define VALIDATE_BUF_SIZE 4096 7850bd6153SVasant Hegde 79bf16a4c2SVasant Hegde /* XXX: Assume candidate image size is <= 1GB */ 80bf16a4c2SVasant Hegde #define MAX_IMAGE_SIZE 0x40000000 8150bd6153SVasant Hegde 8250bd6153SVasant Hegde /* Flash sg list version */ 8350bd6153SVasant Hegde #define SG_LIST_VERSION (1UL) 8450bd6153SVasant Hegde 8550bd6153SVasant Hegde /* Image status */ 8650bd6153SVasant Hegde enum { 8750bd6153SVasant Hegde IMAGE_INVALID, 8850bd6153SVasant Hegde IMAGE_LOADING, 8950bd6153SVasant Hegde IMAGE_READY, 9050bd6153SVasant Hegde }; 9150bd6153SVasant Hegde 9250bd6153SVasant Hegde /* Candidate image data */ 9350bd6153SVasant Hegde struct image_data_t { 9450bd6153SVasant Hegde int status; 9550bd6153SVasant Hegde void *data; 9650bd6153SVasant Hegde uint32_t size; 9750bd6153SVasant Hegde }; 9850bd6153SVasant Hegde 9950bd6153SVasant Hegde /* Candidate image header */ 10050bd6153SVasant Hegde struct image_header_t { 10150bd6153SVasant Hegde uint16_t magic; 10250bd6153SVasant Hegde uint16_t version; 10350bd6153SVasant Hegde uint32_t size; 10450bd6153SVasant Hegde }; 10550bd6153SVasant Hegde 10650bd6153SVasant Hegde struct validate_flash_t { 10750bd6153SVasant Hegde int status; /* Return status */ 10850bd6153SVasant Hegde void *buf; /* Candiate image buffer */ 10950bd6153SVasant Hegde uint32_t buf_size; /* Image size */ 11050bd6153SVasant Hegde uint32_t result; /* Update results token */ 11150bd6153SVasant Hegde }; 11250bd6153SVasant Hegde 11350bd6153SVasant Hegde struct manage_flash_t { 11450bd6153SVasant Hegde int status; /* Return status */ 11550bd6153SVasant Hegde }; 11650bd6153SVasant Hegde 11750bd6153SVasant Hegde struct update_flash_t { 11850bd6153SVasant Hegde int status; /* Return status */ 11950bd6153SVasant Hegde }; 12050bd6153SVasant Hegde 12150bd6153SVasant Hegde static struct image_header_t image_header; 12250bd6153SVasant Hegde static struct image_data_t image_data; 12350bd6153SVasant Hegde static struct validate_flash_t validate_flash_data; 12450bd6153SVasant Hegde static struct manage_flash_t manage_flash_data; 12550bd6153SVasant Hegde static struct update_flash_t update_flash_data; 12650bd6153SVasant Hegde 12750bd6153SVasant Hegde static DEFINE_MUTEX(image_data_mutex); 12850bd6153SVasant Hegde 12950bd6153SVasant Hegde /* 13050bd6153SVasant Hegde * Validate candidate image 13150bd6153SVasant Hegde */ 13250bd6153SVasant Hegde static inline void opal_flash_validate(void) 13350bd6153SVasant Hegde { 13450bd6153SVasant Hegde struct validate_flash_t *args_buf = &validate_flash_data; 13550bd6153SVasant Hegde 13650bd6153SVasant Hegde args_buf->status = opal_validate_flash(__pa(args_buf->buf), 13750bd6153SVasant Hegde &(args_buf->buf_size), 13850bd6153SVasant Hegde &(args_buf->result)); 13950bd6153SVasant Hegde } 14050bd6153SVasant Hegde 14150bd6153SVasant Hegde /* 14250bd6153SVasant Hegde * Validate output format: 14350bd6153SVasant Hegde * validate result token 14450bd6153SVasant Hegde * current image version details 14550bd6153SVasant Hegde * new image version details 14650bd6153SVasant Hegde */ 14750bd6153SVasant Hegde static ssize_t validate_show(struct kobject *kobj, 14850bd6153SVasant Hegde struct kobj_attribute *attr, char *buf) 14950bd6153SVasant Hegde { 15050bd6153SVasant Hegde struct validate_flash_t *args_buf = &validate_flash_data; 15150bd6153SVasant Hegde int len; 15250bd6153SVasant Hegde 15350bd6153SVasant Hegde /* Candidate image is not validated */ 15450bd6153SVasant Hegde if (args_buf->status < VALIDATE_TMP_UPDATE) { 15550bd6153SVasant Hegde len = sprintf(buf, "%d\n", args_buf->status); 15650bd6153SVasant Hegde goto out; 15750bd6153SVasant Hegde } 15850bd6153SVasant Hegde 15950bd6153SVasant Hegde /* Result token */ 16050bd6153SVasant Hegde len = sprintf(buf, "%d\n", args_buf->result); 16150bd6153SVasant Hegde 16250bd6153SVasant Hegde /* Current and candidate image version details */ 16350bd6153SVasant Hegde if ((args_buf->result != VALIDATE_TMP_UPDATE) && 16450bd6153SVasant Hegde (args_buf->result < VALIDATE_CUR_UNKNOWN)) 16550bd6153SVasant Hegde goto out; 16650bd6153SVasant Hegde 16750bd6153SVasant Hegde if (args_buf->buf_size > (VALIDATE_BUF_SIZE - len)) { 16850bd6153SVasant Hegde memcpy(buf + len, args_buf->buf, VALIDATE_BUF_SIZE - len); 16950bd6153SVasant Hegde len = VALIDATE_BUF_SIZE; 17050bd6153SVasant Hegde } else { 17150bd6153SVasant Hegde memcpy(buf + len, args_buf->buf, args_buf->buf_size); 17250bd6153SVasant Hegde len += args_buf->buf_size; 17350bd6153SVasant Hegde } 17450bd6153SVasant Hegde out: 17550bd6153SVasant Hegde /* Set status to default */ 17650bd6153SVasant Hegde args_buf->status = FLASH_NO_OP; 17750bd6153SVasant Hegde return len; 17850bd6153SVasant Hegde } 17950bd6153SVasant Hegde 18050bd6153SVasant Hegde /* 18150bd6153SVasant Hegde * Validate candidate firmware image 18250bd6153SVasant Hegde * 18350bd6153SVasant Hegde * Note: 18450bd6153SVasant Hegde * We are only interested in first 4K bytes of the 18550bd6153SVasant Hegde * candidate image. 18650bd6153SVasant Hegde */ 18750bd6153SVasant Hegde static ssize_t validate_store(struct kobject *kobj, 18850bd6153SVasant Hegde struct kobj_attribute *attr, 18950bd6153SVasant Hegde const char *buf, size_t count) 19050bd6153SVasant Hegde { 19150bd6153SVasant Hegde struct validate_flash_t *args_buf = &validate_flash_data; 19250bd6153SVasant Hegde 19350bd6153SVasant Hegde if (buf[0] != '1') 19450bd6153SVasant Hegde return -EINVAL; 19550bd6153SVasant Hegde 19650bd6153SVasant Hegde mutex_lock(&image_data_mutex); 19750bd6153SVasant Hegde 19850bd6153SVasant Hegde if (image_data.status != IMAGE_READY || 19950bd6153SVasant Hegde image_data.size < VALIDATE_BUF_SIZE) { 20050bd6153SVasant Hegde args_buf->result = VALIDATE_INVALID_IMG; 20150bd6153SVasant Hegde args_buf->status = VALIDATE_IMG_INCOMPLETE; 20250bd6153SVasant Hegde goto out; 20350bd6153SVasant Hegde } 20450bd6153SVasant Hegde 20550bd6153SVasant Hegde /* Copy first 4k bytes of candidate image */ 20650bd6153SVasant Hegde memcpy(args_buf->buf, image_data.data, VALIDATE_BUF_SIZE); 20750bd6153SVasant Hegde 20850bd6153SVasant Hegde args_buf->status = VALIDATE_IMG_READY; 20950bd6153SVasant Hegde args_buf->buf_size = VALIDATE_BUF_SIZE; 21050bd6153SVasant Hegde 21150bd6153SVasant Hegde /* Validate candidate image */ 21250bd6153SVasant Hegde opal_flash_validate(); 21350bd6153SVasant Hegde 21450bd6153SVasant Hegde out: 21550bd6153SVasant Hegde mutex_unlock(&image_data_mutex); 21650bd6153SVasant Hegde return count; 21750bd6153SVasant Hegde } 21850bd6153SVasant Hegde 21950bd6153SVasant Hegde /* 22050bd6153SVasant Hegde * Manage flash routine 22150bd6153SVasant Hegde */ 22250bd6153SVasant Hegde static inline void opal_flash_manage(uint8_t op) 22350bd6153SVasant Hegde { 22450bd6153SVasant Hegde struct manage_flash_t *const args_buf = &manage_flash_data; 22550bd6153SVasant Hegde 22650bd6153SVasant Hegde args_buf->status = opal_manage_flash(op); 22750bd6153SVasant Hegde } 22850bd6153SVasant Hegde 22950bd6153SVasant Hegde /* 23050bd6153SVasant Hegde * Show manage flash status 23150bd6153SVasant Hegde */ 23250bd6153SVasant Hegde static ssize_t manage_show(struct kobject *kobj, 23350bd6153SVasant Hegde struct kobj_attribute *attr, char *buf) 23450bd6153SVasant Hegde { 23550bd6153SVasant Hegde struct manage_flash_t *const args_buf = &manage_flash_data; 23650bd6153SVasant Hegde int rc; 23750bd6153SVasant Hegde 23850bd6153SVasant Hegde rc = sprintf(buf, "%d\n", args_buf->status); 23950bd6153SVasant Hegde /* Set status to default*/ 24050bd6153SVasant Hegde args_buf->status = FLASH_NO_OP; 24150bd6153SVasant Hegde return rc; 24250bd6153SVasant Hegde } 24350bd6153SVasant Hegde 24450bd6153SVasant Hegde /* 24550bd6153SVasant Hegde * Manage operations: 24650bd6153SVasant Hegde * 0 - Reject 24750bd6153SVasant Hegde * 1 - Commit 24850bd6153SVasant Hegde */ 24950bd6153SVasant Hegde static ssize_t manage_store(struct kobject *kobj, 25050bd6153SVasant Hegde struct kobj_attribute *attr, 25150bd6153SVasant Hegde const char *buf, size_t count) 25250bd6153SVasant Hegde { 25350bd6153SVasant Hegde uint8_t op; 25450bd6153SVasant Hegde switch (buf[0]) { 25550bd6153SVasant Hegde case '0': 25650bd6153SVasant Hegde op = FLASH_REJECT_TMP_SIDE; 25750bd6153SVasant Hegde break; 25850bd6153SVasant Hegde case '1': 25950bd6153SVasant Hegde op = FLASH_COMMIT_TMP_SIDE; 26050bd6153SVasant Hegde break; 26150bd6153SVasant Hegde default: 26250bd6153SVasant Hegde return -EINVAL; 26350bd6153SVasant Hegde } 26450bd6153SVasant Hegde 26550bd6153SVasant Hegde /* commit/reject temporary image */ 26650bd6153SVasant Hegde opal_flash_manage(op); 26750bd6153SVasant Hegde return count; 26850bd6153SVasant Hegde } 26950bd6153SVasant Hegde 27050bd6153SVasant Hegde /* 27150bd6153SVasant Hegde * Free sg list 27250bd6153SVasant Hegde */ 27350bd6153SVasant Hegde static void free_sg_list(struct opal_sg_list *list) 27450bd6153SVasant Hegde { 27550bd6153SVasant Hegde struct opal_sg_list *sg1; 27650bd6153SVasant Hegde while (list) { 27750bd6153SVasant Hegde sg1 = list->next; 27850bd6153SVasant Hegde kfree(list); 27950bd6153SVasant Hegde list = sg1; 28050bd6153SVasant Hegde } 28150bd6153SVasant Hegde list = NULL; 28250bd6153SVasant Hegde } 28350bd6153SVasant Hegde 28450bd6153SVasant Hegde /* 28550bd6153SVasant Hegde * Build candidate image scatter gather list 28650bd6153SVasant Hegde * 28750bd6153SVasant Hegde * list format: 28850bd6153SVasant Hegde * ----------------------------------- 28950bd6153SVasant Hegde * | VER (8) | Entry length in bytes | 29050bd6153SVasant Hegde * ----------------------------------- 29150bd6153SVasant Hegde * | Pointer to next entry | 29250bd6153SVasant Hegde * ----------------------------------- 29350bd6153SVasant Hegde * | Address of memory area 1 | 29450bd6153SVasant Hegde * ----------------------------------- 29550bd6153SVasant Hegde * | Length of memory area 1 | 29650bd6153SVasant Hegde * ----------------------------------- 29750bd6153SVasant Hegde * | ......... | 29850bd6153SVasant Hegde * ----------------------------------- 29950bd6153SVasant Hegde * | ......... | 30050bd6153SVasant Hegde * ----------------------------------- 30150bd6153SVasant Hegde * | Address of memory area N | 30250bd6153SVasant Hegde * ----------------------------------- 30350bd6153SVasant Hegde * | Length of memory area N | 30450bd6153SVasant Hegde * ----------------------------------- 30550bd6153SVasant Hegde */ 30650bd6153SVasant Hegde static struct opal_sg_list *image_data_to_sglist(void) 30750bd6153SVasant Hegde { 30850bd6153SVasant Hegde struct opal_sg_list *sg1, *list = NULL; 30950bd6153SVasant Hegde void *addr; 31050bd6153SVasant Hegde int size; 31150bd6153SVasant Hegde 31250bd6153SVasant Hegde addr = image_data.data; 31350bd6153SVasant Hegde size = image_data.size; 31450bd6153SVasant Hegde 3157e1ce5a4SVasant Hegde sg1 = kzalloc(PAGE_SIZE, GFP_KERNEL); 31650bd6153SVasant Hegde if (!sg1) 31750bd6153SVasant Hegde return NULL; 31850bd6153SVasant Hegde 31950bd6153SVasant Hegde list = sg1; 32050bd6153SVasant Hegde sg1->num_entries = 0; 32150bd6153SVasant Hegde while (size > 0) { 32250bd6153SVasant Hegde /* Translate virtual address to physical address */ 32350bd6153SVasant Hegde sg1->entry[sg1->num_entries].data = 32450bd6153SVasant Hegde (void *)(vmalloc_to_pfn(addr) << PAGE_SHIFT); 32550bd6153SVasant Hegde 32650bd6153SVasant Hegde if (size > PAGE_SIZE) 32750bd6153SVasant Hegde sg1->entry[sg1->num_entries].length = PAGE_SIZE; 32850bd6153SVasant Hegde else 32950bd6153SVasant Hegde sg1->entry[sg1->num_entries].length = size; 33050bd6153SVasant Hegde 33150bd6153SVasant Hegde sg1->num_entries++; 33250bd6153SVasant Hegde if (sg1->num_entries >= SG_ENTRIES_PER_NODE) { 3337e1ce5a4SVasant Hegde sg1->next = kzalloc(PAGE_SIZE, GFP_KERNEL); 33450bd6153SVasant Hegde if (!sg1->next) { 33550bd6153SVasant Hegde pr_err("%s : Failed to allocate memory\n", 33650bd6153SVasant Hegde __func__); 33750bd6153SVasant Hegde goto nomem; 33850bd6153SVasant Hegde } 33950bd6153SVasant Hegde 34050bd6153SVasant Hegde sg1 = sg1->next; 34150bd6153SVasant Hegde sg1->num_entries = 0; 34250bd6153SVasant Hegde } 34350bd6153SVasant Hegde addr += PAGE_SIZE; 34450bd6153SVasant Hegde size -= PAGE_SIZE; 34550bd6153SVasant Hegde } 34650bd6153SVasant Hegde return list; 34750bd6153SVasant Hegde nomem: 34850bd6153SVasant Hegde free_sg_list(list); 34950bd6153SVasant Hegde return NULL; 35050bd6153SVasant Hegde } 35150bd6153SVasant Hegde 35250bd6153SVasant Hegde /* 35350bd6153SVasant Hegde * OPAL update flash 35450bd6153SVasant Hegde */ 35550bd6153SVasant Hegde static int opal_flash_update(int op) 35650bd6153SVasant Hegde { 35750bd6153SVasant Hegde struct opal_sg_list *sg, *list, *next; 35850bd6153SVasant Hegde unsigned long addr; 35950bd6153SVasant Hegde int64_t rc = OPAL_PARAMETER; 36050bd6153SVasant Hegde 36150bd6153SVasant Hegde if (op == FLASH_UPDATE_CANCEL) { 36250bd6153SVasant Hegde pr_alert("FLASH: Image update cancelled\n"); 36350bd6153SVasant Hegde addr = '\0'; 36450bd6153SVasant Hegde goto flash; 36550bd6153SVasant Hegde } 36650bd6153SVasant Hegde 36750bd6153SVasant Hegde list = image_data_to_sglist(); 36850bd6153SVasant Hegde if (!list) 36950bd6153SVasant Hegde goto invalid_img; 37050bd6153SVasant Hegde 37150bd6153SVasant Hegde /* First entry address */ 37250bd6153SVasant Hegde addr = __pa(list); 37350bd6153SVasant Hegde 37450bd6153SVasant Hegde /* Translate sg list address to absolute */ 37550bd6153SVasant Hegde for (sg = list; sg; sg = next) { 37650bd6153SVasant Hegde next = sg->next; 37750bd6153SVasant Hegde /* Don't translate NULL pointer for last entry */ 37850bd6153SVasant Hegde if (sg->next) 37950bd6153SVasant Hegde sg->next = (struct opal_sg_list *)__pa(sg->next); 38050bd6153SVasant Hegde else 38150bd6153SVasant Hegde sg->next = NULL; 38250bd6153SVasant Hegde 3837e1ce5a4SVasant Hegde /* 3847e1ce5a4SVasant Hegde * Convert num_entries to version/length format 3857e1ce5a4SVasant Hegde * to satisfy OPAL. 3867e1ce5a4SVasant Hegde */ 38750bd6153SVasant Hegde sg->num_entries = (SG_LIST_VERSION << 56) | 38850bd6153SVasant Hegde (sg->num_entries * sizeof(struct opal_sg_entry) + 16); 38950bd6153SVasant Hegde } 39050bd6153SVasant Hegde 39150bd6153SVasant Hegde pr_alert("FLASH: Image is %u bytes\n", image_data.size); 39250bd6153SVasant Hegde pr_alert("FLASH: Image update requested\n"); 39350bd6153SVasant Hegde pr_alert("FLASH: Image will be updated during system reboot\n"); 39450bd6153SVasant Hegde pr_alert("FLASH: This will take several minutes. Do not power off!\n"); 39550bd6153SVasant Hegde 39650bd6153SVasant Hegde flash: 39750bd6153SVasant Hegde rc = opal_update_flash(addr); 39850bd6153SVasant Hegde 39950bd6153SVasant Hegde invalid_img: 40050bd6153SVasant Hegde return rc; 40150bd6153SVasant Hegde } 40250bd6153SVasant Hegde 40350bd6153SVasant Hegde /* 40450bd6153SVasant Hegde * Show candidate image status 40550bd6153SVasant Hegde */ 40650bd6153SVasant Hegde static ssize_t update_show(struct kobject *kobj, 40750bd6153SVasant Hegde struct kobj_attribute *attr, char *buf) 40850bd6153SVasant Hegde { 40950bd6153SVasant Hegde struct update_flash_t *const args_buf = &update_flash_data; 41050bd6153SVasant Hegde return sprintf(buf, "%d\n", args_buf->status); 41150bd6153SVasant Hegde } 41250bd6153SVasant Hegde 41350bd6153SVasant Hegde /* 41450bd6153SVasant Hegde * Set update image flag 41550bd6153SVasant Hegde * 1 - Flash new image 41650bd6153SVasant Hegde * 0 - Cancel flash request 41750bd6153SVasant Hegde */ 41850bd6153SVasant Hegde static ssize_t update_store(struct kobject *kobj, 41950bd6153SVasant Hegde struct kobj_attribute *attr, 42050bd6153SVasant Hegde const char *buf, size_t count) 42150bd6153SVasant Hegde { 42250bd6153SVasant Hegde struct update_flash_t *const args_buf = &update_flash_data; 42350bd6153SVasant Hegde int rc = count; 42450bd6153SVasant Hegde 42550bd6153SVasant Hegde mutex_lock(&image_data_mutex); 42650bd6153SVasant Hegde 42750bd6153SVasant Hegde switch (buf[0]) { 42850bd6153SVasant Hegde case '0': 42950bd6153SVasant Hegde if (args_buf->status == FLASH_IMG_READY) 43050bd6153SVasant Hegde opal_flash_update(FLASH_UPDATE_CANCEL); 43150bd6153SVasant Hegde args_buf->status = FLASH_NO_OP; 43250bd6153SVasant Hegde break; 43350bd6153SVasant Hegde case '1': 43450bd6153SVasant Hegde /* Image is loaded? */ 43550bd6153SVasant Hegde if (image_data.status == IMAGE_READY) 43650bd6153SVasant Hegde args_buf->status = 43750bd6153SVasant Hegde opal_flash_update(FLASH_UPDATE_INIT); 43850bd6153SVasant Hegde else 43950bd6153SVasant Hegde args_buf->status = FLASH_INVALID_IMG; 44050bd6153SVasant Hegde break; 44150bd6153SVasant Hegde default: 44250bd6153SVasant Hegde rc = -EINVAL; 44350bd6153SVasant Hegde } 44450bd6153SVasant Hegde 44550bd6153SVasant Hegde mutex_unlock(&image_data_mutex); 44650bd6153SVasant Hegde return rc; 44750bd6153SVasant Hegde } 44850bd6153SVasant Hegde 44950bd6153SVasant Hegde /* 45050bd6153SVasant Hegde * Free image buffer 45150bd6153SVasant Hegde */ 45250bd6153SVasant Hegde static void free_image_buf(void) 45350bd6153SVasant Hegde { 45450bd6153SVasant Hegde void *addr; 45550bd6153SVasant Hegde int size; 45650bd6153SVasant Hegde 45750bd6153SVasant Hegde addr = image_data.data; 45850bd6153SVasant Hegde size = PAGE_ALIGN(image_data.size); 45950bd6153SVasant Hegde while (size > 0) { 46050bd6153SVasant Hegde ClearPageReserved(vmalloc_to_page(addr)); 46150bd6153SVasant Hegde addr += PAGE_SIZE; 46250bd6153SVasant Hegde size -= PAGE_SIZE; 46350bd6153SVasant Hegde } 46450bd6153SVasant Hegde vfree(image_data.data); 46550bd6153SVasant Hegde image_data.data = NULL; 46650bd6153SVasant Hegde image_data.status = IMAGE_INVALID; 46750bd6153SVasant Hegde } 46850bd6153SVasant Hegde 46950bd6153SVasant Hegde /* 47050bd6153SVasant Hegde * Allocate image buffer. 47150bd6153SVasant Hegde */ 47250bd6153SVasant Hegde static int alloc_image_buf(char *buffer, size_t count) 47350bd6153SVasant Hegde { 47450bd6153SVasant Hegde void *addr; 47550bd6153SVasant Hegde int size; 47650bd6153SVasant Hegde 47750bd6153SVasant Hegde if (count < sizeof(struct image_header_t)) { 47850bd6153SVasant Hegde pr_warn("FLASH: Invalid candidate image\n"); 47950bd6153SVasant Hegde return -EINVAL; 48050bd6153SVasant Hegde } 48150bd6153SVasant Hegde 48250bd6153SVasant Hegde memcpy(&image_header, (void *)buffer, sizeof(struct image_header_t)); 48350bd6153SVasant Hegde image_data.size = be32_to_cpu(image_header.size); 48450bd6153SVasant Hegde pr_debug("FLASH: Candiate image size = %u\n", image_data.size); 48550bd6153SVasant Hegde 48650bd6153SVasant Hegde if (image_data.size > MAX_IMAGE_SIZE) { 48750bd6153SVasant Hegde pr_warn("FLASH: Too large image\n"); 48850bd6153SVasant Hegde return -EINVAL; 48950bd6153SVasant Hegde } 49050bd6153SVasant Hegde if (image_data.size < VALIDATE_BUF_SIZE) { 49150bd6153SVasant Hegde pr_warn("FLASH: Image is shorter than expected\n"); 49250bd6153SVasant Hegde return -EINVAL; 49350bd6153SVasant Hegde } 49450bd6153SVasant Hegde 49550bd6153SVasant Hegde image_data.data = vzalloc(PAGE_ALIGN(image_data.size)); 49650bd6153SVasant Hegde if (!image_data.data) { 49750bd6153SVasant Hegde pr_err("%s : Failed to allocate memory\n", __func__); 49850bd6153SVasant Hegde return -ENOMEM; 49950bd6153SVasant Hegde } 50050bd6153SVasant Hegde 50150bd6153SVasant Hegde /* Pin memory */ 50250bd6153SVasant Hegde addr = image_data.data; 50350bd6153SVasant Hegde size = PAGE_ALIGN(image_data.size); 50450bd6153SVasant Hegde while (size > 0) { 50550bd6153SVasant Hegde SetPageReserved(vmalloc_to_page(addr)); 50650bd6153SVasant Hegde addr += PAGE_SIZE; 50750bd6153SVasant Hegde size -= PAGE_SIZE; 50850bd6153SVasant Hegde } 50950bd6153SVasant Hegde 51050bd6153SVasant Hegde image_data.status = IMAGE_LOADING; 51150bd6153SVasant Hegde return 0; 51250bd6153SVasant Hegde } 51350bd6153SVasant Hegde 51450bd6153SVasant Hegde /* 51550bd6153SVasant Hegde * Copy candidate image 51650bd6153SVasant Hegde * 51750bd6153SVasant Hegde * Parse candidate image header to get total image size 51850bd6153SVasant Hegde * and pre-allocate required memory. 51950bd6153SVasant Hegde */ 52050bd6153SVasant Hegde static ssize_t image_data_write(struct file *filp, struct kobject *kobj, 52150bd6153SVasant Hegde struct bin_attribute *bin_attr, 52250bd6153SVasant Hegde char *buffer, loff_t pos, size_t count) 52350bd6153SVasant Hegde { 52450bd6153SVasant Hegde int rc; 52550bd6153SVasant Hegde 52650bd6153SVasant Hegde mutex_lock(&image_data_mutex); 52750bd6153SVasant Hegde 52850bd6153SVasant Hegde /* New image ? */ 52950bd6153SVasant Hegde if (pos == 0) { 53050bd6153SVasant Hegde /* Free memory, if already allocated */ 53150bd6153SVasant Hegde if (image_data.data) 53250bd6153SVasant Hegde free_image_buf(); 53350bd6153SVasant Hegde 53450bd6153SVasant Hegde /* Cancel outstanding image update request */ 53550bd6153SVasant Hegde if (update_flash_data.status == FLASH_IMG_READY) 53650bd6153SVasant Hegde opal_flash_update(FLASH_UPDATE_CANCEL); 53750bd6153SVasant Hegde 53850bd6153SVasant Hegde /* Allocate memory */ 53950bd6153SVasant Hegde rc = alloc_image_buf(buffer, count); 54050bd6153SVasant Hegde if (rc) 54150bd6153SVasant Hegde goto out; 54250bd6153SVasant Hegde } 54350bd6153SVasant Hegde 54450bd6153SVasant Hegde if (image_data.status != IMAGE_LOADING) { 54550bd6153SVasant Hegde rc = -ENOMEM; 54650bd6153SVasant Hegde goto out; 54750bd6153SVasant Hegde } 54850bd6153SVasant Hegde 54950bd6153SVasant Hegde if ((pos + count) > image_data.size) { 55050bd6153SVasant Hegde rc = -EINVAL; 55150bd6153SVasant Hegde goto out; 55250bd6153SVasant Hegde } 55350bd6153SVasant Hegde 55450bd6153SVasant Hegde memcpy(image_data.data + pos, (void *)buffer, count); 55550bd6153SVasant Hegde rc = count; 55650bd6153SVasant Hegde 55750bd6153SVasant Hegde /* Set image status */ 55850bd6153SVasant Hegde if ((pos + count) == image_data.size) { 55950bd6153SVasant Hegde pr_debug("FLASH: Candidate image loaded....\n"); 56050bd6153SVasant Hegde image_data.status = IMAGE_READY; 56150bd6153SVasant Hegde } 56250bd6153SVasant Hegde 56350bd6153SVasant Hegde out: 56450bd6153SVasant Hegde mutex_unlock(&image_data_mutex); 56550bd6153SVasant Hegde return rc; 56650bd6153SVasant Hegde } 56750bd6153SVasant Hegde 56850bd6153SVasant Hegde /* 56950bd6153SVasant Hegde * sysfs interface : 57050bd6153SVasant Hegde * OPAL uses below sysfs files for code update. 57150bd6153SVasant Hegde * We create these files under /sys/firmware/opal. 57250bd6153SVasant Hegde * 57350bd6153SVasant Hegde * image : Interface to load candidate firmware image 57450bd6153SVasant Hegde * validate_flash : Validate firmware image 57550bd6153SVasant Hegde * manage_flash : Commit/Reject firmware image 57650bd6153SVasant Hegde * update_flash : Flash new firmware image 57750bd6153SVasant Hegde * 57850bd6153SVasant Hegde */ 57950bd6153SVasant Hegde static struct bin_attribute image_data_attr = { 58050bd6153SVasant Hegde .attr = {.name = "image", .mode = 0200}, 58150bd6153SVasant Hegde .size = MAX_IMAGE_SIZE, /* Limit image size */ 58250bd6153SVasant Hegde .write = image_data_write, 58350bd6153SVasant Hegde }; 58450bd6153SVasant Hegde 58550bd6153SVasant Hegde static struct kobj_attribute validate_attribute = 58650bd6153SVasant Hegde __ATTR(validate_flash, 0600, validate_show, validate_store); 58750bd6153SVasant Hegde 58850bd6153SVasant Hegde static struct kobj_attribute manage_attribute = 58950bd6153SVasant Hegde __ATTR(manage_flash, 0600, manage_show, manage_store); 59050bd6153SVasant Hegde 59150bd6153SVasant Hegde static struct kobj_attribute update_attribute = 59250bd6153SVasant Hegde __ATTR(update_flash, 0600, update_show, update_store); 59350bd6153SVasant Hegde 59450bd6153SVasant Hegde static struct attribute *image_op_attrs[] = { 59550bd6153SVasant Hegde &validate_attribute.attr, 59650bd6153SVasant Hegde &manage_attribute.attr, 59750bd6153SVasant Hegde &update_attribute.attr, 59850bd6153SVasant Hegde NULL /* need to NULL terminate the list of attributes */ 59950bd6153SVasant Hegde }; 60050bd6153SVasant Hegde 60150bd6153SVasant Hegde static struct attribute_group image_op_attr_group = { 60250bd6153SVasant Hegde .attrs = image_op_attrs, 60350bd6153SVasant Hegde }; 60450bd6153SVasant Hegde 60550bd6153SVasant Hegde void __init opal_flash_init(void) 60650bd6153SVasant Hegde { 60750bd6153SVasant Hegde int ret; 60850bd6153SVasant Hegde 60950bd6153SVasant Hegde /* Allocate validate image buffer */ 61050bd6153SVasant Hegde validate_flash_data.buf = kzalloc(VALIDATE_BUF_SIZE, GFP_KERNEL); 61150bd6153SVasant Hegde if (!validate_flash_data.buf) { 61250bd6153SVasant Hegde pr_err("%s : Failed to allocate memory\n", __func__); 61350bd6153SVasant Hegde return; 61450bd6153SVasant Hegde } 61550bd6153SVasant Hegde 61650bd6153SVasant Hegde /* Make sure /sys/firmware/opal directory is created */ 61750bd6153SVasant Hegde if (!opal_kobj) { 61850bd6153SVasant Hegde pr_warn("FLASH: opal kobject is not available\n"); 61950bd6153SVasant Hegde goto nokobj; 62050bd6153SVasant Hegde } 62150bd6153SVasant Hegde 62250bd6153SVasant Hegde /* Create the sysfs files */ 62350bd6153SVasant Hegde ret = sysfs_create_group(opal_kobj, &image_op_attr_group); 62450bd6153SVasant Hegde if (ret) { 62550bd6153SVasant Hegde pr_warn("FLASH: Failed to create sysfs files\n"); 62650bd6153SVasant Hegde goto nokobj; 62750bd6153SVasant Hegde } 62850bd6153SVasant Hegde 62950bd6153SVasant Hegde ret = sysfs_create_bin_file(opal_kobj, &image_data_attr); 63050bd6153SVasant Hegde if (ret) { 63150bd6153SVasant Hegde pr_warn("FLASH: Failed to create sysfs files\n"); 63250bd6153SVasant Hegde goto nosysfs_file; 63350bd6153SVasant Hegde } 63450bd6153SVasant Hegde 63550bd6153SVasant Hegde /* Set default status */ 63650bd6153SVasant Hegde validate_flash_data.status = FLASH_NO_OP; 63750bd6153SVasant Hegde manage_flash_data.status = FLASH_NO_OP; 63850bd6153SVasant Hegde update_flash_data.status = FLASH_NO_OP; 63950bd6153SVasant Hegde image_data.status = IMAGE_INVALID; 64050bd6153SVasant Hegde return; 64150bd6153SVasant Hegde 64250bd6153SVasant Hegde nosysfs_file: 64350bd6153SVasant Hegde sysfs_remove_group(opal_kobj, &image_op_attr_group); 64450bd6153SVasant Hegde 64550bd6153SVasant Hegde nokobj: 64650bd6153SVasant Hegde kfree(validate_flash_data.buf); 64750bd6153SVasant Hegde return; 64850bd6153SVasant Hegde } 649