blob: 9c922b85811d65014794699a1de164eee3902707 [file] [log] [blame]
David Hendricksee712472012-05-23 21:50:59 -07001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2012 The Chromium OS Authors. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * Neither the name of Google or the names of contributors or
18 * licensors may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 * This software is provided "AS IS," without a warranty of any kind.
22 * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
23 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
24 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
25 * GOOGLE INC AND ITS LICENSORS SHALL NOT BE LIABLE
26 * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
27 * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
28 * GOOGLE OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
29 * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
30 * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
31 * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
32 * EVEN IF GOOGLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
33 */
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080034#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <unistd.h>
38#include "flashchips.h"
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080039#include "fmap.h"
David Hendricksa9151312013-07-01 12:21:01 -070040#include "gec.h"
Louis Yung-Chieh Loc0505242012-08-09 23:11:32 +080041#include "gec_lock.h"
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +080042#include "gec_ec_commands.h"
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080043#include "programmer.h"
44#include "spi.h"
45#include "writeprotect.h"
46
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +080047/* FIXME: used for wp hacks */
48#include <sys/types.h>
49#include <sys/stat.h>
50#include <fcntl.h>
51#include <unistd.h>
52struct wp_data {
53 int enable;
54 unsigned int start;
55 unsigned int len;
56};
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +080057#define WP_STATE_HACK_FILENAME "/mnt/stateful_partition/flashrom_wp_state"
58
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +080059/* If software sync is enabled, then we don't try the latest firmware copy
60 * after updating.
61 */
62#define SOFTWARE_SYNC_ENABLED
63
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080064/* 1 if we want the flashrom to call erase_and_write_flash() again. */
65static int need_2nd_pass = 0;
66
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +080067/* 1 if we want the flashrom to try jumping to new firmware after update. */
68static int try_latest_firmware = 0;
69
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080070/* The range of each firmware copy from the image file to update.
71 * But re-define the .flags as the valid flag to indicate the firmware is
72 * new or not (if flags = 1).
73 */
74static struct fmap_area fwcopy[4]; // [0] is not used.
75
76/* The names of enum lpc_current_image to match in FMAP area names. */
David Hendricksbf8c4dd2012-07-19 12:13:17 -070077static const char *sections[3] = {
78 "UNKNOWN SECTION", // EC_IMAGE_UNKNOWN -- never matches
79 "EC_RO",
80 "EC_RW",
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080081};
82
Simon Glassc453a642013-07-01 18:08:53 +090083/* EC_FLASH_REGION_WP_RO is the highest numbered region so it also indicates
84 * the number of regions */
85static struct ec_response_flash_region_info regions[EC_FLASH_REGION_WP_RO + 1];
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080086
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080087/* Given the range not able to update, mark the corresponding
88 * firmware as old.
89 */
90static void gec_invalidate_copy(unsigned int addr, unsigned int len)
91{
92 int i;
93
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +080094 for (i = EC_IMAGE_RO; i < ARRAY_SIZE(fwcopy); i++) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080095 struct fmap_area *fw = &fwcopy[i];
96 if ((addr >= fw->offset && (addr < fw->offset + fw->size)) ||
97 (fw->offset >= addr && (fw->offset < addr + len))) {
98 msg_pdbg("Mark firmware [%s] as old.\n",
99 sections[i]);
100 fw->flags = 0; // mark as old
101 }
102 }
103}
104
105
Simon Glass01c11672013-07-01 18:03:33 +0900106static int gec_get_current_image(struct gec_priv *priv)
107{
108 struct ec_response_get_version resp;
109 int rc;
110
111 rc = priv->ec_command(EC_CMD_GET_VERSION, 0, NULL, 0, &resp,
112 sizeof(resp));
113 if (rc < 0) {
114 msg_perr("GEC cannot get the running copy: rc=%d\n", rc);
115 return rc;
116 }
117 if (resp.current_image == EC_IMAGE_UNKNOWN) {
118 msg_perr("GEC gets unknown running copy\n");
119 return -1;
120 }
121
122 return resp.current_image;
123}
124
125
Simon Glass3c01dca2013-07-01 18:07:34 +0900126static int gec_get_region_info(struct gec_priv *priv,
127 enum ec_flash_region region,
128 struct ec_response_flash_region_info *info)
129{
130 struct ec_params_flash_region_info req;
131 struct ec_response_flash_region_info resp;
132 int rc;
133
134 req.region = region;
135 rc = priv->ec_command(EC_CMD_FLASH_REGION_INFO,
136 EC_VER_FLASH_REGION_INFO, &req, sizeof(req),
137 &resp, sizeof(resp));
138 if (rc < 0) {
139 msg_perr("Cannot get the WP_RO region info: %d\n", rc);
140 return rc;
141 }
142
143 info->offset = resp.offset;
144 info->size = resp.size;
145 return 0;
146}
147
David Hendricksf9461c72013-07-11 19:02:13 -0700148/**
149 * Get the versions of the command supported by the EC.
150 *
151 * @param cmd Command
152 * @param pmask Destination for version mask; will be set to 0 on
153 * error.
154 * @return 0 if success, <0 if error
155 */
156static int ec_get_cmd_versions(int cmd, uint32_t *pmask)
157{
158 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
159 struct ec_params_get_cmd_versions pver;
160 struct ec_response_get_cmd_versions rver;
161 int rc;
162
163 *pmask = 0;
164
165 pver.cmd = cmd;
166 rc = priv->ec_command(EC_CMD_GET_CMD_VERSIONS, 0,
167 &pver, sizeof(pver), &rver, sizeof(rver));
168
169 if (rc < 0)
170 return rc;
171
172 *pmask = rver.version_mask;
173 return rc;
174}
175
176/**
177 * Return non-zero if the EC supports the command and version
178 *
179 * @param cmd Command to check
180 * @param ver Version to check
181 * @return non-zero if command version supported; 0 if not.
182 */
183static int ec_cmd_version_supported(int cmd, int ver)
184{
185 uint32_t mask = 0;
186 int rc;
187
188 rc = ec_get_cmd_versions(cmd, &mask);
189 if (rc < 0)
190 return rc;
191
192 return (mask & EC_VER_MASK(ver)) ? 1 : 0;
193}
194
195static int gec_set_max_write_size(void)
196{
197 int rc;
198 struct ec_response_flash_info info;
199 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
200 unsigned int pdata_max_size;
201
202 /*
203 * Determine whether we can use version 1 of the command with more
204 * data, or only version 0.
205 */
206 rc = ec_cmd_version_supported(EC_CMD_FLASH_WRITE, EC_VER_FLASH_WRITE);
207 if (rc < 0)
208 return rc;
209 else if (rc == 0)
210 pdata_max_size = EC_FLASH_WRITE_VER0_SIZE;
211 else
212 pdata_max_size = EC_PROTO2_MAX_PARAM_SIZE - 8;
213
214 rc = priv->ec_command(EC_CMD_FLASH_INFO, 0,
215 NULL, 0, &info, sizeof(info));
216 if (rc < 0) {
217 msg_perr("%s(): FLASH_INFO returns %d.\n", __func__, rc);
218 return rc;
219 }
220
221 opaque_programmer->max_data_write =
222 (pdata_max_size/info.write_block_size) * info.write_block_size;
223 return rc;
224}
Simon Glass3c01dca2013-07-01 18:07:34 +0900225
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800226/* Asks EC to jump to a firmware copy. If target is EC_IMAGE_UNKNOWN,
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800227 * then this functions picks a NEW firmware copy and jumps to it. Note that
228 * RO is preferred, then A, finally B.
229 *
230 * Returns 0 for success.
231 */
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800232static int gec_jump_copy(enum ec_current_image target) {
233 struct ec_response_get_version c;
234 struct ec_params_reboot_ec p;
David Hendricks7cfbd022012-05-20 17:25:51 -0700235 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800236 int rc;
237
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800238 /* Since the EC may return EC_RES_SUCCESS twice if the EC doesn't
239 * jump to different firmware copy. The second EC_RES_SUCCESS would
240 * set the OBF=1 and the next command cannot be executed.
241 * Thus, we call EC to jump only if the target is different.
242 */
Simon Glass01c11672013-07-01 18:03:33 +0900243 rc = gec_get_current_image(priv);
244 if (rc < 0)
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800245 return 1;
Simon Glassc453a642013-07-01 18:08:53 +0900246 if (rc == target)
247 return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800248
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800249 memset(&p, 0, sizeof(p));
Simon Glassc453a642013-07-01 18:08:53 +0900250
251 /* Translate target --> EC reboot command parameter */
252 switch (target) {
253 case EC_IMAGE_RO:
254 p.cmd = EC_REBOOT_JUMP_RO;
255 break;
256 case EC_IMAGE_RW:
257 p.cmd = EC_REBOOT_JUMP_RW;
258 break;
259 default:
260 /*
261 * If target is unspecified, set EC reboot command to use
262 * a new image. Also set "target" so that it may be used
263 * to update the priv->current_image if jump is successful.
264 */
265 if (fwcopy[EC_IMAGE_RO].flags) {
266 p.cmd = EC_REBOOT_JUMP_RO;
267 target = EC_IMAGE_RO;
268 } else if (fwcopy[EC_IMAGE_RW].flags) {
269 p.cmd = EC_REBOOT_JUMP_RW;
270 target = EC_IMAGE_RW;
271 } else {
272 p.cmd = EC_IMAGE_UNKNOWN;
273 }
274 break;
275 }
276
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800277 msg_pdbg("GEC is jumping to [%s]\n", sections[p.cmd]);
278 if (p.cmd == EC_IMAGE_UNKNOWN) return 1;
279
280 if (c.current_image == p.cmd) {
281 msg_pdbg("GEC is already in [%s]\n", sections[p.cmd]);
Simon Glassc453a642013-07-01 18:08:53 +0900282 priv->current_image = target;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800283 return 0;
284 }
285
286 rc = priv->ec_command(EC_CMD_REBOOT_EC, 0,
David Hendricks7cfbd022012-05-20 17:25:51 -0700287 &p, sizeof(p), NULL, 0);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800288 if (rc < 0) {
289 msg_perr("GEC cannot jump to [%s]:%d\n",
290 sections[p.cmd], rc);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800291 } else {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800292 msg_pdbg("GEC has jumped to [%s]\n", sections[p.cmd]);
293 rc = EC_RES_SUCCESS;
Simon Glass01c11672013-07-01 18:03:33 +0900294 priv->current_image = target;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800295 }
296
297 /* Sleep 1 sec to wait the EC re-init. */
298 usleep(1000000);
299
David Hendricksf9461c72013-07-11 19:02:13 -0700300 /* update max data write size in case we're jumping to an EC
301 * firmware with different protocol */
302 gec_set_max_write_size();
303
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800304 return rc;
305}
306
307
308/* Given an image, this function parses FMAP and recognize the firmware
309 * ranges.
310 */
311int gec_prepare(uint8_t *image, int size) {
David Hendricks7cfbd022012-05-20 17:25:51 -0700312 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800313 struct fmap *fmap;
314 int i, j;
315
Louis Yung-Chieh Lo0eaa0ca2012-05-29 15:28:58 +0800316 if (!(priv && priv->detected)) return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800317
318 // Parse the fmap in the image file and cache the firmware ranges.
319 fmap = fmap_find_in_memory(image, size);
320 if (!fmap) return 0;
321
322 // Lookup RO/A/B sections in FMAP.
323 for (i = 0; i < fmap->nareas; i++) {
324 struct fmap_area *fa = &fmap->areas[i];
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800325 for (j = EC_IMAGE_RO; j < ARRAY_SIZE(sections); j++) {
David Hendricks5b06c882012-05-20 18:27:25 -0700326 if (!strcmp(sections[j], (const char *)fa->name)) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800327 msg_pdbg("Found '%s' in image.\n", fa->name);
328 memcpy(&fwcopy[j], fa, sizeof(*fa));
329 fwcopy[j].flags = 1; // mark as new
330 }
331 }
332 }
333
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800334 /* Warning: before update, we jump the EC to RO copy. If you want to
335 * change this behavior, please also check the gec_finish().
336 */
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800337 return gec_jump_copy(EC_IMAGE_RO);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800338}
339
340
341/* Returns >0 if we need 2nd pass of erase_and_write_flash().
342 * <0 if we cannot jump to any firmware copy.
343 * ==0 if no more pass is needed.
344 *
345 * This function also jumps to new-updated firmware copy before return >0.
346 */
347int gec_need_2nd_pass(void) {
David Hendricks7cfbd022012-05-20 17:25:51 -0700348 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
349
Louis Yung-Chieh Lo0eaa0ca2012-05-29 15:28:58 +0800350 if (!(priv && priv->detected)) return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800351
352 if (need_2nd_pass) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800353 if (gec_jump_copy(EC_IMAGE_UNKNOWN)) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800354 return -1;
355 }
356 }
357
358 return need_2nd_pass;
359}
360
361
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800362/* Returns 0 for success.
363 *
364 * Try latest firmware: B > A > RO
365 *
366 * This function assumes the EC jumps to RO at gec_prepare() so that
367 * the fwcopy[RO].flags is old (0) and A/B are new. Please also refine
368 * this code logic if you change the gec_prepare() behavior.
369 */
370int gec_finish(void) {
371 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
372
373 if (!(priv && priv->detected)) return 0;
374
375 if (try_latest_firmware) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800376 if (fwcopy[EC_IMAGE_RW].flags &&
377 gec_jump_copy(EC_IMAGE_RW) == 0) return 0;
378 return gec_jump_copy(EC_IMAGE_RO);
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800379 }
380
381 return 0;
382}
383
384
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800385int gec_read(struct flashchip *flash, uint8_t *readarr,
386 unsigned int blockaddr, unsigned int readcnt) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800387 int rc = 0;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800388 struct ec_params_flash_read p;
David Hendricks7cfbd022012-05-20 17:25:51 -0700389 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
David Hendricksd6a0f662012-05-29 14:39:50 -0700390 int maxlen = opaque_programmer->max_data_read;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800391 uint8_t buf[maxlen];
David Hendricks133083b2012-07-17 20:39:38 -0700392 int offset = 0, count;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800393
David Hendricks133083b2012-07-17 20:39:38 -0700394 while (offset < readcnt) {
395 count = min(maxlen, readcnt - offset);
396 p.offset = blockaddr + offset;
397 p.size = count;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800398 rc = priv->ec_command(EC_CMD_FLASH_READ, 0,
399 &p, sizeof(p), buf, count);
400 if (rc < 0) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800401 msg_perr("GEC: Flash read error at offset 0x%x\n",
David Hendricks133083b2012-07-17 20:39:38 -0700402 blockaddr + offset);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800403 return rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800404 } else {
405 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800406 }
407
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800408 memcpy(readarr + offset, buf, count);
David Hendricks133083b2012-07-17 20:39:38 -0700409 offset += count;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800410 }
411
412 return rc;
413}
414
415
Simon Glassc453a642013-07-01 18:08:53 +0900416/*
417 * returns 0 to indicate area does not overlap current EC image
418 * returns 1 to indicate area overlaps current EC image or error
419 */
420static int in_current_image(struct gec_priv *priv,
421 unsigned int addr, unsigned int len)
422{
423 int ret;
424 enum ec_current_image image;
425 uint32_t region_offset;
426 uint32_t region_size;
427
428 image = priv->current_image;
429 region_offset = priv->region[image].offset;
430 region_size = priv->region[image].size;
431
432 if ((addr + len - 1 < region_offset) ||
433 (addr > region_offset + region_size - 1)) {
434 return 0;
435 }
436 return 1;
437}
438
439
David Hendricks7cfbd022012-05-20 17:25:51 -0700440int gec_block_erase(struct flashchip *flash,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800441 unsigned int blockaddr,
442 unsigned int len) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800443 struct ec_params_flash_erase erase;
David Hendricks7cfbd022012-05-20 17:25:51 -0700444 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800445 int rc;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800446
Simon Glassc453a642013-07-01 18:08:53 +0900447 if (in_current_image(priv, blockaddr, len)) {
448 gec_invalidate_copy(blockaddr, len);
449 need_2nd_pass = 1;
450 return ACCESS_DENIED;
451 }
452
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800453 erase.offset = blockaddr;
454 erase.size = len;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800455 rc = priv->ec_command(EC_CMD_FLASH_ERASE, 0,
David Hendricks7cfbd022012-05-20 17:25:51 -0700456 &erase, sizeof(erase), NULL, 0);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800457 if (rc == -EC_RES_ACCESS_DENIED) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800458 // this is active image.
459 gec_invalidate_copy(blockaddr, len);
460 need_2nd_pass = 1;
461 return ACCESS_DENIED;
462 }
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800463 if (rc < 0) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800464 msg_perr("GEC: Flash erase error at address 0x%x, rc=%d\n",
465 blockaddr, rc);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800466 return rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800467 } else {
468 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800469 }
470
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800471#ifndef SOFTWARE_SYNC_ENABLED
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800472 try_latest_firmware = 1;
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800473#endif
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800474 return rc;
475}
476
477
478int gec_write(struct flashchip *flash, uint8_t *buf, unsigned int addr,
479 unsigned int nbytes) {
480 int i, rc = 0;
481 unsigned int written = 0;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800482 struct ec_params_flash_write p;
David Hendricks7cfbd022012-05-20 17:25:51 -0700483 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
David Hendricksd6a0f662012-05-29 14:39:50 -0700484 int maxlen = opaque_programmer->max_data_write;
David Hendricks2d6db772013-07-10 21:07:48 -0700485 uint8_t *packet;
486
487 packet = malloc(sizeof(p) + maxlen);
488 if (!packet)
489 return -1;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800490
491 for (i = 0; i < nbytes; i += written) {
David Hendricksd6a0f662012-05-29 14:39:50 -0700492 written = min(nbytes - i, maxlen);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800493 p.offset = addr + i;
494 p.size = written;
Simon Glassc453a642013-07-01 18:08:53 +0900495
496 if (in_current_image(priv, p.offset, p.size)) {
497 gec_invalidate_copy(addr, nbytes);
498 need_2nd_pass = 1;
499 return ACCESS_DENIED;
500 }
501
David Hendricks2d6db772013-07-10 21:07:48 -0700502 memcpy(packet, &p, sizeof(p));
503 memcpy(packet + sizeof(p), &buf[i], written);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800504 rc = priv->ec_command(EC_CMD_FLASH_WRITE, 0,
David Hendricks2d6db772013-07-10 21:07:48 -0700505 packet, sizeof(p) + p.size, NULL, 0);
506
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800507 if (rc == -EC_RES_ACCESS_DENIED) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800508 // this is active image.
509 gec_invalidate_copy(addr, nbytes);
510 need_2nd_pass = 1;
511 return ACCESS_DENIED;
512 }
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800513
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800514 if (rc < 0) break;
515 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800516 }
517
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800518#ifndef SOFTWARE_SYNC_ENABLED
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800519 try_latest_firmware = 1;
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800520#endif
David Hendricks2d6db772013-07-10 21:07:48 -0700521 free(packet);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800522 return rc;
523}
524
525
526static int gec_list_ranges(const struct flashchip *flash) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800527 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Simon Glass3c01dca2013-07-01 18:07:34 +0900528 struct ec_response_flash_region_info info;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800529 int rc;
530
Simon Glass3c01dca2013-07-01 18:07:34 +0900531 rc = gec_get_region_info(priv, EC_FLASH_REGION_WP_RO, &info);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800532 if (rc < 0) {
533 msg_perr("Cannot get the WP_RO region info: %d\n", rc);
534 return 1;
535 }
536
537 msg_pinfo("Supported write protect range:\n");
538 msg_pinfo(" disable: start=0x%06x len=0x%06x\n", 0, 0);
Simon Glass3c01dca2013-07-01 18:07:34 +0900539 msg_pinfo(" enable: start=0x%06x len=0x%06x\n", info.offset,
540 info.size);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800541
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800542 return 0;
543}
544
545
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800546/*
547 * Helper function for flash protection.
548 *
549 * On EC API v1, the EC write protection has been simplified to one-bit:
550 * EC_FLASH_PROTECT_RO_AT_BOOT, which means the state is either enabled
551 * or disabled. However, this is different from the SPI-style write protect
552 * behavior. Thus, we re-define the flashrom command (SPI-style) so that
553 * either SRP or range is non-zero, the EC_FLASH_PROTECT_RO_AT_BOOT is set.
554 *
555 * SRP Range | PROTECT_RO_AT_BOOT
556 * 0 0 | 0
557 * 0 non-zero | 1
558 * 1 0 | 1
559 * 1 non-zero | 1
560 *
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800561 *
562 * Besides, to make the protection take effect as soon as possible, we
563 * try to set EC_FLASH_PROTECT_RO_NOW at the same time. However, not
564 * every EC supports RO_NOW, thus we then try to protect the entire chip.
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800565 */
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800566static int set_wp(int enable) {
567 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
568 struct ec_params_flash_protect p;
569 struct ec_response_flash_protect r;
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800570 const int ro_at_boot_flag = EC_FLASH_PROTECT_RO_AT_BOOT;
571 const int ro_now_flag = EC_FLASH_PROTECT_RO_NOW;
572 int need_an_ec_cold_reset = 0;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800573 int rc;
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800574
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800575 /* Try to set RO_AT_BOOT and RO_NOW first */
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800576 memset(&p, 0, sizeof(p));
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800577 p.mask = (ro_at_boot_flag | ro_now_flag);
578 p.flags = enable ? (ro_at_boot_flag | ro_now_flag) : 0;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800579 rc = priv->ec_command(EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT,
580 &p, sizeof(p), &r, sizeof(r));
581 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800582 msg_perr("FAILED: Cannot set the RO_AT_BOOT and RO_NOW: %d\n",
583 rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800584 return 1;
585 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800586
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800587 /* Read back */
588 memset(&p, 0, sizeof(p));
589 rc = priv->ec_command(EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT,
590 &p, sizeof(p), &r, sizeof(r));
591 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800592 msg_perr("FAILED: Cannot get RO_AT_BOOT and RO_NOW: %d\n",
593 rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800594 return 1;
595 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800596
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800597 if (!enable) {
598 /* The disable case is easier to check. */
599 if (r.flags & ro_at_boot_flag) {
600 msg_perr("FAILED: RO_AT_BOOT is not clear.\n");
601 return 1;
602 } else if (r.flags & ro_now_flag) {
603 msg_perr("FAILED: RO_NOW is asserted unexpectedly.\n");
604 need_an_ec_cold_reset = 1;
605 goto exit;
606 }
607
608 msg_pdbg("INFO: RO_AT_BOOT is clear.\n");
609 return 0;
610 }
611
612 /* Check if RO_AT_BOOT is set. If not, fail in anyway. */
613 if (r.flags & ro_at_boot_flag) {
614 msg_pdbg("INFO: RO_AT_BOOT has been set.\n");
615 } else {
616 msg_perr("FAILED: RO_AT_BOOT is not set.\n");
617 return 1;
618 }
619
620 /* Then, we check if the protection has been activated. */
621 if (r.flags & ro_now_flag) {
622 /* Good, RO_NOW is set. */
623 msg_pdbg("INFO: RO_NOW is set. WP is active now.\n");
624 } else if (r.writable_flags & EC_FLASH_PROTECT_ALL_NOW) {
625 struct ec_params_reboot_ec reboot;
626
627 msg_pdbg("WARN: RO_NOW is not set. Trying ALL_NOW.\n");
628
629 memset(&p, 0, sizeof(p));
630 p.mask = EC_FLASH_PROTECT_ALL_NOW;
631 p.flags = EC_FLASH_PROTECT_ALL_NOW;
632 rc = priv->ec_command(EC_CMD_FLASH_PROTECT,
633 EC_VER_FLASH_PROTECT,
634 &p, sizeof(p), &r, sizeof(r));
635 if (rc < 0) {
636 msg_perr("FAILED: Cannot set ALL_NOW: %d\n", rc);
637 return 1;
638 }
639
640 /* Read back */
641 memset(&p, 0, sizeof(p));
642 rc = priv->ec_command(EC_CMD_FLASH_PROTECT,
643 EC_VER_FLASH_PROTECT,
644 &p, sizeof(p), &r, sizeof(r));
645 if (rc < 0) {
646 msg_perr("FAILED:Cannot get ALL_NOW: %d\n", rc);
647 return 1;
648 }
649
650 if (!(r.flags & EC_FLASH_PROTECT_ALL_NOW)) {
651 msg_perr("FAILED: ALL_NOW is not set.\n");
652 need_an_ec_cold_reset = 1;
653 goto exit;
654 }
655
656 msg_pdbg("INFO: ALL_NOW has been set. WP is active now.\n");
657
658 /*
659 * Our goal is to protect the RO ASAP. The entire protection
660 * is just a workaround for platform not supporting RO_NOW.
661 * It has side-effect that the RW is also protected and leads
662 * the RW update failed. So, we arrange an EC code reset to
663 * unlock RW ASAP.
664 */
665 memset(&reboot, 0, sizeof(reboot));
666 reboot.cmd = EC_REBOOT_COLD;
667 reboot.flags = EC_REBOOT_FLAG_ON_AP_SHUTDOWN;
668 rc = priv->ec_command(EC_CMD_REBOOT_EC, 0,
669 &reboot, sizeof(reboot), NULL, 0);
670 if (rc < 0) {
671 msg_perr("WARN: Cannot arrange a cold reset at next "
672 "shutdown to unlock entire protect.\n");
673 msg_perr(" But you can do it manually.\n");
674 } else {
675 msg_pdbg("INFO: A cold reset is arranged at next "
676 "shutdown.\n");
677 }
678
679 } else {
680 msg_perr("FAILED: RO_NOW is not set.\n");
681 msg_perr("FAILED: The PROTECT_RO_AT_BOOT is set, but cannot "
682 "make write protection active now.\n");
683 need_an_ec_cold_reset = 1;
684 }
685
686exit:
687 if (need_an_ec_cold_reset) {
688 msg_perr("FAILED: You may need a reboot to take effect of "
689 "PROTECT_RO_AT_BOOT.\n");
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800690 return 1;
691 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800692
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800693 return 0;
694}
695
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800696static int gec_set_range(const struct flashchip *flash,
697 unsigned int start, unsigned int len) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800698 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Simon Glass3c01dca2013-07-01 18:07:34 +0900699 struct ec_response_flash_region_info info;
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800700 int rc;
701
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800702 /* Check if the given range is supported */
Simon Glass3c01dca2013-07-01 18:07:34 +0900703 rc = gec_get_region_info(priv, EC_FLASH_REGION_WP_RO, &info);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800704 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800705 msg_perr("FAILED: Cannot get the WP_RO region info: %d\n", rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800706 return 1;
707 }
708 if ((!start && !len) || /* list supported ranges */
Simon Glass3c01dca2013-07-01 18:07:34 +0900709 ((start == info.offset) && (len == info.size))) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800710 /* pass */
711 } else {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800712 msg_perr("FAILED: Unsupported write protection range "
713 "(0x%06x,0x%06x)\n\n", start, len);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800714 msg_perr("Currently supported range:\n");
715 msg_perr(" disable: (0x%06x,0x%06x)\n", 0, 0);
Simon Glass3c01dca2013-07-01 18:07:34 +0900716 msg_perr(" enable: (0x%06x,0x%06x)\n", info.offset,
717 info.size);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800718 return 1;
719 }
720
721 return set_wp(!!len);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800722}
723
724
David Hendricks1c09f802012-10-03 11:03:48 -0700725static int gec_enable_writeprotect(const struct flashchip *flash,
726 enum wp_mode wp_mode) {
727 int ret;
728
729 switch (wp_mode) {
730 case WP_MODE_HARDWARE:
731 ret = set_wp(1);
732 break;
733 default:
734 msg_perr("%s():%d Unsupported write-protection mode\n",
735 __func__, __LINE__);
736 ret = 1;
737 break;
738 }
739
740 return ret;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800741}
742
743
744static int gec_disable_writeprotect(const struct flashchip *flash) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800745 return set_wp(0);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800746}
747
748
749static int gec_wp_status(const struct flashchip *flash) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800750 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
751 struct ec_params_flash_protect p;
752 struct ec_response_flash_protect r;
753 int start, len; /* wp range */
754 int enabled;
755 int rc;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800756
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800757 memset(&p, 0, sizeof(p));
758 rc = priv->ec_command(EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT,
759 &p, sizeof(p), &r, sizeof(r));
760 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800761 msg_perr("FAILED: Cannot get the write protection status: %d\n",
762 rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800763 return 1;
764 } else if (rc < sizeof(r)) {
David Hendricksf797dde2012-10-30 11:39:12 -0700765 msg_perr("FAILED: Too little data returned (expected:%zd, "
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800766 "actual:%d)\n", sizeof(r), rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800767 return 1;
768 }
769
770 start = len = 0;
771 if (r.flags & EC_FLASH_PROTECT_RO_AT_BOOT) {
Simon Glass3c01dca2013-07-01 18:07:34 +0900772 struct ec_response_flash_region_info info;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800773
774 msg_pdbg("%s(): EC_FLASH_PROTECT_RO_AT_BOOT is set.\n",
775 __func__);
Simon Glass3c01dca2013-07-01 18:07:34 +0900776 rc = gec_get_region_info(priv, EC_FLASH_REGION_WP_RO, &info);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800777 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800778 msg_perr("FAILED: Cannot get the WP_RO region info: "
779 "%d\n", rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800780 return 1;
781 }
Simon Glass3c01dca2013-07-01 18:07:34 +0900782 start = info.offset;
783 len = info.size;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800784 } else {
785 msg_pdbg("%s(): EC_FLASH_PROTECT_RO_AT_BOOT is clear.\n",
786 __func__);
787 }
788
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800789 /*
790 * If neither RO_NOW or ALL_NOW is set, it means write protect is
791 * NOT active now.
792 */
793 if (!(r.flags & (EC_FLASH_PROTECT_RO_NOW | EC_FLASH_PROTECT_ALL_NOW)))
794 start = len = 0;
795
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800796 /* Remove the SPI-style messages. */
797 enabled = r.flags & EC_FLASH_PROTECT_RO_AT_BOOT ? 1 : 0;
798 msg_pinfo("WP: status: 0x%02x\n", enabled ? 0x80 : 0x00);
799 msg_pinfo("WP: status.srp0: %x\n", enabled);
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800800 msg_pinfo("WP: write protect is %s.\n",
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800801 enabled ? "enabled" : "disabled");
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800802 msg_pinfo("WP: write protect range: start=0x%08x, len=0x%08x\n",
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800803 start, len);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800804
805 return 0;
806}
807
808
David Hendricks7cfbd022012-05-20 17:25:51 -0700809int gec_probe_size(struct flashchip *flash) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800810 int rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800811 struct ec_response_flash_info info;
David Hendricks194b3bb2013-07-16 14:32:26 -0700812 struct ec_response_get_chip_info chip_info;
David Hendricks7cfbd022012-05-20 17:25:51 -0700813 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800814 struct block_eraser *eraser;
815 static struct wp wp = {
816 .list_ranges = gec_list_ranges,
817 .set_range = gec_set_range,
818 .enable = gec_enable_writeprotect,
819 .disable = gec_disable_writeprotect,
820 .wp_status = gec_wp_status,
821 };
822
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800823 rc = priv->ec_command(EC_CMD_FLASH_INFO, 0,
David Hendricks7cfbd022012-05-20 17:25:51 -0700824 NULL, 0, &info, sizeof(info));
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800825 if (rc < 0) {
826 msg_perr("%s(): FLASH_INFO returns %d.\n", __func__, rc);
827 return 0;
828 }
Simon Glass01c11672013-07-01 18:03:33 +0900829 rc = gec_get_current_image(priv);
830 if (rc < 0) {
831 msg_perr("%s(): Failed to probe (no current image): %d\n",
832 __func__, rc);
833 return 0;
834 }
835 priv->current_image = rc;
Simon Glassc453a642013-07-01 18:08:53 +0900836 priv->region = &regions[0];
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800837
838 flash->total_size = info.flash_size / 1024;
Simon Glass144fc2c2013-07-16 09:33:35 -0600839 flash->page_size = opaque_programmer->max_data_read;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800840 flash->tested = TEST_OK_PREW;
841 eraser = &flash->block_erasers[0];
842 eraser->eraseblocks[0].size = info.erase_block_size;
843 eraser->eraseblocks[0].count = info.flash_size /
844 eraser->eraseblocks[0].size;
845 flash->wp = &wp;
846
David Hendricks194b3bb2013-07-16 14:32:26 -0700847 /*
848 * Some STM32 variants erase bits to 0. For now, assume that this
849 * applies to STM32L parts.
850 *
851 * FIXME: This info will eventually be exposed via some EC command.
852 * See chrome-os-partner:20973.
853 */
854 rc = priv->ec_command(EC_CMD_GET_CHIP_INFO, 0,
855 NULL, 0, &chip_info, sizeof(chip_info));
856 if (rc < 0) {
857 msg_perr("%s(): CHIP_INFO returned %d.\n", __func__, rc);
858 return 0;
859 }
860 if (!strncmp(chip_info.name, "stm32l", 6))
861 flash->feature_bits |= FEATURE_ERASE_TO_ZERO;
862
David Hendricksf9461c72013-07-11 19:02:13 -0700863 gec_set_max_write_size();
864
Simon Glassc453a642013-07-01 18:08:53 +0900865 /* FIXME: EC_IMAGE_* is ordered differently from EC_FLASH_REGION_*,
866 * so we need to be careful about using these enums as array indices */
867 rc = gec_get_region_info(priv, EC_FLASH_REGION_RO,
868 &priv->region[EC_IMAGE_RO]);
869 if (rc) {
870 msg_perr("%s(): Failed to probe (cannot find RO region): %d\n",
871 __func__, rc);
872 return 0;
873 }
874
875 rc = gec_get_region_info(priv, EC_FLASH_REGION_RW,
876 &priv->region[EC_IMAGE_RW]);
877 if (rc) {
878 msg_perr("%s(): Failed to probe (cannot find RW region): %d\n",
879 __func__, rc);
880 return 0;
881 }
882
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800883 return 1;
884};