blob: ee73b77223c79825f3124754a96ae8e2286026c3 [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
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <unistd.h>
39#include "flashchips.h"
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080040#include "fmap.h"
David Hendricksa9151312013-07-01 12:21:01 -070041#include "gec.h"
Louis Yung-Chieh Loc0505242012-08-09 23:11:32 +080042#include "gec_lock.h"
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +080043#include "gec_ec_commands.h"
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080044#include "programmer.h"
45#include "spi.h"
46#include "writeprotect.h"
47
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +080048/* FIXME: used for wp hacks */
49#include <sys/types.h>
50#include <sys/stat.h>
51#include <fcntl.h>
52#include <unistd.h>
53struct wp_data {
54 int enable;
55 unsigned int start;
56 unsigned int len;
57};
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +080058#define WP_STATE_HACK_FILENAME "/mnt/stateful_partition/flashrom_wp_state"
59
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +080060/* If software sync is enabled, then we don't try the latest firmware copy
61 * after updating.
62 */
63#define SOFTWARE_SYNC_ENABLED
64
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080065/* 1 if we want the flashrom to call erase_and_write_flash() again. */
66static int need_2nd_pass = 0;
67
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +080068/* 1 if we want the flashrom to try jumping to new firmware after update. */
69static int try_latest_firmware = 0;
70
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080071/* The range of each firmware copy from the image file to update.
72 * But re-define the .flags as the valid flag to indicate the firmware is
73 * new or not (if flags = 1).
74 */
75static struct fmap_area fwcopy[4]; // [0] is not used.
76
77/* The names of enum lpc_current_image to match in FMAP area names. */
David Hendricksbf8c4dd2012-07-19 12:13:17 -070078static const char *sections[3] = {
79 "UNKNOWN SECTION", // EC_IMAGE_UNKNOWN -- never matches
80 "EC_RO",
81 "EC_RW",
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080082};
83
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080084
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080085/* Given the range not able to update, mark the corresponding
86 * firmware as old.
87 */
88static void gec_invalidate_copy(unsigned int addr, unsigned int len)
89{
90 int i;
91
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +080092 for (i = EC_IMAGE_RO; i < ARRAY_SIZE(fwcopy); i++) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080093 struct fmap_area *fw = &fwcopy[i];
94 if ((addr >= fw->offset && (addr < fw->offset + fw->size)) ||
95 (fw->offset >= addr && (fw->offset < addr + len))) {
96 msg_pdbg("Mark firmware [%s] as old.\n",
97 sections[i]);
98 fw->flags = 0; // mark as old
99 }
100 }
101}
102
103
Simon Glass01c11672013-07-01 18:03:33 +0900104static int gec_get_current_image(struct gec_priv *priv)
105{
106 struct ec_response_get_version resp;
107 int rc;
108
109 rc = priv->ec_command(EC_CMD_GET_VERSION, 0, NULL, 0, &resp,
110 sizeof(resp));
111 if (rc < 0) {
112 msg_perr("GEC cannot get the running copy: rc=%d\n", rc);
113 return rc;
114 }
115 if (resp.current_image == EC_IMAGE_UNKNOWN) {
116 msg_perr("GEC gets unknown running copy\n");
117 return -1;
118 }
119
120 return resp.current_image;
121}
122
123
Simon Glass3c01dca2013-07-01 18:07:34 +0900124static int gec_get_region_info(struct gec_priv *priv,
125 enum ec_flash_region region,
126 struct ec_response_flash_region_info *info)
127{
128 struct ec_params_flash_region_info req;
129 struct ec_response_flash_region_info resp;
130 int rc;
131
132 req.region = region;
133 rc = priv->ec_command(EC_CMD_FLASH_REGION_INFO,
134 EC_VER_FLASH_REGION_INFO, &req, sizeof(req),
135 &resp, sizeof(resp));
136 if (rc < 0) {
137 msg_perr("Cannot get the WP_RO region info: %d\n", rc);
138 return rc;
139 }
140
141 info->offset = resp.offset;
142 info->size = resp.size;
143 return 0;
144}
145
146
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800147/* Asks EC to jump to a firmware copy. If target is EC_IMAGE_UNKNOWN,
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800148 * then this functions picks a NEW firmware copy and jumps to it. Note that
149 * RO is preferred, then A, finally B.
150 *
151 * Returns 0 for success.
152 */
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800153static int gec_jump_copy(enum ec_current_image target) {
154 struct ec_response_get_version c;
155 struct ec_params_reboot_ec p;
David Hendricks7cfbd022012-05-20 17:25:51 -0700156 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800157 int rc;
158
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800159 /* Since the EC may return EC_RES_SUCCESS twice if the EC doesn't
160 * jump to different firmware copy. The second EC_RES_SUCCESS would
161 * set the OBF=1 and the next command cannot be executed.
162 * Thus, we call EC to jump only if the target is different.
163 */
Simon Glass01c11672013-07-01 18:03:33 +0900164 rc = gec_get_current_image(priv);
165 if (rc < 0)
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800166 return 1;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800167
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800168 memset(&p, 0, sizeof(p));
169 p.cmd = target != EC_IMAGE_UNKNOWN ? target :
170 fwcopy[EC_IMAGE_RO].flags ? EC_IMAGE_RO :
171 fwcopy[EC_IMAGE_RW].flags ? EC_IMAGE_RW :
172 EC_IMAGE_UNKNOWN;
173 msg_pdbg("GEC is jumping to [%s]\n", sections[p.cmd]);
174 if (p.cmd == EC_IMAGE_UNKNOWN) return 1;
175
176 if (c.current_image == p.cmd) {
177 msg_pdbg("GEC is already in [%s]\n", sections[p.cmd]);
178 return 0;
179 }
180
181 rc = priv->ec_command(EC_CMD_REBOOT_EC, 0,
David Hendricks7cfbd022012-05-20 17:25:51 -0700182 &p, sizeof(p), NULL, 0);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800183 if (rc < 0) {
184 msg_perr("GEC cannot jump to [%s]:%d\n",
185 sections[p.cmd], rc);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800186 } else {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800187 msg_pdbg("GEC has jumped to [%s]\n", sections[p.cmd]);
188 rc = EC_RES_SUCCESS;
Simon Glass01c11672013-07-01 18:03:33 +0900189 priv->current_image = target;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800190 }
191
192 /* Sleep 1 sec to wait the EC re-init. */
193 usleep(1000000);
194
195 return rc;
196}
197
198
199/* Given an image, this function parses FMAP and recognize the firmware
200 * ranges.
201 */
202int gec_prepare(uint8_t *image, int size) {
David Hendricks7cfbd022012-05-20 17:25:51 -0700203 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800204 struct fmap *fmap;
205 int i, j;
206
Louis Yung-Chieh Lo0eaa0ca2012-05-29 15:28:58 +0800207 if (!(priv && priv->detected)) return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800208
209 // Parse the fmap in the image file and cache the firmware ranges.
210 fmap = fmap_find_in_memory(image, size);
211 if (!fmap) return 0;
212
213 // Lookup RO/A/B sections in FMAP.
214 for (i = 0; i < fmap->nareas; i++) {
215 struct fmap_area *fa = &fmap->areas[i];
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800216 for (j = EC_IMAGE_RO; j < ARRAY_SIZE(sections); j++) {
David Hendricks5b06c882012-05-20 18:27:25 -0700217 if (!strcmp(sections[j], (const char *)fa->name)) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800218 msg_pdbg("Found '%s' in image.\n", fa->name);
219 memcpy(&fwcopy[j], fa, sizeof(*fa));
220 fwcopy[j].flags = 1; // mark as new
221 }
222 }
223 }
224
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800225 /* Warning: before update, we jump the EC to RO copy. If you want to
226 * change this behavior, please also check the gec_finish().
227 */
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800228 return gec_jump_copy(EC_IMAGE_RO);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800229}
230
231
232/* Returns >0 if we need 2nd pass of erase_and_write_flash().
233 * <0 if we cannot jump to any firmware copy.
234 * ==0 if no more pass is needed.
235 *
236 * This function also jumps to new-updated firmware copy before return >0.
237 */
238int gec_need_2nd_pass(void) {
David Hendricks7cfbd022012-05-20 17:25:51 -0700239 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
240
Louis Yung-Chieh Lo0eaa0ca2012-05-29 15:28:58 +0800241 if (!(priv && priv->detected)) return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800242
243 if (need_2nd_pass) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800244 if (gec_jump_copy(EC_IMAGE_UNKNOWN)) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800245 return -1;
246 }
247 }
248
249 return need_2nd_pass;
250}
251
252
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800253/* Returns 0 for success.
254 *
255 * Try latest firmware: B > A > RO
256 *
257 * This function assumes the EC jumps to RO at gec_prepare() so that
258 * the fwcopy[RO].flags is old (0) and A/B are new. Please also refine
259 * this code logic if you change the gec_prepare() behavior.
260 */
261int gec_finish(void) {
262 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
263
264 if (!(priv && priv->detected)) return 0;
265
266 if (try_latest_firmware) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800267 if (fwcopy[EC_IMAGE_RW].flags &&
268 gec_jump_copy(EC_IMAGE_RW) == 0) return 0;
269 return gec_jump_copy(EC_IMAGE_RO);
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800270 }
271
272 return 0;
273}
274
275
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800276int gec_read(struct flashchip *flash, uint8_t *readarr,
277 unsigned int blockaddr, unsigned int readcnt) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800278 int rc = 0;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800279 struct ec_params_flash_read p;
David Hendricks7cfbd022012-05-20 17:25:51 -0700280 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
David Hendricksd6a0f662012-05-29 14:39:50 -0700281 int maxlen = opaque_programmer->max_data_read;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800282 uint8_t buf[maxlen];
David Hendricks133083b2012-07-17 20:39:38 -0700283 int offset = 0, count;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800284
David Hendricks133083b2012-07-17 20:39:38 -0700285 while (offset < readcnt) {
286 count = min(maxlen, readcnt - offset);
287 p.offset = blockaddr + offset;
288 p.size = count;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800289 rc = priv->ec_command(EC_CMD_FLASH_READ, 0,
290 &p, sizeof(p), buf, count);
291 if (rc < 0) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800292 msg_perr("GEC: Flash read error at offset 0x%x\n",
David Hendricks133083b2012-07-17 20:39:38 -0700293 blockaddr + offset);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800294 return rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800295 } else {
296 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800297 }
298
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800299 memcpy(readarr + offset, buf, count);
David Hendricks133083b2012-07-17 20:39:38 -0700300 offset += count;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800301 }
302
303 return rc;
304}
305
306
David Hendricks7cfbd022012-05-20 17:25:51 -0700307int gec_block_erase(struct flashchip *flash,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800308 unsigned int blockaddr,
309 unsigned int len) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800310 struct ec_params_flash_erase erase;
David Hendricks7cfbd022012-05-20 17:25:51 -0700311 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800312 int rc;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800313
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800314 erase.offset = blockaddr;
315 erase.size = len;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800316 rc = priv->ec_command(EC_CMD_FLASH_ERASE, 0,
David Hendricks7cfbd022012-05-20 17:25:51 -0700317 &erase, sizeof(erase), NULL, 0);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800318 if (rc == -EC_RES_ACCESS_DENIED) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800319 // this is active image.
320 gec_invalidate_copy(blockaddr, len);
321 need_2nd_pass = 1;
322 return ACCESS_DENIED;
323 }
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800324 if (rc < 0) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800325 msg_perr("GEC: Flash erase error at address 0x%x, rc=%d\n",
326 blockaddr, rc);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800327 return rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800328 } else {
329 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800330 }
331
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800332#ifndef SOFTWARE_SYNC_ENABLED
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800333 try_latest_firmware = 1;
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800334#endif
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800335 return rc;
336}
337
338
339int gec_write(struct flashchip *flash, uint8_t *buf, unsigned int addr,
340 unsigned int nbytes) {
341 int i, rc = 0;
342 unsigned int written = 0;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800343 struct ec_params_flash_write p;
David Hendricks7cfbd022012-05-20 17:25:51 -0700344 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
David Hendricksd6a0f662012-05-29 14:39:50 -0700345 int maxlen = opaque_programmer->max_data_write;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800346
347 for (i = 0; i < nbytes; i += written) {
David Hendricksd6a0f662012-05-29 14:39:50 -0700348 written = min(nbytes - i, maxlen);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800349 p.offset = addr + i;
350 p.size = written;
351 memcpy(p.data, &buf[i], written);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800352 rc = priv->ec_command(EC_CMD_FLASH_WRITE, 0,
David Hendricks7cfbd022012-05-20 17:25:51 -0700353 &p, sizeof(p), NULL, 0);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800354 if (rc == -EC_RES_ACCESS_DENIED) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800355 // this is active image.
356 gec_invalidate_copy(addr, nbytes);
357 need_2nd_pass = 1;
358 return ACCESS_DENIED;
359 }
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800360
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800361 if (rc < 0) break;
362 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800363 }
364
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800365#ifndef SOFTWARE_SYNC_ENABLED
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800366 try_latest_firmware = 1;
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800367#endif
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800368 return rc;
369}
370
371
372static int gec_list_ranges(const struct flashchip *flash) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800373 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Simon Glass3c01dca2013-07-01 18:07:34 +0900374 struct ec_response_flash_region_info info;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800375 int rc;
376
Simon Glass3c01dca2013-07-01 18:07:34 +0900377 rc = gec_get_region_info(priv, EC_FLASH_REGION_WP_RO, &info);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800378 if (rc < 0) {
379 msg_perr("Cannot get the WP_RO region info: %d\n", rc);
380 return 1;
381 }
382
383 msg_pinfo("Supported write protect range:\n");
384 msg_pinfo(" disable: start=0x%06x len=0x%06x\n", 0, 0);
Simon Glass3c01dca2013-07-01 18:07:34 +0900385 msg_pinfo(" enable: start=0x%06x len=0x%06x\n", info.offset,
386 info.size);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800387
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800388 return 0;
389}
390
391
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800392/*
393 * Helper function for flash protection.
394 *
395 * On EC API v1, the EC write protection has been simplified to one-bit:
396 * EC_FLASH_PROTECT_RO_AT_BOOT, which means the state is either enabled
397 * or disabled. However, this is different from the SPI-style write protect
398 * behavior. Thus, we re-define the flashrom command (SPI-style) so that
399 * either SRP or range is non-zero, the EC_FLASH_PROTECT_RO_AT_BOOT is set.
400 *
401 * SRP Range | PROTECT_RO_AT_BOOT
402 * 0 0 | 0
403 * 0 non-zero | 1
404 * 1 0 | 1
405 * 1 non-zero | 1
406 *
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800407 *
408 * Besides, to make the protection take effect as soon as possible, we
409 * try to set EC_FLASH_PROTECT_RO_NOW at the same time. However, not
410 * every EC supports RO_NOW, thus we then try to protect the entire chip.
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800411 */
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800412static int set_wp(int enable) {
413 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
414 struct ec_params_flash_protect p;
415 struct ec_response_flash_protect r;
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800416 const int ro_at_boot_flag = EC_FLASH_PROTECT_RO_AT_BOOT;
417 const int ro_now_flag = EC_FLASH_PROTECT_RO_NOW;
418 int need_an_ec_cold_reset = 0;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800419 int rc;
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800420
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800421 /* Try to set RO_AT_BOOT and RO_NOW first */
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800422 memset(&p, 0, sizeof(p));
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800423 p.mask = (ro_at_boot_flag | ro_now_flag);
424 p.flags = enable ? (ro_at_boot_flag | ro_now_flag) : 0;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800425 rc = priv->ec_command(EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT,
426 &p, sizeof(p), &r, sizeof(r));
427 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800428 msg_perr("FAILED: Cannot set the RO_AT_BOOT and RO_NOW: %d\n",
429 rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800430 return 1;
431 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800432
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800433 /* Read back */
434 memset(&p, 0, sizeof(p));
435 rc = priv->ec_command(EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT,
436 &p, sizeof(p), &r, sizeof(r));
437 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800438 msg_perr("FAILED: Cannot get RO_AT_BOOT and RO_NOW: %d\n",
439 rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800440 return 1;
441 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800442
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800443 if (!enable) {
444 /* The disable case is easier to check. */
445 if (r.flags & ro_at_boot_flag) {
446 msg_perr("FAILED: RO_AT_BOOT is not clear.\n");
447 return 1;
448 } else if (r.flags & ro_now_flag) {
449 msg_perr("FAILED: RO_NOW is asserted unexpectedly.\n");
450 need_an_ec_cold_reset = 1;
451 goto exit;
452 }
453
454 msg_pdbg("INFO: RO_AT_BOOT is clear.\n");
455 return 0;
456 }
457
458 /* Check if RO_AT_BOOT is set. If not, fail in anyway. */
459 if (r.flags & ro_at_boot_flag) {
460 msg_pdbg("INFO: RO_AT_BOOT has been set.\n");
461 } else {
462 msg_perr("FAILED: RO_AT_BOOT is not set.\n");
463 return 1;
464 }
465
466 /* Then, we check if the protection has been activated. */
467 if (r.flags & ro_now_flag) {
468 /* Good, RO_NOW is set. */
469 msg_pdbg("INFO: RO_NOW is set. WP is active now.\n");
470 } else if (r.writable_flags & EC_FLASH_PROTECT_ALL_NOW) {
471 struct ec_params_reboot_ec reboot;
472
473 msg_pdbg("WARN: RO_NOW is not set. Trying ALL_NOW.\n");
474
475 memset(&p, 0, sizeof(p));
476 p.mask = EC_FLASH_PROTECT_ALL_NOW;
477 p.flags = EC_FLASH_PROTECT_ALL_NOW;
478 rc = priv->ec_command(EC_CMD_FLASH_PROTECT,
479 EC_VER_FLASH_PROTECT,
480 &p, sizeof(p), &r, sizeof(r));
481 if (rc < 0) {
482 msg_perr("FAILED: Cannot set ALL_NOW: %d\n", rc);
483 return 1;
484 }
485
486 /* Read back */
487 memset(&p, 0, sizeof(p));
488 rc = priv->ec_command(EC_CMD_FLASH_PROTECT,
489 EC_VER_FLASH_PROTECT,
490 &p, sizeof(p), &r, sizeof(r));
491 if (rc < 0) {
492 msg_perr("FAILED:Cannot get ALL_NOW: %d\n", rc);
493 return 1;
494 }
495
496 if (!(r.flags & EC_FLASH_PROTECT_ALL_NOW)) {
497 msg_perr("FAILED: ALL_NOW is not set.\n");
498 need_an_ec_cold_reset = 1;
499 goto exit;
500 }
501
502 msg_pdbg("INFO: ALL_NOW has been set. WP is active now.\n");
503
504 /*
505 * Our goal is to protect the RO ASAP. The entire protection
506 * is just a workaround for platform not supporting RO_NOW.
507 * It has side-effect that the RW is also protected and leads
508 * the RW update failed. So, we arrange an EC code reset to
509 * unlock RW ASAP.
510 */
511 memset(&reboot, 0, sizeof(reboot));
512 reboot.cmd = EC_REBOOT_COLD;
513 reboot.flags = EC_REBOOT_FLAG_ON_AP_SHUTDOWN;
514 rc = priv->ec_command(EC_CMD_REBOOT_EC, 0,
515 &reboot, sizeof(reboot), NULL, 0);
516 if (rc < 0) {
517 msg_perr("WARN: Cannot arrange a cold reset at next "
518 "shutdown to unlock entire protect.\n");
519 msg_perr(" But you can do it manually.\n");
520 } else {
521 msg_pdbg("INFO: A cold reset is arranged at next "
522 "shutdown.\n");
523 }
524
525 } else {
526 msg_perr("FAILED: RO_NOW is not set.\n");
527 msg_perr("FAILED: The PROTECT_RO_AT_BOOT is set, but cannot "
528 "make write protection active now.\n");
529 need_an_ec_cold_reset = 1;
530 }
531
532exit:
533 if (need_an_ec_cold_reset) {
534 msg_perr("FAILED: You may need a reboot to take effect of "
535 "PROTECT_RO_AT_BOOT.\n");
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800536 return 1;
537 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800538
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800539 return 0;
540}
541
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800542static int gec_set_range(const struct flashchip *flash,
543 unsigned int start, unsigned int len) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800544 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Simon Glass3c01dca2013-07-01 18:07:34 +0900545 struct ec_response_flash_region_info info;
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800546 int rc;
547
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800548 /* Check if the given range is supported */
Simon Glass3c01dca2013-07-01 18:07:34 +0900549 rc = gec_get_region_info(priv, EC_FLASH_REGION_WP_RO, &info);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800550 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800551 msg_perr("FAILED: Cannot get the WP_RO region info: %d\n", rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800552 return 1;
553 }
554 if ((!start && !len) || /* list supported ranges */
Simon Glass3c01dca2013-07-01 18:07:34 +0900555 ((start == info.offset) && (len == info.size))) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800556 /* pass */
557 } else {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800558 msg_perr("FAILED: Unsupported write protection range "
559 "(0x%06x,0x%06x)\n\n", start, len);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800560 msg_perr("Currently supported range:\n");
561 msg_perr(" disable: (0x%06x,0x%06x)\n", 0, 0);
Simon Glass3c01dca2013-07-01 18:07:34 +0900562 msg_perr(" enable: (0x%06x,0x%06x)\n", info.offset,
563 info.size);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800564 return 1;
565 }
566
567 return set_wp(!!len);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800568}
569
570
David Hendricks1c09f802012-10-03 11:03:48 -0700571static int gec_enable_writeprotect(const struct flashchip *flash,
572 enum wp_mode wp_mode) {
573 int ret;
574
575 switch (wp_mode) {
576 case WP_MODE_HARDWARE:
577 ret = set_wp(1);
578 break;
579 default:
580 msg_perr("%s():%d Unsupported write-protection mode\n",
581 __func__, __LINE__);
582 ret = 1;
583 break;
584 }
585
586 return ret;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800587}
588
589
590static int gec_disable_writeprotect(const struct flashchip *flash) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800591 return set_wp(0);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800592}
593
594
595static int gec_wp_status(const struct flashchip *flash) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800596 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
597 struct ec_params_flash_protect p;
598 struct ec_response_flash_protect r;
599 int start, len; /* wp range */
600 int enabled;
601 int rc;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800602
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800603 memset(&p, 0, sizeof(p));
604 rc = priv->ec_command(EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT,
605 &p, sizeof(p), &r, sizeof(r));
606 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800607 msg_perr("FAILED: Cannot get the write protection status: %d\n",
608 rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800609 return 1;
610 } else if (rc < sizeof(r)) {
David Hendricksf797dde2012-10-30 11:39:12 -0700611 msg_perr("FAILED: Too little data returned (expected:%zd, "
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800612 "actual:%d)\n", sizeof(r), rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800613 return 1;
614 }
615
616 start = len = 0;
617 if (r.flags & EC_FLASH_PROTECT_RO_AT_BOOT) {
Simon Glass3c01dca2013-07-01 18:07:34 +0900618 struct ec_response_flash_region_info info;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800619
620 msg_pdbg("%s(): EC_FLASH_PROTECT_RO_AT_BOOT is set.\n",
621 __func__);
Simon Glass3c01dca2013-07-01 18:07:34 +0900622 rc = gec_get_region_info(priv, EC_FLASH_REGION_WP_RO, &info);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800623 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800624 msg_perr("FAILED: Cannot get the WP_RO region info: "
625 "%d\n", rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800626 return 1;
627 }
Simon Glass3c01dca2013-07-01 18:07:34 +0900628 start = info.offset;
629 len = info.size;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800630 } else {
631 msg_pdbg("%s(): EC_FLASH_PROTECT_RO_AT_BOOT is clear.\n",
632 __func__);
633 }
634
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800635 /*
636 * If neither RO_NOW or ALL_NOW is set, it means write protect is
637 * NOT active now.
638 */
639 if (!(r.flags & (EC_FLASH_PROTECT_RO_NOW | EC_FLASH_PROTECT_ALL_NOW)))
640 start = len = 0;
641
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800642 /* Remove the SPI-style messages. */
643 enabled = r.flags & EC_FLASH_PROTECT_RO_AT_BOOT ? 1 : 0;
644 msg_pinfo("WP: status: 0x%02x\n", enabled ? 0x80 : 0x00);
645 msg_pinfo("WP: status.srp0: %x\n", enabled);
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800646 msg_pinfo("WP: write protect is %s.\n",
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800647 enabled ? "enabled" : "disabled");
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800648 msg_pinfo("WP: write protect range: start=0x%08x, len=0x%08x\n",
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800649 start, len);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800650
651 return 0;
652}
653
654
David Hendricks7cfbd022012-05-20 17:25:51 -0700655int gec_probe_size(struct flashchip *flash) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800656 int rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800657 struct ec_response_flash_info info;
David Hendricks7cfbd022012-05-20 17:25:51 -0700658 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800659 struct block_eraser *eraser;
660 static struct wp wp = {
661 .list_ranges = gec_list_ranges,
662 .set_range = gec_set_range,
663 .enable = gec_enable_writeprotect,
664 .disable = gec_disable_writeprotect,
665 .wp_status = gec_wp_status,
666 };
667
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800668 rc = priv->ec_command(EC_CMD_FLASH_INFO, 0,
David Hendricks7cfbd022012-05-20 17:25:51 -0700669 NULL, 0, &info, sizeof(info));
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800670 if (rc < 0) {
671 msg_perr("%s(): FLASH_INFO returns %d.\n", __func__, rc);
672 return 0;
673 }
Simon Glass01c11672013-07-01 18:03:33 +0900674 rc = gec_get_current_image(priv);
675 if (rc < 0) {
676 msg_perr("%s(): Failed to probe (no current image): %d\n",
677 __func__, rc);
678 return 0;
679 }
680 priv->current_image = rc;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800681
682 flash->total_size = info.flash_size / 1024;
David Hendricks0d3fcb52012-07-08 18:37:43 -0700683 flash->page_size = 64;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800684 flash->tested = TEST_OK_PREW;
685 eraser = &flash->block_erasers[0];
686 eraser->eraseblocks[0].size = info.erase_block_size;
687 eraser->eraseblocks[0].count = info.flash_size /
688 eraser->eraseblocks[0].size;
689 flash->wp = &wp;
690
691 return 1;
692};