18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PowerNV OPAL Firmware Update Interface 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2013 IBM Corp. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define DEBUG 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/reboot.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/kobject.h> 148c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/mm.h> 178c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 188c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 198c2ecf20Sopenharmony_ci#include <linux/delay.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <asm/opal.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* FLASH status codes */ 248c2ecf20Sopenharmony_ci#define FLASH_NO_OP -1099 /* No operation initiated by user */ 258c2ecf20Sopenharmony_ci#define FLASH_NO_AUTH -9002 /* Not a service authority partition */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* Validate image status values */ 288c2ecf20Sopenharmony_ci#define VALIDATE_IMG_READY -1001 /* Image ready for validation */ 298c2ecf20Sopenharmony_ci#define VALIDATE_IMG_INCOMPLETE -1002 /* User copied < VALIDATE_BUF_SIZE */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* Manage image status values */ 328c2ecf20Sopenharmony_ci#define MANAGE_ACTIVE_ERR -9001 /* Cannot overwrite active img */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* Flash image status values */ 358c2ecf20Sopenharmony_ci#define FLASH_IMG_READY 0 /* Img ready for flash on reboot */ 368c2ecf20Sopenharmony_ci#define FLASH_INVALID_IMG -1003 /* Flash image shorter than expected */ 378c2ecf20Sopenharmony_ci#define FLASH_IMG_NULL_DATA -1004 /* Bad data in sg list entry */ 388c2ecf20Sopenharmony_ci#define FLASH_IMG_BAD_LEN -1005 /* Bad length in sg list entry */ 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* Manage operation tokens */ 418c2ecf20Sopenharmony_ci#define FLASH_REJECT_TMP_SIDE 0 /* Reject temporary fw image */ 428c2ecf20Sopenharmony_ci#define FLASH_COMMIT_TMP_SIDE 1 /* Commit temporary fw image */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* Update tokens */ 458c2ecf20Sopenharmony_ci#define FLASH_UPDATE_CANCEL 0 /* Cancel update request */ 468c2ecf20Sopenharmony_ci#define FLASH_UPDATE_INIT 1 /* Initiate update */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* Validate image update result tokens */ 498c2ecf20Sopenharmony_ci#define VALIDATE_TMP_UPDATE 0 /* T side will be updated */ 508c2ecf20Sopenharmony_ci#define VALIDATE_FLASH_AUTH 1 /* Partition does not have authority */ 518c2ecf20Sopenharmony_ci#define VALIDATE_INVALID_IMG 2 /* Candidate image is not valid */ 528c2ecf20Sopenharmony_ci#define VALIDATE_CUR_UNKNOWN 3 /* Current fixpack level is unknown */ 538c2ecf20Sopenharmony_ci/* 548c2ecf20Sopenharmony_ci * Current T side will be committed to P side before being replace with new 558c2ecf20Sopenharmony_ci * image, and the new image is downlevel from current image 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci#define VALIDATE_TMP_COMMIT_DL 4 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * Current T side will be committed to P side before being replaced with new 608c2ecf20Sopenharmony_ci * image 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci#define VALIDATE_TMP_COMMIT 5 638c2ecf20Sopenharmony_ci/* 648c2ecf20Sopenharmony_ci * T side will be updated with a downlevel image 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_ci#define VALIDATE_TMP_UPDATE_DL 6 678c2ecf20Sopenharmony_ci/* 688c2ecf20Sopenharmony_ci * The candidate image's release date is later than the system's firmware 698c2ecf20Sopenharmony_ci * service entitlement date - service warranty period has expired 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_ci#define VALIDATE_OUT_OF_WRNTY 7 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* Validate buffer size */ 748c2ecf20Sopenharmony_ci#define VALIDATE_BUF_SIZE 4096 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* XXX: Assume candidate image size is <= 1GB */ 778c2ecf20Sopenharmony_ci#define MAX_IMAGE_SIZE 0x40000000 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* Image status */ 808c2ecf20Sopenharmony_cienum { 818c2ecf20Sopenharmony_ci IMAGE_INVALID, 828c2ecf20Sopenharmony_ci IMAGE_LOADING, 838c2ecf20Sopenharmony_ci IMAGE_READY, 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* Candidate image data */ 878c2ecf20Sopenharmony_cistruct image_data_t { 888c2ecf20Sopenharmony_ci int status; 898c2ecf20Sopenharmony_ci void *data; 908c2ecf20Sopenharmony_ci uint32_t size; 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* Candidate image header */ 948c2ecf20Sopenharmony_cistruct image_header_t { 958c2ecf20Sopenharmony_ci uint16_t magic; 968c2ecf20Sopenharmony_ci uint16_t version; 978c2ecf20Sopenharmony_ci uint32_t size; 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistruct validate_flash_t { 1018c2ecf20Sopenharmony_ci int status; /* Return status */ 1028c2ecf20Sopenharmony_ci void *buf; /* Candidate image buffer */ 1038c2ecf20Sopenharmony_ci uint32_t buf_size; /* Image size */ 1048c2ecf20Sopenharmony_ci uint32_t result; /* Update results token */ 1058c2ecf20Sopenharmony_ci}; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistruct manage_flash_t { 1088c2ecf20Sopenharmony_ci int status; /* Return status */ 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistruct update_flash_t { 1128c2ecf20Sopenharmony_ci int status; /* Return status */ 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic struct image_header_t image_header; 1168c2ecf20Sopenharmony_cistatic struct image_data_t image_data; 1178c2ecf20Sopenharmony_cistatic struct validate_flash_t validate_flash_data; 1188c2ecf20Sopenharmony_cistatic struct manage_flash_t manage_flash_data; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* Initialize update_flash_data status to No Operation */ 1218c2ecf20Sopenharmony_cistatic struct update_flash_t update_flash_data = { 1228c2ecf20Sopenharmony_ci .status = FLASH_NO_OP, 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(image_data_mutex); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/* 1288c2ecf20Sopenharmony_ci * Validate candidate image 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_cistatic inline void opal_flash_validate(void) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci long ret; 1338c2ecf20Sopenharmony_ci void *buf = validate_flash_data.buf; 1348c2ecf20Sopenharmony_ci __be32 size = cpu_to_be32(validate_flash_data.buf_size); 1358c2ecf20Sopenharmony_ci __be32 result; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci ret = opal_validate_flash(__pa(buf), &size, &result); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci validate_flash_data.status = ret; 1408c2ecf20Sopenharmony_ci validate_flash_data.buf_size = be32_to_cpu(size); 1418c2ecf20Sopenharmony_ci validate_flash_data.result = be32_to_cpu(result); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* 1458c2ecf20Sopenharmony_ci * Validate output format: 1468c2ecf20Sopenharmony_ci * validate result token 1478c2ecf20Sopenharmony_ci * current image version details 1488c2ecf20Sopenharmony_ci * new image version details 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_cistatic ssize_t validate_show(struct kobject *kobj, 1518c2ecf20Sopenharmony_ci struct kobj_attribute *attr, char *buf) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct validate_flash_t *args_buf = &validate_flash_data; 1548c2ecf20Sopenharmony_ci int len; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* Candidate image is not validated */ 1578c2ecf20Sopenharmony_ci if (args_buf->status < VALIDATE_TMP_UPDATE) { 1588c2ecf20Sopenharmony_ci len = sprintf(buf, "%d\n", args_buf->status); 1598c2ecf20Sopenharmony_ci goto out; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* Result token */ 1638c2ecf20Sopenharmony_ci len = sprintf(buf, "%d\n", args_buf->result); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* Current and candidate image version details */ 1668c2ecf20Sopenharmony_ci if ((args_buf->result != VALIDATE_TMP_UPDATE) && 1678c2ecf20Sopenharmony_ci (args_buf->result < VALIDATE_CUR_UNKNOWN)) 1688c2ecf20Sopenharmony_ci goto out; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (args_buf->buf_size > (VALIDATE_BUF_SIZE - len)) { 1718c2ecf20Sopenharmony_ci memcpy(buf + len, args_buf->buf, VALIDATE_BUF_SIZE - len); 1728c2ecf20Sopenharmony_ci len = VALIDATE_BUF_SIZE; 1738c2ecf20Sopenharmony_ci } else { 1748c2ecf20Sopenharmony_ci memcpy(buf + len, args_buf->buf, args_buf->buf_size); 1758c2ecf20Sopenharmony_ci len += args_buf->buf_size; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ciout: 1788c2ecf20Sopenharmony_ci /* Set status to default */ 1798c2ecf20Sopenharmony_ci args_buf->status = FLASH_NO_OP; 1808c2ecf20Sopenharmony_ci return len; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci/* 1848c2ecf20Sopenharmony_ci * Validate candidate firmware image 1858c2ecf20Sopenharmony_ci * 1868c2ecf20Sopenharmony_ci * Note: 1878c2ecf20Sopenharmony_ci * We are only interested in first 4K bytes of the 1888c2ecf20Sopenharmony_ci * candidate image. 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_cistatic ssize_t validate_store(struct kobject *kobj, 1918c2ecf20Sopenharmony_ci struct kobj_attribute *attr, 1928c2ecf20Sopenharmony_ci const char *buf, size_t count) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct validate_flash_t *args_buf = &validate_flash_data; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (buf[0] != '1') 1978c2ecf20Sopenharmony_ci return -EINVAL; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci mutex_lock(&image_data_mutex); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (image_data.status != IMAGE_READY || 2028c2ecf20Sopenharmony_ci image_data.size < VALIDATE_BUF_SIZE) { 2038c2ecf20Sopenharmony_ci args_buf->result = VALIDATE_INVALID_IMG; 2048c2ecf20Sopenharmony_ci args_buf->status = VALIDATE_IMG_INCOMPLETE; 2058c2ecf20Sopenharmony_ci goto out; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* Copy first 4k bytes of candidate image */ 2098c2ecf20Sopenharmony_ci memcpy(args_buf->buf, image_data.data, VALIDATE_BUF_SIZE); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci args_buf->status = VALIDATE_IMG_READY; 2128c2ecf20Sopenharmony_ci args_buf->buf_size = VALIDATE_BUF_SIZE; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* Validate candidate image */ 2158c2ecf20Sopenharmony_ci opal_flash_validate(); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ciout: 2188c2ecf20Sopenharmony_ci mutex_unlock(&image_data_mutex); 2198c2ecf20Sopenharmony_ci return count; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci/* 2238c2ecf20Sopenharmony_ci * Manage flash routine 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_cistatic inline void opal_flash_manage(uint8_t op) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct manage_flash_t *const args_buf = &manage_flash_data; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci args_buf->status = opal_manage_flash(op); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/* 2338c2ecf20Sopenharmony_ci * Show manage flash status 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_cistatic ssize_t manage_show(struct kobject *kobj, 2368c2ecf20Sopenharmony_ci struct kobj_attribute *attr, char *buf) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct manage_flash_t *const args_buf = &manage_flash_data; 2398c2ecf20Sopenharmony_ci int rc; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci rc = sprintf(buf, "%d\n", args_buf->status); 2428c2ecf20Sopenharmony_ci /* Set status to default*/ 2438c2ecf20Sopenharmony_ci args_buf->status = FLASH_NO_OP; 2448c2ecf20Sopenharmony_ci return rc; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/* 2488c2ecf20Sopenharmony_ci * Manage operations: 2498c2ecf20Sopenharmony_ci * 0 - Reject 2508c2ecf20Sopenharmony_ci * 1 - Commit 2518c2ecf20Sopenharmony_ci */ 2528c2ecf20Sopenharmony_cistatic ssize_t manage_store(struct kobject *kobj, 2538c2ecf20Sopenharmony_ci struct kobj_attribute *attr, 2548c2ecf20Sopenharmony_ci const char *buf, size_t count) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci uint8_t op; 2578c2ecf20Sopenharmony_ci switch (buf[0]) { 2588c2ecf20Sopenharmony_ci case '0': 2598c2ecf20Sopenharmony_ci op = FLASH_REJECT_TMP_SIDE; 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci case '1': 2628c2ecf20Sopenharmony_ci op = FLASH_COMMIT_TMP_SIDE; 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci default: 2658c2ecf20Sopenharmony_ci return -EINVAL; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* commit/reject temporary image */ 2698c2ecf20Sopenharmony_ci opal_flash_manage(op); 2708c2ecf20Sopenharmony_ci return count; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci/* 2748c2ecf20Sopenharmony_ci * OPAL update flash 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_cistatic int opal_flash_update(int op) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct opal_sg_list *list; 2798c2ecf20Sopenharmony_ci unsigned long addr; 2808c2ecf20Sopenharmony_ci int64_t rc = OPAL_PARAMETER; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (op == FLASH_UPDATE_CANCEL) { 2838c2ecf20Sopenharmony_ci pr_alert("FLASH: Image update cancelled\n"); 2848c2ecf20Sopenharmony_ci addr = '\0'; 2858c2ecf20Sopenharmony_ci goto flash; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci list = opal_vmalloc_to_sg_list(image_data.data, image_data.size); 2898c2ecf20Sopenharmony_ci if (!list) 2908c2ecf20Sopenharmony_ci goto invalid_img; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* First entry address */ 2938c2ecf20Sopenharmony_ci addr = __pa(list); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ciflash: 2968c2ecf20Sopenharmony_ci rc = opal_update_flash(addr); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ciinvalid_img: 2998c2ecf20Sopenharmony_ci return rc; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/* This gets called just before system reboots */ 3038c2ecf20Sopenharmony_civoid opal_flash_update_print_message(void) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci if (update_flash_data.status != FLASH_IMG_READY) 3068c2ecf20Sopenharmony_ci return; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci pr_alert("FLASH: Flashing new firmware\n"); 3098c2ecf20Sopenharmony_ci pr_alert("FLASH: Image is %u bytes\n", image_data.size); 3108c2ecf20Sopenharmony_ci pr_alert("FLASH: Performing flash and reboot/shutdown\n"); 3118c2ecf20Sopenharmony_ci pr_alert("FLASH: This will take several minutes. Do not power off!\n"); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* Small delay to help getting the above message out */ 3148c2ecf20Sopenharmony_ci msleep(500); 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci/* 3188c2ecf20Sopenharmony_ci * Show candidate image status 3198c2ecf20Sopenharmony_ci */ 3208c2ecf20Sopenharmony_cistatic ssize_t update_show(struct kobject *kobj, 3218c2ecf20Sopenharmony_ci struct kobj_attribute *attr, char *buf) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct update_flash_t *const args_buf = &update_flash_data; 3248c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", args_buf->status); 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci/* 3288c2ecf20Sopenharmony_ci * Set update image flag 3298c2ecf20Sopenharmony_ci * 1 - Flash new image 3308c2ecf20Sopenharmony_ci * 0 - Cancel flash request 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_cistatic ssize_t update_store(struct kobject *kobj, 3338c2ecf20Sopenharmony_ci struct kobj_attribute *attr, 3348c2ecf20Sopenharmony_ci const char *buf, size_t count) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct update_flash_t *const args_buf = &update_flash_data; 3378c2ecf20Sopenharmony_ci int rc = count; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci mutex_lock(&image_data_mutex); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci switch (buf[0]) { 3428c2ecf20Sopenharmony_ci case '0': 3438c2ecf20Sopenharmony_ci if (args_buf->status == FLASH_IMG_READY) 3448c2ecf20Sopenharmony_ci opal_flash_update(FLASH_UPDATE_CANCEL); 3458c2ecf20Sopenharmony_ci args_buf->status = FLASH_NO_OP; 3468c2ecf20Sopenharmony_ci break; 3478c2ecf20Sopenharmony_ci case '1': 3488c2ecf20Sopenharmony_ci /* Image is loaded? */ 3498c2ecf20Sopenharmony_ci if (image_data.status == IMAGE_READY) 3508c2ecf20Sopenharmony_ci args_buf->status = 3518c2ecf20Sopenharmony_ci opal_flash_update(FLASH_UPDATE_INIT); 3528c2ecf20Sopenharmony_ci else 3538c2ecf20Sopenharmony_ci args_buf->status = FLASH_INVALID_IMG; 3548c2ecf20Sopenharmony_ci break; 3558c2ecf20Sopenharmony_ci default: 3568c2ecf20Sopenharmony_ci rc = -EINVAL; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci mutex_unlock(&image_data_mutex); 3608c2ecf20Sopenharmony_ci return rc; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci/* 3648c2ecf20Sopenharmony_ci * Free image buffer 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_cistatic void free_image_buf(void) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci void *addr; 3698c2ecf20Sopenharmony_ci int size; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci addr = image_data.data; 3728c2ecf20Sopenharmony_ci size = PAGE_ALIGN(image_data.size); 3738c2ecf20Sopenharmony_ci while (size > 0) { 3748c2ecf20Sopenharmony_ci ClearPageReserved(vmalloc_to_page(addr)); 3758c2ecf20Sopenharmony_ci addr += PAGE_SIZE; 3768c2ecf20Sopenharmony_ci size -= PAGE_SIZE; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci vfree(image_data.data); 3798c2ecf20Sopenharmony_ci image_data.data = NULL; 3808c2ecf20Sopenharmony_ci image_data.status = IMAGE_INVALID; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci/* 3848c2ecf20Sopenharmony_ci * Allocate image buffer. 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_cistatic int alloc_image_buf(char *buffer, size_t count) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci void *addr; 3898c2ecf20Sopenharmony_ci int size; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (count < sizeof(image_header)) { 3928c2ecf20Sopenharmony_ci pr_warn("FLASH: Invalid candidate image\n"); 3938c2ecf20Sopenharmony_ci return -EINVAL; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci memcpy(&image_header, (void *)buffer, sizeof(image_header)); 3978c2ecf20Sopenharmony_ci image_data.size = be32_to_cpu(image_header.size); 3988c2ecf20Sopenharmony_ci pr_debug("FLASH: Candidate image size = %u\n", image_data.size); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (image_data.size > MAX_IMAGE_SIZE) { 4018c2ecf20Sopenharmony_ci pr_warn("FLASH: Too large image\n"); 4028c2ecf20Sopenharmony_ci return -EINVAL; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci if (image_data.size < VALIDATE_BUF_SIZE) { 4058c2ecf20Sopenharmony_ci pr_warn("FLASH: Image is shorter than expected\n"); 4068c2ecf20Sopenharmony_ci return -EINVAL; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci image_data.data = vzalloc(PAGE_ALIGN(image_data.size)); 4108c2ecf20Sopenharmony_ci if (!image_data.data) { 4118c2ecf20Sopenharmony_ci pr_err("%s : Failed to allocate memory\n", __func__); 4128c2ecf20Sopenharmony_ci return -ENOMEM; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* Pin memory */ 4168c2ecf20Sopenharmony_ci addr = image_data.data; 4178c2ecf20Sopenharmony_ci size = PAGE_ALIGN(image_data.size); 4188c2ecf20Sopenharmony_ci while (size > 0) { 4198c2ecf20Sopenharmony_ci SetPageReserved(vmalloc_to_page(addr)); 4208c2ecf20Sopenharmony_ci addr += PAGE_SIZE; 4218c2ecf20Sopenharmony_ci size -= PAGE_SIZE; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci image_data.status = IMAGE_LOADING; 4258c2ecf20Sopenharmony_ci return 0; 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci/* 4298c2ecf20Sopenharmony_ci * Copy candidate image 4308c2ecf20Sopenharmony_ci * 4318c2ecf20Sopenharmony_ci * Parse candidate image header to get total image size 4328c2ecf20Sopenharmony_ci * and pre-allocate required memory. 4338c2ecf20Sopenharmony_ci */ 4348c2ecf20Sopenharmony_cistatic ssize_t image_data_write(struct file *filp, struct kobject *kobj, 4358c2ecf20Sopenharmony_ci struct bin_attribute *bin_attr, 4368c2ecf20Sopenharmony_ci char *buffer, loff_t pos, size_t count) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci int rc; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci mutex_lock(&image_data_mutex); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* New image ? */ 4438c2ecf20Sopenharmony_ci if (pos == 0) { 4448c2ecf20Sopenharmony_ci /* Free memory, if already allocated */ 4458c2ecf20Sopenharmony_ci if (image_data.data) 4468c2ecf20Sopenharmony_ci free_image_buf(); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* Cancel outstanding image update request */ 4498c2ecf20Sopenharmony_ci if (update_flash_data.status == FLASH_IMG_READY) 4508c2ecf20Sopenharmony_ci opal_flash_update(FLASH_UPDATE_CANCEL); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* Allocate memory */ 4538c2ecf20Sopenharmony_ci rc = alloc_image_buf(buffer, count); 4548c2ecf20Sopenharmony_ci if (rc) 4558c2ecf20Sopenharmony_ci goto out; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (image_data.status != IMAGE_LOADING) { 4598c2ecf20Sopenharmony_ci rc = -ENOMEM; 4608c2ecf20Sopenharmony_ci goto out; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if ((pos + count) > image_data.size) { 4648c2ecf20Sopenharmony_ci rc = -EINVAL; 4658c2ecf20Sopenharmony_ci goto out; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci memcpy(image_data.data + pos, (void *)buffer, count); 4698c2ecf20Sopenharmony_ci rc = count; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* Set image status */ 4728c2ecf20Sopenharmony_ci if ((pos + count) == image_data.size) { 4738c2ecf20Sopenharmony_ci pr_debug("FLASH: Candidate image loaded....\n"); 4748c2ecf20Sopenharmony_ci image_data.status = IMAGE_READY; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ciout: 4788c2ecf20Sopenharmony_ci mutex_unlock(&image_data_mutex); 4798c2ecf20Sopenharmony_ci return rc; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci/* 4838c2ecf20Sopenharmony_ci * sysfs interface : 4848c2ecf20Sopenharmony_ci * OPAL uses below sysfs files for code update. 4858c2ecf20Sopenharmony_ci * We create these files under /sys/firmware/opal. 4868c2ecf20Sopenharmony_ci * 4878c2ecf20Sopenharmony_ci * image : Interface to load candidate firmware image 4888c2ecf20Sopenharmony_ci * validate_flash : Validate firmware image 4898c2ecf20Sopenharmony_ci * manage_flash : Commit/Reject firmware image 4908c2ecf20Sopenharmony_ci * update_flash : Flash new firmware image 4918c2ecf20Sopenharmony_ci * 4928c2ecf20Sopenharmony_ci */ 4938c2ecf20Sopenharmony_cistatic const struct bin_attribute image_data_attr = { 4948c2ecf20Sopenharmony_ci .attr = {.name = "image", .mode = 0200}, 4958c2ecf20Sopenharmony_ci .size = MAX_IMAGE_SIZE, /* Limit image size */ 4968c2ecf20Sopenharmony_ci .write = image_data_write, 4978c2ecf20Sopenharmony_ci}; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic struct kobj_attribute validate_attribute = 5008c2ecf20Sopenharmony_ci __ATTR(validate_flash, 0600, validate_show, validate_store); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic struct kobj_attribute manage_attribute = 5038c2ecf20Sopenharmony_ci __ATTR(manage_flash, 0600, manage_show, manage_store); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic struct kobj_attribute update_attribute = 5068c2ecf20Sopenharmony_ci __ATTR(update_flash, 0600, update_show, update_store); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic struct attribute *image_op_attrs[] = { 5098c2ecf20Sopenharmony_ci &validate_attribute.attr, 5108c2ecf20Sopenharmony_ci &manage_attribute.attr, 5118c2ecf20Sopenharmony_ci &update_attribute.attr, 5128c2ecf20Sopenharmony_ci NULL /* need to NULL terminate the list of attributes */ 5138c2ecf20Sopenharmony_ci}; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic struct attribute_group image_op_attr_group = { 5168c2ecf20Sopenharmony_ci .attrs = image_op_attrs, 5178c2ecf20Sopenharmony_ci}; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_civoid __init opal_flash_update_init(void) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci int ret; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci /* Allocate validate image buffer */ 5248c2ecf20Sopenharmony_ci validate_flash_data.buf = kzalloc(VALIDATE_BUF_SIZE, GFP_KERNEL); 5258c2ecf20Sopenharmony_ci if (!validate_flash_data.buf) { 5268c2ecf20Sopenharmony_ci pr_err("%s : Failed to allocate memory\n", __func__); 5278c2ecf20Sopenharmony_ci return; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* Make sure /sys/firmware/opal directory is created */ 5318c2ecf20Sopenharmony_ci if (!opal_kobj) { 5328c2ecf20Sopenharmony_ci pr_warn("FLASH: opal kobject is not available\n"); 5338c2ecf20Sopenharmony_ci goto nokobj; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* Create the sysfs files */ 5378c2ecf20Sopenharmony_ci ret = sysfs_create_group(opal_kobj, &image_op_attr_group); 5388c2ecf20Sopenharmony_ci if (ret) { 5398c2ecf20Sopenharmony_ci pr_warn("FLASH: Failed to create sysfs files\n"); 5408c2ecf20Sopenharmony_ci goto nokobj; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci ret = sysfs_create_bin_file(opal_kobj, &image_data_attr); 5448c2ecf20Sopenharmony_ci if (ret) { 5458c2ecf20Sopenharmony_ci pr_warn("FLASH: Failed to create sysfs files\n"); 5468c2ecf20Sopenharmony_ci goto nosysfs_file; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci /* Set default status */ 5508c2ecf20Sopenharmony_ci validate_flash_data.status = FLASH_NO_OP; 5518c2ecf20Sopenharmony_ci manage_flash_data.status = FLASH_NO_OP; 5528c2ecf20Sopenharmony_ci update_flash_data.status = FLASH_NO_OP; 5538c2ecf20Sopenharmony_ci image_data.status = IMAGE_INVALID; 5548c2ecf20Sopenharmony_ci return; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cinosysfs_file: 5578c2ecf20Sopenharmony_ci sysfs_remove_group(opal_kobj, &image_op_attr_group); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cinokobj: 5608c2ecf20Sopenharmony_ci kfree(validate_flash_data.buf); 5618c2ecf20Sopenharmony_ci return; 5628c2ecf20Sopenharmony_ci} 563