blob: c770111683be2e4ed18d718ef389e97b244d8cb6 [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
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +080049/* 1 if we want the flashrom to try jumping to new firmware after update. */
50static int try_latest_firmware = 0;
51
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080052/* The range of each firmware copy from the image file to update.
53 * But re-define the .flags as the valid flag to indicate the firmware is
54 * new or not (if flags = 1).
55 */
56static struct fmap_area fwcopy[4]; // [0] is not used.
57
58/* The names of enum lpc_current_image to match in FMAP area names. */
David Hendricksbf8c4dd2012-07-19 12:13:17 -070059static const char *sections[3] = {
60 "UNKNOWN SECTION", // EC_IMAGE_UNKNOWN -- never matches
61 "EC_RO",
62 "EC_RW",
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080063};
64
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080065
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080066/* Given the range not able to update, mark the corresponding
67 * firmware as old.
68 */
69static void gec_invalidate_copy(unsigned int addr, unsigned int len)
70{
71 int i;
72
73 for (i = EC_LPC_IMAGE_RO; i < ARRAY_SIZE(fwcopy); i++) {
74 struct fmap_area *fw = &fwcopy[i];
75 if ((addr >= fw->offset && (addr < fw->offset + fw->size)) ||
76 (fw->offset >= addr && (fw->offset < addr + len))) {
77 msg_pdbg("Mark firmware [%s] as old.\n",
78 sections[i]);
79 fw->flags = 0; // mark as old
80 }
81 }
82}
83
84
85/* Asks EC to jump to a firmware copy. If target is EC_LPC_IMAGE_UNKNOWN,
86 * then this functions picks a NEW firmware copy and jumps to it. Note that
87 * RO is preferred, then A, finally B.
88 *
89 * Returns 0 for success.
90 */
91static int gec_jump_copy(enum lpc_current_image target) {
92 struct lpc_params_reboot_ec p;
David Hendricks7cfbd022012-05-20 17:25:51 -070093 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080094 int rc;
95
Louis Yung-Chieh Lobb128ba2012-05-04 22:57:44 +080096 memset(&p, 0, sizeof(p));
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080097 p.target = target != EC_LPC_IMAGE_UNKNOWN ? target :
98 fwcopy[EC_LPC_IMAGE_RO].flags ? EC_LPC_IMAGE_RO :
David Hendricksbf8c4dd2012-07-19 12:13:17 -070099 fwcopy[EC_LPC_IMAGE_RW].flags ? EC_LPC_IMAGE_RW :
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800100 EC_LPC_IMAGE_UNKNOWN;
101 msg_pdbg("GEC is jumping to [%s]\n", sections[p.target]);
102 if (p.target == EC_LPC_IMAGE_UNKNOWN) return 1;
103
David Hendricks7cfbd022012-05-20 17:25:51 -0700104 rc = priv->ec_command(EC_LPC_COMMAND_REBOOT_EC,
105 &p, sizeof(p), NULL, 0);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800106 if (rc) {
107 msg_perr("GEC cannot jump to [%s]\n", sections[p.target]);
108 } else {
109 msg_pdbg("GEC has jumped to [%s]\n", sections[p.target]);
110 }
111
112 /* Sleep 1 sec to wait the EC re-init. */
113 usleep(1000000);
114
115 return rc;
116}
117
118
119/* Given an image, this function parses FMAP and recognize the firmware
120 * ranges.
121 */
122int gec_prepare(uint8_t *image, int size) {
David Hendricks7cfbd022012-05-20 17:25:51 -0700123 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800124 struct fmap *fmap;
125 int i, j;
126
Louis Yung-Chieh Lo0eaa0ca2012-05-29 15:28:58 +0800127 if (!(priv && priv->detected)) return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800128
129 // Parse the fmap in the image file and cache the firmware ranges.
130 fmap = fmap_find_in_memory(image, size);
131 if (!fmap) return 0;
132
133 // Lookup RO/A/B sections in FMAP.
134 for (i = 0; i < fmap->nareas; i++) {
135 struct fmap_area *fa = &fmap->areas[i];
136 for (j = EC_LPC_IMAGE_RO; j < ARRAY_SIZE(sections); j++) {
David Hendricks5b06c882012-05-20 18:27:25 -0700137 if (!strcmp(sections[j], (const char *)fa->name)) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800138 msg_pdbg("Found '%s' in image.\n", fa->name);
139 memcpy(&fwcopy[j], fa, sizeof(*fa));
140 fwcopy[j].flags = 1; // mark as new
141 }
142 }
143 }
144
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800145 /* Warning: before update, we jump the EC to RO copy. If you want to
146 * change this behavior, please also check the gec_finish().
147 */
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800148 return gec_jump_copy(EC_LPC_IMAGE_RO);
149}
150
151
152/* Returns >0 if we need 2nd pass of erase_and_write_flash().
153 * <0 if we cannot jump to any firmware copy.
154 * ==0 if no more pass is needed.
155 *
156 * This function also jumps to new-updated firmware copy before return >0.
157 */
158int gec_need_2nd_pass(void) {
David Hendricks7cfbd022012-05-20 17:25:51 -0700159 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
160
Louis Yung-Chieh Lo0eaa0ca2012-05-29 15:28:58 +0800161 if (!(priv && priv->detected)) return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800162
163 if (need_2nd_pass) {
164 if (gec_jump_copy(EC_LPC_IMAGE_UNKNOWN)) {
165 return -1;
166 }
167 }
168
169 return need_2nd_pass;
170}
171
172
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800173/* Returns 0 for success.
174 *
175 * Try latest firmware: B > A > RO
176 *
177 * This function assumes the EC jumps to RO at gec_prepare() so that
178 * the fwcopy[RO].flags is old (0) and A/B are new. Please also refine
179 * this code logic if you change the gec_prepare() behavior.
180 */
181int gec_finish(void) {
182 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
183
184 if (!(priv && priv->detected)) return 0;
185
186 if (try_latest_firmware) {
David Hendricksbf8c4dd2012-07-19 12:13:17 -0700187 if (fwcopy[EC_LPC_IMAGE_RW].flags &&
188 gec_jump_copy(EC_LPC_IMAGE_RW) == 0) return 0;
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800189 return gec_jump_copy(EC_LPC_IMAGE_RO);
190 }
191
192 return 0;
193}
194
195
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800196int gec_read(struct flashchip *flash, uint8_t *readarr,
197 unsigned int blockaddr, unsigned int readcnt) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800198 int rc = 0;
199 struct lpc_params_flash_read p;
200 struct lpc_response_flash_read r;
David Hendricks7cfbd022012-05-20 17:25:51 -0700201 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
David Hendricksd6a0f662012-05-29 14:39:50 -0700202 int maxlen = opaque_programmer->max_data_read;
David Hendricks133083b2012-07-17 20:39:38 -0700203 int offset = 0, count;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800204
David Hendricks133083b2012-07-17 20:39:38 -0700205 while (offset < readcnt) {
206 count = min(maxlen, readcnt - offset);
207 p.offset = blockaddr + offset;
208 p.size = count;
David Hendricks7cfbd022012-05-20 17:25:51 -0700209 rc = priv->ec_command(EC_LPC_COMMAND_FLASH_READ,
David Hendricks133083b2012-07-17 20:39:38 -0700210 &p, sizeof(p), &r, count);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800211 if (rc) {
212 msg_perr("GEC: Flash read error at offset 0x%x\n",
David Hendricks133083b2012-07-17 20:39:38 -0700213 blockaddr + offset);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800214 return rc;
215 }
216
David Hendricks133083b2012-07-17 20:39:38 -0700217 memcpy(readarr + offset, r.data, count);
218 offset += count;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800219 }
220
221 return rc;
222}
223
224
David Hendricks7cfbd022012-05-20 17:25:51 -0700225int gec_block_erase(struct flashchip *flash,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800226 unsigned int blockaddr,
227 unsigned int len) {
228 struct lpc_params_flash_erase erase;
David Hendricks7cfbd022012-05-20 17:25:51 -0700229 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800230 int rc;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800231
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800232 erase.offset = blockaddr;
233 erase.size = len;
David Hendricks7cfbd022012-05-20 17:25:51 -0700234 rc = priv->ec_command(EC_LPC_COMMAND_FLASH_ERASE,
235 &erase, sizeof(erase), NULL, 0);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800236 if (rc == EC_LPC_RESULT_ACCESS_DENIED) {
237 // this is active image.
238 gec_invalidate_copy(blockaddr, len);
239 need_2nd_pass = 1;
240 return ACCESS_DENIED;
241 }
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800242 if (rc) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800243 msg_perr("GEC: Flash erase error at address 0x%x, rc=%d\n",
244 blockaddr, rc);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800245 return rc;
246 }
247
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800248 try_latest_firmware = 1;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800249 return rc;
250}
251
252
253int gec_write(struct flashchip *flash, uint8_t *buf, unsigned int addr,
254 unsigned int nbytes) {
255 int i, rc = 0;
256 unsigned int written = 0;
257 struct lpc_params_flash_write p;
David Hendricks7cfbd022012-05-20 17:25:51 -0700258 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
David Hendricksd6a0f662012-05-29 14:39:50 -0700259 int maxlen = opaque_programmer->max_data_write;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800260
261 for (i = 0; i < nbytes; i += written) {
David Hendricksd6a0f662012-05-29 14:39:50 -0700262 written = min(nbytes - i, maxlen);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800263 p.offset = addr + i;
264 p.size = written;
265 memcpy(p.data, &buf[i], written);
David Hendricks7cfbd022012-05-20 17:25:51 -0700266 rc = priv->ec_command(EC_LPC_COMMAND_FLASH_WRITE,
267 &p, sizeof(p), NULL, 0);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800268 if (rc == EC_LPC_RESULT_ACCESS_DENIED) {
269 // this is active image.
270 gec_invalidate_copy(addr, nbytes);
271 need_2nd_pass = 1;
272 return ACCESS_DENIED;
273 }
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800274
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800275 if (rc) break;
276 }
277
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800278 try_latest_firmware = 1;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800279 return rc;
280}
281
282
283static int gec_list_ranges(const struct flashchip *flash) {
284 msg_pinfo("You can specify any range:\n");
285 msg_pinfo(" from: 0x%06x, to: 0x%06x\n", 0, flash->total_size * 1024);
Randall Spangler6e160ef2012-07-18 09:36:25 -0700286 msg_pinfo(" unit: 0x%06x (%dKB)\n", 2048, 2);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800287 return 0;
288}
289
290
291static int gec_set_range(const struct flashchip *flash,
292 unsigned int start, unsigned int len) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800293
Randall Spangler6e160ef2012-07-18 09:36:25 -0700294 /* TODO: update to latest ec_commands.h and reimplement. */
295 msg_perr("GEC: set_range unimplemented\n");
296 return -1;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800297}
298
299
300static int gec_enable_writeprotect(const struct flashchip *flash) {
Randall Spangler6e160ef2012-07-18 09:36:25 -0700301 /* TODO: update to latest ec_commands.h and reimplement. */
302 msg_perr("GEC: enable_writeprotect unimplemented\n");
303 return -1;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800304}
305
306
307static int gec_disable_writeprotect(const struct flashchip *flash) {
Randall Spangler6e160ef2012-07-18 09:36:25 -0700308 /* TODO: update to latest ec_commands.h and reimplement. */
309 msg_perr("GEC: disable_writeprotect unimplemented\n");
310 return -1;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800311}
312
313
314static int gec_wp_status(const struct flashchip *flash) {
Randall Spangler6e160ef2012-07-18 09:36:25 -0700315 /*
316 * TODO: update to latest ec_commands.h and reimplement. For now,
317 * just claim chip is unprotected.
318 */
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800319
320 /* TODO: Fix scripts which rely on SPI-specific terminology. */
Randall Spangler6e160ef2012-07-18 09:36:25 -0700321 msg_pinfo("WP: status: 0x%02x\n", 0);
322 msg_pinfo("WP: status.srp0: %x\n", 0);
323 msg_pinfo("WP: write protect is %s.\n", "disabled");
324 msg_pinfo("WP: write protect range: start=0x%08x, len=0x%08x\n", 0, 0);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800325
326 return 0;
327}
328
329
David Hendricks7cfbd022012-05-20 17:25:51 -0700330int gec_probe_size(struct flashchip *flash) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800331 int rc;
332 struct lpc_response_flash_info info;
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 struct block_eraser *eraser;
335 static struct wp wp = {
336 .list_ranges = gec_list_ranges,
337 .set_range = gec_set_range,
338 .enable = gec_enable_writeprotect,
339 .disable = gec_disable_writeprotect,
340 .wp_status = gec_wp_status,
341 };
342
David Hendricks7cfbd022012-05-20 17:25:51 -0700343 rc = priv->ec_command(EC_LPC_COMMAND_FLASH_INFO,
344 NULL, 0, &info, sizeof(info));
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800345 if (rc) return 0;
346
347 flash->total_size = info.flash_size / 1024;
David Hendricks0d3fcb52012-07-08 18:37:43 -0700348 flash->page_size = 64;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800349 flash->tested = TEST_OK_PREW;
350 eraser = &flash->block_erasers[0];
351 eraser->eraseblocks[0].size = info.erase_block_size;
352 eraser->eraseblocks[0].count = info.flash_size /
353 eraser->eraseblocks[0].size;
354 flash->wp = &wp;
355
356 return 1;
357};