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 /* Image status */
8350bd6153SVasant Hegde enum {
8450bd6153SVasant Hegde 	IMAGE_INVALID,
8550bd6153SVasant Hegde 	IMAGE_LOADING,
8650bd6153SVasant Hegde 	IMAGE_READY,
8750bd6153SVasant Hegde };
8850bd6153SVasant Hegde 
8950bd6153SVasant Hegde /* Candidate image data */
9050bd6153SVasant Hegde struct image_data_t {
9150bd6153SVasant Hegde 	int		status;
9250bd6153SVasant Hegde 	void		*data;
9350bd6153SVasant Hegde 	uint32_t	size;
9450bd6153SVasant Hegde };
9550bd6153SVasant Hegde 
9650bd6153SVasant Hegde /* Candidate image header */
9750bd6153SVasant Hegde struct image_header_t {
9850bd6153SVasant Hegde 	uint16_t	magic;
9950bd6153SVasant Hegde 	uint16_t	version;
10050bd6153SVasant Hegde 	uint32_t	size;
10150bd6153SVasant Hegde };
10250bd6153SVasant Hegde 
10350bd6153SVasant Hegde struct validate_flash_t {
10450bd6153SVasant Hegde 	int		status;		/* Return status */
1058faaaeadSMasanari Iida 	void		*buf;		/* Candidate image buffer */
10650bd6153SVasant Hegde 	uint32_t	buf_size;	/* Image size */
10750bd6153SVasant Hegde 	uint32_t	result;		/* Update results token */
10850bd6153SVasant Hegde };
10950bd6153SVasant Hegde 
11050bd6153SVasant Hegde struct manage_flash_t {
11150bd6153SVasant Hegde 	int status;		/* Return status */
11250bd6153SVasant Hegde };
11350bd6153SVasant Hegde 
11450bd6153SVasant Hegde struct update_flash_t {
11550bd6153SVasant Hegde 	int status;		/* Return status */
11650bd6153SVasant Hegde };
11750bd6153SVasant Hegde 
11850bd6153SVasant Hegde static struct image_header_t	image_header;
11950bd6153SVasant Hegde static struct image_data_t	image_data;
12050bd6153SVasant Hegde static struct validate_flash_t	validate_flash_data;
12150bd6153SVasant Hegde static struct manage_flash_t	manage_flash_data;
12250bd6153SVasant Hegde static struct update_flash_t	update_flash_data;
12350bd6153SVasant Hegde 
12450bd6153SVasant Hegde static DEFINE_MUTEX(image_data_mutex);
12550bd6153SVasant Hegde 
12650bd6153SVasant Hegde /*
12750bd6153SVasant Hegde  * Validate candidate image
12850bd6153SVasant Hegde  */
12950bd6153SVasant Hegde static inline void opal_flash_validate(void)
13050bd6153SVasant Hegde {
131cc146d1dSAnton Blanchard 	long ret;
132cc146d1dSAnton Blanchard 	void *buf = validate_flash_data.buf;
133cc146d1dSAnton Blanchard 	__be32 size, result;
13450bd6153SVasant Hegde 
135cc146d1dSAnton Blanchard 	ret = opal_validate_flash(__pa(buf), &size, &result);
136cc146d1dSAnton Blanchard 
137cc146d1dSAnton Blanchard 	validate_flash_data.status = ret;
138cc146d1dSAnton Blanchard 	validate_flash_data.buf_size = be32_to_cpu(size);
139cc146d1dSAnton Blanchard 	validate_flash_data.result = be32_to_cpu(result);
14050bd6153SVasant Hegde }
14150bd6153SVasant Hegde 
14250bd6153SVasant Hegde /*
14350bd6153SVasant Hegde  * Validate output format:
14450bd6153SVasant Hegde  *     validate result token
14550bd6153SVasant Hegde  *     current image version details
14650bd6153SVasant Hegde  *     new image version details
14750bd6153SVasant Hegde  */
14850bd6153SVasant Hegde static ssize_t validate_show(struct kobject *kobj,
14950bd6153SVasant Hegde 			     struct kobj_attribute *attr, char *buf)
15050bd6153SVasant Hegde {
15150bd6153SVasant Hegde 	struct validate_flash_t *args_buf = &validate_flash_data;
15250bd6153SVasant Hegde 	int len;
15350bd6153SVasant Hegde 
15450bd6153SVasant Hegde 	/* Candidate image is not validated */
15550bd6153SVasant Hegde 	if (args_buf->status < VALIDATE_TMP_UPDATE) {
15650bd6153SVasant Hegde 		len = sprintf(buf, "%d\n", args_buf->status);
15750bd6153SVasant Hegde 		goto out;
15850bd6153SVasant Hegde 	}
15950bd6153SVasant Hegde 
16050bd6153SVasant Hegde 	/* Result token */
16150bd6153SVasant Hegde 	len = sprintf(buf, "%d\n", args_buf->result);
16250bd6153SVasant Hegde 
16350bd6153SVasant Hegde 	/* Current and candidate image version details */
16450bd6153SVasant Hegde 	if ((args_buf->result != VALIDATE_TMP_UPDATE) &&
16550bd6153SVasant Hegde 	    (args_buf->result < VALIDATE_CUR_UNKNOWN))
16650bd6153SVasant Hegde 		goto out;
16750bd6153SVasant Hegde 
16850bd6153SVasant Hegde 	if (args_buf->buf_size > (VALIDATE_BUF_SIZE - len)) {
16950bd6153SVasant Hegde 		memcpy(buf + len, args_buf->buf, VALIDATE_BUF_SIZE - len);
17050bd6153SVasant Hegde 		len = VALIDATE_BUF_SIZE;
17150bd6153SVasant Hegde 	} else {
17250bd6153SVasant Hegde 		memcpy(buf + len, args_buf->buf, args_buf->buf_size);
17350bd6153SVasant Hegde 		len += args_buf->buf_size;
17450bd6153SVasant Hegde 	}
17550bd6153SVasant Hegde out:
17650bd6153SVasant Hegde 	/* Set status to default */
17750bd6153SVasant Hegde 	args_buf->status = FLASH_NO_OP;
17850bd6153SVasant Hegde 	return len;
17950bd6153SVasant Hegde }
18050bd6153SVasant Hegde 
18150bd6153SVasant Hegde /*
18250bd6153SVasant Hegde  * Validate candidate firmware image
18350bd6153SVasant Hegde  *
18450bd6153SVasant Hegde  * Note:
18550bd6153SVasant Hegde  *   We are only interested in first 4K bytes of the
18650bd6153SVasant Hegde  *   candidate image.
18750bd6153SVasant Hegde  */
18850bd6153SVasant Hegde static ssize_t validate_store(struct kobject *kobj,
18950bd6153SVasant Hegde 			      struct kobj_attribute *attr,
19050bd6153SVasant Hegde 			      const char *buf, size_t count)
19150bd6153SVasant Hegde {
19250bd6153SVasant Hegde 	struct validate_flash_t *args_buf = &validate_flash_data;
19350bd6153SVasant Hegde 
19450bd6153SVasant Hegde 	if (buf[0] != '1')
19550bd6153SVasant Hegde 		return -EINVAL;
19650bd6153SVasant Hegde 
19750bd6153SVasant Hegde 	mutex_lock(&image_data_mutex);
19850bd6153SVasant Hegde 
19950bd6153SVasant Hegde 	if (image_data.status != IMAGE_READY ||
20050bd6153SVasant Hegde 	    image_data.size < VALIDATE_BUF_SIZE) {
20150bd6153SVasant Hegde 		args_buf->result = VALIDATE_INVALID_IMG;
20250bd6153SVasant Hegde 		args_buf->status = VALIDATE_IMG_INCOMPLETE;
20350bd6153SVasant Hegde 		goto out;
20450bd6153SVasant Hegde 	}
20550bd6153SVasant Hegde 
20650bd6153SVasant Hegde 	/* Copy first 4k bytes of candidate image */
20750bd6153SVasant Hegde 	memcpy(args_buf->buf, image_data.data, VALIDATE_BUF_SIZE);
20850bd6153SVasant Hegde 
20950bd6153SVasant Hegde 	args_buf->status = VALIDATE_IMG_READY;
21050bd6153SVasant Hegde 	args_buf->buf_size = VALIDATE_BUF_SIZE;
21150bd6153SVasant Hegde 
21250bd6153SVasant Hegde 	/* Validate candidate image */
21350bd6153SVasant Hegde 	opal_flash_validate();
21450bd6153SVasant Hegde 
21550bd6153SVasant Hegde out:
21650bd6153SVasant Hegde 	mutex_unlock(&image_data_mutex);
21750bd6153SVasant Hegde 	return count;
21850bd6153SVasant Hegde }
21950bd6153SVasant Hegde 
22050bd6153SVasant Hegde /*
22150bd6153SVasant Hegde  * Manage flash routine
22250bd6153SVasant Hegde  */
22350bd6153SVasant Hegde static inline void opal_flash_manage(uint8_t op)
22450bd6153SVasant Hegde {
22550bd6153SVasant Hegde 	struct manage_flash_t *const args_buf = &manage_flash_data;
22650bd6153SVasant Hegde 
22750bd6153SVasant Hegde 	args_buf->status = opal_manage_flash(op);
22850bd6153SVasant Hegde }
22950bd6153SVasant Hegde 
23050bd6153SVasant Hegde /*
23150bd6153SVasant Hegde  * Show manage flash status
23250bd6153SVasant Hegde  */
23350bd6153SVasant Hegde static ssize_t manage_show(struct kobject *kobj,
23450bd6153SVasant Hegde 			   struct kobj_attribute *attr, char *buf)
23550bd6153SVasant Hegde {
23650bd6153SVasant Hegde 	struct manage_flash_t *const args_buf = &manage_flash_data;
23750bd6153SVasant Hegde 	int rc;
23850bd6153SVasant Hegde 
23950bd6153SVasant Hegde 	rc = sprintf(buf, "%d\n", args_buf->status);
24050bd6153SVasant Hegde 	/* Set status to default*/
24150bd6153SVasant Hegde 	args_buf->status = FLASH_NO_OP;
24250bd6153SVasant Hegde 	return rc;
24350bd6153SVasant Hegde }
24450bd6153SVasant Hegde 
24550bd6153SVasant Hegde /*
24650bd6153SVasant Hegde  * Manage operations:
24750bd6153SVasant Hegde  *   0 - Reject
24850bd6153SVasant Hegde  *   1 - Commit
24950bd6153SVasant Hegde  */
25050bd6153SVasant Hegde static ssize_t manage_store(struct kobject *kobj,
25150bd6153SVasant Hegde 			    struct kobj_attribute *attr,
25250bd6153SVasant Hegde 			    const char *buf, size_t count)
25350bd6153SVasant Hegde {
25450bd6153SVasant Hegde 	uint8_t op;
25550bd6153SVasant Hegde 	switch (buf[0]) {
25650bd6153SVasant Hegde 	case '0':
25750bd6153SVasant Hegde 		op = FLASH_REJECT_TMP_SIDE;
25850bd6153SVasant Hegde 		break;
25950bd6153SVasant Hegde 	case '1':
26050bd6153SVasant Hegde 		op = FLASH_COMMIT_TMP_SIDE;
26150bd6153SVasant Hegde 		break;
26250bd6153SVasant Hegde 	default:
26350bd6153SVasant Hegde 		return -EINVAL;
26450bd6153SVasant Hegde 	}
26550bd6153SVasant Hegde 
26650bd6153SVasant Hegde 	/* commit/reject temporary image */
26750bd6153SVasant Hegde 	opal_flash_manage(op);
26850bd6153SVasant Hegde 	return count;
26950bd6153SVasant Hegde }
27050bd6153SVasant Hegde 
27150bd6153SVasant Hegde /*
27250bd6153SVasant Hegde  * OPAL update flash
27350bd6153SVasant Hegde  */
27450bd6153SVasant Hegde static int opal_flash_update(int op)
27550bd6153SVasant Hegde {
2763441f04bSAnton Blanchard 	struct opal_sg_list *list;
27750bd6153SVasant Hegde 	unsigned long addr;
27850bd6153SVasant Hegde 	int64_t rc = OPAL_PARAMETER;
27950bd6153SVasant Hegde 
28050bd6153SVasant Hegde 	if (op == FLASH_UPDATE_CANCEL) {
28150bd6153SVasant Hegde 		pr_alert("FLASH: Image update cancelled\n");
28250bd6153SVasant Hegde 		addr = '\0';
28350bd6153SVasant Hegde 		goto flash;
28450bd6153SVasant Hegde 	}
28550bd6153SVasant Hegde 
2863441f04bSAnton Blanchard 	list = opal_vmalloc_to_sg_list(image_data.data, image_data.size);
28750bd6153SVasant Hegde 	if (!list)
28850bd6153SVasant Hegde 		goto invalid_img;
28950bd6153SVasant Hegde 
29050bd6153SVasant Hegde 	/* First entry address */
29150bd6153SVasant Hegde 	addr = __pa(list);
29250bd6153SVasant Hegde 
29350bd6153SVasant Hegde 	pr_alert("FLASH: Image is %u bytes\n", image_data.size);
29450bd6153SVasant Hegde 	pr_alert("FLASH: Image update requested\n");
29550bd6153SVasant Hegde 	pr_alert("FLASH: Image will be updated during system reboot\n");
29650bd6153SVasant Hegde 	pr_alert("FLASH: This will take several minutes. Do not power off!\n");
29750bd6153SVasant Hegde 
29850bd6153SVasant Hegde flash:
29950bd6153SVasant Hegde 	rc = opal_update_flash(addr);
30050bd6153SVasant Hegde 
30150bd6153SVasant Hegde invalid_img:
30250bd6153SVasant Hegde 	return rc;
30350bd6153SVasant Hegde }
30450bd6153SVasant Hegde 
30550bd6153SVasant Hegde /*
30650bd6153SVasant Hegde  * Show candidate image status
30750bd6153SVasant Hegde  */
30850bd6153SVasant Hegde static ssize_t update_show(struct kobject *kobj,
30950bd6153SVasant Hegde 			   struct kobj_attribute *attr, char *buf)
31050bd6153SVasant Hegde {
31150bd6153SVasant Hegde 	struct update_flash_t *const args_buf = &update_flash_data;
31250bd6153SVasant Hegde 	return sprintf(buf, "%d\n", args_buf->status);
31350bd6153SVasant Hegde }
31450bd6153SVasant Hegde 
31550bd6153SVasant Hegde /*
31650bd6153SVasant Hegde  * Set update image flag
31750bd6153SVasant Hegde  *  1 - Flash new image
31850bd6153SVasant Hegde  *  0 - Cancel flash request
31950bd6153SVasant Hegde  */
32050bd6153SVasant Hegde static ssize_t update_store(struct kobject *kobj,
32150bd6153SVasant Hegde 			    struct kobj_attribute *attr,
32250bd6153SVasant Hegde 			    const char *buf, size_t count)
32350bd6153SVasant Hegde {
32450bd6153SVasant Hegde 	struct update_flash_t *const args_buf = &update_flash_data;
32550bd6153SVasant Hegde 	int rc = count;
32650bd6153SVasant Hegde 
32750bd6153SVasant Hegde 	mutex_lock(&image_data_mutex);
32850bd6153SVasant Hegde 
32950bd6153SVasant Hegde 	switch (buf[0]) {
33050bd6153SVasant Hegde 	case '0':
33150bd6153SVasant Hegde 		if (args_buf->status == FLASH_IMG_READY)
33250bd6153SVasant Hegde 			opal_flash_update(FLASH_UPDATE_CANCEL);
33350bd6153SVasant Hegde 		args_buf->status = FLASH_NO_OP;
33450bd6153SVasant Hegde 		break;
33550bd6153SVasant Hegde 	case '1':
33650bd6153SVasant Hegde 		/* Image is loaded? */
33750bd6153SVasant Hegde 		if (image_data.status == IMAGE_READY)
33850bd6153SVasant Hegde 			args_buf->status =
33950bd6153SVasant Hegde 				opal_flash_update(FLASH_UPDATE_INIT);
34050bd6153SVasant Hegde 		else
34150bd6153SVasant Hegde 			args_buf->status = FLASH_INVALID_IMG;
34250bd6153SVasant Hegde 		break;
34350bd6153SVasant Hegde 	default:
34450bd6153SVasant Hegde 		rc = -EINVAL;
34550bd6153SVasant Hegde 	}
34650bd6153SVasant Hegde 
34750bd6153SVasant Hegde 	mutex_unlock(&image_data_mutex);
34850bd6153SVasant Hegde 	return rc;
34950bd6153SVasant Hegde }
35050bd6153SVasant Hegde 
35150bd6153SVasant Hegde /*
35250bd6153SVasant Hegde  * Free image buffer
35350bd6153SVasant Hegde  */
35450bd6153SVasant Hegde static void free_image_buf(void)
35550bd6153SVasant Hegde {
35650bd6153SVasant Hegde 	void *addr;
35750bd6153SVasant Hegde 	int size;
35850bd6153SVasant Hegde 
35950bd6153SVasant Hegde 	addr = image_data.data;
36050bd6153SVasant Hegde 	size = PAGE_ALIGN(image_data.size);
36150bd6153SVasant Hegde 	while (size > 0) {
36250bd6153SVasant Hegde 		ClearPageReserved(vmalloc_to_page(addr));
36350bd6153SVasant Hegde 		addr += PAGE_SIZE;
36450bd6153SVasant Hegde 		size -= PAGE_SIZE;
36550bd6153SVasant Hegde 	}
36650bd6153SVasant Hegde 	vfree(image_data.data);
36750bd6153SVasant Hegde 	image_data.data = NULL;
36850bd6153SVasant Hegde 	image_data.status = IMAGE_INVALID;
36950bd6153SVasant Hegde }
37050bd6153SVasant Hegde 
37150bd6153SVasant Hegde /*
37250bd6153SVasant Hegde  * Allocate image buffer.
37350bd6153SVasant Hegde  */
37450bd6153SVasant Hegde static int alloc_image_buf(char *buffer, size_t count)
37550bd6153SVasant Hegde {
37650bd6153SVasant Hegde 	void *addr;
37750bd6153SVasant Hegde 	int size;
37850bd6153SVasant Hegde 
37950bd6153SVasant Hegde 	if (count < sizeof(struct image_header_t)) {
38050bd6153SVasant Hegde 		pr_warn("FLASH: Invalid candidate image\n");
38150bd6153SVasant Hegde 		return -EINVAL;
38250bd6153SVasant Hegde 	}
38350bd6153SVasant Hegde 
38450bd6153SVasant Hegde 	memcpy(&image_header, (void *)buffer, sizeof(struct image_header_t));
38550bd6153SVasant Hegde 	image_data.size = be32_to_cpu(image_header.size);
3868faaaeadSMasanari Iida 	pr_debug("FLASH: Candidate image size = %u\n", image_data.size);
38750bd6153SVasant Hegde 
38850bd6153SVasant Hegde 	if (image_data.size > MAX_IMAGE_SIZE) {
38950bd6153SVasant Hegde 		pr_warn("FLASH: Too large image\n");
39050bd6153SVasant Hegde 		return -EINVAL;
39150bd6153SVasant Hegde 	}
39250bd6153SVasant Hegde 	if (image_data.size < VALIDATE_BUF_SIZE) {
39350bd6153SVasant Hegde 		pr_warn("FLASH: Image is shorter than expected\n");
39450bd6153SVasant Hegde 		return -EINVAL;
39550bd6153SVasant Hegde 	}
39650bd6153SVasant Hegde 
39750bd6153SVasant Hegde 	image_data.data = vzalloc(PAGE_ALIGN(image_data.size));
39850bd6153SVasant Hegde 	if (!image_data.data) {
39950bd6153SVasant Hegde 		pr_err("%s : Failed to allocate memory\n", __func__);
40050bd6153SVasant Hegde 		return -ENOMEM;
40150bd6153SVasant Hegde 	}
40250bd6153SVasant Hegde 
40350bd6153SVasant Hegde 	/* Pin memory */
40450bd6153SVasant Hegde 	addr = image_data.data;
40550bd6153SVasant Hegde 	size = PAGE_ALIGN(image_data.size);
40650bd6153SVasant Hegde 	while (size > 0) {
40750bd6153SVasant Hegde 		SetPageReserved(vmalloc_to_page(addr));
40850bd6153SVasant Hegde 		addr += PAGE_SIZE;
40950bd6153SVasant Hegde 		size -= PAGE_SIZE;
41050bd6153SVasant Hegde 	}
41150bd6153SVasant Hegde 
41250bd6153SVasant Hegde 	image_data.status = IMAGE_LOADING;
41350bd6153SVasant Hegde 	return 0;
41450bd6153SVasant Hegde }
41550bd6153SVasant Hegde 
41650bd6153SVasant Hegde /*
41750bd6153SVasant Hegde  * Copy candidate image
41850bd6153SVasant Hegde  *
41950bd6153SVasant Hegde  * Parse candidate image header to get total image size
42050bd6153SVasant Hegde  * and pre-allocate required memory.
42150bd6153SVasant Hegde  */
42250bd6153SVasant Hegde static ssize_t image_data_write(struct file *filp, struct kobject *kobj,
42350bd6153SVasant Hegde 				struct bin_attribute *bin_attr,
42450bd6153SVasant Hegde 				char *buffer, loff_t pos, size_t count)
42550bd6153SVasant Hegde {
42650bd6153SVasant Hegde 	int rc;
42750bd6153SVasant Hegde 
42850bd6153SVasant Hegde 	mutex_lock(&image_data_mutex);
42950bd6153SVasant Hegde 
43050bd6153SVasant Hegde 	/* New image ? */
43150bd6153SVasant Hegde 	if (pos == 0) {
43250bd6153SVasant Hegde 		/* Free memory, if already allocated */
43350bd6153SVasant Hegde 		if (image_data.data)
43450bd6153SVasant Hegde 			free_image_buf();
43550bd6153SVasant Hegde 
43650bd6153SVasant Hegde 		/* Cancel outstanding image update request */
43750bd6153SVasant Hegde 		if (update_flash_data.status == FLASH_IMG_READY)
43850bd6153SVasant Hegde 			opal_flash_update(FLASH_UPDATE_CANCEL);
43950bd6153SVasant Hegde 
44050bd6153SVasant Hegde 		/* Allocate memory */
44150bd6153SVasant Hegde 		rc = alloc_image_buf(buffer, count);
44250bd6153SVasant Hegde 		if (rc)
44350bd6153SVasant Hegde 			goto out;
44450bd6153SVasant Hegde 	}
44550bd6153SVasant Hegde 
44650bd6153SVasant Hegde 	if (image_data.status != IMAGE_LOADING) {
44750bd6153SVasant Hegde 		rc = -ENOMEM;
44850bd6153SVasant Hegde 		goto out;
44950bd6153SVasant Hegde 	}
45050bd6153SVasant Hegde 
45150bd6153SVasant Hegde 	if ((pos + count) > image_data.size) {
45250bd6153SVasant Hegde 		rc = -EINVAL;
45350bd6153SVasant Hegde 		goto out;
45450bd6153SVasant Hegde 	}
45550bd6153SVasant Hegde 
45650bd6153SVasant Hegde 	memcpy(image_data.data + pos, (void *)buffer, count);
45750bd6153SVasant Hegde 	rc = count;
45850bd6153SVasant Hegde 
45950bd6153SVasant Hegde 	/* Set image status */
46050bd6153SVasant Hegde 	if ((pos + count) == image_data.size) {
46150bd6153SVasant Hegde 		pr_debug("FLASH: Candidate image loaded....\n");
46250bd6153SVasant Hegde 		image_data.status = IMAGE_READY;
46350bd6153SVasant Hegde 	}
46450bd6153SVasant Hegde 
46550bd6153SVasant Hegde out:
46650bd6153SVasant Hegde 	mutex_unlock(&image_data_mutex);
46750bd6153SVasant Hegde 	return rc;
46850bd6153SVasant Hegde }
46950bd6153SVasant Hegde 
47050bd6153SVasant Hegde /*
47150bd6153SVasant Hegde  * sysfs interface :
47250bd6153SVasant Hegde  *  OPAL uses below sysfs files for code update.
47350bd6153SVasant Hegde  *  We create these files under /sys/firmware/opal.
47450bd6153SVasant Hegde  *
47550bd6153SVasant Hegde  *   image		: Interface to load candidate firmware image
47650bd6153SVasant Hegde  *   validate_flash	: Validate firmware image
47750bd6153SVasant Hegde  *   manage_flash	: Commit/Reject firmware image
47850bd6153SVasant Hegde  *   update_flash	: Flash new firmware image
47950bd6153SVasant Hegde  *
48050bd6153SVasant Hegde  */
48150bd6153SVasant Hegde static struct bin_attribute image_data_attr = {
48250bd6153SVasant Hegde 	.attr = {.name = "image", .mode = 0200},
48350bd6153SVasant Hegde 	.size = MAX_IMAGE_SIZE,	/* Limit image size */
48450bd6153SVasant Hegde 	.write = image_data_write,
48550bd6153SVasant Hegde };
48650bd6153SVasant Hegde 
48750bd6153SVasant Hegde static struct kobj_attribute validate_attribute =
48850bd6153SVasant Hegde 	__ATTR(validate_flash, 0600, validate_show, validate_store);
48950bd6153SVasant Hegde 
49050bd6153SVasant Hegde static struct kobj_attribute manage_attribute =
49150bd6153SVasant Hegde 	__ATTR(manage_flash, 0600, manage_show, manage_store);
49250bd6153SVasant Hegde 
49350bd6153SVasant Hegde static struct kobj_attribute update_attribute =
49450bd6153SVasant Hegde 	__ATTR(update_flash, 0600, update_show, update_store);
49550bd6153SVasant Hegde 
49650bd6153SVasant Hegde static struct attribute *image_op_attrs[] = {
49750bd6153SVasant Hegde 	&validate_attribute.attr,
49850bd6153SVasant Hegde 	&manage_attribute.attr,
49950bd6153SVasant Hegde 	&update_attribute.attr,
50050bd6153SVasant Hegde 	NULL	/* need to NULL terminate the list of attributes */
50150bd6153SVasant Hegde };
50250bd6153SVasant Hegde 
50350bd6153SVasant Hegde static struct attribute_group image_op_attr_group = {
50450bd6153SVasant Hegde 	.attrs = image_op_attrs,
50550bd6153SVasant Hegde };
50650bd6153SVasant Hegde 
50750bd6153SVasant Hegde void __init opal_flash_init(void)
50850bd6153SVasant Hegde {
50950bd6153SVasant Hegde 	int ret;
51050bd6153SVasant Hegde 
51150bd6153SVasant Hegde 	/* Allocate validate image buffer */
51250bd6153SVasant Hegde 	validate_flash_data.buf = kzalloc(VALIDATE_BUF_SIZE, GFP_KERNEL);
51350bd6153SVasant Hegde 	if (!validate_flash_data.buf) {
51450bd6153SVasant Hegde 		pr_err("%s : Failed to allocate memory\n", __func__);
51550bd6153SVasant Hegde 		return;
51650bd6153SVasant Hegde 	}
51750bd6153SVasant Hegde 
51850bd6153SVasant Hegde 	/* Make sure /sys/firmware/opal directory is created */
51950bd6153SVasant Hegde 	if (!opal_kobj) {
52050bd6153SVasant Hegde 		pr_warn("FLASH: opal kobject is not available\n");
52150bd6153SVasant Hegde 		goto nokobj;
52250bd6153SVasant Hegde 	}
52350bd6153SVasant Hegde 
52450bd6153SVasant Hegde 	/* Create the sysfs files */
52550bd6153SVasant Hegde 	ret = sysfs_create_group(opal_kobj, &image_op_attr_group);
52650bd6153SVasant Hegde 	if (ret) {
52750bd6153SVasant Hegde 		pr_warn("FLASH: Failed to create sysfs files\n");
52850bd6153SVasant Hegde 		goto nokobj;
52950bd6153SVasant Hegde 	}
53050bd6153SVasant Hegde 
53150bd6153SVasant Hegde 	ret = sysfs_create_bin_file(opal_kobj, &image_data_attr);
53250bd6153SVasant Hegde 	if (ret) {
53350bd6153SVasant Hegde 		pr_warn("FLASH: Failed to create sysfs files\n");
53450bd6153SVasant Hegde 		goto nosysfs_file;
53550bd6153SVasant Hegde 	}
53650bd6153SVasant Hegde 
53750bd6153SVasant Hegde 	/* Set default status */
53850bd6153SVasant Hegde 	validate_flash_data.status = FLASH_NO_OP;
53950bd6153SVasant Hegde 	manage_flash_data.status = FLASH_NO_OP;
54050bd6153SVasant Hegde 	update_flash_data.status = FLASH_NO_OP;
54150bd6153SVasant Hegde 	image_data.status = IMAGE_INVALID;
54250bd6153SVasant Hegde 	return;
54350bd6153SVasant Hegde 
54450bd6153SVasant Hegde nosysfs_file:
54550bd6153SVasant Hegde 	sysfs_remove_group(opal_kobj, &image_op_attr_group);
54650bd6153SVasant Hegde 
54750bd6153SVasant Hegde nokobj:
54850bd6153SVasant Hegde 	kfree(validate_flash_data.buf);
54950bd6153SVasant Hegde 	return;
55050bd6153SVasant Hegde }
551