blob: 51052b1c0825d0ced4d66590245e10080ea33442 [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 */
David Hendricks14935fe2014-08-14 17:38:24 -070034#include <errno.h>
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080035#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 Hendricksa5c5cf82014-08-11 16:40:17 -070041#include "cros_ec.h"
42#include "cros_ec_lock.h"
43#include "cros_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. */
Gwendal Grignou94e87d62014-11-25 15:34:15 -080078static const char *sections[] = {
David Hendricksbf8c4dd2012-07-19 12:13:17 -070079 "UNKNOWN SECTION", // EC_IMAGE_UNKNOWN -- never matches
80 "EC_RO",
81 "EC_RW",
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080082};
83
Gwendal Grignou94e87d62014-11-25 15:34:15 -080084/*
85 * The names of the different device that can be found in a machine.
86 * Order is important: for backward compatibilty issue,
87 * 'ec' must be 0, 'pd' must be 1.
88 */
89static const char *ec_type[] = {
90 [0] = "ec",
91 [1] = "pd",
92 [2] = "sh",
93};
94
Simon Glassc453a642013-07-01 18:08:53 +090095/* EC_FLASH_REGION_WP_RO is the highest numbered region so it also indicates
96 * the number of regions */
97static struct ec_response_flash_region_info regions[EC_FLASH_REGION_WP_RO + 1];
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080098
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080099/* Given the range not able to update, mark the corresponding
100 * firmware as old.
101 */
David Hendricksb907de32014-08-11 16:47:09 -0700102static void cros_ec_invalidate_copy(unsigned int addr, unsigned int len)
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800103{
104 int i;
105
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800106 for (i = EC_IMAGE_RO; i < ARRAY_SIZE(fwcopy); i++) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800107 struct fmap_area *fw = &fwcopy[i];
108 if ((addr >= fw->offset && (addr < fw->offset + fw->size)) ||
109 (fw->offset >= addr && (fw->offset < addr + len))) {
110 msg_pdbg("Mark firmware [%s] as old.\n",
111 sections[i]);
112 fw->flags = 0; // mark as old
113 }
114 }
115}
116
117
David Hendricksd13d90d2016-08-09 17:00:52 -0700118static int cros_ec_get_current_image(struct cros_ec_priv *priv)
Simon Glass01c11672013-07-01 18:03:33 +0900119{
120 struct ec_response_get_version resp;
121 int rc;
David Hendricksac1d25c2016-08-09 17:00:58 -0700122
David Hendricksd13d90d2016-08-09 17:00:52 -0700123 rc = priv->ec_command(EC_CMD_GET_VERSION,
David Hendricks14935fe2014-08-14 17:38:24 -0700124 0, NULL, 0, &resp, sizeof(resp));
Simon Glass01c11672013-07-01 18:03:33 +0900125 if (rc < 0) {
David Hendricksb907de32014-08-11 16:47:09 -0700126 msg_perr("CROS_EC cannot get the running copy: rc=%d\n", rc);
Simon Glass01c11672013-07-01 18:03:33 +0900127 return rc;
128 }
129 if (resp.current_image == EC_IMAGE_UNKNOWN) {
David Hendricksb907de32014-08-11 16:47:09 -0700130 msg_perr("CROS_EC gets unknown running copy\n");
Simon Glass01c11672013-07-01 18:03:33 +0900131 return -1;
132 }
133
134 return resp.current_image;
135}
136
137
David Hendricksd13d90d2016-08-09 17:00:52 -0700138static int cros_ec_get_region_info(struct cros_ec_priv *priv,
139 enum ec_flash_region region,
Simon Glass3c01dca2013-07-01 18:07:34 +0900140 struct ec_response_flash_region_info *info)
141{
142 struct ec_params_flash_region_info req;
143 struct ec_response_flash_region_info resp;
144 int rc;
145
146 req.region = region;
David Hendricksd13d90d2016-08-09 17:00:52 -0700147 rc = priv->ec_command(EC_CMD_FLASH_REGION_INFO,
Simon Glass3c01dca2013-07-01 18:07:34 +0900148 EC_VER_FLASH_REGION_INFO, &req, sizeof(req),
149 &resp, sizeof(resp));
150 if (rc < 0) {
151 msg_perr("Cannot get the WP_RO region info: %d\n", rc);
152 return rc;
153 }
154
155 info->offset = resp.offset;
156 info->size = resp.size;
157 return 0;
158}
159
David Hendricksf9461c72013-07-11 19:02:13 -0700160/**
161 * Get the versions of the command supported by the EC.
162 *
163 * @param cmd Command
164 * @param pmask Destination for version mask; will be set to 0 on
165 * error.
166 * @return 0 if success, <0 if error
167 */
David Hendricksac1d25c2016-08-09 17:00:58 -0700168static int ec_get_cmd_versions(int cmd, uint32_t *pmask)
David Hendricksf9461c72013-07-11 19:02:13 -0700169{
David Hendricksac1d25c2016-08-09 17:00:58 -0700170 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
David Hendricksf9461c72013-07-11 19:02:13 -0700171 struct ec_params_get_cmd_versions pver;
172 struct ec_response_get_cmd_versions rver;
173 int rc;
174
175 *pmask = 0;
176
177 pver.cmd = cmd;
David Hendricksd13d90d2016-08-09 17:00:52 -0700178 rc = priv->ec_command(EC_CMD_GET_CMD_VERSIONS, 0,
David Hendricksf9461c72013-07-11 19:02:13 -0700179 &pver, sizeof(pver), &rver, sizeof(rver));
180
181 if (rc < 0)
182 return rc;
183
184 *pmask = rver.version_mask;
185 return rc;
186}
187
188/**
189 * Return non-zero if the EC supports the command and version
190 *
191 * @param cmd Command to check
192 * @param ver Version to check
193 * @return non-zero if command version supported; 0 if not.
194 */
David Hendricksac1d25c2016-08-09 17:00:58 -0700195static int ec_cmd_version_supported(int cmd, int ver)
David Hendricksf9461c72013-07-11 19:02:13 -0700196{
197 uint32_t mask = 0;
198 int rc;
David Hendricksd13d90d2016-08-09 17:00:52 -0700199
David Hendricksac1d25c2016-08-09 17:00:58 -0700200 rc = ec_get_cmd_versions(cmd, &mask);
David Hendricksf9461c72013-07-11 19:02:13 -0700201 if (rc < 0)
202 return rc;
203
204 return (mask & EC_VER_MASK(ver)) ? 1 : 0;
205}
206
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700207/* returns 0 if successful or <0 to indicate error */
David Hendricksd13d90d2016-08-09 17:00:52 -0700208static int set_ideal_write_size(struct cros_ec_priv *priv)
David Hendricksf9461c72013-07-11 19:02:13 -0700209{
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700210 int cmd_version, ret;
David Hendricksf9461c72013-07-11 19:02:13 -0700211
David Hendricksac1d25c2016-08-09 17:00:58 -0700212 cmd_version = ec_cmd_version_supported(EC_CMD_FLASH_WRITE,
David Hendricksfb405f12014-08-19 22:42:30 -0700213 EC_VER_FLASH_WRITE);
214 if (cmd_version < 0) {
215 msg_perr("Cannot determine write command version\n");
216 return cmd_version;
217 } else if (cmd_version == 0) {
218 struct ec_response_flash_info info;
David Hendricksf9461c72013-07-11 19:02:13 -0700219
David Hendricksd13d90d2016-08-09 17:00:52 -0700220 ret = priv->ec_command(EC_CMD_FLASH_INFO,
David Hendricksfb405f12014-08-19 22:42:30 -0700221 cmd_version, NULL, 0, &info, sizeof(info));
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700222 if (ret < 0) {
David Hendricksfb405f12014-08-19 22:42:30 -0700223 msg_perr("%s(): Cannot get flash info.\n", __func__);
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700224 return ret;
David Hendricksfb405f12014-08-19 22:42:30 -0700225 }
226
David Hendricksd13d90d2016-08-09 17:00:52 -0700227 priv->ideal_write_size = EC_FLASH_WRITE_VER0_SIZE;
David Hendricksfb405f12014-08-19 22:42:30 -0700228 } else {
229 struct ec_response_flash_info_1 info;
230
David Hendricksd13d90d2016-08-09 17:00:52 -0700231 ret = priv->ec_command(EC_CMD_FLASH_INFO,
David Hendricksfb405f12014-08-19 22:42:30 -0700232 cmd_version, NULL, 0, &info, sizeof(info));
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700233 if (ret < 0) {
David Hendricksfb405f12014-08-19 22:42:30 -0700234 msg_perr("%s(): Cannot get flash info.\n", __func__);
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700235 return ret;
David Hendricksfb405f12014-08-19 22:42:30 -0700236 }
237
David Hendricksd13d90d2016-08-09 17:00:52 -0700238 priv->ideal_write_size = info.write_ideal_size;
David Hendricksf9461c72013-07-11 19:02:13 -0700239 }
240
David Hendricksfb405f12014-08-19 22:42:30 -0700241 return 0;
David Hendricksf9461c72013-07-11 19:02:13 -0700242}
Simon Glass3c01dca2013-07-01 18:07:34 +0900243
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800244/* Asks EC to jump to a firmware copy. If target is EC_IMAGE_UNKNOWN,
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800245 * then this functions picks a NEW firmware copy and jumps to it. Note that
246 * RO is preferred, then A, finally B.
247 *
248 * Returns 0 for success.
249 */
David Hendricksac1d25c2016-08-09 17:00:58 -0700250static int cros_ec_jump_copy(enum ec_current_image target) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800251 struct ec_params_reboot_ec p;
David Hendricksac1d25c2016-08-09 17:00:58 -0700252 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800253 int rc;
Vadim Bendebury9fa26e82013-09-19 13:56:32 -0700254 int current_image;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800255
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800256 /* Since the EC may return EC_RES_SUCCESS twice if the EC doesn't
257 * jump to different firmware copy. The second EC_RES_SUCCESS would
258 * set the OBF=1 and the next command cannot be executed.
259 * Thus, we call EC to jump only if the target is different.
260 */
David Hendricksd13d90d2016-08-09 17:00:52 -0700261 current_image = cros_ec_get_current_image(priv);
Vadim Bendebury9fa26e82013-09-19 13:56:32 -0700262 if (current_image < 0)
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800263 return 1;
Vadim Bendebury9fa26e82013-09-19 13:56:32 -0700264 if (current_image == target)
Simon Glassc453a642013-07-01 18:08:53 +0900265 return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800266
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800267 memset(&p, 0, sizeof(p));
Simon Glassc453a642013-07-01 18:08:53 +0900268
269 /* Translate target --> EC reboot command parameter */
270 switch (target) {
271 case EC_IMAGE_RO:
272 p.cmd = EC_REBOOT_JUMP_RO;
273 break;
274 case EC_IMAGE_RW:
275 p.cmd = EC_REBOOT_JUMP_RW;
276 break;
277 default:
278 /*
279 * If target is unspecified, set EC reboot command to use
280 * a new image. Also set "target" so that it may be used
281 * to update the priv->current_image if jump is successful.
282 */
283 if (fwcopy[EC_IMAGE_RO].flags) {
284 p.cmd = EC_REBOOT_JUMP_RO;
285 target = EC_IMAGE_RO;
286 } else if (fwcopy[EC_IMAGE_RW].flags) {
287 p.cmd = EC_REBOOT_JUMP_RW;
288 target = EC_IMAGE_RW;
289 } else {
290 p.cmd = EC_IMAGE_UNKNOWN;
291 }
292 break;
293 }
294
David Hendricksb907de32014-08-11 16:47:09 -0700295 msg_pdbg("CROS_EC is jumping to [%s]\n", sections[p.cmd]);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800296 if (p.cmd == EC_IMAGE_UNKNOWN) return 1;
297
Vadim Bendebury9fa26e82013-09-19 13:56:32 -0700298 if (current_image == p.cmd) {
David Hendricksb907de32014-08-11 16:47:09 -0700299 msg_pdbg("CROS_EC is already in [%s]\n", sections[p.cmd]);
David Hendricksd13d90d2016-08-09 17:00:52 -0700300 priv->current_image = target;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800301 return 0;
302 }
303
David Hendricksd13d90d2016-08-09 17:00:52 -0700304 rc = priv->ec_command(EC_CMD_REBOOT_EC,
David Hendricks14935fe2014-08-14 17:38:24 -0700305 0, &p, sizeof(p), NULL, 0);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800306 if (rc < 0) {
David Hendricksb907de32014-08-11 16:47:09 -0700307 msg_perr("CROS_EC cannot jump to [%s]:%d\n",
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800308 sections[p.cmd], rc);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800309 } else {
David Hendricksb907de32014-08-11 16:47:09 -0700310 msg_pdbg("CROS_EC has jumped to [%s]\n", sections[p.cmd]);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800311 rc = EC_RES_SUCCESS;
David Hendricksd13d90d2016-08-09 17:00:52 -0700312 priv->current_image = target;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800313 }
314
315 /* Sleep 1 sec to wait the EC re-init. */
316 usleep(1000000);
317
David Hendricksf9461c72013-07-11 19:02:13 -0700318 /* update max data write size in case we're jumping to an EC
319 * firmware with different protocol */
David Hendricksd13d90d2016-08-09 17:00:52 -0700320 set_ideal_write_size(priv);
David Hendricksf9461c72013-07-11 19:02:13 -0700321
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800322 return rc;
323}
324
325
326/* Given an image, this function parses FMAP and recognize the firmware
327 * ranges.
328 */
David Hendricksac1d25c2016-08-09 17:00:58 -0700329int cros_ec_prepare(uint8_t *image, int size) {
330 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800331 struct fmap *fmap;
332 int i, j;
333
David Hendricksd13d90d2016-08-09 17:00:52 -0700334 if (!(priv && priv->detected)) return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800335
336 // Parse the fmap in the image file and cache the firmware ranges.
337 fmap = fmap_find_in_memory(image, size);
338 if (!fmap) return 0;
339
340 // Lookup RO/A/B sections in FMAP.
341 for (i = 0; i < fmap->nareas; i++) {
342 struct fmap_area *fa = &fmap->areas[i];
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800343 for (j = EC_IMAGE_RO; j < ARRAY_SIZE(sections); j++) {
David Hendricks5b06c882012-05-20 18:27:25 -0700344 if (!strcmp(sections[j], (const char *)fa->name)) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800345 msg_pdbg("Found '%s' in image.\n", fa->name);
346 memcpy(&fwcopy[j], fa, sizeof(*fa));
347 fwcopy[j].flags = 1; // mark as new
348 }
349 }
350 }
351
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800352 /* Warning: before update, we jump the EC to RO copy. If you want to
David Hendricksb907de32014-08-11 16:47:09 -0700353 * change this behavior, please also check the cros_ec_finish().
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800354 */
David Hendricksac1d25c2016-08-09 17:00:58 -0700355 return cros_ec_jump_copy(EC_IMAGE_RO);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800356}
357
358
359/* Returns >0 if we need 2nd pass of erase_and_write_flash().
360 * <0 if we cannot jump to any firmware copy.
361 * ==0 if no more pass is needed.
362 *
363 * This function also jumps to new-updated firmware copy before return >0.
364 */
David Hendricksac1d25c2016-08-09 17:00:58 -0700365int cros_ec_need_2nd_pass(void) {
366 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
David Hendricksd13d90d2016-08-09 17:00:52 -0700367
368 if (!(priv && priv->detected)) return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800369
370 if (need_2nd_pass) {
David Hendricksac1d25c2016-08-09 17:00:58 -0700371 if (cros_ec_jump_copy(EC_IMAGE_UNKNOWN)) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800372 return -1;
373 }
374 }
375
376 return need_2nd_pass;
377}
378
379
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800380/* Returns 0 for success.
381 *
382 * Try latest firmware: B > A > RO
383 *
David Hendricksb907de32014-08-11 16:47:09 -0700384 * This function assumes the EC jumps to RO at cros_ec_prepare() so that
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800385 * the fwcopy[RO].flags is old (0) and A/B are new. Please also refine
David Hendricksb907de32014-08-11 16:47:09 -0700386 * this code logic if you change the cros_ec_prepare() behavior.
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800387 */
David Hendricksac1d25c2016-08-09 17:00:58 -0700388int cros_ec_finish(void) {
389 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
David Hendricksd13d90d2016-08-09 17:00:52 -0700390
391 if (!(priv && priv->detected)) return 0;
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800392
393 if (try_latest_firmware) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800394 if (fwcopy[EC_IMAGE_RW].flags &&
David Hendricksac1d25c2016-08-09 17:00:58 -0700395 cros_ec_jump_copy(EC_IMAGE_RW) == 0) return 0;
396 return cros_ec_jump_copy(EC_IMAGE_RO);
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800397 }
398
399 return 0;
400}
401
402
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700403int cros_ec_read(struct flashctx *flash, uint8_t *readarr,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800404 unsigned int blockaddr, unsigned int readcnt) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800405 int rc = 0;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800406 struct ec_params_flash_read p;
David Hendricksac1d25c2016-08-09 17:00:58 -0700407 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
408 int maxlen = opaque_programmer->max_data_read;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800409 uint8_t buf[maxlen];
David Hendricks133083b2012-07-17 20:39:38 -0700410 int offset = 0, count;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800411
David Hendricks133083b2012-07-17 20:39:38 -0700412 while (offset < readcnt) {
413 count = min(maxlen, readcnt - offset);
414 p.offset = blockaddr + offset;
415 p.size = count;
David Hendricksd13d90d2016-08-09 17:00:52 -0700416 rc = priv->ec_command(EC_CMD_FLASH_READ,
David Hendricks14935fe2014-08-14 17:38:24 -0700417 0, &p, sizeof(p), buf, count);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800418 if (rc < 0) {
David Hendricksb907de32014-08-11 16:47:09 -0700419 msg_perr("CROS_EC: Flash read error at offset 0x%x\n",
David Hendricks133083b2012-07-17 20:39:38 -0700420 blockaddr + offset);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800421 return rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800422 } else {
423 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800424 }
425
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800426 memcpy(readarr + offset, buf, count);
David Hendricks133083b2012-07-17 20:39:38 -0700427 offset += count;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800428 }
429
430 return rc;
431}
432
433
Simon Glassc453a642013-07-01 18:08:53 +0900434/*
435 * returns 0 to indicate area does not overlap current EC image
436 * returns 1 to indicate area overlaps current EC image or error
437 */
David Hendricksd13d90d2016-08-09 17:00:52 -0700438static int in_current_image(struct cros_ec_priv *priv,
439 unsigned int addr, unsigned int len)
Simon Glassc453a642013-07-01 18:08:53 +0900440{
Simon Glassc453a642013-07-01 18:08:53 +0900441 enum ec_current_image image;
442 uint32_t region_offset;
443 uint32_t region_size;
444
David Hendricksd13d90d2016-08-09 17:00:52 -0700445 image = priv->current_image;
446 region_offset = priv->region[image].offset;
447 region_size = priv->region[image].size;
Simon Glassc453a642013-07-01 18:08:53 +0900448
449 if ((addr + len - 1 < region_offset) ||
450 (addr > region_offset + region_size - 1)) {
451 return 0;
452 }
453 return 1;
454}
455
456
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700457int cros_ec_block_erase(struct flashctx *flash,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800458 unsigned int blockaddr,
459 unsigned int len) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800460 struct ec_params_flash_erase erase;
David Hendricksac1d25c2016-08-09 17:00:58 -0700461 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800462 int rc;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800463
David Hendricksd13d90d2016-08-09 17:00:52 -0700464 if (in_current_image(priv, blockaddr, len)) {
David Hendricksb907de32014-08-11 16:47:09 -0700465 cros_ec_invalidate_copy(blockaddr, len);
Simon Glassc453a642013-07-01 18:08:53 +0900466 need_2nd_pass = 1;
467 return ACCESS_DENIED;
468 }
469
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800470 erase.offset = blockaddr;
471 erase.size = len;
David Hendricksd13d90d2016-08-09 17:00:52 -0700472 rc = priv->ec_command(EC_CMD_FLASH_ERASE,
David Hendricks14935fe2014-08-14 17:38:24 -0700473 0, &erase, sizeof(erase), NULL, 0);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800474 if (rc == -EC_RES_ACCESS_DENIED) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800475 // this is active image.
David Hendricksb907de32014-08-11 16:47:09 -0700476 cros_ec_invalidate_copy(blockaddr, len);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800477 need_2nd_pass = 1;
478 return ACCESS_DENIED;
479 }
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800480 if (rc < 0) {
David Hendricksb907de32014-08-11 16:47:09 -0700481 msg_perr("CROS_EC: Flash erase error at address 0x%x, rc=%d\n",
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800482 blockaddr, rc);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800483 return rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800484 } else {
485 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800486 }
487
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800488#ifndef SOFTWARE_SYNC_ENABLED
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800489 try_latest_firmware = 1;
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800490#endif
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800491 return rc;
492}
493
494
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700495int cros_ec_write(struct flashctx *flash, uint8_t *buf, unsigned int addr,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800496 unsigned int nbytes) {
497 int i, rc = 0;
Ken Chang69c31b82014-10-28 15:17:21 +0800498 unsigned int written = 0, real_write_size;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800499 struct ec_params_flash_write p;
David Hendricksac1d25c2016-08-09 17:00:58 -0700500 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
David Hendricks2d6db772013-07-10 21:07:48 -0700501 uint8_t *packet;
502
Ken Chang69c31b82014-10-28 15:17:21 +0800503 /*
504 * For chrome-os-partner:33035, to workaround the undersized
505 * outdata buffer issue in kernel.
506 */
David Hendricksac1d25c2016-08-09 17:00:58 -0700507 real_write_size = min(opaque_programmer->max_data_write,
David Hendricksd13d90d2016-08-09 17:00:52 -0700508 priv->ideal_write_size);
Ken Chang69c31b82014-10-28 15:17:21 +0800509 packet = malloc(sizeof(p) + real_write_size);
David Hendricks2d6db772013-07-10 21:07:48 -0700510 if (!packet)
511 return -1;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800512
513 for (i = 0; i < nbytes; i += written) {
Ken Chang69c31b82014-10-28 15:17:21 +0800514 written = min(nbytes - i, real_write_size);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800515 p.offset = addr + i;
516 p.size = written;
Simon Glassc453a642013-07-01 18:08:53 +0900517
David Hendricksd13d90d2016-08-09 17:00:52 -0700518 if (in_current_image(priv, p.offset, p.size)) {
David Hendricksb907de32014-08-11 16:47:09 -0700519 cros_ec_invalidate_copy(addr, nbytes);
Simon Glassc453a642013-07-01 18:08:53 +0900520 need_2nd_pass = 1;
521 return ACCESS_DENIED;
522 }
523
David Hendricks2d6db772013-07-10 21:07:48 -0700524 memcpy(packet, &p, sizeof(p));
525 memcpy(packet + sizeof(p), &buf[i], written);
David Hendricksd13d90d2016-08-09 17:00:52 -0700526 rc = priv->ec_command(EC_CMD_FLASH_WRITE,
David Hendricks14935fe2014-08-14 17:38:24 -0700527 0, packet, sizeof(p) + p.size, NULL, 0);
David Hendricks2d6db772013-07-10 21:07:48 -0700528
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800529 if (rc == -EC_RES_ACCESS_DENIED) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800530 // this is active image.
David Hendricksb907de32014-08-11 16:47:09 -0700531 cros_ec_invalidate_copy(addr, nbytes);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800532 need_2nd_pass = 1;
533 return ACCESS_DENIED;
534 }
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800535
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800536 if (rc < 0) break;
537 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800538 }
539
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800540#ifndef SOFTWARE_SYNC_ENABLED
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800541 try_latest_firmware = 1;
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800542#endif
David Hendricks2d6db772013-07-10 21:07:48 -0700543 free(packet);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800544 return rc;
545}
546
547
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700548static int cros_ec_list_ranges(const struct flashctx *flash) {
David Hendricksac1d25c2016-08-09 17:00:58 -0700549 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
Simon Glass3c01dca2013-07-01 18:07:34 +0900550 struct ec_response_flash_region_info info;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800551 int rc;
552
David Hendricksd13d90d2016-08-09 17:00:52 -0700553 rc = cros_ec_get_region_info(priv, EC_FLASH_REGION_WP_RO, &info);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800554 if (rc < 0) {
555 msg_perr("Cannot get the WP_RO region info: %d\n", rc);
556 return 1;
557 }
558
559 msg_pinfo("Supported write protect range:\n");
560 msg_pinfo(" disable: start=0x%06x len=0x%06x\n", 0, 0);
Simon Glass3c01dca2013-07-01 18:07:34 +0900561 msg_pinfo(" enable: start=0x%06x len=0x%06x\n", info.offset,
562 info.size);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800563
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800564 return 0;
565}
566
567
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800568/*
569 * Helper function for flash protection.
570 *
571 * On EC API v1, the EC write protection has been simplified to one-bit:
572 * EC_FLASH_PROTECT_RO_AT_BOOT, which means the state is either enabled
573 * or disabled. However, this is different from the SPI-style write protect
574 * behavior. Thus, we re-define the flashrom command (SPI-style) so that
575 * either SRP or range is non-zero, the EC_FLASH_PROTECT_RO_AT_BOOT is set.
576 *
577 * SRP Range | PROTECT_RO_AT_BOOT
578 * 0 0 | 0
579 * 0 non-zero | 1
580 * 1 0 | 1
581 * 1 non-zero | 1
582 *
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800583 *
584 * Besides, to make the protection take effect as soon as possible, we
585 * try to set EC_FLASH_PROTECT_RO_NOW at the same time. However, not
586 * every EC supports RO_NOW, thus we then try to protect the entire chip.
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800587 */
David Hendricksac1d25c2016-08-09 17:00:58 -0700588static int set_wp(int enable) {
589 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800590 struct ec_params_flash_protect p;
591 struct ec_response_flash_protect r;
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800592 const int ro_at_boot_flag = EC_FLASH_PROTECT_RO_AT_BOOT;
593 const int ro_now_flag = EC_FLASH_PROTECT_RO_NOW;
594 int need_an_ec_cold_reset = 0;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800595 int rc;
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800596
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800597 /* Try to set RO_AT_BOOT and RO_NOW first */
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800598 memset(&p, 0, sizeof(p));
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800599 p.mask = (ro_at_boot_flag | ro_now_flag);
600 p.flags = enable ? (ro_at_boot_flag | ro_now_flag) : 0;
David Hendricksd13d90d2016-08-09 17:00:52 -0700601 rc = priv->ec_command(EC_CMD_FLASH_PROTECT,
David Hendricks14935fe2014-08-14 17:38:24 -0700602 EC_VER_FLASH_PROTECT, &p, sizeof(p), &r, sizeof(r));
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800603 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800604 msg_perr("FAILED: Cannot set the RO_AT_BOOT and RO_NOW: %d\n",
605 rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800606 return 1;
607 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800608
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800609 /* Read back */
610 memset(&p, 0, sizeof(p));
David Hendricksd13d90d2016-08-09 17:00:52 -0700611 rc = priv->ec_command(EC_CMD_FLASH_PROTECT,
David Hendricks14935fe2014-08-14 17:38:24 -0700612 EC_VER_FLASH_PROTECT, &p, sizeof(p), &r, sizeof(r));
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800613 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800614 msg_perr("FAILED: Cannot get RO_AT_BOOT and RO_NOW: %d\n",
615 rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800616 return 1;
617 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800618
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800619 if (!enable) {
620 /* The disable case is easier to check. */
621 if (r.flags & ro_at_boot_flag) {
622 msg_perr("FAILED: RO_AT_BOOT is not clear.\n");
623 return 1;
624 } else if (r.flags & ro_now_flag) {
625 msg_perr("FAILED: RO_NOW is asserted unexpectedly.\n");
626 need_an_ec_cold_reset = 1;
627 goto exit;
628 }
629
630 msg_pdbg("INFO: RO_AT_BOOT is clear.\n");
631 return 0;
632 }
633
634 /* Check if RO_AT_BOOT is set. If not, fail in anyway. */
635 if (r.flags & ro_at_boot_flag) {
636 msg_pdbg("INFO: RO_AT_BOOT has been set.\n");
637 } else {
638 msg_perr("FAILED: RO_AT_BOOT is not set.\n");
639 return 1;
640 }
641
642 /* Then, we check if the protection has been activated. */
643 if (r.flags & ro_now_flag) {
644 /* Good, RO_NOW is set. */
645 msg_pdbg("INFO: RO_NOW is set. WP is active now.\n");
646 } else if (r.writable_flags & EC_FLASH_PROTECT_ALL_NOW) {
647 struct ec_params_reboot_ec reboot;
648
649 msg_pdbg("WARN: RO_NOW is not set. Trying ALL_NOW.\n");
650
651 memset(&p, 0, sizeof(p));
652 p.mask = EC_FLASH_PROTECT_ALL_NOW;
653 p.flags = EC_FLASH_PROTECT_ALL_NOW;
David Hendricksd13d90d2016-08-09 17:00:52 -0700654 rc = priv->ec_command(EC_CMD_FLASH_PROTECT,
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800655 EC_VER_FLASH_PROTECT,
656 &p, sizeof(p), &r, sizeof(r));
657 if (rc < 0) {
658 msg_perr("FAILED: Cannot set ALL_NOW: %d\n", rc);
659 return 1;
660 }
661
662 /* Read back */
663 memset(&p, 0, sizeof(p));
David Hendricksd13d90d2016-08-09 17:00:52 -0700664 rc = priv->ec_command(EC_CMD_FLASH_PROTECT,
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800665 EC_VER_FLASH_PROTECT,
666 &p, sizeof(p), &r, sizeof(r));
667 if (rc < 0) {
668 msg_perr("FAILED:Cannot get ALL_NOW: %d\n", rc);
669 return 1;
670 }
671
672 if (!(r.flags & EC_FLASH_PROTECT_ALL_NOW)) {
673 msg_perr("FAILED: ALL_NOW is not set.\n");
674 need_an_ec_cold_reset = 1;
675 goto exit;
676 }
677
678 msg_pdbg("INFO: ALL_NOW has been set. WP is active now.\n");
679
680 /*
681 * Our goal is to protect the RO ASAP. The entire protection
682 * is just a workaround for platform not supporting RO_NOW.
683 * It has side-effect that the RW is also protected and leads
684 * the RW update failed. So, we arrange an EC code reset to
685 * unlock RW ASAP.
686 */
687 memset(&reboot, 0, sizeof(reboot));
688 reboot.cmd = EC_REBOOT_COLD;
689 reboot.flags = EC_REBOOT_FLAG_ON_AP_SHUTDOWN;
David Hendricksd13d90d2016-08-09 17:00:52 -0700690 rc = priv->ec_command(EC_CMD_REBOOT_EC,
David Hendricks14935fe2014-08-14 17:38:24 -0700691 0, &reboot, sizeof(reboot), NULL, 0);
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800692 if (rc < 0) {
693 msg_perr("WARN: Cannot arrange a cold reset at next "
694 "shutdown to unlock entire protect.\n");
695 msg_perr(" But you can do it manually.\n");
696 } else {
697 msg_pdbg("INFO: A cold reset is arranged at next "
698 "shutdown.\n");
699 }
700
701 } else {
702 msg_perr("FAILED: RO_NOW is not set.\n");
703 msg_perr("FAILED: The PROTECT_RO_AT_BOOT is set, but cannot "
704 "make write protection active now.\n");
705 need_an_ec_cold_reset = 1;
706 }
707
708exit:
709 if (need_an_ec_cold_reset) {
710 msg_perr("FAILED: You may need a reboot to take effect of "
711 "PROTECT_RO_AT_BOOT.\n");
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800712 return 1;
713 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800714
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800715 return 0;
716}
717
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700718static int cros_ec_set_range(const struct flashctx *flash,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800719 unsigned int start, unsigned int len) {
David Hendricksac1d25c2016-08-09 17:00:58 -0700720 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
Simon Glass3c01dca2013-07-01 18:07:34 +0900721 struct ec_response_flash_region_info info;
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800722 int rc;
723
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800724 /* Check if the given range is supported */
David Hendricksd13d90d2016-08-09 17:00:52 -0700725 rc = cros_ec_get_region_info(priv, EC_FLASH_REGION_WP_RO, &info);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800726 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800727 msg_perr("FAILED: Cannot get the WP_RO region info: %d\n", rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800728 return 1;
729 }
730 if ((!start && !len) || /* list supported ranges */
Simon Glass3c01dca2013-07-01 18:07:34 +0900731 ((start == info.offset) && (len == info.size))) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800732 /* pass */
733 } else {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800734 msg_perr("FAILED: Unsupported write protection range "
735 "(0x%06x,0x%06x)\n\n", start, len);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800736 msg_perr("Currently supported range:\n");
737 msg_perr(" disable: (0x%06x,0x%06x)\n", 0, 0);
Simon Glass3c01dca2013-07-01 18:07:34 +0900738 msg_perr(" enable: (0x%06x,0x%06x)\n", info.offset,
739 info.size);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800740 return 1;
741 }
742
David Hendricksac1d25c2016-08-09 17:00:58 -0700743 return set_wp(!!len);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800744}
745
746
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700747static int cros_ec_enable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -0700748 enum wp_mode wp_mode) {
749 int ret;
750
751 switch (wp_mode) {
752 case WP_MODE_HARDWARE:
David Hendricksac1d25c2016-08-09 17:00:58 -0700753 ret = set_wp(1);
David Hendricks1c09f802012-10-03 11:03:48 -0700754 break;
755 default:
756 msg_perr("%s():%d Unsupported write-protection mode\n",
757 __func__, __LINE__);
758 ret = 1;
759 break;
760 }
761
762 return ret;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800763}
764
765
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700766static int cros_ec_disable_writeprotect(const struct flashctx *flash) {
David Hendricksac1d25c2016-08-09 17:00:58 -0700767 return set_wp(0);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800768}
769
770
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700771static int cros_ec_wp_status(const struct flashctx *flash) {
David Hendricksac1d25c2016-08-09 17:00:58 -0700772 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800773 struct ec_params_flash_protect p;
774 struct ec_response_flash_protect r;
775 int start, len; /* wp range */
776 int enabled;
777 int rc;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800778
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800779 memset(&p, 0, sizeof(p));
David Hendricksd13d90d2016-08-09 17:00:52 -0700780 rc = priv->ec_command(EC_CMD_FLASH_PROTECT,
David Hendricks14935fe2014-08-14 17:38:24 -0700781 EC_VER_FLASH_PROTECT, &p, sizeof(p), &r, sizeof(r));
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800782 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800783 msg_perr("FAILED: Cannot get the write protection status: %d\n",
784 rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800785 return 1;
786 } else if (rc < sizeof(r)) {
David Hendricksf797dde2012-10-30 11:39:12 -0700787 msg_perr("FAILED: Too little data returned (expected:%zd, "
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800788 "actual:%d)\n", sizeof(r), rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800789 return 1;
790 }
791
792 start = len = 0;
793 if (r.flags & EC_FLASH_PROTECT_RO_AT_BOOT) {
Simon Glass3c01dca2013-07-01 18:07:34 +0900794 struct ec_response_flash_region_info info;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800795
796 msg_pdbg("%s(): EC_FLASH_PROTECT_RO_AT_BOOT is set.\n",
797 __func__);
David Hendricksd13d90d2016-08-09 17:00:52 -0700798 rc = cros_ec_get_region_info(priv, EC_FLASH_REGION_WP_RO, &info);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800799 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800800 msg_perr("FAILED: Cannot get the WP_RO region info: "
801 "%d\n", rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800802 return 1;
803 }
Simon Glass3c01dca2013-07-01 18:07:34 +0900804 start = info.offset;
805 len = info.size;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800806 } else {
807 msg_pdbg("%s(): EC_FLASH_PROTECT_RO_AT_BOOT is clear.\n",
808 __func__);
809 }
810
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800811 /*
812 * If neither RO_NOW or ALL_NOW is set, it means write protect is
813 * NOT active now.
814 */
815 if (!(r.flags & (EC_FLASH_PROTECT_RO_NOW | EC_FLASH_PROTECT_ALL_NOW)))
816 start = len = 0;
817
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800818 /* Remove the SPI-style messages. */
819 enabled = r.flags & EC_FLASH_PROTECT_RO_AT_BOOT ? 1 : 0;
820 msg_pinfo("WP: status: 0x%02x\n", enabled ? 0x80 : 0x00);
821 msg_pinfo("WP: status.srp0: %x\n", enabled);
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800822 msg_pinfo("WP: write protect is %s.\n",
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800823 enabled ? "enabled" : "disabled");
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800824 msg_pinfo("WP: write protect range: start=0x%08x, len=0x%08x\n",
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800825 start, len);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800826
827 return 0;
828}
829
David Hendrickse5454932013-11-04 18:16:11 -0800830/* perform basic "hello" test to see if we can talk to the EC */
David Hendricksb907de32014-08-11 16:47:09 -0700831int cros_ec_test(struct cros_ec_priv *priv)
David Hendrickse5454932013-11-04 18:16:11 -0800832{
833 struct ec_params_hello request;
834 struct ec_response_hello response;
David Hendrickse5454932013-11-04 18:16:11 -0800835 int rc = 0;
836
837 /* Say hello to EC. */
838 request.in_data = 0xf0e0d0c0; /* Expect EC will add on 0x01020304. */
839 msg_pdbg("%s: sending HELLO request with 0x%08x\n",
840 __func__, request.in_data);
Gwendal Grignou94e87d62014-11-25 15:34:15 -0800841 rc = priv->ec_command(EC_CMD_HELLO, 0, &request,
David Hendrickse5454932013-11-04 18:16:11 -0800842 sizeof(request), &response, sizeof(response));
843 msg_pdbg("%s: response: 0x%08x\n", __func__, response.out_data);
844
845 if (rc < 0 || response.out_data != 0xf1e2d3c4) {
846 msg_pdbg("response.out_data is not 0xf1e2d3c4.\n"
847 "rc=%d, request=0x%x response=0x%x\n",
848 rc, request.in_data, response.out_data);
849 return 1;
850 }
851
852 return 0;
853}
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800854
David Hendricksd13d90d2016-08-09 17:00:52 -0700855void cros_ec_set_max_size(struct cros_ec_priv *priv,
856 struct opaque_programmer *op) {
Puthikorn Voravootivatc0993cf2014-08-28 16:04:58 -0700857 struct ec_response_get_protocol_info info;
858 int rc = 0;
859 msg_pdbg("%s: sending protoinfo command\n", __func__);
Gwendal Grignou94e87d62014-11-25 15:34:15 -0800860 rc = priv->ec_command(EC_CMD_GET_PROTOCOL_INFO, 0, NULL, 0,
Puthikorn Voravootivatc0993cf2014-08-28 16:04:58 -0700861 &info, sizeof(info));
862 msg_pdbg("%s: rc:%d\n", __func__, rc);
863
864 if (rc == sizeof(info)) {
865 op->max_data_write = min(op->max_data_write,
866 info.max_request_packet_size -
867 sizeof(struct ec_host_request));
868 op->max_data_read = min(op->max_data_read,
869 info.max_response_packet_size -
870 sizeof(struct ec_host_response));
871 msg_pdbg("%s: max_write:%d max_read:%d\n", __func__,
872 op->max_data_write, op->max_data_read);
873 }
874}
875
David Hendricks14935fe2014-08-14 17:38:24 -0700876
877/*
David Hendricks052446b2014-09-11 11:26:51 -0700878 * Returns 0 to indicate success, non-zero otherwise
David Hendricks14935fe2014-08-14 17:38:24 -0700879 *
880 * This function parses programmer parameters from the command line. Since
881 * CrOS EC hangs off the "internal programmer" (AP, PCH, etc) this gets
882 * run during internal programmer initialization.
883 */
884int cros_ec_parse_param(struct cros_ec_priv *priv)
885{
886 char *p;
Souvik Ghoshf1608b42016-06-30 16:03:55 -0700887
David Hendricksd13d90d2016-08-09 17:00:52 -0700888 p = extract_programmer_param("dev");
David Hendricks14935fe2014-08-14 17:38:24 -0700889 if (p) {
890 unsigned int index;
891 char *endptr = NULL;
892
893 errno = 0;
Gwendal Grignou94e87d62014-11-25 15:34:15 -0800894 /*
895 * For backward compatibility, check if the index is
896 * a number: 0: main EC, 1: PD
897 * works only on Samus.
898 */
David Hendricks14935fe2014-08-14 17:38:24 -0700899 index = strtoul(p, &endptr, 10);
900 if (errno || (endptr != (p + 1)) || (strlen(p) > 1)) {
901 msg_perr("Invalid argument: \"%s\"\n", p);
902 return 1;
903 }
904
Gwendal Grignou94e87d62014-11-25 15:34:15 -0800905 if (index > 1) {
David Hendricks14935fe2014-08-14 17:38:24 -0700906 msg_perr("%s: Invalid device index\n", __func__);
907 return 1;
908 }
Gwendal Grignou94e87d62014-11-25 15:34:15 -0800909 priv->dev = ec_type[index];
910 msg_pdbg("Target %s used\n", priv->dev);
911 }
David Hendricks14935fe2014-08-14 17:38:24 -0700912
Gwendal Grignou94e87d62014-11-25 15:34:15 -0800913 p = extract_programmer_param("type");
914 if (p) {
915 unsigned int index;
916 for (index = 0; index < ARRAY_SIZE(ec_type); index++)
917 if (!strcmp(p, ec_type[index]))
918 break;
919 if (index == ARRAY_SIZE(ec_type)) {
920 msg_perr("Invalid argument: \"%s\"\n", p);
921 return 1;
922 }
923 priv->dev = ec_type[index];
924 msg_pdbg("Target %s used\n", priv->dev);
David Hendricks14935fe2014-08-14 17:38:24 -0700925 }
926
Duncan Laurie84328722014-09-10 23:25:01 -0700927 p = extract_programmer_param("block");
928 if (p) {
929 unsigned int block;
930 char *endptr = NULL;
931
932 errno = 0;
933 block = strtoul(p, &endptr, 0);
934 if (errno || (strlen(p) > 10) || (endptr != (p + strlen(p)))) {
935 msg_perr("Invalid argument: \"%s\"\n", p);
936 return 1;
937 }
938
939 if (block <= 0) {
940 msg_perr("%s: Invalid block size\n", __func__);
941 return 1;
942 }
943
944 msg_pdbg("Override block size to 0x%x\n", block);
945 priv->erase_block_size = block;
946 }
947
David Hendricks14935fe2014-08-14 17:38:24 -0700948 return 0;
949}
950
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700951int cros_ec_probe_size(struct flashctx *flash) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800952 int rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800953 struct ec_response_flash_info info;
David Hendricks194b3bb2013-07-16 14:32:26 -0700954 struct ec_response_get_chip_info chip_info;
David Hendricksac1d25c2016-08-09 17:00:58 -0700955 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800956 struct block_eraser *eraser;
957 static struct wp wp = {
David Hendricksb907de32014-08-11 16:47:09 -0700958 .list_ranges = cros_ec_list_ranges,
959 .set_range = cros_ec_set_range,
960 .enable = cros_ec_enable_writeprotect,
961 .disable = cros_ec_disable_writeprotect,
962 .wp_status = cros_ec_wp_status,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800963 };
964
David Hendricksd13d90d2016-08-09 17:00:52 -0700965 rc = priv->ec_command(EC_CMD_FLASH_INFO,
David Hendricks14935fe2014-08-14 17:38:24 -0700966 0, NULL, 0, &info, sizeof(info));
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800967 if (rc < 0) {
968 msg_perr("%s(): FLASH_INFO returns %d.\n", __func__, rc);
969 return 0;
970 }
David Hendricksd13d90d2016-08-09 17:00:52 -0700971 rc = cros_ec_get_current_image(priv);
Simon Glass01c11672013-07-01 18:03:33 +0900972 if (rc < 0) {
973 msg_perr("%s(): Failed to probe (no current image): %d\n",
974 __func__, rc);
975 return 0;
976 }
David Hendricksd13d90d2016-08-09 17:00:52 -0700977 priv->current_image = rc;
978 priv->region = &regions[0];
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800979
980 flash->total_size = info.flash_size / 1024;
David Hendricksac1d25c2016-08-09 17:00:58 -0700981 flash->page_size = opaque_programmer->max_data_read;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800982 flash->tested = TEST_OK_PREW;
983 eraser = &flash->block_erasers[0];
Duncan Laurie84328722014-09-10 23:25:01 -0700984
985 /* Allow overriding the erase block size in case EC is incorrect */
David Hendricksd13d90d2016-08-09 17:00:52 -0700986 if (priv->erase_block_size > 0)
987 eraser->eraseblocks[0].size = priv->erase_block_size;
Duncan Laurie84328722014-09-10 23:25:01 -0700988 else
989 eraser->eraseblocks[0].size = info.erase_block_size;
990
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800991 eraser->eraseblocks[0].count = info.flash_size /
992 eraser->eraseblocks[0].size;
993 flash->wp = &wp;
994
David Hendricks194b3bb2013-07-16 14:32:26 -0700995 /*
996 * Some STM32 variants erase bits to 0. For now, assume that this
997 * applies to STM32L parts.
998 *
999 * FIXME: This info will eventually be exposed via some EC command.
1000 * See chrome-os-partner:20973.
1001 */
David Hendricksd13d90d2016-08-09 17:00:52 -07001002 rc = priv->ec_command(EC_CMD_GET_CHIP_INFO,
David Hendricks14935fe2014-08-14 17:38:24 -07001003 0, NULL, 0, &chip_info, sizeof(chip_info));
David Hendricks194b3bb2013-07-16 14:32:26 -07001004 if (rc < 0) {
1005 msg_perr("%s(): CHIP_INFO returned %d.\n", __func__, rc);
1006 return 0;
1007 }
1008 if (!strncmp(chip_info.name, "stm32l", 6))
1009 flash->feature_bits |= FEATURE_ERASE_TO_ZERO;
1010
David Hendricksd13d90d2016-08-09 17:00:52 -07001011 rc = set_ideal_write_size(priv);
David Hendricksfbd5e6d2014-08-21 15:01:43 -07001012 if (rc < 0) {
1013 msg_perr("%s(): Unable to set write size\n", __func__);
1014 return 0;
1015 }
David Hendricksf9461c72013-07-11 19:02:13 -07001016
Simon Glassc453a642013-07-01 18:08:53 +09001017 /* FIXME: EC_IMAGE_* is ordered differently from EC_FLASH_REGION_*,
1018 * so we need to be careful about using these enums as array indices */
David Hendricksd13d90d2016-08-09 17:00:52 -07001019 rc = cros_ec_get_region_info(priv, EC_FLASH_REGION_RO,
1020 &priv->region[EC_IMAGE_RO]);
Simon Glassc453a642013-07-01 18:08:53 +09001021 if (rc) {
1022 msg_perr("%s(): Failed to probe (cannot find RO region): %d\n",
1023 __func__, rc);
1024 return 0;
1025 }
1026
David Hendricksd13d90d2016-08-09 17:00:52 -07001027 rc = cros_ec_get_region_info(priv, EC_FLASH_REGION_RW,
1028 &priv->region[EC_IMAGE_RW]);
Simon Glassc453a642013-07-01 18:08:53 +09001029 if (rc) {
1030 msg_perr("%s(): Failed to probe (cannot find RW region): %d\n",
1031 __func__, rc);
1032 return 0;
1033 }
1034
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +08001035 return 1;
1036};