Home | History | Annotate | Download | only in updater
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 /* Read/write/erase Embedded Controller integrated flash */
     17 
     18 #define LOG_TAG "fwtool"
     19 
     20 #include <stdint.h>
     21 #include <stdlib.h>
     22 #include <string.h>
     23 #include <unistd.h>
     24 
     25 #include <errno.h>
     26 #include <fcntl.h>
     27 #include <sys/ioctl.h>
     28 #include <sys/param.h>
     29 #include <sys/stat.h>
     30 #include <sys/types.h>
     31 
     32 #include "ec_commands.h"
     33 #include "flash_device.h"
     34 #include "update_log.h"
     35 
     36 #define CROS_EC_DEV_NAME "/dev/cros_ec"
     37 
     38 struct cros_ec_command {
     39 	uint32_t version;
     40 	uint32_t command;
     41 	uint8_t *outdata;
     42 	uint32_t outsize;
     43 	uint8_t *indata;
     44 	uint32_t insize;
     45 	uint32_t result;
     46 };
     47 
     48 #define CROS_EC_DEV_IOCXCMD    _IOWR(':', 0, struct cros_ec_command)
     49 #define CROS_EC_DEV_IOCRDMEM   _IOWR(':', 1, struct cros_ec_readmem)
     50 
     51 struct ec_data {
     52 	int fd;
     53 	struct ec_response_get_protocol_info proto;
     54 	struct ec_response_flash_info_1 info;
     55 	struct ec_response_flash_region_info ro_region;
     56 };
     57 
     58 static int ec_command(void *hnd, int command, int version,
     59 		const void *outdata, int outsize, void *indata, int insize)
     60 {
     61 	struct ec_data *ec = hnd;
     62 	struct cros_ec_command s_cmd;
     63 	int r;
     64 
     65 	if (ec->fd < 0)
     66 		return -ENODEV;
     67 
     68 	s_cmd.command = command;
     69 	s_cmd.version = version;
     70 	s_cmd.result = 0xff;
     71 	s_cmd.outsize = outsize;
     72 	s_cmd.outdata = (uint8_t *)outdata;
     73 	s_cmd.insize = insize;
     74 	s_cmd.indata = indata;
     75 
     76 	r = ioctl(ec->fd, CROS_EC_DEV_IOCXCMD, &s_cmd);
     77 	if (r < 0) {
     78 		ALOGD("Cmd 0x%x failed %d\n", command, errno);
     79 		return -errno;
     80 	} else if (s_cmd.result != EC_RES_SUCCESS) {
     81 		ALOGD("Cmd 0x%x error %d\n", command, s_cmd.result);
     82 		return s_cmd.result;
     83 	}
     84 
     85 	return 0;
     86 }
     87 
     88 static void *ec_open(const void *params)
     89 {
     90 	int res;
     91 	struct ec_params_flash_region_info region;
     92 	const char *path = params ? params : CROS_EC_DEV_NAME;
     93 	struct ec_data *dev = calloc(1, sizeof(struct ec_data));
     94 	if (!dev)
     95 		return NULL;
     96 
     97 	dev->fd = open(path, O_RDWR);
     98 	if (dev->fd == -1) {
     99 		ALOGE("Cannot open EC device %s : %d\n", path, errno);
    100 		goto out_free;
    101 	}
    102 
    103 	res = ec_command(dev, EC_CMD_GET_PROTOCOL_INFO, 0, NULL, 0,
    104 			 &dev->proto, sizeof(dev->proto));
    105 	if (res) {
    106 		ALOGE("Cannot get EC protocol info for %s : %d\n", path, res);
    107 		goto out_close;
    108 	}
    109 
    110 	res = ec_command(dev, EC_CMD_FLASH_INFO, 1, NULL, 0,
    111 			 &dev->info, sizeof(dev->info));
    112 	if (res) {
    113 		ALOGE("Cannot get EC flash info for %s : %d\n", path, res);
    114 		goto out_close;
    115 	}
    116 
    117 	region.region = EC_FLASH_REGION_RO;
    118 	res = ec_command(dev, EC_CMD_FLASH_REGION_INFO, 1,
    119 			 &region, sizeof(region),
    120 			 &dev->ro_region, sizeof(dev->ro_region));
    121 	if (res) {
    122 		ALOGE("Cannot get EC RO info for %s : %d\n", path, res);
    123 		goto out_close;
    124 	}
    125 
    126 	ALOGD("EC %s: size %d erase_block_size %d write_ideal_size %d\n",
    127 		path, dev->info.flash_size, dev->info.erase_block_size,
    128 		dev->info.write_ideal_size);
    129 
    130 	return dev;
    131 
    132 out_close:
    133 	close(dev->fd);
    134 	dev->fd = -1;
    135 out_free:
    136 	free(dev);
    137 
    138 	return NULL;
    139 }
    140 
    141 static void ec_close(void *hnd)
    142 {
    143 	struct ec_data *dev = hnd;
    144 
    145 	close(dev->fd);
    146 	free(dev);
    147 }
    148 
    149 static int ec_read(void *hnd, off_t offset, void *buffer, size_t count)
    150 {
    151 	struct ec_data *dev = hnd;
    152 	ssize_t res;
    153 	struct ec_params_flash_read p;
    154 	uint8_t *ptr = buffer;
    155 	uint32_t read_size = dev->proto.max_response_packet_size
    156 				- sizeof(struct ec_host_response);
    157 
    158 	while (count) {
    159 		p.offset = offset;
    160 		p.size = MIN(read_size, count);
    161 		res = ec_command(dev, EC_CMD_FLASH_READ, 0, &p, sizeof(p),
    162 			 ptr, read_size);
    163 		if (res) {
    164 			ALOGW("Cannot read at %ld : %zd\n", offset, res);
    165 			return res;
    166 		}
    167 		count -= p.size;
    168 		ptr += p.size;
    169 		offset += p.size;
    170 	}
    171 	return 0;
    172 }
    173 
    174 static int ec_write(void *hnd, off_t offset, void *buffer, size_t count)
    175 {
    176 	struct ec_data *dev = hnd;
    177 	ssize_t res;
    178 	struct ec_params_flash_write *p;
    179 	uint8_t *packet_data;
    180 	uint8_t *ptr = buffer;
    181 	uint32_t write_size = dev->info.write_ideal_size;
    182 	uint32_t total_size = sizeof(*p) +  write_size;
    183 
    184 	p = malloc(total_size);
    185 	if (!p)
    186 		return -ENOMEM;
    187 	packet_data = (uint8_t *)p + sizeof(*p);
    188 
    189 	while (count) {
    190 		p->offset = offset;
    191 		p->size = write_size;
    192 		memcpy(packet_data, ptr, write_size);
    193 		res = ec_command(dev, EC_CMD_FLASH_WRITE, 1, p, total_size,
    194 			 NULL, 0);
    195 		if (res) {
    196 			ALOGW("Cannot write at %ld : %zd\n", offset, res);
    197 			return res;
    198 		}
    199 		count -= write_size;
    200 		ptr += write_size;
    201 		offset += write_size;
    202 	}
    203 	return 0;
    204 }
    205 
    206 static int ec_erase(void *hnd, off_t offset, size_t count)
    207 {
    208 	struct ec_data *dev = hnd;
    209 	int res;
    210 	struct ec_params_flash_erase erase;
    211 
    212 	erase.offset = offset;
    213 	erase.size = count;
    214 	res = ec_command(dev, EC_CMD_FLASH_ERASE, 0, &erase, sizeof(erase),
    215 			 NULL, 0);
    216 	if (res) {
    217 		ALOGW("Cannot erase at %ld : %d\n", offset, res);
    218 		return res;
    219 	}
    220 
    221 	return 0;
    222 }
    223 
    224 static size_t ec_get_size(void *hnd)
    225 {
    226 	struct ec_data *dev = hnd;
    227 
    228 	return dev && dev->fd > 0 ? dev->info.flash_size : 0;
    229 }
    230 
    231 static size_t ec_get_write_size(void *hnd)
    232 {
    233 	struct ec_data *dev = hnd;
    234 
    235 	return dev && dev->fd > 0 ? dev->info.write_ideal_size : 0;
    236 }
    237 
    238 static size_t ec_get_erase_size(void *hnd)
    239 {
    240 	struct ec_data *dev = hnd;
    241 
    242 	return dev && dev->fd > 0 ? dev->info.erase_block_size : 0;
    243 }
    244 
    245 static off_t ec_get_fmap_offset(void *hnd)
    246 {
    247 	struct ec_data *dev = hnd;
    248 
    249 	if (!hnd)
    250 		return 0;
    251 
    252 	/*
    253 	 * Try to find the FMAP signature at 64-byte boundaries
    254          * from the end of the RO region.
    255 	 */
    256 	return dev->ro_region.offset + dev->ro_region.size;
    257 }
    258 
    259 const struct flash_device_ops flash_ec_ops = {
    260 	.name = "ec",
    261 	.open = ec_open,
    262 	.close = ec_close,
    263 	.read = ec_read,
    264 	.write = ec_write,
    265 	.erase = ec_erase,
    266 	.get_size = ec_get_size,
    267 	.get_write_size = ec_get_write_size,
    268 	.get_erase_size = ec_get_erase_size,
    269 	.get_fmap_offset = ec_get_fmap_offset,
    270 	.cmd = ec_command,
    271 };
    272