diff --git a/bsl.c b/bsl.c index ee3adc3..6cf7100 100644 --- a/bsl.c +++ b/bsl.c @@ -212,20 +212,9 @@ static void bsl_destroy(device_t dev_base) free(dev); } -static int bsl_erase(struct bsl_device *dev) -{ - /* Constants found from viewing gdbproxy's activities */ - return bsl_xfer(dev, CMD_ERASE, 0x2500, NULL, 0x0069); -} - static int bsl_ctl(device_t dev_base, device_ctl_t type) { - struct bsl_device *dev = (struct bsl_device *)dev_base; - switch (type) { - case DEVICE_CTL_ERASE: - return bsl_erase(dev); - case DEVICE_CTL_HALT: /* Ignore halt requests */ return 0; @@ -320,6 +309,20 @@ static int bsl_readmem(device_t dev_base, return 0; } +static int bsl_erase(device_t dev_base, device_erase_type_t type, + address_t addr) +{ + struct bsl_device *dev = (struct bsl_device *)dev_base; + + if (type != DEVICE_ERASE_MAIN) { + printc_err("bsl: only main erase is supported\n"); + return -1; + } + + /* Constants found from viewing gdbproxy's activities */ + return bsl_xfer(dev, CMD_ERASE, 0x2500, NULL, 0x0069); +} + static int enter_via_fet(struct bsl_device *dev) { uint8_t buf[16]; @@ -372,6 +375,7 @@ device_t bsl_open(const char *device) dev->base.destroy = bsl_destroy; dev->base.readmem = bsl_readmem; dev->base.writemem = bsl_writemem; + dev->base.erase = bsl_erase; dev->base.getregs = bsl_getregs; dev->base.setregs = bsl_setregs; dev->base.ctl = bsl_ctl; diff --git a/cmddb.c b/cmddb.c index 37b378c..d2630f0 100644 --- a/cmddb.c +++ b/cmddb.c @@ -124,8 +124,10 @@ const struct cmddb_record commands[] = { .name = "erase", .func = cmd_erase, .help = -"erase\n" -" Erase the device under test.\n" +"erase [all|segment] [address]\n" +" Erase the device under test. With no arguments, erases all of main\n" +" memory. Specify arguments to perform a mass erase, or to erase\n" +" individual segments.\n" }, { .name = "step", @@ -224,7 +226,14 @@ const struct cmddb_record commands[] = { "cgraph
[function]\n" " Analyse the range given and produce a call graph. Displays a summary\n" " of all functions if no function address is given.\n" - } + }, + { + .name = "locka", + .func = cmd_locka, + .help = +"locka [set|clear]\n" +" Show or change the status of the LOCKA flash write-protect bit.\n" + }, }; int cmddb_get(const char *name, struct cmddb_record *ret) diff --git a/devcmd.c b/devcmd.c index 24788c3..f24680f 100644 --- a/devcmd.c +++ b/devcmd.c @@ -138,11 +138,38 @@ int cmd_reset(char **arg) int cmd_erase(char **arg) { + const char *type_text = get_arg(arg); + const char *seg_text = get_arg(arg); + device_erase_type_t type = DEVICE_ERASE_MAIN; + address_t segment = 0; + + if (seg_text && expr_eval(stab_default, seg_text, &segment) < 0) { + printc_err("erase: invalid expression: %s\n", seg_text); + return -1; + } + + if (type_text) { + if (!strcasecmp(type_text, "all")) { + type = DEVICE_ERASE_ALL; + } else if (!strcasecmp(type_text, "segment")) { + type = DEVICE_ERASE_SEGMENT; + if (!seg_text) { + printc_err("erase: expected segment " + "address\n"); + return -1; + } + } else { + printc_err("erase: unknown erase type: %s\n", + type_text); + return -1; + } + } + if (device_default->ctl(device_default, DEVICE_CTL_HALT) < 0) return -1; printc("Erasing...\n"); - return device_default->ctl(device_default, DEVICE_CTL_ERASE); + return device_default->erase(device_default, type, segment); } int cmd_step(char **arg) @@ -604,3 +631,59 @@ int cmd_break(char **arg) return 0; } + +#define FCTL3 0x012c +#define FCTL3_LOCKA 0x40 +#define FWKEY 0xa5 +#define FRKEY 0x96 + +int cmd_locka(char **arg) +{ + const char *status = get_arg(arg); + int value = 0; + uint8_t regval[2]; + + if (status) { + if (!strcasecmp(status, "set")) { + value = FCTL3_LOCKA; + } else if (!strcasecmp(status, "clear")) { + value = 0; + } else { + printc_err("locka: unknown action: %s\n", status); + return -1; + } + } + + if (device_default->readmem(device_default, FCTL3, regval, 2) < 0) { + printc_err("locka: can't read FCTL3 register\n"); + return -1; + } + + if (regval[1] != FRKEY) { + printc_err("warning: locka: read key invalid (got 0x%02x)\n", + regval[1]); + return -1; + } + + if (status && ((regval[0] & FCTL3_LOCKA) != value)) { + printc_dbg("Toggling LOCKA bit\n"); + + regval[0] |= FCTL3_LOCKA; + regval[1] = FWKEY; + + if (device_default->writemem(device_default, FCTL3, + regval, 2) < 0) { + printc_err("locka: can't write FCTL3 register\n"); + return -1; + } + + if (device_default->readmem(device_default, FCTL3, + regval, 2) < 0) { + printc_err("locka: can't read FCTL3 register\n"); + return -1; + } + } + + printc("LOCKA is %s\n", (regval[0] & FCTL3_LOCKA) ? "set" : "clear"); + return 0; +} diff --git a/devcmd.h b/devcmd.h index 7b5bbbc..fa20af9 100644 --- a/devcmd.h +++ b/devcmd.h @@ -34,5 +34,6 @@ int cmd_load(char **arg); int cmd_setbreak(char **arg); int cmd_delbreak(char **arg); int cmd_break(char **arg); +int cmd_locka(char **arg); #endif diff --git a/device.h b/device.h index fed2b2f..be0a890 100644 --- a/device.h +++ b/device.h @@ -31,8 +31,7 @@ typedef enum { DEVICE_CTL_RESET, DEVICE_CTL_RUN, DEVICE_CTL_HALT, - DEVICE_CTL_STEP, - DEVICE_CTL_ERASE + DEVICE_CTL_STEP } device_ctl_t; typedef enum { @@ -42,6 +41,12 @@ typedef enum { DEVICE_STATUS_ERROR } device_status_t; +typedef enum { + DEVICE_ERASE_ALL, + DEVICE_ERASE_MAIN, + DEVICE_ERASE_SEGMENT +} device_erase_type_t; + #define DEVICE_NUM_REGS 16 #define DEVICE_MAX_BREAKPOINTS 32 @@ -71,6 +76,10 @@ struct device { int (*writemem)(device_t dev, address_t addr, const uint8_t *mem, address_t len); + /* Erase memory */ + int (*erase)(device_t dev, device_erase_type_t type, + address_t address); + /* Read/write registers */ int (*getregs)(device_t dev, address_t *regs); int (*setregs)(device_t dev, const address_t *regs); diff --git a/fet.c b/fet.c index 0f0a245..c3e869f 100644 --- a/fet.c +++ b/fet.c @@ -575,12 +575,11 @@ static int do_run(struct fet_device *dev, int type) return 0; } -static int do_erase(struct fet_device *dev) +static int fet_erase(device_t dev_base, device_erase_type_t type, + address_t addr) { - if (xfer(dev, C_RESET, NULL, 0, 3, FET_RESET_ALL, 0, 0) < 0) { - printc_err("fet: reset before erase failed\n"); - return -1; - } + struct fet_device *dev = (struct fet_device *)dev_base; + int fet_erase_type = FET_ERASE_MAIN; if (xfer(dev, C_CONFIGURE, NULL, 0, 2, FET_CONFIG_CLKCTRL, 0x26) < 0) { printc_err("fet: config (1) failed\n"); @@ -592,8 +591,27 @@ static int do_erase(struct fet_device *dev) return -1; } - if (xfer(dev, C_ERASE, NULL, 0, 3, FET_ERASE_MAIN, - dev->code_start, 0) < 0) { + switch (type) { + case DEVICE_ERASE_MAIN: + fet_erase_type = FET_ERASE_MAIN; + addr = dev->code_start; + break; + + case DEVICE_ERASE_SEGMENT: + fet_erase_type = FET_ERASE_SEGMENT; + break; + + case DEVICE_ERASE_ALL: + fet_erase_type = FET_ERASE_ALL; + addr = dev->code_start; + break; + + default: + printc_err("fet: unsupported erase type\n"); + return -1; + } + + if (xfer(dev, C_ERASE, NULL, 0, 3, fet_erase_type, addr, 0) < 0) { printc_err("fet: erase command failed\n"); return -1; } @@ -686,9 +704,6 @@ static int fet_ctl(device_t dev_base, device_ctl_t action) break; } break; - - case DEVICE_CTL_ERASE: - return do_erase(dev); } return 0; @@ -921,6 +936,7 @@ device_t fet_open(transport_t transport, int proto_flags, int vcc_mv, dev->base.setregs = fet_setregs; dev->base.ctl = fet_ctl; dev->base.poll = fet_poll; + dev->base.erase = fet_erase; dev->transport = transport; dev->proto_flags = proto_flags; diff --git a/flash_bsl.c b/flash_bsl.c index 71b9efe..90d0257 100644 --- a/flash_bsl.c +++ b/flash_bsl.c @@ -327,12 +327,19 @@ static int flash_bsl_readmem(device_t dev_base, return 0; } -static int flash_bsl_erase(struct flash_bsl_device *dev) +static int flash_bsl_erase(device_t dev_base, device_erase_type_t type, + address_t addr) { + struct flash_bsl_device *dev = (struct flash_bsl_device *)dev_base; const uint8_t mass_erase_cmd[] = { MASS_ERASE }; uint8_t response_buffer[16]; int ret; + if (type != DEVICE_ERASE_ALL) { + printc_err("flash_bsl_erase: only mass erase is supported\n"); + return -1; + } + if (flash_bsl_send(dev, mass_erase_cmd, sizeof(mass_erase_cmd)) < 0) { printc_err("flash_bsl_erase: failed to send erase command\n"); return -1; @@ -380,7 +387,7 @@ static int flash_bsl_unlock(struct flash_bsl_device *dev) int ret; /* mass erase (this might wipe Information Memory on some devices */ - if (flash_bsl_erase(dev) < 0) { + if (flash_bsl_erase((device_t)dev, DEVICE_ERASE_ALL, 0) < 0) { printc_err("flash_bsl_unlock: warning: erase failed\n"); } @@ -419,12 +426,7 @@ static int flash_bsl_unlock(struct flash_bsl_device *dev) static int flash_bsl_ctl(device_t dev_base, device_ctl_t type) { - struct flash_bsl_device *dev = (struct flash_bsl_device *)dev_base; - switch (type) { - case DEVICE_CTL_ERASE: - return flash_bsl_erase(dev); - case DEVICE_CTL_HALT: /* Ignore halt requests */ return 0; @@ -628,6 +630,7 @@ device_t flash_bsl_open(const char *device, int long_password) dev->base.setregs = flash_bsl_setregs; dev->base.ctl = flash_bsl_ctl; dev->base.poll = flash_bsl_poll; + dev->base.erase = flash_bsl_erase; dev->serial_fd = open_serial_even_parity(device, B9600); if (dev->serial_fd < 0) { diff --git a/mspdebug.man b/mspdebug.man index dce16a5..64fccff 100644 --- a/mspdebug.man +++ b/mspdebug.man @@ -166,9 +166,14 @@ length (64 bytes) is disassembled and shown. If symbols are available, then all addresses used as operands are translated into \fIsymbol\fR+\fIoffset\fR form. -.IP "\fBerase\fR" -Erase the device under test. All code memory is erased (but not -information or boot memory). +.IP "\fBerase\fR [\fBall\fR|\fBsegment\fR] [\fIaddress\fR]" +Erase the device under test. With no arguments, all code memory is erased +(but not information or boot memory). With the argument "all", a mass +erase is performed (the results may depend on the state of the LOCKA +bit in the flash memory controller). + +Specify "segment" and a memory address to erase an individual flash +segment. .IP "\fBgdb\fR [\fIport\fR]" Start a GDB remote stub, optionally specifying a TCP port to listen on. If no port is given, the default port is 2000. @@ -265,6 +270,17 @@ command is like \fBprog\fR, but it does not load symbols or erase the device before programming. The CPU is reset and halted before and after programming. +.IP "\fBlocka\fR [\fBset\fR|\fBclear\fR]" +Show or change the status of the LOCKA bit in the chip's memory +controller. The LOCKA bit is set on POR and acts as a write-protect bit +for info segment A. This segment contains factory-configured calibration +data, and under normal circumstances, should not be changed. + +If the LOCKA bit is cleared, erasing the info A segment is possible. + +The LOCKA bit also affects the behaviour of the "erase all" command. If +LOCKA is set (the default), only main memory is erased. If LOCKA is +cleared, main and information memory are both erased. .IP "\fBmd\fR \fIaddress\fR [\fIlength\fR]" Read the specified section of device memory and display it as a canonical\-style hexdump. Both arguments may be address expressions. If diff --git a/prog.c b/prog.c index 1b7e577..69fddfe 100644 --- a/prog.c +++ b/prog.c @@ -34,8 +34,8 @@ int prog_flush(struct prog_data *prog) if (!prog->have_erased && (prog->flags & PROG_WANT_ERASE)) { printc("Erasing...\n"); - if (device_default->ctl(device_default, - DEVICE_CTL_ERASE) < 0) + if (device_default->erase(device_default, + DEVICE_ERASE_MAIN, 0) < 0) return -1; prog->have_erased = 1; diff --git a/sim.c b/sim.c index 5fd00b8..7d594e0 100644 --- a/sim.c +++ b/sim.c @@ -504,10 +504,6 @@ static int sim_ctl(device_t dev_base, device_ctl_t op) dev->regs[MSP430_REG_PC] = MEM_GETW(dev, 0xfffe); return 0; - case DEVICE_CTL_ERASE: - memset(dev->memory, 0xff, MEM_SIZE); - return 0; - case DEVICE_CTL_HALT: dev->running = 0; return 0; @@ -523,6 +519,30 @@ static int sim_ctl(device_t dev_base, device_ctl_t op) return 0; } +static int sim_erase(device_t dev_base, device_erase_type_t type, + address_t addr) +{ + struct sim_device *dev = (struct sim_device *)dev_base; + + switch (type) { + case DEVICE_ERASE_MAIN: + memset(dev->memory + 0x2000, 0xff, MEM_SIZE - 0x2000); + break; + + case DEVICE_ERASE_ALL: + memset(dev->memory, 0xff, MEM_SIZE); + break; + + case DEVICE_ERASE_SEGMENT: + addr &= ~0x3f; + addr &= (MEM_SIZE - 1); + memset(dev->memory + addr, 0xff, 64); + break; + } + + return 0; +} + static device_status_t sim_poll(device_t dev_base) { struct sim_device *dev = (struct sim_device *)dev_base; @@ -583,6 +603,7 @@ device_t sim_open(sim_fetch_func_t fetch_func, dev->base.destroy = sim_destroy; dev->base.readmem = sim_readmem; dev->base.writemem = sim_writemem; + dev->base.erase = sim_erase; dev->base.getregs = sim_getregs; dev->base.setregs = sim_setregs; dev->base.ctl = sim_ctl;