| 
									
										
										
										
											2013-10-24 16:04:58 +05:30
										 |  |  | /*
 | 
					
						
							|  |  |  |  * PowerNV OPAL Firmware Update Interface | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright 2013 IBM Corp. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  |  * modify it under the terms of the GNU General Public License | 
					
						
							|  |  |  |  * as published by the Free Software Foundation; either version | 
					
						
							|  |  |  |  * 2 of the License, or (at your option) any later version. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define DEBUG
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/reboot.h>
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/kobject.h>
 | 
					
						
							|  |  |  | #include <linux/sysfs.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  | #include <linux/mm.h>
 | 
					
						
							|  |  |  | #include <linux/vmalloc.h>
 | 
					
						
							|  |  |  | #include <linux/pagemap.h>
 | 
					
						
							| 
									
										
										
										
											2014-04-09 22:48:55 +05:30
										 |  |  | #include <linux/delay.h>
 | 
					
						
							| 
									
										
										
										
											2013-10-24 16:04:58 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  | #include <asm/opal.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* FLASH status codes */ | 
					
						
							|  |  |  | #define FLASH_NO_OP		-1099	/* No operation initiated by user */
 | 
					
						
							|  |  |  | #define FLASH_NO_AUTH		-9002	/* Not a service authority partition */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Validate image status values */ | 
					
						
							|  |  |  | #define VALIDATE_IMG_READY	-1001	/* Image ready for validation */
 | 
					
						
							|  |  |  | #define VALIDATE_IMG_INCOMPLETE	-1002	/* User copied < VALIDATE_BUF_SIZE */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Manage image status values */ | 
					
						
							|  |  |  | #define MANAGE_ACTIVE_ERR	-9001	/* Cannot overwrite active img */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Flash image status values */ | 
					
						
							|  |  |  | #define FLASH_IMG_READY		0	/* Img ready for flash on reboot */
 | 
					
						
							|  |  |  | #define FLASH_INVALID_IMG	-1003	/* Flash image shorter than expected */
 | 
					
						
							|  |  |  | #define FLASH_IMG_NULL_DATA	-1004	/* Bad data in sg list entry */
 | 
					
						
							|  |  |  | #define FLASH_IMG_BAD_LEN	-1005	/* Bad length in sg list entry */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Manage operation tokens */ | 
					
						
							|  |  |  | #define FLASH_REJECT_TMP_SIDE	0	/* Reject temporary fw image */
 | 
					
						
							|  |  |  | #define FLASH_COMMIT_TMP_SIDE	1	/* Commit temporary fw image */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Update tokens */ | 
					
						
							|  |  |  | #define FLASH_UPDATE_CANCEL	0	/* Cancel update request */
 | 
					
						
							|  |  |  | #define FLASH_UPDATE_INIT	1	/* Initiate update */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Validate image update result tokens */ | 
					
						
							|  |  |  | #define VALIDATE_TMP_UPDATE	0     /* T side will be updated */
 | 
					
						
							|  |  |  | #define VALIDATE_FLASH_AUTH	1     /* Partition does not have authority */
 | 
					
						
							|  |  |  | #define VALIDATE_INVALID_IMG	2     /* Candidate image is not valid */
 | 
					
						
							|  |  |  | #define VALIDATE_CUR_UNKNOWN	3     /* Current fixpack level is unknown */
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Current T side will be committed to P side before being replace with new | 
					
						
							|  |  |  |  * image, and the new image is downlevel from current image | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #define VALIDATE_TMP_COMMIT_DL	4
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Current T side will be committed to P side before being replaced with new | 
					
						
							|  |  |  |  * image | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #define VALIDATE_TMP_COMMIT	5
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * T side will be updated with a downlevel image | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #define VALIDATE_TMP_UPDATE_DL	6
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * The candidate image's release date is later than the system's firmware | 
					
						
							|  |  |  |  * service entitlement date - service warranty period has expired | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #define VALIDATE_OUT_OF_WRNTY	7
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Validate buffer size */ | 
					
						
							|  |  |  | #define VALIDATE_BUF_SIZE	4096
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-02 17:00:42 +05:30
										 |  |  | /* XXX: Assume candidate image size is <= 1GB */ | 
					
						
							|  |  |  | #define MAX_IMAGE_SIZE	0x40000000
 | 
					
						
							| 
									
										
										
										
											2013-10-24 16:04:58 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  | /* Image status */ | 
					
						
							|  |  |  | enum { | 
					
						
							|  |  |  | 	IMAGE_INVALID, | 
					
						
							|  |  |  | 	IMAGE_LOADING, | 
					
						
							|  |  |  | 	IMAGE_READY, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Candidate image data */ | 
					
						
							|  |  |  | struct image_data_t { | 
					
						
							|  |  |  | 	int		status; | 
					
						
							|  |  |  | 	void		*data; | 
					
						
							|  |  |  | 	uint32_t	size; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Candidate image header */ | 
					
						
							|  |  |  | struct image_header_t { | 
					
						
							|  |  |  | 	uint16_t	magic; | 
					
						
							|  |  |  | 	uint16_t	version; | 
					
						
							|  |  |  | 	uint32_t	size; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct validate_flash_t { | 
					
						
							|  |  |  | 	int		status;		/* Return status */ | 
					
						
							| 
									
										
										
										
											2014-01-07 21:58:06 +09:00
										 |  |  | 	void		*buf;		/* Candidate image buffer */ | 
					
						
							| 
									
										
										
										
											2013-10-24 16:04:58 +05:30
										 |  |  | 	uint32_t	buf_size;	/* Image size */ | 
					
						
							|  |  |  | 	uint32_t	result;		/* Update results token */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct manage_flash_t { | 
					
						
							|  |  |  | 	int status;		/* Return status */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct update_flash_t { | 
					
						
							|  |  |  | 	int status;		/* Return status */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct image_header_t	image_header; | 
					
						
							|  |  |  | static struct image_data_t	image_data; | 
					
						
							|  |  |  | static struct validate_flash_t	validate_flash_data; | 
					
						
							|  |  |  | static struct manage_flash_t	manage_flash_data; | 
					
						
							|  |  |  | static struct update_flash_t	update_flash_data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static DEFINE_MUTEX(image_data_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Validate candidate image | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static inline void opal_flash_validate(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-04-24 07:25:34 +10:00
										 |  |  | 	long ret; | 
					
						
							|  |  |  | 	void *buf = validate_flash_data.buf; | 
					
						
							| 
									
										
										
										
											2014-06-05 10:13:40 +05:30
										 |  |  | 	__be32 size = cpu_to_be32(validate_flash_data.buf_size); | 
					
						
							|  |  |  | 	__be32 result; | 
					
						
							| 
									
										
										
										
											2014-04-24 07:25:34 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ret = opal_validate_flash(__pa(buf), &size, &result); | 
					
						
							| 
									
										
										
										
											2013-10-24 16:04:58 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-04-24 07:25:34 +10:00
										 |  |  | 	validate_flash_data.status = ret; | 
					
						
							|  |  |  | 	validate_flash_data.buf_size = be32_to_cpu(size); | 
					
						
							|  |  |  | 	validate_flash_data.result = be32_to_cpu(result); | 
					
						
							| 
									
										
										
										
											2013-10-24 16:04:58 +05:30
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Validate output format: | 
					
						
							|  |  |  |  *     validate result token | 
					
						
							|  |  |  |  *     current image version details | 
					
						
							|  |  |  |  *     new image version details | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static ssize_t validate_show(struct kobject *kobj, | 
					
						
							|  |  |  | 			     struct kobj_attribute *attr, char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct validate_flash_t *args_buf = &validate_flash_data; | 
					
						
							|  |  |  | 	int len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Candidate image is not validated */ | 
					
						
							|  |  |  | 	if (args_buf->status < VALIDATE_TMP_UPDATE) { | 
					
						
							|  |  |  | 		len = sprintf(buf, "%d\n", args_buf->status); | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Result token */ | 
					
						
							|  |  |  | 	len = sprintf(buf, "%d\n", args_buf->result); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Current and candidate image version details */ | 
					
						
							|  |  |  | 	if ((args_buf->result != VALIDATE_TMP_UPDATE) && | 
					
						
							|  |  |  | 	    (args_buf->result < VALIDATE_CUR_UNKNOWN)) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (args_buf->buf_size > (VALIDATE_BUF_SIZE - len)) { | 
					
						
							|  |  |  | 		memcpy(buf + len, args_buf->buf, VALIDATE_BUF_SIZE - len); | 
					
						
							|  |  |  | 		len = VALIDATE_BUF_SIZE; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		memcpy(buf + len, args_buf->buf, args_buf->buf_size); | 
					
						
							|  |  |  | 		len += args_buf->buf_size; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	/* Set status to default */ | 
					
						
							|  |  |  | 	args_buf->status = FLASH_NO_OP; | 
					
						
							|  |  |  | 	return len; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Validate candidate firmware image | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Note: | 
					
						
							|  |  |  |  *   We are only interested in first 4K bytes of the | 
					
						
							|  |  |  |  *   candidate image. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static ssize_t validate_store(struct kobject *kobj, | 
					
						
							|  |  |  | 			      struct kobj_attribute *attr, | 
					
						
							|  |  |  | 			      const char *buf, size_t count) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct validate_flash_t *args_buf = &validate_flash_data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (buf[0] != '1') | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&image_data_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (image_data.status != IMAGE_READY || | 
					
						
							|  |  |  | 	    image_data.size < VALIDATE_BUF_SIZE) { | 
					
						
							|  |  |  | 		args_buf->result = VALIDATE_INVALID_IMG; | 
					
						
							|  |  |  | 		args_buf->status = VALIDATE_IMG_INCOMPLETE; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Copy first 4k bytes of candidate image */ | 
					
						
							|  |  |  | 	memcpy(args_buf->buf, image_data.data, VALIDATE_BUF_SIZE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	args_buf->status = VALIDATE_IMG_READY; | 
					
						
							|  |  |  | 	args_buf->buf_size = VALIDATE_BUF_SIZE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Validate candidate image */ | 
					
						
							|  |  |  | 	opal_flash_validate(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	mutex_unlock(&image_data_mutex); | 
					
						
							|  |  |  | 	return count; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Manage flash routine | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static inline void opal_flash_manage(uint8_t op) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct manage_flash_t *const args_buf = &manage_flash_data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	args_buf->status = opal_manage_flash(op); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Show manage flash status | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static ssize_t manage_show(struct kobject *kobj, | 
					
						
							|  |  |  | 			   struct kobj_attribute *attr, char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct manage_flash_t *const args_buf = &manage_flash_data; | 
					
						
							|  |  |  | 	int rc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rc = sprintf(buf, "%d\n", args_buf->status); | 
					
						
							|  |  |  | 	/* Set status to default*/ | 
					
						
							|  |  |  | 	args_buf->status = FLASH_NO_OP; | 
					
						
							|  |  |  | 	return rc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Manage operations: | 
					
						
							|  |  |  |  *   0 - Reject | 
					
						
							|  |  |  |  *   1 - Commit | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static ssize_t manage_store(struct kobject *kobj, | 
					
						
							|  |  |  | 			    struct kobj_attribute *attr, | 
					
						
							|  |  |  | 			    const char *buf, size_t count) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uint8_t op; | 
					
						
							|  |  |  | 	switch (buf[0]) { | 
					
						
							|  |  |  | 	case '0': | 
					
						
							|  |  |  | 		op = FLASH_REJECT_TMP_SIDE; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case '1': | 
					
						
							|  |  |  | 		op = FLASH_COMMIT_TMP_SIDE; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* commit/reject temporary image */ | 
					
						
							|  |  |  | 	opal_flash_manage(op); | 
					
						
							|  |  |  | 	return count; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * OPAL update flash | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int opal_flash_update(int op) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-04-22 15:01:26 +10:00
										 |  |  | 	struct opal_sg_list *list; | 
					
						
							| 
									
										
										
										
											2013-10-24 16:04:58 +05:30
										 |  |  | 	unsigned long addr; | 
					
						
							|  |  |  | 	int64_t rc = OPAL_PARAMETER; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (op == FLASH_UPDATE_CANCEL) { | 
					
						
							|  |  |  | 		pr_alert("FLASH: Image update cancelled\n"); | 
					
						
							|  |  |  | 		addr = '\0'; | 
					
						
							|  |  |  | 		goto flash; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-04-22 15:01:26 +10:00
										 |  |  | 	list = opal_vmalloc_to_sg_list(image_data.data, image_data.size); | 
					
						
							| 
									
										
										
										
											2013-10-24 16:04:58 +05:30
										 |  |  | 	if (!list) | 
					
						
							|  |  |  | 		goto invalid_img; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* First entry address */ | 
					
						
							|  |  |  | 	addr = __pa(list); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | flash: | 
					
						
							|  |  |  | 	rc = opal_update_flash(addr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | invalid_img: | 
					
						
							|  |  |  | 	return rc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-04-09 22:48:55 +05:30
										 |  |  | /* Return CPUs to OPAL before starting FW update */ | 
					
						
							|  |  |  | static void flash_return_cpu(void *info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int cpu = smp_processor_id(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!cpu_online(cpu)) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Disable IRQ */ | 
					
						
							|  |  |  | 	hard_irq_disable(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Return the CPU to OPAL */ | 
					
						
							|  |  |  | 	opal_return_cpu(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* This gets called just before system reboots */ | 
					
						
							|  |  |  | void opal_flash_term_callback(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct cpumask mask; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (update_flash_data.status != FLASH_IMG_READY) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pr_alert("FLASH: Flashing new firmware\n"); | 
					
						
							|  |  |  | 	pr_alert("FLASH: Image is %u bytes\n", image_data.size); | 
					
						
							|  |  |  | 	pr_alert("FLASH: Performing flash and reboot/shutdown\n"); | 
					
						
							|  |  |  | 	pr_alert("FLASH: This will take several minutes. Do not power off!\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Small delay to help getting the above message out */ | 
					
						
							|  |  |  | 	msleep(500); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Return secondary CPUs to firmware */ | 
					
						
							|  |  |  | 	cpumask_copy(&mask, cpu_online_mask); | 
					
						
							|  |  |  | 	cpumask_clear_cpu(smp_processor_id(), &mask); | 
					
						
							|  |  |  | 	if (!cpumask_empty(&mask)) | 
					
						
							|  |  |  | 		smp_call_function_many(&mask, | 
					
						
							|  |  |  | 				       flash_return_cpu, NULL, false); | 
					
						
							|  |  |  | 	/* Hard disable interrupts */ | 
					
						
							|  |  |  | 	hard_irq_disable(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-24 16:04:58 +05:30
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Show candidate image status | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static ssize_t update_show(struct kobject *kobj, | 
					
						
							|  |  |  | 			   struct kobj_attribute *attr, char *buf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct update_flash_t *const args_buf = &update_flash_data; | 
					
						
							|  |  |  | 	return sprintf(buf, "%d\n", args_buf->status); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Set update image flag | 
					
						
							|  |  |  |  *  1 - Flash new image | 
					
						
							|  |  |  |  *  0 - Cancel flash request | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static ssize_t update_store(struct kobject *kobj, | 
					
						
							|  |  |  | 			    struct kobj_attribute *attr, | 
					
						
							|  |  |  | 			    const char *buf, size_t count) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct update_flash_t *const args_buf = &update_flash_data; | 
					
						
							|  |  |  | 	int rc = count; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&image_data_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (buf[0]) { | 
					
						
							|  |  |  | 	case '0': | 
					
						
							|  |  |  | 		if (args_buf->status == FLASH_IMG_READY) | 
					
						
							|  |  |  | 			opal_flash_update(FLASH_UPDATE_CANCEL); | 
					
						
							|  |  |  | 		args_buf->status = FLASH_NO_OP; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case '1': | 
					
						
							|  |  |  | 		/* Image is loaded? */ | 
					
						
							|  |  |  | 		if (image_data.status == IMAGE_READY) | 
					
						
							|  |  |  | 			args_buf->status = | 
					
						
							|  |  |  | 				opal_flash_update(FLASH_UPDATE_INIT); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			args_buf->status = FLASH_INVALID_IMG; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		rc = -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_unlock(&image_data_mutex); | 
					
						
							|  |  |  | 	return rc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Free image buffer | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void free_image_buf(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	void *addr; | 
					
						
							|  |  |  | 	int size; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	addr = image_data.data; | 
					
						
							|  |  |  | 	size = PAGE_ALIGN(image_data.size); | 
					
						
							|  |  |  | 	while (size > 0) { | 
					
						
							|  |  |  | 		ClearPageReserved(vmalloc_to_page(addr)); | 
					
						
							|  |  |  | 		addr += PAGE_SIZE; | 
					
						
							|  |  |  | 		size -= PAGE_SIZE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vfree(image_data.data); | 
					
						
							|  |  |  | 	image_data.data = NULL; | 
					
						
							|  |  |  | 	image_data.status = IMAGE_INVALID; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Allocate image buffer. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int alloc_image_buf(char *buffer, size_t count) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	void *addr; | 
					
						
							|  |  |  | 	int size; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (count < sizeof(struct image_header_t)) { | 
					
						
							|  |  |  | 		pr_warn("FLASH: Invalid candidate image\n"); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	memcpy(&image_header, (void *)buffer, sizeof(struct image_header_t)); | 
					
						
							|  |  |  | 	image_data.size = be32_to_cpu(image_header.size); | 
					
						
							| 
									
										
										
										
											2014-01-07 21:58:06 +09:00
										 |  |  | 	pr_debug("FLASH: Candidate image size = %u\n", image_data.size); | 
					
						
							| 
									
										
										
										
											2013-10-24 16:04:58 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (image_data.size > MAX_IMAGE_SIZE) { | 
					
						
							|  |  |  | 		pr_warn("FLASH: Too large image\n"); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (image_data.size < VALIDATE_BUF_SIZE) { | 
					
						
							|  |  |  | 		pr_warn("FLASH: Image is shorter than expected\n"); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	image_data.data = vzalloc(PAGE_ALIGN(image_data.size)); | 
					
						
							|  |  |  | 	if (!image_data.data) { | 
					
						
							|  |  |  | 		pr_err("%s : Failed to allocate memory\n", __func__); | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Pin memory */ | 
					
						
							|  |  |  | 	addr = image_data.data; | 
					
						
							|  |  |  | 	size = PAGE_ALIGN(image_data.size); | 
					
						
							|  |  |  | 	while (size > 0) { | 
					
						
							|  |  |  | 		SetPageReserved(vmalloc_to_page(addr)); | 
					
						
							|  |  |  | 		addr += PAGE_SIZE; | 
					
						
							|  |  |  | 		size -= PAGE_SIZE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	image_data.status = IMAGE_LOADING; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Copy candidate image | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Parse candidate image header to get total image size | 
					
						
							|  |  |  |  * and pre-allocate required memory. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static ssize_t image_data_write(struct file *filp, struct kobject *kobj, | 
					
						
							|  |  |  | 				struct bin_attribute *bin_attr, | 
					
						
							|  |  |  | 				char *buffer, loff_t pos, size_t count) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int rc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&image_data_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* New image ? */ | 
					
						
							|  |  |  | 	if (pos == 0) { | 
					
						
							|  |  |  | 		/* Free memory, if already allocated */ | 
					
						
							|  |  |  | 		if (image_data.data) | 
					
						
							|  |  |  | 			free_image_buf(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Cancel outstanding image update request */ | 
					
						
							|  |  |  | 		if (update_flash_data.status == FLASH_IMG_READY) | 
					
						
							|  |  |  | 			opal_flash_update(FLASH_UPDATE_CANCEL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Allocate memory */ | 
					
						
							|  |  |  | 		rc = alloc_image_buf(buffer, count); | 
					
						
							|  |  |  | 		if (rc) | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (image_data.status != IMAGE_LOADING) { | 
					
						
							|  |  |  | 		rc = -ENOMEM; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((pos + count) > image_data.size) { | 
					
						
							|  |  |  | 		rc = -EINVAL; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	memcpy(image_data.data + pos, (void *)buffer, count); | 
					
						
							|  |  |  | 	rc = count; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set image status */ | 
					
						
							|  |  |  | 	if ((pos + count) == image_data.size) { | 
					
						
							|  |  |  | 		pr_debug("FLASH: Candidate image loaded....\n"); | 
					
						
							|  |  |  | 		image_data.status = IMAGE_READY; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	mutex_unlock(&image_data_mutex); | 
					
						
							|  |  |  | 	return rc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * sysfs interface : | 
					
						
							|  |  |  |  *  OPAL uses below sysfs files for code update. | 
					
						
							|  |  |  |  *  We create these files under /sys/firmware/opal. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   image		: Interface to load candidate firmware image | 
					
						
							|  |  |  |  *   validate_flash	: Validate firmware image | 
					
						
							|  |  |  |  *   manage_flash	: Commit/Reject firmware image | 
					
						
							|  |  |  |  *   update_flash	: Flash new firmware image | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static struct bin_attribute image_data_attr = { | 
					
						
							|  |  |  | 	.attr = {.name = "image", .mode = 0200}, | 
					
						
							|  |  |  | 	.size = MAX_IMAGE_SIZE,	/* Limit image size */ | 
					
						
							|  |  |  | 	.write = image_data_write, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct kobj_attribute validate_attribute = | 
					
						
							|  |  |  | 	__ATTR(validate_flash, 0600, validate_show, validate_store); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct kobj_attribute manage_attribute = | 
					
						
							|  |  |  | 	__ATTR(manage_flash, 0600, manage_show, manage_store); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct kobj_attribute update_attribute = | 
					
						
							|  |  |  | 	__ATTR(update_flash, 0600, update_show, update_store); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct attribute *image_op_attrs[] = { | 
					
						
							|  |  |  | 	&validate_attribute.attr, | 
					
						
							|  |  |  | 	&manage_attribute.attr, | 
					
						
							|  |  |  | 	&update_attribute.attr, | 
					
						
							|  |  |  | 	NULL	/* need to NULL terminate the list of attributes */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct attribute_group image_op_attr_group = { | 
					
						
							|  |  |  | 	.attrs = image_op_attrs, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void __init opal_flash_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Allocate validate image buffer */ | 
					
						
							|  |  |  | 	validate_flash_data.buf = kzalloc(VALIDATE_BUF_SIZE, GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!validate_flash_data.buf) { | 
					
						
							|  |  |  | 		pr_err("%s : Failed to allocate memory\n", __func__); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Make sure /sys/firmware/opal directory is created */ | 
					
						
							|  |  |  | 	if (!opal_kobj) { | 
					
						
							|  |  |  | 		pr_warn("FLASH: opal kobject is not available\n"); | 
					
						
							|  |  |  | 		goto nokobj; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Create the sysfs files */ | 
					
						
							|  |  |  | 	ret = sysfs_create_group(opal_kobj, &image_op_attr_group); | 
					
						
							|  |  |  | 	if (ret) { | 
					
						
							|  |  |  | 		pr_warn("FLASH: Failed to create sysfs files\n"); | 
					
						
							|  |  |  | 		goto nokobj; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = sysfs_create_bin_file(opal_kobj, &image_data_attr); | 
					
						
							|  |  |  | 	if (ret) { | 
					
						
							|  |  |  | 		pr_warn("FLASH: Failed to create sysfs files\n"); | 
					
						
							|  |  |  | 		goto nosysfs_file; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set default status */ | 
					
						
							|  |  |  | 	validate_flash_data.status = FLASH_NO_OP; | 
					
						
							|  |  |  | 	manage_flash_data.status = FLASH_NO_OP; | 
					
						
							|  |  |  | 	update_flash_data.status = FLASH_NO_OP; | 
					
						
							|  |  |  | 	image_data.status = IMAGE_INVALID; | 
					
						
							|  |  |  | 	return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | nosysfs_file: | 
					
						
							|  |  |  | 	sysfs_remove_group(opal_kobj, &image_op_attr_group); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | nokobj: | 
					
						
							|  |  |  | 	kfree(validate_flash_data.buf); | 
					
						
							|  |  |  | 	return; | 
					
						
							|  |  |  | } |