diff --git a/AUTHORS b/AUTHORS index d720a23..1e51bfe 100644 --- a/AUTHORS +++ b/AUTHORS @@ -80,3 +80,6 @@ Peter Bägel : Jose Angel Caso Sanchez : * FET v2 database from MSP430.DLL. + +Paolo Zebelloni : + * Testing, debugging and research for the ROM BSL driver. diff --git a/Makefile b/Makefile index 6efbdf7..f84bc3e 100644 --- a/Makefile +++ b/Makefile @@ -150,6 +150,7 @@ OBJ=\ drivers/v3hil.o \ drivers/fet3.o \ drivers/bsllib.o \ + drivers/rom_bsl.o \ formats/binfile.o \ formats/coff.o \ formats/elf32.o \ diff --git a/drivers/rom_bsl.c b/drivers/rom_bsl.c new file mode 100644 index 0000000..7a5b0b8 --- /dev/null +++ b/drivers/rom_bsl.c @@ -0,0 +1,470 @@ +/* MSPDebug - debugging tool for the eZ430 + * Copyright (C) 2009-2014 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 +#include +#include + +#include "rom_bsl.h" +#include "util.h" +#include "output.h" +#include "bsllib.h" +#include "util/sport.h" + +struct rom_bsl_device { + struct device base; + + sport_t fd; + const char *seq; + + uint8_t reply_buf[256]; + int reply_len; +}; + +#define DATA_HDR 0x80 +#define DATA_ACK 0x90 +#define DATA_NAK 0xA0 + +static int rom_bsl_ack(struct rom_bsl_device *dev) +{ + uint8_t reply; + + if (sport_read_all(dev->fd, &reply, 1) < 0) { + pr_error("rom_bsl: failed to receive reply"); + return -1; + } + + if (reply == DATA_NAK) { + printc_err("rom_bsl: received NAK\n"); + return -1; + } + + if (reply != DATA_ACK) { + printc_err("rom_bsl: bad ack character: %x\n", reply); + return -1; + } + + return 0; +} + +static int rom_bsl_sync(struct rom_bsl_device *dev) +{ + static const uint8_t c = DATA_HDR; + int tries = 2; + + if (sport_flush(dev->fd) < 0) { + pr_error("rom_bsl: tcflush"); + return -1; + } + + while (tries--) { + if (sport_write_all(dev->fd, &c, 1) < 0) { + pr_error("rom_bsl: write error"); + continue; + } + + if (!rom_bsl_ack(dev)) + return 0; + } + + printc_err("rom_bsl: sync failed\n"); + return -1; +} + +static int send_command(struct rom_bsl_device *dev, + int code, uint16_t addr, + const uint8_t *data, int len) +{ + uint8_t pktbuf[256]; + uint8_t cklow = 0xff; + uint8_t ckhigh = 0xff; + int pktlen = data ? len + 4 : 4; + int i; + + if (pktlen + 6 > sizeof(pktbuf)) { + printc_err("rom_bsl: payload too large: %d\n", len); + return -1; + } + + pktbuf[0] = DATA_HDR; + pktbuf[1] = code; + pktbuf[2] = pktlen; + pktbuf[3] = pktlen; + pktbuf[4] = addr & 0xff; + pktbuf[5] = addr >> 8; + pktbuf[6] = len & 0xff; + pktbuf[7] = len >> 8; + + if (data) + memcpy(pktbuf + 8, data, len); + + for (i = 0; i < pktlen + 4; i += 2) + cklow ^= pktbuf[i]; + for (i = 1; i < pktlen + 4; i += 2) + ckhigh ^= pktbuf[i]; + + pktbuf[pktlen + 4] = cklow; + pktbuf[pktlen + 5] = ckhigh; + +#ifdef DEBUG_ROM_BSL + debug_hexdump("Send", pktbuf, pktlen + 6); +#endif + + if (sport_write_all(dev->fd, pktbuf, pktlen + 6) < 0) { + pr_error("rom_bsl: write error"); + return -1; + } + + return 0; +} + +static int verify_checksum(struct rom_bsl_device *dev) +{ + uint8_t cklow = 0xff; + uint8_t ckhigh = 0xff; + int i; + + for (i = 0; i < dev->reply_len; i += 2) + cklow ^= dev->reply_buf[i]; + for (i = 1; i < dev->reply_len; i += 2) + ckhigh ^= dev->reply_buf[i]; + + if (cklow || ckhigh) { + printc_err("rom_bsl: checksum invalid (%02x %02x)\n", + cklow, ckhigh); + return -1; + } + + return 0; +} + +static int fetch_reply(struct rom_bsl_device *dev) +{ + dev->reply_len = 0; + + for (;;) { + int r = sport_read(dev->fd, + dev->reply_buf + dev->reply_len, + sizeof(dev->reply_buf) - dev->reply_len); + + if (!r) { + printc_err("rom_bsl: read timeout\n"); + return -1; + } + + if (r < 0) { + pr_error("rom_bsl: read error"); + return -1; + } + +#ifdef DEBUG_ROM_BSL + debug_hexdump("Receive", dev->reply_buf + dev->reply_len, r); +#endif + dev->reply_len += r; + + if (dev->reply_buf[0] == DATA_ACK) { + return 0; + } else if (dev->reply_buf[0] == DATA_HDR) { + if (dev->reply_len >= 6 && + dev->reply_len == dev->reply_buf[2] + 6) + return verify_checksum(dev); + } else if (dev->reply_buf[0] == DATA_NAK) { + printc_err("rom_bsl: received NAK\n"); + return -1; + } else { + printc_err("rom_bsl: unknown reply type: %02x\n", + dev->reply_buf[0]); + return -1; + } + + if (dev->reply_len >= sizeof(dev->reply_buf)) { + printc_err("rom_bsl: reply buffer overflow\n"); + return -1; + } + } +} + +static int rom_bsl_xfer(struct rom_bsl_device *dev, + int command_code, uint16_t addr, const uint8_t *txdata, + int len) +{ + if (rom_bsl_sync(dev) < 0 || + send_command(dev, command_code, addr, txdata, len) < 0 || + fetch_reply(dev) < 0) { + printc_err("rom_bsl: failed on command 0x%02x " + "(addr = 0x%04x, len = 0x%04x)\n", + command_code, addr, len); + return -1; + } + + return 0; +} + +#define CMD_MASS_ERASE 0x18 +#define CMD_ERASE_SEGMENT 0x16 +#define CMD_TX_DATA 0x14 +#define CMD_RX_DATA 0x12 +#define CMD_TX_VERSION 0x1e +#define CMD_RX_PASSWORD 0x10 + +static void rom_bsl_destroy(device_t dev_base) +{ + struct rom_bsl_device *dev = (struct rom_bsl_device *)dev_base; + + if (bsllib_seq_do(dev->fd, bsllib_seq_next(dev->seq)) < 0) + pr_error("warning: rom_bsl: exit sequence failed"); + + sport_close(dev->fd); + free(dev); +} + +static int rom_bsl_ctl(device_t dev_base, device_ctl_t type) +{ + (void)dev_base; + + switch (type) { + case DEVICE_CTL_HALT: + /* Ignore halt requests */ + return 0; + + case DEVICE_CTL_RESET: + /* Ignore reset requests */ + return 0; + + default: + printc_err("rom_bsl: CPU control is not possible\n"); + } + + return -1; +} + +static device_status_t rom_bsl_poll(device_t dev_base) +{ + (void)dev_base; + + return DEVICE_STATUS_HALTED; +} + +static int rom_bsl_getregs(device_t dev_base, address_t *regs) +{ + (void)dev_base; + (void)regs; + + printc_err("rom_bsl: register fetch is not implemented\n"); + return -1; +} + +static int rom_bsl_setregs(device_t dev_base, const address_t *regs) +{ + (void)dev_base; + (void)regs; + + printc_err("rom_bsl: register store is not implemented\n"); + return -1; +} + +static int rom_bsl_writemem(device_t dev_base, + address_t addr, const uint8_t *mem, address_t len) +{ + struct rom_bsl_device *dev = (struct rom_bsl_device *)dev_base; + + if (addr >= 0x10000 || len > 0x10000 || addr + len > 0x10000) { + printc_err("rom_bsl: memory write out of range\n"); + return -1; + } + + while (len) { + int wlen = len > 100 ? 100 : len; + int r; + + r = rom_bsl_xfer(dev, CMD_RX_DATA, addr, mem, wlen); + + if (r < 0) { + printc_err("rom_bsl: failed to write to 0x%04x\n", + addr); + return -1; + } + + mem += wlen; + len -= wlen; + addr += wlen; + } + + return 0; +} + +static int rom_bsl_readmem(device_t dev_base, + address_t addr, uint8_t *mem, address_t len) +{ + struct rom_bsl_device *dev = (struct rom_bsl_device *)dev_base; + + if (addr >= 0x10000 || len > 0x10000 || addr + len > 0x10000) { + printc_err("rom_bsl: memory read out of range\n"); + return -1; + } + + while (len) { + address_t count = len; + + if (count > 128) + count = 128; + + if (rom_bsl_xfer(dev, CMD_TX_DATA, addr, NULL, count) < 0) { + printc_err("rom_bsl: failed to read memory\n"); + return -1; + } + + if (count > dev->reply_buf[2]) + count = dev->reply_buf[2]; + + memcpy(mem, dev->reply_buf + 4, count); + mem += count; + len -= count; + addr += count; + } + + return 0; +} + +static int rom_bsl_erase(device_t dev_base, device_erase_type_t type, + address_t addr) +{ + struct rom_bsl_device *dev = (struct rom_bsl_device *)dev_base; + + (void)addr; + + switch (type) { + case DEVICE_ERASE_MAIN: + return rom_bsl_xfer(dev, CMD_ERASE_SEGMENT, + 0xfffe, NULL, 0xa504); + + case DEVICE_ERASE_SEGMENT: + return rom_bsl_xfer(dev, CMD_ERASE_SEGMENT, + addr, NULL, 0xa502); + + case DEVICE_ERASE_ALL: + return rom_bsl_xfer(dev, CMD_MASS_ERASE, + 0xfffe, NULL, 0xa506); + } + + return 0; +} + +static int unlock_device(struct rom_bsl_device *dev) +{ + const static uint8_t password[32] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }; + + printc_dbg("Performing mass erase...\n"); + + if (rom_bsl_xfer(dev, CMD_MASS_ERASE, 0xfffe, NULL, 0xa506) < 0) { + printc_err("rom_bsl: initial mass erase failed\n"); + return -1; + } + + printc_dbg("Sending password...\n"); + + if (rom_bsl_xfer(dev, CMD_RX_PASSWORD, 0, + password, sizeof(password)) < 0) { + printc_err("rom_bsl: RX password failed\n"); + return -1; + } + + return 0; +} + +static device_t rom_bsl_open(const struct device_args *args) +{ + struct rom_bsl_device *dev; + + if (!(args->flags & DEVICE_FLAG_TTY)) { + printc_err("rom_bsl: raw USB access is not supported"); + return NULL; + } + + dev = malloc(sizeof(*dev)); + if (!dev) { + pr_error("rom_bsl: can't allocate memory"); + return NULL; + } + + memset(dev, 0, sizeof(*dev)); + + dev->base.type = &device_rom_bsl; + + dev->fd = sport_open(args->path, 9600, SPORT_EVEN_PARITY); + if (SPORT_ISERR(dev->fd)) { + pr_error("sport_open"); + free(dev); + return NULL; + } + + dev->seq = args->bsl_entry_seq; + if (!dev->seq) + dev->seq = "DR,r,R,r,d,R:DR,r"; + + if (bsllib_seq_do(dev->fd, dev->seq) < 0) { + pr_error("rom_bsl: entry sequence failed"); + goto fail; + } + + delay_ms(500); + + /* Show BSL version */ + if (rom_bsl_xfer(dev, CMD_TX_VERSION, 0, NULL, 0) < 0) + printc_err("warning: rom_bsl: failed to read version\n"); + else if (dev->reply_len < 19) + printc_err("warning: rom_bsl: short reply\n"); + else + printc_dbg("BSL version is %x.%02x\n", + dev->reply_buf[15], + dev->reply_buf[16]); + + if (unlock_device(dev) < 0) { + printc_err("rom_bsl: failed to unlock\n"); + goto fail; + } + + return (device_t)dev; + + fail: + sport_close(dev->fd); + free(dev); + return NULL; +} + +const struct device_class device_rom_bsl = { + .name = "rom-bsl", + .help = "ROM bootstrap loader", + .open = rom_bsl_open, + .destroy = rom_bsl_destroy, + .readmem = rom_bsl_readmem, + .writemem = rom_bsl_writemem, + .erase = rom_bsl_erase, + .getregs = rom_bsl_getregs, + .setregs = rom_bsl_setregs, + .ctl = rom_bsl_ctl, + .poll = rom_bsl_poll +}; diff --git a/drivers/rom_bsl.h b/drivers/rom_bsl.h new file mode 100644 index 0000000..cf01192 --- /dev/null +++ b/drivers/rom_bsl.h @@ -0,0 +1,27 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2009-2014 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 ROM_BSL_H_ +#define ROM_BSL_H_ + +#include "device.h" + +/* MSP430 FET Bootloader implementation. */ +extern const struct device_class device_rom_bsl; + +#endif diff --git a/mspdebug.man b/mspdebug.man index 9c42bfa..ac603ca 100644 --- a/mspdebug.man +++ b/mspdebug.man @@ -98,7 +98,8 @@ accompanying the source release for more information on embedded mode. Specify a BSL entry sequence. Each character specifies a modem control line transition (R: RTS on, r: RTS off, D: DTR on, d: DTR off). A comma indicates a delay. The entry and exit sequences are separated by a -colon. The default value is \fBdR,r,R,r,R,D:dR,DR\fR. +colon. The default value is \fBdR,r,R,r,R,D:dR,DR\fR, for the +\fBflash-bsl\fR driver. .SH DRIVERS For drivers supporting both USB and tty access, USB is the default, unless specified otherwise (see \fB-d\fR above). @@ -204,6 +205,15 @@ fuller-featured bootloader into RAM for execution. .IP "\fBezfet\fR" This driver is for Texas Instruments' eZ-FET devices. It supports USB and tty access. It does not support breakpoint control. +.IP "\fBrom-bsl\fR" +This driver is for the old-style (ROM) bootstrap loader. It supports tty +access only. Entry is attempted via the RTS/DTR signals. The default +sequence is \fBDR,r,R,r,d,R:DR,r\fR, but you can override this with the +\fB\-\-bsl\-entry\-sequence\fR option. + +\fBWARNING:\fR this driver unlocks the BSL by performing a mass erase. +There are reports of this operation causing an erase of info A in some +devices. Use at your own risk. .SH COMMANDS MSPDebug can accept commands either through an interactive prompt, or non-interactively when specified on the command line. The supported diff --git a/ui/main.c b/ui/main.c index d3d8eb0..5bd8483 100644 --- a/ui/main.c +++ b/ui/main.c @@ -56,6 +56,7 @@ #include "pif.h" #include "loadbsl.h" #include "fet3.h" +#include "rom_bsl.h" #include "chipinfo.h" #ifdef __CYGWIN__ @@ -87,7 +88,8 @@ static const struct device_class *const driver_table[] = { &device_goodfet, &device_pif, &device_loadbsl, - &device_ezfet + &device_ezfet, + &device_rom_bsl }; static const char *version_text =