From c5357940ab0108b4102442d07825c44d5be0d22f Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Thu, 13 Jun 2013 02:02:53 +0200 Subject: hwstub: major improvement in the stub and the tools Fix the stub in many way to correctly detect the STMP family and act upon that. Drop some unused commands and bump version. Rewrite the tool to allows scripting in lua and load the register description from an XML file using the regtools. Introduce a new tool to load and run code using the hwstub (either binary format or Rockbox additive scramble format). Also switch to an optimise version of the memcpy/move/set functions to correctly handle alignement issue (like writing a full word/half-word when possible for registers which is crucial) Change-Id: Id1d5cfe0b1b47e8b43900d32c5cd6eafae6414f6 --- utils/hwstub/tools/Makefile | 36 +- utils/hwstub/tools/hwemul_tool.c | 558 ----------------------- utils/hwstub/tools/hwstub_load.cpp | 316 +++++++++++++ utils/hwstub/tools/hwstub_shell.cpp | 873 ++++++++++++++++++++++++++++++++++++ utils/hwstub/tools/init.lua | 104 +++++ 5 files changed, 1319 insertions(+), 568 deletions(-) delete mode 100644 utils/hwstub/tools/hwemul_tool.c create mode 100644 utils/hwstub/tools/hwstub_load.cpp create mode 100644 utils/hwstub/tools/hwstub_shell.cpp create mode 100644 utils/hwstub/tools/init.lua (limited to 'utils/hwstub/tools') diff --git a/utils/hwstub/tools/Makefile b/utils/hwstub/tools/Makefile index 3466a4e776..6db0c709b1 100644 --- a/utils/hwstub/tools/Makefile +++ b/utils/hwstub/tools/Makefile @@ -1,22 +1,38 @@ CC=gcc -AR=ar -HWEMUL_LIB_DIR=../lib -CFLAGS=-W -Wall -O2 `pkg-config --cflags libusb-1.0` -std=c99 -g -I$(HWEMUL_LIB_DIR) -LDFLAGS=`pkg-config --libs libusb-1.0` -lreadline -EXEC=hwemul_tool -HWEMUL_LIB=$(HWEMUL_LIB_DIR)/libhwemul.a +CXX=g++ +LD=g++ +HWSTUB_LIB_DIR=../lib +REGTOOLS_LIB_DIR=../../regtools/lib +CFLAGS=-Wall -O2 `pkg-config --cflags libusb-1.0` -std=c99 -g -I$(HWSTUB_LIB_DIR) -I$(REGTOOLS_LIB_DIR) `pkg-config --cflags lua5.2` +CXXFLAGS=-Wall -O2 `pkg-config --cflags libusb-1.0` -g -I$(HWSTUB_LIB_DIR) -I$(REGTOOLS_LIB_DIR) `pkg-config --cflags lua5.2` +LDFLAGS=`pkg-config --libs libusb-1.0` `pkg-config --libs lua5.2` -lreadline -L$(HWSTUB_LIB_DIR) -L$(REGTOOLS_LIB_DIR) -lsocdesc -lhwstub `xml2-config --libs` +EXEC=hwstub_shell hwstub_load SRC=$(wildcard *.c) -OBJ=$(SRC:.c=.o) +SRCXX=$(wildcard *.cpp) +OBJ=$(SRC:.c=.o) $(SRCXX:.cpp=.o) +LIBS=$(HWSTUB_LIB_DIR)/libhwstub.a $(REGTOOLS_LIB_DIR)/libsocdesc.a all: $(EXEC) +$(HWSTUB_LIB_DIR)/libhwstub.a: + make -C $(HWSTUB_LIB_DIR) + +$(REGTOOLS_LIB_DIR)/libsocdesc.a: + make -C $(REGTOOLS_LIB_DIR) + %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< -hwemul_tool: hwemul_tool.o $(HWEMUL_LIB) - $(CC) -o $@ $^ $(LDFLAGS) +%.o: %.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +hwstub_shell: hwstub_shell.o $(LIBS) + $(LD) -o $@ $^ $(LDFLAGS) + +hwstub_load: hwstub_load.o $(LIBS) + $(LD) -o $@ $^ $(LDFLAGS) clean: - rm -rf $(OBJ) $(LIB) + rm -rf $(OBJ) $(LIB) $(EXEC) diff --git a/utils/hwstub/tools/hwemul_tool.c b/utils/hwstub/tools/hwemul_tool.c deleted file mode 100644 index d75cd7a957..0000000000 --- a/utils/hwstub/tools/hwemul_tool.c +++ /dev/null @@ -1,558 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * 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 -#include -#include -#include -#include -#include -#include - -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 \tCall address \n"); - printf(" quit\t\tQuit this session\n"); - printf(" read32 \tRead a 32-bit word at \n"); - printf(" write32 \tRead the 32-bit word at \n"); - printf(" read \tRead a register by name\n"); - printf(" read .\tRead a register field by name\n"); - printf(" soc \tSelect the soc description to use\n"); - printf(" write \tWrite a register by name\n"); - printf(" write .\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); -} - -void interactive_mode(void) -{ - rl_bind_key('\t', rl_complete); - while(1) - { - char *input = readline("> "); - if(!input) - break; - add_history(input); - int ret = parse_command(input); - free(input); - if(ret == 0) - break; - } -} - -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"); - - interactive_mode(); - - Lerr: - if(features.feature_mask & HWEMUL_FEATURE_LOG) - { - if(!g_quiet) - printf("Device log:\n"); - print_log(&hwdev); - } - hwemul_release(&hwdev); - return 1; -} diff --git a/utils/hwstub/tools/hwstub_load.cpp b/utils/hwstub/tools/hwstub_load.cpp new file mode 100644 index 0000000000..d58eb83396 --- /dev/null +++ b/utils/hwstub/tools/hwstub_load.cpp @@ -0,0 +1,316 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2013 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 "hwstub.h" +#include +#include +#include +#include +#include +#include + +struct player_info_t +{ + const char *name; + const char *username; + int modelnum; +}; + +enum image_type_t +{ + IT_RAW, + IT_ROCKBOX, + IT_DETECT, + /* positive values reserved for rockbox-specific models */ +}; + +struct player_info_t players[] = +{ + { "zenv", "Zen V", 85 }, + { "zmoz", "Zen Mozaic", 87 }, + { "zen", "Zen", 88 }, + { "zxfi", "Zen X-Fi", 86 }, + { NULL, 0 }, +}; + +enum image_type_t detect_type(unsigned char *buffer, size_t size) +{ + if(size < 8) + return IT_RAW; + int player; + for(player = 0; players[player].name; player++) + if(memcmp(buffer + 4, players[player].name, 4) == 0) + break; + if(players[player].name == NULL) + return IT_RAW; + unsigned long checksum = players[player].modelnum; + for(size_t i = 8; i < size; i++) + checksum += buffer[i]; + unsigned long expected = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3]; + if(checksum != expected) + return IT_RAW; + return IT_ROCKBOX; +} + +const char *get_player_name(unsigned char *buffer) +{ + for(int player = 0; players[player].name; player++) + if(memcmp(buffer, players[player].name, 4) == 0) + return players[player].username; + return NULL; +} + +bool could_be_rockbox(unsigned char *buffer, size_t size) +{ + /* usually target use 3 or 4 digits */ + if(size >= 8 && isprint(buffer[4]) && isprint(buffer[5]) && isprint(buffer[6]) && + (isprint(buffer[7]) || buffer[7] == 0)) + { + unsigned long checksum = 0; + for(size_t i = 8; i < size; i++) + checksum += buffer[i]; + unsigned long expected = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3]; + unsigned long expected_modelnm = expected - checksum; + if(expected_modelnm < 150) + fprintf(stderr, "This file looks like a valid rockbox image but I don't know this player: %.4s (modelnum=%ld)\n", + buffer + 4, expected_modelnm); + else + fprintf(stderr, "This file could be a valid rockbox image but I don't know this player and the checksum is strange: %.4s\n", + buffer + 4); + return true; + } + else + return false; +} + +void usage(void) +{ + printf("usage: hwstub_load [options] \n"); + printf("options:\n"); + printf(" --help/-? Display this help\n"); + printf(" --quiet/-q Quiet output\n"); + printf(" --type/-t Override file type\n"); + printf("file types:\n"); + printf(" raw Load a raw binary blob\n"); + printf(" rockbox Load a rockbox image produced by scramble\n"); + printf(" detect Try to guess the format\n"); + printf("known players:"); + for(int i = 0; players[i].name; i++) + printf(" %s", players[i].name); + printf("\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + bool quiet = false; + struct hwstub_device_t hwdev; + enum image_type_t type = IT_DETECT; + + // parse command line + while(1) + { + static struct option long_options[] = + { + {"help", no_argument, 0, '?'}, + {"quiet", no_argument, 0, 'q'}, + {"type", required_argument, 0, 't'}, + {0, 0, 0, 0} + }; + + int c = getopt_long(argc, argv, "?qt:", long_options, NULL); + if(c == -1) + break; + switch(c) + { + case -1: + break; + case 'q': + quiet = true; + break; + case '?': + usage(); + break; + case 't': + if(strcmp(optarg, "raw") == 0) + type = IT_RAW; + else if(strcmp(optarg, "rockbox") == 0) + type = IT_ROCKBOX; + else if(strcmp(optarg, "detect") == 0) + type = IT_DETECT; + else + { + fprintf(stderr, "Unknown file type '%s'\n", optarg); + return 1; + } + break; + default: + abort(); + } + } + + if(optind + 2 != argc) + usage(); + + char *end; + unsigned long addr = strtoul(argv[optind], &end, 0); + if(*end) + { + fprintf(stderr, "Invalid load address\n"); + return 2; + } + + FILE *f = fopen(argv[optind + 1], "rb"); + if(f == NULL) + { + fprintf(stderr, "Cannot open file for reading: %m\n"); + return 3; + } + fseek(f, 0, SEEK_END); + size_t size = ftell(f); + fseek(f, 0, SEEK_SET); + unsigned char *buffer = (unsigned char*)malloc(size); + fread(buffer, size, 1, f); + fclose(f); + + if(type == IT_ROCKBOX || type == IT_DETECT) + { + enum image_type_t det = detect_type(buffer, size); + if(type == IT_ROCKBOX && det != IT_ROCKBOX) + { + if(!could_be_rockbox(buffer, size)) + fprintf(stderr, "This file does not appear to be valid rockbox image.\n"); + return 4; + } + if(type == IT_DETECT && det == IT_RAW) + could_be_rockbox(buffer, size); + type = det; + if(type == IT_ROCKBOX) + { + if(!quiet) + printf("Rockox image is for player %s (%.4s)\n", get_player_name(buffer + 4), buffer + 4); + memmove(buffer, buffer + 8, size - 8); + size -= 8; + } + } + + if(!quiet) + { + if(type == IT_RAW) + printf("Loading raw image at %#lx\n", addr); + else + printf("Loading rockbox image at %#lx\n", addr); + } + + // create usb context + libusb_context *ctx; + libusb_init(&ctx); + libusb_set_debug(ctx, 3); + + // look for device + if(!quiet) + printf("Looking for device %#04x:%#04x...\n", HWSTUB_USB_VID, HWSTUB_USB_PID); + + libusb_device_handle *handle = libusb_open_device_with_vid_pid(ctx, + HWSTUB_USB_VID, HWSTUB_USB_PID); + if(handle == NULL) + { + fprintf(stderr, "No device found\n"); + return 1; + } + + // admin stuff + libusb_device *mydev = libusb_get_device(handle); + if(!quiet) + { + printf("device found at %d:%d\n", + libusb_get_bus_number(mydev), + libusb_get_device_address(mydev)); + } + hwdev.handle = handle; + if(hwstub_probe(&hwdev)) + { + fprintf(stderr, "Cannot probe device!\n"); + return 1; + } + + // get hwstub information + struct usb_resp_info_version_t hwdev_ver; + int ret = hwstub_get_info(&hwdev, HWSTUB_INFO_VERSION, &hwdev_ver, sizeof(hwdev_ver)); + if(ret != sizeof(hwdev_ver)) + { + fprintf(stderr, "Cannot get version!\n"); + goto Lerr; + } + if(hwdev_ver.major != HWSTUB_VERSION_MAJOR || hwdev_ver.minor < HWSTUB_VERSION_MINOR) + { + printf("Warning: this tool is possibly incompatible with your device:\n"); + printf("Device version: %d.%d.%d\n", hwdev_ver.major, hwdev_ver.minor, hwdev_ver.revision); + printf("Host version: %d.%d.%d\n", HWSTUB_VERSION_MAJOR, HWSTUB_VERSION_MINOR, HWSTUB_VERSION_REV); + } + + // get features + struct usb_resp_info_features_t hwdev_features; + ret = hwstub_get_info(&hwdev, HWSTUB_INFO_FEATURES, &hwdev_features, sizeof(hwdev_features)); + if(ret != sizeof(hwdev_features)) + { + fprintf(stderr, "Cannot get features: %d\n", ret); + goto Lerr; + } + if(!(hwdev_features.feature_mask & HWSTUB_RW_MEM)) + { + fprintf(stderr, "Device doesn't support R/W commands\n"); + goto Lerr; + } + if(!(hwdev_features.feature_mask & HWSTUB_JUMP)) + { + fprintf(stderr, "Device doesn't support jump commands\n"); + goto Lerr; + } + ret = hwstub_rw_mem(&hwdev, 0, addr, buffer, size); + if(ret != (int)size) + { + fprintf(stderr, "Image write failed\n"); + goto Lerr; + } + hwstub_jump(&hwdev, addr); + + hwstub_release(&hwdev); + return 0; + + Lerr: + // display log if handled + if(hwdev_features.feature_mask & HWSTUB_FEATURE_LOG) + { + fprintf(stderr, "Device log:\n"); + do + { + char buffer[128]; + int length = hwstub_get_log(&hwdev, buffer, sizeof(buffer) - 1); + if(length <= 0) + break; + buffer[length] = 0; + fprintf(stderr, "%s", buffer); + }while(1); + } + hwstub_release(&hwdev); + return 1; +} + diff --git a/utils/hwstub/tools/hwstub_shell.cpp b/utils/hwstub/tools/hwstub_shell.cpp new file mode 100644 index 0000000000..58147319e0 --- /dev/null +++ b/utils/hwstub/tools/hwstub_shell.cpp @@ -0,0 +1,873 @@ +/*************************************************************************** + * __________ __ ___. + * 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 "hwstub.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "soc_desc.hpp" + +#if LUA_VERSION_NUM < 502 +#warning You need at least lua 5.2 +#endif + +/** + * Global variables + */ +bool g_quiet = false; +struct hwstub_device_t g_hwdev; +struct usb_resp_info_version_t g_hwdev_ver; +struct usb_resp_info_layout_t g_hwdev_layout; +struct usb_resp_info_features_t g_hwdev_features; +struct usb_resp_info_stmp_t g_hwdev_stmp; +lua_State *g_lua; + +/** + * hw specific + */ + +void print_log(struct hwstub_device_t *hwdev) +{ + do + { + char buffer[128]; + int length = hwstub_get_log(hwdev, buffer, sizeof(buffer) - 1); + if(length <= 0) + break; + buffer[length] = 0; + printf("%s", buffer); + }while(1); +} + +/** + * Lua specific + */ +int my_lua_help(lua_State *state) +{ + bool has_sub = false; + // implement help() in C so that we do not rely on the init to implement it + // help can take optional arguments + int n = lua_gettop(state); + + lua_getglobal(state, "hwstub"); + if(!lua_istable(state, -1)) + goto Lerr; + lua_getfield(state, -1, "help"); + if(!lua_istable(state, -1)) + goto Lerr; + + for(int i = 1; i <= n; i++) + { + lua_pushvalue(state, i); + lua_gettable(state, -2); + if(lua_isnil(state, -1)) + { + printf("I don't know subtopic '%s'!\n", lua_tostring(state, i)); + return 0; + } + if(!lua_istable(state, -1)) + { + printf("Subtopic '%s' is not a table!\n", lua_tostring(state, i)); + return 0; + } + } + + printf("================[ HELP "); + for(int i = 1; i <= n; i++) + printf("> %s ", lua_tostring(state, i)); + printf("]================\n"); + + lua_pushnil(state); + while(lua_next(state, -2)) + { + // key is at -2 and value at -1 + if(lua_isstring(state, -1)) + printf("%s\n", lua_tostring(state, -1)); + else if(lua_istable(state, -1)) + has_sub = true; + // pop value but keep key + lua_pop(state, 1); + } + + if(has_sub) + { + printf("\n"); + printf("You can get more information on the following subtopics:\n"); + lua_pushnil(state); + while(lua_next(state, -2)) + { + // key is at -2 and value at -1 + if(lua_istable(state, -1)) + printf("* %s\n", lua_tostring(state, -2)); + // pop value but keep key + lua_pop(state, 1); + } + } + printf("================[ STOP ]================\n"); + + return 0; + + Lerr: + printf("There is a problem with the Lua context. Help is expected to be in hwstub.help\n"); + printf("You must have messed badly the environment.\n"); + return 0; +} + +typedef soc_word_t (*hw_readn_fn_t)(lua_State *state, soc_addr_t addr); +typedef void (*hw_writen_fn_t)(lua_State *state, soc_addr_t addr, soc_word_t val); + +soc_word_t hw_read8(lua_State *state, soc_addr_t addr) +{ + uint8_t u; + if(hwstub_rw_mem(&g_hwdev, 1, addr, &u, sizeof(u)) != sizeof(u)) + luaL_error(state, "fail to read8 @ %p", addr); + return u; +} + +soc_word_t hw_read16(lua_State *state, soc_addr_t addr) +{ + uint16_t u; + if(hwstub_rw_mem(&g_hwdev, 1, addr, &u, sizeof(u)) != sizeof(u)) + luaL_error(state, "fail to read16 @ %p", addr); + return u; +} + +soc_word_t hw_read32(lua_State *state, soc_addr_t addr) +{ + uint32_t u; + if(hwstub_rw_mem(&g_hwdev, 1, addr, &u, sizeof(u)) != sizeof(u)) + luaL_error(state, "fail to read32 @ %p", addr); + return u; +} + +void hw_write8(lua_State *state, soc_addr_t addr, soc_word_t val) +{ + uint8_t u = val; + if(hwstub_rw_mem(&g_hwdev, 0, addr, &u, sizeof(u)) != sizeof(u)) + luaL_error(state, "fail to write8 @ %p", addr); +} + +void hw_write16(lua_State *state, soc_addr_t addr, soc_word_t val) +{ + uint16_t u = val; + if(hwstub_rw_mem(&g_hwdev, 0, addr, &u, sizeof(u)) != sizeof(u)) + luaL_error(state, "fail to write16 @ %p", addr); +} + +void hw_write32(lua_State *state, soc_addr_t addr, soc_word_t val) +{ + uint32_t u = val; + if(hwstub_rw_mem(&g_hwdev, 0, addr, &u, sizeof(u)) != sizeof(u)) + luaL_error(state, "fail to write32 @ %p", addr); +} + +int my_lua_readn(lua_State *state) +{ + hw_readn_fn_t fn = (hw_readn_fn_t)lua_touserdata(state, lua_upvalueindex(1)); + int n = lua_gettop(state); + if(n != 1) + luaL_error(state, "readn takes a single argument"); + lua_pushunsigned(state, fn(state, luaL_checkunsigned(state, 1))); + return 1; +} + +int my_lua_writen(lua_State *state) +{ + hw_writen_fn_t fn = (hw_writen_fn_t)lua_touserdata(state, lua_upvalueindex(1)); + int n = lua_gettop(state); + if(n != 2) + luaL_error(state, "writen takes two arguments"); + fn(state, luaL_checkunsigned(state, 1), luaL_checkunsigned(state, 2)); + return 0; +} + +int my_lua_printlog(lua_State *state) +{ + print_log(&g_hwdev); + return 0; +} + +bool my_lua_import_hwstub() +{ + int oldtop = lua_gettop(g_lua); + + lua_newtable(g_lua); // hwstub + + lua_newtable(g_lua); // options + lua_pushboolean(g_lua, g_quiet); + lua_setfield(g_lua, -2, "quiet"); + lua_setfield(g_lua, -2, "options"); + + lua_newtable(g_lua); // dev + lua_newtable(g_lua); // version + lua_pushinteger(g_lua, g_hwdev_ver.major); + lua_setfield(g_lua, -2, "major"); + lua_pushinteger(g_lua, g_hwdev_ver.minor); + lua_setfield(g_lua, -2, "minor"); + lua_pushinteger(g_lua, g_hwdev_ver.revision); + lua_setfield(g_lua, -2, "revision"); + lua_setfield(g_lua, -2, "version"); + + lua_newtable(g_lua); // layout + lua_newtable(g_lua); // ocram + lua_newtable(g_lua); // code + lua_pushinteger(g_lua, g_hwdev_layout.oc_code_start); + lua_setfield(g_lua, -2, "start"); + lua_pushinteger(g_lua, g_hwdev_layout.oc_code_size); + lua_setfield(g_lua, -2, "size"); + lua_setfield(g_lua, -2, "code"); + lua_newtable(g_lua); // stack + lua_pushinteger(g_lua, g_hwdev_layout.oc_stack_start); + lua_setfield(g_lua, -2, "start"); + lua_pushinteger(g_lua, g_hwdev_layout.oc_stack_size); + lua_setfield(g_lua, -2, "size"); + lua_setfield(g_lua, -2, "stack"); + lua_newtable(g_lua); // buffer + lua_pushinteger(g_lua, g_hwdev_layout.oc_buffer_start); + lua_setfield(g_lua, -2, "start"); + lua_pushinteger(g_lua, g_hwdev_layout.oc_buffer_size); + lua_setfield(g_lua, -2, "size"); + lua_setfield(g_lua, -2, "buffer"); + lua_setfield(g_lua, -2, "ocram"); + lua_setfield(g_lua, -2, "layout"); + + lua_newtable(g_lua); // stmp + lua_pushinteger(g_lua, g_hwdev_stmp.chipid); + lua_setfield(g_lua, -2, "chipid"); + lua_pushinteger(g_lua, g_hwdev_stmp.rev); + lua_setfield(g_lua, -2, "rev"); + lua_setfield(g_lua, -2, "stmp"); + + lua_newtable(g_lua); // features + lua_pushboolean(g_lua, !!(g_hwdev_features.feature_mask & HWSTUB_FEATURE_LOG)); + lua_setfield(g_lua, -2, "log"); + lua_pushboolean(g_lua, !!(g_hwdev_features.feature_mask & HWSTUB_FEATURE_MEM)); + lua_setfield(g_lua, -2, "mem"); + lua_pushboolean(g_lua, !!(g_hwdev_features.feature_mask & HWSTUB_FEATURE_CALL)); + lua_setfield(g_lua, -2, "call"); + lua_pushboolean(g_lua, !!(g_hwdev_features.feature_mask & HWSTUB_FEATURE_JUMP)); + lua_setfield(g_lua, -2, "jump"); + lua_pushboolean(g_lua, !!(g_hwdev_features.feature_mask & HWSTUB_FEATURE_AES_OTP)); + lua_setfield(g_lua, -2, "aes_otp"); + lua_setfield(g_lua, -2, "features"); + + lua_pushlightuserdata(g_lua, (void *)&hw_read8); + lua_pushcclosure(g_lua, my_lua_readn, 1); + lua_setfield(g_lua, -2, "read8"); + lua_pushlightuserdata(g_lua, (void *)&hw_read16); + lua_pushcclosure(g_lua, my_lua_readn, 1); + lua_setfield(g_lua, -2, "read16"); + lua_pushlightuserdata(g_lua, (void *)&hw_read32); + lua_pushcclosure(g_lua, my_lua_readn, 1); + lua_setfield(g_lua, -2, "read32"); + + lua_pushlightuserdata(g_lua, (void *)&hw_write8); + lua_pushcclosure(g_lua, my_lua_writen, 1); + lua_setfield(g_lua, -2, "write8"); + lua_pushlightuserdata(g_lua, (void *)&hw_write16); + lua_pushcclosure(g_lua, my_lua_writen, 1); + lua_setfield(g_lua, -2, "write16"); + lua_pushlightuserdata(g_lua, (void *)&hw_write32); + lua_pushcclosure(g_lua, my_lua_writen, 1); + lua_setfield(g_lua, -2, "write32"); + lua_pushcclosure(g_lua, my_lua_printlog, 0); + lua_setfield(g_lua, -2, "print_log"); + + lua_setfield(g_lua, -2, "dev"); + + lua_newtable(g_lua); // host + lua_newtable(g_lua); // version + lua_pushinteger(g_lua, HWSTUB_VERSION_MAJOR); + lua_setfield(g_lua, -2, "major"); + lua_pushinteger(g_lua, HWSTUB_VERSION_MINOR); + lua_setfield(g_lua, -2, "minor"); + lua_pushinteger(g_lua, HWSTUB_VERSION_REV); + lua_setfield(g_lua, -2, "revision"); + lua_setfield(g_lua, -2, "version"); + lua_setfield(g_lua, -2, "host"); + + lua_newtable(g_lua); // soc + lua_setfield(g_lua, -2, "soc"); + + lua_newtable(g_lua); // help + lua_pushinteger(g_lua, 1); + lua_pushstring(g_lua, "This is the help for hwstub_tool. This tools uses Lua to interpret commands."); + lua_settable(g_lua, -3); + lua_pushinteger(g_lua, 2); + lua_pushstring(g_lua, "You can get help by running help(). Help is organised in topics and subtopics and so on."); + lua_settable(g_lua, -3); + lua_pushinteger(g_lua, 3); + lua_pushstring(g_lua, "If you want to access the help of topic x, subtopic y, subsubtopic z, type help(x,y,z)."); + lua_settable(g_lua, -3); + lua_pushinteger(g_lua, 4); + lua_pushstring(g_lua, "Example: help(\"hwstub\")."); + lua_settable(g_lua, -3); + lua_setfield(g_lua, -2, "help"); + + lua_setglobal(g_lua, "hwstub"); + + lua_pushcfunction(g_lua, my_lua_help); + lua_setglobal(g_lua, "help"); + + if(lua_gettop(g_lua) != oldtop) + { + printf("internal error: unbalanced my_lua_import_soc"); + return false; + } + return true; +} + +int my_lua_read_reg(lua_State *state) +{ + int n = lua_gettop(state); + if(n != 0) + luaL_error(state, "read() takes no argument"); + soc_addr_t addr = lua_tounsigned(state, lua_upvalueindex(1)); + lua_pushunsigned(state, hw_read32(state, addr)); + return 1; +} + +int my_lua_write_reg(lua_State *state) +{ + int n = lua_gettop(state); + if(n != 1) + luaL_error(state, "write() takes one argument"); + soc_word_t val = luaL_checkunsigned(state, 1); + soc_addr_t addr = lua_tounsigned(state, lua_upvalueindex(1)); + hw_write32(state, addr, val); + return 0; +} + +int my_lua_read_field(lua_State *state) +{ + int n = lua_gettop(state); + if(n != 0) + luaL_error(state, "read() takes no argument"); + soc_addr_t addr = lua_tounsigned(state, lua_upvalueindex(1)); + soc_word_t shift = lua_tounsigned(state, lua_upvalueindex(2)); + soc_word_t mask = lua_tounsigned(state, lua_upvalueindex(3)); + lua_pushunsigned(state, (hw_read32(state, addr) >> shift) & mask); + return 1; +} + +int my_lua_write_field(lua_State *state) +{ + int n = lua_gettop(state); + if(n != 0 && n!= 1) + luaL_error(state, "write() takes one or no argument"); + soc_addr_t addr = lua_tounsigned(state, lua_upvalueindex(1)); + soc_word_t shift = lua_tounsigned(state, lua_upvalueindex(2)); + soc_word_t mask = lua_tounsigned(state, lua_upvalueindex(3)); + bool is_sct = lua_toboolean(state, lua_upvalueindex(5)); + + soc_word_t value = mask; + if(n == 1) + { + if(!lua_isnumber(state, 1) && lua_isstring(state, 1)) + { + lua_pushvalue(state, lua_upvalueindex(4)); + lua_pushvalue(state, 1); + lua_gettable(state, -2); + if(lua_isnil(state, -1)) + luaL_error(state, "field has no value %s", lua_tostring(state, 1)); + value = luaL_checkunsigned(state, -1); + lua_pop(state, 2); + } + else + value = luaL_checkunsigned(state, 1); + value &= mask; + } + + if(!is_sct) + value = value << shift | (hw_read32(state, addr) & ~(mask << shift)); + else + value <<= shift; + + hw_write32(state, addr, value); + return 0; +} + +void my_lua_create_field(soc_addr_t addr, const soc_reg_field_t& field, bool sct) +{ + lua_newtable(g_lua); + + lua_pushstring(g_lua, field.name.c_str()); + lua_setfield(g_lua, -2, "name"); + + lua_pushunsigned(g_lua, addr); + lua_setfield(g_lua, -2, "addr"); + + lua_pushboolean(g_lua, sct); + lua_setfield(g_lua, -2, "sct"); + + lua_pushunsigned(g_lua, field.first_bit); + lua_setfield(g_lua, -2, "first_bit"); + + lua_pushunsigned(g_lua, field.last_bit); + lua_setfield(g_lua, -2, "last_bit"); + + lua_pushunsigned(g_lua, field.bitmask()); + lua_setfield(g_lua, -2, "bitmask"); + + soc_word_t local_bitmask = field.bitmask() >> field.first_bit; + lua_pushunsigned(g_lua, local_bitmask); + lua_setfield(g_lua, -2, "local_bitmask"); + + lua_pushunsigned(g_lua, addr); + lua_pushunsigned(g_lua, field.first_bit); + lua_pushunsigned(g_lua, local_bitmask); + lua_pushcclosure(g_lua, my_lua_read_field, 3); + lua_setfield(g_lua, -2, "read"); + + lua_pushunsigned(g_lua, addr); + lua_pushunsigned(g_lua, field.first_bit); + lua_pushunsigned(g_lua, local_bitmask); + lua_pushvalue(g_lua, -4); + lua_pushboolean(g_lua, false); + lua_pushcclosure(g_lua, my_lua_write_field, 5); + lua_setfield(g_lua, -2, "write"); + + if(sct) + { + lua_pushunsigned(g_lua, addr + 4); + lua_pushunsigned(g_lua, field.first_bit); + lua_pushunsigned(g_lua, local_bitmask); + lua_pushvalue(g_lua, -4); + lua_pushboolean(g_lua, true); + lua_pushcclosure(g_lua, my_lua_write_field, 5); + lua_setfield(g_lua, -2, "set"); + + lua_pushunsigned(g_lua, addr + 8); + lua_pushunsigned(g_lua, field.first_bit); + lua_pushunsigned(g_lua, local_bitmask); + lua_pushvalue(g_lua, -4); + lua_pushboolean(g_lua, true); + lua_pushcclosure(g_lua, my_lua_write_field, 5); + lua_setfield(g_lua, -2, "clr"); + + lua_pushunsigned(g_lua, addr + 12); + lua_pushunsigned(g_lua, field.first_bit); + lua_pushunsigned(g_lua, local_bitmask); + lua_pushvalue(g_lua, -4); + lua_pushboolean(g_lua, true); + lua_pushcclosure(g_lua, my_lua_write_field, 5); + lua_setfield(g_lua, -2, "tog"); + } + + for(size_t i = 0; i < field.value.size(); i++) + { + lua_pushunsigned(g_lua, field.value[i].value); + lua_setfield(g_lua, -2, field.value[i].name.c_str()); + } +} + +void my_lua_create_reg(soc_addr_t addr, size_t index, const soc_reg_t& reg) +{ + lua_newtable(g_lua); + + lua_pushstring(g_lua, reg.addr[index].name.c_str()); + lua_setfield(g_lua, -2, "name"); + + lua_pushunsigned(g_lua, addr + reg.addr[index].addr); + lua_setfield(g_lua, -2, "addr"); + + lua_pushboolean(g_lua, !!(reg.flags & REG_HAS_SCT)); + lua_setfield(g_lua, -2, "sct"); + + lua_pushunsigned(g_lua, addr + reg.addr[index].addr); + lua_pushcclosure(g_lua, my_lua_read_reg, 1); + lua_setfield(g_lua, -2, "read"); + + lua_pushunsigned(g_lua, addr + reg.addr[index].addr); + lua_pushcclosure(g_lua, my_lua_write_reg, 1); + lua_setfield(g_lua, -2, "write"); + + if(reg.flags & REG_HAS_SCT) + { + lua_pushunsigned(g_lua, addr + reg.addr[index].addr + 4); + lua_pushcclosure(g_lua, my_lua_write_reg, 1); + lua_setfield(g_lua, -2, "set"); + + lua_pushunsigned(g_lua, addr + reg.addr[index].addr + 8); + lua_pushcclosure(g_lua, my_lua_write_reg, 1); + lua_setfield(g_lua, -2, "clr"); + + lua_pushunsigned(g_lua, addr + reg.addr[index].addr + 12); + lua_pushcclosure(g_lua, my_lua_write_reg, 1); + lua_setfield(g_lua, -2, "tog"); + } + + for(size_t i = 0; i < reg.field.size(); i++) + { + my_lua_create_field(addr + reg.addr[index].addr, reg.field[i], + reg.flags & REG_HAS_SCT); + lua_setfield(g_lua, -2, reg.field[i].name.c_str()); + } +} + +void my_lua_create_dev(size_t index, const soc_dev_t& dev) +{ + lua_newtable(g_lua); + + lua_pushstring(g_lua, dev.addr[index].name.c_str()); + lua_setfield(g_lua, -2, "name"); + + lua_pushunsigned(g_lua, dev.addr[index].addr); + lua_setfield(g_lua, -2, "addr"); + + for(size_t i = 0; i < dev.reg.size(); i++) + { + bool table = dev.reg[i].addr.size() > 1; + if(table) + lua_newtable(g_lua); + else + lua_pushnil(g_lua); + + for(size_t k = 0; k < dev.reg[i].addr.size(); k++) + { + my_lua_create_reg(dev.addr[index].addr, k, dev.reg[i]); + if(table) + { + lua_pushinteger(g_lua, k); + lua_pushvalue(g_lua, -2); + lua_settable(g_lua, -4); + } + lua_setfield(g_lua, -3, dev.reg[i].addr[k].name.c_str()); + } + + if(table) + lua_setfield(g_lua, -2, dev.reg[i].name.c_str()); + else + lua_pop(g_lua, 1); + } +} + +bool my_lua_import_soc(const soc_t& soc) +{ + int oldtop = lua_gettop(g_lua); + + lua_getglobal(g_lua, "hwstub"); + lua_getfield(g_lua, -1, "soc"); + + lua_newtable(g_lua); + + lua_pushstring(g_lua, soc.name.c_str()); + lua_setfield(g_lua, -2, "name"); + + lua_pushstring(g_lua, soc.desc.c_str()); + lua_setfield(g_lua, -2, "desc"); + + for(size_t i = 0; i < soc.dev.size(); i++) + { + bool table = soc.dev[i].addr.size() > 1; + if(table) + lua_newtable(g_lua); + else + lua_pushnil(g_lua); + + for(size_t k = 0; k < soc.dev[i].addr.size(); k++) + { + my_lua_create_dev(k, soc.dev[i]); + if(table) + { + lua_pushinteger(g_lua, k + 1); + lua_pushvalue(g_lua, -2); + lua_settable(g_lua, -4); + } + lua_setfield(g_lua, -3, soc.dev[i].addr[k].name.c_str()); + } + + if(table) + lua_setfield(g_lua, -2, soc.dev[i].name.c_str()); + else + lua_pop(g_lua, 1); + } + + lua_setfield(g_lua, -2, soc.name.c_str()); + + lua_pop(g_lua, 2); + + if(lua_gettop(g_lua) != oldtop) + { + printf("internal error: unbalanced my_lua_import_soc\n"); + return false; + } + return true; +} + +bool my_lua_import_soc(const std::vector< soc_t >& socs) +{ + for(size_t i = 0; i < socs.size(); i++) + { + if(!g_quiet) + printf("importing %s...\n", socs[i].name.c_str()); + if(!my_lua_import_soc(socs[i])) + return false; + } + return true; +} + +/** + * glue + */ + +void usage(void) +{ + printf("hwstub_tool, compiled with hwstub %d.%d.%d\n", + HWSTUB_VERSION_MAJOR, HWSTUB_VERSION_MINOR, HWSTUB_VERSION_REV); + printf("\n"); + printf("usage: hwstub_tool [options] \n"); + printf("options:\n"); + printf(" --help/-?\tDisplay this help\n"); + printf(" --quiet/-q\tQuiet non-command messages\n"); + printf(" -i \tSet lua init file (default is init.lua)\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + const char *lua_init = "init.lua"; + // parse command line + 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, "?qi:", long_options, NULL); + if(c == -1) + break; + switch(c) + { + case -1: + break; + case 'q': + g_quiet = true; + break; + case '?': + usage(); + break; + case 'i': + lua_init = optarg; + break; + default: + abort(); + } + } + + // load register descriptions + std::vector< soc_t > socs; + for(int i = optind; i < argc; i++) + if(!soc_desc_parse_xml(argv[i], socs)) + { + printf("Cannot load description '%s'\n", argv[i]); + return 2; + } + + // create usb context + libusb_context *ctx; + libusb_init(&ctx); + libusb_set_debug(ctx, 3); + + // look for device + if(!g_quiet) + printf("Looking for device %#04x:%#04x...\n", HWSTUB_USB_VID, HWSTUB_USB_PID); + + libusb_device_handle *handle = libusb_open_device_with_vid_pid(ctx, + HWSTUB_USB_VID, HWSTUB_USB_PID); + if(handle == NULL) + { + printf("No device found\n"); + return 1; + } + + // admin stuff + 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)); + } + g_hwdev.handle = handle; + if(hwstub_probe(&g_hwdev)) + { + printf("Cannot probe device!\n"); + return 1; + } + + // get hwstub information + int ret = hwstub_get_info(&g_hwdev, HWSTUB_INFO_VERSION, &g_hwdev_ver, sizeof(g_hwdev_ver)); + if(ret != sizeof(g_hwdev_ver)) + { + printf("Cannot get version!\n"); + goto Lerr; + } + if(g_hwdev_ver.major != HWSTUB_VERSION_MAJOR || g_hwdev_ver.minor < HWSTUB_VERSION_MINOR) + { + printf("Warning: this tool is possibly incompatible with your device:\n"); + printf("Device version: %d.%d.%d\n", g_hwdev_ver.major, g_hwdev_ver.minor, g_hwdev_ver.revision); + printf("Host version: %d.%d.%d\n", HWSTUB_VERSION_MAJOR, HWSTUB_VERSION_MINOR, HWSTUB_VERSION_REV); + } + + // get memory layout information + ret = hwstub_get_info(&g_hwdev, HWSTUB_INFO_LAYOUT, &g_hwdev_layout, sizeof(g_hwdev_layout)); + if(ret != sizeof(g_hwdev_layout)) + { + printf("Cannot get layout: %d\n", ret); + goto Lerr; + } + + // get features + ret = hwstub_get_info(&g_hwdev, HWSTUB_INFO_FEATURES, &g_hwdev_features, sizeof(g_hwdev_features)); + if(ret != sizeof(g_hwdev_features)) + { + printf("Cannot get features: %d\n", ret); + goto Lerr; + } + + // get STMP specific information + ret = hwstub_get_info(&g_hwdev, HWSTUB_INFO_STMP, &g_hwdev_stmp, sizeof(g_hwdev_stmp)); + if(ret != sizeof(g_hwdev_stmp)) + { + printf("Cannot get stmp: %d\n", ret); + goto Lerr; + } + + // dump ROM + if(!g_quiet) + { + void *rom = malloc(64 * 1024); + ret = hwstub_rw_mem(&g_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); + } + + // test DCP +#if 0 + 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(!hwstub_aes_otp(&g_hwdev, &dcp_test, sizeof(dcp_test), HWSTUB_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"); + } +#endif + + /** Init lua */ + + // create lua state + g_lua = luaL_newstate(); + if(g_lua == NULL) + { + printf("Cannot create lua state\n"); + return 1; + } + // import hwstub + if(!my_lua_import_hwstub()) + printf("Cannot import hwstub description into Lua context\n"); + // open all standard libraires + luaL_openlibs(g_lua); + // import socs + if(!my_lua_import_soc(socs)) + printf("Cannot import SoC descriptions into Lua context\n"); + + if(luaL_dofile(g_lua, lua_init)) + printf("error in init: %s\n", lua_tostring(g_lua, -1)); + lua_pop(g_lua, lua_gettop(g_lua)); + + /** start interactive mode */ + if(!g_quiet) + printf("Starting interactive lua session. Type 'help()' to get some help\n"); + + // use readline to provide some history and completion + rl_bind_key('\t', rl_complete); + while(1) + { + char *input = readline("> "); + if(!input) + break; + add_history(input); + // evaluate string + if(luaL_dostring(g_lua, input)) + printf("error: %s\n", lua_tostring(g_lua, -1)); + // pop everything to start from a clean stack + lua_pop(g_lua, lua_gettop(g_lua)); + free(input); + } + + Lerr: + // display log if handled + if(g_hwdev_features.feature_mask & HWSTUB_FEATURE_LOG) + { + if(!g_quiet) + printf("Device log:\n"); + print_log(&g_hwdev); + } + hwstub_release(&g_hwdev); + return 1; +} diff --git a/utils/hwstub/tools/init.lua b/utils/hwstub/tools/init.lua new file mode 100644 index 0000000000..142c77e20a --- /dev/null +++ b/utils/hwstub/tools/init.lua @@ -0,0 +1,104 @@ +-- init code for hwstub_tools + +-- +-- HELP +-- +HELP = hwstub.help + +function HELP:create_topic(name) + self[name] = { create_topic = HELP.create_topic, add = HELP.add, get_topic = HELP.get_topic } + return self[name] +end + +function HELP:get_topic(name) + return self[name] +end + +function HELP:add(text) + table.insert(self, text) +end + +do + local h = HELP:create_topic("hwstub") + h:add("This tool uses a number of well-defined namespaces (tables) to organise its features.") + h:add("The hwstub table contains a number of information and functions related to the tool itself.") + h:add("Of particular interest are") + h:add("* hwstub.host which holds host specific information.") + h:add("* hwstub.dev which holds device specific information. See DEV") + h:add("* hwstub.help (aka HELP) which holds the help. See HELP."); + h:add("* hwstub.soc which holds soc specific information. See HW"); + + h = HELP:create_topic("HELP"); + h:add("This variable redirects to hwstub.help and provides access to the help system."); + h:add("You can enhance the help using the following methods on any topic (including HELP itself)."); + h:add("* t:create_topic(s) to create a new subtopic named s under topic t"); + h:add("* t:add(s) to add a help line to topic t"); + h:add("* t:get_topic(s) to get the subtopic s under topic t"); + + h = HELP:create_topic("DEV"); + h:add("This variable redirects to hwstub.dev and provides direct access to the device."); + h:add("It contains some information about the device and the following methods."); + h:add("* read8/16/32(a) reads a 8/16/32-bit integer at address a"); + h:add("* write8/16/32(a, v) writes the 8/16/32-bit integer v at address a"); + + h = HELP:create_topic("HW"); + h:add("This variable redirects to the current soc under hwstub.soc and should be changed by calling hwstub:soc:select only."); + h:add("The complete register tree can be found under HW in a well organise fashion."); + h:add("* HW.dev points to device dev"); + h:add("* HW.dev[i] points to device devi if there are several copies of the device at different addresses."); + h:add("* HW.dev.reg points to the register reg under dev"); + h:add("* HW.dev.reg[i] points to the register regi if there are several copies."); + h:add("* HW.dev.reg.f points to the field f under register reg."); + h:add("* HW.dev.reg.f.v gives the value of named value v of field f."); + h:add("* All registers can be read using HW.dev.reg.read() and written using HW.dev.reg.write(v)."); + h:add("* Register with a SCT variant also implement HW.dev.reg.set/clr/tog(v)."); + h:add("* All register field can be read using HW.dev.reg.f.read() and written using HW.dev.reg.f.write(v)."); + h:add("* Field writes can either give a integer or a named value to write(v)."); + h:add("* Register with a SCT variant also implement HW.dev.reg.f.set/clr/tog(v) with the same properties."); + h:add("* All devices, registers and fields also have descriptions available such as addresses."); +end + +-- +-- INFO +-- + +if not hwstub.options.quiet then + print("information") + print(" hwstub") + print(" version: " .. string.format("%d.%d.%d", hwstub.host.version.major, + hwstub.host.version.minor, hwstub.host.version.revision)) + print(" device") + print(" version: " .. string.format("%d.%d.%d", hwstub.dev.version.major, + hwstub.dev.version.minor, hwstub.dev.version.revision)) + print(" layout") + print(" on-chip ram") + print(" code: " .. string.format("%#x bytes @ %#x", + hwstub.dev.layout.ocram.code.size, hwstub.dev.layout.ocram.code.start)) + print(" stack: " .. string.format("%#x bytes @ %#x", + hwstub.dev.layout.ocram.stack.size, hwstub.dev.layout.ocram.stack.start)) + print(" buffer: " .. string.format("%#x bytes @ %#x", + hwstub.dev.layout.ocram.buffer.size, hwstub.dev.layout.ocram.buffer.start)) + print(" features"); + print(" log: " .. tostring(hwstub.dev.features.log)) + print(" mem: " .. tostring(hwstub.dev.features.mem)) + print(" call: " .. tostring(hwstub.dev.features.call)) + print(" jump: " .. tostring(hwstub.dev.features.jump)) + print(" aes_otp: " .. tostring(hwstub.dev.features.aes_otp)) +end + +-- +-- SOC +-- +function hwstub.soc:select(soc) + if self[soc] == nil then return false end + print("Selecting soc " .. soc .. ". Redirecting HW to hwstub.soc." .. soc) + HW = self[soc] + return true +end + +-- +-- DEV +-- +DEV = hwstub.dev + +require "lua/load" -- cgit v1.2.3