150bd6153SVasant Hegde /*
250bd6153SVasant Hegde  * PowerNV OPAL Firmware Update Interface
350bd6153SVasant Hegde  *
450bd6153SVasant Hegde  * Copyright 2013 IBM Corp.
550bd6153SVasant Hegde  *
650bd6153SVasant Hegde  * This program is free software; you can redistribute it and/or
750bd6153SVasant Hegde  * modify it under the terms of the GNU General Public License
850bd6153SVasant Hegde  * as published by the Free Software Foundation; either version
950bd6153SVasant Hegde  * 2 of the License, or (at your option) any later version.
1050bd6153SVasant Hegde  */
1150bd6153SVasant Hegde 
1250bd6153SVasant Hegde #define DEBUG
1350bd6153SVasant Hegde 
1450bd6153SVasant Hegde #include <linux/kernel.h>
1550bd6153SVasant Hegde #include <linux/reboot.h>
1650bd6153SVasant Hegde #include <linux/init.h>
1750bd6153SVasant Hegde #include <linux/kobject.h>
1850bd6153SVasant Hegde #include <linux/sysfs.h>
1950bd6153SVasant Hegde #include <linux/slab.h>
2050bd6153SVasant Hegde #include <linux/mm.h>
2150bd6153SVasant Hegde #include <linux/vmalloc.h>
2250bd6153SVasant Hegde #include <linux/pagemap.h>
232196c6f1SVasant Hegde #include <linux/delay.h>
2450bd6153SVasant Hegde 
2550bd6153SVasant Hegde #include <asm/opal.h>
2650bd6153SVasant Hegde 
2750bd6153SVasant Hegde /* FLASH status codes */
2850bd6153SVasant Hegde #define FLASH_NO_OP		-1099	/* No operation initiated by user */
2950bd6153SVasant Hegde #define FLASH_NO_AUTH		-9002	/* Not a service authority partition */
3050bd6153SVasant Hegde 
3150bd6153SVasant Hegde /* Validate image status values */
3250bd6153SVasant Hegde #define VALIDATE_IMG_READY	-1001	/* Image ready for validation */
3350bd6153SVasant Hegde #define VALIDATE_IMG_INCOMPLETE	-1002	/* User copied < VALIDATE_BUF_SIZE */
3450bd6153SVasant Hegde 
3550bd6153SVasant Hegde /* Manage image status values */
3650bd6153SVasant Hegde #define MANAGE_ACTIVE_ERR	-9001	/* Cannot overwrite active img */
3750bd6153SVasant Hegde 
3850bd6153SVasant Hegde /* Flash image status values */
3950bd6153SVasant Hegde #define FLASH_IMG_READY		0	/* Img ready for flash on reboot */
4050bd6153SVasant Hegde #define FLASH_INVALID_IMG	-1003	/* Flash image shorter than expected */
4150bd6153SVasant Hegde #define FLASH_IMG_NULL_DATA	-1004	/* Bad data in sg list entry */
4250bd6153SVasant Hegde #define FLASH_IMG_BAD_LEN	-1005	/* Bad length in sg list entry */
4350bd6153SVasant Hegde 
4450bd6153SVasant Hegde /* Manage operation tokens */
4550bd6153SVasant Hegde #define FLASH_REJECT_TMP_SIDE	0	/* Reject temporary fw image */
4650bd6153SVasant Hegde #define FLASH_COMMIT_TMP_SIDE	1	/* Commit temporary fw image */
4750bd6153SVasant Hegde 
4850bd6153SVasant Hegde /* Update tokens */
4950bd6153SVasant Hegde #define FLASH_UPDATE_CANCEL	0	/* Cancel update request */
5050bd6153SVasant Hegde #define FLASH_UPDATE_INIT	1	/* Initiate update */
5150bd6153SVasant Hegde 
5250bd6153SVasant Hegde /* Validate image update result tokens */
5350bd6153SVasant Hegde #define VALIDATE_TMP_UPDATE	0     /* T side will be updated */
5450bd6153SVasant Hegde #define VALIDATE_FLASH_AUTH	1     /* Partition does not have authority */
5550bd6153SVasant Hegde #define VALIDATE_INVALID_IMG	2     /* Candidate image is not valid */
5650bd6153SVasant Hegde #define VALIDATE_CUR_UNKNOWN	3     /* Current fixpack level is unknown */
5750bd6153SVasant Hegde /*
5850bd6153SVasant Hegde  * Current T side will be committed to P side before being replace with new
5950bd6153SVasant Hegde  * image, and the new image is downlevel from current image
6050bd6153SVasant Hegde  */
6150bd6153SVasant Hegde #define VALIDATE_TMP_COMMIT_DL	4
6250bd6153SVasant Hegde /*
6350bd6153SVasant Hegde  * Current T side will be committed to P side before being replaced with new
6450bd6153SVasant Hegde  * image
6550bd6153SVasant Hegde  */
6650bd6153SVasant Hegde #define VALIDATE_TMP_COMMIT	5
6750bd6153SVasant Hegde /*
6850bd6153SVasant Hegde  * T side will be updated with a downlevel image
6950bd6153SVasant Hegde  */
7050bd6153SVasant Hegde #define VALIDATE_TMP_UPDATE_DL	6
7150bd6153SVasant Hegde /*
7250bd6153SVasant Hegde  * The candidate image's release date is later than the system's firmware
7350bd6153SVasant Hegde  * service entitlement date - service warranty period has expired
7450bd6153SVasant Hegde  */
7550bd6153SVasant Hegde #define VALIDATE_OUT_OF_WRNTY	7
7650bd6153SVasant Hegde 
7750bd6153SVasant Hegde /* Validate buffer size */
7850bd6153SVasant Hegde #define VALIDATE_BUF_SIZE	4096
7950bd6153SVasant Hegde 
80bf16a4c2SVasant Hegde /* XXX: Assume candidate image size is <= 1GB */
81bf16a4c2SVasant Hegde #define MAX_IMAGE_SIZE	0x40000000
8250bd6153SVasant Hegde 
8350bd6153SVasant Hegde /* Image status */
8450bd6153SVasant Hegde enum {
8550bd6153SVasant Hegde 	IMAGE_INVALID,
8650bd6153SVasant Hegde 	IMAGE_LOADING,
8750bd6153SVasant Hegde 	IMAGE_READY,
8850bd6153SVasant Hegde };
8950bd6153SVasant Hegde 
9050bd6153SVasant Hegde /* Candidate image data */
9150bd6153SVasant Hegde struct image_data_t {
9250bd6153SVasant Hegde 	int		status;
9350bd6153SVasant Hegde 	void		*data;
9450bd6153SVasant Hegde 	uint32_t	size;
9550bd6153SVasant Hegde };
9650bd6153SVasant Hegde 
9750bd6153SVasant Hegde /* Candidate image header */
9850bd6153SVasant Hegde struct image_header_t {
9950bd6153SVasant Hegde 	uint16_t	magic;
10050bd6153SVasant Hegde 	uint16_t	version;
10150bd6153SVasant Hegde 	uint32_t	size;
10250bd6153SVasant Hegde };
10350bd6153SVasant Hegde 
10450bd6153SVasant Hegde struct validate_flash_t {
10550bd6153SVasant Hegde 	int		status;		/* Return status */
1068faaaeadSMasanari Iida 	void		*buf;		/* Candidate image buffer */
10750bd6153SVasant Hegde 	uint32_t	buf_size;	/* Image size */
10850bd6153SVasant Hegde 	uint32_t	result;		/* Update results token */
10950bd6153SVasant Hegde };
11050bd6153SVasant Hegde 
11150bd6153SVasant Hegde struct manage_flash_t {
11250bd6153SVasant Hegde 	int status;		/* Return status */
11350bd6153SVasant Hegde };
11450bd6153SVasant Hegde 
11550bd6153SVasant Hegde struct update_flash_t {
11650bd6153SVasant Hegde 	int status;		/* Return status */
11750bd6153SVasant Hegde };
11850bd6153SVasant Hegde 
11950bd6153SVasant Hegde static struct image_header_t	image_header;
12050bd6153SVasant Hegde static struct image_data_t	image_data;
12150bd6153SVasant Hegde static struct validate_flash_t	validate_flash_data;
12250bd6153SVasant Hegde static struct manage_flash_t	manage_flash_data;
1233f77df7fSVasant Hegde 
1243f77df7fSVasant Hegde /* Initialize update_flash_data status to No Operation */
1253f77df7fSVasant Hegde static struct update_flash_t	update_flash_data = {
1263f77df7fSVasant Hegde 	.status = FLASH_NO_OP,
1273f77df7fSVasant Hegde };
12850bd6153SVasant Hegde 
12950bd6153SVasant Hegde static DEFINE_MUTEX(image_data_mutex);
13050bd6153SVasant Hegde 
13150bd6153SVasant Hegde /*
13250bd6153SVasant Hegde  * Validate candidate image
13350bd6153SVasant Hegde  */
13450bd6153SVasant Hegde static inline void opal_flash_validate(void)
13550bd6153SVasant Hegde {
136cc146d1dSAnton Blanchard 	long ret;
137cc146d1dSAnton Blanchard 	void *buf = validate_flash_data.buf;
1388b8f7bf4SVasant Hegde 	__be32 size = cpu_to_be32(validate_flash_data.buf_size);
1398b8f7bf4SVasant Hegde 	__be32 result;
14050bd6153SVasant Hegde 
141cc146d1dSAnton Blanchard 	ret = opal_validate_flash(__pa(buf), &size, &result);
142cc146d1dSAnton Blanchard 
143cc146d1dSAnton Blanchard 	validate_flash_data.status = ret;
144cc146d1dSAnton Blanchard 	validate_flash_data.buf_size = be32_to_cpu(size);
145cc146d1dSAnton Blanchard 	validate_flash_data.result = be32_to_cpu(result);
14650bd6153SVasant Hegde }
14750bd6153SVasant Hegde 
14850bd6153SVasant Hegde /*
14950bd6153SVasant Hegde  * Validate output format:
15050bd6153SVasant Hegde  *     validate result token
15150bd6153SVasant Hegde  *     current image version details
15250bd6153SVasant Hegde  *     new image version details
15350bd6153SVasant Hegde  */
15450bd6153SVasant Hegde static ssize_t validate_show(struct kobject *kobj,
15550bd6153SVasant Hegde 			     struct kobj_attribute *attr, char *buf)
15650bd6153SVasant Hegde {
15750bd6153SVasant Hegde 	struct validate_flash_t *args_buf = &validate_flash_data;
15850bd6153SVasant Hegde 	int len;
15950bd6153SVasant Hegde 
16050bd6153SVasant Hegde 	/* Candidate image is not validated */
16150bd6153SVasant Hegde 	if (args_buf->status < VALIDATE_TMP_UPDATE) {
16250bd6153SVasant Hegde 		len = sprintf(buf, "%d\n", args_buf->status);
16350bd6153SVasant Hegde 		goto out;
16450bd6153SVasant Hegde 	}
16550bd6153SVasant Hegde 
16650bd6153SVasant Hegde 	/* Result token */
16750bd6153SVasant Hegde 	len = sprintf(buf, "%d\n", args_buf->result);
16850bd6153SVasant Hegde 
16950bd6153SVasant Hegde 	/* Current and candidate image version details */
17050bd6153SVasant Hegde 	if ((args_buf->result != VALIDATE_TMP_UPDATE) &&
17150bd6153SVasant Hegde 	    (args_buf->result < VALIDATE_CUR_UNKNOWN))
17250bd6153SVasant Hegde 		goto out;
17350bd6153SVasant Hegde 
17450bd6153SVasant Hegde 	if (args_buf->buf_size > (VALIDATE_BUF_SIZE - len)) {
17550bd6153SVasant Hegde 		memcpy(buf + len, args_buf->buf, VALIDATE_BUF_SIZE - len);
17650bd6153SVasant Hegde 		len = VALIDATE_BUF_SIZE;
17750bd6153SVasant Hegde 	} else {
17850bd6153SVasant Hegde 		memcpy(buf + len, args_buf->buf, args_buf->buf_size);
17950bd6153SVasant Hegde 		len += args_buf->buf_size;
18050bd6153SVasant Hegde 	}
18150bd6153SVasant Hegde out:
18250bd6153SVasant Hegde 	/* Set status to default */
18350bd6153SVasant Hegde 	args_buf->status = FLASH_NO_OP;
18450bd6153SVasant Hegde 	return len;
18550bd6153SVasant Hegde }
18650bd6153SVasant Hegde 
18750bd6153SVasant Hegde /*
18850bd6153SVasant Hegde  * Validate candidate firmware image
18950bd6153SVasant Hegde  *
19050bd6153SVasant Hegde  * Note:
19150bd6153SVasant Hegde  *   We are only interested in first 4K bytes of the
19250bd6153SVasant Hegde  *   candidate image.
19350bd6153SVasant Hegde  */
19450bd6153SVasant Hegde static ssize_t validate_store(struct kobject *kobj,
19550bd6153SVasant Hegde 			      struct kobj_attribute *attr,
19650bd6153SVasant Hegde 			      const char *buf, size_t count)
19750bd6153SVasant Hegde {
19850bd6153SVasant Hegde 	struct validate_flash_t *args_buf = &validate_flash_data;
19950bd6153SVasant Hegde 
20050bd6153SVasant Hegde 	if (buf[0] != '1')
20150bd6153SVasant Hegde 		return -EINVAL;
20250bd6153SVasant Hegde 
20350bd6153SVasant Hegde 	mutex_lock(&image_data_mutex);
20450bd6153SVasant Hegde 
20550bd6153SVasant Hegde 	if (image_data.status != IMAGE_READY ||
20650bd6153SVasant Hegde 	    image_data.size < VALIDATE_BUF_SIZE) {
20750bd6153SVasant Hegde 		args_buf->result = VALIDATE_INVALID_IMG;
20850bd6153SVasant Hegde 		args_buf->status = VALIDATE_IMG_INCOMPLETE;
20950bd6153SVasant Hegde 		goto out;
21050bd6153SVasant Hegde 	}
21150bd6153SVasant Hegde 
21250bd6153SVasant Hegde 	/* Copy first 4k bytes of candidate image */
21350bd6153SVasant Hegde 	memcpy(args_buf->buf, image_data.data, VALIDATE_BUF_SIZE);
21450bd6153SVasant Hegde 
21550bd6153SVasant Hegde 	args_buf->status = VALIDATE_IMG_READY;
21650bd6153SVasant Hegde 	args_buf->buf_size = VALIDATE_BUF_SIZE;
21750bd6153SVasant Hegde 
21850bd6153SVasant Hegde 	/* Validate candidate image */
21950bd6153SVasant Hegde 	opal_flash_validate();
22050bd6153SVasant Hegde 
22150bd6153SVasant Hegde out:
22250bd6153SVasant Hegde 	mutex_unlock(&image_data_mutex);
22350bd6153SVasant Hegde 	return count;
22450bd6153SVasant Hegde }
22550bd6153SVasant Hegde 
22650bd6153SVasant Hegde /*
22750bd6153SVasant Hegde  * Manage flash routine
22850bd6153SVasant Hegde  */
22950bd6153SVasant Hegde static inline void opal_flash_manage(uint8_t op)
23050bd6153SVasant Hegde {
23150bd6153SVasant Hegde 	struct manage_flash_t *const args_buf = &manage_flash_data;
23250bd6153SVasant Hegde 
23350bd6153SVasant Hegde 	args_buf->status = opal_manage_flash(op);
23450bd6153SVasant Hegde }
23550bd6153SVasant Hegde 
23650bd6153SVasant Hegde /*
23750bd6153SVasant Hegde  * Show manage flash status
23850bd6153SVasant Hegde  */
23950bd6153SVasant Hegde static ssize_t manage_show(struct kobject *kobj,
24050bd6153SVasant Hegde 			   struct kobj_attribute *attr, char *buf)
24150bd6153SVasant Hegde {
24250bd6153SVasant Hegde 	struct manage_flash_t *const args_buf = &manage_flash_data;
24350bd6153SVasant Hegde 	int rc;
24450bd6153SVasant Hegde 
24550bd6153SVasant Hegde 	rc = sprintf(buf, "%d\n", args_buf->status);
24650bd6153SVasant Hegde 	/* Set status to default*/
24750bd6153SVasant Hegde 	args_buf->status = FLASH_NO_OP;
24850bd6153SVasant Hegde 	return rc;
24950bd6153SVasant Hegde }
25050bd6153SVasant Hegde 
25150bd6153SVasant Hegde /*
25250bd6153SVasant Hegde  * Manage operations:
25350bd6153SVasant Hegde  *   0 - Reject
25450bd6153SVasant Hegde  *   1 - Commit
25550bd6153SVasant Hegde  */
25650bd6153SVasant Hegde static ssize_t manage_store(struct kobject *kobj,
25750bd6153SVasant Hegde 			    struct kobj_attribute *attr,
25850bd6153SVasant Hegde 			    const char *buf, size_t count)
25950bd6153SVasant Hegde {
26050bd6153SVasant Hegde 	uint8_t op;
26150bd6153SVasant Hegde 	switch (buf[0]) {
26250bd6153SVasant Hegde 	case '0':
26350bd6153SVasant Hegde 		op = FLASH_REJECT_TMP_SIDE;
26450bd6153SVasant Hegde 		break;
26550bd6153SVasant Hegde 	case '1':
26650bd6153SVasant Hegde 		op = FLASH_COMMIT_TMP_SIDE;
26750bd6153SVasant Hegde 		break;
26850bd6153SVasant Hegde 	default:
26950bd6153SVasant Hegde 		return -EINVAL;
27050bd6153SVasant Hegde 	}
27150bd6153SVasant Hegde 
27250bd6153SVasant Hegde 	/* commit/reject temporary image */
27350bd6153SVasant Hegde 	opal_flash_manage(op);
27450bd6153SVasant Hegde 	return count;
27550bd6153SVasant Hegde }
27650bd6153SVasant Hegde 
27750bd6153SVasant Hegde /*
27850bd6153SVasant Hegde  * OPAL update flash
27950bd6153SVasant Hegde  */
28050bd6153SVasant Hegde static int opal_flash_update(int op)
28150bd6153SVasant Hegde {
2823441f04bSAnton Blanchard 	struct opal_sg_list *list;
28350bd6153SVasant Hegde 	unsigned long addr;
28450bd6153SVasant Hegde 	int64_t rc = OPAL_PARAMETER;
28550bd6153SVasant Hegde 
28650bd6153SVasant Hegde 	if (op == FLASH_UPDATE_CANCEL) {
28750bd6153SVasant Hegde 		pr_alert("FLASH: Image update cancelled\n");
28850bd6153SVasant Hegde 		addr = '\0';
28950bd6153SVasant Hegde 		goto flash;
29050bd6153SVasant Hegde 	}
29150bd6153SVasant Hegde 
2923441f04bSAnton Blanchard 	list = opal_vmalloc_to_sg_list(image_data.data, image_data.size);
29350bd6153SVasant Hegde 	if (!list)
29450bd6153SVasant Hegde 		goto invalid_img;
29550bd6153SVasant Hegde 
29650bd6153SVasant Hegde 	/* First entry address */
29750bd6153SVasant Hegde 	addr = __pa(list);
29850bd6153SVasant Hegde 
29950bd6153SVasant Hegde flash:
30050bd6153SVasant Hegde 	rc = opal_update_flash(addr);
30150bd6153SVasant Hegde 
30250bd6153SVasant Hegde invalid_img:
30350bd6153SVasant Hegde 	return rc;
30450bd6153SVasant Hegde }
30550bd6153SVasant Hegde 
3062196c6f1SVasant Hegde /* Return CPUs to OPAL before starting FW update */
3072196c6f1SVasant Hegde static void flash_return_cpu(void *info)
3082196c6f1SVasant Hegde {
3092196c6f1SVasant Hegde 	int cpu = smp_processor_id();
3102196c6f1SVasant Hegde 
3112196c6f1SVasant Hegde 	if (!cpu_online(cpu))
3122196c6f1SVasant Hegde 		return;
3132196c6f1SVasant Hegde 
3142196c6f1SVasant Hegde 	/* Disable IRQ */
3152196c6f1SVasant Hegde 	hard_irq_disable();
3162196c6f1SVasant Hegde 
3172196c6f1SVasant Hegde 	/* Return the CPU to OPAL */
3182196c6f1SVasant Hegde 	opal_return_cpu();
3192196c6f1SVasant Hegde }
3202196c6f1SVasant Hegde 
3212196c6f1SVasant Hegde /* This gets called just before system reboots */
3222196c6f1SVasant Hegde void opal_flash_term_callback(void)
3232196c6f1SVasant Hegde {
3242196c6f1SVasant Hegde 	struct cpumask mask;
3252196c6f1SVasant Hegde 
3262196c6f1SVasant Hegde 	if (update_flash_data.status != FLASH_IMG_READY)
3272196c6f1SVasant Hegde 		return;
3282196c6f1SVasant Hegde 
3292196c6f1SVasant Hegde 	pr_alert("FLASH: Flashing new firmware\n");
3302196c6f1SVasant Hegde 	pr_alert("FLASH: Image is %u bytes\n", image_data.size);
3312196c6f1SVasant Hegde 	pr_alert("FLASH: Performing flash and reboot/shutdown\n");
3322196c6f1SVasant Hegde 	pr_alert("FLASH: This will take several minutes. Do not power off!\n");
3332196c6f1SVasant Hegde 
3342196c6f1SVasant Hegde 	/* Small delay to help getting the above message out */
3352196c6f1SVasant Hegde 	msleep(500);
3362196c6f1SVasant Hegde 
3372196c6f1SVasant Hegde 	/* Return secondary CPUs to firmware */
3382196c6f1SVasant Hegde 	cpumask_copy(&mask, cpu_online_mask);
3392196c6f1SVasant Hegde 	cpumask_clear_cpu(smp_processor_id(), &mask);
3402196c6f1SVasant Hegde 	if (!cpumask_empty(&mask))
3412196c6f1SVasant Hegde 		smp_call_function_many(&mask,
3422196c6f1SVasant Hegde 				       flash_return_cpu, NULL, false);
3432196c6f1SVasant Hegde 	/* Hard disable interrupts */
3442196c6f1SVasant Hegde 	hard_irq_disable();
3452196c6f1SVasant Hegde }
3462196c6f1SVasant Hegde 
34750bd6153SVasant Hegde /*
34850bd6153SVasant Hegde  * Show candidate image status
34950bd6153SVasant Hegde  */
35050bd6153SVasant Hegde static ssize_t update_show(struct kobject *kobj,
35150bd6153SVasant Hegde 			   struct kobj_attribute *attr, char *buf)
35250bd6153SVasant Hegde {
35350bd6153SVasant Hegde 	struct update_flash_t *const args_buf = &update_flash_data;
35450bd6153SVasant Hegde 	return sprintf(buf, "%d\n", args_buf->status);
35550bd6153SVasant Hegde }
35650bd6153SVasant Hegde 
35750bd6153SVasant Hegde /*
35850bd6153SVasant Hegde  * Set update image flag
35950bd6153SVasant Hegde  *  1 - Flash new image
36050bd6153SVasant Hegde  *  0 - Cancel flash request
36150bd6153SVasant Hegde  */
36250bd6153SVasant Hegde static ssize_t update_store(struct kobject *kobj,
36350bd6153SVasant Hegde 			    struct kobj_attribute *attr,
36450bd6153SVasant Hegde 			    const char *buf, size_t count)
36550bd6153SVasant Hegde {
36650bd6153SVasant Hegde 	struct update_flash_t *const args_buf = &update_flash_data;
36750bd6153SVasant Hegde 	int rc = count;
36850bd6153SVasant Hegde 
36950bd6153SVasant Hegde 	mutex_lock(&image_data_mutex);
37050bd6153SVasant Hegde 
37150bd6153SVasant Hegde 	switch (buf[0]) {
37250bd6153SVasant Hegde 	case '0':
37350bd6153SVasant Hegde 		if (args_buf->status == FLASH_IMG_READY)
37450bd6153SVasant Hegde 			opal_flash_update(FLASH_UPDATE_CANCEL);
37550bd6153SVasant Hegde 		args_buf->status = FLASH_NO_OP;
37650bd6153SVasant Hegde 		break;
37750bd6153SVasant Hegde 	case '1':
37850bd6153SVasant Hegde 		/* Image is loaded? */
37950bd6153SVasant Hegde 		if (image_data.status == IMAGE_READY)
38050bd6153SVasant Hegde 			args_buf->status =
38150bd6153SVasant Hegde 				opal_flash_update(FLASH_UPDATE_INIT);
38250bd6153SVasant Hegde 		else
38350bd6153SVasant Hegde 			args_buf->status = FLASH_INVALID_IMG;
38450bd6153SVasant Hegde 		break;
38550bd6153SVasant Hegde 	default:
38650bd6153SVasant Hegde 		rc = -EINVAL;
38750bd6153SVasant Hegde 	}
38850bd6153SVasant Hegde 
38950bd6153SVasant Hegde 	mutex_unlock(&image_data_mutex);
39050bd6153SVasant Hegde 	return rc;
39150bd6153SVasant Hegde }
39250bd6153SVasant Hegde 
39350bd6153SVasant Hegde /*
39450bd6153SVasant Hegde  * Free image buffer
39550bd6153SVasant Hegde  */
39650bd6153SVasant Hegde static void free_image_buf(void)
39750bd6153SVasant Hegde {
39850bd6153SVasant Hegde 	void *addr;
39950bd6153SVasant Hegde 	int size;
40050bd6153SVasant Hegde 
40150bd6153SVasant Hegde 	addr = image_data.data;
40250bd6153SVasant Hegde 	size = PAGE_ALIGN(image_data.size);
40350bd6153SVasant Hegde 	while (size > 0) {
40450bd6153SVasant Hegde 		ClearPageReserved(vmalloc_to_page(addr));
40550bd6153SVasant Hegde 		addr += PAGE_SIZE;
40650bd6153SVasant Hegde 		size -= PAGE_SIZE;
40750bd6153SVasant Hegde 	}
40850bd6153SVasant Hegde 	vfree(image_data.data);
40950bd6153SVasant Hegde 	image_data.data = NULL;
41050bd6153SVasant Hegde 	image_data.status = IMAGE_INVALID;
41150bd6153SVasant Hegde }
41250bd6153SVasant Hegde 
41350bd6153SVasant Hegde /*
41450bd6153SVasant Hegde  * Allocate image buffer.
41550bd6153SVasant Hegde  */
41650bd6153SVasant Hegde static int alloc_image_buf(char *buffer, size_t count)
41750bd6153SVasant Hegde {
41850bd6153SVasant Hegde 	void *addr;
41950bd6153SVasant Hegde 	int size;
42050bd6153SVasant Hegde 
42150bd6153SVasant Hegde 	if (count < sizeof(struct image_header_t)) {
42250bd6153SVasant Hegde 		pr_warn("FLASH: Invalid candidate image\n");
42350bd6153SVasant Hegde 		return -EINVAL;
42450bd6153SVasant Hegde 	}
42550bd6153SVasant Hegde 
42650bd6153SVasant Hegde 	memcpy(&image_header, (void *)buffer, sizeof(struct image_header_t));
42750bd6153SVasant Hegde 	image_data.size = be32_to_cpu(image_header.size);
4288faaaeadSMasanari Iida 	pr_debug("FLASH: Candidate image size = %u\n", image_data.size);
42950bd6153SVasant Hegde 
43050bd6153SVasant Hegde 	if (image_data.size > MAX_IMAGE_SIZE) {
43150bd6153SVasant Hegde 		pr_warn("FLASH: Too large image\n");
43250bd6153SVasant Hegde 		return -EINVAL;
43350bd6153SVasant Hegde 	}
43450bd6153SVasant Hegde 	if (image_data.size < VALIDATE_BUF_SIZE) {
43550bd6153SVasant Hegde 		pr_warn("FLASH: Image is shorter than expected\n");
43650bd6153SVasant Hegde 		return -EINVAL;
43750bd6153SVasant Hegde 	}
43850bd6153SVasant Hegde 
43950bd6153SVasant Hegde 	image_data.data = vzalloc(PAGE_ALIGN(image_data.size));
44050bd6153SVasant Hegde 	if (!image_data.data) {
44150bd6153SVasant Hegde 		pr_err("%s : Failed to allocate memory\n", __func__);
44250bd6153SVasant Hegde 		return -ENOMEM;
44350bd6153SVasant Hegde 	}
44450bd6153SVasant Hegde 
44550bd6153SVasant Hegde 	/* Pin memory */
44650bd6153SVasant Hegde 	addr = image_data.data;
44750bd6153SVasant Hegde 	size = PAGE_ALIGN(image_data.size);
44850bd6153SVasant Hegde 	while (size > 0) {
44950bd6153SVasant Hegde 		SetPageReserved(vmalloc_to_page(addr));
45050bd6153SVasant Hegde 		addr += PAGE_SIZE;
45150bd6153SVasant Hegde 		size -= PAGE_SIZE;
45250bd6153SVasant Hegde 	}
45350bd6153SVasant Hegde 
45450bd6153SVasant Hegde 	image_data.status = IMAGE_LOADING;
45550bd6153SVasant Hegde 	return 0;
45650bd6153SVasant Hegde }
45750bd6153SVasant Hegde 
45850bd6153SVasant Hegde /*
45950bd6153SVasant Hegde  * Copy candidate image
46050bd6153SVasant Hegde  *
46150bd6153SVasant Hegde  * Parse candidate image header to get total image size
46250bd6153SVasant Hegde  * and pre-allocate required memory.
46350bd6153SVasant Hegde  */
46450bd6153SVasant Hegde static ssize_t image_data_write(struct file *filp, struct kobject *kobj,
46550bd6153SVasant Hegde 				struct bin_attribute *bin_attr,
46650bd6153SVasant Hegde 				char *buffer, loff_t pos, size_t count)
46750bd6153SVasant Hegde {
46850bd6153SVasant Hegde 	int rc;
46950bd6153SVasant Hegde 
47050bd6153SVasant Hegde 	mutex_lock(&image_data_mutex);
47150bd6153SVasant Hegde 
47250bd6153SVasant Hegde 	/* New image ? */
47350bd6153SVasant Hegde 	if (pos == 0) {
47450bd6153SVasant Hegde 		/* Free memory, if already allocated */
47550bd6153SVasant Hegde 		if (image_data.data)
47650bd6153SVasant Hegde 			free_image_buf();
47750bd6153SVasant Hegde 
47850bd6153SVasant Hegde 		/* Cancel outstanding image update request */
47950bd6153SVasant Hegde 		if (update_flash_data.status == FLASH_IMG_READY)
48050bd6153SVasant Hegde 			opal_flash_update(FLASH_UPDATE_CANCEL);
48150bd6153SVasant Hegde 
48250bd6153SVasant Hegde 		/* Allocate memory */
48350bd6153SVasant Hegde 		rc = alloc_image_buf(buffer, count);
48450bd6153SVasant Hegde 		if (rc)
48550bd6153SVasant Hegde 			goto out;
48650bd6153SVasant Hegde 	}
48750bd6153SVasant Hegde 
48850bd6153SVasant Hegde 	if (image_data.status != IMAGE_LOADING) {
48950bd6153SVasant Hegde 		rc = -ENOMEM;
49050bd6153SVasant Hegde 		goto out;
49150bd6153SVasant Hegde 	}
49250bd6153SVasant Hegde 
49350bd6153SVasant Hegde 	if ((pos + count) > image_data.size) {
49450bd6153SVasant Hegde 		rc = -EINVAL;
49550bd6153SVasant Hegde 		goto out;
49650bd6153SVasant Hegde 	}
49750bd6153SVasant Hegde 
49850bd6153SVasant Hegde 	memcpy(image_data.data + pos, (void *)buffer, count);
49950bd6153SVasant Hegde 	rc = count;
50050bd6153SVasant Hegde 
50150bd6153SVasant Hegde 	/* Set image status */
50250bd6153SVasant Hegde 	if ((pos + count) == image_data.size) {
50350bd6153SVasant Hegde 		pr_debug("FLASH: Candidate image loaded....\n");
50450bd6153SVasant Hegde 		image_data.status = IMAGE_READY;
50550bd6153SVasant Hegde 	}
50650bd6153SVasant Hegde 
50750bd6153SVasant Hegde out:
50850bd6153SVasant Hegde 	mutex_unlock(&image_data_mutex);
50950bd6153SVasant Hegde 	return rc;
51050bd6153SVasant Hegde }
51150bd6153SVasant Hegde 
51250bd6153SVasant Hegde /*
51350bd6153SVasant Hegde  * sysfs interface :
51450bd6153SVasant Hegde  *  OPAL uses below sysfs files for code update.
51550bd6153SVasant Hegde  *  We create these files under /sys/firmware/opal.
51650bd6153SVasant Hegde  *
51750bd6153SVasant Hegde  *   image		: Interface to load candidate firmware image
51850bd6153SVasant Hegde  *   validate_flash	: Validate firmware image
51950bd6153SVasant Hegde  *   manage_flash	: Commit/Reject firmware image
52050bd6153SVasant Hegde  *   update_flash	: Flash new firmware image
52150bd6153SVasant Hegde  *
52250bd6153SVasant Hegde  */
5238bfa42abSBhumika Goyal static const struct bin_attribute image_data_attr = {
52450bd6153SVasant Hegde 	.attr = {.name = "image", .mode = 0200},
52550bd6153SVasant Hegde 	.size = MAX_IMAGE_SIZE,	/* Limit image size */
52650bd6153SVasant Hegde 	.write = image_data_write,
52750bd6153SVasant Hegde };
52850bd6153SVasant Hegde 
52950bd6153SVasant Hegde static struct kobj_attribute validate_attribute =
53050bd6153SVasant Hegde 	__ATTR(validate_flash, 0600, validate_show, validate_store);
53150bd6153SVasant Hegde 
53250bd6153SVasant Hegde static struct kobj_attribute manage_attribute =
53350bd6153SVasant Hegde 	__ATTR(manage_flash, 0600, manage_show, manage_store);
53450bd6153SVasant Hegde 
53550bd6153SVasant Hegde static struct kobj_attribute update_attribute =
53650bd6153SVasant Hegde 	__ATTR(update_flash, 0600, update_show, update_store);
53750bd6153SVasant Hegde 
53850bd6153SVasant Hegde static struct attribute *image_op_attrs[] = {
53950bd6153SVasant Hegde 	&validate_attribute.attr,
54050bd6153SVasant Hegde 	&manage_attribute.attr,
54150bd6153SVasant Hegde 	&update_attribute.attr,
54250bd6153SVasant Hegde 	NULL	/* need to NULL terminate the list of attributes */
54350bd6153SVasant Hegde };
54450bd6153SVasant Hegde 
54550bd6153SVasant Hegde static struct attribute_group image_op_attr_group = {
54650bd6153SVasant Hegde 	.attrs = image_op_attrs,
54750bd6153SVasant Hegde };
54850bd6153SVasant Hegde 
549ed59190eSCyril Bur void __init opal_flash_update_init(void)
55050bd6153SVasant Hegde {
55150bd6153SVasant Hegde 	int ret;
55250bd6153SVasant Hegde 
55350bd6153SVasant Hegde 	/* Allocate validate image buffer */
55450bd6153SVasant Hegde 	validate_flash_data.buf = kzalloc(VALIDATE_BUF_SIZE, GFP_KERNEL);
55550bd6153SVasant Hegde 	if (!validate_flash_data.buf) {
55650bd6153SVasant Hegde 		pr_err("%s : Failed to allocate memory\n", __func__);
55750bd6153SVasant Hegde 		return;
55850bd6153SVasant Hegde 	}
55950bd6153SVasant Hegde 
56050bd6153SVasant Hegde 	/* Make sure /sys/firmware/opal directory is created */
56150bd6153SVasant Hegde 	if (!opal_kobj) {
56250bd6153SVasant Hegde 		pr_warn("FLASH: opal kobject is not available\n");
56350bd6153SVasant Hegde 		goto nokobj;
56450bd6153SVasant Hegde 	}
56550bd6153SVasant Hegde 
56650bd6153SVasant Hegde 	/* Create the sysfs files */
56750bd6153SVasant Hegde 	ret = sysfs_create_group(opal_kobj, &image_op_attr_group);
56850bd6153SVasant Hegde 	if (ret) {
56950bd6153SVasant Hegde 		pr_warn("FLASH: Failed to create sysfs files\n");
57050bd6153SVasant Hegde 		goto nokobj;
57150bd6153SVasant Hegde 	}
57250bd6153SVasant Hegde 
57350bd6153SVasant Hegde 	ret = sysfs_create_bin_file(opal_kobj, &image_data_attr);
57450bd6153SVasant Hegde 	if (ret) {
57550bd6153SVasant Hegde 		pr_warn("FLASH: Failed to create sysfs files\n");
57650bd6153SVasant Hegde 		goto nosysfs_file;
57750bd6153SVasant Hegde 	}
57850bd6153SVasant Hegde 
57950bd6153SVasant Hegde 	/* Set default status */
58050bd6153SVasant Hegde 	validate_flash_data.status = FLASH_NO_OP;
58150bd6153SVasant Hegde 	manage_flash_data.status = FLASH_NO_OP;
58250bd6153SVasant Hegde 	update_flash_data.status = FLASH_NO_OP;
58350bd6153SVasant Hegde 	image_data.status = IMAGE_INVALID;
58450bd6153SVasant Hegde 	return;
58550bd6153SVasant Hegde 
58650bd6153SVasant Hegde nosysfs_file:
58750bd6153SVasant Hegde 	sysfs_remove_group(opal_kobj, &image_op_attr_group);
58850bd6153SVasant Hegde 
58950bd6153SVasant Hegde nokobj:
59050bd6153SVasant Hegde 	kfree(validate_flash_data.buf);
59150bd6153SVasant Hegde 	return;
59250bd6153SVasant Hegde }
593