diff --git a/Makefile b/Makefile index c814f84..57b4258 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ clean: .SUFFIXES: .c .o -mspdebug: main.o fet.o rf2500.o dis.o uif.o +mspdebug: main.o fet.o rf2500.o dis.o uif.o ihex.o elf32.o $(CC) $(CFLAGS) -o $@ $^ -lusb .c.o: diff --git a/binfile.h b/binfile.h new file mode 100644 index 0000000..bccc442 --- /dev/null +++ b/binfile.h @@ -0,0 +1,36 @@ +/* MSPDebug - debugging tool for the eZ430 + * Copyright (C) 2009 Daniel Beer + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef BINFILE_H_ +#define BINFILE_H_ + +#include +#include + +/* Callback for binary image data */ +typedef int (*imgfunc_t)(u_int16_t addr, const u_int8_t *data, int len); + +/* Intel HEX file support */ +int ihex_check(FILE *in); +int ihex_extract(FILE *in, imgfunc_t cb); + +/* ELF32 file support */ +int elf32_check(FILE *in); +int elf32_extract(FILE *in, imgfunc_t cb); + +#endif diff --git a/elf32.c b/elf32.c new file mode 100644 index 0000000..fb9c51a --- /dev/null +++ b/elf32.c @@ -0,0 +1,192 @@ +/* MSPDebug - debugging tool for the eZ430 + * Copyright (C) 2009 Daniel Beer + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include "binfile.h" + +#define EM_MSP430 0x69 + +static const u_int8_t elf32_id[] = { + ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, ELFCLASS32 +}; + +#define MAX_PHDRS 32 +#define MAX_SHDRS 32 + +static Elf32_Ehdr file_ehdr; +static Elf32_Phdr file_phdrs[MAX_PHDRS]; +static Elf32_Shdr file_shdrs[MAX_SHDRS]; + +static int read_ehdr(FILE *in) +{ + int i; + + /* Read and check the ELF header */ + rewind(in); + if (fread(&file_ehdr, sizeof(file_ehdr), 1, in) < 0) { + perror("elf32: couldn't read ELF header"); + return -1; + } + + for (i = 0; i < sizeof(elf32_id); i++) + if (file_ehdr.e_ident[i] != elf32_id[i]) { + fprintf(stderr, "elf32: not an ELF32 file\n"); + return -1; + } + + return 0; +} + +static int read_phdr(FILE *in) +{ + int i; + + if (file_ehdr.e_phnum > MAX_PHDRS) { + fprintf(stderr, "elf32: too many program headers: %d\n", + file_ehdr.e_phnum); + return -1; + } + + for (i = 0; i < file_ehdr.e_phnum; i++) { + if (fseek(in, i * file_ehdr.e_phentsize + file_ehdr.e_phoff, + SEEK_SET) < 0) { + fprintf(stderr, "elf32: can't seek to phdr %d\n", i); + return -1; + } + + if (fread(&file_phdrs[i], sizeof(file_phdrs[0]), 1, in) < 0) { + fprintf(stderr, "elf32: can't read phdr %d: %s\n", + i, strerror(errno)); + return -1; + } + } + + return 0; +} + +static int read_shdr(FILE *in) +{ + int i; + + if (file_ehdr.e_shnum > MAX_SHDRS) { + fprintf(stderr, "elf32: too many section headers: %d\n", + file_ehdr.e_shnum); + return -1; + } + + for (i = 0; i < file_ehdr.e_shnum; i++) { + if (fseek(in, i * file_ehdr.e_shentsize + file_ehdr.e_shoff, + SEEK_SET) < 0) { + fprintf(stderr, "elf32: can't seek to phdr %d\n", i); + return -1; + } + + if (fread(&file_shdrs[i], sizeof(file_shdrs[0]), 1, in) < 0) { + fprintf(stderr, "elf32: can't read phdr %d: %s\n", + i, strerror(errno)); + return -1; + } + } + + return 0; +} + +static u_int32_t file_to_phys(u_int32_t v) +{ + int i; + + for (i = 0; i < file_ehdr.e_phnum; i++) { + Elf32_Phdr *p = &file_phdrs[i]; + + if (v >= p->p_offset && v - p->p_offset < p->p_filesz) + return v - p->p_offset + p->p_paddr; + } + + return v; +} + +static int feed_section(FILE *in, int offset, int size, imgfunc_t cb) +{ + u_int8_t buf[1024]; + u_int16_t addr = file_to_phys(offset); + + if (fseek(in, offset, SEEK_SET) < 0) { + perror("elf32: can't seek to section"); + return -1; + } + + while (size) { + int ask = size > sizeof(buf) ? sizeof(buf) : size; + int len = fread(buf, 1, ask, in); + + if (len < 0) { + perror("elf32: can't read section"); + return -1; + } + + if (cb(addr, buf, len) < 0) + return -1; + + size -= len; + offset += len; + addr += len; + } + + return 0; +} + +int elf32_extract(FILE *in, imgfunc_t cb) +{ + int i; + + if (read_ehdr(in) < 0) + return -1; + if (file_ehdr.e_machine != EM_MSP430) { + fprintf(stderr, "elf32: this is not an MSP430 ELF32\n"); + return -1; + } + + if (read_phdr(in) < 0) + return -1; + if (read_shdr(in) < 0) + return -1; + + for (i = 0; i < file_ehdr.e_shnum; i++) { + Elf32_Shdr *s = &file_shdrs[i]; + + if (s->sh_type == SHT_PROGBITS && s->sh_flags & SHF_ALLOC && + feed_section(in, s->sh_offset, s->sh_size, cb) < 0) + return -1; + } + + return 0; +} + +int elf32_check(FILE *in) +{ + int i; + + rewind(in); + for (i = 0; i < sizeof(elf32_id); i++) + if (fgetc(in) != elf32_id[i]) + return 0; + + return 1; +} diff --git a/fet.c b/fet.c index a76773a..504ab63 100644 --- a/fet.c +++ b/fet.c @@ -105,7 +105,7 @@ static void init_codes(void) * needs to be stored in little-endian format at the end of the payload. */ -static u_int16_t calc_checksum(const char *data, int len) +static u_int16_t calc_checksum(const u_int8_t *data, int len) { int i; u_int16_t cksum = 0xffff; @@ -140,13 +140,13 @@ static u_int16_t calc_checksum(const char *data, int len) * * No checksums are included. */ -static int send_rf2500_data(const char *data, int len) +static int send_rf2500_data(const u_int8_t *data, int len) { int offset = 0; assert (fet_transport != NULL); while (len) { - char pbuf[63]; + u_int8_t pbuf[63]; int plen = len > 59 ? 59 : len; pbuf[0] = 0x83; @@ -165,7 +165,7 @@ static int send_rf2500_data(const char *data, int len) return 0; } -static char fet_buf[65538]; +static u_int8_t fet_buf[65538]; static int fet_len; #define MAX_PARAMS 16 @@ -178,7 +178,7 @@ static struct { int argc; u_int32_t argv[MAX_PARAMS]; - char *data; + u_int8_t *data; int datalen; } fet_reply; @@ -372,12 +372,12 @@ static int recv_packet(void) static int send_command(int command_code, const u_int32_t *params, int nparams, - const char *extra, int exlen) + const u_int8_t *extra, int exlen) { - char datapkt[256]; + u_int8_t datapkt[256]; int len = 0; - char buf[512]; + u_int8_t buf[512]; u_int16_t cksum; int i = 0; int j; @@ -449,7 +449,7 @@ static int send_command(int command_code, return fet_transport->send(buf, i); } -static int xfer(int command_code, const char *data, int datalen, +static int xfer(int command_code, const u_int8_t *data, int datalen, int nparams, ...) { u_int32_t params[MAX_PARAMS]; @@ -534,7 +534,7 @@ static const struct { } }; -extern void hexdump(int addr, const char *data, int len); +extern void hexdump(int addr, const u_int8_t *data, int len); static int do_identify(void) { @@ -633,7 +633,7 @@ int fet_open(const struct fet_transport *tr, int proto_flags, int vcc_mv) * This is RF2500-specific. */ if (fet_is_rf2500) { - static const char data[] = { + static const u_int8_t data[] = { 0x00, 0x80, 0xff, 0xff, 0x00, 0x00, 0x00, 0x10, 0xff, 0x10, 0x40, 0x00, 0x00, 0x02, 0xff, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, @@ -703,7 +703,7 @@ int fet_get_context(u_int16_t *regs) int fet_set_context(u_int16_t *regs) { - char buf[FET_NUM_REGS * 4]; + u_int8_t buf[FET_NUM_REGS * 4];; int i; int ret; @@ -729,7 +729,7 @@ int fet_set_context(u_int16_t *regs) return 0; } -int fet_read_mem(u_int16_t addr, char *buffer, int count) +int fet_read_mem(u_int16_t addr, u_int8_t *buffer, int count) { while (count) { int plen = count > 128 ? 128 : count; @@ -755,7 +755,7 @@ int fet_read_mem(u_int16_t addr, char *buffer, int count) return 0; } -int fet_write_mem(u_int16_t addr, char *buffer, int count) +int fet_write_mem(u_int16_t addr, const u_int8_t *buffer, int count) { while (count) { int plen = count > 128 ? 128 : count; diff --git a/fet.h b/fet.h index 2307c83..3470885 100644 --- a/fet.h +++ b/fet.h @@ -39,8 +39,8 @@ int rf2500_open(void); * high-level functions. */ struct fet_transport { - int (*send)(const char *data, int len); - int (*recv)(char *data, int max_len); + int (*send)(const u_int8_t *data, int len); + int (*recv)(u_int8_t *data, int max_len); void (*close)(void); }; @@ -90,8 +90,8 @@ int fet_erase(int type, u_int16_t addr, int len); /* Read and write memory. fet_write_mem can be used to reflash the * device, but only after an erase. */ -int fet_read_mem(u_int16_t addr, char *buffer, int count); -int fet_write_mem(u_int16_t addr, char *buffer, int count); +int fet_read_mem(u_int16_t addr, u_int8_t *buffer, int count); +int fet_write_mem(u_int16_t addr, const u_int8_t *buffer, int count); /* Fetch the device status. If the device is currently running, then * the FET_POLL_RUNNING flag will be set. FET_POLL_BREAKPOINT is set diff --git a/ihex.c b/ihex.c new file mode 100644 index 0000000..e7d7d07 --- /dev/null +++ b/ihex.c @@ -0,0 +1,93 @@ +/* MSPDebug - debugging tool for the eZ430 + * Copyright (C) 2009 Daniel Beer + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include "binfile.h" + +int ihex_check(FILE *in) +{ + rewind(in); + return fgetc(in) == ':'; +} + +static int feed_line(FILE *in, u_int8_t *data, int nbytes, imgfunc_t cb) +{ + u_int8_t cksum = 0; + int i; + + if (nbytes < 5 || data[3]) + return 0; + + /* Verify checksum */ + for (i = 0; i + 1 < nbytes; i++) + cksum += data[i]; + cksum = ~(cksum - 1) & 0xff; + + if (data[nbytes - 1] != cksum) { + fprintf(stderr, "ihex: invalid checksum: %02x " + "(calculated %02x)\n", data[nbytes - 1], cksum); + return -1; + } + + return cb(((u_int16_t)data[1]) << 8 | ((u_int16_t)data[2]), + data + 4, nbytes - 5); +} + +int ihex_extract(FILE *in, imgfunc_t cb) +{ + char buf[128]; + int lno = 0; + + rewind(in); + while (fgets(buf, sizeof(buf), in)) { + int len = strlen(buf); + int i; + u_int8_t data[64]; + int nbytes; + + lno++; + if (buf[0] != ':') { + fprintf(stderr, "ihex: line %d: invalid start " + "marker\n", lno); + continue; + } + + /* Trim trailing whitespace */ + while (len && isspace(buf[len - 1])) + len--; + buf[len] = 0; + + /* Decode hex digits */ + nbytes = (len - 1) / 2; + for (i = 0; i < nbytes; i++) { + char d[] = {buf[i * 2 + 1], buf[i * 2 + 2], 0}; + + data[i] = strtoul(d, NULL, 16); + } + + /* Handle the line */ + if (feed_line(in, data, nbytes, cb) < 0) { + fprintf(stderr, "ihex: error on line %d\n", lno); + return -1; + } + } + + return 0; +} diff --git a/main.c b/main.c index ccc8b14..a58aa1e 100644 --- a/main.c +++ b/main.c @@ -26,8 +26,9 @@ #include "dis.h" #include "fet.h" +#include "binfile.h" -void hexdump(int addr, const char *data, int len) +void hexdump(int addr, const u_int8_t *data, int len) { int offset = 0; @@ -145,7 +146,7 @@ static int cmd_md(char **arg) } while (length) { - char buf[128]; + u_int8_t buf[128]; int blen = length > sizeof(buf) ? sizeof(buf) : length; if (fet_read_mem(offset, buf, blen) < 0) @@ -202,7 +203,7 @@ static int cmd_dis(char **arg) char *len_text = get_arg(arg); unsigned int offset = 0; unsigned int length = 0; - char buf[128]; + u_int8_t buf[128]; if (!off_text) { fprintf(stderr, "md: offset must be specified\n"); @@ -235,7 +236,7 @@ static int cmd_reset(char **arg) static int cmd_regs(char **arg) { u_int16_t regs[FET_NUM_REGS]; - char code[16]; + u_int8_t code[16]; if (fet_get_context(regs) < 0) return -1; @@ -325,107 +326,76 @@ static int cmd_step(char **arg) return cmd_regs(NULL); } -static int hexval(const char *text, int len) -{ - int value = 0; +/************************************************************************ + * Flash image programming state machine. + */ - while (len && *text) { - value <<= 4; - - if (*text >= 'A' && *text <= 'F') - value += *text - 'A' + 10; - else if (*text >= 'a' && *text <= 'f') - value += *text - 'a' + 10; - else if (isdigit(*text)) - value += *text - '0'; - - text++; - len--; - } - - return value; -} - -static char prog_buf[128]; +static u_int8_t prog_buf[128]; static u_int16_t prog_addr; static int prog_len; +static int prog_have_erased; + +static void prog_init(void) +{ + prog_len = 0; + prog_have_erased = 0; +} static int prog_flush(void) { - int wlen = prog_len; + while (prog_len) { + int wlen = prog_len; - if (!prog_len) - return 0; + /* Writing across this address seems to cause a hang */ + if (prog_addr < 0x999a && wlen + prog_addr > 0x999a) + wlen = 0x999a - prog_addr; - /* Writing across this address seems to cause a hang */ - if (prog_addr < 0x999a && wlen + prog_addr > 0x999a) - wlen = 0x999a - prog_addr; + if (!prog_have_erased) { + printf("Erasing...\n"); + if (fet_erase(FET_ERASE_ALL, 0x1000, 0x100) < 0) + return -1; + prog_have_erased = 1; + } - printf("Writing %3d bytes to %04x...\n", wlen, prog_addr); + printf("Writing %3d bytes to %04x...\n", wlen, prog_addr); + if (fet_write_mem(prog_addr, prog_buf, wlen) < 0) + return -1; - if (fet_write_mem(prog_addr, prog_buf, wlen) < 0) - return -1; - - memmove(prog_buf, prog_buf + wlen, prog_len - wlen); - prog_len -= wlen; - prog_addr += wlen; - - return 0; -} - -static int prog_hex(int lno, const char *hex) -{ - int len = strlen(hex); - int count, address, type, cksum = 0; - int i; - - if (*hex != ':') - return 0; - - hex++; - len--; - - while (len && isspace(hex[len - 1])) - len--; - - if (len < 10) - return 0; - - count = hexval(hex, 2); - address = hexval(hex + 2, 4); - type = hexval(hex + 6, 2); - - if (type) - return 0; - - for (i = 0; i + 2 < len; i += 2) - cksum = (cksum + hexval(hex + i, 2)) - & 0xff; - cksum = ~(cksum - 1) & 0xff; - - if (count * 2 + 10 != len) { - fprintf(stderr, "warning: length mismatch at line %d\n", lno); - count = (len - 10) / 2; + memmove(prog_buf, prog_buf + wlen, prog_len - wlen); + prog_len -= wlen; + prog_addr += wlen; } - if (cksum != hexval(hex + len - 2, 2)) - fprintf(stderr, "warning: invalid checksum at line %d\n", lno); + return 0; +} - for (i = 0; i < count; i++) { - int offset; +static int prog_feed(u_int16_t addr, const u_int8_t *data, int len) +{ + /* Flush if this section is discontiguous */ + if (prog_len && prog_addr + prog_len != addr && prog_flush() < 0) + return -1; - offset = address + i - prog_addr; - if (offset < 0 || offset >= sizeof(prog_buf)) + if (!prog_len) + prog_addr = addr; + + /* Add the buffer in piece by piece, flushing when it gets + * full. + */ + while (len) { + int count = sizeof(prog_buf) - prog_len; + + if (count > len) + count = len; + + if (!count) { if (prog_flush() < 0) return -1; - - if (!prog_len) - prog_addr = address + i; - - offset = address + i - prog_addr; - prog_buf[offset] = hexval(hex + 8 + i * 2, 2); - if (offset + 1 > prog_len) - prog_len = offset + 1; + } else { + memcpy(prog_buf + prog_len, data, count); + prog_len += count; + data += count; + len -= count; + } } return 0; @@ -434,35 +404,31 @@ static int prog_hex(int lno, const char *hex) static int cmd_prog(char **arg) { FILE *in = fopen(*arg, "r"); - char text[256]; - int lno = 1; + int result = 0; if (!in) { fprintf(stderr, "prog: %s: %s\n", *arg, strerror(errno)); return -1; } - printf("Erasing...\n"); - if (fet_erase(FET_ERASE_ALL, 0x1000, 0x100) < 0) { + if (fet_reset(FET_RESET_ALL | FET_RESET_HALT) < 0) { fclose(in); return -1; } - if (fet_reset(FET_RESET_ALL | FET_RESET_HALT) < 0) - return -1; + prog_init(); + if (elf32_check(in)) + result = elf32_extract(in, prog_feed); + else if (ihex_check(in)) + result = ihex_extract(in, prog_feed); + else + fprintf(stderr, "%s: unknown file type\n", *arg); + + if (!result) + result = prog_flush(); - prog_len = 0; - while (fgets(text, sizeof(text), in)) - if (prog_hex(lno++, text) < 0) { - fclose(in); - return -1; - } fclose(in); - - if (prog_flush() < 0) - return -1; - - return fet_reset(FET_RESET_ALL | FET_RESET_HALT); + return result; } static const struct command all_commands[] = { diff --git a/rf2500.c b/rf2500.c index d5934a7..6e2ccc7 100644 --- a/rf2500.c +++ b/rf2500.c @@ -22,7 +22,7 @@ #include "fet.h" -void hexdump(int addr, const char *data, int len); +extern void hexdump(int addr, const u_int8_t *data, int len); /********************************************************************* * USB transport @@ -87,10 +87,10 @@ static int usbtr_open_device(struct usb_device *dev) return -1; } -static int usbtr_send(const char *data, int len) +static int usbtr_send(const u_int8_t *data, int len) { while (len) { - char pbuf[256]; + u_int8_t pbuf[256]; int plen = len > 255 ? 255 : len; int txlen = plen + 1; @@ -112,7 +112,7 @@ static int usbtr_send(const char *data, int len) hexdump(0, pbuf, txlen); #endif if (usb_bulk_write(usbtr_handle, USB_FET_OUT_EP, - pbuf, txlen, 10000) < 0) { + (const char *)pbuf, txlen, 10000) < 0) { perror("usbtr_send"); return -1; } @@ -124,7 +124,7 @@ static int usbtr_send(const char *data, int len) return 0; } -static char usbtr_buf[64]; +static u_int8_t usbtr_buf[64]; static int usbtr_len; static int usbtr_offset; @@ -136,13 +136,14 @@ static void usbtr_flush(void) buf, sizeof(buf), 100) >= 0); } -static int usbtr_recv(char *databuf, int max_len) +static int usbtr_recv(u_int8_t *databuf, int max_len) { int rlen; if (usbtr_offset >= usbtr_len) { if (usb_bulk_read(usbtr_handle, USB_FET_IN_EP, - usbtr_buf, sizeof(usbtr_buf), 10000) < 0) { + (char *)usbtr_buf, sizeof(usbtr_buf), + 10000) < 0) { perror("usbtr_recv"); return -1; } diff --git a/uif.c b/uif.c index d871e64..222f327 100644 --- a/uif.c +++ b/uif.c @@ -28,11 +28,11 @@ #include "fet.h" -void hexdump(int addr, const char *data, int len); +extern void hexdump(int addr, const u_int8_t *data, int len); static int serial_fd = -1; -static int serial_send(const char *data, int len) +static int serial_send(const u_int8_t *data, int len) { int result; @@ -60,7 +60,7 @@ static int serial_send(const char *data, int len) return 0; } -static int serial_recv(char *data, int max_len) +static int serial_recv(u_int8_t *data, int max_len) { int len;