blob: 9ca678a41c4b62d8dd9027dc9731c689ba2b4f87 [file] [log] [blame]
/*
* 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(&params, '\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, &params.gbb_data, &params.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(&params,
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;
}