# HG changeset patch # User Michael Spacefalcon # Date 1365992394 0 # Node ID cb736d95338d18e2a66a7ce8d9418287a5572f66 mokosrec2bin utility written diff -r 000000000000 -r cb736d95338d .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Mon Apr 15 02:19:54 2013 +0000 @@ -0,0 +1,1 @@ +mokosrec2bin diff -r 000000000000 -r cb736d95338d Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Mon Apr 15 02:19:54 2013 +0000 @@ -0,0 +1,13 @@ +CC= gcc +CFLAGS= -O2 +PROGS= mokosrec2bin + +all: ${PROGS} + +${PROGS}: + ${CC} ${CFLAGS} -o $@ $@.c + +mokosrec2bin: mokosrec2bin.c + +clean: + rm -f ${PROGS} *.o *errs *.out diff -r 000000000000 -r cb736d95338d mokosrec2bin.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mokosrec2bin.c Mon Apr 15 02:19:54 2013 +0000 @@ -0,0 +1,226 @@ +/* + * The *.m0 format in which the Closedmoko firmware images have been + * distributed is a form of SREC, but further examination quickly + * reveals that the data bytes are swapped: the byte order is neither + * the natural little-endian ARM one of the Calypso, nor a word-wise + * ARM BE; instead the upper and lower bytes of each 16-bit shortword + * are swapped relative to the natural byte/word order of the Calypso + * ARM processor. + * + * Before this Closedmoko firmware can be examined with any of the + * standard reverse eng tools (ARM disassemblers or even the good old + * strings), it needs to be converted from the weird byte-swapped SREC + * format to straight binary in the natural byte order. The present + * utility accomplishes that conversion. + * + * Written by Spacefalcon the Outlaw. + */ + +#include +#include +#include +#include + +char *infname; +FILE *inf, *outf; +u_char fillbyte; +char srecbuf[80]; +u_char srecbin[40]; +int lineno, state; +u_long lastaddr; + +u_char header[6] = {0x06, 0x00, 0x00, 'H', 'D', 'R'}; + +decode_hex_byte(s) + char *s; +{ + register int u, l; + + if (!isxdigit(s[0]) || !isxdigit(s[1])) + return(-1); + if (isdigit(s[0])) + u = s[0] - '0'; + else if (isupper(s[0])) + u = s[0] - 'A' + 10; + else + u = s[0] - 'a' + 10; + if (isdigit(s[1])) + l = s[1] - '0'; + else if (isupper(s[1])) + l = s[1] - 'A' + 10; + else + l = s[1] - 'a' + 10; + return((u << 4) | l); +} + +srec2bin() +{ + register int i, l, b; + + l = decode_hex_byte(srecbuf + 2); + if (l < 1) { + fprintf(stderr, "%s line %d: S-record length octet is bad\n", + infname, lineno); + exit(1); + } + srecbin[0] = l; + if (l > 35) { + fprintf(stderr, + "%s line %d: S-record is longer than expected\n", + infname, lineno); + exit(1); + } + for (i = 1; i <= l; i++) { + b = decode_hex_byte(srecbuf + i*2 + 2); + if (b < 0) { + fprintf(stderr, "%s line %d: hex decode error\n", + infname, lineno); + exit(1); + } + srecbin[i] = b; + } + return(0); +} + +srec_cksum() +{ + u_char accum; + register int i, len; + + len = srecbin[0] + 1; + accum = 0; + for (i = 0; i < len; i++) + accum += srecbin[i]; + if (accum != 0xFF) { + fprintf(stderr, "%s line %d: bad checksum\n", infname, lineno); + exit(1); + } + return(0); +} + +main(argc, argv) + char **argv; +{ + register int i; + u_long curaddr; + int datalen; + + if (argc < 3 || argc > 4) { +usage: fprintf(stderr, "usage: %s input.m0 output.bin [fill-byte]\n", + argv[0]); + exit(1); + } + infname = argv[1]; + inf = fopen(infname, "r"); + if (!inf) { + perror(infname); + exit(1); + } + if (argc > 3) { + i = decode_hex_byte(argv[3]); + if (i >= 0) + fillbyte = i; + else + goto usage; + } else + fillbyte = 0; + + state = 0; + for (lineno = 1; ; lineno++) { + if (!fgets(srecbuf, sizeof srecbuf, inf)) { + fprintf(stderr, "%s: premature EOF\n", infname); + exit(1); + } + if (srecbuf[0] != 'S') { + fprintf(stderr, "%s line %d: not an S-record\n", + infname, lineno); + exit(1); + } + switch (srecbuf[1]) { + case '0': + if (state == 0) + break; + else + goto badtype; + case '3': + if (state == 0) + goto badtype; + else + break; + case '7': + if (state == 2) + break; + else + goto badtype; + default: + badtype: + fprintf(stderr, + "%s line %d: S-record type unexpected\n", + infname, lineno); + exit(1); + } + srec2bin(); + srec_cksum(); + if (state == 0) { + if (bcmp(srecbin, header, 6)) { + fprintf(stderr, "%s: expected header missing\n", + infname); + exit(1); + } + state = 1; + continue; + } + switch (srecbuf[1]) { + case '3': + if (srecbin[0] < 6) { + fprintf(stderr, + "%s line %d: S3 record is too short\n", + infname, lineno); + exit(1); + } + curaddr = (srecbin[1] << 24) | (srecbin[2] << 16) | + (srecbin[3] << 8) | srecbin[4]; + if (curaddr & 1) { + fprintf(stderr, "%s line %d: odd address\n", + infname, lineno); + exit(1); + } + datalen = srecbin[0] - 5; + if (datalen & 1) { + fprintf(stderr, "%s line %d: odd data length\n", + infname, lineno); + exit(1); + } + if (state < 2) { + outf = fopen(argv[2], "w"); + if (!outf) { + perror(argv[2]); + exit(1); + } + state = 2; + lastaddr = 0; + } + if (curaddr < lastaddr) { + fprintf(stderr, + "%s line %d: address going backwards\n", + infname, lineno); + exit(1); + } + while (lastaddr < curaddr) { + putc(fillbyte, outf); + lastaddr++; + } + for (i = 0; i < datalen; i += 2) { + putc(srecbin[i + 6], outf); + putc(srecbin[i + 5], outf); + } + lastaddr = curaddr + datalen; + continue; + case '7': + fclose(outf); + exit(0); + default: + abort(); + } + } +}