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
ec_command(void * hnd,int command,int version,const void * outdata,int outsize,void * indata,int insize)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
ec_open(const void * params)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 ®ion, 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
ec_close(void * hnd)141 static void ec_close(void *hnd)
142 {
143 struct ec_data *dev = hnd;
144
145 close(dev->fd);
146 free(dev);
147 }
148
ec_read(void * hnd,off_t offset,void * buffer,size_t count)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
ec_write(void * hnd,off_t offset,void * buffer,size_t count)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
ec_erase(void * hnd,off_t offset,size_t count)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
ec_get_size(void * hnd)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
ec_get_write_size(void * hnd)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
ec_get_erase_size(void * hnd)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
ec_get_fmap_offset(void * hnd)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