Commit 308c9756 authored by Marek Vasut's avatar Marek Vasut

mxssb: Parse the SB commands from text input

Implement simple parser that reads command from text input. This
allows us to get rid of the built-in tables and replace them with
built-in blocks of text. In the long run, they are easily replacable
by file input.

Note that in order to preserve all functionality, the table containing
instructions need to be hand-patched, therefore there is some copying
happening in set_cpu(). Moreover, set_cpu() is now called a little
later to ensure the filenames of spl and u-boot are already read.
Signed-off-by: Marek Vasut's avatarMarek Vasut <marex@denx.de>
parent 9504b2fc
......@@ -19,6 +19,7 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <asm/byteorder.h>
......@@ -73,6 +74,11 @@ struct sb_image_ctx {
EVP_MD_CTX md_ctx;
int section_count;
uint8_t digest[32];
char **sb_input;
unsigned int sb_insts;
unsigned int sb_insts_pos;
char *spl_filename;
char *uboot_filename;
struct sb_key_dictionary_key sb_dict_key;
......@@ -96,60 +102,34 @@ static struct sb_ivt_header sb_uboot_ivt = {
.self = SB_IVT_LOAD_ADDR,
};
static struct sb_source_entry mx23_boot_list[] = {
{
.tag = ROM_TAG_CMD,
.flags = ROM_TAG_CMD_FLAG_ROM_LAST_TAG,
}, {
.tag = ROM_LOAD_CMD,
.address = SB_SPL_LOAD_ADDR,
/* .payload is loaded at run time. */
/* .length is loaded at run time. */
}, {
.tag = ROM_CALL_CMD,
.address = SB_SPL_ENTRY_ADDR,
}, {
.tag = ROM_LOAD_CMD,
.address = SB_UBOOT_LOAD_ADDR,
/* .payload is loaded at run time. */
/* .length is loaded at run time. */
}, {
.tag = ROM_CALL_CMD,
.address = SB_UBOOT_LOAD_ADDR,
}
/*
* Instruction semantics:
* NOOP
* TAG [LAST]
* LOAD address file
* LOAD IVT address IVT_entry_point
* CALL [HAB] address [r0_arg]
*/
static const char *sb_input_mx23[] = {
"CPU mx23",
"SECTION 0x0",
" TAG LAST",
" LOAD 0x0 "/* spl/u-boot-spl.bin appended below */,
" CALL 0x14 0x0",
" LOAD 0x0 "/* u-boot.bin appended below */,
" CALL 0x40000100 0x0",
};
static struct sb_source_entry mx28_boot_list[] = {
{
.tag = ROM_TAG_CMD,
.flags = ROM_TAG_CMD_FLAG_ROM_LAST_TAG,
}, {
.tag = ROM_LOAD_CMD,
.address = SB_SPL_LOAD_ADDR,
/* .payload is loaded at run time. */
/* .length is loaded at run time. */
}, {
.tag = ROM_LOAD_CMD,
.address = SB_IVT_LOAD_ADDR,
.filename = "ivt:spl",
}, {
.tag = ROM_CALL_CMD,
.flags = ROM_CALL_CMD_FLAG_HAB,
.address = SB_IVT_LOAD_ADDR,
}, {
.tag = ROM_LOAD_CMD,
.address = SB_UBOOT_LOAD_ADDR,
/* .payload is loaded at run time. */
/* .length is loaded at run time. */
}, {
.tag = ROM_LOAD_CMD,
.address = SB_IVT_LOAD_ADDR,
.filename = "ivt:u-boot",
}, {
.tag = ROM_CALL_CMD,
.flags = ROM_CALL_CMD_FLAG_HAB,
.address = SB_IVT_LOAD_ADDR,
}
static const char *sb_input_mx28[] = {
"CPU mx28",
"SECTION 0x0",
" TAG LAST",
" LOAD 0x0 "/* spl/u-boot-spl.bin appended below */,
" LOAD IVT 0x8000 0x14",
" CALL HAB 0x8000 0x0",
" LOAD 0x40000100 "/* u-boot.bin appended below */,
" LOAD IVT 0x8000 0x40000100",
" CALL HAB 0x8000 0x0",
};
/* Blank image key. */
......@@ -239,45 +219,6 @@ static uint32_t crc32(uint8_t *data, int len)
/*
* Code
*/
static struct sb_source_entry *sb_get_boot_list(const int cpu)
{
switch (cpu) {
case CPU_MX23:
return mx23_boot_list;
case CPU_MX28:
return mx28_boot_list;
}
return 0;
}
static unsigned int sb_get_boot_list_size(const int cpu)
{
switch (cpu) {
case CPU_MX23:
return ARRAY_SIZE(mx23_boot_list);
case CPU_MX28:
return ARRAY_SIZE(mx28_boot_list);
}
return 0;
}
static int sb_get_boot_list_payload_slot(const int cpu, const int spl)
{
if (spl)
return 1;
switch (cpu) {
case CPU_MX23:
return 3;
case CPU_MX28:
return 4;
}
return 0;
}
static time_t sb_get_timestamp(void)
{
struct tm time_2000 = {
......@@ -485,18 +426,18 @@ err:
return -EINVAL;
}
static int sb_load_file(struct sb_cmd_ctx *cctx, struct sb_source_entry *src)
static int sb_load_file(struct sb_cmd_ctx *cctx, char *filename)
{
off_t real_size, roundup_size;
uint8_t *data;
int fd, ret;
if (!src->filename) {
if (!filename) {
fprintf(stderr, "ERR: Missing filename!\n");
return -EINVAL;
}
fd = open(src->filename, O_RDONLY);
fd = open(filename, O_RDONLY);
if (fd < 0)
goto err_open;
......@@ -526,7 +467,7 @@ err_alloc:
err_file:
close(fd);
err_open:
fprintf(stderr, "ERR: Failed to load file \"%s\"\n", src->filename);
fprintf(stderr, "ERR: Failed to load file \"%s\"\n", filename);
return -EINVAL;
}
......@@ -542,40 +483,116 @@ static uint8_t sb_command_checksum(struct sb_command *inst)
return csum;
}
static int sb_tag_to_command(struct sb_source_entry *src,
struct sb_cmd_ctx *cctx)
static long sb_token_to_long(char *tok)
{
char *endptr;
long id;
if (tok[0] != '0' || tok[1] != 'x') {
fprintf(stderr, "ERR: Invalid hexadecimal number!\n");
return -EINVAL;
}
tok += 2;
id = strtol(tok, &endptr, 16);
if ((errno == ERANGE && (id == LONG_MAX || id == LONG_MIN)) ||
(errno != 0 && id == 0)) {
fprintf(stderr, "ERR: Section number can't be decoded!\n");
return -EINVAL;
}
if (endptr == tok) {
fprintf(stderr, "ERR: Deformed section ID!\n");
return -EINVAL;
}
return id;
}
static int sb_tag_to_command(struct sb_cmd_ctx *cctx, char *line)
{
struct sb_command *cmd = &cctx->payload;
int ret;
ssize_t len;
char *tok;
long dest;
tok = strtok(line, " ");
if (!tok) { /* No command on this line. */
fprintf(stderr, "WARN: No command on this line, ignoring!\n");
return 0;
}
len = strlen(tok);
if ((len != 3) && (len != 4)) {
fprintf(stderr, "WARN: Unknown command!\n");
return -EINVAL;
}
if ((len == 3) &&
(strncmp(tok, "TAG", 3) && strncmp(tok, "NOP", 3))) {
fprintf(stderr, "ERR: Unknown command!\n");
return -EINVAL;
}
if ((len == 4) &&
(strncmp(tok, "LOAD", 4) && strncmp(tok, "FILL", 4) &&
strncmp(tok, "JUMP", 4) && strncmp(tok, "CALL", 4) &&
strncmp(tok, "MODE", 4))) {
fprintf(stderr, "ERR: Unknown command!\n");
return -EINVAL;
}
cmd->header.checksum = 0x5a;
cmd->header.tag = src->tag;
cmd->header.flags = src->flags;
switch (src->tag) {
case ROM_NOP_CMD:
switch (tok[0]) {
case 'N': /* ROM_NOP_CMD */
cmd->header.tag = ROM_NOP_CMD;
break;
case ROM_TAG_CMD:
/* TAG instruction is filled later */
case 'T': /* ROM_TAG_CMD */
cmd->header.tag = ROM_TAG_CMD;
cmd->header.flags = 1; // HACK
/* Rest of TAG instruction is filled later. */
break;
case ROM_LOAD_CMD:
/* Load the file */
if (!strcmp(src->filename, "ivt:spl")) {
case 'L': /* ROM_LOAD_CMD */
tok = strtok(NULL, " ");
if (!tok)
return -EINVAL;
/* Check for "IVT" flag. */
if ((strlen(tok) == 3) && !strncmp(tok, "IVT", 3)) {
cctx->ivt = 1;
cctx->length = sizeof(sb_spl_ivt);
cctx->data = malloc(cctx->length);
if (!cctx->data)
return -ENOMEM;
memcpy(cctx->data, &sb_spl_ivt, cctx->length);
} else if (!strcmp(src->filename, "ivt:u-boot")) {
cctx->ivt = 1;
cctx->length = sizeof(sb_uboot_ivt);
cctx->data = malloc(cctx->length);
if (!cctx->data)
tok = strtok(NULL, " ");
if (!tok)
return -EINVAL;
}
/* Read load destination address. */
dest = sb_token_to_long(tok);
if (dest < 0)
return -EINVAL;
/* Read filename or IVT entrypoint. */
tok = strtok(NULL, " ");
if (!tok)
return -EINVAL;
if (cctx->ivt) {
struct sb_ivt_header *ivt;
long ivtep = sb_token_to_long(tok);
if (ivtep < 0)
return -EINVAL;
ivt = calloc(1, sizeof(*ivt));
if (!ivt)
return -ENOMEM;
memcpy(cctx->data, &sb_uboot_ivt, cctx->length);
ivt->header = SB_HAB_IVT_HEADER(sizeof(*ivt)),
ivt->entry = ivtep,
ivt->self = dest,
cctx->data = (uint8_t *)ivt;
cctx->length = sizeof(*ivt);
} else {
ret = sb_load_file(cctx, src);
ret = sb_load_file(cctx, tok);
if (ret)
return ret;
}
......@@ -584,21 +601,48 @@ static int sb_tag_to_command(struct sb_source_entry *src,
fprintf(stderr, "WARN: Unaligned payload!\n");
/* Fill LOAD instruction. */
cmd->load.address = src->address;
cmd->header.tag = ROM_LOAD_CMD;
cmd->load.address = dest;
cmd->load.count = cctx->length;
cmd->load.crc32 = crc32(cctx->data, cctx->length);
break;
case ROM_FILL_CMD: /* UNSUPPORTED */
case 'F': /* ROM_FILL_CMD ; UNSUPPORTED */
cmd->header.tag = ROM_FILL_CMD;
goto unsupp;
case ROM_JUMP_CMD: /* UNSUPPORTED */
case 'J': /* ROM_JUMP_CMD ; UNSUPPORTED */
cmd->header.tag = ROM_JUMP_CMD;
goto unsupp;
case ROM_CALL_CMD:
cmd->call.address = src->address;
/* FIXME ... make this configurable */
cmd->call.argument = 0x00000000;
case 'C': /* ROM_CALL_CMD */
tok = strtok(NULL, " ");
if (!tok)
return -EINVAL;
/* Check for "HAB" flag. */
if ((strlen(tok) == 3) && !strncmp(tok, "HAB", 3)) {
cmd->header.flags = 1; // HACK
tok = strtok(NULL, " ");
if (!tok)
return -EINVAL;
}
/* Read load destination address. */
dest = sb_token_to_long(tok);
if (dest < 0)
return -EINVAL;
long arg = 0x0;
tok = strtok(NULL, " ");
if (tok) {
arg = sb_token_to_long(tok);
if (arg < 0)
return -EINVAL;
}
cmd->header.tag = ROM_CALL_CMD;
cmd->call.address = dest;
cmd->call.argument = arg;
break;
case ROM_MODE_CMD: /* UNSUPPORTED */
case 'M': /* ROM_MODE_CMD ; UNSUPPORTED */
cmd->header.tag = ROM_MODE_CMD;
goto unsupp;
}
......@@ -611,12 +655,15 @@ unsupp:
static int sb_parse_cmds(struct sb_section_ctx *sctx, struct sb_image_ctx *ictx)
{
struct sb_cmd_ctx *cctx, *prev = NULL, *head = NULL;
struct sb_source_entry *src = sb_get_boot_list(ictx->target_cpu);
unsigned int insts = sb_get_boot_list_size(ictx->target_cpu);
uint32_t len = 0;
int ret;
char *line;
for (; ictx->sb_insts_pos < ictx->sb_insts; ictx->sb_insts_pos++) {
line = strdup(ictx->sb_input[ictx->sb_insts_pos]);
if (!line)
goto err;
while (insts--) {
cctx = calloc(1, sizeof(*cctx));
if (!cctx)
goto err;
......@@ -624,7 +671,7 @@ static int sb_parse_cmds(struct sb_section_ctx *sctx, struct sb_image_ctx *ictx)
if (!head)
head = cctx;
ret = sb_tag_to_command(src, cctx);
ret = sb_tag_to_command(cctx, line);
if (ret)
goto err;
......@@ -636,7 +683,7 @@ static int sb_parse_cmds(struct sb_section_ctx *sctx, struct sb_image_ctx *ictx)
if (prev)
prev->cmd = cctx;
prev = cctx;
src++;
free(line);
}
sctx->cmd = head;
......@@ -644,6 +691,10 @@ static int sb_parse_cmds(struct sb_section_ctx *sctx, struct sb_image_ctx *ictx)
return 0;
err:
if (line)
free(line);
if (cctx)
free(cctx);
while (head) {
cctx = head;
head = head->cmd;
......@@ -660,9 +711,29 @@ static int sb_parse_sections(struct sb_image_ctx *ictx)
struct sb_section_ctx *sctx, *head = NULL, *prev = NULL;
struct sb_sections_header *shdr;
int ret;
int sects = 1;
long id;
char *tok, *line;
while (ictx->sb_insts_pos < ictx->sb_insts) {
line = strdup(ictx->sb_input[ictx->sb_insts_pos]);
if (!line)
goto fail;
while (sects--) {
tok = strtok(line, " ");
if (!tok || (tok[0] != 'S')) { /* SECTION */
fprintf(stderr, "ERR: Not a section, skipping!\n");
ictx->sb_insts_pos++;
free(line);
continue;
}
tok = strtok(NULL, " ");
if (!tok) {
fprintf(stderr, "ERR: Section without number!\n");
goto fail;
}
id = sb_token_to_long(tok);
if (id < 0)
goto fail;
sctx = calloc(1, sizeof(*sctx));
if (!sctx)
......@@ -671,6 +742,12 @@ static int sb_parse_sections(struct sb_image_ctx *ictx)
if (!head)
head = sctx;
/*
* The sb_insts_pos will also be modified in sb_parse_cmds()!
* Preincrement it here so the sb_parse_cmd() starts with the
* first command entry in the section.
*/
ictx->sb_insts_pos++;
ret = sb_parse_cmds(sctx, ictx);
if (ret)
goto fail;
......@@ -679,14 +756,16 @@ static int sb_parse_sections(struct sb_image_ctx *ictx)
/* FIXME -- bootable by default. */
sctx->boot = 1;
/* FIXME -- the section ID should be configurable */
shdr->section_number = ictx->section_count++;
shdr->section_number = id;
shdr->section_size = sctx->size / SB_BLOCK_SIZE;
shdr->section_flags = sctx->boot ? SB_SECTION_FLAG_BOOTABLE : 0;
ictx->section_count++;
if (prev)
prev->sect = sctx;
prev = sctx;
free(line);
}
ictx->sect = head;
......@@ -694,6 +773,10 @@ static int sb_parse_sections(struct sb_image_ctx *ictx)
return 0;
fail:
if (line)
free(line);
if (sctx)
free(sctx);
while (head) {
sctx = head;
c_head = head->cmd;
......@@ -888,9 +971,7 @@ static void sb_free_image(struct sb_image_ctx *ictx)
{
struct sb_section_ctx *sctx = ictx->sect, *s_head;
struct sb_cmd_ctx *cctx, *c_head;
if (!sctx)
return;
unsigned int i;
while (sctx) {
s_head = sctx;
......@@ -907,17 +988,15 @@ static void sb_free_image(struct sb_image_ctx *ictx)
sctx = sctx->sect;
free(s_head);
}
}
static int sb_load_binary(struct sb_image_ctx *ictx,
const char *filename, const unsigned int spl)
{
const int slot = sb_get_boot_list_payload_slot(ictx->target_cpu, spl);
struct sb_source_entry *lst = sb_get_boot_list(ictx->target_cpu);
lst[slot].filename = (char *)filename;
return 0;
/* Free the instruction table. */
if (ictx->sb_input) {
for (i = 0; i < ictx->sb_insts; i++) {
if (ictx->sb_input[i])
free(ictx->sb_input[i]);
}
free(ictx->sb_input);
}
}
static void print_help(const char *pn)
......@@ -934,28 +1013,86 @@ static void print_help(const char *pn)
static int set_cpu(struct sb_image_ctx *ictx, const char *pn, const char *cpu)
{
char **table;
unsigned int i;
size_t flen, llen;
if (!strcmp("mx23", cpu)) {
ictx->target_cpu = CPU_MX23;
return 0;
ictx->sb_insts = sizeof(sb_input_mx23)/sizeof(sb_input_mx23[0]);
ictx->sb_insts_pos = 0;
}
if (!strcmp("mx28", cpu)) {
ictx->target_cpu = CPU_MX28;
return 0;
ictx->sb_insts = sizeof(sb_input_mx28)/sizeof(sb_input_mx28[0]);
ictx->sb_insts_pos = 0;
}
fprintf(stderr, "ERROR: Invalid CPU model selected (\"%s\")!\n\n", cpu);
print_help(pn);
if (ictx->target_cpu == CPU_UNKNOWN) {
fprintf(stderr, "ERROR: Invalid CPU model selected (\"%s\")!\n\n", cpu);
print_help(pn);
return -EINVAL;
}
return -1;
table = calloc(1, ictx->sb_insts * sizeof(*table));
if (!table)
return -ENOMEM;
for (i = 0; i < ictx->sb_insts; i++) {
/* Compat -- patch in the file names */
if (ictx->target_cpu == CPU_MX23) {
if (i == 3) {
llen = strlen(sb_input_mx23[i]);
flen = strlen(ictx->spl_filename);
table[i] = calloc(1, llen + flen + 1);
if (!table[i])
return -ENOMEM;
snprintf(table[i], llen + flen + 1, "%s%s",
sb_input_mx23[i], ictx->spl_filename);
} else if (i == 5) {
llen = strlen(sb_input_mx23[i]);
flen = strlen(ictx->uboot_filename);
table[i] = calloc(1, llen + flen + 1);
if (!table[i])
return -ENOMEM;
snprintf(table[i], llen + flen + 1, "%s%s",
sb_input_mx23[i], ictx->uboot_filename);
} else {
table[i] = strdup(sb_input_mx23[i]);
}
} else {
if (i == 3) {
llen = strlen(sb_input_mx28[i]);
flen = strlen(ictx->spl_filename);
table[i] = calloc(1, llen + flen + 1);
if (!table[i])
return -ENOMEM;
snprintf(table[i], llen + flen + 1, "%s%s",
sb_input_mx28[i], ictx->spl_filename);
} else if (i == 6) {
llen = strlen(sb_input_mx28[i]);
flen = strlen(ictx->uboot_filename);
table[i] = calloc(1, llen + flen + 1);
if (!table[i])
return -ENOMEM;
snprintf(table[i], llen + flen + 1, "%s%s",
sb_input_mx28[i], ictx->uboot_filename);
} else {
table[i] = strdup(sb_input_mx28[i]);
}
}
}
ictx->sb_input = table;
return 0;
}
int main(int argc, char **argv)
{
int ret;
int opt;
char *spl_filename = NULL;
char *uboot_filename = NULL;
char *cpuid;
struct sb_image_ctx ctx;
memset(&ctx, 0, sizeof(ctx));
......@@ -964,15 +1101,13 @@ int main(int argc, char **argv)
while ((opt = getopt(argc, argv, "c:s:u:o:vh")) != -1) {
switch (opt) {
case 'c':
ret = set_cpu(&ctx, argv[0], optarg);
if (ret)
return ret;
cpuid = optarg;
break;
case 's':
spl_filename = optarg;
ctx.spl_filename = optarg;
break;
case 'u':
uboot_filename = optarg;
ctx.uboot_filename = optarg;
break;