blob: 0c5d7c22d46d34d64e3fa46f35fb80a041affff8 [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>
Souvik Ghosh586968a2016-08-11 17:56:24 -070053
54struct cros_ec_priv *cros_ec_priv;
David Hendricks393deec2016-11-23 16:15:05 -080055static int ignore_wp_range_command = 0;
Souvik Ghosh586968a2016-08-11 17:56:24 -070056
David Hendricksb64b39a2016-10-11 13:48:06 -070057static int set_wp(int enable); /* FIXME: move set_wp() */
58
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +080059struct wp_data {
60 int enable;
61 unsigned int start;
62 unsigned int len;
63};
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +080064#define WP_STATE_HACK_FILENAME "/mnt/stateful_partition/flashrom_wp_state"
65
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +080066/* If software sync is enabled, then we don't try the latest firmware copy
67 * after updating.
68 */
69#define SOFTWARE_SYNC_ENABLED
70
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080071/* 1 if we want the flashrom to call erase_and_write_flash() again. */
72static int need_2nd_pass = 0;
73
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +080074/* 1 if we want the flashrom to try jumping to new firmware after update. */
75static int try_latest_firmware = 0;
76
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080077/* The range of each firmware copy from the image file to update.
78 * But re-define the .flags as the valid flag to indicate the firmware is
79 * new or not (if flags = 1).
80 */
81static struct fmap_area fwcopy[4]; // [0] is not used.
82
83/* The names of enum lpc_current_image to match in FMAP area names. */
Gwendal Grignou94e87d62014-11-25 15:34:15 -080084static const char *sections[] = {
David Hendricksbf8c4dd2012-07-19 12:13:17 -070085 "UNKNOWN SECTION", // EC_IMAGE_UNKNOWN -- never matches
86 "EC_RO",
87 "EC_RW",
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080088};
89
Gwendal Grignou94e87d62014-11-25 15:34:15 -080090/*
91 * The names of the different device that can be found in a machine.
92 * Order is important: for backward compatibilty issue,
93 * 'ec' must be 0, 'pd' must be 1.
94 */
95static const char *ec_type[] = {
96 [0] = "ec",
97 [1] = "pd",
98 [2] = "sh",
99};
100
Simon Glassc453a642013-07-01 18:08:53 +0900101/* EC_FLASH_REGION_WP_RO is the highest numbered region so it also indicates
102 * the number of regions */
103static struct ec_response_flash_region_info regions[EC_FLASH_REGION_WP_RO + 1];
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800104
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 */
David Hendricksb907de32014-08-11 16:47:09 -0700108static void cros_ec_invalidate_copy(unsigned int addr, unsigned int len)
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800109{
110 int i;
111
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800112 for (i = EC_IMAGE_RO; i < ARRAY_SIZE(fwcopy); i++) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800113 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
Souvik Ghosh586968a2016-08-11 17:56:24 -0700124static int cros_ec_get_current_image(void)
Simon Glass01c11672013-07-01 18:03:33 +0900125{
126 struct ec_response_get_version resp;
127 int rc;
David Hendricksac1d25c2016-08-09 17:00:58 -0700128
Souvik Ghosh586968a2016-08-11 17:56:24 -0700129 rc = cros_ec_priv->ec_command(EC_CMD_GET_VERSION,
David Hendricks14935fe2014-08-14 17:38:24 -0700130 0, NULL, 0, &resp, sizeof(resp));
Simon Glass01c11672013-07-01 18:03:33 +0900131 if (rc < 0) {
David Hendricksb907de32014-08-11 16:47:09 -0700132 msg_perr("CROS_EC cannot get the running copy: rc=%d\n", rc);
Simon Glass01c11672013-07-01 18:03:33 +0900133 return rc;
134 }
135 if (resp.current_image == EC_IMAGE_UNKNOWN) {
David Hendricksb907de32014-08-11 16:47:09 -0700136 msg_perr("CROS_EC gets unknown running copy\n");
Simon Glass01c11672013-07-01 18:03:33 +0900137 return -1;
138 }
139
140 return resp.current_image;
141}
142
143
Souvik Ghosh586968a2016-08-11 17:56:24 -0700144static int cros_ec_get_region_info(enum ec_flash_region region,
Simon Glass3c01dca2013-07-01 18:07:34 +0900145 struct ec_response_flash_region_info *info)
146{
147 struct ec_params_flash_region_info req;
148 struct ec_response_flash_region_info resp;
149 int rc;
150
151 req.region = region;
Souvik Ghosh586968a2016-08-11 17:56:24 -0700152 rc = cros_ec_priv->ec_command(EC_CMD_FLASH_REGION_INFO,
Simon Glass3c01dca2013-07-01 18:07:34 +0900153 EC_VER_FLASH_REGION_INFO, &req, sizeof(req),
154 &resp, sizeof(resp));
155 if (rc < 0) {
156 msg_perr("Cannot get the WP_RO region info: %d\n", rc);
157 return rc;
158 }
159
160 info->offset = resp.offset;
161 info->size = resp.size;
162 return 0;
163}
164
David Hendricksf9461c72013-07-11 19:02:13 -0700165/**
166 * Get the versions of the command supported by the EC.
167 *
168 * @param cmd Command
169 * @param pmask Destination for version mask; will be set to 0 on
170 * error.
171 * @return 0 if success, <0 if error
172 */
David Hendricksac1d25c2016-08-09 17:00:58 -0700173static int ec_get_cmd_versions(int cmd, uint32_t *pmask)
David Hendricksf9461c72013-07-11 19:02:13 -0700174{
David Hendricksf9461c72013-07-11 19:02:13 -0700175 struct ec_params_get_cmd_versions pver;
176 struct ec_response_get_cmd_versions rver;
177 int rc;
178
179 *pmask = 0;
180
181 pver.cmd = cmd;
Souvik Ghosh586968a2016-08-11 17:56:24 -0700182 rc = cros_ec_priv->ec_command(EC_CMD_GET_CMD_VERSIONS, 0,
David Hendricksf9461c72013-07-11 19:02:13 -0700183 &pver, sizeof(pver), &rver, sizeof(rver));
184
185 if (rc < 0)
186 return rc;
187
188 *pmask = rver.version_mask;
189 return rc;
190}
191
192/**
193 * Return non-zero if the EC supports the command and version
194 *
195 * @param cmd Command to check
196 * @param ver Version to check
197 * @return non-zero if command version supported; 0 if not.
198 */
David Hendricksac1d25c2016-08-09 17:00:58 -0700199static int ec_cmd_version_supported(int cmd, int ver)
David Hendricksf9461c72013-07-11 19:02:13 -0700200{
201 uint32_t mask = 0;
202 int rc;
David Hendricksd13d90d2016-08-09 17:00:52 -0700203
David Hendricksac1d25c2016-08-09 17:00:58 -0700204 rc = ec_get_cmd_versions(cmd, &mask);
David Hendricksf9461c72013-07-11 19:02:13 -0700205 if (rc < 0)
206 return rc;
207
208 return (mask & EC_VER_MASK(ver)) ? 1 : 0;
209}
210
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700211/* returns 0 if successful or <0 to indicate error */
Souvik Ghosh586968a2016-08-11 17:56:24 -0700212static int set_ideal_write_size(void)
David Hendricksf9461c72013-07-11 19:02:13 -0700213{
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700214 int cmd_version, ret;
David Hendricksf9461c72013-07-11 19:02:13 -0700215
David Hendricksac1d25c2016-08-09 17:00:58 -0700216 cmd_version = ec_cmd_version_supported(EC_CMD_FLASH_WRITE,
David Hendricksfb405f12014-08-19 22:42:30 -0700217 EC_VER_FLASH_WRITE);
218 if (cmd_version < 0) {
219 msg_perr("Cannot determine write command version\n");
220 return cmd_version;
221 } else if (cmd_version == 0) {
222 struct ec_response_flash_info info;
David Hendricksf9461c72013-07-11 19:02:13 -0700223
Souvik Ghosh586968a2016-08-11 17:56:24 -0700224 ret = cros_ec_priv->ec_command(EC_CMD_FLASH_INFO,
David Hendricksfb405f12014-08-19 22:42:30 -0700225 cmd_version, NULL, 0, &info, sizeof(info));
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700226 if (ret < 0) {
David Hendricksfb405f12014-08-19 22:42:30 -0700227 msg_perr("%s(): Cannot get flash info.\n", __func__);
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700228 return ret;
David Hendricksfb405f12014-08-19 22:42:30 -0700229 }
230
Souvik Ghosh586968a2016-08-11 17:56:24 -0700231 cros_ec_priv->ideal_write_size = EC_FLASH_WRITE_VER0_SIZE;
David Hendricksfb405f12014-08-19 22:42:30 -0700232 } else {
233 struct ec_response_flash_info_1 info;
234
Souvik Ghosh586968a2016-08-11 17:56:24 -0700235 ret = cros_ec_priv->ec_command(EC_CMD_FLASH_INFO,
David Hendricksfb405f12014-08-19 22:42:30 -0700236 cmd_version, NULL, 0, &info, sizeof(info));
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700237 if (ret < 0) {
David Hendricksfb405f12014-08-19 22:42:30 -0700238 msg_perr("%s(): Cannot get flash info.\n", __func__);
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700239 return ret;
David Hendricksfb405f12014-08-19 22:42:30 -0700240 }
241
Souvik Ghosh586968a2016-08-11 17:56:24 -0700242 cros_ec_priv->ideal_write_size = info.write_ideal_size;
David Hendricksf9461c72013-07-11 19:02:13 -0700243 }
244
David Hendricksfb405f12014-08-19 22:42:30 -0700245 return 0;
David Hendricksf9461c72013-07-11 19:02:13 -0700246}
Simon Glass3c01dca2013-07-01 18:07:34 +0900247
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800248/* Asks EC to jump to a firmware copy. If target is EC_IMAGE_UNKNOWN,
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800249 * then this functions picks a NEW firmware copy and jumps to it. Note that
250 * RO is preferred, then A, finally B.
251 *
252 * Returns 0 for success.
253 */
David Hendricksac1d25c2016-08-09 17:00:58 -0700254static int cros_ec_jump_copy(enum ec_current_image target) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800255 struct ec_params_reboot_ec p;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800256 int rc;
Vadim Bendebury9fa26e82013-09-19 13:56:32 -0700257 int current_image;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800258
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800259 /* Since the EC may return EC_RES_SUCCESS twice if the EC doesn't
260 * jump to different firmware copy. The second EC_RES_SUCCESS would
261 * set the OBF=1 and the next command cannot be executed.
262 * Thus, we call EC to jump only if the target is different.
263 */
Souvik Ghosh586968a2016-08-11 17:56:24 -0700264 current_image = cros_ec_get_current_image();
Vadim Bendebury9fa26e82013-09-19 13:56:32 -0700265 if (current_image < 0)
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800266 return 1;
Vadim Bendebury9fa26e82013-09-19 13:56:32 -0700267 if (current_image == target)
Simon Glassc453a642013-07-01 18:08:53 +0900268 return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800269
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800270 memset(&p, 0, sizeof(p));
Simon Glassc453a642013-07-01 18:08:53 +0900271
272 /* Translate target --> EC reboot command parameter */
273 switch (target) {
274 case EC_IMAGE_RO:
275 p.cmd = EC_REBOOT_JUMP_RO;
276 break;
277 case EC_IMAGE_RW:
278 p.cmd = EC_REBOOT_JUMP_RW;
279 break;
280 default:
281 /*
282 * If target is unspecified, set EC reboot command to use
283 * a new image. Also set "target" so that it may be used
284 * to update the priv->current_image if jump is successful.
285 */
286 if (fwcopy[EC_IMAGE_RO].flags) {
287 p.cmd = EC_REBOOT_JUMP_RO;
288 target = EC_IMAGE_RO;
289 } else if (fwcopy[EC_IMAGE_RW].flags) {
290 p.cmd = EC_REBOOT_JUMP_RW;
291 target = EC_IMAGE_RW;
292 } else {
293 p.cmd = EC_IMAGE_UNKNOWN;
294 }
295 break;
296 }
297
David Hendricksb907de32014-08-11 16:47:09 -0700298 msg_pdbg("CROS_EC is jumping to [%s]\n", sections[p.cmd]);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800299 if (p.cmd == EC_IMAGE_UNKNOWN) return 1;
300
Vadim Bendebury9fa26e82013-09-19 13:56:32 -0700301 if (current_image == p.cmd) {
David Hendricksb907de32014-08-11 16:47:09 -0700302 msg_pdbg("CROS_EC is already in [%s]\n", sections[p.cmd]);
Souvik Ghosh586968a2016-08-11 17:56:24 -0700303 cros_ec_priv->current_image = target;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800304 return 0;
305 }
306
Souvik Ghosh586968a2016-08-11 17:56:24 -0700307 rc = cros_ec_priv->ec_command(EC_CMD_REBOOT_EC,
David Hendricks14935fe2014-08-14 17:38:24 -0700308 0, &p, sizeof(p), NULL, 0);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800309 if (rc < 0) {
David Hendricksb907de32014-08-11 16:47:09 -0700310 msg_perr("CROS_EC cannot jump to [%s]:%d\n",
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800311 sections[p.cmd], rc);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800312 } else {
David Hendricksb907de32014-08-11 16:47:09 -0700313 msg_pdbg("CROS_EC has jumped to [%s]\n", sections[p.cmd]);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800314 rc = EC_RES_SUCCESS;
Souvik Ghosh586968a2016-08-11 17:56:24 -0700315 cros_ec_priv->current_image = target;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800316 }
317
318 /* Sleep 1 sec to wait the EC re-init. */
319 usleep(1000000);
320
David Hendricksf9461c72013-07-11 19:02:13 -0700321 /* update max data write size in case we're jumping to an EC
322 * firmware with different protocol */
Souvik Ghosh586968a2016-08-11 17:56:24 -0700323 set_ideal_write_size();
David Hendricksf9461c72013-07-11 19:02:13 -0700324
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800325 return rc;
326}
327
David Hendricksb64b39a2016-10-11 13:48:06 -0700328static int cros_ec_restore_wp(void *data)
329{
330 msg_pdbg("Restoring EC soft WP.\n");
331 return set_wp(1);
332}
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800333
David Hendricksb64b39a2016-10-11 13:48:06 -0700334static int cros_ec_wp_is_enabled(void)
335{
336 struct ec_params_flash_protect p;
337 struct ec_response_flash_protect r;
338 int rc;
339
340 memset(&p, 0, sizeof(p));
341 rc = cros_ec_priv->ec_command(EC_CMD_FLASH_PROTECT,
342 EC_VER_FLASH_PROTECT, &p, sizeof(p), &r, sizeof(r));
343 if (rc < 0) {
344 msg_perr("FAILED: Cannot get the write protection status: %d\n",
345 rc);
346 return -1;
347 } else if (rc < sizeof(r)) {
348 msg_perr("FAILED: Too little data returned (expected:%zd, "
349 "actual:%d)\n", sizeof(r), rc);
350 return -1;
351 }
352
353 if (r.flags & (EC_FLASH_PROTECT_RO_NOW | EC_FLASH_PROTECT_ALL_NOW))
354 return 1;
355
356 return 0;
357}
358
359/*
360 * Prepare EC for update:
361 * - Disable soft WP if needed.
362 * - Parse flashmap.
363 * - Jump to RO firmware.
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800364 */
David Hendricksac1d25c2016-08-09 17:00:58 -0700365int cros_ec_prepare(uint8_t *image, int size) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800366 struct fmap *fmap;
David Hendricksb64b39a2016-10-11 13:48:06 -0700367 int i, j, wp_status;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800368
Souvik Ghosh586968a2016-08-11 17:56:24 -0700369 if (!(cros_ec_priv && cros_ec_priv->detected)) return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800370
David Hendricksb64b39a2016-10-11 13:48:06 -0700371 /*
372 * If HW WP is disabled we may still need to disable write protection
373 * that is active on the EC. Otherwise the EC can reject erase/write
374 * commands.
375 *
376 * Failure is OK since HW WP might be enabled or the EC needs to be
377 * rebooted for the change to take effect. We can still update RW
378 * portions.
379 *
380 * If disabled here, EC WP will be restored at the end so that
381 * "--wp-enable" does not need to be run later. This greatly
382 * simplifies logic for developers and scripts.
383 */
384 wp_status = cros_ec_wp_is_enabled();
385 if (wp_status < 0) {
386 return 1;
387 } else if (wp_status == 1) {
388 msg_pdbg("Attempting to disable EC soft WP.\n");
389 if (!set_wp(0)) {
390 msg_pdbg("EC soft WP disabled successfully.\n");
391 if (register_shutdown(cros_ec_restore_wp, NULL))
392 return 1;
393 } else {
394 msg_pdbg("Failed. Hardware WP might in effect or EC "
395 "needs to be rebooted first.\n");
396 }
397 } else {
398 msg_pdbg("EC soft WP is already disabled.\n");
399 }
400
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800401 // Parse the fmap in the image file and cache the firmware ranges.
402 fmap = fmap_find_in_memory(image, size);
403 if (!fmap) return 0;
404
405 // Lookup RO/A/B sections in FMAP.
406 for (i = 0; i < fmap->nareas; i++) {
407 struct fmap_area *fa = &fmap->areas[i];
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800408 for (j = EC_IMAGE_RO; j < ARRAY_SIZE(sections); j++) {
David Hendricks5b06c882012-05-20 18:27:25 -0700409 if (!strcmp(sections[j], (const char *)fa->name)) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800410 msg_pdbg("Found '%s' in image.\n", fa->name);
411 memcpy(&fwcopy[j], fa, sizeof(*fa));
412 fwcopy[j].flags = 1; // mark as new
413 }
414 }
415 }
416
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800417 /* Warning: before update, we jump the EC to RO copy. If you want to
David Hendricksb907de32014-08-11 16:47:09 -0700418 * change this behavior, please also check the cros_ec_finish().
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800419 */
David Hendricksac1d25c2016-08-09 17:00:58 -0700420 return cros_ec_jump_copy(EC_IMAGE_RO);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800421}
422
423
424/* Returns >0 if we need 2nd pass of erase_and_write_flash().
425 * <0 if we cannot jump to any firmware copy.
426 * ==0 if no more pass is needed.
427 *
428 * This function also jumps to new-updated firmware copy before return >0.
429 */
David Hendricksac1d25c2016-08-09 17:00:58 -0700430int cros_ec_need_2nd_pass(void) {
Souvik Ghosh586968a2016-08-11 17:56:24 -0700431 if (!(cros_ec_priv && cros_ec_priv->detected)) return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800432
433 if (need_2nd_pass) {
David Hendricksac1d25c2016-08-09 17:00:58 -0700434 if (cros_ec_jump_copy(EC_IMAGE_UNKNOWN)) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800435 return -1;
436 }
437 }
438
439 return need_2nd_pass;
440}
441
442
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800443/* Returns 0 for success.
444 *
445 * Try latest firmware: B > A > RO
446 *
David Hendricksb907de32014-08-11 16:47:09 -0700447 * This function assumes the EC jumps to RO at cros_ec_prepare() so that
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800448 * the fwcopy[RO].flags is old (0) and A/B are new. Please also refine
David Hendricksb907de32014-08-11 16:47:09 -0700449 * this code logic if you change the cros_ec_prepare() behavior.
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800450 */
David Hendricksac1d25c2016-08-09 17:00:58 -0700451int cros_ec_finish(void) {
Souvik Ghosh586968a2016-08-11 17:56:24 -0700452 if (!(cros_ec_priv && cros_ec_priv->detected)) return 0;
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800453
454 if (try_latest_firmware) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800455 if (fwcopy[EC_IMAGE_RW].flags &&
David Hendricksac1d25c2016-08-09 17:00:58 -0700456 cros_ec_jump_copy(EC_IMAGE_RW) == 0) return 0;
457 return cros_ec_jump_copy(EC_IMAGE_RO);
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800458 }
459
460 return 0;
461}
462
463
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700464int cros_ec_read(struct flashctx *flash, uint8_t *readarr,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800465 unsigned int blockaddr, unsigned int readcnt) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800466 int rc = 0;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800467 struct ec_params_flash_read p;
David Hendricksac1d25c2016-08-09 17:00:58 -0700468 int maxlen = opaque_programmer->max_data_read;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800469 uint8_t buf[maxlen];
David Hendricks133083b2012-07-17 20:39:38 -0700470 int offset = 0, count;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800471
David Hendricks133083b2012-07-17 20:39:38 -0700472 while (offset < readcnt) {
473 count = min(maxlen, readcnt - offset);
474 p.offset = blockaddr + offset;
475 p.size = count;
Souvik Ghosh586968a2016-08-11 17:56:24 -0700476 rc = cros_ec_priv->ec_command(EC_CMD_FLASH_READ,
David Hendricks14935fe2014-08-14 17:38:24 -0700477 0, &p, sizeof(p), buf, count);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800478 if (rc < 0) {
David Hendricksb907de32014-08-11 16:47:09 -0700479 msg_perr("CROS_EC: Flash read error at offset 0x%x\n",
David Hendricks133083b2012-07-17 20:39:38 -0700480 blockaddr + offset);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800481 return rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800482 } else {
483 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800484 }
485
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800486 memcpy(readarr + offset, buf, count);
David Hendricks133083b2012-07-17 20:39:38 -0700487 offset += count;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800488 }
489
490 return rc;
491}
492
493
Simon Glassc453a642013-07-01 18:08:53 +0900494/*
495 * returns 0 to indicate area does not overlap current EC image
496 * returns 1 to indicate area overlaps current EC image or error
497 */
Souvik Ghosh586968a2016-08-11 17:56:24 -0700498static int in_current_image(unsigned int addr, unsigned int len)
Simon Glassc453a642013-07-01 18:08:53 +0900499{
Simon Glassc453a642013-07-01 18:08:53 +0900500 enum ec_current_image image;
501 uint32_t region_offset;
502 uint32_t region_size;
503
Souvik Ghosh586968a2016-08-11 17:56:24 -0700504 image = cros_ec_priv->current_image;
505 region_offset = cros_ec_priv->region[image].offset;
506 region_size = cros_ec_priv->region[image].size;
Simon Glassc453a642013-07-01 18:08:53 +0900507
508 if ((addr + len - 1 < region_offset) ||
509 (addr > region_offset + region_size - 1)) {
510 return 0;
511 }
512 return 1;
513}
514
515
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700516int cros_ec_block_erase(struct flashctx *flash,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800517 unsigned int blockaddr,
518 unsigned int len) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800519 struct ec_params_flash_erase erase;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800520 int rc;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800521
Souvik Ghosh586968a2016-08-11 17:56:24 -0700522 if (in_current_image(blockaddr, len)) {
David Hendricksb907de32014-08-11 16:47:09 -0700523 cros_ec_invalidate_copy(blockaddr, len);
Simon Glassc453a642013-07-01 18:08:53 +0900524 need_2nd_pass = 1;
525 return ACCESS_DENIED;
526 }
527
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800528 erase.offset = blockaddr;
529 erase.size = len;
Souvik Ghosh586968a2016-08-11 17:56:24 -0700530 rc = cros_ec_priv->ec_command(EC_CMD_FLASH_ERASE,
David Hendricks14935fe2014-08-14 17:38:24 -0700531 0, &erase, sizeof(erase), NULL, 0);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800532 if (rc == -EC_RES_ACCESS_DENIED) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800533 // this is active image.
David Hendricksb907de32014-08-11 16:47:09 -0700534 cros_ec_invalidate_copy(blockaddr, len);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800535 need_2nd_pass = 1;
536 return ACCESS_DENIED;
537 }
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800538 if (rc < 0) {
David Hendricksb907de32014-08-11 16:47:09 -0700539 msg_perr("CROS_EC: Flash erase error at address 0x%x, rc=%d\n",
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800540 blockaddr, rc);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800541 return rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800542 } else {
543 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800544 }
545
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800546#ifndef SOFTWARE_SYNC_ENABLED
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800547 try_latest_firmware = 1;
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800548#endif
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800549 return rc;
550}
551
552
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700553int cros_ec_write(struct flashctx *flash, uint8_t *buf, unsigned int addr,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800554 unsigned int nbytes) {
555 int i, rc = 0;
Ken Chang69c31b82014-10-28 15:17:21 +0800556 unsigned int written = 0, real_write_size;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800557 struct ec_params_flash_write p;
David Hendricks2d6db772013-07-10 21:07:48 -0700558 uint8_t *packet;
559
Ken Chang69c31b82014-10-28 15:17:21 +0800560 /*
561 * For chrome-os-partner:33035, to workaround the undersized
562 * outdata buffer issue in kernel.
563 */
David Hendricksac1d25c2016-08-09 17:00:58 -0700564 real_write_size = min(opaque_programmer->max_data_write,
Souvik Ghosh586968a2016-08-11 17:56:24 -0700565 cros_ec_priv->ideal_write_size);
Ken Chang69c31b82014-10-28 15:17:21 +0800566 packet = malloc(sizeof(p) + real_write_size);
David Hendricks2d6db772013-07-10 21:07:48 -0700567 if (!packet)
568 return -1;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800569
570 for (i = 0; i < nbytes; i += written) {
Ken Chang69c31b82014-10-28 15:17:21 +0800571 written = min(nbytes - i, real_write_size);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800572 p.offset = addr + i;
573 p.size = written;
Simon Glassc453a642013-07-01 18:08:53 +0900574
Souvik Ghosh586968a2016-08-11 17:56:24 -0700575 if (in_current_image(p.offset, p.size)) {
David Hendricksb907de32014-08-11 16:47:09 -0700576 cros_ec_invalidate_copy(addr, nbytes);
Simon Glassc453a642013-07-01 18:08:53 +0900577 need_2nd_pass = 1;
578 return ACCESS_DENIED;
579 }
580
David Hendricks2d6db772013-07-10 21:07:48 -0700581 memcpy(packet, &p, sizeof(p));
582 memcpy(packet + sizeof(p), &buf[i], written);
Souvik Ghosh586968a2016-08-11 17:56:24 -0700583 rc = cros_ec_priv->ec_command(EC_CMD_FLASH_WRITE,
David Hendricks14935fe2014-08-14 17:38:24 -0700584 0, packet, sizeof(p) + p.size, NULL, 0);
David Hendricks2d6db772013-07-10 21:07:48 -0700585
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800586 if (rc == -EC_RES_ACCESS_DENIED) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800587 // this is active image.
David Hendricksb907de32014-08-11 16:47:09 -0700588 cros_ec_invalidate_copy(addr, nbytes);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800589 need_2nd_pass = 1;
590 return ACCESS_DENIED;
591 }
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800592
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800593 if (rc < 0) break;
594 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800595 }
596
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800597#ifndef SOFTWARE_SYNC_ENABLED
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800598 try_latest_firmware = 1;
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800599#endif
David Hendricks2d6db772013-07-10 21:07:48 -0700600 free(packet);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800601 return rc;
602}
603
604
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700605static int cros_ec_list_ranges(const struct flashctx *flash) {
Simon Glass3c01dca2013-07-01 18:07:34 +0900606 struct ec_response_flash_region_info info;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800607 int rc;
608
Souvik Ghosh586968a2016-08-11 17:56:24 -0700609 rc = cros_ec_get_region_info(EC_FLASH_REGION_WP_RO, &info);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800610 if (rc < 0) {
611 msg_perr("Cannot get the WP_RO region info: %d\n", rc);
612 return 1;
613 }
614
615 msg_pinfo("Supported write protect range:\n");
616 msg_pinfo(" disable: start=0x%06x len=0x%06x\n", 0, 0);
Simon Glass3c01dca2013-07-01 18:07:34 +0900617 msg_pinfo(" enable: start=0x%06x len=0x%06x\n", info.offset,
618 info.size);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800619
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800620 return 0;
621}
622
623
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800624/*
625 * Helper function for flash protection.
626 *
627 * On EC API v1, the EC write protection has been simplified to one-bit:
628 * EC_FLASH_PROTECT_RO_AT_BOOT, which means the state is either enabled
629 * or disabled. However, this is different from the SPI-style write protect
630 * behavior. Thus, we re-define the flashrom command (SPI-style) so that
631 * either SRP or range is non-zero, the EC_FLASH_PROTECT_RO_AT_BOOT is set.
632 *
633 * SRP Range | PROTECT_RO_AT_BOOT
634 * 0 0 | 0
635 * 0 non-zero | 1
636 * 1 0 | 1
637 * 1 non-zero | 1
638 *
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800639 *
640 * Besides, to make the protection take effect as soon as possible, we
641 * try to set EC_FLASH_PROTECT_RO_NOW at the same time. However, not
642 * every EC supports RO_NOW, thus we then try to protect the entire chip.
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800643 */
David Hendricksac1d25c2016-08-09 17:00:58 -0700644static int set_wp(int enable) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800645 struct ec_params_flash_protect p;
646 struct ec_response_flash_protect r;
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800647 const int ro_at_boot_flag = EC_FLASH_PROTECT_RO_AT_BOOT;
648 const int ro_now_flag = EC_FLASH_PROTECT_RO_NOW;
649 int need_an_ec_cold_reset = 0;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800650 int rc;
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800651
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800652 /* Try to set RO_AT_BOOT and RO_NOW first */
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800653 memset(&p, 0, sizeof(p));
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800654 p.mask = (ro_at_boot_flag | ro_now_flag);
655 p.flags = enable ? (ro_at_boot_flag | ro_now_flag) : 0;
Souvik Ghosh586968a2016-08-11 17:56:24 -0700656 rc = cros_ec_priv->ec_command(EC_CMD_FLASH_PROTECT,
David Hendricks14935fe2014-08-14 17:38:24 -0700657 EC_VER_FLASH_PROTECT, &p, sizeof(p), &r, sizeof(r));
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800658 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800659 msg_perr("FAILED: Cannot set the RO_AT_BOOT and RO_NOW: %d\n",
660 rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800661 return 1;
662 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800663
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800664 /* Read back */
665 memset(&p, 0, sizeof(p));
Souvik Ghosh586968a2016-08-11 17:56:24 -0700666 rc = cros_ec_priv->ec_command(EC_CMD_FLASH_PROTECT,
David Hendricks14935fe2014-08-14 17:38:24 -0700667 EC_VER_FLASH_PROTECT, &p, sizeof(p), &r, sizeof(r));
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800668 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800669 msg_perr("FAILED: Cannot get RO_AT_BOOT and RO_NOW: %d\n",
670 rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800671 return 1;
672 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800673
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800674 if (!enable) {
675 /* The disable case is easier to check. */
676 if (r.flags & ro_at_boot_flag) {
677 msg_perr("FAILED: RO_AT_BOOT is not clear.\n");
678 return 1;
679 } else if (r.flags & ro_now_flag) {
680 msg_perr("FAILED: RO_NOW is asserted unexpectedly.\n");
681 need_an_ec_cold_reset = 1;
682 goto exit;
683 }
684
685 msg_pdbg("INFO: RO_AT_BOOT is clear.\n");
686 return 0;
687 }
688
689 /* Check if RO_AT_BOOT is set. If not, fail in anyway. */
690 if (r.flags & ro_at_boot_flag) {
691 msg_pdbg("INFO: RO_AT_BOOT has been set.\n");
692 } else {
693 msg_perr("FAILED: RO_AT_BOOT is not set.\n");
694 return 1;
695 }
696
697 /* Then, we check if the protection has been activated. */
698 if (r.flags & ro_now_flag) {
699 /* Good, RO_NOW is set. */
700 msg_pdbg("INFO: RO_NOW is set. WP is active now.\n");
701 } else if (r.writable_flags & EC_FLASH_PROTECT_ALL_NOW) {
702 struct ec_params_reboot_ec reboot;
703
704 msg_pdbg("WARN: RO_NOW is not set. Trying ALL_NOW.\n");
705
706 memset(&p, 0, sizeof(p));
707 p.mask = EC_FLASH_PROTECT_ALL_NOW;
708 p.flags = EC_FLASH_PROTECT_ALL_NOW;
Souvik Ghosh586968a2016-08-11 17:56:24 -0700709 rc = cros_ec_priv->ec_command(EC_CMD_FLASH_PROTECT,
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800710 EC_VER_FLASH_PROTECT,
711 &p, sizeof(p), &r, sizeof(r));
712 if (rc < 0) {
713 msg_perr("FAILED: Cannot set ALL_NOW: %d\n", rc);
714 return 1;
715 }
716
717 /* Read back */
718 memset(&p, 0, sizeof(p));
Souvik Ghosh586968a2016-08-11 17:56:24 -0700719 rc = cros_ec_priv->ec_command(EC_CMD_FLASH_PROTECT,
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800720 EC_VER_FLASH_PROTECT,
721 &p, sizeof(p), &r, sizeof(r));
722 if (rc < 0) {
723 msg_perr("FAILED:Cannot get ALL_NOW: %d\n", rc);
724 return 1;
725 }
726
727 if (!(r.flags & EC_FLASH_PROTECT_ALL_NOW)) {
728 msg_perr("FAILED: ALL_NOW is not set.\n");
729 need_an_ec_cold_reset = 1;
730 goto exit;
731 }
732
733 msg_pdbg("INFO: ALL_NOW has been set. WP is active now.\n");
734
735 /*
736 * Our goal is to protect the RO ASAP. The entire protection
737 * is just a workaround for platform not supporting RO_NOW.
738 * It has side-effect that the RW is also protected and leads
739 * the RW update failed. So, we arrange an EC code reset to
740 * unlock RW ASAP.
741 */
742 memset(&reboot, 0, sizeof(reboot));
743 reboot.cmd = EC_REBOOT_COLD;
744 reboot.flags = EC_REBOOT_FLAG_ON_AP_SHUTDOWN;
Souvik Ghosh586968a2016-08-11 17:56:24 -0700745 rc = cros_ec_priv->ec_command(EC_CMD_REBOOT_EC,
David Hendricks14935fe2014-08-14 17:38:24 -0700746 0, &reboot, sizeof(reboot), NULL, 0);
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800747 if (rc < 0) {
748 msg_perr("WARN: Cannot arrange a cold reset at next "
749 "shutdown to unlock entire protect.\n");
750 msg_perr(" But you can do it manually.\n");
751 } else {
752 msg_pdbg("INFO: A cold reset is arranged at next "
753 "shutdown.\n");
754 }
755
756 } else {
757 msg_perr("FAILED: RO_NOW is not set.\n");
758 msg_perr("FAILED: The PROTECT_RO_AT_BOOT is set, but cannot "
759 "make write protection active now.\n");
760 need_an_ec_cold_reset = 1;
761 }
762
763exit:
764 if (need_an_ec_cold_reset) {
765 msg_perr("FAILED: You may need a reboot to take effect of "
766 "PROTECT_RO_AT_BOOT.\n");
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800767 return 1;
768 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800769
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800770 return 0;
771}
772
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700773static int cros_ec_set_range(const struct flashctx *flash,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800774 unsigned int start, unsigned int len) {
Simon Glass3c01dca2013-07-01 18:07:34 +0900775 struct ec_response_flash_region_info info;
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800776 int rc;
777
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800778 /* Check if the given range is supported */
Souvik Ghosh586968a2016-08-11 17:56:24 -0700779 rc = cros_ec_get_region_info(EC_FLASH_REGION_WP_RO, &info);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800780 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800781 msg_perr("FAILED: Cannot get the WP_RO region info: %d\n", rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800782 return 1;
783 }
784 if ((!start && !len) || /* list supported ranges */
Simon Glass3c01dca2013-07-01 18:07:34 +0900785 ((start == info.offset) && (len == info.size))) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800786 /* pass */
787 } else {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800788 msg_perr("FAILED: Unsupported write protection range "
789 "(0x%06x,0x%06x)\n\n", start, len);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800790 msg_perr("Currently supported range:\n");
791 msg_perr(" disable: (0x%06x,0x%06x)\n", 0, 0);
Simon Glass3c01dca2013-07-01 18:07:34 +0900792 msg_perr(" enable: (0x%06x,0x%06x)\n", info.offset,
793 info.size);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800794 return 1;
795 }
796
David Hendricks393deec2016-11-23 16:15:05 -0800797 if (ignore_wp_range_command)
798 return 0;
David Hendricksac1d25c2016-08-09 17:00:58 -0700799 return set_wp(!!len);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800800}
801
802
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700803static int cros_ec_enable_writeprotect(const struct flashctx *flash,
David Hendricks1c09f802012-10-03 11:03:48 -0700804 enum wp_mode wp_mode) {
805 int ret;
806
807 switch (wp_mode) {
808 case WP_MODE_HARDWARE:
David Hendricksac1d25c2016-08-09 17:00:58 -0700809 ret = set_wp(1);
David Hendricks1c09f802012-10-03 11:03:48 -0700810 break;
811 default:
812 msg_perr("%s():%d Unsupported write-protection mode\n",
813 __func__, __LINE__);
814 ret = 1;
815 break;
816 }
817
818 return ret;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800819}
820
821
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700822static int cros_ec_disable_writeprotect(const struct flashctx *flash) {
David Hendricks393deec2016-11-23 16:15:05 -0800823 /* --wp-range implicitly enables write protection on CrOS EC, so force
824 it not to if --wp-disable is what the user really wants. */
825 ignore_wp_range_command = 1;
David Hendricksac1d25c2016-08-09 17:00:58 -0700826 return set_wp(0);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800827}
828
829
Souvik Ghosh586968a2016-08-11 17:56:24 -0700830static int cros_ec_wp_status(const struct flashctx *flash) {;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800831 struct ec_params_flash_protect p;
832 struct ec_response_flash_protect r;
833 int start, len; /* wp range */
834 int enabled;
835 int rc;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800836
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800837 memset(&p, 0, sizeof(p));
Souvik Ghosh586968a2016-08-11 17:56:24 -0700838 rc = cros_ec_priv->ec_command(EC_CMD_FLASH_PROTECT,
David Hendricks14935fe2014-08-14 17:38:24 -0700839 EC_VER_FLASH_PROTECT, &p, sizeof(p), &r, sizeof(r));
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800840 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800841 msg_perr("FAILED: Cannot get the write protection status: %d\n",
842 rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800843 return 1;
844 } else if (rc < sizeof(r)) {
David Hendricksf797dde2012-10-30 11:39:12 -0700845 msg_perr("FAILED: Too little data returned (expected:%zd, "
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800846 "actual:%d)\n", sizeof(r), rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800847 return 1;
848 }
849
850 start = len = 0;
851 if (r.flags & EC_FLASH_PROTECT_RO_AT_BOOT) {
Simon Glass3c01dca2013-07-01 18:07:34 +0900852 struct ec_response_flash_region_info info;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800853
854 msg_pdbg("%s(): EC_FLASH_PROTECT_RO_AT_BOOT is set.\n",
855 __func__);
Souvik Ghosh586968a2016-08-11 17:56:24 -0700856 rc = cros_ec_get_region_info(EC_FLASH_REGION_WP_RO, &info);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800857 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800858 msg_perr("FAILED: Cannot get the WP_RO region info: "
859 "%d\n", rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800860 return 1;
861 }
Simon Glass3c01dca2013-07-01 18:07:34 +0900862 start = info.offset;
863 len = info.size;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800864 } else {
865 msg_pdbg("%s(): EC_FLASH_PROTECT_RO_AT_BOOT is clear.\n",
866 __func__);
867 }
868
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800869 /*
870 * If neither RO_NOW or ALL_NOW is set, it means write protect is
871 * NOT active now.
872 */
873 if (!(r.flags & (EC_FLASH_PROTECT_RO_NOW | EC_FLASH_PROTECT_ALL_NOW)))
874 start = len = 0;
875
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800876 /* Remove the SPI-style messages. */
877 enabled = r.flags & EC_FLASH_PROTECT_RO_AT_BOOT ? 1 : 0;
878 msg_pinfo("WP: status: 0x%02x\n", enabled ? 0x80 : 0x00);
879 msg_pinfo("WP: status.srp0: %x\n", enabled);
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800880 msg_pinfo("WP: write protect is %s.\n",
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800881 enabled ? "enabled" : "disabled");
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800882 msg_pinfo("WP: write protect range: start=0x%08x, len=0x%08x\n",
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800883 start, len);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800884
885 return 0;
886}
887
David Hendrickse5454932013-11-04 18:16:11 -0800888/* perform basic "hello" test to see if we can talk to the EC */
David Hendricksb907de32014-08-11 16:47:09 -0700889int cros_ec_test(struct cros_ec_priv *priv)
David Hendrickse5454932013-11-04 18:16:11 -0800890{
891 struct ec_params_hello request;
892 struct ec_response_hello response;
David Hendrickse5454932013-11-04 18:16:11 -0800893 int rc = 0;
894
895 /* Say hello to EC. */
896 request.in_data = 0xf0e0d0c0; /* Expect EC will add on 0x01020304. */
897 msg_pdbg("%s: sending HELLO request with 0x%08x\n",
898 __func__, request.in_data);
Gwendal Grignou94e87d62014-11-25 15:34:15 -0800899 rc = priv->ec_command(EC_CMD_HELLO, 0, &request,
David Hendrickse5454932013-11-04 18:16:11 -0800900 sizeof(request), &response, sizeof(response));
901 msg_pdbg("%s: response: 0x%08x\n", __func__, response.out_data);
902
903 if (rc < 0 || response.out_data != 0xf1e2d3c4) {
904 msg_pdbg("response.out_data is not 0xf1e2d3c4.\n"
905 "rc=%d, request=0x%x response=0x%x\n",
906 rc, request.in_data, response.out_data);
907 return 1;
908 }
909
910 return 0;
911}
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800912
David Hendricksd13d90d2016-08-09 17:00:52 -0700913void cros_ec_set_max_size(struct cros_ec_priv *priv,
914 struct opaque_programmer *op) {
Puthikorn Voravootivatc0993cf2014-08-28 16:04:58 -0700915 struct ec_response_get_protocol_info info;
916 int rc = 0;
917 msg_pdbg("%s: sending protoinfo command\n", __func__);
Gwendal Grignou94e87d62014-11-25 15:34:15 -0800918 rc = priv->ec_command(EC_CMD_GET_PROTOCOL_INFO, 0, NULL, 0,
Puthikorn Voravootivatc0993cf2014-08-28 16:04:58 -0700919 &info, sizeof(info));
920 msg_pdbg("%s: rc:%d\n", __func__, rc);
921
922 if (rc == sizeof(info)) {
923 op->max_data_write = min(op->max_data_write,
924 info.max_request_packet_size -
925 sizeof(struct ec_host_request));
926 op->max_data_read = min(op->max_data_read,
927 info.max_response_packet_size -
928 sizeof(struct ec_host_response));
929 msg_pdbg("%s: max_write:%d max_read:%d\n", __func__,
930 op->max_data_write, op->max_data_read);
931 }
932}
933
David Hendricks14935fe2014-08-14 17:38:24 -0700934
935/*
David Hendricks052446b2014-09-11 11:26:51 -0700936 * Returns 0 to indicate success, non-zero otherwise
David Hendricks14935fe2014-08-14 17:38:24 -0700937 *
938 * This function parses programmer parameters from the command line. Since
939 * CrOS EC hangs off the "internal programmer" (AP, PCH, etc) this gets
940 * run during internal programmer initialization.
941 */
942int cros_ec_parse_param(struct cros_ec_priv *priv)
943{
David Hendricks98b3c572016-11-30 01:50:08 +0000944 char *p;
Souvik Ghoshf1608b42016-06-30 16:03:55 -0700945
David Hendricks98b3c572016-11-30 01:50:08 +0000946 p = extract_programmer_param("dev");
947 if (p) {
David Hendricks14935fe2014-08-14 17:38:24 -0700948 unsigned int index;
949 char *endptr = NULL;
950
951 errno = 0;
Gwendal Grignou94e87d62014-11-25 15:34:15 -0800952 /*
953 * For backward compatibility, check if the index is
954 * a number: 0: main EC, 1: PD
955 * works only on Samus.
956 */
David Hendricks98b3c572016-11-30 01:50:08 +0000957 index = strtoul(p, &endptr, 10);
958 if (errno || (endptr != (p + 1)) || (strlen(p) > 1)) {
959 msg_perr("Invalid argument: \"%s\"\n", p);
960 return 1;
David Hendricks14935fe2014-08-14 17:38:24 -0700961 }
962
Gwendal Grignou94e87d62014-11-25 15:34:15 -0800963 if (index > 1) {
David Hendricks14935fe2014-08-14 17:38:24 -0700964 msg_perr("%s: Invalid device index\n", __func__);
David Hendricks98b3c572016-11-30 01:50:08 +0000965 return 1;
David Hendricks14935fe2014-08-14 17:38:24 -0700966 }
Gwendal Grignou94e87d62014-11-25 15:34:15 -0800967 priv->dev = ec_type[index];
968 msg_pdbg("Target %s used\n", priv->dev);
969 }
David Hendricks14935fe2014-08-14 17:38:24 -0700970
David Hendricks98b3c572016-11-30 01:50:08 +0000971 p = extract_programmer_param("type");
972 if (p) {
Gwendal Grignou94e87d62014-11-25 15:34:15 -0800973 unsigned int index;
974 for (index = 0; index < ARRAY_SIZE(ec_type); index++)
David Hendricks98b3c572016-11-30 01:50:08 +0000975 if (!strcmp(p, ec_type[index]))
Gwendal Grignou94e87d62014-11-25 15:34:15 -0800976 break;
977 if (index == ARRAY_SIZE(ec_type)) {
David Hendricks98b3c572016-11-30 01:50:08 +0000978 msg_perr("Invalid argument: \"%s\"\n", p);
979 return 1;
Gwendal Grignou94e87d62014-11-25 15:34:15 -0800980 }
981 priv->dev = ec_type[index];
982 msg_pdbg("Target %s used\n", priv->dev);
David Hendricks14935fe2014-08-14 17:38:24 -0700983 }
984
David Hendricks98b3c572016-11-30 01:50:08 +0000985 p = extract_programmer_param("block");
986 if (p) {
987 unsigned int block;
Duncan Laurie84328722014-09-10 23:25:01 -0700988 char *endptr = NULL;
989
990 errno = 0;
David Hendricks98b3c572016-11-30 01:50:08 +0000991 block = strtoul(p, &endptr, 0);
992 if (errno || (strlen(p) > 10) || (endptr != (p + strlen(p)))) {
993 msg_perr("Invalid argument: \"%s\"\n", p);
994 return 1;
Duncan Laurie84328722014-09-10 23:25:01 -0700995 }
996
David Hendricks98b3c572016-11-30 01:50:08 +0000997 if (block <= 0) {
Duncan Laurie84328722014-09-10 23:25:01 -0700998 msg_perr("%s: Invalid block size\n", __func__);
David Hendricks98b3c572016-11-30 01:50:08 +0000999 return 1;
Duncan Laurie84328722014-09-10 23:25:01 -07001000 }
1001
David Hendricks98b3c572016-11-30 01:50:08 +00001002 msg_pdbg("Override block size to 0x%x\n", block);
1003 priv->erase_block_size = block;
Duncan Laurie84328722014-09-10 23:25:01 -07001004 }
1005
David Hendricks98b3c572016-11-30 01:50:08 +00001006 return 0;
David Hendricks14935fe2014-08-14 17:38:24 -07001007}
1008
Souvik Ghoshd75cd672016-06-17 14:21:39 -07001009int cros_ec_probe_size(struct flashctx *flash) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +08001010 int rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +08001011 struct ec_response_flash_info info;
David Hendricksa672b042016-09-19 12:37:36 -07001012 struct ec_response_flash_spi_info spi_info;
David Hendricks194b3bb2013-07-16 14:32:26 -07001013 struct ec_response_get_chip_info chip_info;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +08001014 struct block_eraser *eraser;
1015 static struct wp wp = {
David Hendricksb907de32014-08-11 16:47:09 -07001016 .list_ranges = cros_ec_list_ranges,
1017 .set_range = cros_ec_set_range,
1018 .enable = cros_ec_enable_writeprotect,
1019 .disable = cros_ec_disable_writeprotect,
1020 .wp_status = cros_ec_wp_status,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +08001021 };
1022
Souvik Ghosh586968a2016-08-11 17:56:24 -07001023 rc = cros_ec_priv->ec_command(EC_CMD_FLASH_INFO,
David Hendricks14935fe2014-08-14 17:38:24 -07001024 0, NULL, 0, &info, sizeof(info));
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +08001025 if (rc < 0) {
1026 msg_perr("%s(): FLASH_INFO returns %d.\n", __func__, rc);
1027 return 0;
1028 }
Souvik Ghosh586968a2016-08-11 17:56:24 -07001029 rc = cros_ec_get_current_image();
Simon Glass01c11672013-07-01 18:03:33 +09001030 if (rc < 0) {
1031 msg_perr("%s(): Failed to probe (no current image): %d\n",
1032 __func__, rc);
1033 return 0;
1034 }
Souvik Ghosh586968a2016-08-11 17:56:24 -07001035 cros_ec_priv->current_image = rc;
1036 cros_ec_priv->region = &regions[0];
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +08001037
1038 flash->total_size = info.flash_size / 1024;
David Hendricksac1d25c2016-08-09 17:00:58 -07001039 flash->page_size = opaque_programmer->max_data_read;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +08001040 eraser = &flash->block_erasers[0];
Duncan Laurie84328722014-09-10 23:25:01 -07001041
1042 /* Allow overriding the erase block size in case EC is incorrect */
Souvik Ghosh586968a2016-08-11 17:56:24 -07001043 if (cros_ec_priv->erase_block_size > 0)
1044 eraser->eraseblocks[0].size = cros_ec_priv->erase_block_size;
Duncan Laurie84328722014-09-10 23:25:01 -07001045 else
1046 eraser->eraseblocks[0].size = info.erase_block_size;
1047
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +08001048 eraser->eraseblocks[0].count = info.flash_size /
1049 eraser->eraseblocks[0].size;
1050 flash->wp = &wp;
1051
David Hendricks194b3bb2013-07-16 14:32:26 -07001052 /*
1053 * Some STM32 variants erase bits to 0. For now, assume that this
1054 * applies to STM32L parts.
1055 *
1056 * FIXME: This info will eventually be exposed via some EC command.
1057 * See chrome-os-partner:20973.
1058 */
Souvik Ghosh586968a2016-08-11 17:56:24 -07001059 rc = cros_ec_priv->ec_command(EC_CMD_GET_CHIP_INFO,
David Hendricks14935fe2014-08-14 17:38:24 -07001060 0, NULL, 0, &chip_info, sizeof(chip_info));
David Hendricks194b3bb2013-07-16 14:32:26 -07001061 if (rc < 0) {
1062 msg_perr("%s(): CHIP_INFO returned %d.\n", __func__, rc);
1063 return 0;
1064 }
1065 if (!strncmp(chip_info.name, "stm32l", 6))
1066 flash->feature_bits |= FEATURE_ERASE_TO_ZERO;
1067
Souvik Ghosh586968a2016-08-11 17:56:24 -07001068 rc = set_ideal_write_size();
David Hendricksfbd5e6d2014-08-21 15:01:43 -07001069 if (rc < 0) {
1070 msg_perr("%s(): Unable to set write size\n", __func__);
1071 return 0;
1072 }
David Hendricksf9461c72013-07-11 19:02:13 -07001073
David Hendricksa672b042016-09-19 12:37:36 -07001074 rc = cros_ec_priv->ec_command(EC_CMD_FLASH_SPI_INFO,
1075 0, NULL, 0, &spi_info, sizeof(spi_info));
1076 if (rc < 0) {
1077 static char chip_vendor[32];
1078 static char chip_name[32];
1079
1080 memcpy(chip_vendor, chip_info.vendor, sizeof(chip_vendor));
1081 memcpy(chip_name, chip_info.name, sizeof(chip_name));
1082 flash->vendor = chip_vendor;
1083 flash->name = chip_name;
1084 flash->tested = TEST_OK_PREWU;
1085 } else {
1086 const struct flashchip *f;
1087 uint32_t mfg = spi_info.jedec[0];
1088 uint32_t model = (spi_info.jedec[1] << 8) | spi_info.jedec[2];
1089
1090 for (f = flashchips; f && f->name; f++) {
1091 if (f->bustype != BUS_SPI)
1092 continue;
1093 if ((f->manufacture_id == mfg) &&
1094 f->model_id == model) {
1095 flash->vendor = f->vendor;
1096 flash->name = f->name;
1097 flash->tested = f->tested;
1098 break;
1099 }
1100 }
1101 }
1102
Simon Glassc453a642013-07-01 18:08:53 +09001103 /* FIXME: EC_IMAGE_* is ordered differently from EC_FLASH_REGION_*,
1104 * so we need to be careful about using these enums as array indices */
Souvik Ghosh586968a2016-08-11 17:56:24 -07001105 rc = cros_ec_get_region_info(EC_FLASH_REGION_RO,
1106 &cros_ec_priv->region[EC_IMAGE_RO]);
Simon Glassc453a642013-07-01 18:08:53 +09001107 if (rc) {
1108 msg_perr("%s(): Failed to probe (cannot find RO region): %d\n",
1109 __func__, rc);
1110 return 0;
1111 }
1112
Souvik Ghosh586968a2016-08-11 17:56:24 -07001113 rc = cros_ec_get_region_info(EC_FLASH_REGION_RW,
1114 &cros_ec_priv->region[EC_IMAGE_RW]);
Simon Glassc453a642013-07-01 18:08:53 +09001115 if (rc) {
1116 msg_perr("%s(): Failed to probe (cannot find RW region): %d\n",
1117 __func__, rc);
1118 return 0;
1119 }
1120
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +08001121 return 1;
1122};