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