blob: a65d37712aee23707bab8f95134182e2026a5357 [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 Loedb0cba2011-12-09 17:06:54 +080041#include "gec_lpc_commands.h"
42#include "programmer.h"
43#include "spi.h"
44#include "writeprotect.h"
45
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080046/* 1 if we want the flashrom to call erase_and_write_flash() again. */
47static int need_2nd_pass = 0;
48
49/* The range of each firmware copy from the image file to update.
50 * But re-define the .flags as the valid flag to indicate the firmware is
51 * new or not (if flags = 1).
52 */
53static struct fmap_area fwcopy[4]; // [0] is not used.
54
55/* The names of enum lpc_current_image to match in FMAP area names. */
56static const char *sections[4] = {
57 "UNKNOWN SECTION", // EC_LPC_IMAGE_UNKNOWN -- never matches
58 "RO_SECTION", // EC_LPC_IMAGE_RO
59 "RW_SECTION_A", // EC_LPC_IMAGE_RW_A
60 "RW_SECTION_B", // EC_LPC_IMAGE_RW_B
61};
62
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080063
64#ifdef SUPPORT_CHECKSUM
65static verify_checksum(uint8_t* expected,
66 unsigned int addr,
67 unsigned int count) {
68 int rc;
69 struct lpc_params_flash_checksum csp;
70 struct lpc_response_flash_checksum csr;
David Hendricks7cfbd022012-05-20 17:25:51 -070071 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080072 uint8_t cs;
73 int j;
74
75 csp.offset = addr;
76 csp.size = count;
77
David Hendricks7cfbd022012-05-20 17:25:51 -070078 rc = priv->ec_command(EC_LPC_COMMAND_FLASH_CHECKSUM,
79 &csp, sizeof(csp), &csr, sizeof(csr));
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080080 if (rc) {
81 msg_perr("GEC: verify_checksum() error.\n");
82 return rc;
83 }
84
85 for (cs = 0, j = 0; j < count; ++j) {
86 BYTE_IN(cs, expected[j]);
87 }
88 if (cs != csr.checksum) {
89 msg_pdbg("GEC: checksum dismatch at 0x%02x "
90 "(ec: 0x%02x, local: 0x%02x). Retry.\n",
91 addr, csr.checksum, cs);
92 msg_pdbg("GEC: ");
93 for (j = 0; j < count; ++j) {
94 msg_pdbg("%02x-", expected[j]);
95 if ((j & 15) == 15) msg_pdbg("\nGEC: ");
96 }
97 programmer_delay(1000);
98 return 1;
99 }
100 return 0;
101}
102#endif /* SUPPORT_CHECKSUM */
103
104
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800105/* Given the range not able to update, mark the corresponding
106 * firmware as old.
107 */
108static void gec_invalidate_copy(unsigned int addr, unsigned int len)
109{
110 int i;
111
112 for (i = EC_LPC_IMAGE_RO; i < ARRAY_SIZE(fwcopy); i++) {
113 struct fmap_area *fw = &fwcopy[i];
114 if ((addr >= fw->offset && (addr < fw->offset + fw->size)) ||
115 (fw->offset >= addr && (fw->offset < addr + len))) {
116 msg_pdbg("Mark firmware [%s] as old.\n",
117 sections[i]);
118 fw->flags = 0; // mark as old
119 }
120 }
121}
122
123
124/* Asks EC to jump to a firmware copy. If target is EC_LPC_IMAGE_UNKNOWN,
125 * then this functions picks a NEW firmware copy and jumps to it. Note that
126 * RO is preferred, then A, finally B.
127 *
128 * Returns 0 for success.
129 */
130static int gec_jump_copy(enum lpc_current_image target) {
131 struct lpc_params_reboot_ec p;
David Hendricks7cfbd022012-05-20 17:25:51 -0700132 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800133 int rc;
134
Louis Yung-Chieh Lobb128ba2012-05-04 22:57:44 +0800135 memset(&p, 0, sizeof(p));
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800136 p.target = target != EC_LPC_IMAGE_UNKNOWN ? target :
137 fwcopy[EC_LPC_IMAGE_RO].flags ? EC_LPC_IMAGE_RO :
138 fwcopy[EC_LPC_IMAGE_RW_A].flags ? EC_LPC_IMAGE_RW_A :
139 fwcopy[EC_LPC_IMAGE_RW_B].flags ? EC_LPC_IMAGE_RW_B :
140 EC_LPC_IMAGE_UNKNOWN;
141 msg_pdbg("GEC is jumping to [%s]\n", sections[p.target]);
142 if (p.target == EC_LPC_IMAGE_UNKNOWN) return 1;
143
David Hendricks7cfbd022012-05-20 17:25:51 -0700144 rc = priv->ec_command(EC_LPC_COMMAND_REBOOT_EC,
145 &p, sizeof(p), NULL, 0);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800146 if (rc) {
147 msg_perr("GEC cannot jump to [%s]\n", sections[p.target]);
148 } else {
149 msg_pdbg("GEC has jumped to [%s]\n", sections[p.target]);
150 }
151
152 /* Sleep 1 sec to wait the EC re-init. */
153 usleep(1000000);
154
155 return rc;
156}
157
158
159/* Given an image, this function parses FMAP and recognize the firmware
160 * ranges.
161 */
162int gec_prepare(uint8_t *image, int size) {
David Hendricks7cfbd022012-05-20 17:25:51 -0700163 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800164 struct fmap *fmap;
165 int i, j;
166
David Hendricks7cfbd022012-05-20 17:25:51 -0700167 if (!priv->detected) return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800168
169 // Parse the fmap in the image file and cache the firmware ranges.
170 fmap = fmap_find_in_memory(image, size);
171 if (!fmap) return 0;
172
173 // Lookup RO/A/B sections in FMAP.
174 for (i = 0; i < fmap->nareas; i++) {
175 struct fmap_area *fa = &fmap->areas[i];
176 for (j = EC_LPC_IMAGE_RO; j < ARRAY_SIZE(sections); j++) {
David Hendricks5b06c882012-05-20 18:27:25 -0700177 if (!strcmp(sections[j], (const char *)fa->name)) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800178 msg_pdbg("Found '%s' in image.\n", fa->name);
179 memcpy(&fwcopy[j], fa, sizeof(*fa));
180 fwcopy[j].flags = 1; // mark as new
181 }
182 }
183 }
184
185 return gec_jump_copy(EC_LPC_IMAGE_RO);
186}
187
188
189/* Returns >0 if we need 2nd pass of erase_and_write_flash().
190 * <0 if we cannot jump to any firmware copy.
191 * ==0 if no more pass is needed.
192 *
193 * This function also jumps to new-updated firmware copy before return >0.
194 */
195int gec_need_2nd_pass(void) {
David Hendricks7cfbd022012-05-20 17:25:51 -0700196 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
197
198 if (!priv->detected) return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800199
200 if (need_2nd_pass) {
201 if (gec_jump_copy(EC_LPC_IMAGE_UNKNOWN)) {
202 return -1;
203 }
204 }
205
206 return need_2nd_pass;
207}
208
209
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800210int gec_read(struct flashchip *flash, uint8_t *readarr,
211 unsigned int blockaddr, unsigned int readcnt) {
212 int i;
213 int rc = 0;
214 struct lpc_params_flash_read p;
215 struct lpc_response_flash_read r;
David Hendricks7cfbd022012-05-20 17:25:51 -0700216 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800217
218 for (i = 0; i < readcnt; i += EC_LPC_FLASH_SIZE_MAX) {
219 p.offset = blockaddr + i;
220 p.size = min(readcnt - i, EC_LPC_FLASH_SIZE_MAX);
David Hendricks7cfbd022012-05-20 17:25:51 -0700221 rc = priv->ec_command(EC_LPC_COMMAND_FLASH_READ,
222 &p, sizeof(p), &r, sizeof(r));
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800223 if (rc) {
224 msg_perr("GEC: Flash read error at offset 0x%x\n",
225 blockaddr + i);
226 return rc;
227 }
228
229#ifdef SUPPORT_CHECKSUM
230 if (verify_checksum(r.data, blockaddr + i,
231 min(readcnt - i, EC_LPC_FLASH_SIZE_MAX))) {
232 msg_pdbg("GEC: re-read...\n");
233 i -= EC_LPC_FLASH_SIZE_MAX;
234 continue;
235 }
236#endif
237 memcpy(readarr + i, r.data, p.size);
238 }
239
240 return rc;
241}
242
243
David Hendricks7cfbd022012-05-20 17:25:51 -0700244int gec_block_erase(struct flashchip *flash,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800245 unsigned int blockaddr,
246 unsigned int len) {
247 struct lpc_params_flash_erase erase;
David Hendricks7cfbd022012-05-20 17:25:51 -0700248 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800249 int rc;
David Hendricks5b06c882012-05-20 18:27:25 -0700250#ifdef SUPPORT_CHECKSUM
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800251 uint8_t *blank;
David Hendricks5b06c882012-05-20 18:27:25 -0700252#endif
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800253
254#ifdef SUPPORT_CHECKSUM
255re_erase:
256#endif
257 erase.offset = blockaddr;
258 erase.size = len;
David Hendricks7cfbd022012-05-20 17:25:51 -0700259 rc = priv->ec_command(EC_LPC_COMMAND_FLASH_ERASE,
260 &erase, sizeof(erase), NULL, 0);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800261 if (rc == EC_LPC_RESULT_ACCESS_DENIED) {
262 // this is active image.
263 gec_invalidate_copy(blockaddr, len);
264 need_2nd_pass = 1;
265 return ACCESS_DENIED;
266 }
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800267 if (rc) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800268 msg_perr("GEC: Flash erase error at address 0x%x, rc=%d\n",
269 blockaddr, rc);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800270 return rc;
271 }
272
273#ifdef SUPPORT_CHECKSUM
274 blank = malloc(len);
275 memset(blank, 0xff, len);
276 if (verify_checksum(blank, blockaddr, len)) {
277 msg_pdbg("GEC: Re-erase...\n");
278 goto re_erase;
279 }
280#endif
281
282 return rc;
283}
284
285
286int gec_write(struct flashchip *flash, uint8_t *buf, unsigned int addr,
287 unsigned int nbytes) {
288 int i, rc = 0;
289 unsigned int written = 0;
290 struct lpc_params_flash_write p;
David Hendricks7cfbd022012-05-20 17:25:51 -0700291 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800292
293 for (i = 0; i < nbytes; i += written) {
294 written = min(nbytes - i, EC_LPC_FLASH_SIZE_MAX);
295 p.offset = addr + i;
296 p.size = written;
297 memcpy(p.data, &buf[i], written);
David Hendricks7cfbd022012-05-20 17:25:51 -0700298 rc = priv->ec_command(EC_LPC_COMMAND_FLASH_WRITE,
299 &p, sizeof(p), NULL, 0);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800300 if (rc == EC_LPC_RESULT_ACCESS_DENIED) {
301 // this is active image.
302 gec_invalidate_copy(addr, nbytes);
303 need_2nd_pass = 1;
304 return ACCESS_DENIED;
305 }
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800306
307#ifdef SUPPORT_CHECKSUM
308 if (verify_checksum(&buf[i], addr + i, written)) {
309 msg_pdbg("GEC: re-write...\n");
310 i -= written;
311 continue;
312 }
313#endif
314
315 if (rc) break;
316 }
317
318 return rc;
319}
320
321
322static int gec_list_ranges(const struct flashchip *flash) {
323 msg_pinfo("You can specify any range:\n");
324 msg_pinfo(" from: 0x%06x, to: 0x%06x\n", 0, flash->total_size * 1024);
325 msg_pinfo(" unit: 0x%06x (%dKB)\n", 2048, 2048);
326 return 0;
327}
328
329
330static int gec_set_range(const struct flashchip *flash,
331 unsigned int start, unsigned int len) {
332 struct lpc_params_flash_wp_range p;
David Hendricks7cfbd022012-05-20 17:25:51 -0700333 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800334 int rc;
335
336 p.offset = start;
337 p.size = len;
David Hendricks7cfbd022012-05-20 17:25:51 -0700338 rc = priv->ec_command(EC_LPC_COMMAND_FLASH_WP_SET_RANGE,
339 &p, sizeof(p), NULL, 0);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800340 if (rc) {
341 msg_perr("GEC: wp_set_range error: rc=%d\n", rc);
342 return rc;
343 }
344
345 return 0;
346}
347
348
349static int gec_enable_writeprotect(const struct flashchip *flash) {
350 struct lpc_params_flash_wp_enable p;
David Hendricks7cfbd022012-05-20 17:25:51 -0700351 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800352 int rc;
353
354 p.enable_wp = 1;
David Hendricks7cfbd022012-05-20 17:25:51 -0700355 rc = priv->ec_command(EC_LPC_COMMAND_FLASH_WP_ENABLE,
356 &p, sizeof(p), NULL, 0);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800357 if (rc) {
358 msg_perr("GEC: wp_enable_wp error: rc=%d\n", rc);
359 }
360
361 return rc;
362}
363
364
365static int gec_disable_writeprotect(const struct flashchip *flash) {
366 struct lpc_params_flash_wp_enable p;
David Hendricks7cfbd022012-05-20 17:25:51 -0700367 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800368 int rc;
369
370 p.enable_wp = 0;
David Hendricks7cfbd022012-05-20 17:25:51 -0700371 rc = priv->ec_command(EC_LPC_COMMAND_FLASH_WP_ENABLE,
372 &p, sizeof(p), NULL, 0);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800373 if (rc) {
374 msg_perr("GEC: wp_disable_wp error: rc=%d\n", rc);
375 } else {
376 msg_pinfo("Disabled WP. Reboot EC and de-assert #WP.\n");
377 }
378
379 return rc;
380}
381
382
383static int gec_wp_status(const struct flashchip *flash) {
384 int rc;
385 struct lpc_response_flash_wp_range range;
386 struct lpc_response_flash_wp_enable en;
David Hendricks7cfbd022012-05-20 17:25:51 -0700387 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800388 uint8_t value;
389
David Hendricks7cfbd022012-05-20 17:25:51 -0700390 rc = priv->ec_command(EC_LPC_COMMAND_FLASH_WP_GET_RANGE,
391 NULL, 0, &range, sizeof(range));
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800392 if (rc) {
393 msg_perr("GEC: wp_get_wp_range error: rc=%d\n", rc);
394 return rc;
395 }
David Hendricks7cfbd022012-05-20 17:25:51 -0700396 rc = priv->ec_command(EC_LPC_COMMAND_FLASH_WP_GET_STATE,
397 NULL, 0, &en, sizeof(en));
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800398 if (rc) {
399 msg_perr("GEC: wp_get_wp_state error: rc=%d\n", rc);
400 return rc;
401 }
402
403 /* TODO: Fix scripts which rely on SPI-specific terminology. */
404 value = (en.enable_wp << 7);
405 msg_pinfo("WP: status: 0x%02x\n", value);
406 msg_pinfo("WP: status.srp0: %x\n", en.enable_wp);
407 msg_pinfo("WP: write protect is %s.\n",
408 en.enable_wp ? "enabled" : "disabled");
409 msg_pinfo("WP: write protect range: start=0x%08x, len=0x%08x\n",
410 range.offset, range.size);
411
412 return 0;
413}
414
415
David Hendricks7cfbd022012-05-20 17:25:51 -0700416int gec_probe_size(struct flashchip *flash) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800417 int rc;
418 struct lpc_response_flash_info info;
David Hendricks7cfbd022012-05-20 17:25:51 -0700419 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800420 struct block_eraser *eraser;
421 static struct wp wp = {
422 .list_ranges = gec_list_ranges,
423 .set_range = gec_set_range,
424 .enable = gec_enable_writeprotect,
425 .disable = gec_disable_writeprotect,
426 .wp_status = gec_wp_status,
427 };
428
David Hendricks7cfbd022012-05-20 17:25:51 -0700429 rc = priv->ec_command(EC_LPC_COMMAND_FLASH_INFO,
430 NULL, 0, &info, sizeof(info));
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800431 if (rc) return 0;
432
433 flash->total_size = info.flash_size / 1024;
434 flash->page_size = min(info.write_block_size,
435 info.erase_block_size);
436 flash->tested = TEST_OK_PREW;
437 eraser = &flash->block_erasers[0];
438 eraser->eraseblocks[0].size = info.erase_block_size;
439 eraser->eraseblocks[0].count = info.flash_size /
440 eraser->eraseblocks[0].size;
441 flash->wp = &wp;
442
443 return 1;
444};