diff --git a/utils/imxtools/sbtools/Makefile b/utils/imxtools/sbtools/Makefile index bc7180d866..16ae0878b0 100644 --- a/utils/imxtools/sbtools/Makefile +++ b/utils/imxtools/sbtools/Makefile @@ -3,7 +3,7 @@ CC=gcc LD=gcc CFLAGS=-g -std=c99 -W -Wall `pkg-config --cflags libusb-1.0` $(DEFINES) LDFLAGS=`pkg-config --libs libusb-1.0` -BINS=elftosb sbtoelf sbloader rsrctool +BINS=elftosb sbtoelf sbloader rsrctool elftosb1 all: $(BINS) @@ -16,6 +16,9 @@ sbtoelf: sbtoelf.o crc.o crypto.o aes128.o sha1.o xorcrypt.o elf.o misc.o sb.o s elftosb: elftosb.o crc.o crypto.o aes128.o sha1.o elf.o dbparser.o misc.o sb.o $(LD) -o $@ $^ $(LDFLAGS) +elftosb1: elftosb1.o xorcrypt.o elf.o misc.o sb1.o + $(LD) -o $@ $^ $(LDFLAGS) + sbloader: sbloader.o $(LD) -o $@ $^ $(LDFLAGS) diff --git a/utils/imxtools/sbtools/elftosb1.c b/utils/imxtools/sbtools/elftosb1.c new file mode 100644 index 0000000000..a654b6b34a --- /dev/null +++ b/utils/imxtools/sbtools/elftosb1.c @@ -0,0 +1,543 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2012 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. + * + ****************************************************************************/ + +#define _ISOC99_SOURCE +#define _POSIX_C_SOURCE 200809L /* for strdup */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crypto.h" +#include "elf.h" +#include "sb1.h" +#include "misc.h" + +#define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round)) + +/** + * Globals + */ + +char *g_output_file; +bool g_critical; +bool g_final; +bool g_strict = true; +uint32_t g_jump_arg; + +/** + * Helpers + */ + +typedef char* (*get_next_arg_t)(void *user); + +struct cmd_line_next_arg_user_t +{ + int argc; + char **argv; +}; + +static char *cmd_line_next_arg(void *user) +{ + struct cmd_line_next_arg_user_t *uu = user; + if(uu->argc == 0) + return NULL; + uu->argc--; + uu->argv++; + return *(uu->argv - 1); +} + +static bool elf_read(void *user, uint32_t addr, void *buf, size_t count) +{ + if(fseek((FILE *)user, addr, SEEK_SET) == -1) + return false; + return fread(buf, 1, count, (FILE *)user) == count; +} + +static void elf_printf(void *user, bool error, const char *fmt, ...) +{ + if(!g_debug && !error) + return; + (void) user; + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + +static int sb1_add_inst(struct sb1_file_t *sb, struct sb1_inst_t *insts, int nr_insts) +{ + sb->insts = augment_array(sb->insts, sizeof(struct sb1_inst_t), sb->nr_insts, + insts, nr_insts); + sb->nr_insts += nr_insts; + return 0; +} + +static int sb1_add_load(struct sb1_file_t *sb, void *data, int size, uint32_t addr) +{ + while(size > 0) + { + int len = MIN(size, SB1_CMD_MAX_LOAD_SIZE); + struct sb1_inst_t inst; + memset(&inst, 0, sizeof(inst)); + inst.cmd = SB1_INST_LOAD; + inst.size = len; + inst.addr = addr; + inst.critical = g_critical; + inst.data = xmalloc(len); + memcpy(inst.data, data, len); + if(g_debug) + printf("Add instruction: load %#x bytes at %#x\n", len, addr); + int ret = sb1_add_inst(sb, &inst, 1); + if(ret < 0) + return ret; + data += len; + size -= len; + addr += len; + } + return 0; +} + +static int sb1_add_switch(struct sb1_file_t *sb, uint32_t driver) +{ + struct sb1_inst_t inst; + memset(&inst, 0, sizeof(inst)); + inst.cmd = SB1_INST_MODE; + inst.critical = g_critical; + inst.mode = driver; + if(g_debug) + printf("Add instruction: switch driver to %#x\n", driver); + g_final = true; + return sb1_add_inst(sb, &inst, 1); +} + +static int sb1_add_sdram(struct sb1_file_t *sb, uint32_t cs, uint32_t size) +{ + struct sb1_inst_t inst; + memset(&inst, 0, sizeof(inst)); + inst.cmd = SB1_INST_SDRAM; + inst.critical = g_critical; + inst.sdram.chip_select = cs; + inst.sdram.size_index = sb1_sdram_index_by_size(size); + if(sb1_sdram_index_by_size(size) < 0) + bug("Unknown SDRAM size: %d MB\n", size); + if(g_debug) + printf("Add instruction: init SDRAM (chip select=%d, size=%d MB)\n", cs, size); + return sb1_add_inst(sb, &inst, 1); +} + +static int sb1_add_call(struct sb1_file_t *sb, uint32_t addr, uint32_t arg) +{ + struct sb1_inst_t inst; + memset(&inst, 0, sizeof(inst)); + inst.cmd = SB1_INST_CALL; + inst.critical = g_critical; + inst.addr = addr; + inst.argument = arg; + if(g_debug) + printf("Add instruction: call %#x with argument %#x\n", addr, arg); + return sb1_add_inst(sb, &inst, 1); +} + +static int sb1_add_jump(struct sb1_file_t *sb, uint32_t addr, uint32_t arg) +{ + struct sb1_inst_t inst; + memset(&inst, 0, sizeof(inst)); + inst.cmd = SB1_INST_JUMP; + inst.critical = g_critical; + inst.addr = addr; + inst.argument = arg; + if(g_debug) + printf("Add instruction: jump %#x with argument %#x\n", addr, arg); + g_final = true; + return sb1_add_inst(sb, &inst, 1); +} + +static int sb1_add_fill(struct sb1_file_t *sb, uint32_t pattern, uint32_t size, uint32_t addr) +{ + while(size > 0) + { + int len = MIN(size, SB1_CMD_MAX_FILL_SIZE); + struct sb1_inst_t inst; + memset(&inst, 0, sizeof(inst)); + inst.cmd = SB1_INST_FILL; + inst.critical = g_critical; + inst.size = len; + inst.addr = addr; + inst.pattern = pattern; + inst.datatype = SB1_DATATYPE_UINT32; + if(g_debug) + printf("Add instruction: fill %#x bytes with pattern %#x at address %#x\n", + size, pattern, addr); + int ret = sb1_add_inst(sb, &inst, 1); + if(ret < 0) + return ret; + size -= len; + addr += len; + } + + return 0; +} + +/** + * SB file modification + */ + +static void generate_default_sb_version(struct sb1_version_t *ver) +{ + ver->major = ver->minor = ver->revision = 0x9999; +} + +static struct sb1_file_t *create_sb1_file(void) +{ + struct sb1_file_t *sb = xmalloc(sizeof(struct sb1_file_t)); + memset(sb, 0, sizeof(struct sb1_file_t)); + + /* default versions and key, apply_args() will overwrite if specified */ + generate_default_sb_version(&sb->product_ver); + generate_default_sb_version(&sb->component_ver); + sb1_get_default_key(&sb->key); + + return sb; +} + +static void *load_file(const char *filename, int *size) +{ + FILE *fd = fopen(filename, "rb"); + if(fd == NULL) + bug("cannot open '%s' for reading\n", filename); + if(g_debug) + printf("Loading binary file '%s'...\n", filename); + fseek(fd, 0, SEEK_END); + *size = ftell(fd); + fseek(fd, 0, SEEK_SET); + void *data = xmalloc(*size); + fread(data, 1, *size, fd); + fclose(fd); + return data; +} + +/** + * Command line parsing + */ + +#define MAX_NR_ARGS 2 + +#define ARG_STR 0 +#define ARG_UINT 1 + +#define CMD_FN(name) \ + int name(struct sb1_file_t *sb, union cmd_arg_t args[MAX_NR_ARGS]) + +union cmd_arg_t +{ + char *str; + unsigned long uint; +}; + +typedef int (*process_arg_t)(struct sb1_file_t *sb, union cmd_arg_t args[MAX_NR_ARGS]); + +struct cmd_entry_t +{ + const char *name; + int nr_args; + int arg_type[MAX_NR_ARGS]; + process_arg_t fn; +}; + +/* Callbacks */ + +static void usage(void); + +CMD_FN(cmd_help) +{ + (void) args; + (void) sb; + usage(); + return 0; +} + +CMD_FN(cmd_debug) +{ + (void) args; + (void) sb; + g_debug = true; + return 0; +} + +CMD_FN(cmd_drive_tag) +{ + sb->drive_tag = args[0].uint; + return 0; +} + +CMD_FN(cmd_load_binary) +{ + int size; + void *data = load_file(args[0].str, &size); + int ret = sb1_add_load(sb, data, size, args[1].uint); + free(data); + return ret; +} + +CMD_FN(cmd_output) +{ + (void) sb; + g_output_file = strdup(args[0].str); + return 0; +} + +CMD_FN(cmd_switch) +{ + return sb1_add_switch(sb, args[0].uint); +} + +CMD_FN(cmd_sdram) +{ + return sb1_add_sdram(sb, args[0].uint, args[1].uint); +} + +CMD_FN(cmd_critical) +{ + (void) sb; + (void) args; + g_critical = true; + return 0; +} + +CMD_FN(cmd_clear_critical) +{ + (void) sb; + (void) args; + g_critical = false; + return 0; +} + +CMD_FN(cmd_strict) +{ + (void) sb; + (void) args; + g_strict = true; + return 0; +} + +CMD_FN(cmd_clear_strict) +{ + (void) sb; + (void) args; + g_strict = false; + return 0; +} + +CMD_FN(cmd_call) +{ + /* FIXME: the proprietary sbtoelf always sets argument to 0 ?! */ + return sb1_add_call(sb, args[0].uint, g_jump_arg); +} + +CMD_FN(cmd_jumparg) +{ + (void) sb; + g_jump_arg = args[0].uint; + return 0; +} + +static int load_elf(struct sb1_file_t *sb, const char *filename, int act) +{ + struct elf_params_t elf; + FILE *fd = fopen(filename, "rb"); + if(fd == NULL) + bug("cannot open '%s'\n", filename); + if(g_debug) + printf("Loading elf file '%s'...\n", filename); + elf_init(&elf); + bool loaded = elf_read_file(&elf, elf_read, elf_printf, fd); + fclose(fd); + if(!loaded) + bug("error loading elf file '%s'\n", filename); + //elf_translate_addresses(&elf); + elf_sort_by_address(&elf); + + struct elf_section_t *esec = elf.first_section; + while(esec) + { + if(esec->type == EST_LOAD) + sb1_add_load(sb, esec->section, esec->size, esec->addr); + else if(esec->type == EST_FILL) + sb1_add_fill(sb, esec->pattern, esec->size, esec->addr); + esec = esec->next; + } + + int ret = 0; + if(act == SB1_INST_JUMP || act == SB1_INST_CALL) + { + if(!elf.has_start_addr) + bug("Cannot jump/call: '%s' has no start address!\n", filename); + if(act == SB1_INST_JUMP) + ret = sb1_add_jump(sb, elf.start_addr, g_jump_arg); + else + ret = sb1_add_call(sb, elf.start_addr, g_jump_arg); + } + + elf_release(&elf); + + return ret; +} + +CMD_FN(cmd_load) +{ + return load_elf(sb, args[0].str, SB1_INST_LOAD); +} + +CMD_FN(cmd_loadjump) +{ + return load_elf(sb, args[0].str, SB1_INST_JUMP); +} + +CMD_FN(cmd_loadjumpreturn) +{ + return load_elf(sb, args[0].str, SB1_INST_CALL); +} + +#define CMD(name,fn,nr_args,...) {name,nr_args,{__VA_ARGS__},fn}, +struct cmd_entry_t g_cmds[] = +{ + CMD("-d", cmd_debug, 0) + CMD("-debugon", cmd_debug, 0) + CMD("-h", cmd_help, 0) + CMD("-?", cmd_help, 0) + CMD("-load-binary", cmd_load_binary, 2, ARG_STR, ARG_UINT) + CMD("-drive-tag", cmd_drive_tag, 1, ARG_UINT) + CMD("-o", cmd_output, 1, ARG_STR) + CMD("-w", cmd_switch, 1, ARG_UINT) + CMD("-switchdriver", cmd_switch, 1, ARG_UINT) + CMD("-sdram", cmd_sdram, 2, ARG_UINT, ARG_UINT) + CMD("-c", cmd_critical, 0) + CMD("-critical", cmd_critical, 0) + CMD("-C", cmd_clear_critical, 0) + CMD("-noncritical", cmd_clear_critical, 0) + CMD("-n", cmd_strict, 0) + CMD("-strict", cmd_strict, 0) + CMD("-N", cmd_clear_strict, 0) + CMD("-nonstrict", cmd_clear_strict, 0) + CMD("-call", cmd_call, 1, ARG_UINT) + CMD("-jumparg", cmd_jumparg, 1, ARG_UINT) + CMD("-f", cmd_load, 1, ARG_STR) + CMD("-load", cmd_load, 1, ARG_STR) + CMD("-r", cmd_loadjumpreturn, 1, ARG_STR) + CMD("-loadjumpreturn", cmd_loadjumpreturn, 1, ARG_STR) + CMD("-j", cmd_loadjump, 1, ARG_STR) + CMD("-loadjump", cmd_loadjump, 1, ARG_STR) +}; +#undef CMD + +#define NR_CMDS (int)(sizeof(g_cmds) / sizeof(g_cmds[0])) + +static int apply_args(struct sb1_file_t *sb, get_next_arg_t next, void *user) +{ + while(true) + { + /* next command ? */ + char *cmd = next(user); + if(cmd == NULL) + break; + /* switch */ + int i = 0; + while(i < NR_CMDS && strcmp(cmd, g_cmds[i].name) != 0) + i++; + if(i == NR_CMDS) + bug("Unknown option '%s'\n", cmd); + union cmd_arg_t args[MAX_NR_ARGS]; + for(int j = 0; j < g_cmds[i].nr_args; j++) + { + args[j].str = next(user); + if(args[j].str == NULL) + bug("Option '%s' requires %d arguments, only %d given\n", cmd, g_cmds[i].nr_args, j); + if(g_cmds[i].arg_type[j] == ARG_UINT) + { + char *end; + args[j].uint = strtoul(args[j].str, &end, 0); + if(*end) + bug("Option '%s' expects an integer as argument %d\n", cmd, j + 1); + } + } + int ret = g_cmds[i].fn(sb, args); + if(ret < 0) + return ret; + } + return 0; +} + +static void usage(void) +{ + printf("Usage: elftosb1 [options]\n"); + printf("Options:\n"); + printf(" -h/-?/-help\t\t\tDisplay this message\n"); + printf(" -o \t\t\tSet output file\n"); + printf(" -d/-debugon\t\t\tEnable debug output\n"); + printf(" -k \t\t\tSet key file\n"); + printf(" -load-binary \tLoad a binary file at a specified address\n"); + printf(" -drive-tag \t\tSpecify drive tag\n"); + printf(" -w/-switchdriver \tSwitch driver\n"); + printf(" -sdram \tInit SDRAM\n"); + printf(" -f/-load \t\tLoad a ELF file\n"); + + exit(1); +} + +int main(int argc, char **argv) +{ + if(argc <= 1) + usage(); + + struct sb1_file_t *sb = create_sb1_file(); + + struct cmd_line_next_arg_user_t u; + u.argc = argc - 1; + u.argv = argv + 1; + int ret = apply_args(sb, &cmd_line_next_arg, &u); + if(ret < 0) + { + sb1_free(sb); + return ret; + } + + if(!g_output_file) + bug("You must specify an output file\n"); + if(!g_final) + { + if(g_strict) + bug("There is no final command in this command stream!\n"); + else + printf("Warning: there is no final command in this command stream!\n"); + } + + enum sb1_error_t err = sb1_write_file(sb, g_output_file); + if(err != SB1_SUCCESS) + printf("Error: %d\n", err); + + return ret; +} +