blob: b18cddde41684c188a7cad149fe3d83743ab71af [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"
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
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080083
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080084/* Given the range not able to update, mark the corresponding
85 * firmware as old.
86 */
87static void gec_invalidate_copy(unsigned int addr, unsigned int len)
88{
89 int i;
90
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +080091 for (i = EC_IMAGE_RO; i < ARRAY_SIZE(fwcopy); i++) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080092 struct fmap_area *fw = &fwcopy[i];
93 if ((addr >= fw->offset && (addr < fw->offset + fw->size)) ||
94 (fw->offset >= addr && (fw->offset < addr + len))) {
95 msg_pdbg("Mark firmware [%s] as old.\n",
96 sections[i]);
97 fw->flags = 0; // mark as old
98 }
99 }
100}
101
102
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800103/* Asks EC to jump to a firmware copy. If target is EC_IMAGE_UNKNOWN,
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800104 * then this functions picks a NEW firmware copy and jumps to it. Note that
105 * RO is preferred, then A, finally B.
106 *
107 * Returns 0 for success.
108 */
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800109static int gec_jump_copy(enum ec_current_image target) {
110 struct ec_response_get_version c;
111 struct ec_params_reboot_ec p;
David Hendricks7cfbd022012-05-20 17:25:51 -0700112 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800113 int rc;
114
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800115 /* Since the EC may return EC_RES_SUCCESS twice if the EC doesn't
116 * jump to different firmware copy. The second EC_RES_SUCCESS would
117 * set the OBF=1 and the next command cannot be executed.
118 * Thus, we call EC to jump only if the target is different.
119 */
120 rc = priv->ec_command(EC_CMD_GET_VERSION, 0, NULL, 0, &c, sizeof(c));
Louis Yung-Chieh Lo5c97c9f2012-08-16 13:33:36 +0800121 if (rc < 0 || c.current_image == EC_IMAGE_UNKNOWN) {
122 msg_perr("GEC cannot get the running copy: rc=%d image=%s\n",
123 rc, sections[c.current_image]);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800124 return 1;
125 }
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800126
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800127 memset(&p, 0, sizeof(p));
128 p.cmd = target != EC_IMAGE_UNKNOWN ? target :
129 fwcopy[EC_IMAGE_RO].flags ? EC_IMAGE_RO :
130 fwcopy[EC_IMAGE_RW].flags ? EC_IMAGE_RW :
131 EC_IMAGE_UNKNOWN;
132 msg_pdbg("GEC is jumping to [%s]\n", sections[p.cmd]);
133 if (p.cmd == EC_IMAGE_UNKNOWN) return 1;
134
135 if (c.current_image == p.cmd) {
136 msg_pdbg("GEC is already in [%s]\n", sections[p.cmd]);
137 return 0;
138 }
139
140 rc = priv->ec_command(EC_CMD_REBOOT_EC, 0,
David Hendricks7cfbd022012-05-20 17:25:51 -0700141 &p, sizeof(p), NULL, 0);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800142 if (rc < 0) {
143 msg_perr("GEC cannot jump to [%s]:%d\n",
144 sections[p.cmd], rc);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800145 } else {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800146 msg_pdbg("GEC has jumped to [%s]\n", sections[p.cmd]);
147 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800148 }
149
150 /* Sleep 1 sec to wait the EC re-init. */
151 usleep(1000000);
152
153 return rc;
154}
155
156
157/* Given an image, this function parses FMAP and recognize the firmware
158 * ranges.
159 */
160int gec_prepare(uint8_t *image, int size) {
David Hendricks7cfbd022012-05-20 17:25:51 -0700161 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800162 struct fmap *fmap;
163 int i, j;
164
Louis Yung-Chieh Lo0eaa0ca2012-05-29 15:28:58 +0800165 if (!(priv && priv->detected)) return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800166
167 // Parse the fmap in the image file and cache the firmware ranges.
168 fmap = fmap_find_in_memory(image, size);
169 if (!fmap) return 0;
170
171 // Lookup RO/A/B sections in FMAP.
172 for (i = 0; i < fmap->nareas; i++) {
173 struct fmap_area *fa = &fmap->areas[i];
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800174 for (j = EC_IMAGE_RO; j < ARRAY_SIZE(sections); j++) {
David Hendricks5b06c882012-05-20 18:27:25 -0700175 if (!strcmp(sections[j], (const char *)fa->name)) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800176 msg_pdbg("Found '%s' in image.\n", fa->name);
177 memcpy(&fwcopy[j], fa, sizeof(*fa));
178 fwcopy[j].flags = 1; // mark as new
179 }
180 }
181 }
182
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800183 /* Warning: before update, we jump the EC to RO copy. If you want to
184 * change this behavior, please also check the gec_finish().
185 */
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800186 return gec_jump_copy(EC_IMAGE_RO);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800187}
188
189
190/* Returns >0 if we need 2nd pass of erase_and_write_flash().
191 * <0 if we cannot jump to any firmware copy.
192 * ==0 if no more pass is needed.
193 *
194 * This function also jumps to new-updated firmware copy before return >0.
195 */
196int gec_need_2nd_pass(void) {
David Hendricks7cfbd022012-05-20 17:25:51 -0700197 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
198
Louis Yung-Chieh Lo0eaa0ca2012-05-29 15:28:58 +0800199 if (!(priv && priv->detected)) return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800200
201 if (need_2nd_pass) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800202 if (gec_jump_copy(EC_IMAGE_UNKNOWN)) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800203 return -1;
204 }
205 }
206
207 return need_2nd_pass;
208}
209
210
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800211/* Returns 0 for success.
212 *
213 * Try latest firmware: B > A > RO
214 *
215 * This function assumes the EC jumps to RO at gec_prepare() so that
216 * the fwcopy[RO].flags is old (0) and A/B are new. Please also refine
217 * this code logic if you change the gec_prepare() behavior.
218 */
219int gec_finish(void) {
220 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
221
222 if (!(priv && priv->detected)) return 0;
223
224 if (try_latest_firmware) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800225 if (fwcopy[EC_IMAGE_RW].flags &&
226 gec_jump_copy(EC_IMAGE_RW) == 0) return 0;
227 return gec_jump_copy(EC_IMAGE_RO);
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800228 }
229
230 return 0;
231}
232
233
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800234int gec_read(struct flashchip *flash, uint8_t *readarr,
235 unsigned int blockaddr, unsigned int readcnt) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800236 int rc = 0;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800237 struct ec_params_flash_read p;
David Hendricks7cfbd022012-05-20 17:25:51 -0700238 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
David Hendricksd6a0f662012-05-29 14:39:50 -0700239 int maxlen = opaque_programmer->max_data_read;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800240 uint8_t buf[maxlen];
David Hendricks133083b2012-07-17 20:39:38 -0700241 int offset = 0, count;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800242
David Hendricks133083b2012-07-17 20:39:38 -0700243 while (offset < readcnt) {
244 count = min(maxlen, readcnt - offset);
245 p.offset = blockaddr + offset;
246 p.size = count;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800247 rc = priv->ec_command(EC_CMD_FLASH_READ, 0,
248 &p, sizeof(p), buf, count);
249 if (rc < 0) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800250 msg_perr("GEC: Flash read error at offset 0x%x\n",
David Hendricks133083b2012-07-17 20:39:38 -0700251 blockaddr + offset);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800252 return rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800253 } else {
254 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800255 }
256
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800257 memcpy(readarr + offset, buf, count);
David Hendricks133083b2012-07-17 20:39:38 -0700258 offset += count;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800259 }
260
261 return rc;
262}
263
264
David Hendricks7cfbd022012-05-20 17:25:51 -0700265int gec_block_erase(struct flashchip *flash,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800266 unsigned int blockaddr,
267 unsigned int len) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800268 struct ec_params_flash_erase erase;
David Hendricks7cfbd022012-05-20 17:25:51 -0700269 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800270 int rc;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800271
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800272 erase.offset = blockaddr;
273 erase.size = len;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800274 rc = priv->ec_command(EC_CMD_FLASH_ERASE, 0,
David Hendricks7cfbd022012-05-20 17:25:51 -0700275 &erase, sizeof(erase), NULL, 0);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800276 if (rc == -EC_RES_ACCESS_DENIED) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800277 // this is active image.
278 gec_invalidate_copy(blockaddr, len);
279 need_2nd_pass = 1;
280 return ACCESS_DENIED;
281 }
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800282 if (rc < 0) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800283 msg_perr("GEC: Flash erase error at address 0x%x, rc=%d\n",
284 blockaddr, rc);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800285 return rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800286 } else {
287 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800288 }
289
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800290#ifndef SOFTWARE_SYNC_ENABLED
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800291 try_latest_firmware = 1;
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800292#endif
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800293 return rc;
294}
295
296
297int gec_write(struct flashchip *flash, uint8_t *buf, unsigned int addr,
298 unsigned int nbytes) {
299 int i, rc = 0;
300 unsigned int written = 0;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800301 struct ec_params_flash_write p;
David Hendricks7cfbd022012-05-20 17:25:51 -0700302 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
David Hendricksd6a0f662012-05-29 14:39:50 -0700303 int maxlen = opaque_programmer->max_data_write;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800304
305 for (i = 0; i < nbytes; i += written) {
David Hendricksd6a0f662012-05-29 14:39:50 -0700306 written = min(nbytes - i, maxlen);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800307 p.offset = addr + i;
308 p.size = written;
309 memcpy(p.data, &buf[i], written);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800310 rc = priv->ec_command(EC_CMD_FLASH_WRITE, 0,
David Hendricks7cfbd022012-05-20 17:25:51 -0700311 &p, sizeof(p), NULL, 0);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800312 if (rc == -EC_RES_ACCESS_DENIED) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800313 // this is active image.
314 gec_invalidate_copy(addr, nbytes);
315 need_2nd_pass = 1;
316 return ACCESS_DENIED;
317 }
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800318
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800319 if (rc < 0) break;
320 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800321 }
322
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800323#ifndef SOFTWARE_SYNC_ENABLED
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800324 try_latest_firmware = 1;
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800325#endif
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800326 return rc;
327}
328
329
330static int gec_list_ranges(const struct flashchip *flash) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800331 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
332 struct ec_params_flash_region_info p;
333 struct ec_response_flash_region_info r;
334 int rc;
335
336 p.region = EC_FLASH_REGION_WP_RO;
337 rc = priv->ec_command(EC_CMD_FLASH_REGION_INFO,
338 EC_VER_FLASH_REGION_INFO, &p, sizeof(p), &r, sizeof(r));
339 if (rc < 0) {
340 msg_perr("Cannot get the WP_RO region info: %d\n", rc);
341 return 1;
342 }
343
344 msg_pinfo("Supported write protect range:\n");
345 msg_pinfo(" disable: start=0x%06x len=0x%06x\n", 0, 0);
346 msg_pinfo(" enable: start=0x%06x len=0x%06x\n", r.offset, r.size);
347
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800348 return 0;
349}
350
351
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800352/*
353 * Helper function for flash protection.
354 *
355 * On EC API v1, the EC write protection has been simplified to one-bit:
356 * EC_FLASH_PROTECT_RO_AT_BOOT, which means the state is either enabled
357 * or disabled. However, this is different from the SPI-style write protect
358 * behavior. Thus, we re-define the flashrom command (SPI-style) so that
359 * either SRP or range is non-zero, the EC_FLASH_PROTECT_RO_AT_BOOT is set.
360 *
361 * SRP Range | PROTECT_RO_AT_BOOT
362 * 0 0 | 0
363 * 0 non-zero | 1
364 * 1 0 | 1
365 * 1 non-zero | 1
366 *
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800367 *
368 * Besides, to make the protection take effect as soon as possible, we
369 * try to set EC_FLASH_PROTECT_RO_NOW at the same time. However, not
370 * every EC supports RO_NOW, thus we then try to protect the entire chip.
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800371 */
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800372static int set_wp(int enable) {
373 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
374 struct ec_params_flash_protect p;
375 struct ec_response_flash_protect r;
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800376 const int ro_at_boot_flag = EC_FLASH_PROTECT_RO_AT_BOOT;
377 const int ro_now_flag = EC_FLASH_PROTECT_RO_NOW;
378 int need_an_ec_cold_reset = 0;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800379 int rc;
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800380
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800381 /* Try to set RO_AT_BOOT and RO_NOW first */
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800382 memset(&p, 0, sizeof(p));
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800383 p.mask = (ro_at_boot_flag | ro_now_flag);
384 p.flags = enable ? (ro_at_boot_flag | ro_now_flag) : 0;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800385 rc = priv->ec_command(EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT,
386 &p, sizeof(p), &r, sizeof(r));
387 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800388 msg_perr("FAILED: Cannot set the RO_AT_BOOT and RO_NOW: %d\n",
389 rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800390 return 1;
391 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800392
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800393 /* Read back */
394 memset(&p, 0, sizeof(p));
395 rc = priv->ec_command(EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT,
396 &p, sizeof(p), &r, sizeof(r));
397 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800398 msg_perr("FAILED: Cannot get RO_AT_BOOT and RO_NOW: %d\n",
399 rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800400 return 1;
401 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800402
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800403 if (!enable) {
404 /* The disable case is easier to check. */
405 if (r.flags & ro_at_boot_flag) {
406 msg_perr("FAILED: RO_AT_BOOT is not clear.\n");
407 return 1;
408 } else if (r.flags & ro_now_flag) {
409 msg_perr("FAILED: RO_NOW is asserted unexpectedly.\n");
410 need_an_ec_cold_reset = 1;
411 goto exit;
412 }
413
414 msg_pdbg("INFO: RO_AT_BOOT is clear.\n");
415 return 0;
416 }
417
418 /* Check if RO_AT_BOOT is set. If not, fail in anyway. */
419 if (r.flags & ro_at_boot_flag) {
420 msg_pdbg("INFO: RO_AT_BOOT has been set.\n");
421 } else {
422 msg_perr("FAILED: RO_AT_BOOT is not set.\n");
423 return 1;
424 }
425
426 /* Then, we check if the protection has been activated. */
427 if (r.flags & ro_now_flag) {
428 /* Good, RO_NOW is set. */
429 msg_pdbg("INFO: RO_NOW is set. WP is active now.\n");
430 } else if (r.writable_flags & EC_FLASH_PROTECT_ALL_NOW) {
431 struct ec_params_reboot_ec reboot;
432
433 msg_pdbg("WARN: RO_NOW is not set. Trying ALL_NOW.\n");
434
435 memset(&p, 0, sizeof(p));
436 p.mask = EC_FLASH_PROTECT_ALL_NOW;
437 p.flags = EC_FLASH_PROTECT_ALL_NOW;
438 rc = priv->ec_command(EC_CMD_FLASH_PROTECT,
439 EC_VER_FLASH_PROTECT,
440 &p, sizeof(p), &r, sizeof(r));
441 if (rc < 0) {
442 msg_perr("FAILED: Cannot set ALL_NOW: %d\n", rc);
443 return 1;
444 }
445
446 /* Read back */
447 memset(&p, 0, sizeof(p));
448 rc = priv->ec_command(EC_CMD_FLASH_PROTECT,
449 EC_VER_FLASH_PROTECT,
450 &p, sizeof(p), &r, sizeof(r));
451 if (rc < 0) {
452 msg_perr("FAILED:Cannot get ALL_NOW: %d\n", rc);
453 return 1;
454 }
455
456 if (!(r.flags & EC_FLASH_PROTECT_ALL_NOW)) {
457 msg_perr("FAILED: ALL_NOW is not set.\n");
458 need_an_ec_cold_reset = 1;
459 goto exit;
460 }
461
462 msg_pdbg("INFO: ALL_NOW has been set. WP is active now.\n");
463
464 /*
465 * Our goal is to protect the RO ASAP. The entire protection
466 * is just a workaround for platform not supporting RO_NOW.
467 * It has side-effect that the RW is also protected and leads
468 * the RW update failed. So, we arrange an EC code reset to
469 * unlock RW ASAP.
470 */
471 memset(&reboot, 0, sizeof(reboot));
472 reboot.cmd = EC_REBOOT_COLD;
473 reboot.flags = EC_REBOOT_FLAG_ON_AP_SHUTDOWN;
474 rc = priv->ec_command(EC_CMD_REBOOT_EC, 0,
475 &reboot, sizeof(reboot), NULL, 0);
476 if (rc < 0) {
477 msg_perr("WARN: Cannot arrange a cold reset at next "
478 "shutdown to unlock entire protect.\n");
479 msg_perr(" But you can do it manually.\n");
480 } else {
481 msg_pdbg("INFO: A cold reset is arranged at next "
482 "shutdown.\n");
483 }
484
485 } else {
486 msg_perr("FAILED: RO_NOW is not set.\n");
487 msg_perr("FAILED: The PROTECT_RO_AT_BOOT is set, but cannot "
488 "make write protection active now.\n");
489 need_an_ec_cold_reset = 1;
490 }
491
492exit:
493 if (need_an_ec_cold_reset) {
494 msg_perr("FAILED: You may need a reboot to take effect of "
495 "PROTECT_RO_AT_BOOT.\n");
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800496 return 1;
497 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800498
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800499 return 0;
500}
501
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800502static int gec_set_range(const struct flashchip *flash,
503 unsigned int start, unsigned int len) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800504 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
505 struct ec_params_flash_region_info p;
506 struct ec_response_flash_region_info r;
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800507 int rc;
508
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800509 /* Check if the given range is supported */
510 p.region = EC_FLASH_REGION_WP_RO;
511 rc = priv->ec_command(EC_CMD_FLASH_REGION_INFO,
512 EC_VER_FLASH_REGION_INFO, &p, sizeof(p), &r, sizeof(r));
513 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800514 msg_perr("FAILED: Cannot get the WP_RO region info: %d\n", rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800515 return 1;
516 }
517 if ((!start && !len) || /* list supported ranges */
518 ((start == r.offset) && (len == r.size))) {
519 /* pass */
520 } else {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800521 msg_perr("FAILED: Unsupported write protection range "
522 "(0x%06x,0x%06x)\n\n", start, len);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800523 msg_perr("Currently supported range:\n");
524 msg_perr(" disable: (0x%06x,0x%06x)\n", 0, 0);
525 msg_perr(" enable: (0x%06x,0x%06x)\n", r.offset, r.size);
526 return 1;
527 }
528
529 return set_wp(!!len);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800530}
531
532
David Hendricks1c09f802012-10-03 11:03:48 -0700533static int gec_enable_writeprotect(const struct flashchip *flash,
534 enum wp_mode wp_mode) {
535 int ret;
536
537 switch (wp_mode) {
538 case WP_MODE_HARDWARE:
539 ret = set_wp(1);
540 break;
541 default:
542 msg_perr("%s():%d Unsupported write-protection mode\n",
543 __func__, __LINE__);
544 ret = 1;
545 break;
546 }
547
548 return ret;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800549}
550
551
552static int gec_disable_writeprotect(const struct flashchip *flash) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800553 return set_wp(0);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800554}
555
556
557static int gec_wp_status(const struct flashchip *flash) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800558 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
559 struct ec_params_flash_protect p;
560 struct ec_response_flash_protect r;
561 int start, len; /* wp range */
562 int enabled;
563 int rc;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800564
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800565 memset(&p, 0, sizeof(p));
566 rc = priv->ec_command(EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT,
567 &p, sizeof(p), &r, sizeof(r));
568 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800569 msg_perr("FAILED: Cannot get the write protection status: %d\n",
570 rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800571 return 1;
572 } else if (rc < sizeof(r)) {
David Hendricksf797dde2012-10-30 11:39:12 -0700573 msg_perr("FAILED: Too little data returned (expected:%zd, "
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800574 "actual:%d)\n", sizeof(r), rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800575 return 1;
576 }
577
578 start = len = 0;
579 if (r.flags & EC_FLASH_PROTECT_RO_AT_BOOT) {
580 struct ec_params_flash_region_info reg_p;
581 struct ec_response_flash_region_info reg_r;
582
583 msg_pdbg("%s(): EC_FLASH_PROTECT_RO_AT_BOOT is set.\n",
584 __func__);
585 reg_p.region = EC_FLASH_REGION_WP_RO;
586 rc = priv->ec_command(EC_CMD_FLASH_REGION_INFO,
587 EC_VER_FLASH_REGION_INFO,
588 &reg_p, sizeof(reg_p), &reg_r, sizeof(reg_r));
589 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800590 msg_perr("FAILED: Cannot get the WP_RO region info: "
591 "%d\n", rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800592 return 1;
593 }
594 start = reg_r.offset;
595 len = reg_r.size;
596 } else {
597 msg_pdbg("%s(): EC_FLASH_PROTECT_RO_AT_BOOT is clear.\n",
598 __func__);
599 }
600
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800601 /*
602 * If neither RO_NOW or ALL_NOW is set, it means write protect is
603 * NOT active now.
604 */
605 if (!(r.flags & (EC_FLASH_PROTECT_RO_NOW | EC_FLASH_PROTECT_ALL_NOW)))
606 start = len = 0;
607
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800608 /* Remove the SPI-style messages. */
609 enabled = r.flags & EC_FLASH_PROTECT_RO_AT_BOOT ? 1 : 0;
610 msg_pinfo("WP: status: 0x%02x\n", enabled ? 0x80 : 0x00);
611 msg_pinfo("WP: status.srp0: %x\n", enabled);
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800612 msg_pinfo("WP: write protect is %s.\n",
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800613 enabled ? "enabled" : "disabled");
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800614 msg_pinfo("WP: write protect range: start=0x%08x, len=0x%08x\n",
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800615 start, len);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800616
617 return 0;
618}
619
620
David Hendricks7cfbd022012-05-20 17:25:51 -0700621int gec_probe_size(struct flashchip *flash) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800622 int rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800623 struct ec_response_flash_info info;
David Hendricks7cfbd022012-05-20 17:25:51 -0700624 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800625 struct block_eraser *eraser;
626 static struct wp wp = {
627 .list_ranges = gec_list_ranges,
628 .set_range = gec_set_range,
629 .enable = gec_enable_writeprotect,
630 .disable = gec_disable_writeprotect,
631 .wp_status = gec_wp_status,
632 };
633
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800634 rc = priv->ec_command(EC_CMD_FLASH_INFO, 0,
David Hendricks7cfbd022012-05-20 17:25:51 -0700635 NULL, 0, &info, sizeof(info));
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800636 if (rc < 0) {
637 msg_perr("%s(): FLASH_INFO returns %d.\n", __func__, rc);
638 return 0;
639 }
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800640
641 flash->total_size = info.flash_size / 1024;
David Hendricks0d3fcb52012-07-08 18:37:43 -0700642 flash->page_size = 64;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800643 flash->tested = TEST_OK_PREW;
644 eraser = &flash->block_erasers[0];
645 eraser->eraseblocks[0].size = info.erase_block_size;
646 eraser->eraseblocks[0].count = info.flash_size /
647 eraser->eraseblocks[0].size;
648 flash->wp = &wp;
649
650 return 1;
651};