Add compiler/decompiler for lightbar bytecode programs.

BUG=none
BRANCH=ToT
TEST=manual

  make BOARD=samus
  for i in extra/lightbar/programs/[g-z]*.bin; do
    ./build/samus/util/lbcc -d $i /tmp/x.lbs
    ./build/samus/util/lbcc /tmp/x.lbs /tmp/x.bin
    cmp $i /tmp/x.bin
  done

Change-Id: I86c014c425e917ecafadd1c6845fcf2e5b4edbb7
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/220244
This commit is contained in:
Bill Richardson
2014-09-26 15:50:23 -07:00
committed by chrome-internal-fetch
parent af3d103dbc
commit 87762fa699
6 changed files with 640 additions and 29 deletions

View File

@@ -988,23 +988,8 @@ static struct lb_program cur_prog;
static struct lb_program next_prog;
static uint8_t pc;
enum lb_color {
LB_COL_RED,
LB_COL_GREEN,
LB_COL_BLUE,
LB_COL_ALL
};
enum lb_control {
LB_CONT_COLOR0,
LB_CONT_COLOR1,
LB_CONT_PHASE,
LB_CONT_MAX
};
static uint8_t led_desc[NUM_LEDS][LB_CONT_MAX][3];
static uint32_t lb_ramp_delay;
/* Get one byte of data pointed to by the pc and advance
* the pc forward.
*/
@@ -1261,33 +1246,26 @@ static uint32_t lightbyte_CYCLE(void)
#undef GET_INTERP_VALUE
#define OPCODE_TABLE \
OP(JUMP), \
OP(DELAY), \
OP(SET_BRIGHTNESS), \
OP(SET_COLOR), \
OP(SET_DELAY_TIME), \
OP(RAMP_ONCE), \
OP(CYCLE_ONCE), \
OP(CYCLE),
#define OP(X) X
#include "lightbar_opcode_list.h"
enum lightbyte_opcode {
OPCODE_TABLE
LIGHTBAR_OPCODE_TABLE
HALT,
MAX_OPCODE
};
#undef OP
#define OP(X) lightbyte_ ## X
#include "lightbar_opcode_list.h"
static uint32_t (*lightbyte_dispatch[])(void) = {
OPCODE_TABLE
LIGHTBAR_OPCODE_TABLE
};
#undef OP
#define OP(X) # X
#include "lightbar_opcode_list.h"
static const char * const lightbyte_names[] = {
OPCODE_TABLE
LIGHTBAR_OPCODE_TABLE
"HALT"
};
#undef OP

View File

@@ -1037,6 +1037,7 @@ struct lightbar_params_v1 {
/* Lightbyte program. */
#define LB_PROG_LEN 192
#define LB_PROG_MAX_OPERANDS 4
struct lb_program {
uint8_t size;
uint8_t data[LB_PROG_LEN];

View File

@@ -19,6 +19,21 @@ enum lightbar_sequence {
};
#undef LBMSG
/* Bytecode field constants */
enum lb_color {
LB_COL_RED,
LB_COL_GREEN,
LB_COL_BLUE,
LB_COL_ALL
};
enum lb_control {
LB_CONT_COLOR0,
LB_CONT_COLOR1,
LB_CONT_PHASE,
LB_CONT_MAX
};
/* Request a preset sequence from the lightbar task. */
void lightbar_sequence(enum lightbar_sequence s);

View File

@@ -0,0 +1,15 @@
/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* This defines a list of lightbar opcodes for programmable sequences.
*/
#define LIGHTBAR_OPCODE_TABLE \
OP(JUMP), \
OP(DELAY), \
OP(SET_BRIGHTNESS), \
OP(SET_COLOR), \
OP(SET_DELAY_TIME), \
OP(RAMP_ONCE), \
OP(CYCLE_ONCE), \
OP(CYCLE),

View File

@@ -6,7 +6,7 @@
# Host tools build
#
host-util-bin=ectool lbplay burn_my_ec stm32mon ec_sb_firmware_update
host-util-bin=ectool lbplay burn_my_ec stm32mon ec_sb_firmware_update lbcc
comm-objs=$(util-lock-objs:%=lock/%) comm-host.o comm-dev.o
ifeq ($(CHIP),mec1322)

602
util/lbcc.c Normal file
View File

@@ -0,0 +1,602 @@
/*
* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <errno.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "compile_time_macros.h"
#include "ec_commands.h"
#include "lb_common.h"
#include "lightbar.h"
static const char usage[] =
"\n"
"Usage: %s [OPTIONS] [INFILE [OUTFILE]]\n"
"\n"
"This compiles or decompiles the lightbar programmable bytecode.\n"
"\n"
"Options:\n"
" -d Decode binary to ascii\n"
" -v Decode output should be verbose\n"
"\n";
/* globals */
static int hit_errors;
static int opt_verbose;
static int is_jump_target[LB_PROG_LEN]; /* does program jump here? */
static int is_instruction[LB_PROG_LEN]; /* instruction or operand? */
static char *label[LB_PROG_LEN]; /* labels we've seen */
static char *reloc_label[LB_PROG_LEN]; /* put label target here */
static void Error(const char *format, ...)
{
va_list ap;
va_start(ap, format);
fprintf(stderr, "ERROR: ");
vfprintf(stderr, format, ap);
va_end(ap);
hit_errors++;
}
static void Warning(const char *format, ...)
{
va_list ap;
va_start(ap, format);
fprintf(stderr, "Warning: ");
vfprintf(stderr, format, ap);
va_end(ap);
}
/* The longest line should have a label, an opcode, and the max operands */
#define MAX_WORDS (2 + LB_PROG_MAX_OPERANDS)
struct safe_lb_program {
struct lb_program p;
uint8_t zeros[LB_PROG_MAX_OPERANDS];
} __packed;
#define OP(X) X
#include "lightbar_opcode_list.h"
enum lightbyte_opcode {
LIGHTBAR_OPCODE_TABLE
HALT,
MAX_OPCODE
};
#undef OP
static const char const *opcode_sym[] = {
"jump", "wait", "bright", "color",
"step", "ramp.1", "cycle.1", "cycle",
"halt",
};
BUILD_ASSERT(ARRAY_SIZE(opcode_sym) == MAX_OPCODE);
static const char const *control_sym[] = {
"beg", "end", "phase", "<invalid>"
};
static const char const *color_sym[] = {
"r", "g", "b", "rgb"
};
static void read_binary(FILE *fp, struct safe_lb_program *prog)
{
int got;
memset(prog, 0, sizeof(*prog));
/* Read up to one more byte than we need, so we know if it's too big */
got = fread(prog->p.data, 1, LB_PROG_LEN + 1, fp);
if (got < 1) {
Error("Unable to read any input: ");
if (feof(fp))
fprintf(stderr, "EOF\n");
else if (ferror(fp))
fprintf(stderr, "%s\n", strerror(errno));
else
fprintf(stderr, "no idea why.\n");
} else if (got > LB_PROG_LEN) {
Warning("Truncating input at %d bytes\n", LB_PROG_LEN);
prog->zeros[0] = 0;
got = LB_PROG_LEN;
} else {
prog->p.size = got;
}
}
/* Returns number of operands required by an opcode */
static int num_operands(uint8_t cmd, uint8_t *arg)
{
int operands = 0;
switch (cmd) {
case JUMP:
case SET_BRIGHTNESS:
operands = 1;
break;
case DELAY:
case SET_DELAY_TIME:
operands = 4;
break;
case SET_COLOR:
if ((arg[0] & 0x03) == LB_COL_ALL)
operands = 4;
else
operands = 2;
break;
default:
break;
}
return operands;
}
static uint32_t val32(uint8_t *ptr)
{
uint32_t val;
val = (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
return val;
}
/* returns number of operands consumed */
static int print_op(FILE *fp, uint8_t addr, uint8_t cmd, uint8_t *arg)
{
uint8_t led, color, control;
int i, operands;
operands = num_operands(cmd, arg);
/* assume valid instruction for now */
is_instruction[addr] = 1;
if (opt_verbose) {
fprintf(fp, "%02x: %02x", addr, cmd);
for (i = 0; i < LB_PROG_MAX_OPERANDS; i++)
if (i < operands)
fprintf(fp, " %02x", arg[i]);
else
fprintf(fp, " ");
fprintf(fp, "\t");
}
if (is_jump_target[addr])
fprintf(fp, "L00%02x:", addr);
fprintf(fp, "\t");
if (cmd < MAX_OPCODE)
fprintf(fp, "%s", opcode_sym[cmd]);
switch (cmd) {
case JUMP:
fprintf(fp, "\tL00%02x\n", arg[0]);
break;
case DELAY:
case SET_DELAY_TIME:
fprintf(fp, "\t%d\n", val32(arg));
break;
case SET_BRIGHTNESS:
fprintf(fp, "\t%d\n", arg[0]);
break;
case SET_COLOR:
led = arg[0] >> 4;
control = (arg[0] >> 2) & 0x03;
color = arg[0] & 0x03;
fprintf(fp, "\t");
if (led >= NUM_LEDS)
fprintf(fp, "all");
else
fprintf(fp, "%d", led);
fprintf(fp, ".%s", control_sym[control]);
fprintf(fp, ".%s", color_sym[color]);
if (color == LB_COL_ALL)
fprintf(fp, "\t0x%02x 0x%02x 0x%02x\n",
arg[1], arg[2], arg[3]);
else
fprintf(fp, "\t0x%02x\n", arg[1]);
break;
case RAMP_ONCE:
case CYCLE_ONCE:
case CYCLE:
case HALT:
fprintf(fp, "\n");
break;
default:
fprintf(fp, "-- invalid opcode 0x%02x --\n", cmd);
is_instruction[addr] = 0;
hit_errors++;
}
return operands;
}
static void disassemble_prog(FILE *fp, struct safe_lb_program *prog)
{
int i;
uint8_t *ptr, targ;
/* Scan the program once to identify all the jump targets,
* so we can print the labels when we encounter them. */
for (i = 0; i < prog->p.size; i++) {
ptr = &prog->p.data[i];
if (ptr[0] == JUMP) {
targ = ptr[1];
is_jump_target[targ] = 1;
}
i += num_operands(*ptr, ptr + 1);
}
/* Now disassemble */
for (i = 0; i < prog->p.size; i++) {
ptr = &prog->p.data[i];
i += print_op(fp, i, *ptr, ptr + 1);
}
/* Finally, make sure the program doesn't jump to any location other
* than a valid instruction */
for (i = 0; i < LB_PROG_LEN; i++)
if (is_jump_target[i] && !is_instruction[i]) {
Warning("program jumps to 0x%02x, "
"which is not a valid instruction\n", i);
}
}
/* We'll split each line into an array of these. */
struct parse_s {
char *word;
int is_num;
uint32_t val;
};
/* Fills in struct, returns number of words found. Note that pointers are only
* copied. The strings they point to are not duplicated. */
static int split_line(char *buf, char *delim, struct parse_s *elt, int max)
{
char *w, *ptr, *buf_savetok;
int i;
char *e = 0;
memset(elt, 0, max * sizeof(*elt));
for (ptr = buf, i = 0;
i < max && (w = strtok_r(ptr, delim, &buf_savetok)) != 0;
ptr = 0, i++) {
elt[i].word = w;
elt[i].val = (uint32_t)strtoul(w, &e, 0);
if (!e || !*e)
elt[i].is_num = 1;
}
return i;
}
/* Decode color arg. Return 0 if bogus, number of additional args if okay. */
static int is_color_arg(char *buf, uint32_t *valp)
{
struct parse_s token[MAX_WORDS];
uint8_t led, control, color, val;
int i;
int rv = 1;
if (!buf)
return 0;
/* There should be three terms, separated with '.' */
i = split_line(buf, ".,", token, MAX_WORDS);
if (i != 3)
return 0;
if (!strcmp("all", token[0].word)) {
led = NUM_LEDS;
} else if (token[0].is_num) {
led = token[0].val;
} else {
Error("Invalid LED \"%s\"\n", token[0].word);
return 0;
}
for (i = 0; i < LB_CONT_MAX; i++)
if (!strcmp(token[1].word, control_sym[i])) {
control = i;
break;
}
if (i >= LB_CONT_MAX)
return 0;
for (i = 0; i < ARRAY_SIZE(color_sym); i++)
if (!strcmp(token[2].word, color_sym[i])) {
color = i;
break;
}
if (i >= ARRAY_SIZE(color_sym))
return 0;
val = ((led & 0xF) << 4) | ((control & 0x3) << 2) | (color & 0x3);
*valp = val;
if (color == LB_COL_ALL)
rv = 3;
return rv;
}
static void fixup_symbols(struct safe_lb_program *prog)
{
int i, j;
for (i = 0; i < LB_PROG_LEN; i++) {
if (reloc_label[i]) {
/* Looking for reloc label */
for (j = 0; j < LB_PROG_LEN; j++) {
if (label[j] && !strcmp(label[j],
reloc_label[i])) {
prog->p.data[i] = j;
break;
}
}
if (j >= LB_PROG_LEN)
Error("Can't find label %s from line %d\n", j);
}
}
}
static void compile(FILE *fp, struct safe_lb_program *prog)
{
char buf[128];
struct parse_s token[MAX_WORDS];
char *s;
int line = 0, chopping = 0;
uint8_t addr = 0;
int opcode;
int wnum, wordcnt;
int i;
while (fgets(buf, sizeof(buf), fp)) {
/* We truncate lines that are too long */
s = strchr(buf, '\n');
if (chopping) {
if (s)
chopping = 0;
continue;
}
/* Got something to look at */
line++;
if (!s) {
chopping = 1;
Warning("truncating line %d\n", line);
}
/* Ignore comments */
s = strchr(buf, '#');
if (s)
*s = '\0';
wordcnt = split_line(buf, " \t\n", token, MAX_WORDS);
if (!wordcnt)
continue;
wnum = 0;
/* A label must be the first word, ends with a ':' (no spaces
* before it), and doesn't start with a ':' */
s = strchr(token[0].word, ':');
if (s && s[1] == '\0' && s != token[0].word) {
*s = '\0';
label[addr] = strdup(token[0].word);
wnum++;
}
/* How about an opcode? */
for (opcode = 0; opcode < MAX_OPCODE; opcode++)
if (!strcasecmp(token[wnum].word, opcode_sym[opcode]))
break;
if (opcode >= MAX_OPCODE) {
Error("Unrecognized opcode \"%s\""
" at line %d\n", token[wnum].word, line);
continue;
}
/* Do we even have a place to write this opcode? */
if (addr >= LB_PROG_LEN) {
Error("out of program space at line %d\n", line);
break;
}
/* Got an opcode. Save it! */
prog->p.data[addr++] = opcode;
wnum++;
/* Now we need operands. */
switch (opcode) {
case JUMP:
/* a label */
if (token[wnum].word)
reloc_label[addr++] = strdup(token[wnum].word);
else
Error("Missing jump target at line %d\n", line);
break;
case SET_BRIGHTNESS:
/* one 8-bit arg */
if (token[wnum].is_num)
prog->p.data[addr++] = token[wnum].val;
else
Error("Missing/invalid arg at line %d\n", line);
break;
case DELAY:
case SET_DELAY_TIME:
/* one 32-bit arg */
if (token[wnum].is_num) {
prog->p.data[addr++] =
(token[wnum].val >> 24) & 0xff;
prog->p.data[addr++] =
(token[wnum].val >> 16) & 0xff;
prog->p.data[addr++] =
(token[wnum].val >> 8) & 0xff;
prog->p.data[addr++] =
token[wnum].val & 0xff;
} else {
Error("Missing/invalid arg at line %d\n", line);
}
break;
case SET_COLOR:
/* one magic word, then one or three more 8-bit args */
i = is_color_arg(token[wnum].word, &token[wnum].val);
if (!i) {
Error("Missing/invalid arg at line %d\n", line);
break;
}
/* save the magic number */
prog->p.data[addr++] = token[wnum++].val;
while (i--) {
/* and the others */
if (token[wnum].is_num) {
prog->p.data[addr++] =
token[wnum++].val;
} else {
Error("Missing/Invalid arg "
"at line %d\n", line);
break;
}
}
break;
default:
/* No args needed */
break;
}
/* Did we run past the end? */
if (addr > LB_PROG_LEN) {
Error("out of program space at line %d\n", line);
break;
}
}
if (ferror(fp))
Error("problem while reading input: %s\n", strerror(errno));
if (!hit_errors)
fixup_symbols(prog);
if (!hit_errors)
prog->p.size = addr;
if (!prog->p.size)
Error("input file produced no output bytes\n");
}
int main(int argc, char *argv[])
{
struct safe_lb_program safe_prog;
int opt_decode = 0;
int c;
int errorcnt = 0;
char *infile, *outfile;
FILE *ifp, *ofp;
char *progname = strrchr(argv[0], '/');
if (progname)
progname++;
else
progname = argv[0];
opterr = 0; /* quiet, you */
while ((c = getopt(argc, argv, ":dv")) != -1) {
switch (c) {
case 'd':
opt_decode = 1;
break;
case 'v':
opt_verbose = 1;
break;
case '?':
fprintf(stderr, "%s: unrecognized switch: -%c\n",
progname, optopt);
errorcnt++;
break;
case ':':
fprintf(stderr, "%s: missing argument to -%c\n",
progname, optopt);
errorcnt++;
break;
default:
errorcnt++;
break;
}
}
if (errorcnt) {
fprintf(stderr, "\nUsage: %s [options] ...\n\n", progname);
exit(1);
}
if (argc - optind > 0) {
infile = argv[optind];
ifp = fopen(infile, "rb");
if (!ifp) {
fprintf(stderr,
"%s: Unable to open %s for reading: %s\n",
progname, infile, strerror(errno));
exit(1);
}
} else {
infile = "stdin";
ifp = stdin;
}
if (argc - optind > 1) {
outfile = argv[optind + 1];
ofp = fopen(outfile, "wb");
if (!ofp) {
fprintf(stderr,
"%s: Unable to open %s for writing: %s\n",
progname, outfile, strerror(errno));
exit(1);
}
} else {
outfile = "stdout";
ofp = stdout;
}
if (opt_decode) {
read_binary(ifp, &safe_prog);
fclose(ifp);
if (hit_errors)
return 1;
fprintf(ofp, "# %s\n", infile);
disassemble_prog(ofp, &safe_prog);
fclose(ofp);
} else {
memset(&safe_prog, 0, sizeof(safe_prog));
compile(ifp, &safe_prog);
fclose(ifp);
if (!hit_errors) {
if (1 != fwrite(safe_prog.p.data,
safe_prog.p.size, 1, ofp))
Error("%s: Unable to write to %s: %s\n",
progname, outfile, strerror(errno));
else
fprintf(stderr, "0x%02x bytes written to %s\n",
safe_prog.p.size, outfile);
}
fclose(ofp);
}
return hit_errors;
}