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