| /* |
| * 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 Chrome OS Verify Boot. Probably not useful to you if |
| * you are not developing firmware stuff. */ |
| |
| #include <common.h> |
| #include <command.h> |
| #include <malloc.h> |
| #include <chromeos/common.h> |
| #include <chromeos/firmware_storage.h> |
| #include <chromeos/fmap.h> |
| #include <chromeos/gbb_bmpblk.h> |
| #include <chromeos/gpio.h> |
| #include <chromeos/load_firmware_helper.h> |
| #include <chromeos/load_kernel_helper.h> |
| #include <chromeos/power_management.h> |
| #include <chromeos/vboot_nvstorage_helper.h> |
| #include <chromeos/os_storage.h> |
| |
| /* Verify Boot interface */ |
| #include <boot_device.h> |
| #include <gbb_header.h> |
| #include <load_firmware_fw.h> |
| #include <load_kernel_fw.h> |
| #include <vboot_nvstorage.h> |
| #include <vboot_struct.h> |
| |
| #define USAGE(ret, cmdtp, fmt, ...) do { \ |
| printf(fmt, ##__VA_ARGS__); \ |
| cmd_usage(cmdtp); \ |
| return (ret); \ |
| } while (0) |
| |
| int do_cros (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); |
| int do_test_gpio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); |
| int do_bootdev (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); |
| #ifdef CONFIG_CHROMEOS_BMPBLK |
| int do_bmpblk (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); |
| #endif /* CONFIG_CHROMEOS_BMPBLK */ |
| int do_fmap (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); |
| int do_nvram (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); |
| int do_load_fw (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); |
| int do_load_k (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); |
| int do_cros_reboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); |
| int do_cros_help(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); |
| |
| U_BOOT_CMD(cros, CONFIG_SYS_MAXARGS, 1, do_cros, |
| "perform action (try \"cros help\")", |
| "[action [args...]]\n - perform action with arguments" |
| ); |
| |
| cmd_tbl_t cmd_cros_sub[] = { |
| U_BOOT_CMD_MKENT(test_gpio, 0, 1, do_test_gpio, |
| "test Chrome OS verified boot GPIOs", |
| ""), |
| U_BOOT_CMD_MKENT(bootdev, 4, 1, do_bootdev, |
| "show/set/read/write boot device", |
| "[sub-action args...]\n - perform sub-action\n" |
| "\n" |
| "Subactions:\n" |
| " - show boot device (when no sub-action)\n" |
| "set iface dev [part]\n - set boot device\n" |
| "read addr block count\n - read from boot device\n" |
| "write addr block count\n - write to boot device"), |
| #ifdef CONFIG_CHROMEOS_BMPBLK |
| U_BOOT_CMD_MKENT(bmpblk, 3, 1, do_bmpblk, |
| "Manipulate GBB BMP block", |
| "display <gbbaddr> <screen#> - display the screen\n" |
| "bmpblk info <gbbaddr> <screen#> - print the screen info\n"), |
| #endif /* CONFIG_CHROMEOS_BMPBLK */ |
| U_BOOT_CMD_MKENT(fmap, 2, 1, do_fmap, |
| "Find and print flash map", |
| "addr len\n - Find and print flash map " |
| "in memory[addr, addr+len]\n"), |
| U_BOOT_CMD_MKENT(nvram, 2, 1, do_nvram, |
| "Read/write nvram", |
| "read/write\n" |
| "read\n - Read nvram\n" |
| "write [0-9a-fA-F]{VBNV_BLOCK_SIZE*2}\n - Write nvram\n"), |
| U_BOOT_CMD_MKENT(load_fw, 4, 1, do_load_fw, |
| "Load firmware from memory " |
| "(you have to download the image before running this)", |
| "boot_flags addr len shdata\n - Wrapper of LoadFirmware. " |
| "Load firmware from [addr, addr+len] and " |
| "store shared data at shdata\n"), |
| U_BOOT_CMD_MKENT(load_k, 2, 1, do_load_k, |
| "Load kernel from the boot device", |
| "boot_flags shdata\n - Load kernel with boot_flags and " |
| "modify shared data at shdata\n"), |
| U_BOOT_CMD_MKENT(cros_reboot, 1, 1, do_cros_reboot, |
| "Cold reboot the machine", |
| ""), |
| U_BOOT_CMD_MKENT(help, 1, 1, do_cros_help, |
| "show this message", |
| "[action]") |
| }; |
| |
| int do_cros(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
| { |
| cmd_tbl_t *c; |
| |
| if (argc < 2) |
| USAGE(0, cmdtp, "Missing action\n"); |
| |
| c = find_cmd_tbl(argv[1], &cmd_cros_sub[0], ARRAY_SIZE(cmd_cros_sub)); |
| if (!c) |
| USAGE(1, cmdtp, "Unrecognized action: %s\n", argv[1]); |
| |
| return c->cmd(c, flag, argc - 1, argv + 1); |
| } |
| |
| static void sleep(int second) |
| { |
| const ulong start = get_timer(0); |
| const ulong delay = second * CONFIG_SYS_HZ; |
| |
| while (!ctrlc() && get_timer(start) < delay) |
| udelay(100); |
| } |
| |
| int do_test_gpio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
| { |
| enum { WAIT = 2 }; /* 2 seconds */ |
| |
| struct { |
| const char *gpio_name; |
| int (*accessor)(void); |
| } testcase[3] = { |
| { |
| .gpio_name = "write protect", |
| .accessor = is_firmware_write_protect_gpio_asserted |
| }, |
| { |
| .gpio_name = "recovery mode", |
| .accessor = is_recovery_mode_gpio_asserted |
| }, |
| { |
| .gpio_name = "developer mode", |
| .accessor = is_developer_mode_gpio_asserted |
| }, |
| }; |
| const char *gpio_name; |
| int i; |
| |
| for (i = 0; i < sizeof(testcase) / sizeof(testcase[0]); i ++) { |
| gpio_name = testcase[i].gpio_name; |
| |
| printf("Please enable %s...\n", gpio_name); |
| sleep(WAIT); |
| if (testcase[i].accessor()) |
| printf("TEST PASS: %s is enabled\n", gpio_name); |
| else |
| printf("TEST FAIL: %s is not enabled\n", gpio_name); |
| printf("\n"); |
| |
| printf("Please disable %s...\n", gpio_name); |
| sleep(WAIT); |
| if (!testcase[i].accessor()) |
| printf("TEST PASS: %s is disabled\n", gpio_name); |
| else |
| printf("TEST FAIL: %s is not disabled\n", gpio_name); |
| printf("\n"); |
| } |
| |
| return 0; |
| } |
| |
| int do_bootdev(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
| { |
| enum { |
| SET, READ, WRITE |
| } opcode; |
| |
| block_dev_desc_t *dev_desc; |
| int dev, part, retcode; |
| uint64_t blk, cnt; |
| void *buf; |
| |
| if (argc < 2) { /* show boot device information */ |
| if ((dev_desc = get_bootdev()) == NULL) |
| puts("No boot device set\n"); |
| else { |
| printf("offset=0x%lx limit=0x%lx\n", get_offset(), |
| get_limit()); |
| dev_print(dev_desc); |
| } |
| return 0; |
| } |
| |
| if (!strcmp(argv[1], "set")) |
| opcode = SET; |
| else if (!strcmp(argv[1], "read")) |
| opcode = READ; |
| else if (!strcmp(argv[1], "write")) |
| opcode = WRITE; |
| else |
| USAGE(1, cmdtp, "Unrecognized action: %s\n", argv[1]); |
| |
| /* apply De Morgan's laws on |
| * !((argc == 4 && opcode == SET) || argc == 5) */ |
| if ((argc != 4 || opcode != SET) && argc != 5) |
| USAGE(1, cmdtp, "Wrong number of arguments\n"); |
| |
| if (opcode == SET) { |
| dev = (int) simple_strtoul(argv[3], NULL, 16); |
| part = (argc < 5) ? |
| 0 : (int) simple_strtoul(argv[4], NULL, 16); |
| |
| printf("Set boot device to %s %d %d\n", argv[2], dev, part); |
| if (set_bootdev(argv[2], dev, part)) { |
| puts("Set bootdev failed\n"); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* assert(opcode == READ || opcode == WRITE); */ |
| |
| buf = (void *) simple_strtoul(argv[2], NULL, 16); |
| blk = (uint64_t) simple_strtoul(argv[3], NULL, 16); |
| cnt = (uint64_t) simple_strtoul(argv[4], NULL, 16); |
| |
| retcode = (opcode == READ) ? |
| BootDeviceReadLBA(blk, cnt, buf) : |
| BootDeviceWriteLBA(blk, cnt, buf); |
| |
| if (retcode) |
| USAGE(1, cmdtp, opcode == READ ? |
| "Read failed\n" : "Write failed\n"); |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_CHROMEOS_BMPBLK |
| int do_bmpblk(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
| { |
| uint8_t *addr; |
| int index; |
| int ret; |
| |
| if (argc != 4) |
| return cmd_usage(cmdtp); |
| |
| addr = (uint8_t *)simple_strtoul(argv[2], NULL, 16); |
| index = (int)simple_strtoul(argv[3], NULL, 10); |
| |
| if (!strcmp(argv[1], "info")) |
| return print_screen_info_in_bmpblk(addr, index); |
| |
| if (!strcmp(argv[1], "display")) { |
| ret = display_screen_in_bmpblk(addr, index); |
| switch (ret) { |
| case BMPBLK_OK: |
| return 0; |
| case BMPBLK_UNSUPPORTED_COMPRESSION: |
| printf("Error: Compression not supported.\n"); |
| break; |
| case BMPBLK_LZMA_DECOMPRESS_FAILED: |
| printf("Error: LZMA decompress failed.\n"); |
| break; |
| case BMPBLK_BMP_DISPLAY_FAILED: |
| printf("Error: BMP display failed.\n"); |
| break; |
| default: |
| printf("Unknown failure: %d.\n", ret); |
| break; |
| } |
| |
| return 1; |
| } |
| |
| return cmd_usage(cmdtp); |
| } |
| #endif /* CONFIG_CHROMEOS_BMPBLK */ |
| |
| static void _print_fmap(struct fmap *fmap) |
| { |
| static const struct { |
| uint16_t flag; |
| const char *str; |
| } flag2str[] = { |
| { 1 << 0, "static" }, |
| { 1 << 1, "compressed" }, |
| { 0, NULL }, |
| }; |
| |
| int i, j; |
| uint16_t flags; |
| |
| printf("fmap_signature: 0x%016llx\n", |
| (unsigned long long) fmap->signature); |
| printf("fmap_ver_major: %d\n", fmap->ver_major); |
| printf("fmap_ver_minor: %d\n", fmap->ver_minor); |
| printf("fmap_base: 0x%016llx\n", (unsigned long long) fmap->base); |
| printf("fmap_size: 0x%04x\n", fmap->size); |
| printf("fmap_name: \"%s\"\n", fmap->name); |
| printf("fmap_nareas: %d\n", fmap->nareas); |
| |
| for (i = 0; i < fmap->nareas; i++) { |
| printf("area_offset: 0x%08x\n", fmap->areas[i].offset); |
| printf("area_size: 0x%08x\n", fmap->areas[i].size); |
| printf("area_name: \"%s\"\n", fmap->areas[i].name); |
| printf("area_flags_raw: 0x%02x\n", fmap->areas[i].flags); |
| |
| flags = fmap->areas[i].flags; |
| if (!flags) |
| continue; |
| |
| puts("area_flags:"); |
| for (j = 0; flag2str[j].flag; j++) |
| if (flags & flag2str[j].flag) |
| printf(" %s", flag2str[j].str); |
| putc('\n'); |
| } |
| } |
| |
| int do_fmap(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
| { |
| const uint8_t *addr; |
| size_t len; |
| off_t offset; |
| struct fmap *fmap; |
| |
| if (argc != 3) |
| USAGE(1, cmdtp, "Wrong number of arguments\n"); |
| |
| addr = (const uint8_t *) simple_strtoul(argv[1], NULL, 16); |
| len = (size_t) simple_strtoul(argv[2], NULL, 16); |
| offset = fmap_find(addr, len); |
| if (offset < 0) { |
| printf("No map found in addr=0x%08lx len=0x%08x\n", |
| (unsigned long) addr, len); |
| return 1; |
| } |
| fmap = (struct fmap *) (addr + offset); |
| |
| _print_fmap(fmap); |
| |
| return 0; |
| } |
| |
| static int ascii_to_integer(int c) |
| { |
| if ('0' <= c && c <= '9') |
| return c - '0'; |
| else if ('a' <= c && c <= 'f') |
| return c - 'a' + 10; |
| else if ('A' <= c && c <= 'F') |
| return c - 'A' + 10; |
| else |
| return -1; |
| } |
| |
| int do_nvram(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
| { |
| const char charmap[] = "0123456789abcdef"; |
| VbNvContext nvcxt; |
| int rc = 1, status, i, c, d; |
| |
| if (!strcmp(argv[1], "read")) { |
| if (argc != 2) { |
| printf("read does not accept arguments\n"); |
| cmd_usage(cmdtp); |
| goto EXIT; |
| } |
| |
| status = read_nvcontext(&nvcxt); |
| if (status) { |
| printf("read nvram fail: %d\n", status); |
| goto EXIT; |
| } else |
| rc = 0; |
| |
| printf("nvram content: "); |
| for (i = 0; i < VBNV_BLOCK_SIZE; i++) { |
| putc(charmap[nvcxt.raw[i] >> 4]); |
| putc(charmap[nvcxt.raw[i] & 0x0f]); |
| } |
| putc('\n'); |
| |
| VbNvSetup(&nvcxt); |
| |
| printf("content after VbNvSetup: "); |
| for (i = 0; i < VBNV_BLOCK_SIZE; i++) { |
| putc(charmap[nvcxt.raw[i] >> 4]); |
| putc(charmap[nvcxt.raw[i] & 0x0f]); |
| } |
| putc('\n'); |
| } else if (!strcmp(argv[1], "write")) { |
| if (argc != 3) { |
| printf("write requires one argument\n"); |
| cmd_usage(cmdtp); |
| goto EXIT; |
| } |
| |
| if (strlen(argv[2]) != VBNV_BLOCK_SIZE * 2) { |
| printf("write needs %d bytes of data\n", |
| VBNV_BLOCK_SIZE); |
| cmd_usage(cmdtp); |
| goto EXIT; |
| } |
| |
| for (i = 0; i < VBNV_BLOCK_SIZE; i++) { |
| c = argv[2][i<<1]; |
| d = argv[2][(i<<1) + 1]; |
| c = ascii_to_integer(c); |
| d = ascii_to_integer(d); |
| if (c == -1 || d == -1) { |
| printf("invalid hex string: %c%c\n", |
| argv[2][i<<1], |
| argv[2][(i<<1) + 1]); |
| goto EXIT; |
| } |
| nvcxt.raw[i] = (c << 4) + d; |
| } |
| |
| nvcxt.regenerate_crc = 1; |
| VbNvTeardown(&nvcxt); |
| |
| printf("content after VbNvTeardown: "); |
| for (i = 0; i < VBNV_BLOCK_SIZE; i++) { |
| putc(charmap[nvcxt.raw[i] >> 4]); |
| putc(charmap[nvcxt.raw[i] & 0x0f]); |
| } |
| putc('\n'); |
| |
| status = write_nvcontext(&nvcxt); |
| if (status) |
| printf("write nvram fail: %d\n", status); |
| else |
| rc = 0; |
| } else { |
| printf("Unknown command: %s\n", argv[1]); |
| cmd_usage(cmdtp); |
| } |
| |
| EXIT: |
| return rc; |
| } |
| |
| int do_load_fw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
| { |
| int status; |
| LoadFirmwareParams params; |
| uint64_t boot_flags = 0; |
| uint8_t *shared_data_blob = NULL, *firmware_data = NULL; |
| firmware_storage_t file; |
| VbNvContext nvcxt; |
| void *beg, *end; |
| |
| if (argc != 5) |
| USAGE(1, cmdtp, "Wrong number of arguments\n"); |
| |
| memset(¶ms, '\0', sizeof(params)); |
| |
| boot_flags = (uint64_t) simple_strtoul(argv[1], NULL, 16); |
| |
| beg = (void*) simple_strtoul(argv[2], NULL, 16); |
| end = beg + simple_strtoul(argv[3], NULL, 16); |
| firmware_storage_init_ram(&file, beg, end); |
| |
| shared_data_blob = (uint8_t*) simple_strtoul(argv[4], NULL, 16); |
| |
| /* TODO let user set cookie content */ |
| memset(nvcxt.raw, '\0', sizeof(nvcxt.raw)); |
| |
| status = load_firmware_wrapper(&file, |
| boot_flags, &nvcxt, shared_data_blob, &firmware_data); |
| |
| printf("LoadFirmware returns: "); |
| switch (status) { |
| case LOAD_FIRMWARE_SUCCESS: |
| printf("LOAD_FIRMWARE_SUCCESS: firmware_index: %lld\n", |
| params.firmware_index); |
| break; |
| case LOAD_FIRMWARE_RECOVERY: |
| puts("LOAD_FIRMWARE_RECOVERY\n"); |
| break; |
| case LOAD_FIRMWARE_REBOOT: |
| puts("LOAD_FIRMWARE_REBOOT\n"); |
| break; |
| default: |
| printf("%d (unknown value!)\n", status); |
| break; |
| } |
| |
| free(firmware_data); |
| |
| return status == LOAD_FIRMWARE_SUCCESS ? 0 : 1; |
| } |
| |
| int do_load_k(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
| { |
| int retcode = 1; |
| int status; |
| LoadKernelParams params; |
| firmware_storage_t file; |
| VbNvContext nvcxt; |
| int i; |
| |
| if (argc != 3) |
| USAGE(1, cmdtp, "Wrong number of arguments\n"); |
| |
| if (firmware_storage_init(&file)) { |
| printf("error: cannot init firmware storage\n"); |
| return 1; |
| } |
| |
| if (load_gbb(&file, ¶ms.gbb_data, ¶ms.gbb_size)) { |
| printf("error: cannot read gbb\n"); |
| return 1; |
| } |
| |
| /* We don't even care if it fails here, just do our best. */ |
| read_nvcontext(&nvcxt); |
| |
| params.boot_flags = (uint64_t) simple_strtoul(argv[1], NULL, 16); |
| params.shared_data_blob = (uint8_t*) simple_strtoul(argv[2], NULL, 16); |
| printf("boot_flags: 0x%08llx\n", params.boot_flags); |
| printf("shared_data_blob: 0x%p\n", params.shared_data_blob); |
| |
| status = load_kernel_wrapper_core(¶ms, |
| params.gbb_data, params.gbb_size, |
| params.boot_flags, &nvcxt, |
| params.shared_data_blob, |
| getenv("bypass_load_kernel") != NULL); |
| |
| switch (status) { |
| case LOAD_KERNEL_SUCCESS: |
| printf("success: good kernel found on device\n"); |
| printf("kernel_buffer: 0x%p\n", params.kernel_buffer); |
| printf("partition_number: %lld\n", params.partition_number); |
| printf("bootloader_address: 0x%llx\n", |
| params.bootloader_address); |
| printf("bootloader_size: 0x%llx\n", params.bootloader_size); |
| printf("partition_guid: "); |
| for (i = 0; i < 16; i++) |
| printf(" %02x", params.partition_guid[i]); |
| putc('\n'); |
| retcode = 0; |
| break; |
| case LOAD_KERNEL_NOT_FOUND: |
| puts("fail: no kernel found on device\n"); |
| break; |
| case LOAD_KERNEL_INVALID: |
| puts("fail: only invalid kernels found on device\n"); |
| break; |
| case LOAD_KERNEL_RECOVERY: |
| puts("fail: internal error: reboot to recovery mode\n"); |
| break; |
| case LOAD_KERNEL_REBOOT: |
| puts("fail: internal error; reboot to current mode\n"); |
| break; |
| default: |
| printf("fail: unexpected return status from LoadKernel: %d\n", |
| status); |
| break; |
| } |
| |
| printf("bytes_per_lba: %lld\n", params.bytes_per_lba); |
| printf("ending_lba: 0x%08llx\n", params.ending_lba); |
| |
| return retcode; |
| } |
| |
| int do_cros_reboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
| { |
| cros_reboot(); |
| return 0; |
| } |
| |
| int do_cros_help(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
| { |
| cmd_tbl_t *c; |
| |
| if (argc < 2) |
| return _do_help(&cmd_cros_sub[0], ARRAY_SIZE(cmd_cros_sub), |
| cmdtp, flag, argc, argv); |
| |
| c = find_cmd_tbl(argv[1], &cmd_cros_sub[0], ARRAY_SIZE(cmd_cros_sub)); |
| if (!c) |
| USAGE(1, cmdtp, "Unrecognized action: %s\n", argv[1]); |
| |
| cmd_usage(c); |
| return 0; |
| } |