diff options
author | Amaury Pouly <amaury.pouly@gmail.com> | 2012-11-14 12:51:51 +0100 |
---|---|---|
committer | Amaury Pouly <amaury.pouly@gmail.com> | 2012-11-14 12:51:51 +0100 |
commit | f44d95630c1629627b99a2ccf06b434d23027bb3 (patch) | |
tree | b93bef2c03028e0af42e840a6e5944eead2c082b /utils/imxtools/hwemul/tools/hwemul_tool.c | |
parent | ec2153f2dd265129c69c690f9c6c19b61ba9bf18 (diff) | |
download | rockbox-f44d95630c1629627b99a2ccf06b434d23027bb3.tar.gz rockbox-f44d95630c1629627b99a2ccf06b434d23027bb3.zip |
imxtools: introduce hwemul
The hwemul tool is a small binary blob running on the device
that can received commands over USB. It is mainly intended to be
loaded using the recory mode and allows to read/write registers,
memory, use the OTP device, ... The tool is split into three
parts: dev/ contains the actual blob (which handles both imx233
and stmp3700), lib/ contains the communication library and can
also use the register description produced by the regtools/
to ease register by name, tools/ contains an interactive tool
to send commands to the device when running the blob.
Change-Id: Ie8cb32e987f825d8ed750d48071e43415b4dacb3
Diffstat (limited to 'utils/imxtools/hwemul/tools/hwemul_tool.c')
-rw-r--r-- | utils/imxtools/hwemul/tools/hwemul_tool.c | 558 |
1 files changed, 558 insertions, 0 deletions
diff --git a/utils/imxtools/hwemul/tools/hwemul_tool.c b/utils/imxtools/hwemul/tools/hwemul_tool.c new file mode 100644 index 0000000000..c6056edf96 --- /dev/null +++ b/utils/imxtools/hwemul/tools/hwemul_tool.c @@ -0,0 +1,558 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2012 by Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "hwemul.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <stdbool.h> + +bool g_quiet = false; +struct hwemul_device_t hwdev; +struct hwemul_soc_t *cur_soc = NULL; + +void print_log(struct hwemul_device_t *hwdev) +{ + do + { + char buffer[128]; + int length = hwemul_get_log(hwdev, buffer, sizeof(buffer) - 1); + if(length <= 0) + break; + buffer[length] = 0; + printf("%s", buffer); + }while(1); +} + +int print_help() +{ + printf("Commands:\n"); + printf(" help\t\tDisplay this help\n"); + printf(" call <addr>\tCall address <addr>\n"); + printf(" quit\t\tQuit this session\n"); + printf(" read32 <addr>\tRead a 32-bit word at <addr>\n"); + printf(" write32 <value> <addr>\tRead the 32-bit word <value> at <addr>\n"); + printf(" read <regname>\tRead a register by name\n"); + printf(" read <regname>.<field>\tRead a register field by name\n"); + printf(" soc <socname>\tSelect the soc description to use\n"); + printf(" write <value> <regname>\tWrite a register by name\n"); + printf(" write <value <regname>.<field>\tWrite a register field by name\n"); + printf(" NOTE: if the register is SCT variant, no read is performed.\n"); + return 1; +} + +int syntax_error(char *str) +{ + printf("Syntax error at '%s'. Type 'help' to get some help.\n", str); + return 1; +} + +int parse_uint32(char *str, uint32_t *u) +{ + char *end; + *u = strtoul(str, &end, 0); + return *end == 0; +} + +int do_call(uint32_t a) +{ + hwemul_call(&hwdev, a); + return 1; +} + +int parse_call() +{ + char *arg = strtok(NULL, " "); + uint32_t addr; + if(arg && parse_uint32(arg, &addr)) + return do_call(addr); + else + return syntax_error(arg); +} + +int do_read32(uint32_t a) +{ + uint32_t val; + if(hwemul_rw_mem(&hwdev, 1, a, &val, sizeof(val)) == sizeof(val)) + printf("%#x = %#x\n", a, val); + else + printf("read error at %#x\n", a); + return 1; +} + +int parse_read32() +{ + char *arg = strtok(NULL, " "); + uint32_t addr; + if(arg && parse_uint32(arg, &addr)) + return do_read32(addr); + else + return syntax_error(arg); +} + +int do_write32(uint32_t val, uint32_t a) +{ + if(hwemul_rw_mem(&hwdev, 0, a, &val, sizeof(val)) == sizeof(val)) + printf("data written\n"); + else + printf("write error at %#x\n", a); + return 1; +} + +int parse_write32() +{ + char *arg = strtok(NULL, " "); + uint32_t val; + if(!arg || !parse_uint32(arg, &val)) + return syntax_error(arg); + uint32_t addr; + arg = strtok(NULL, " "); + if(arg && parse_uint32(arg, &addr)) + return do_write32(val, addr); + else + return syntax_error(arg); +} + +struct hwemul_soc_t *find_soc_by_name(const char *soc) +{ + struct hwemul_soc_list_t *list = hwemul_get_soc_list(); + for(size_t i = 0; i < list->nr_socs; i++) + if(strcmp(soc, list->socs[i]->name) == 0) + return list->socs[i]; + return NULL; +} + +struct hwemul_soc_reg_t *find_reg_by_name(struct hwemul_soc_t *soc, const char *reg) +{ + for(size_t i = 0; i < soc->nr_regs; i++) + if(strcmp(reg, soc->regs_by_name[i]->name) == 0) + return soc->regs_by_name[i]; + return NULL; +} + +struct hwemul_soc_reg_field_t *find_field_by_name(struct hwemul_soc_reg_t *reg, const char *field) +{ + for(size_t i = 0; i < reg->nr_fields; i++) + if(strcmp(field, reg->fields_by_name[i]->name) == 0) + return reg->fields_by_name[i]; + return NULL; +} + + +int do_read(char *regname) +{ + char *dot = strchr(regname, '.'); + if(dot != NULL) + *dot++ = 0; + if(cur_soc == NULL) + { + printf("No soc selected!\n"); + return 1; + } + struct hwemul_soc_reg_t *reg = find_reg_by_name(cur_soc, regname); + if(reg == NULL) + { + printf("no reg '%s' found\n", regname); + return 1; + } + uint32_t val; + if(hwemul_rw_mem(&hwdev, 1, reg->addr, &val, sizeof(val)) != sizeof(val)) + { + printf("read error at %#x\n", reg->addr); + return 1; + } + if(dot) + { + struct hwemul_soc_reg_field_t *field = find_field_by_name(reg, dot); + if(field == NULL) + { + printf("no field '%s' found\n", dot); + return 1; + } + val >>= field->first_bit; + val &= (1 << (field->last_bit - field->first_bit + 1)) - 1; + printf("%s.%s = %#x\n", regname, dot, val); + } + else + printf("%s = %#x\n", regname, val); + return 1; +} + +int parse_read() +{ + char *arg = strtok(NULL, " "); + if(arg) + return do_read(arg); + else + return syntax_error(arg); +} + +int do_soc(char *soc) +{ + struct hwemul_soc_t *s = find_soc_by_name(soc); + if(s == NULL) + printf("no soc '%s' found\n", soc); + else + cur_soc = s; + return 1; +} + +int parse_soc() +{ + char *arg = strtok(NULL, " "); + if(arg) + return do_soc(arg); + else + return syntax_error(arg); +} + +int do_write(uint32_t val, char *regname) +{ + char *dot = strchr(regname, '.'); + if(dot != NULL) + *dot++ = 0; + if(cur_soc == NULL) + { + printf("No soc selected!\n"); + return 1; + } + struct hwemul_soc_reg_t *reg = find_reg_by_name(cur_soc, regname); + int is_sct = 0; + uint32_t addr_off = 0; + if(reg == NULL) + { + size_t len = strlen(regname); + /* try SCT variant */ + if(strcmp(regname + len - 4, "_SET") == 0) + addr_off = 4; + else if(strcmp(regname + len - 4, "_CLR") == 0) + addr_off = 8; + else if(strcmp(regname + len - 4, "_TOG") == 0) + addr_off = 12; + else + { + printf("no reg '%s' found\n", regname); + return 1; + } + is_sct = 1; + regname[len - 4] = 0; + reg = find_reg_by_name(cur_soc, regname); + if(reg == NULL) + { + printf("no reg '%s' found\n", regname); + return 1; + } + } + if(dot) + { + struct hwemul_soc_reg_field_t *field = find_field_by_name(reg, dot); + if(field == NULL) + { + printf("no field '%s' found\n", dot); + return 1; + } + uint32_t actual_val = 0; + if(!is_sct) + { + if(hwemul_rw_mem(&hwdev, 1, reg->addr, &actual_val, sizeof(actual_val)) != sizeof(actual_val)) + { + printf("read error at %#x\n", reg->addr); + return 1; + } + printf("read %#x at %#x\n", actual_val, reg->addr); + } + uint32_t mask = ((1 << (field->last_bit - field->first_bit + 1)) - 1) << field->first_bit; + printf("mask=%#x\n", mask); + val = (actual_val & ~mask) | ((val << field->first_bit) & mask); + } + printf("write %#x to %#x\n", val, reg->addr + addr_off); + if(hwemul_rw_mem(&hwdev, 0, reg->addr + addr_off, &val, sizeof(val)) != sizeof(val)) + { + printf("write error at %#x\n", reg->addr); + return 1; + } + return 1; +} + +int parse_write() +{ + char *arg = strtok(NULL, " "); + uint32_t val; + if(!arg || !parse_uint32(arg, &val)) + return syntax_error(arg); + arg = strtok(NULL, " "); + if(arg) + return do_write(val, arg); + else + return syntax_error(arg); +} + +int parse_command(char *cmd) +{ + if(strcmp(cmd, "help") == 0) + return print_help(); + if(strcmp(cmd, "quit") == 0) + return 0; + if(strcmp(cmd, "call") == 0) + return parse_call(); + if(strcmp(cmd, "read32") == 0) + return parse_read32(); + if(strcmp(cmd, "write32") == 0) + return parse_write32(); + if(strcmp(cmd, "read") == 0) + return parse_read(); + if(strcmp(cmd, "soc") == 0) + return parse_soc(); + if(strcmp(cmd, "write") == 0) + return parse_write(); + return syntax_error(cmd); +} + +int do_command() +{ + char *line = NULL; + int size = 0; + getline(&line, &size, stdin); + char *end = strchr(line, '\n'); + if(end) + *end = 0; + char *pch = strtok(line, " "); + int ret; + if(pch) + ret = parse_command(pch); + else + ret = print_help(); + free(line); + return ret; +} + +void usage(void) +{ + printf("hwemul_tool, compiled with hwemul %d.%d.%d\n", + HWEMUL_VERSION_MAJOR, HWEMUL_VERSION_MINOR, HWEMUL_VERSION_REV); + printf("available soc descriptions:"); + for(unsigned i = 0; i < hwemul_get_soc_list()->nr_socs; i++) + printf(" %s", hwemul_get_soc_list()->socs[i]->name); + printf("\n"); + printf("usage: hwemul_tool [options]\n"); + printf("options:\n"); + printf(" --help/-?\tDisplay this help\n"); + printf(" --quiet/-q\tQuiet non-command messages\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + while(1) + { + static struct option long_options[] = + { + {"help", no_argument, 0, '?'}, + {"quiet", no_argument, 0, 'q'}, + {0, 0, 0, 0} + }; + + int c = getopt_long(argc, argv, "?q", long_options, NULL); + if(c == -1) + break; + switch(c) + { + case -1: + break; + case 'q': + g_quiet = true; + break; + case '?': + usage(); + break; + default: + abort(); + } + } + + if(argc - optind != 0) + { + usage(); + return 1; + } + + libusb_context *ctx; + libusb_init(&ctx); + libusb_set_debug(ctx, 3); + + if(!g_quiet) + printf("Looking for device %#04x:%#04x...\n", HWEMUL_USB_VID, HWEMUL_USB_PID); + + libusb_device_handle *handle = libusb_open_device_with_vid_pid(ctx, + HWEMUL_USB_VID, HWEMUL_USB_PID); + if(handle == NULL) + { + printf("No device found\n"); + return 1; + } + + libusb_device *mydev = libusb_get_device(handle); + if(!g_quiet) + { + printf("device found at %d:%d\n", + libusb_get_bus_number(mydev), + libusb_get_device_address(mydev)); + } + hwdev.handle = handle; + if(hwemul_probe(&hwdev)) + { + printf("Cannot probe device!\n"); + return 1; + } + + struct usb_resp_info_version_t ver; + int ret = hwemul_get_info(&hwdev, HWEMUL_INFO_VERSION, &ver, sizeof(ver)); + if(ret != sizeof(ver)) + { + printf("Cannot get version!\n"); + goto Lerr; + } + if(!g_quiet) + printf("Device version: %d.%d.%d\n", ver.major, ver.minor, ver.revision); + + struct usb_resp_info_layout_t layout; + ret = hwemul_get_info(&hwdev, HWEMUL_INFO_LAYOUT, &layout, sizeof(layout)); + if(ret != sizeof(layout)) + { + printf("Cannot get layout: %d\n", ret); + goto Lerr; + } + if(!g_quiet) + { + printf("Device layout:\n"); + printf(" Code: 0x%x (0x%x)\n", layout.oc_code_start, layout.oc_code_size); + printf(" Stack: 0x%x (0x%x)\n", layout.oc_stack_start, layout.oc_stack_size); + printf(" Buffer: 0x%x (0x%x)\n", layout.oc_buffer_start, layout.oc_buffer_size); + } + + struct usb_resp_info_features_t features; + ret = hwemul_get_info(&hwdev, HWEMUL_INFO_FEATURES, &features, sizeof(features)); + if(ret != sizeof(features)) + { + printf("Cannot get features: %d\n", ret); + goto Lerr; + } + if(!g_quiet) + { + printf("Device features:"); + if(features.feature_mask & HWEMUL_FEATURE_LOG) + printf(" log"); + if(features.feature_mask & HWEMUL_FEATURE_MEM) + printf(" mem"); + if(features.feature_mask & HWEMUL_FEATURE_CALL) + printf(" call"); + if(features.feature_mask & HWEMUL_FEATURE_JUMP) + printf(" jump"); + if(features.feature_mask & HWEMUL_FEATURE_AES_OTP) + printf(" aes_otp"); + printf("\n"); + } + + struct usb_resp_info_stmp_t stmp; + ret = hwemul_get_info(&hwdev, HWEMUL_INFO_STMP, &stmp, sizeof(stmp)); + if(ret != sizeof(stmp)) + { + printf("Cannot get stmp: %d\n", ret); + goto Lerr; + } + if(!g_quiet) + { + printf("Device stmp:\n"); + printf(" chip ID: %x (%s)\n", stmp.chipid,hwemul_get_product_string(&stmp)); + printf(" revision: %d (%s)\n", stmp.rev, hwemul_get_rev_string(&stmp)); + printf(" supported: %d\n", stmp.is_supported); + } + + if(!g_quiet) + { + void *rom = malloc(64 * 1024); + ret = hwemul_rw_mem(&hwdev, 1, 0xc0000000, rom, 64 * 1024); + if(ret != 64 * 1024) + { + printf("Cannot read ROM: %d\n", ret); + goto Lerr; + } + + printf("ROM successfully read!\n"); + FILE *f = fopen("rom.bin", "wb"); + fwrite(rom, 64 * 1024, 1, f); + fclose(f); + } + + if(!g_quiet) + { + struct + { + uint8_t iv[16]; + uint8_t data[16]; + } __attribute__((packed)) dcp_test; + + for(int i = 0; i < 16; i++) + dcp_test.iv[i] = rand(); + for(int i = 0; i < 16; i++) + dcp_test.data[i] = rand(); + printf("DCP\n"); + printf(" IN\n"); + printf(" IV:"); + for(int i = 0; i < 16; i++) + printf(" %02x", dcp_test.iv[i]); + printf("\n"); + printf(" IV:"); + for(int i = 0; i < 16; i++) + printf(" %02x", dcp_test.data[i]); + printf("\n"); + + if(!hwemul_aes_otp(&hwdev, &dcp_test, sizeof(dcp_test), HWEMUL_AES_OTP_ENCRYPT)) + { + printf(" OUT\n"); + printf(" IV:"); + for(int i = 0; i < 16; i++) + printf(" %02x", dcp_test.iv[i]); + printf("\n"); + printf(" IV:"); + for(int i = 0; i < 16; i++) + printf(" %02x", dcp_test.data[i]); + printf("\n"); + } + else + printf("DCP error!\n"); + } + + if(!g_quiet) + printf("Starting interactive session. Type 'help' to get help.\n"); + while(1) + if(!do_command()) + break; + Lerr: + if(features.feature_mask & HWEMUL_FEATURE_LOG) + { + if(!g_quiet) + printf("Device log:\n"); + print_log(&hwdev); + } + hwemul_release(&hwdev); + return 1; +} |