blob: 56e6c44d6060fc502c0cd617e62f93554b99fda9 [file] [log] [blame]
Angel Pons986d50e2020-04-02 23:48:53 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -06002
3#include <assert.h>
4#include <cbfs.h>
5#include <console/console.h>
6#include <delay.h>
7#include <ec/google/chromeec/ec.h>
Tim Wawrzynczak14dd0732019-12-06 09:28:29 -07008#include <halt.h>
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -06009#include <security/vboot/misc.h>
10#include <security/vboot/vbnv.h>
11#include <security/vboot/vboot_common.h>
12#include <timer.h>
Tim Wawrzynczakf9e74992019-10-25 14:59:43 -060013#include <timestamp.h>
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -060014#include <vb2_api.h>
15
16#define _EC_FILENAME(select, suffix) \
17 (select == VB_SELECT_FIRMWARE_READONLY ? "ecro" suffix : "ecrw" suffix)
18#define EC_IMAGE_FILENAME(select) _EC_FILENAME(select, "")
19#define EC_HASH_FILENAME(select) _EC_FILENAME(select, ".hash")
20
21/* Wait 10 ms between attempts to check if EC's hash is ready */
22#define CROS_EC_HASH_CHECK_DELAY_MS 10
23/* Give the EC 2 seconds to finish calculating its hash */
24#define CROS_EC_HASH_TIMEOUT_MS 2000
25
26/* Wait 3 seconds after software sync for EC to clear the limit power flag. */
27#define LIMIT_POWER_WAIT_TIMEOUT_MS 3000
28/* Check the limit power flag every 10 ms while waiting. */
29#define LIMIT_POWER_POLL_SLEEP_MS 10
30
31/* Wait 3 seconds for EC to sysjump to RW */
32#define CROS_EC_SYSJUMP_TIMEOUT_MS 3000
33
34/*
35 * The external API for EC software sync. This function calls into
36 * vboot, which kicks off the process. Vboot runs the verified boot
37 * logic, and requires the client program to provide callbacks which
38 * perform the work.
39 */
40void vboot_sync_ec(void)
41{
42 vb2_error_t retval = VB2_SUCCESS;
43 struct vb2_context *ctx;
44
Jakub Czapigaad6157e2022-02-15 11:50:31 +010045 timestamp_add_now(TS_EC_SYNC_START);
Tim Wawrzynczakf9e74992019-10-25 14:59:43 -060046
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -060047 ctx = vboot_get_context();
48 ctx->flags |= VB2_CONTEXT_EC_SYNC_SUPPORTED;
49
50 retval = vb2api_ec_sync(ctx);
dnojiridff56a02020-04-03 10:56:43 -070051 vboot_save_data(ctx);
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -060052
Tim Wawrzynczak14dd0732019-12-06 09:28:29 -070053 switch (retval) {
54 case VB2_SUCCESS:
55 break;
56
Yu-Ping Wu30322782020-04-17 18:39:01 +080057 case VB2_REQUEST_REBOOT_EC_TO_RO:
Tim Wawrzynczak14dd0732019-12-06 09:28:29 -070058 printk(BIOS_INFO, "EC Reboot requested. Doing cold reboot\n");
59 if (google_chromeec_reboot(0, EC_REBOOT_COLD, 0))
60 printk(BIOS_EMERG, "Failed to get EC to cold reboot\n");
61
62 halt();
63 break;
64
65 /* Only for EC-EFS */
Yu-Ping Wu30322782020-04-17 18:39:01 +080066 case VB2_REQUEST_REBOOT_EC_SWITCH_RW:
Tim Wawrzynczak14dd0732019-12-06 09:28:29 -070067 printk(BIOS_INFO, "Switch EC slot requested. Doing cold reboot\n");
68 if (google_chromeec_reboot(0, EC_REBOOT_COLD,
69 EC_REBOOT_FLAG_SWITCH_RW_SLOT))
70 printk(BIOS_EMERG, "Failed to get EC to cold reboot\n");
71
72 halt();
73 break;
74
Yu-Ping Wu30322782020-04-17 18:39:01 +080075 case VB2_REQUEST_REBOOT:
Tim Wawrzynczak14dd0732019-12-06 09:28:29 -070076 printk(BIOS_INFO, "Reboot requested. Doing warm reboot\n");
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -060077 vboot_reboot();
Tim Wawrzynczak14dd0732019-12-06 09:28:29 -070078 break;
79
80 default:
81 printk(BIOS_ERR, "EC software sync failed (%#x),"
82 " rebooting\n", retval);
83 vboot_reboot();
84 break;
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -060085 }
Tim Wawrzynczakf9e74992019-10-25 14:59:43 -060086
Jakub Czapigaad6157e2022-02-15 11:50:31 +010087 timestamp_add_now(TS_EC_SYNC_END);
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -060088}
89
90/* Convert firmware image type into a flash offset */
91static uint32_t get_vboot_hash_offset(enum vb2_firmware_selection select)
92{
93 switch (select) {
94 case VB_SELECT_FIRMWARE_READONLY:
95 return EC_VBOOT_HASH_OFFSET_RO;
96 case VB_SELECT_FIRMWARE_EC_UPDATE:
97 return EC_VBOOT_HASH_OFFSET_UPDATE;
98 default:
99 return EC_VBOOT_HASH_OFFSET_ACTIVE;
100 }
101}
102
103/*
104 * Asks the EC to calculate a hash of the specified firmware image, and
105 * returns the information in **hash and *hash_size.
106 */
107static vb2_error_t ec_hash_image(enum vb2_firmware_selection select,
108 const uint8_t **hash, int *hash_size)
109{
110 static struct ec_response_vboot_hash resp;
111 uint32_t hash_offset;
112 int recalc_requested = 0;
113 struct stopwatch sw;
114
115 hash_offset = get_vboot_hash_offset(select);
116
117 stopwatch_init_msecs_expire(&sw, CROS_EC_HASH_TIMEOUT_MS);
118 do {
119 if (google_chromeec_get_vboot_hash(hash_offset, &resp))
120 return VB2_ERROR_UNKNOWN;
121
122 switch (resp.status) {
123 case EC_VBOOT_HASH_STATUS_NONE:
124 /*
125 * There is no hash available right now.
126 * Request a recalc if it hasn't been done yet.
127 */
128 if (recalc_requested)
129 break;
130
131 printk(BIOS_WARNING,
132 "%s: No valid hash (status=%d size=%d). "
133 "Computing...\n", __func__, resp.status,
134 resp.size);
135
136 if (google_chromeec_start_vboot_hash(
137 EC_VBOOT_HASH_TYPE_SHA256, hash_offset, &resp))
138 return VB2_ERROR_UNKNOWN;
139
140 recalc_requested = 1;
141
142 /*
143 * Expect status to be busy since we just sent
144 * a recalc request.
145 */
146 resp.status = EC_VBOOT_HASH_STATUS_BUSY;
147
148 /* Hash just started calculating, let it go for a bit */
149 mdelay(CROS_EC_HASH_CHECK_DELAY_MS);
150 break;
151
152 case EC_VBOOT_HASH_STATUS_BUSY:
153 /* Hash is still calculating. */
154 mdelay(CROS_EC_HASH_CHECK_DELAY_MS);
155 break;
156
157 case EC_VBOOT_HASH_STATUS_DONE: /* intentional fallthrough */
158 default:
159 /* Hash is ready! */
160 break;
161 }
162 } while (resp.status == EC_VBOOT_HASH_STATUS_BUSY &&
163 !stopwatch_expired(&sw));
164
Tim Wawrzynczakf9e74992019-10-25 14:59:43 -0600165 timestamp_add_now(TS_EC_HASH_READY);
166
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600167 if (resp.status != EC_VBOOT_HASH_STATUS_DONE) {
168 printk(BIOS_ERR, "%s: Hash status not done: %d\n", __func__,
169 resp.status);
170 return VB2_ERROR_UNKNOWN;
171 }
172 if (resp.hash_type != EC_VBOOT_HASH_TYPE_SHA256) {
173 printk(BIOS_ERR, "EC hash was the wrong type.\n");
174 return VB2_ERROR_UNKNOWN;
175 }
176
177 printk(BIOS_INFO, "EC took %luus to calculate image hash\n",
178 stopwatch_duration_usecs(&sw));
179
180 *hash = resp.hash_digest;
181 *hash_size = resp.digest_size;
182
183 return VB2_SUCCESS;
184}
185
186/*
187 * Asks the EC to protect or unprotect the specified flash region.
188 */
189static vb2_error_t ec_protect_flash(enum vb2_firmware_selection select, int enable)
190{
191 struct ec_response_flash_protect resp;
192 uint32_t protected_region = EC_FLASH_PROTECT_ALL_NOW;
193 const uint32_t mask = EC_FLASH_PROTECT_ALL_NOW | EC_FLASH_PROTECT_ALL_AT_BOOT;
194
195 if (select == VB_SELECT_FIRMWARE_READONLY)
196 protected_region = EC_FLASH_PROTECT_RO_NOW;
197
198 if (google_chromeec_flash_protect(mask, enable ? mask : 0, &resp) != 0)
199 return VB2_ERROR_UNKNOWN;
200
201 if (!enable) {
202 /* If protection is still enabled, need reboot */
203 if (resp.flags & protected_region)
Yu-Ping Wu30322782020-04-17 18:39:01 +0800204 return VB2_REQUEST_REBOOT_EC_TO_RO;
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600205
206 return VB2_SUCCESS;
207 }
208
209 /*
210 * If write protect and ro-at-boot aren't both asserted, don't expect
211 * protection enabled.
212 */
213 if ((~resp.flags) & (EC_FLASH_PROTECT_GPIO_ASSERTED |
214 EC_FLASH_PROTECT_RO_AT_BOOT))
215 return VB2_SUCCESS;
216
217 /* If flash is protected now, success */
218 if (resp.flags & EC_FLASH_PROTECT_ALL_NOW)
219 return VB2_SUCCESS;
220
221 /* If RW will be protected at boot but not now, need a reboot */
222 if (resp.flags & EC_FLASH_PROTECT_ALL_AT_BOOT)
Yu-Ping Wu30322782020-04-17 18:39:01 +0800223 return VB2_REQUEST_REBOOT_EC_TO_RO;
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600224
225 /* Otherwise, it's an error */
226 return VB2_ERROR_UNKNOWN;
227}
228
229/* Convert a firmware image type to an EC flash region */
230static enum ec_flash_region vboot_to_ec_region(enum vb2_firmware_selection select)
231{
232 switch (select) {
233 case VB_SELECT_FIRMWARE_READONLY:
234 return EC_FLASH_REGION_WP_RO;
235 case VB_SELECT_FIRMWARE_EC_UPDATE:
236 return EC_FLASH_REGION_UPDATE;
237 default:
238 return EC_FLASH_REGION_ACTIVE;
239 }
240}
241
242/*
Julius Wernerb3182fb2021-04-12 16:28:37 -0700243 * Send an image to the EC in burst-sized chunks.
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600244 */
Julius Wernerb3182fb2021-04-12 16:28:37 -0700245static vb2_error_t ec_flash_write(void *image, uint32_t region_offset,
246 int image_size)
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600247{
248 struct ec_response_get_protocol_info resp_proto;
249 struct ec_response_flash_info resp_flash;
250 ssize_t pdata_max_size;
251 ssize_t burst;
252 uint8_t *file_buf;
253 struct ec_params_flash_write *params;
254 uint32_t end, off;
255
256 /*
257 * Get EC's protocol information, so that we can figure out how much
258 * data can be sent in one message.
259 */
260 if (google_chromeec_get_protocol_info(&resp_proto)) {
261 printk(BIOS_ERR, "Failed to get EC protocol information; "
262 "skipping flash write\n");
263 return VB2_ERROR_UNKNOWN;
264 }
265
266 /*
267 * Determine burst size. This must be a multiple of the write block
268 * size, and must also fit into the host parameter buffer.
269 */
270 if (google_chromeec_flash_info(&resp_flash)) {
271 printk(BIOS_ERR, "Failed to get EC flash information; "
272 "skipping flash write\n");
273 return VB2_ERROR_UNKNOWN;
274 }
275
276 /* Limit the potential buffer stack allocation to 1K */
277 pdata_max_size = MIN(1024, resp_proto.max_request_packet_size -
278 sizeof(struct ec_host_request));
279
280 /* Round burst to a multiple of the flash write block size */
281 burst = pdata_max_size - sizeof(*params);
282 burst = (burst / resp_flash.write_block_size) *
283 resp_flash.write_block_size;
284
285 /* Buffer too small */
286 if (burst <= 0) {
287 printk(BIOS_ERR, "Flash write buffer too small! skipping "
288 "flash write\n");
289 return VB2_ERROR_UNKNOWN;
290 }
291
292 /* Allocate buffer on the stack */
293 params = alloca(burst + sizeof(*params));
294
295 /* Fill up the buffer */
296 end = region_offset + image_size;
Julius Wernerb3182fb2021-04-12 16:28:37 -0700297 file_buf = image;
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600298 for (off = region_offset; off < end; off += burst) {
299 uint32_t todo = MIN(end - off, burst);
300 uint32_t xfer_size = todo + sizeof(*params);
301
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600302 params->offset = off;
303 params->size = todo;
304
305 /* Read todo bytes into the buffer */
306 memcpy(params + 1, file_buf, todo);
307
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600308 /* Make sure to add back in the size of the parameters */
309 if (google_chromeec_flash_write_block(
310 (const uint8_t *)params, xfer_size)) {
311 printk(BIOS_ERR, "EC failed flash write command, "
312 "relative offset %u!\n", off - region_offset);
313 return VB2_ERROR_UNKNOWN;
314 }
Julius Wernerb3182fb2021-04-12 16:28:37 -0700315
316 file_buf += todo;
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600317 }
318
319 return VB2_SUCCESS;
320}
321
322/*
323 * The logic for updating an EC firmware image.
324 */
325static vb2_error_t ec_update_image(enum vb2_firmware_selection select)
326{
327 uint32_t region_offset, region_size;
328 enum ec_flash_region region;
329 vb2_error_t rv;
Julius Wernerb3182fb2021-04-12 16:28:37 -0700330 void *image;
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600331 size_t image_size;
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600332
333 /* Un-protect the flash region */
334 rv = ec_protect_flash(select, 0);
335 if (rv != VB2_SUCCESS)
336 return rv;
337
338 /* Convert vboot region into an EC region */
339 region = vboot_to_ec_region(select);
340
341 /* Get information about the flash region */
342 if (google_chromeec_flash_region_info(region, &region_offset,
343 &region_size))
344 return VB2_ERROR_UNKNOWN;
345
Julius Wernerb3182fb2021-04-12 16:28:37 -0700346 /* Map the CBFS file */
347 image = cbfs_map(EC_IMAGE_FILENAME(select), &image_size);
348 if (!image)
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600349 return VB2_ERROR_UNKNOWN;
350
Julius Wernerb3182fb2021-04-12 16:28:37 -0700351 rv = VB2_ERROR_UNKNOWN;
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600352
353 /* Bail if the image is too large */
354 if (image_size > region_size)
Julius Wernerb3182fb2021-04-12 16:28:37 -0700355 goto unmap;
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600356
357 /* Erase the region */
358 if (google_chromeec_flash_erase(region_offset, region_size))
Julius Wernerb3182fb2021-04-12 16:28:37 -0700359 goto unmap;
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600360
361 /* Write the image into the region */
Julius Wernerb3182fb2021-04-12 16:28:37 -0700362 if (ec_flash_write(image, region_offset, image_size))
363 goto unmap;
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600364
365 /* Verify the image */
366 if (google_chromeec_efs_verify(region))
Julius Wernerb3182fb2021-04-12 16:28:37 -0700367 goto unmap;
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600368
Julius Wernerb3182fb2021-04-12 16:28:37 -0700369 rv = VB2_SUCCESS;
370
371unmap:
372 cbfs_unmap(image);
373 return rv;
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600374}
375
376static vb2_error_t ec_get_expected_hash(enum vb2_firmware_selection select,
377 const uint8_t **hash,
378 int *hash_size)
379{
380 size_t size;
381 const char *filename = EC_HASH_FILENAME(select);
Julius Werner834b3ec2020-03-04 16:52:08 -0800382
383 /* vboot has no API to return this memory, so must permanently leak a mapping here. */
384 const uint8_t *file = cbfs_map(filename, &size);
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600385
386 if (file == NULL)
387 return VB2_ERROR_UNKNOWN;
388
389 *hash = file;
390 *hash_size = (int)size;
391
392 return VB2_SUCCESS;
393}
394
395/***********************************************************************
396 * Vboot Callbacks
397 ***********************************************************************/
398
399/*
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600400 * Write opaque data into NV storage region.
401 */
Joel Kitching1debc0c2019-11-27 14:25:16 +0800402vb2_error_t vb2ex_commit_data(struct vb2_context *ctx)
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600403{
Joel Kitching1debc0c2019-11-27 14:25:16 +0800404 save_vbnv(ctx->nvdata);
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600405 return VB2_SUCCESS;
406}
407
408/*
409 * Report whether the EC is in RW or not.
410 */
411vb2_error_t vb2ex_ec_running_rw(int *in_rw)
412{
413 *in_rw = !google_ec_running_ro();
414 return VB2_SUCCESS;
415}
416
417/*
418 * Callback for when Vboot is finished.
419 */
420vb2_error_t vb2ex_ec_vboot_done(struct vb2_context *ctx)
421{
422 int limit_power = 0;
423 bool message_printed = false;
424 struct stopwatch sw;
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600425 int in_recovery = !!(ctx->flags & VB2_CONTEXT_RECOVERY_MODE);
426
427 /*
428 * Do not wait for the limit power flag to be cleared in
429 * recovery mode since we didn't just sysjump.
430 */
431 if (in_recovery)
432 return VB2_SUCCESS;
433
Tim Wawrzynczakf9e74992019-10-25 14:59:43 -0600434 timestamp_add_now(TS_EC_POWER_LIMIT_WAIT);
435
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600436 stopwatch_init_msecs_expire(&sw, LIMIT_POWER_WAIT_TIMEOUT_MS);
437
Tim Wawrzynczakf9e74992019-10-25 14:59:43 -0600438 /* Ensure we have enough power to continue booting. */
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600439 while (1) {
440 if (google_chromeec_read_limit_power_request(&limit_power)) {
441 printk(BIOS_ERR, "Failed to check EC limit power"
442 "flag.\n");
Tim Wawrzynczakf9e74992019-10-25 14:59:43 -0600443 return VB2_ERROR_UNKNOWN;
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600444 }
445
446 if (!limit_power || stopwatch_expired(&sw))
447 break;
448
449 if (!message_printed) {
450 printk(BIOS_SPEW,
451 "Waiting for EC to clear limit power flag.\n");
452 message_printed = true;
453 }
454
455 mdelay(LIMIT_POWER_POLL_SLEEP_MS);
456 }
457
458 if (limit_power) {
459 printk(BIOS_INFO,
460 "EC requests limited power usage. Request shutdown.\n");
Yu-Ping Wu30322782020-04-17 18:39:01 +0800461 return VB2_REQUEST_SHUTDOWN;
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600462 } else {
463 printk(BIOS_INFO, "Waited %luus to clear limit power flag.\n",
464 stopwatch_duration_usecs(&sw));
465 }
466
Tim Wawrzynczakf9e74992019-10-25 14:59:43 -0600467 return VB2_SUCCESS;
Tim Wawrzynczakd6fc5572019-10-25 14:58:15 -0600468}
469
470/*
471 * Support battery cutoff if required.
472 */
473vb2_error_t vb2ex_ec_battery_cutoff(void)
474{
475 if (google_chromeec_battery_cutoff(EC_BATTERY_CUTOFF_FLAG_AT_SHUTDOWN))
476 return VB2_ERROR_UNKNOWN;
477
478 return VB2_SUCCESS;
479}
480
481/*
482 * Vboot callback for calculating an EC image's hash.
483 */
484vb2_error_t vb2ex_ec_hash_image(enum vb2_firmware_selection select,
485 const uint8_t **hash, int *hash_size)
486{
487 return ec_hash_image(select, hash, hash_size);
488}
489
490/*
491 * Vboot callback for EC flash protection.
492 */
493vb2_error_t vb2ex_ec_protect(enum vb2_firmware_selection select)
494{
495 return ec_protect_flash(select, 1);
496}
497
498/*
499 * Get hash for image.
500 */
501vb2_error_t vb2ex_ec_get_expected_image_hash(enum vb2_firmware_selection select,
502 const uint8_t **hash,
503 int *hash_size)
504{
505 return ec_get_expected_hash(select, hash, hash_size);
506}
507
508/*
509 * Disable further sysjumps (i.e., stay in RW until next reboot)
510 */
511vb2_error_t vb2ex_ec_disable_jump(void)
512{
513 if (google_chromeec_reboot(0, EC_REBOOT_DISABLE_JUMP, 0))
514 return VB2_ERROR_UNKNOWN;
515
516 return VB2_SUCCESS;
517}
518
519/*
520 * Update EC image.
521 */
522vb2_error_t vb2ex_ec_update_image(enum vb2_firmware_selection select)
523{
524 return ec_update_image(select);
525}
526
527/*
528 * Vboot callback for commanding EC to sysjump to RW.
529 */
530vb2_error_t vb2ex_ec_jump_to_rw(void)
531{
532 struct stopwatch sw;
533
534 if (google_chromeec_reboot(0, EC_REBOOT_JUMP_RW, 0))
535 return VB2_ERROR_UNKNOWN;
536
537 /* Give the EC 3 seconds to sysjump */
538 stopwatch_init_msecs_expire(&sw, CROS_EC_SYSJUMP_TIMEOUT_MS);
539
540 /* Default delay to wait after EC reboot */
541 mdelay(50);
542 while (google_chromeec_hello()) {
543 if (stopwatch_expired(&sw)) {
544 printk(BIOS_ERR, "EC did not return from reboot after %luus\n",
545 stopwatch_duration_usecs(&sw));
546 return VB2_ERROR_UNKNOWN;
547 }
548
549 mdelay(5);
550 }
551
552 printk(BIOS_INFO, "\nEC returned from reboot after %luus\n",
553 stopwatch_duration_usecs(&sw));
554
555 return VB2_SUCCESS;
556}