| /* |
| * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| * |
| * Alternatively, this software may be distributed under the terms of the |
| * GNU General Public License ("GPL") version 2 as published by the Free |
| * Software Foundation. |
| */ |
| |
| /* |
| * Debug commands for testing basic verified-boot related utilities. |
| */ |
| |
| #include <common.h> |
| #include <command.h> |
| #include <cros/common.h> |
| #include <cros/cros_fdtdec.h> |
| #include <cros/firmware_storage.h> |
| #include <cros/memory_wipe.h> |
| #include <cros/power_management.h> |
| #include <cros/vboot_flag.h> |
| #include <vboot_api.h> |
| |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| /* |
| * TODO: Pick a better region for test instead of GBB. |
| * We now test the region of GBB. |
| */ |
| #define TEST_FW_START 0xc1000 |
| #define DEFAULT_TEST_FW_LENGTH 0x1000 |
| |
| static int do_vboot_test_fwrw(cmd_tbl_t *cmdtp, |
| int flag, int argc, char * const argv[]) |
| { |
| int ret = 0; |
| firmware_storage_t file; |
| uint32_t test_length, i; |
| uint8_t *original_buf, *target_buf, *verify_buf; |
| uint64_t t0, t1; |
| |
| switch (argc) { |
| case 1: /* if no argument given, use the default length */ |
| test_length = DEFAULT_TEST_FW_LENGTH; |
| break; |
| case 2: /* use argument */ |
| test_length = simple_strtoul(argv[1], NULL, 16); |
| if (!test_length) { |
| VbExDebug("The first argument is not a number!\n"); |
| return cmd_usage(cmdtp); |
| } |
| break; |
| default: |
| return cmd_usage(cmdtp); |
| } |
| |
| #ifndef CONFIG_HARDWARE_MAPPED_SPI |
| /* Allocate the buffer and fill the target test pattern. */ |
| original_buf = VbExMalloc(test_length); |
| verify_buf = VbExMalloc(test_length); |
| #endif |
| target_buf = VbExMalloc(test_length); |
| |
| /* Fill the target test pattern. */ |
| for (i = 0; i < test_length; i++) |
| target_buf[i] = i & 0xff; |
| |
| /* Open firmware storage device. */ |
| if (firmware_storage_open_spi(&file)) { |
| VbExDebug("Failed to open firmware device!\n"); |
| return 1; |
| } |
| |
| t0 = VbExGetTimer(); |
| if (file.read(&file, TEST_FW_START, |
| test_length, BT_EXTRA original_buf)) { |
| VbExDebug("Failed to read firmware!\n"); |
| goto out; |
| } |
| t1 = VbExGetTimer(); |
| VbExDebug("test_fwrw: fw_read, length: %#x, time: %llu\n", |
| test_length, t1 - t0); |
| |
| t0 = VbExGetTimer(); |
| ret = file.write(&file, TEST_FW_START, test_length, target_buf); |
| t1 = VbExGetTimer(); |
| VbExDebug("test_fwrw: fw_write, length: %#x, time: %llu\n", |
| test_length, t1 - t0); |
| |
| if (ret) { |
| VbExDebug("Failed to write firmware!\n"); |
| ret = 1; |
| } else { |
| /* Read back and verify the data. */ |
| file.read(&file, TEST_FW_START, test_length, |
| BT_EXTRA verify_buf); |
| if (memcmp(target_buf, verify_buf, test_length) != 0) { |
| VbExDebug("Verify failed. The target data wrote " |
| "wrong.\n"); |
| ret = 1; |
| } |
| } |
| |
| /* Write the original data back. */ |
| if (file.write(&file, TEST_FW_START, test_length, original_buf)) { |
| VbExDebug("Failed to write the original data back. The " |
| "firmware may now be corrupt.\n"); |
| |
| } |
| |
| out: |
| file.close(&file); |
| |
| #ifndef CONFIG_HARDWARE_MAPPED_SPI |
| VbExFree(original_buf); |
| VbExFree(verify_buf); |
| #endif |
| VbExFree(target_buf); |
| |
| if (ret == 0) |
| VbExDebug("Read and write firmware test SUCCESS.\n"); |
| |
| return ret; |
| } |
| |
| static int do_vboot_test_memwipe(cmd_tbl_t *cmdtp, |
| int flag, int argc, char * const argv[]) |
| { |
| memory_wipe_t wipe; |
| char s[] = "ABCDEFGHIJ"; |
| const char r[] = "\0BCDE\0GHIJ"; |
| const size_t size = strlen(s); |
| uintptr_t base = (uintptr_t)s; |
| |
| memory_wipe_init(&wipe); |
| memory_wipe_add(&wipe, base + 0, base + 1); |
| /* Result: -BCDEFGHIJ */ |
| memory_wipe_sub(&wipe, base + 1, base + 2); |
| /* Result: -BCDEFGHIJ */ |
| memory_wipe_add(&wipe, base + 1, base + 2); |
| /* Result: --CDEFGHIJ */ |
| memory_wipe_sub(&wipe, base + 1, base + 2); |
| /* Result: -BCDEFGHIJ */ |
| memory_wipe_add(&wipe, base + 0, base + 2); |
| /* Result: --CDEFGHIJ */ |
| memory_wipe_sub(&wipe, base + 1, base + 3); |
| /* Result: -BCDEFGHIJ */ |
| memory_wipe_add(&wipe, base + 4, base + 6); |
| /* Result: -BCD--GHIJ */ |
| memory_wipe_add(&wipe, base + 3, base + 4); |
| /* Result: -BC---GHIJ */ |
| memory_wipe_sub(&wipe, base + 3, base + 7); |
| /* Result: -BCDEFGHIJ */ |
| memory_wipe_add(&wipe, base + 2, base + 8); |
| /* Result: -B------IJ */ |
| memory_wipe_sub(&wipe, base + 1, base + 9); |
| /* Result: -BCDEFGHIJ */ |
| memory_wipe_add(&wipe, base + 4, base + 7); |
| /* Result: -BCD---HIJ */ |
| memory_wipe_sub(&wipe, base + 3, base + 5); |
| /* Result: -BCDE--HIJ */ |
| memory_wipe_sub(&wipe, base + 6, base + 8); |
| /* Result: -BCDE-GHIJ */ |
| memory_wipe_execute(&wipe); |
| |
| if (memcmp(s, r, size)) { |
| int i; |
| |
| VbExDebug("Expected: "); |
| for (i = 0; i < size; i++) |
| VbExDebug("%c", r[i] ? r[i] : '-'); |
| VbExDebug("\nGot: "); |
| for (i = 0; i < size; i++) |
| VbExDebug("%c", s[i] ? s[i] : '-'); |
| VbExDebug("\nFailed to wipe the expected regions!\n"); |
| return 1; |
| } |
| |
| VbExDebug("Memory wipe test SUCCESS!\n"); |
| return 0; |
| } |
| |
| static int do_vboot_test_gpio(cmd_tbl_t *cmdtp, |
| int flag, int argc, char * const argv[]) |
| { |
| int i, ret = 0; |
| |
| for (i = 0; i < VBOOT_FLAG_MAX_FLAGS; i++) { |
| if (vboot_flag_dump(i, NULL)) { |
| VbExDebug("Failed to dump GPIO, %d!\n", i); |
| ret = 1; |
| } |
| } |
| return ret; |
| } |
| |
| static int do_vboot_reboot(cmd_tbl_t *cmdtp, |
| int flag, int argc, char * const argv[]) |
| { |
| cold_reboot(); |
| return 1; /* It should never reach here */ |
| } |
| |
| static int do_vboot_poweroff(cmd_tbl_t *cmdtp, |
| int flag, int argc, char * const argv[]) |
| { |
| power_off(); |
| return 1; /* It should never reach here */ |
| } |
| |
| static void show_ec_bin(const char *name, struct fmap_entry *entry) |
| { |
| u32 *data; |
| int i; |
| |
| printf("EC binary %s at %#x, length %#x\n", name, entry->offset, |
| entry->length); |
| if (entry->length) { |
| data = (u32 *)(CONFIG_SYS_TEXT_BASE + entry->offset); |
| printf("%p: ", data); |
| for (i = 0; i < 4; i++) |
| printf("%08x ", data[i]); |
| putc('\n'); |
| } |
| } |
| |
| static void show_entry(const char *name, struct fmap_entry *entry) |
| { |
| printf(" %s: offset=%#x, length=%#x\n", name, entry->offset, |
| entry->length); |
| } |
| |
| static void show_firmware_entry(const char *name, |
| struct fmap_firmware_entry *fw_entry) |
| { |
| printf("%s:\n", name); |
| show_entry("boot", &fw_entry->boot); |
| show_entry("vblock", &fw_entry->vblock); |
| show_entry("firmware_id", &fw_entry->firmware_id); |
| printf("block_offset: %llx\n", fw_entry->block_offset); |
| show_ec_bin(name, &fw_entry->ec_bin); |
| puts("\n"); |
| } |
| |
| static int do_vboot_fmap(cmd_tbl_t *cmdtp, int flag, |
| int argc, char * const argv[]) |
| { |
| struct twostop_fmap fmap; |
| |
| /* TODO(sjg@chromium.org): Best to automate thist test with sandbox */ |
| if (cros_fdtdec_flashmap(gd->fdt_blob, &fmap)) { |
| VbExDebug("Failed to load fmap config from fdt!\n"); |
| return 1; |
| } |
| |
| puts("ro:\n"); |
| show_entry("fmap", &fmap.readonly.fmap); |
| show_entry("gbb", &fmap.readonly.gbb); |
| show_entry("firmware_id", &fmap.readonly.firmware_id); |
| printf("flash_base: %u\n", fmap.flash_base); |
| |
| show_firmware_entry("rw-a", &fmap.readwrite_a); |
| show_firmware_entry("rw-b", &fmap.readwrite_b); |
| |
| return 0; |
| } |
| |
| static int do_vboot_test_all(cmd_tbl_t *cmdtp, |
| int flag, int argc, char * const argv[]) |
| { |
| int ret = 0; |
| |
| ret |= do_vboot_test_fwrw(cmdtp, flag, argc, argv); |
| ret |= do_vboot_test_memwipe(cmdtp, flag, argc, argv); |
| ret |= do_vboot_test_gpio(cmdtp, flag, argc, argv); |
| ret |= do_vboot_fmap(cmdtp, flag, argc, argv); |
| |
| return ret; |
| } |
| |
| static cmd_tbl_t cmd_vboot_test_sub[] = { |
| U_BOOT_CMD_MKENT(all, 0, 1, do_vboot_test_all, "", ""), |
| U_BOOT_CMD_MKENT(fwrw, 0, 1, do_vboot_test_fwrw, "", ""), |
| U_BOOT_CMD_MKENT(memwipe, 0, 1, do_vboot_test_memwipe, "", ""), |
| U_BOOT_CMD_MKENT(gpio, 0, 1, do_vboot_test_gpio, "", ""), |
| U_BOOT_CMD_MKENT(reboot, 0, 1, do_vboot_reboot, "", ""), |
| U_BOOT_CMD_MKENT(poweroff, 0, 1, do_vboot_poweroff, "", ""), |
| U_BOOT_CMD_MKENT(fmap, 0, 1, do_vboot_fmap, "", ""), |
| }; |
| |
| static int do_vboot_test(cmd_tbl_t *cmdtp, |
| int flag, int argc, char * const argv[]) |
| { |
| cmd_tbl_t *c; |
| |
| if (argc < 2) |
| return cmd_usage(cmdtp); |
| argc--; |
| argv++; |
| |
| c = find_cmd_tbl(argv[0], &cmd_vboot_test_sub[0], |
| ARRAY_SIZE(cmd_vboot_test_sub)); |
| if (c) |
| return c->cmd(c, flag, argc, argv); |
| else |
| return cmd_usage(cmdtp); |
| } |
| |
| U_BOOT_CMD(vboot_test, CONFIG_SYS_MAXARGS, 1, do_vboot_test, |
| "Perform tests for basic vboot related utilities", |
| "all - perform all tests\n" |
| "vboot_test fwrw [length] - test the firmware read/write\n" |
| "vboot_test memwipe - test the memory wipe functions\n" |
| "vboot_test gpio - print the status of gpio\n" |
| "vboot_test reboot - test reboot (board will be rebooted!)\n" |
| "vboot_test poweroff - test poweroff (board will be shut down!)\n" |
| "vboot_test fmap - check that flashmap can be decoded\n" |
| ); |
| |