From: Karl Palsson Date: Thu, 6 Oct 2011 23:22:33 +0000 (+0000) Subject: Initial commit of workable stm32l debug X-Git-Url: https://git.gag.com/?a=commitdiff_plain;h=d060c3c040681c13e95d6b63982ccd16eeb83800;p=fw%2Fstlink Initial commit of workable stm32l debug Move towards a standard libstlink, with backends for libusb (stm32l discovery) and scsi passthrough (stm32vl discovery) and a common front end. Verified that stm32l branch works much the same, but more to go for stm32vl. --- diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4cf2775 --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ + +VPATH=src + +SOURCES_LIB=stlink-common.c stlink-usb.c #stlink-sg.c +OBJS_LIB=$(SOURCES_LIB:.c=.o) + +CFLAGS+=-DCONFIG_USE_LIBUSB +#CFLAGS+=-DCONFIG_USE_LIBSG +CFLAGS+= -std=gnu99 +CFLAGS+=-Wall -Wextra + +LDFLAGS=-lstlink -lusb-1.0 -L. + +LIBRARY=libstlink.a + +all: $(LIBRARY) test_usb #test_sg + +$(LIBRARY): $(OBJS_LIB) + @echo "objs are $(OBJS_LIB)" + $(AR) -cr $@ $^ + @echo "done making library" + + +test_sg: test_sg.o $(LIBRARY) + @echo "building test_sg" + $(CC) $(LDFLAGS) -o $@ + +test_usb: test_usb.o $(LIBRARY) + @echo "building test_usb" + $(CC) test_usb.o $(LDFLAGS) -o $@ + @echo "done linking" + +%.o: %.c + @echo "building $^ into $@" + $(CC) $(CFLAGS) -c $^ -o $@ + @echo "done compiling" + +clean: + rm -rf $(OBJS_LIB) + rm -rf $(LIBRARY) + rm -rf test_usb* + rm -rf test_sg* + +.PHONY: clean all diff --git a/build/Makefile b/build/Makefile deleted file mode 100644 index c50747f..0000000 --- a/build/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -PRG := st-util -DEBUG := #-DDEBUG - -all: $(PRG) - -LIBS := \ - -lsgutils2 - -OBJS += \ - stlink-hw.o gdb-remote.o gdb-server.o - -$(PRG): $(OBJS) - gcc -o $(PRG) $(OBJS) $(LIBS) - -%.o: ../src/%.c - gcc -O3 -g3 -Wall -Werror -c -std=gnu99 -MMD -MP \ - -fno-strict-aliasing -Wno-unused $(DEBUG) \ - -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)"\ - -o "$@" "$<" - -clean: - @rm -vf *.d *.o $(PRG) diff --git a/gdbserver/gdb-remote.c b/gdbserver/gdb-remote.c new file mode 100644 index 0000000..f6bf02d --- /dev/null +++ b/gdbserver/gdb-remote.c @@ -0,0 +1,156 @@ +/* -*- tab-width:8 -*- */ + +/* + Copyright (C) 2011 Peter Zotov + Use of this source code is governed by a BSD-style + license that can be found in the LICENSE file. +*/ + +#include +#include +#include +#include +#include +#include + +static const char hex[] = "0123456789abcdef"; + +int gdb_send_packet(int fd, char* data) { + unsigned length = strlen(data) + 5; + char* packet = malloc(length); /* '$' data (hex) '#' cksum (hex) */ + + memset(packet, 0, length); + + packet[0] = '$'; + + uint8_t cksum = 0; + for(int i = 0; i < strlen(data); i++) { + packet[i + 1] = data[i]; + cksum += data[i]; + } + + packet[length - 4] = '#'; + packet[length - 3] = hex[cksum >> 4]; + packet[length - 2] = hex[cksum & 0xf]; + + while(1) { + if(write(fd, packet, length) != length) { + free(packet); + return -2; + } + + char ack; + if(read(fd, &ack, 1) != 1) { + free(packet); + return -2; + } + + if(ack == '+') { + free(packet); + return 0; + } + } +} + +#define ALLOC_STEP 1024 + +int gdb_recv_packet(int fd, char** buffer) { + unsigned packet_size = ALLOC_STEP + 1, packet_idx = 0; + uint8_t cksum = 0; + char recv_cksum[3] = {0}; + char* packet_buffer = malloc(packet_size); + unsigned state; + +start: + state = 0; + /* + * 0: waiting $ + * 1: data, waiting # + * 2: cksum 1 + * 3: cksum 2 + * 4: fin + */ + + char c; + while(state != 4) { + if(read(fd, &c, 1) != 1) { + return -2; + } + + switch(state) { + case 0: + if(c != '$') { + // ignore + } else { + state = 1; + } + break; + + case 1: + if(c == '#') { + state = 2; + } else { + packet_buffer[packet_idx++] = c; + cksum += c; + + if(packet_idx == packet_size) { + packet_size += ALLOC_STEP; + packet_buffer = realloc(packet_buffer, packet_size); + } + } + break; + + case 2: + recv_cksum[0] = c; + state = 3; + break; + + case 3: + recv_cksum[1] = c; + state = 4; + break; + } + } + + uint8_t recv_cksum_int = strtoul(recv_cksum, NULL, 16); + if(recv_cksum_int != cksum) { + char nack = '-'; + if(write(fd, &nack, 1) != 1) { + return -2; + } + + goto start; + } else { + char ack = '+'; + if(write(fd, &ack, 1) != 1) { + return -2; + } + } + + packet_buffer[packet_idx] = 0; + *buffer = packet_buffer; + + return packet_idx; +} + +// Here we skip any characters which are not \x03, GDB interrupt. +// As we use the mode with ACK, in a (very unlikely) situation of a packet +// lost because of this skipping, it will be resent anyway. +int gdb_check_for_interrupt(int fd) { + struct pollfd pfd; + pfd.fd = fd; + pfd.events = POLLIN; + + if(poll(&pfd, 1, 0) != 0) { + char c; + + if(read(fd, &c, 1) != 1) + return -2; + + if(c == '\x03') // ^C + return 1; + } + + return 0; +} + diff --git a/gdbserver/gdb-remote.h b/gdbserver/gdb-remote.h new file mode 100644 index 0000000..bfa0104 --- /dev/null +++ b/gdbserver/gdb-remote.h @@ -0,0 +1,8 @@ +#ifndef _GDB_REMOTE_H_ +#define _GDB_REMOTE_H_ + +int gdb_send_packet(int fd, char* data); +int gdb_recv_packet(int fd, char** buffer); +int gdb_check_for_interrupt(int fd); + +#endif diff --git a/gdbserver/gdb-server.c b/gdbserver/gdb-server.c new file mode 100644 index 0000000..04fd2bc --- /dev/null +++ b/gdbserver/gdb-server.c @@ -0,0 +1,949 @@ +/* -*- tab-width:8 -*- */ + +/* + Copyright (C) 2011 Peter Zotov + Use of this source code is governed by a BSD-style + license that can be found in the LICENSE file. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "gdb-remote.h" +#include "stlink-hw.h" + +#define FLASH_BASE 0x08000000 +#define FLASH_PAGE (sl->flash_pgsz) +#define FLASH_PAGE_MASK (~((1 << 10) - 1)) +#define FLASH_SIZE (FLASH_PAGE * 128) + +static const char hex[] = "0123456789abcdef"; + +static const char* current_memory_map = NULL; + +struct chip_params { + uint32_t chip_id; + char* description; + uint32_t max_flash_size, flash_pagesize; + uint32_t sram_size; + uint32_t bootrom_base, bootrom_size; +} const devices[] = { + { 0x412, "Low-density device", + 0x8000, 0x400, 0x2800, 0x1ffff000, 0x800 }, + { 0x410, "Medium-density device", + 0x20000, 0x400, 0x5000, 0x1ffff000, 0x800 }, + { 0x414, "High-density device", + 0x80000, 0x800, 0x10000, 0x1ffff000, 0x800 }, + { 0x418, "Connectivity line device", + 0x40000, 0x800, 0x10000, 0x1fffb000, 0x4800 }, + { 0x420, "Medium-density value line device", + 0x20000, 0x400, 0x2000, 0x1ffff000, 0x800 }, + { 0x428, "High-density value line device", + 0x80000, 0x800, 0x8000, 0x1ffff000, 0x800 }, + { 0x430, "XL-density device", + 0x100000, 0x800, 0x18000, 0x1fffe000, 0x1800 }, + { 0 } +}; + +int serve(struct stlink* sl, int port); +char* make_memory_map(const struct chip_params *params, uint32_t flash_size); + +int main(int argc, char** argv) { + if(argc != 3) { + fprintf(stderr, "Usage: %s /dev/sgX\n", argv[0]); + return 1; + } + + struct stlink *sl = stlink_quirk_open(argv[2], 0); + if (sl == NULL) + return 1; + + if(stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) + stlink_enter_swd_mode(sl); + + uint32_t chip_id; + + stlink_read_mem32(sl, 0xE0042000, 4); + chip_id = sl->q_buf[0] | (sl->q_buf[1] << 8) | (sl->q_buf[2] << 16) | + (sl->q_buf[3] << 24); + + printf("Chip ID is %08x.\n", chip_id); + + const struct chip_params* params = NULL; + + for(int i = 0; i < sizeof(devices) / sizeof(devices[0]); i++) { + if(devices[i].chip_id == (chip_id & 0xFFF)) { + params = &devices[i]; + break; + } + } + + if(params == NULL) { + fprintf(stderr, "Cannot recognize the connected device!\n"); + return 0; + } + + printf("Device connected: %s\n", params->description); + printf("Device parameters: SRAM: 0x%x bytes, Flash: up to 0x%x bytes in pages of 0x%x bytes\n", + params->sram_size, params->max_flash_size, params->flash_pagesize); + + FLASH_PAGE = params->flash_pagesize; + + uint32_t flash_size; + + stlink_read_mem32(sl, 0x1FFFF7E0, 4); + flash_size = sl->q_buf[0] | (sl->q_buf[1] << 8); + + printf("Flash size is %d KiB.\n", flash_size); + // memory map is in 1k blocks. + current_memory_map = make_memory_map(params, flash_size * 0x400); + + int port = atoi(argv[1]); + + while(serve(sl, port) == 0); + + stlink_close(sl); + + return 0; +} + +static const char* const memory_map_template = + "" + "" + "" + " " // code = sram, bootrom or flash; flash is bigger + " " // sram 8k + " " + " 0x%x" + " " + " " // peripheral regs + " " // cortex regs + " " // bootrom + " " // option byte area + ""; + +char* make_memory_map(const struct chip_params *params, uint32_t flash_size) { + /* This will be freed in serve() */ + char* map = malloc(4096); + map[0] = '\0'; + + snprintf(map, 4096, memory_map_template, + flash_size, + params->sram_size, + flash_size, params->flash_pagesize, + params->bootrom_base, params->bootrom_size); + + return map; +} + + +/* + * DWT_COMP0 0xE0001020 + * DWT_MASK0 0xE0001024 + * DWT_FUNCTION0 0xE0001028 + * DWT_COMP1 0xE0001030 + * DWT_MASK1 0xE0001034 + * DWT_FUNCTION1 0xE0001038 + * DWT_COMP2 0xE0001040 + * DWT_MASK2 0xE0001044 + * DWT_FUNCTION2 0xE0001048 + * DWT_COMP3 0xE0001050 + * DWT_MASK3 0xE0001054 + * DWT_FUNCTION3 0xE0001058 + */ + +#define DATA_WATCH_NUM 4 + +enum watchfun { WATCHDISABLED = 0, WATCHREAD = 5, WATCHWRITE = 6, WATCHACCESS = 7 }; + +struct code_hw_watchpoint { + stm32_addr_t addr; + uint8_t mask; + enum watchfun fun; +}; + +struct code_hw_watchpoint data_watches[DATA_WATCH_NUM]; + +static void init_data_watchpoints(struct stlink *sl) { + int i; + + #ifdef DEBUG + printf("init watchpoints\n"); + #endif + + // set trcena in debug command to turn on dwt unit + stlink_read_mem32(sl, 0xE000EDFC, 4); + sl->q_buf[3] |= 1; + stlink_write_mem32(sl, 0xE000EDFC, 4); + + // make sure all watchpoints are cleared + memset(sl->q_buf, 0, 4); + for(int i = 0; i < DATA_WATCH_NUM; i++) { + data_watches[i].fun = WATCHDISABLED; + stlink_write_mem32(sl, 0xe0001028 + i * 16, 4); + } +} + +static int add_data_watchpoint(struct stlink* sl, enum watchfun wf, stm32_addr_t addr, unsigned int len) +{ + int i = 0; + uint32_t mask, temp; + + // computer mask + // find a free watchpoint + // configure + + mask = -1; + i = len; + while(i) { + i >>= 1; + mask++; + } + + if((mask != -1) && (mask < 16)) { + for(i = 0; i < DATA_WATCH_NUM; i++) { + // is this an empty slot ? + if(data_watches[i].fun == WATCHDISABLED) { + #ifdef DEBUG + printf("insert watchpoint %d addr %x wf %u mask %u len %d\n", i, addr, wf, mask, len); + #endif + + data_watches[i].fun = wf; + data_watches[i].addr = addr; + data_watches[i].mask = mask; + + // insert comparator address + sl->q_buf[0] = (addr & 0xff); + sl->q_buf[1] = ((addr >> 8) & 0xff); + sl->q_buf[2] = ((addr >> 16) & 0xff); + sl->q_buf[3] = ((addr >> 24) & 0xff); + + stlink_write_mem32(sl, 0xE0001020 + i * 16, 4); + + // insert mask + memset(sl->q_buf, 0, 4); + sl->q_buf[0] = mask; + stlink_write_mem32(sl, 0xE0001024 + i * 16, 4); + + // insert function + memset(sl->q_buf, 0, 4); + sl->q_buf[0] = wf; + stlink_write_mem32(sl, 0xE0001028 + i * 16, 4); + + // just to make sure the matched bit is clear ! + stlink_read_mem32(sl, 0xE0001028 + i * 16, 4); + return 0; + } + } + } + + #ifdef DEBUG + printf("failure: add watchpoints addr %x wf %u len %u\n", addr, wf, len); + #endif + return -1; +} + +static int delete_data_watchpoint(struct stlink* sl, stm32_addr_t addr) +{ + int i; + + for(i = 0 ; i < DATA_WATCH_NUM; i++) { + if((data_watches[i].addr == addr) && (data_watches[i].fun != WATCHDISABLED)) { + #ifdef DEBUG + printf("delete watchpoint %d addr %x\n", i, addr); + #endif + + memset(sl->q_buf, 0, 4); + data_watches[i].fun = WATCHDISABLED; + stlink_write_mem32(sl, 0xe0001028 + i * 16, 4); + + return 0; + } + } + + #ifdef DEBUG + printf("failure: delete watchpoint addr %x\n", addr); + #endif + + return -1; +} + +#define CODE_BREAK_NUM 6 +#define CODE_BREAK_LOW 0x01 +#define CODE_BREAK_HIGH 0x02 + +struct code_hw_breakpoint { + stm32_addr_t addr; + int type; +}; + +struct code_hw_breakpoint code_breaks[CODE_BREAK_NUM]; + +static void init_code_breakpoints(struct stlink* sl) { + memset(sl->q_buf, 0, 4); + sl->q_buf[0] = 0x03; // KEY | ENABLE + stlink_write_mem32(sl, 0xe0002000, 4); + + memset(sl->q_buf, 0, 4); + for(int i = 0; i < CODE_BREAK_NUM; i++) { + code_breaks[i].type = 0; + stlink_write_mem32(sl, 0xe0002008 + i * 4, 4); + } +} + +static int update_code_breakpoint(struct stlink* sl, stm32_addr_t addr, int set) { + stm32_addr_t fpb_addr = addr & ~0x3; + int type = addr & 0x2 ? CODE_BREAK_HIGH : CODE_BREAK_LOW; + + if(addr & 1) { + fprintf(stderr, "update_code_breakpoint: unaligned address %08x\n", addr); + return -1; + } + + int id = -1; + for(int i = 0; i < CODE_BREAK_NUM; i++) { + if(fpb_addr == code_breaks[i].addr || + (set && code_breaks[i].type == 0)) { + id = i; + break; + } + } + + if(id == -1) { + if(set) return -1; // Free slot not found + else return 0; // Breakpoint is already removed + } + + struct code_hw_breakpoint* brk = &code_breaks[id]; + + brk->addr = fpb_addr; + + if(set) brk->type |= type; + else brk->type &= ~type; + + memset(sl->q_buf, 0, 4); + + if(brk->type == 0) { + #ifdef DEBUG + printf("clearing hw break %d\n", id); + #endif + + stlink_write_mem32(sl, 0xe0002008 + id * 4, 4); + } else { + sl->q_buf[0] = ( brk->addr & 0xff) | 1; + sl->q_buf[1] = ((brk->addr >> 8) & 0xff); + sl->q_buf[2] = ((brk->addr >> 16) & 0xff); + sl->q_buf[3] = ((brk->addr >> 24) & 0xff) | (brk->type << 6); + + #ifdef DEBUG + printf("setting hw break %d at %08x (%d)\n", + id, brk->addr, brk->type); + printf("reg %02x %02x %02x %02x\n", + sl->q_buf[3], sl->q_buf[2], sl->q_buf[1], sl->q_buf[0]); + #endif + + stlink_write_mem32(sl, 0xe0002008 + id * 4, 4); + } + + return 0; +} + + +struct flash_block { + stm32_addr_t addr; + unsigned length; + uint8_t* data; + + struct flash_block* next; +}; + +static struct flash_block* flash_root; + +static int flash_add_block(stm32_addr_t addr, unsigned length, + struct stlink* sl) { + if(addr < FLASH_BASE || addr + length > FLASH_BASE + FLASH_SIZE) { + fprintf(stderr, "flash_add_block: incorrect bounds\n"); + return -1; + } + + if(addr % FLASH_PAGE != 0 || length % FLASH_PAGE != 0) { + fprintf(stderr, "flash_add_block: unaligned block\n"); + return -1; + } + + struct flash_block* new = malloc(sizeof(struct flash_block)); + new->next = flash_root; + + new->addr = addr; + new->length = length; + new->data = calloc(length, 1); + + flash_root = new; + + return 0; +} + +static int flash_populate(stm32_addr_t addr, uint8_t* data, unsigned length) { + int fit_blocks = 0, fit_length = 0; + + for(struct flash_block* fb = flash_root; fb; fb = fb->next) { + /* Block: ------X------Y-------- + * Data: a-----b + * a--b + * a-----------b + * Block intersects with data, if: + * a < Y && b > x + */ + + unsigned X = fb->addr, Y = fb->addr + fb->length; + unsigned a = addr, b = addr + length; + if(a < Y && b > X) { + // from start of the block + unsigned start = (a > X ? a : X) - X; + unsigned end = (b > Y ? Y : b) - X; + + memcpy(fb->data + start, data, end - start); + + fit_blocks++; + fit_length += end - start; + } + } + + if(fit_blocks == 0) { + fprintf(stderr, "Unfit data block %08x -> %04x\n", addr, length); + return -1; + } + + if(fit_length != length) { + fprintf(stderr, "warning: data block %08x -> %04x truncated to %04x\n", + addr, length, fit_length); + fprintf(stderr, "(this is not an error, just a GDB glitch)\n"); + } + + return 0; +} + +static int flash_go(struct stlink* sl) { + int error = -1; + + // Some kinds of clock settings do not allow writing to flash. + stlink_reset(sl); + + for(struct flash_block* fb = flash_root; fb; fb = fb->next) { + #ifdef DEBUG + printf("flash_do: block %08x -> %04x\n", fb->addr, fb->length); + #endif + + unsigned length = fb->length; + for(stm32_addr_t page = fb->addr; page < fb->addr + fb->length; page += FLASH_PAGE) { + #ifdef DEBUG + printf("flash_do: page %08x\n", page); + #endif + + stlink_erase_flash_page(sl, page); + + if(stlink_write_flash(sl, page, fb->data + (page - fb->addr), + length > FLASH_PAGE ? FLASH_PAGE : length) < 0) + goto error; + } + + } + + stlink_reset(sl); + + error = 0; + +error: + for(struct flash_block* fb = flash_root, *next; fb; fb = next) { + next = fb->next; + free(fb->data); + free(fb); + } + + flash_root = NULL; + + return error; +} + +int serve(struct stlink* sl, int port) { + int sock = socket(AF_INET, SOCK_STREAM, 0); + if(sock < 0) { + perror("socket"); + return 1; + } + + unsigned int val = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + + struct sockaddr_in serv_addr = {0}; + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + serv_addr.sin_port = htons(port); + + if(bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + perror("bind"); + return 1; + } + + if(listen(sock, 5) < 0) { + perror("listen"); + return 1; + } + + stlink_force_debug(sl); + stlink_reset(sl); + init_code_breakpoints(sl); + init_data_watchpoints(sl); + + printf("Listening at *:%d...\n", port); + + int client = accept(sock, NULL, NULL); + if(client < 0) { + perror("accept"); + return 1; + } + + close(sock); + + printf("GDB connected.\n"); + + /* + * To allow resetting the chip from GDB it is required to + * emulate attaching and detaching to target. + */ + unsigned int attached = 1; + + while(1) { + char* packet; + + int status = gdb_recv_packet(client, &packet); + if(status < 0) { + fprintf(stderr, "cannot recv: %d\n", status); + return 1; + } + + #ifdef DEBUG + printf("recv: %s\n", packet); + #endif + + char* reply = NULL; + + switch(packet[0]) { + case 'q': { + if(packet[1] == 'P' || packet[1] == 'C' || packet[1] == 'L') { + reply = strdup(""); + break; + } + + char *separator = strstr(packet, ":"), *params = ""; + if(separator == NULL) { + separator = packet + strlen(packet); + } else { + params = separator + 1; + } + + unsigned queryNameLength = (separator - &packet[1]); + char* queryName = calloc(queryNameLength + 1, 1); + strncpy(queryName, &packet[1], queryNameLength); + + #ifdef DEBUG + printf("query: %s;%s\n", queryName, params); + #endif + + if(!strcmp(queryName, "Supported")) { + reply = strdup("PacketSize=3fff;qXfer:memory-map:read+"); + } else if(!strcmp(queryName, "Xfer")) { + char *type, *op, *annex, *s_addr, *s_length; + char *tok = params; + + type = strsep(&tok, ":"); + op = strsep(&tok, ":"); + annex = strsep(&tok, ":"); + s_addr = strsep(&tok, ","); + s_length = tok; + + unsigned addr = strtoul(s_addr, NULL, 16), + length = strtoul(s_length, NULL, 16); + + #ifdef DEBUG + printf("Xfer: type:%s;op:%s;annex:%s;addr:%d;length:%d\n", + type, op, annex, addr, length); + #endif + + const char* data = NULL; + + if(!strcmp(type, "memory-map") && !strcmp(op, "read")) + data = current_memory_map; + + if(data) { + unsigned data_length = strlen(data); + if(addr + length > data_length) + length = data_length - addr; + + if(length == 0) { + reply = strdup("l"); + } else { + reply = calloc(length + 2, 1); + reply[0] = 'm'; + strncpy(&reply[1], data, length); + } + } + } + + if(reply == NULL) + reply = strdup(""); + + free(queryName); + + break; + } + + case 'v': { + char *params = NULL; + char *cmdName = strtok_r(packet, ":;", ¶ms); + + cmdName++; // vCommand -> Command + + if(!strcmp(cmdName, "FlashErase")) { + char *s_addr, *s_length; + char *tok = params; + + s_addr = strsep(&tok, ","); + s_length = tok; + + unsigned addr = strtoul(s_addr, NULL, 16), + length = strtoul(s_length, NULL, 16); + + #ifdef DEBUG + printf("FlashErase: addr:%08x,len:%04x\n", + addr, length); + #endif + + if(flash_add_block(addr, length, sl) < 0) { + reply = strdup("E00"); + } else { + reply = strdup("OK"); + } + } else if(!strcmp(cmdName, "FlashWrite")) { + char *s_addr, *data; + char *tok = params; + + s_addr = strsep(&tok, ":"); + data = tok; + + unsigned addr = strtoul(s_addr, NULL, 16); + unsigned data_length = status - (data - packet); + + // Length of decoded data cannot be more than + // encoded, as escapes are removed. + // Additional byte is reserved for alignment fix. + uint8_t *decoded = calloc(data_length + 1, 1); + unsigned dec_index = 0; + for(int i = 0; i < data_length; i++) { + if(data[i] == 0x7d) { + i++; + decoded[dec_index++] = data[i] ^ 0x20; + } else { + decoded[dec_index++] = data[i]; + } + } + + // Fix alignment + if(dec_index % 2 != 0) + dec_index++; + + #ifdef DEBUG + printf("binary packet %d -> %d\n", data_length, dec_index); + #endif + + if(flash_populate(addr, decoded, dec_index) < 0) { + reply = strdup("E00"); + } else { + reply = strdup("OK"); + } + } else if(!strcmp(cmdName, "FlashDone")) { + if(flash_go(sl) < 0) { + reply = strdup("E00"); + } else { + reply = strdup("OK"); + } + } else if(!strcmp(cmdName, "Kill")) { + attached = 0; + + reply = strdup("OK"); + } + + if(reply == NULL) + reply = strdup(""); + + break; + } + + case 'c': + stlink_run(sl); + + while(1) { + int status = gdb_check_for_interrupt(client); + if(status < 0) { + fprintf(stderr, "cannot check for int: %d\n", status); + return 1; + } + + if(status == 1) { + stlink_force_debug(sl); + break; + } + + stlink_status(sl); + if(sl->core_stat == STLINK_CORE_HALTED) { + break; + } + + usleep(100000); + } + + reply = strdup("S05"); // TRAP + break; + + case 's': + stlink_step(sl); + + reply = strdup("S05"); // TRAP + break; + + case '?': + if(attached) { + reply = strdup("S05"); // TRAP + } else { + /* Stub shall reply OK if not attached. */ + reply = strdup("OK"); + } + break; + + case 'g': + stlink_read_all_regs(sl); + + reply = calloc(8 * 16 + 1, 1); + for(int i = 0; i < 16; i++) + sprintf(&reply[i * 8], "%08x", htonl(sl->reg.r[i])); + + break; + + case 'p': { + unsigned id = strtoul(&packet[1], NULL, 16), reg = 0xDEADDEAD; + + if(id < 16) { + stlink_read_reg(sl, id); + reg = htonl(sl->reg.r[id]); + } else if(id == 0x19) { + stlink_read_reg(sl, 16); + reg = htonl(sl->reg.xpsr); + } else { + reply = strdup("E00"); + } + + reply = calloc(8 + 1, 1); + sprintf(reply, "%08x", reg); + + break; + } + + case 'P': { + char* s_reg = &packet[1]; + char* s_value = strstr(&packet[1], "=") + 1; + + unsigned reg = strtoul(s_reg, NULL, 16); + unsigned value = strtoul(s_value, NULL, 16); + + if(reg < 16) { + stlink_write_reg(sl, ntohl(value), reg); + } else if(reg == 0x19) { + stlink_write_reg(sl, ntohl(value), 16); + } else { + reply = strdup("E00"); + } + + if(!reply) { + reply = strdup("OK"); + } + + break; + } + + case 'G': + for(int i = 0; i < 16; i++) { + char str[9] = {0}; + strncpy(str, &packet[1 + i * 8], 8); + uint32_t reg = strtoul(str, NULL, 16); + stlink_write_reg(sl, ntohl(reg), i); + } + + reply = strdup("OK"); + break; + + case 'm': { + char* s_start = &packet[1]; + char* s_count = strstr(&packet[1], ",") + 1; + + stm32_addr_t start = strtoul(s_start, NULL, 16); + unsigned count = strtoul(s_count, NULL, 16); + + unsigned adj_start = start % 4; + + stlink_read_mem32(sl, start - adj_start, (count % 4 == 0) ? + count : count + 4 - (count % 4)); + + reply = calloc(count * 2 + 1, 1); + for(int i = 0; i < count; i++) { + reply[i * 2 + 0] = hex[sl->q_buf[i + adj_start] >> 4]; + reply[i * 2 + 1] = hex[sl->q_buf[i + adj_start] & 0xf]; + } + + break; + } + + case 'M': { + char* s_start = &packet[1]; + char* s_count = strstr(&packet[1], ",") + 1; + char* hexdata = strstr(packet, ":") + 1; + + stm32_addr_t start = strtoul(s_start, NULL, 16); + unsigned count = strtoul(s_count, NULL, 16); + + for(int i = 0; i < count; i ++) { + char hex[3] = { hexdata[i*2], hexdata[i*2+1], 0 }; + uint8_t byte = strtoul(hex, NULL, 16); + sl->q_buf[i] = byte; + } + + if((count % 4) == 0 && (start % 4) == 0) { + stlink_write_mem32(sl, start, count); + } else { + stlink_write_mem8(sl, start, count); + } + + reply = strdup("OK"); + + break; + } + + case 'Z': { + char *endptr; + stm32_addr_t addr = strtoul(&packet[3], &endptr, 16); + stm32_addr_t len = strtoul(&endptr[1], NULL, 16); + + switch (packet[1]) { + case '1': + if(update_code_breakpoint(sl, addr, 1) < 0) { + reply = strdup("E00"); + } else { + reply = strdup("OK"); + } + break; + + case '2': // insert write watchpoint + case '3': // insert read watchpoint + case '4': // insert access watchpoint + { + enum watchfun wf; + if(packet[1] == '2') { + wf = WATCHWRITE; + } else if(packet[1] == '3') { + wf = WATCHREAD; + } else { + wf = WATCHACCESS; + if(add_data_watchpoint(sl, wf, addr, len) < 0) { + reply = strdup("E00"); + } else { + reply = strdup("OK"); + break; + } + } + } + + default: + reply = strdup(""); + } + break; + } + case 'z': { + char *endptr; + stm32_addr_t addr = strtoul(&packet[3], &endptr, 16); + stm32_addr_t len = strtoul(&endptr[1], NULL, 16); + + switch (packet[1]) { + case '1': // remove breakpoint + update_code_breakpoint(sl, addr, 0); + reply = strdup("OK"); + break; + + case '2' : // remove write watchpoint + case '3' : // remove read watchpoint + case '4' : // remove access watchpoint + if(delete_data_watchpoint(sl, addr) < 0) { + reply = strdup("E00"); + } else { + reply = strdup("OK"); + break; + } + + default: + reply = strdup(""); + } + break; + } + + case '!': { + /* + * Enter extended mode which allows restarting. + * We do support that always. + */ + + reply = strdup("OK"); + + break; + } + + case 'R': { + /* Reset the core. */ + + stlink_reset(sl); + init_code_breakpoints(sl); + init_data_watchpoints(sl); + + attached = 1; + + reply = strdup("OK"); + + break; + } + + default: + reply = strdup(""); + } + + if(reply) { + #ifdef DEBUG + printf("send: %s\n", reply); + #endif + + int result = gdb_send_packet(client, reply); + if(result != 0) { + fprintf(stderr, "cannot send: %d\n", result); + return 1; + } + + free(reply); + } + + free(packet); + } + + return 0; +} diff --git a/src/gdb-remote.c b/src/gdb-remote.c deleted file mode 100644 index f6bf02d..0000000 --- a/src/gdb-remote.c +++ /dev/null @@ -1,156 +0,0 @@ -/* -*- tab-width:8 -*- */ - -/* - Copyright (C) 2011 Peter Zotov - Use of this source code is governed by a BSD-style - license that can be found in the LICENSE file. -*/ - -#include -#include -#include -#include -#include -#include - -static const char hex[] = "0123456789abcdef"; - -int gdb_send_packet(int fd, char* data) { - unsigned length = strlen(data) + 5; - char* packet = malloc(length); /* '$' data (hex) '#' cksum (hex) */ - - memset(packet, 0, length); - - packet[0] = '$'; - - uint8_t cksum = 0; - for(int i = 0; i < strlen(data); i++) { - packet[i + 1] = data[i]; - cksum += data[i]; - } - - packet[length - 4] = '#'; - packet[length - 3] = hex[cksum >> 4]; - packet[length - 2] = hex[cksum & 0xf]; - - while(1) { - if(write(fd, packet, length) != length) { - free(packet); - return -2; - } - - char ack; - if(read(fd, &ack, 1) != 1) { - free(packet); - return -2; - } - - if(ack == '+') { - free(packet); - return 0; - } - } -} - -#define ALLOC_STEP 1024 - -int gdb_recv_packet(int fd, char** buffer) { - unsigned packet_size = ALLOC_STEP + 1, packet_idx = 0; - uint8_t cksum = 0; - char recv_cksum[3] = {0}; - char* packet_buffer = malloc(packet_size); - unsigned state; - -start: - state = 0; - /* - * 0: waiting $ - * 1: data, waiting # - * 2: cksum 1 - * 3: cksum 2 - * 4: fin - */ - - char c; - while(state != 4) { - if(read(fd, &c, 1) != 1) { - return -2; - } - - switch(state) { - case 0: - if(c != '$') { - // ignore - } else { - state = 1; - } - break; - - case 1: - if(c == '#') { - state = 2; - } else { - packet_buffer[packet_idx++] = c; - cksum += c; - - if(packet_idx == packet_size) { - packet_size += ALLOC_STEP; - packet_buffer = realloc(packet_buffer, packet_size); - } - } - break; - - case 2: - recv_cksum[0] = c; - state = 3; - break; - - case 3: - recv_cksum[1] = c; - state = 4; - break; - } - } - - uint8_t recv_cksum_int = strtoul(recv_cksum, NULL, 16); - if(recv_cksum_int != cksum) { - char nack = '-'; - if(write(fd, &nack, 1) != 1) { - return -2; - } - - goto start; - } else { - char ack = '+'; - if(write(fd, &ack, 1) != 1) { - return -2; - } - } - - packet_buffer[packet_idx] = 0; - *buffer = packet_buffer; - - return packet_idx; -} - -// Here we skip any characters which are not \x03, GDB interrupt. -// As we use the mode with ACK, in a (very unlikely) situation of a packet -// lost because of this skipping, it will be resent anyway. -int gdb_check_for_interrupt(int fd) { - struct pollfd pfd; - pfd.fd = fd; - pfd.events = POLLIN; - - if(poll(&pfd, 1, 0) != 0) { - char c; - - if(read(fd, &c, 1) != 1) - return -2; - - if(c == '\x03') // ^C - return 1; - } - - return 0; -} - diff --git a/src/gdb-remote.h b/src/gdb-remote.h deleted file mode 100644 index bfa0104..0000000 --- a/src/gdb-remote.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _GDB_REMOTE_H_ -#define _GDB_REMOTE_H_ - -int gdb_send_packet(int fd, char* data); -int gdb_recv_packet(int fd, char** buffer); -int gdb_check_for_interrupt(int fd); - -#endif diff --git a/src/gdb-server.c b/src/gdb-server.c deleted file mode 100644 index 04fd2bc..0000000 --- a/src/gdb-server.c +++ /dev/null @@ -1,949 +0,0 @@ -/* -*- tab-width:8 -*- */ - -/* - Copyright (C) 2011 Peter Zotov - Use of this source code is governed by a BSD-style - license that can be found in the LICENSE file. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "gdb-remote.h" -#include "stlink-hw.h" - -#define FLASH_BASE 0x08000000 -#define FLASH_PAGE (sl->flash_pgsz) -#define FLASH_PAGE_MASK (~((1 << 10) - 1)) -#define FLASH_SIZE (FLASH_PAGE * 128) - -static const char hex[] = "0123456789abcdef"; - -static const char* current_memory_map = NULL; - -struct chip_params { - uint32_t chip_id; - char* description; - uint32_t max_flash_size, flash_pagesize; - uint32_t sram_size; - uint32_t bootrom_base, bootrom_size; -} const devices[] = { - { 0x412, "Low-density device", - 0x8000, 0x400, 0x2800, 0x1ffff000, 0x800 }, - { 0x410, "Medium-density device", - 0x20000, 0x400, 0x5000, 0x1ffff000, 0x800 }, - { 0x414, "High-density device", - 0x80000, 0x800, 0x10000, 0x1ffff000, 0x800 }, - { 0x418, "Connectivity line device", - 0x40000, 0x800, 0x10000, 0x1fffb000, 0x4800 }, - { 0x420, "Medium-density value line device", - 0x20000, 0x400, 0x2000, 0x1ffff000, 0x800 }, - { 0x428, "High-density value line device", - 0x80000, 0x800, 0x8000, 0x1ffff000, 0x800 }, - { 0x430, "XL-density device", - 0x100000, 0x800, 0x18000, 0x1fffe000, 0x1800 }, - { 0 } -}; - -int serve(struct stlink* sl, int port); -char* make_memory_map(const struct chip_params *params, uint32_t flash_size); - -int main(int argc, char** argv) { - if(argc != 3) { - fprintf(stderr, "Usage: %s /dev/sgX\n", argv[0]); - return 1; - } - - struct stlink *sl = stlink_quirk_open(argv[2], 0); - if (sl == NULL) - return 1; - - if(stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) - stlink_enter_swd_mode(sl); - - uint32_t chip_id; - - stlink_read_mem32(sl, 0xE0042000, 4); - chip_id = sl->q_buf[0] | (sl->q_buf[1] << 8) | (sl->q_buf[2] << 16) | - (sl->q_buf[3] << 24); - - printf("Chip ID is %08x.\n", chip_id); - - const struct chip_params* params = NULL; - - for(int i = 0; i < sizeof(devices) / sizeof(devices[0]); i++) { - if(devices[i].chip_id == (chip_id & 0xFFF)) { - params = &devices[i]; - break; - } - } - - if(params == NULL) { - fprintf(stderr, "Cannot recognize the connected device!\n"); - return 0; - } - - printf("Device connected: %s\n", params->description); - printf("Device parameters: SRAM: 0x%x bytes, Flash: up to 0x%x bytes in pages of 0x%x bytes\n", - params->sram_size, params->max_flash_size, params->flash_pagesize); - - FLASH_PAGE = params->flash_pagesize; - - uint32_t flash_size; - - stlink_read_mem32(sl, 0x1FFFF7E0, 4); - flash_size = sl->q_buf[0] | (sl->q_buf[1] << 8); - - printf("Flash size is %d KiB.\n", flash_size); - // memory map is in 1k blocks. - current_memory_map = make_memory_map(params, flash_size * 0x400); - - int port = atoi(argv[1]); - - while(serve(sl, port) == 0); - - stlink_close(sl); - - return 0; -} - -static const char* const memory_map_template = - "" - "" - "" - " " // code = sram, bootrom or flash; flash is bigger - " " // sram 8k - " " - " 0x%x" - " " - " " // peripheral regs - " " // cortex regs - " " // bootrom - " " // option byte area - ""; - -char* make_memory_map(const struct chip_params *params, uint32_t flash_size) { - /* This will be freed in serve() */ - char* map = malloc(4096); - map[0] = '\0'; - - snprintf(map, 4096, memory_map_template, - flash_size, - params->sram_size, - flash_size, params->flash_pagesize, - params->bootrom_base, params->bootrom_size); - - return map; -} - - -/* - * DWT_COMP0 0xE0001020 - * DWT_MASK0 0xE0001024 - * DWT_FUNCTION0 0xE0001028 - * DWT_COMP1 0xE0001030 - * DWT_MASK1 0xE0001034 - * DWT_FUNCTION1 0xE0001038 - * DWT_COMP2 0xE0001040 - * DWT_MASK2 0xE0001044 - * DWT_FUNCTION2 0xE0001048 - * DWT_COMP3 0xE0001050 - * DWT_MASK3 0xE0001054 - * DWT_FUNCTION3 0xE0001058 - */ - -#define DATA_WATCH_NUM 4 - -enum watchfun { WATCHDISABLED = 0, WATCHREAD = 5, WATCHWRITE = 6, WATCHACCESS = 7 }; - -struct code_hw_watchpoint { - stm32_addr_t addr; - uint8_t mask; - enum watchfun fun; -}; - -struct code_hw_watchpoint data_watches[DATA_WATCH_NUM]; - -static void init_data_watchpoints(struct stlink *sl) { - int i; - - #ifdef DEBUG - printf("init watchpoints\n"); - #endif - - // set trcena in debug command to turn on dwt unit - stlink_read_mem32(sl, 0xE000EDFC, 4); - sl->q_buf[3] |= 1; - stlink_write_mem32(sl, 0xE000EDFC, 4); - - // make sure all watchpoints are cleared - memset(sl->q_buf, 0, 4); - for(int i = 0; i < DATA_WATCH_NUM; i++) { - data_watches[i].fun = WATCHDISABLED; - stlink_write_mem32(sl, 0xe0001028 + i * 16, 4); - } -} - -static int add_data_watchpoint(struct stlink* sl, enum watchfun wf, stm32_addr_t addr, unsigned int len) -{ - int i = 0; - uint32_t mask, temp; - - // computer mask - // find a free watchpoint - // configure - - mask = -1; - i = len; - while(i) { - i >>= 1; - mask++; - } - - if((mask != -1) && (mask < 16)) { - for(i = 0; i < DATA_WATCH_NUM; i++) { - // is this an empty slot ? - if(data_watches[i].fun == WATCHDISABLED) { - #ifdef DEBUG - printf("insert watchpoint %d addr %x wf %u mask %u len %d\n", i, addr, wf, mask, len); - #endif - - data_watches[i].fun = wf; - data_watches[i].addr = addr; - data_watches[i].mask = mask; - - // insert comparator address - sl->q_buf[0] = (addr & 0xff); - sl->q_buf[1] = ((addr >> 8) & 0xff); - sl->q_buf[2] = ((addr >> 16) & 0xff); - sl->q_buf[3] = ((addr >> 24) & 0xff); - - stlink_write_mem32(sl, 0xE0001020 + i * 16, 4); - - // insert mask - memset(sl->q_buf, 0, 4); - sl->q_buf[0] = mask; - stlink_write_mem32(sl, 0xE0001024 + i * 16, 4); - - // insert function - memset(sl->q_buf, 0, 4); - sl->q_buf[0] = wf; - stlink_write_mem32(sl, 0xE0001028 + i * 16, 4); - - // just to make sure the matched bit is clear ! - stlink_read_mem32(sl, 0xE0001028 + i * 16, 4); - return 0; - } - } - } - - #ifdef DEBUG - printf("failure: add watchpoints addr %x wf %u len %u\n", addr, wf, len); - #endif - return -1; -} - -static int delete_data_watchpoint(struct stlink* sl, stm32_addr_t addr) -{ - int i; - - for(i = 0 ; i < DATA_WATCH_NUM; i++) { - if((data_watches[i].addr == addr) && (data_watches[i].fun != WATCHDISABLED)) { - #ifdef DEBUG - printf("delete watchpoint %d addr %x\n", i, addr); - #endif - - memset(sl->q_buf, 0, 4); - data_watches[i].fun = WATCHDISABLED; - stlink_write_mem32(sl, 0xe0001028 + i * 16, 4); - - return 0; - } - } - - #ifdef DEBUG - printf("failure: delete watchpoint addr %x\n", addr); - #endif - - return -1; -} - -#define CODE_BREAK_NUM 6 -#define CODE_BREAK_LOW 0x01 -#define CODE_BREAK_HIGH 0x02 - -struct code_hw_breakpoint { - stm32_addr_t addr; - int type; -}; - -struct code_hw_breakpoint code_breaks[CODE_BREAK_NUM]; - -static void init_code_breakpoints(struct stlink* sl) { - memset(sl->q_buf, 0, 4); - sl->q_buf[0] = 0x03; // KEY | ENABLE - stlink_write_mem32(sl, 0xe0002000, 4); - - memset(sl->q_buf, 0, 4); - for(int i = 0; i < CODE_BREAK_NUM; i++) { - code_breaks[i].type = 0; - stlink_write_mem32(sl, 0xe0002008 + i * 4, 4); - } -} - -static int update_code_breakpoint(struct stlink* sl, stm32_addr_t addr, int set) { - stm32_addr_t fpb_addr = addr & ~0x3; - int type = addr & 0x2 ? CODE_BREAK_HIGH : CODE_BREAK_LOW; - - if(addr & 1) { - fprintf(stderr, "update_code_breakpoint: unaligned address %08x\n", addr); - return -1; - } - - int id = -1; - for(int i = 0; i < CODE_BREAK_NUM; i++) { - if(fpb_addr == code_breaks[i].addr || - (set && code_breaks[i].type == 0)) { - id = i; - break; - } - } - - if(id == -1) { - if(set) return -1; // Free slot not found - else return 0; // Breakpoint is already removed - } - - struct code_hw_breakpoint* brk = &code_breaks[id]; - - brk->addr = fpb_addr; - - if(set) brk->type |= type; - else brk->type &= ~type; - - memset(sl->q_buf, 0, 4); - - if(brk->type == 0) { - #ifdef DEBUG - printf("clearing hw break %d\n", id); - #endif - - stlink_write_mem32(sl, 0xe0002008 + id * 4, 4); - } else { - sl->q_buf[0] = ( brk->addr & 0xff) | 1; - sl->q_buf[1] = ((brk->addr >> 8) & 0xff); - sl->q_buf[2] = ((brk->addr >> 16) & 0xff); - sl->q_buf[3] = ((brk->addr >> 24) & 0xff) | (brk->type << 6); - - #ifdef DEBUG - printf("setting hw break %d at %08x (%d)\n", - id, brk->addr, brk->type); - printf("reg %02x %02x %02x %02x\n", - sl->q_buf[3], sl->q_buf[2], sl->q_buf[1], sl->q_buf[0]); - #endif - - stlink_write_mem32(sl, 0xe0002008 + id * 4, 4); - } - - return 0; -} - - -struct flash_block { - stm32_addr_t addr; - unsigned length; - uint8_t* data; - - struct flash_block* next; -}; - -static struct flash_block* flash_root; - -static int flash_add_block(stm32_addr_t addr, unsigned length, - struct stlink* sl) { - if(addr < FLASH_BASE || addr + length > FLASH_BASE + FLASH_SIZE) { - fprintf(stderr, "flash_add_block: incorrect bounds\n"); - return -1; - } - - if(addr % FLASH_PAGE != 0 || length % FLASH_PAGE != 0) { - fprintf(stderr, "flash_add_block: unaligned block\n"); - return -1; - } - - struct flash_block* new = malloc(sizeof(struct flash_block)); - new->next = flash_root; - - new->addr = addr; - new->length = length; - new->data = calloc(length, 1); - - flash_root = new; - - return 0; -} - -static int flash_populate(stm32_addr_t addr, uint8_t* data, unsigned length) { - int fit_blocks = 0, fit_length = 0; - - for(struct flash_block* fb = flash_root; fb; fb = fb->next) { - /* Block: ------X------Y-------- - * Data: a-----b - * a--b - * a-----------b - * Block intersects with data, if: - * a < Y && b > x - */ - - unsigned X = fb->addr, Y = fb->addr + fb->length; - unsigned a = addr, b = addr + length; - if(a < Y && b > X) { - // from start of the block - unsigned start = (a > X ? a : X) - X; - unsigned end = (b > Y ? Y : b) - X; - - memcpy(fb->data + start, data, end - start); - - fit_blocks++; - fit_length += end - start; - } - } - - if(fit_blocks == 0) { - fprintf(stderr, "Unfit data block %08x -> %04x\n", addr, length); - return -1; - } - - if(fit_length != length) { - fprintf(stderr, "warning: data block %08x -> %04x truncated to %04x\n", - addr, length, fit_length); - fprintf(stderr, "(this is not an error, just a GDB glitch)\n"); - } - - return 0; -} - -static int flash_go(struct stlink* sl) { - int error = -1; - - // Some kinds of clock settings do not allow writing to flash. - stlink_reset(sl); - - for(struct flash_block* fb = flash_root; fb; fb = fb->next) { - #ifdef DEBUG - printf("flash_do: block %08x -> %04x\n", fb->addr, fb->length); - #endif - - unsigned length = fb->length; - for(stm32_addr_t page = fb->addr; page < fb->addr + fb->length; page += FLASH_PAGE) { - #ifdef DEBUG - printf("flash_do: page %08x\n", page); - #endif - - stlink_erase_flash_page(sl, page); - - if(stlink_write_flash(sl, page, fb->data + (page - fb->addr), - length > FLASH_PAGE ? FLASH_PAGE : length) < 0) - goto error; - } - - } - - stlink_reset(sl); - - error = 0; - -error: - for(struct flash_block* fb = flash_root, *next; fb; fb = next) { - next = fb->next; - free(fb->data); - free(fb); - } - - flash_root = NULL; - - return error; -} - -int serve(struct stlink* sl, int port) { - int sock = socket(AF_INET, SOCK_STREAM, 0); - if(sock < 0) { - perror("socket"); - return 1; - } - - unsigned int val = 1; - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); - - struct sockaddr_in serv_addr = {0}; - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - serv_addr.sin_port = htons(port); - - if(bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { - perror("bind"); - return 1; - } - - if(listen(sock, 5) < 0) { - perror("listen"); - return 1; - } - - stlink_force_debug(sl); - stlink_reset(sl); - init_code_breakpoints(sl); - init_data_watchpoints(sl); - - printf("Listening at *:%d...\n", port); - - int client = accept(sock, NULL, NULL); - if(client < 0) { - perror("accept"); - return 1; - } - - close(sock); - - printf("GDB connected.\n"); - - /* - * To allow resetting the chip from GDB it is required to - * emulate attaching and detaching to target. - */ - unsigned int attached = 1; - - while(1) { - char* packet; - - int status = gdb_recv_packet(client, &packet); - if(status < 0) { - fprintf(stderr, "cannot recv: %d\n", status); - return 1; - } - - #ifdef DEBUG - printf("recv: %s\n", packet); - #endif - - char* reply = NULL; - - switch(packet[0]) { - case 'q': { - if(packet[1] == 'P' || packet[1] == 'C' || packet[1] == 'L') { - reply = strdup(""); - break; - } - - char *separator = strstr(packet, ":"), *params = ""; - if(separator == NULL) { - separator = packet + strlen(packet); - } else { - params = separator + 1; - } - - unsigned queryNameLength = (separator - &packet[1]); - char* queryName = calloc(queryNameLength + 1, 1); - strncpy(queryName, &packet[1], queryNameLength); - - #ifdef DEBUG - printf("query: %s;%s\n", queryName, params); - #endif - - if(!strcmp(queryName, "Supported")) { - reply = strdup("PacketSize=3fff;qXfer:memory-map:read+"); - } else if(!strcmp(queryName, "Xfer")) { - char *type, *op, *annex, *s_addr, *s_length; - char *tok = params; - - type = strsep(&tok, ":"); - op = strsep(&tok, ":"); - annex = strsep(&tok, ":"); - s_addr = strsep(&tok, ","); - s_length = tok; - - unsigned addr = strtoul(s_addr, NULL, 16), - length = strtoul(s_length, NULL, 16); - - #ifdef DEBUG - printf("Xfer: type:%s;op:%s;annex:%s;addr:%d;length:%d\n", - type, op, annex, addr, length); - #endif - - const char* data = NULL; - - if(!strcmp(type, "memory-map") && !strcmp(op, "read")) - data = current_memory_map; - - if(data) { - unsigned data_length = strlen(data); - if(addr + length > data_length) - length = data_length - addr; - - if(length == 0) { - reply = strdup("l"); - } else { - reply = calloc(length + 2, 1); - reply[0] = 'm'; - strncpy(&reply[1], data, length); - } - } - } - - if(reply == NULL) - reply = strdup(""); - - free(queryName); - - break; - } - - case 'v': { - char *params = NULL; - char *cmdName = strtok_r(packet, ":;", ¶ms); - - cmdName++; // vCommand -> Command - - if(!strcmp(cmdName, "FlashErase")) { - char *s_addr, *s_length; - char *tok = params; - - s_addr = strsep(&tok, ","); - s_length = tok; - - unsigned addr = strtoul(s_addr, NULL, 16), - length = strtoul(s_length, NULL, 16); - - #ifdef DEBUG - printf("FlashErase: addr:%08x,len:%04x\n", - addr, length); - #endif - - if(flash_add_block(addr, length, sl) < 0) { - reply = strdup("E00"); - } else { - reply = strdup("OK"); - } - } else if(!strcmp(cmdName, "FlashWrite")) { - char *s_addr, *data; - char *tok = params; - - s_addr = strsep(&tok, ":"); - data = tok; - - unsigned addr = strtoul(s_addr, NULL, 16); - unsigned data_length = status - (data - packet); - - // Length of decoded data cannot be more than - // encoded, as escapes are removed. - // Additional byte is reserved for alignment fix. - uint8_t *decoded = calloc(data_length + 1, 1); - unsigned dec_index = 0; - for(int i = 0; i < data_length; i++) { - if(data[i] == 0x7d) { - i++; - decoded[dec_index++] = data[i] ^ 0x20; - } else { - decoded[dec_index++] = data[i]; - } - } - - // Fix alignment - if(dec_index % 2 != 0) - dec_index++; - - #ifdef DEBUG - printf("binary packet %d -> %d\n", data_length, dec_index); - #endif - - if(flash_populate(addr, decoded, dec_index) < 0) { - reply = strdup("E00"); - } else { - reply = strdup("OK"); - } - } else if(!strcmp(cmdName, "FlashDone")) { - if(flash_go(sl) < 0) { - reply = strdup("E00"); - } else { - reply = strdup("OK"); - } - } else if(!strcmp(cmdName, "Kill")) { - attached = 0; - - reply = strdup("OK"); - } - - if(reply == NULL) - reply = strdup(""); - - break; - } - - case 'c': - stlink_run(sl); - - while(1) { - int status = gdb_check_for_interrupt(client); - if(status < 0) { - fprintf(stderr, "cannot check for int: %d\n", status); - return 1; - } - - if(status == 1) { - stlink_force_debug(sl); - break; - } - - stlink_status(sl); - if(sl->core_stat == STLINK_CORE_HALTED) { - break; - } - - usleep(100000); - } - - reply = strdup("S05"); // TRAP - break; - - case 's': - stlink_step(sl); - - reply = strdup("S05"); // TRAP - break; - - case '?': - if(attached) { - reply = strdup("S05"); // TRAP - } else { - /* Stub shall reply OK if not attached. */ - reply = strdup("OK"); - } - break; - - case 'g': - stlink_read_all_regs(sl); - - reply = calloc(8 * 16 + 1, 1); - for(int i = 0; i < 16; i++) - sprintf(&reply[i * 8], "%08x", htonl(sl->reg.r[i])); - - break; - - case 'p': { - unsigned id = strtoul(&packet[1], NULL, 16), reg = 0xDEADDEAD; - - if(id < 16) { - stlink_read_reg(sl, id); - reg = htonl(sl->reg.r[id]); - } else if(id == 0x19) { - stlink_read_reg(sl, 16); - reg = htonl(sl->reg.xpsr); - } else { - reply = strdup("E00"); - } - - reply = calloc(8 + 1, 1); - sprintf(reply, "%08x", reg); - - break; - } - - case 'P': { - char* s_reg = &packet[1]; - char* s_value = strstr(&packet[1], "=") + 1; - - unsigned reg = strtoul(s_reg, NULL, 16); - unsigned value = strtoul(s_value, NULL, 16); - - if(reg < 16) { - stlink_write_reg(sl, ntohl(value), reg); - } else if(reg == 0x19) { - stlink_write_reg(sl, ntohl(value), 16); - } else { - reply = strdup("E00"); - } - - if(!reply) { - reply = strdup("OK"); - } - - break; - } - - case 'G': - for(int i = 0; i < 16; i++) { - char str[9] = {0}; - strncpy(str, &packet[1 + i * 8], 8); - uint32_t reg = strtoul(str, NULL, 16); - stlink_write_reg(sl, ntohl(reg), i); - } - - reply = strdup("OK"); - break; - - case 'm': { - char* s_start = &packet[1]; - char* s_count = strstr(&packet[1], ",") + 1; - - stm32_addr_t start = strtoul(s_start, NULL, 16); - unsigned count = strtoul(s_count, NULL, 16); - - unsigned adj_start = start % 4; - - stlink_read_mem32(sl, start - adj_start, (count % 4 == 0) ? - count : count + 4 - (count % 4)); - - reply = calloc(count * 2 + 1, 1); - for(int i = 0; i < count; i++) { - reply[i * 2 + 0] = hex[sl->q_buf[i + adj_start] >> 4]; - reply[i * 2 + 1] = hex[sl->q_buf[i + adj_start] & 0xf]; - } - - break; - } - - case 'M': { - char* s_start = &packet[1]; - char* s_count = strstr(&packet[1], ",") + 1; - char* hexdata = strstr(packet, ":") + 1; - - stm32_addr_t start = strtoul(s_start, NULL, 16); - unsigned count = strtoul(s_count, NULL, 16); - - for(int i = 0; i < count; i ++) { - char hex[3] = { hexdata[i*2], hexdata[i*2+1], 0 }; - uint8_t byte = strtoul(hex, NULL, 16); - sl->q_buf[i] = byte; - } - - if((count % 4) == 0 && (start % 4) == 0) { - stlink_write_mem32(sl, start, count); - } else { - stlink_write_mem8(sl, start, count); - } - - reply = strdup("OK"); - - break; - } - - case 'Z': { - char *endptr; - stm32_addr_t addr = strtoul(&packet[3], &endptr, 16); - stm32_addr_t len = strtoul(&endptr[1], NULL, 16); - - switch (packet[1]) { - case '1': - if(update_code_breakpoint(sl, addr, 1) < 0) { - reply = strdup("E00"); - } else { - reply = strdup("OK"); - } - break; - - case '2': // insert write watchpoint - case '3': // insert read watchpoint - case '4': // insert access watchpoint - { - enum watchfun wf; - if(packet[1] == '2') { - wf = WATCHWRITE; - } else if(packet[1] == '3') { - wf = WATCHREAD; - } else { - wf = WATCHACCESS; - if(add_data_watchpoint(sl, wf, addr, len) < 0) { - reply = strdup("E00"); - } else { - reply = strdup("OK"); - break; - } - } - } - - default: - reply = strdup(""); - } - break; - } - case 'z': { - char *endptr; - stm32_addr_t addr = strtoul(&packet[3], &endptr, 16); - stm32_addr_t len = strtoul(&endptr[1], NULL, 16); - - switch (packet[1]) { - case '1': // remove breakpoint - update_code_breakpoint(sl, addr, 0); - reply = strdup("OK"); - break; - - case '2' : // remove write watchpoint - case '3' : // remove read watchpoint - case '4' : // remove access watchpoint - if(delete_data_watchpoint(sl, addr) < 0) { - reply = strdup("E00"); - } else { - reply = strdup("OK"); - break; - } - - default: - reply = strdup(""); - } - break; - } - - case '!': { - /* - * Enter extended mode which allows restarting. - * We do support that always. - */ - - reply = strdup("OK"); - - break; - } - - case 'R': { - /* Reset the core. */ - - stlink_reset(sl); - init_code_breakpoints(sl); - init_data_watchpoints(sl); - - attached = 1; - - reply = strdup("OK"); - - break; - } - - default: - reply = strdup(""); - } - - if(reply) { - #ifdef DEBUG - printf("send: %s\n", reply); - #endif - - int result = gdb_send_packet(client, reply); - if(result != 0) { - fprintf(stderr, "cannot send: %d\n", result); - return 1; - } - - free(reply); - } - - free(packet); - } - - return 0; -} diff --git a/src/stlink-common.c b/src/stlink-common.c new file mode 100644 index 0000000..53a9d29 --- /dev/null +++ b/src/stlink-common.c @@ -0,0 +1,351 @@ + + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +#include "stlink-common.h" + +void D(stlink_t *sl, char *txt) { + if (sl->verbose > 1) + fputs(txt, stderr); +} + +void DD(stlink_t *sl, char *format, ...) { + if (sl->verbose > 0) { + va_list list; + va_start(list, format); + vfprintf(stderr, format, list); + va_end(list); + } +} + +// Delegates to the backends... + +void stlink_close(stlink_t *sl) { + D(sl, "\n*** stlink_close ***\n"); + sl->backend->close(sl); + + free(sl); +} + +void stlink_exit_debug_mode(stlink_t *sl) { + D(sl, "\n*** stlink_exit_debug_mode ***\n"); + sl->backend->exit_debug_mode(sl); +} + +void stlink_enter_swd_mode(stlink_t *sl) { + D(sl, "\n*** stlink_enter_swd_mode ***\n"); + sl->backend->enter_swd_mode(sl); +} + +void stlink_exit_dfu_mode(stlink_t *sl) { + D(sl, "\n*** stlink_exit_duf_mode ***\n"); + sl->backend->exit_dfu_mode(sl); +} + +void stlink_core_id(stlink_t *sl) { + D(sl, "\n*** stlink_core_id ***\n"); + sl->backend->core_id(sl); + DD(sl, "core_id = 0x%08x\n", sl->core_id); +} + +void stlink_reset(stlink_t *sl) { + D(sl, "\n*** stlink_reset ***\n"); + sl->backend->reset(sl); + +} + +void stlink_run(stlink_t *sl) { + D(sl, "\n*** stlink_core_id ***\n"); + sl->backend->run(sl); + DD(sl, "core_id = 0x%08x\n", sl->core_id); +} + +void stlink_status(stlink_t *sl) { + D(sl, "\n*** stlink_core_id ***\n"); + sl->backend->status(sl); + stlink_core_stat(sl); + DD(sl, "core_id = 0x%08x\n", sl->core_id); +} + +void stlink_version(stlink_t *sl) { + D(sl, "*** looking up stlink version\n"); + sl->backend->version(sl); +} + +void stlink_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { + D(sl, "\n*** stlink_write_mem32 ***\n"); + if (len % 4 != 0) { + fprintf(stderr, "Error: Data length doesn't have a 32 bit alignment: +%d byte.\n", len % 4); + return; + } + sl->backend->write_mem32(sl, addr, len); +} + +void stlink_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) { + D(sl, "\n*** stlink_write_mem8 ***\n"); + sl->backend->write_mem8(sl, addr, len); +} + + + + + + +// End of delegates.... Common code below here... + +// Endianness +// http://www.ibm.com/developerworks/aix/library/au-endianc/index.html +// const int i = 1; +// #define is_bigendian() ( (*(char*)&i) == 0 ) + +static inline unsigned int is_bigendian(void) { + static volatile const unsigned int i = 1; + return *(volatile const char*) &i == 0; +} + +uint16_t read_uint16(const unsigned char *c, const int pt) { + uint32_t ui; + char *p = (char *) &ui; + + if (!is_bigendian()) { // le -> le (don't swap) + p[0] = c[pt]; + p[1] = c[pt + 1]; + } else { + p[0] = c[pt + 1]; + p[1] = c[pt]; + } + return ui; +} + +void stlink_core_stat(stlink_t *sl) { + if (sl->q_len <= 0) + return; + + stlink_print_data(sl); + + switch (sl->q_buf[0]) { + case STLINK_CORE_RUNNING: + sl->core_stat = STLINK_CORE_RUNNING; + DD(sl, " core status: running\n"); + return; + case STLINK_CORE_HALTED: + sl->core_stat = STLINK_CORE_HALTED; + DD(sl, " core status: halted\n"); + return; + default: + sl->core_stat = STLINK_CORE_STAT_UNKNOWN; + fprintf(stderr, " core status: unknown\n"); + } +} + +void stlink_print_data(stlink_t *sl) { + if (sl->q_len <= 0 || sl->verbose < 2) + return; + if (sl->verbose > 2) + fprintf(stdout, "data_len = %d 0x%x\n", sl->q_len, sl->q_len); + + for (int i = 0; i < sl->q_len; i++) { + if (i % 16 == 0) { + /* + if (sl->q_data_dir == Q_DATA_OUT) + fprintf(stdout, "\n<- 0x%08x ", sl->q_addr + i); + else + fprintf(stdout, "\n-> 0x%08x ", sl->q_addr + i); + */ + } + fprintf(stdout, " %02x", (unsigned int) sl->q_buf[i]); + } + fputs("\n\n", stdout); +} + +/* memory mapped file */ + +typedef struct mapped_file { + uint8_t* base; + size_t len; +} mapped_file_t; + +#define MAPPED_FILE_INITIALIZER { NULL, 0 } + +static int map_file(mapped_file_t* mf, const char* path) { + int error = -1; + struct stat st; + + const int fd = open(path, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "open(%s) == -1\n", path); + return -1; + } + + if (fstat(fd, &st) == -1) { + fprintf(stderr, "fstat() == -1\n"); + goto on_error; + } + + mf->base = (uint8_t*) mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (mf->base == MAP_FAILED) { + fprintf(stderr, "mmap() == MAP_FAILED\n"); + goto on_error; + } + + mf->len = st.st_size; + + /* success */ + error = 0; + +on_error: + close(fd); + + return error; +} + +static void unmap_file(mapped_file_t* mf) { + munmap((void*) mf->base, mf->len); + mf->base = (unsigned char*) MAP_FAILED; + mf->len = 0; +} + +static int check_file +(stlink_t* sl, mapped_file_t* mf, stm32_addr_t addr) { + size_t off; + + for (off = 0; off < mf->len; off += sl->flash_pgsz) { + size_t aligned_size; + + /* adjust last page size */ + size_t cmp_size = sl->flash_pgsz; + if ((off + sl->flash_pgsz) > mf->len) + cmp_size = mf->len - off; + + aligned_size = cmp_size; + if (aligned_size & (4 - 1)) + aligned_size = (cmp_size + 4) & ~(4 - 1); + + stlink_read_mem32(sl, addr + off, aligned_size); + + if (memcmp(sl->q_buf, mf->base + off, cmp_size)) + return -1; + } + + return 0; +} + +int stlink_fwrite_sram +(stlink_t * sl, const char* path, stm32_addr_t addr) { + /* write the file in sram at addr */ + + int error = -1; + size_t off; + mapped_file_t mf = MAPPED_FILE_INITIALIZER; + + if (map_file(&mf, path) == -1) { + fprintf(stderr, "map_file() == -1\n"); + return -1; + } + + /* check addr range is inside the sram */ + if (addr < sl->sram_base) { + fprintf(stderr, "addr too low\n"); + goto on_error; + } else if ((addr + mf.len) < addr) { + fprintf(stderr, "addr overruns\n"); + goto on_error; + } else if ((addr + mf.len) > (sl->sram_base + sl->sram_size)) { + fprintf(stderr, "addr too high\n"); + goto on_error; + } else if ((addr & 3) || (mf.len & 3)) { + /* todo */ + fprintf(stderr, "unaligned addr or size\n"); + goto on_error; + } + + /* do the copy by 1k blocks */ + for (off = 0; off < mf.len; off += 1024) { + size_t size = 1024; + if ((off + size) > mf.len) + size = mf.len - off; + + memcpy(sl->q_buf, mf.base + off, size); + + /* round size if needed */ + if (size & 3) + size += 2; + + stlink_write_mem32(sl, addr + off, size); + } + + /* check the file ha been written */ + if (check_file(sl, &mf, addr) == -1) { + fprintf(stderr, "check_file() == -1\n"); + goto on_error; + } + + /* success */ + error = 0; + +on_error: + unmap_file(&mf); + return error; +} + +int stlink_fread(stlink_t* sl, const char* path, stm32_addr_t addr, size_t size) { + /* read size bytes from addr to file */ + + int error = -1; + size_t off; + + const int fd = open(path, O_RDWR | O_TRUNC | O_CREAT, 00700); + if (fd == -1) { + fprintf(stderr, "open(%s) == -1\n", path); + return -1; + } + + /* do the copy by 1k blocks */ + for (off = 0; off < size; off += 1024) { + size_t read_size = 1024; + if ((off + read_size) > size) + read_size = off + read_size; + + /* round size if needed */ + if (read_size & 3) + read_size = (read_size + 4) & ~(3); + + stlink_read_mem32(sl, addr + off, read_size); + + if (write(fd, sl->q_buf, read_size) != (ssize_t) read_size) { + fprintf(stderr, "write() != read_size\n"); + goto on_error; + } + } + + /* success */ + error = 0; + +on_error: + close(fd); + + return error; +} + +typedef struct flash_loader { + stm32_addr_t loader_addr; /* loader sram adddr */ + stm32_addr_t buf_addr; /* buffer sram address */ +} flash_loader_t; + +int write_buffer_to_sram +(stlink_t *sl, flash_loader_t* fl, const uint8_t* buf, size_t size) { + /* write the buffer right after the loader */ + memcpy(sl->q_buf, buf, size); + stlink_write_mem8(sl, fl->buf_addr, size); + return 0; +} diff --git a/src/stlink-common.h b/src/stlink-common.h new file mode 100644 index 0000000..7fbc0ab --- /dev/null +++ b/src/stlink-common.h @@ -0,0 +1,190 @@ +/* + * File: stlink-common.h + * Bulk import from stlink-hw.h + * + * This should contain all the common top level stlink interfaces, regardless + * of how the backend does the work.... + */ + +#ifndef STLINK_COMMON_H +#define STLINK_COMMON_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + + // Max data transfer size. + // 6kB = max mem32_read block, 8kB sram + //#define Q_BUF_LEN 96 +#define Q_BUF_LEN 1024 * 100 + + // st-link vendor cmd's +#define USB_ST_VID 0x0483 +#define USB_STLINK_PID 0x3744 + + // STLINK_DEBUG_RESETSYS, etc: +#define STLINK_OK 0x80 +#define STLINK_FALSE 0x81 +#define STLINK_CORE_RUNNING 0x80 +#define STLINK_CORE_HALTED 0x81 +#define STLINK_CORE_STAT_UNKNOWN -1 + +#define STLINK_GET_VERSION 0xf1 +#define STLINK_GET_CURRENT_MODE 0xf5 + +#define STLINK_DEBUG_COMMAND 0xF2 +#define STLINK_DFU_COMMAND 0xF3 +#define STLINK_DFU_EXIT 0x07 + + // STLINK_GET_CURRENT_MODE +#define STLINK_DEV_DFU_MODE 0x00 +#define STLINK_DEV_MASS_MODE 0x01 +#define STLINK_DEV_DEBUG_MODE 0x02 +#define STLINK_DEV_UNKNOWN_MODE -1 + + // jtag mode cmds +#define STLINK_DEBUG_ENTER 0x20 +#define STLINK_DEBUG_EXIT 0x21 +#define STLINK_DEBUG_READCOREID 0x22 +#define STLINK_DEBUG_GETSTATUS 0x01 +#define STLINK_DEBUG_FORCEDEBUG 0x02 +#define STLINK_DEBUG_RESETSYS 0x03 +#define STLINK_DEBUG_READALLREGS 0x04 +#define STLINK_DEBUG_READREG 0x05 +#define STLINK_DEBUG_WRITEREG 0x06 +#define STLINK_DEBUG_READMEM_32BIT 0x07 +#define STLINK_DEBUG_WRITEMEM_32BIT 0x08 +#define STLINK_DEBUG_RUNCORE 0x09 +#define STLINK_DEBUG_STEPCORE 0x0a +#define STLINK_DEBUG_SETFP 0x0b +#define STLINK_DEBUG_WRITEMEM_8BIT 0x0d +#define STLINK_DEBUG_CLEARFP 0x0e +#define STLINK_DEBUG_WRITEDEBUGREG 0x0f +#define STLINK_DEBUG_ENTER_SWD 0xa3 +#define STLINK_DEBUG_ENTER_JTAG 0x00 + + typedef struct { + uint32_t r[16]; + uint32_t xpsr; + uint32_t main_sp; + uint32_t process_sp; + uint32_t rw; + uint32_t rw2; + } reg; + + typedef uint32_t stm32_addr_t; + + enum transport_type { + TRANSPORT_TYPE_ZERO = 0, + TRANSPORT_TYPE_LIBSG, + TRANSPORT_TYPE_LIBUSB, + TRANSPORT_TYPE_INVALID + }; + + typedef struct _stlink stlink_t; + + typedef struct _stlink_backend { + void (*close) (stlink_t* sl); + void (*exit_debug_mode) (stlink_t *sl); + void (*enter_swd_mode) (stlink_t *sl); + void (*enter_jtag_mode) (stlink_t *stl); + void (*exit_dfu_mode) (stlink_t *stl); + void (*core_id) (stlink_t *stl); + void (*reset) (stlink_t *stl); + void (*run) (stlink_t *stl); + void (*status) (stlink_t *stl); + void (*version) (stlink_t *stl); + void (*write_mem32) (stlink_t *sl, uint32_t addr, uint16_t len); + void (*write_mem8) (stlink_t *sl, uint32_t addr, uint16_t len); + } stlink_backend_t; + + struct _stlink { + struct _stlink_backend *backend; + void *backend_data; + + // Data transferred from or to device + unsigned char q_buf[Q_BUF_LEN]; + int q_len; + + // transport layer verboseness: 0 for no debug info, 10 for lots + int verbose; + uint32_t core_id; + int core_stat; + + + + /* medium density stm32 flash settings */ +#define STM32_FLASH_BASE 0x08000000 +#define STM32_FLASH_SIZE (128 * 1024) +#define STM32_FLASH_PGSZ 1024 + stm32_addr_t flash_base; + size_t flash_size; + size_t flash_pgsz; + + /* in flash system memory */ +#define STM32_SYSTEM_BASE 0x1ffff000 +#define STM32_SYSTEM_SIZE (2 * 1024) + stm32_addr_t sys_base; + size_t sys_size; + + /* sram settings */ +#define STM32_SRAM_BASE 0x20000000 +#define STM32_SRAM_SIZE (8 * 1024) + stm32_addr_t sram_base; + size_t sram_size; + + }; + + // some quick and dirty logging... + void D(stlink_t *sl, char *txt); + void DD(stlink_t *sl, char *format, ...); + + //stlink_t* stlink_quirk_open(const char *dev_name, const int verbose); + + // delegated functions... + void stlink_enter_swd_mode(stlink_t *sl); + void stlink_enter_jtag_mode(stlink_t *sl); + void stlink_exit_debug_mode(stlink_t *sl); + void stlink_exit_dfu_mode(stlink_t *sl); + void stlink_close(stlink_t *sl); + void stlink_core_id(stlink_t *sl); + void stlink_reset(stlink_t *sl); + void stlink_run(stlink_t *sl); + void stlink_status(stlink_t *sl); + void stlink_version(stlink_t *sl); + void stlink_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len); + void stlink_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len); + + + // unprocessed + int stlink_current_mode(stlink_t *sl); + void stlink_force_debug(stlink_t *sl); + void stlink_step(stlink_t *sl); + void stlink_read_all_regs(stlink_t *sl); + void stlink_read_reg(stlink_t *sl, int r_idx); + void stlink_write_reg(stlink_t *sl, uint32_t reg, int idx); + void stlink_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len); + + int stlink_erase_flash_page(stlink_t* sl, stm32_addr_t page); + int stlink_erase_flash_mass(stlink_t* sl); + int stlink_write_flash(stlink_t* sl, stm32_addr_t address, uint8_t* data, unsigned length); + + // privates.... + uint16_t read_uint16(const unsigned char *c, const int pt); + void stlink_core_stat(stlink_t *sl); + void stlink_print_data(stlink_t *sl); + + +#include "stlink-sg.h" +#include "stlink-usb.h" + + + +#ifdef __cplusplus +} +#endif + +#endif /* STLINK_COMMON_H */ + diff --git a/src/stlink-hw.c b/src/stlink-hw.c deleted file mode 100644 index 9e9996c..0000000 --- a/src/stlink-hw.c +++ /dev/null @@ -1,1915 +0,0 @@ -/* - Copyright (c) 2010 "Capt'ns Missing Link" Authors. All rights reserved. - Use of this source code is governed by a BSD-style - license that can be found in the LICENSE file. - - A linux stlink access demo. The purpose of this file is to mitigate the usual - "reinventing the wheel" force by incompatible licenses and give you an idea, - how to access the stlink device. That doesn't mean you should be a free-loader - and not contribute your improvements to this code. - - Author: Martin Capitanio - The stlink related constants kindly provided by Oliver Spencer (OpenOCD) - for use in a GPL compatible license. - - Code format ~ TAB = 8, K&R, linux kernel source, golang oriented - Tested compatibility: linux, gcc >= 4.3.3 - - The communication is based on standard USB mass storage device - BOT (Bulk Only Transfer) - - Endpoint 1: BULK_IN, 64 bytes max - - Endpoint 2: BULK_OUT, 64 bytes max - - All CBW transfers are ordered with the LSB (byte 0) first (little endian). - Any command must be answered before sending the next command. - Each USB transfer must complete in less than 1s. - - SB Device Class Definition for Mass Storage Devices: - www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf - - dt - Data Transfer (IN/OUT) - CBW - Command Block Wrapper - CSW - Command Status Wrapper - RFU - Reserved for Future Use - scsi_pt - SCSI pass-through - sg - SCSI generic - - * usb-storage.quirks - http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob_plain;f=Documentation/kernel-parameters.txt - Each entry has the form VID:PID:Flags where VID and PID are Vendor and Product - ID values (4-digit hex numbers) and Flags is a set of characters, each corresponding - to a common usb-storage quirk flag as follows: - - a = SANE_SENSE (collect more than 18 bytes of sense data); - b = BAD_SENSE (don't collect more than 18 bytes of sense data); - c = FIX_CAPACITY (decrease the reported device capacity by one sector); - h = CAPACITY_HEURISTICS (decrease the reported device capacity by one sector if the number is odd); - i = IGNORE_DEVICE (don't bind to this device); - l = NOT_LOCKABLE (don't try to lock and unlock ejectable media); - m = MAX_SECTORS_64 (don't transfer more than 64 sectors = 32 KB at a time); - o = CAPACITY_OK (accept the capacity reported by the device); - r = IGNORE_RESIDUE (the device reports bogus residue values); - s = SINGLE_LUN (the device has only one Logical Unit); - w = NO_WP_DETECT (don't test whether the medium is write-protected). - - Example: quirks=0419:aaf5:rl,0421:0433:rc - http://permalink.gmane.org/gmane.linux.usb.general/35053 - - modprobe -r usb-storage && modprobe usb-storage quirks=483:3744:l - - Equivalently, you can add a line saying - - options usb-storage quirks=483:3744:l - - to your /etc/modprobe.conf or /etc/modprobe.d/local.conf (or add the "quirks=..." - part to an existing options line for usb-storage). - */ - -#define __USE_GNU -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// sgutils2 (apt-get install libsgutils2-dev) -#include -#include - -#include "stlink-hw.h" - -static void D(struct stlink *sl, char *txt) { - if (sl->verbose > 1) - fputs(txt, stderr); -} - -static void DD(struct stlink *sl, char *format, ...) { - if (sl->verbose > 0) { - va_list list; - va_start(list, format); - vfprintf(stderr, format, list); - va_end(list); - } -} - -// Suspends execution of the calling process for -// (at least) ms milliseconds. -static void delay(int ms) { - //fprintf(stderr, "*** wait %d ms\n", ms); - usleep(1000 * ms); -} - -// Endianness -// http://www.ibm.com/developerworks/aix/library/au-endianc/index.html -// const int i = 1; -// #define is_bigendian() ( (*(char*)&i) == 0 ) -static inline unsigned int is_bigendian(void) { - static volatile const unsigned int i = 1; - return *(volatile const char*) &i == 0; -} - -static void write_uint32(unsigned char* buf, uint32_t ui) { - if (!is_bigendian()) { // le -> le (don't swap) - buf[0] = ((unsigned char*) &ui)[0]; - buf[1] = ((unsigned char*) &ui)[1]; - buf[2] = ((unsigned char*) &ui)[2]; - buf[3] = ((unsigned char*) &ui)[3]; - } else { - buf[0] = ((unsigned char*) &ui)[3]; - buf[1] = ((unsigned char*) &ui)[2]; - buf[2] = ((unsigned char*) &ui)[1]; - buf[3] = ((unsigned char*) &ui)[0]; - } -} - -static void write_uint16(unsigned char* buf, uint16_t ui) { - if (!is_bigendian()) { // le -> le (don't swap) - buf[0] = ((unsigned char*) &ui)[0]; - buf[1] = ((unsigned char*) &ui)[1]; - } else { - buf[0] = ((unsigned char*) &ui)[1]; - buf[1] = ((unsigned char*) &ui)[0]; - } -} - -static uint32_t read_uint32(const unsigned char *c, const int pt) { - uint32_t ui; - char *p = (char *) &ui; - - if (!is_bigendian()) { // le -> le (don't swap) - p[0] = c[pt]; - p[1] = c[pt + 1]; - p[2] = c[pt + 2]; - p[3] = c[pt + 3]; - } else { - p[0] = c[pt + 3]; - p[1] = c[pt + 2]; - p[2] = c[pt + 1]; - p[3] = c[pt]; - } - return ui; -} - -static uint16_t read_uint16(const unsigned char *c, const int pt) { - uint32_t ui; - char *p = (char *) &ui; - - if (!is_bigendian()) { // le -> le (don't swap) - p[0] = c[pt]; - p[1] = c[pt + 1]; - } else { - p[0] = c[pt + 1]; - p[1] = c[pt]; - } - return ui; -} - -static void clear_cdb(struct stlink *sl) { - for (int i = 0; i < sizeof(sl->cdb_cmd_blk); i++) - sl->cdb_cmd_blk[i] = 0; - // set default - sl->cdb_cmd_blk[0] = STLINK_DEBUG_COMMAND; - sl->q_data_dir = Q_DATA_IN; -} - -// E.g. make the valgrind happy. -static void clear_buf(struct stlink *sl) { - DD(sl, "*** clear_buf ***\n"); - for (int i = 0; i < sizeof(sl->q_buf); i++) - sl->q_buf[i] = 0; - -} - -static struct stlink* stlink_open(const char *dev_name, const int verbose) { - fprintf(stderr, "\n*** stlink_open [%s] ***\n", dev_name); - int sg_fd = scsi_pt_open_device(dev_name, RDWR, verbose); - if (sg_fd < 0) { - fprintf(stderr, "error opening device: %s: %s\n", dev_name, - safe_strerror(-sg_fd)); - return NULL; - } - - struct stlink *sl = malloc(sizeof(struct stlink)); - if (sl == NULL) { - fprintf(stderr, "struct stlink: out of memory\n"); - return NULL; - } - - sl->sg_fd = sg_fd; - sl->verbose = verbose; - sl->core_stat = STLINK_CORE_STAT_UNKNOWN; - sl->core_id = 0; - sl->q_addr = 0; - clear_buf(sl); - - /* flash memory settings */ - sl->flash_base = STM32_FLASH_BASE; - sl->flash_size = STM32_FLASH_SIZE; - sl->flash_pgsz = STM32_FLASH_PGSZ; - - /* system memory */ - sl->sys_base = STM32_SYSTEM_BASE; - sl->sys_size = STM32_SYSTEM_SIZE; - - /* sram memory settings */ - sl->sram_base = STM32_SRAM_BASE; - sl->sram_size = STM32_SRAM_SIZE; - - return sl; -} - -// close the device, free the allocated memory -void stlink_close(struct stlink *sl) { - D(sl, "\n*** stlink_close ***\n"); - if (sl) { - scsi_pt_close_device(sl->sg_fd); - free(sl); - } -} - -//TODO rewrite/cleanup, save the error in sl -static void stlink_confirm_inq(struct stlink *sl, struct sg_pt_base *ptvp) { - const int e = sl->do_scsi_pt_err; - if (e < 0) { - fprintf(stderr, "scsi_pt error: pass through os error: %s\n", - safe_strerror(-e)); - return; - } else if (e == SCSI_PT_DO_BAD_PARAMS) { - fprintf(stderr, "scsi_pt error: bad pass through setup\n"); - return; - } else if (e == SCSI_PT_DO_TIMEOUT) { - fprintf(stderr, " pass through timeout\n"); - return; - } - const int duration = get_scsi_pt_duration_ms(ptvp); - if ((sl->verbose > 1) && (duration >= 0)) - DD(sl, " duration=%d ms\n", duration); - - // XXX stlink fw sends broken residue, so ignore it and use the known q_len - // "usb-storage quirks=483:3744:r" - // forces residue to be ignored and calculated, but this causes aboard if - // data_len = 0 and by some other data_len values. - - const int resid = get_scsi_pt_resid(ptvp); - const int dsize = sl->q_len - resid; - - const int cat = get_scsi_pt_result_category(ptvp); - char buf[512]; - unsigned int slen; - - switch (cat) { - case SCSI_PT_RESULT_GOOD: - if (sl->verbose && (resid > 0)) - DD(sl, " notice: requested %d bytes but " - "got %d bytes, ignore [broken] residue = %d\n", - sl->q_len, dsize, resid); - break; - case SCSI_PT_RESULT_STATUS: - if (sl->verbose) { - sg_get_scsi_status_str( - get_scsi_pt_status_response(ptvp), sizeof(buf), - buf); - DD(sl, " scsi status: %s\n", buf); - } - return; - case SCSI_PT_RESULT_SENSE: - slen = get_scsi_pt_sense_len(ptvp); - if (sl->verbose) { - sg_get_sense_str("", sl->sense_buf, slen, (sl->verbose - > 1), sizeof(buf), buf); - DD(sl, "%s", buf); - } - if (sl->verbose && (resid > 0)) { - if ((sl->verbose) || (sl->q_len > 0)) - DD(sl, " requested %d bytes but " - "got %d bytes\n", sl->q_len, dsize); - } - return; - case SCSI_PT_RESULT_TRANSPORT_ERR: - if (sl->verbose) { - get_scsi_pt_transport_err_str(ptvp, sizeof(buf), buf); - // http://tldp.org/HOWTO/SCSI-Generic-HOWTO/x291.html - // These codes potentially come from the firmware on a host adapter - // or from one of several hosts that an adapter driver controls. - // The 'host_status' field has the following values: - // [0x07] Internal error detected in the host adapter. - // This may not be fatal (and the command may have succeeded). - DD(sl, " transport: %s", buf); - } - return; - case SCSI_PT_RESULT_OS_ERR: - if (sl->verbose) { - get_scsi_pt_os_err_str(ptvp, sizeof(buf), buf); - DD(sl, " os: %s", buf); - } - return; - default: - fprintf(stderr, " unknown pass through result " - "category (%d)\n", cat); - } -} - -static void stlink_q(struct stlink* sl) { - DD(sl, "CDB["); - for (int i = 0; i < CDB_SL; i++) - DD(sl, " 0x%02x", (unsigned int) sl->cdb_cmd_blk[i]); - DD(sl, "]\n"); - - // Get control command descriptor of scsi structure, - // (one object per command!!) - struct sg_pt_base *ptvp = construct_scsi_pt_obj(); - if (NULL == ptvp) { - fprintf(stderr, "construct_scsi_pt_obj: out of memory\n"); - return; - } - - set_scsi_pt_cdb(ptvp, sl->cdb_cmd_blk, sizeof(sl->cdb_cmd_blk)); - - // set buffer for sense (error information) data - set_scsi_pt_sense(ptvp, sl->sense_buf, sizeof(sl->sense_buf)); - - // Set a buffer to be used for data transferred from device - if (sl->q_data_dir == Q_DATA_IN) { - //clear_buf(sl); - set_scsi_pt_data_in(ptvp, sl->q_buf, sl->q_len); - } else { - set_scsi_pt_data_out(ptvp, sl->q_buf, sl->q_len); - } - // Executes SCSI command (or at least forwards it to lower layers). - sl->do_scsi_pt_err = do_scsi_pt(ptvp, sl->sg_fd, SG_TIMEOUT_SEC, - sl->verbose); - - // check for scsi errors - stlink_confirm_inq(sl, ptvp); - // TODO recycle: clear_scsi_pt_obj(struct sg_pt_base * objp); - destruct_scsi_pt_obj(ptvp); -} - -static void stlink_print_data(struct stlink *sl) { - if (sl->q_len <= 0 || sl->verbose < 2) - return; - if (sl->verbose > 2) - fprintf(stdout, "data_len = %d 0x%x\n", sl->q_len, sl->q_len); - - for (uint32_t i = 0; i < sl->q_len; i++) { - if (i % 16 == 0) { - if (sl->q_data_dir == Q_DATA_OUT) - fprintf(stdout, "\n<- 0x%08x ", sl->q_addr + i); - else - fprintf(stdout, "\n-> 0x%08x ", sl->q_addr + i); - } - fprintf(stdout, " %02x", (unsigned int) sl->q_buf[i]); - } - fputs("\n\n", stdout); -} - -// TODO thinking, cleanup -static void stlink_parse_version(struct stlink *sl) { - sl->st_vid = 0; - sl->stlink_pid = 0; - if (sl->q_len <= 0) { - fprintf(stderr, "Error: could not parse the stlink version"); - return; - } - stlink_print_data(sl); - uint32_t b0 = sl->q_buf[0]; //lsb - uint32_t b1 = sl->q_buf[1]; - uint32_t b2 = sl->q_buf[2]; - uint32_t b3 = sl->q_buf[3]; - uint32_t b4 = sl->q_buf[4]; - uint32_t b5 = sl->q_buf[5]; //msb - - // b0 b1 || b2 b3 | b4 b5 - // 4b | 6b | 6b || 2B | 2B - // stlink_v | jtag_v | swim_v || st_vid | stlink_pid - - sl->stlink_v = (b0 & 0xf0) >> 4; - sl->jtag_v = ((b0 & 0x0f) << 2) | ((b1 & 0xc0) >> 6); - sl->swim_v = b1 & 0x3f; - sl->st_vid = (b3 << 8) | b2; - sl->stlink_pid = (b5 << 8) | b4; - - if (sl->verbose < 2) - return; - - DD(sl, "st vid = 0x%04x (expect 0x%04x)\n", - sl->st_vid, USB_ST_VID); - DD(sl, "stlink pid = 0x%04x (expect 0x%04x)\n", - sl->stlink_pid, USB_STLINK_PID); - DD(sl, "stlink version = 0x%x\n", sl->stlink_v); - DD(sl, "jtag version = 0x%x\n", sl->jtag_v); - DD(sl, "swim version = 0x%x\n", sl->swim_v); - if (sl->jtag_v == 0) - DD(sl, - " notice: the firmware doesn't support a jtag/swd interface\n"); - if (sl->swim_v == 0) - DD(sl, - " notice: the firmware doesn't support a swim interface\n"); - -} - -static int stlink_mode(struct stlink *sl) { - if (sl->q_len <= 0) - return STLINK_DEV_UNKNOWN_MODE; - - stlink_print_data(sl); - - switch (sl->q_buf[0]) { - case STLINK_DEV_DFU_MODE: - DD(sl, "stlink mode: dfu\n"); - return STLINK_DEV_DFU_MODE; - case STLINK_DEV_DEBUG_MODE: - DD(sl, "stlink mode: debug (jtag or swd)\n"); - return STLINK_DEV_DEBUG_MODE; - case STLINK_DEV_MASS_MODE: - DD(sl, "stlink mode: mass\n"); - return STLINK_DEV_MASS_MODE; - } - return STLINK_DEV_UNKNOWN_MODE; -} - -static void stlink_stat(struct stlink *sl, char *txt) { - if (sl->q_len <= 0) - return; - - stlink_print_data(sl); - - switch (sl->q_buf[0]) { - case STLINK_OK: - DD(sl, " %s: ok\n", txt); - return; - case STLINK_FALSE: - DD(sl, " %s: false\n", txt); - return; - default: - DD(sl, " %s: unknown\n", txt); - } -} - -static void stlink_core_stat(struct stlink *sl) { - if (sl->q_len <= 0) - return; - - stlink_print_data(sl); - - switch (sl->q_buf[0]) { - case STLINK_CORE_RUNNING: - sl->core_stat = STLINK_CORE_RUNNING; - DD(sl, " core status: running\n"); - return; - case STLINK_CORE_HALTED: - sl->core_stat = STLINK_CORE_HALTED; - DD(sl, " core status: halted\n"); - return; - default: - sl->core_stat = STLINK_CORE_STAT_UNKNOWN; - fprintf(stderr, " core status: unknown\n"); - } -} - -void stlink_version(struct stlink *sl) { - D(sl, "\n*** stlink_version ***\n"); - clear_cdb(sl); - sl->cdb_cmd_blk[0] = STLINK_GET_VERSION; - sl->q_len = 6; - sl->q_addr = 0; - stlink_q(sl); - stlink_parse_version(sl); -} - -// Get stlink mode: -// STLINK_DEV_DFU_MODE || STLINK_DEV_MASS_MODE || STLINK_DEV_DEBUG_MODE -// usb dfu || usb mass || jtag or swd -int stlink_current_mode(struct stlink *sl) { - D(sl, "\n*** stlink_current_mode ***\n"); - clear_cdb(sl); - sl->cdb_cmd_blk[0] = STLINK_GET_CURRENT_MODE; - sl->q_len = 2; - sl->q_addr = 0; - stlink_q(sl); - return stlink_mode(sl); -} - -// Exit the mass mode and enter the swd debug mode. -void stlink_enter_swd_mode(struct stlink *sl) { - D(sl, "\n*** stlink_enter_swd_mode ***\n"); - clear_cdb(sl); - sl->cdb_cmd_blk[1] = STLINK_DEBUG_ENTER; - sl->cdb_cmd_blk[2] = STLINK_DEBUG_ENTER_SWD; - sl->q_len = 0; // >0 -> aboard - stlink_q(sl); -} - -// Exit the mass mode and enter the jtag debug mode. -// (jtag is disabled in the discovery's stlink firmware) -void stlink_enter_jtag_mode(struct stlink *sl) { - D(sl, "\n*** stlink_enter_jtag_mode ***\n"); - clear_cdb(sl); - sl->cdb_cmd_blk[1] = STLINK_DEBUG_ENTER; - sl->cdb_cmd_blk[2] = STLINK_DEBUG_ENTER_JTAG; - sl->q_len = 0; - stlink_q(sl); -} - -// Exit the jtag or swd mode and enter the mass mode. -void stlink_exit_debug_mode(struct stlink *sl) { - D(sl, "\n*** stlink_exit_debug_mode ***\n"); - clear_cdb(sl); - sl->cdb_cmd_blk[1] = STLINK_DEBUG_EXIT; - sl->q_len = 0; // >0 -> aboard - stlink_q(sl); -} - -// XXX kernel driver performs reset, the device temporally disappears -static void stlink_exit_dfu_mode(struct stlink *sl) { - D(sl, "\n*** stlink_exit_dfu_mode ***\n"); - clear_cdb(sl); - sl->cdb_cmd_blk[0] = STLINK_DFU_COMMAND; - sl->cdb_cmd_blk[1] = STLINK_DFU_EXIT; - sl->q_len = 0; // ?? - stlink_q(sl); - /* - [135121.844564] sd 19:0:0:0: [sdb] Unhandled error code - [135121.844569] sd 19:0:0:0: [sdb] Result: hostbyte=DID_ERROR driverbyte=DRIVER_OK - [135121.844574] sd 19:0:0:0: [sdb] CDB: Read(10): 28 00 00 00 10 00 00 00 08 00 - [135121.844584] end_request: I/O error, dev sdb, sector 4096 - [135121.844590] Buffer I/O error on device sdb, logical block 512 - [135130.122567] usb 6-1: reset full speed USB device using uhci_hcd and address 7 - [135130.274551] usb 6-1: device firmware changed - [135130.274618] usb 6-1: USB disconnect, address 7 - [135130.275186] VFS: busy inodes on changed media or resized disk sdb - [135130.275424] VFS: busy inodes on changed media or resized disk sdb - [135130.286758] VFS: busy inodes on changed media or resized disk sdb - [135130.292796] VFS: busy inodes on changed media or resized disk sdb - [135130.301481] VFS: busy inodes on changed media or resized disk sdb - [135130.304316] VFS: busy inodes on changed media or resized disk sdb - [135130.431113] usb 6-1: new full speed USB device using uhci_hcd and address 8 - [135130.629444] usb-storage 6-1:1.0: Quirks match for vid 0483 pid 3744: 102a1 - [135130.629492] scsi20 : usb-storage 6-1:1.0 - [135131.625600] scsi 20:0:0:0: Direct-Access STM32 PQ: 0 ANSI: 0 - [135131.627010] sd 20:0:0:0: Attached scsi generic sg2 type 0 - [135131.633603] sd 20:0:0:0: [sdb] 64000 512-byte logical blocks: (32.7 MB/31.2 MiB) - [135131.633613] sd 20:0:0:0: [sdb] Assuming Write Enabled - [135131.633620] sd 20:0:0:0: [sdb] Assuming drive cache: write through - [135131.640584] sd 20:0:0:0: [sdb] Assuming Write Enabled - [135131.640592] sd 20:0:0:0: [sdb] Assuming drive cache: write through - [135131.640609] sdb: - [135131.652634] sd 20:0:0:0: [sdb] Assuming Write Enabled - [135131.652639] sd 20:0:0:0: [sdb] Assuming drive cache: write through - [135131.652645] sd 20:0:0:0: [sdb] Attached SCSI removable disk - [135131.671536] sd 20:0:0:0: [sdb] Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE - [135131.671548] sd 20:0:0:0: [sdb] Sense Key : Illegal Request [current] - [135131.671553] sd 20:0:0:0: [sdb] Add. Sense: Logical block address out of range - [135131.671560] sd 20:0:0:0: [sdb] CDB: Read(10): 28 00 00 00 f9 80 00 00 08 00 - [135131.671570] end_request: I/O error, dev sdb, sector 63872 - [135131.671575] Buffer I/O error on device sdb, logical block 7984 - [135131.678527] sd 20:0:0:0: [sdb] Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE - [135131.678532] sd 20:0:0:0: [sdb] Sense Key : Illegal Request [current] - [135131.678537] sd 20:0:0:0: [sdb] Add. Sense: Logical block address out of range - [135131.678542] sd 20:0:0:0: [sdb] CDB: Read(10): 28 00 00 00 f9 80 00 00 08 00 - [135131.678551] end_request: I/O error, dev sdb, sector 63872 - ... - [135131.853565] end_request: I/O error, dev sdb, sector 4096 - */ -} - -void stlink_core_id(struct stlink *sl) { - D(sl, "\n*** stlink_core_id ***\n"); - clear_cdb(sl); - sl->cdb_cmd_blk[1] = STLINK_DEBUG_READCOREID; - sl->q_len = 4; - sl->q_addr = 0; - stlink_q(sl); - sl->core_id = read_uint32(sl->q_buf, 0); - if (sl->verbose < 2) - return; - stlink_print_data(sl); - DD(sl, "core_id = 0x%08x\n", sl->core_id); -} - -// Arm-core reset -> halted state. -void stlink_reset(struct stlink *sl) { - D(sl, "\n*** stlink_reset ***\n"); - clear_cdb(sl); - sl->cdb_cmd_blk[1] = STLINK_DEBUG_RESETSYS; - sl->q_len = 2; - sl->q_addr = 0; - stlink_q(sl); - stlink_stat(sl, "core reset"); -} - -// Arm-core status: halted or running. -void stlink_status(struct stlink *sl) { - D(sl, "\n*** stlink_status ***\n"); - clear_cdb(sl); - sl->cdb_cmd_blk[1] = STLINK_DEBUG_GETSTATUS; - sl->q_len = 2; - sl->q_addr = 0; - stlink_q(sl); - stlink_core_stat(sl); -} - -// Force the core into the debug mode -> halted state. -void stlink_force_debug(struct stlink *sl) { - D(sl, "\n*** stlink_force_debug ***\n"); - clear_cdb(sl); - sl->cdb_cmd_blk[1] = STLINK_DEBUG_FORCEDEBUG; - sl->q_len = 2; - sl->q_addr = 0; - stlink_q(sl); - stlink_stat(sl, "force debug"); -} - -// Read all arm-core registers. -void stlink_read_all_regs(struct stlink *sl) { - D(sl, "\n*** stlink_read_all_regs ***\n"); - clear_cdb(sl); - sl->cdb_cmd_blk[1] = STLINK_DEBUG_READALLREGS; - sl->q_len = 84; - sl->q_addr = 0; - stlink_q(sl); - stlink_print_data(sl); - - // 0-3 | 4-7 | ... | 60-63 | 64-67 | 68-71 | 72-75 | 76-79 | 80-83 - // r0 | r1 | ... | r15 | xpsr | main_sp | process_sp | rw | rw2 - for (int i = 0; i < 16; i++) { - sl->reg.r[i] = read_uint32(sl->q_buf, 4 * i); - if (sl->verbose > 1) - DD(sl, "r%2d = 0x%08x\n", i, sl->reg.r[i]); - } - sl->reg.xpsr = read_uint32(sl->q_buf, 64); - sl->reg.main_sp = read_uint32(sl->q_buf, 68); - sl->reg.process_sp = read_uint32(sl->q_buf, 72); - sl->reg.rw = read_uint32(sl->q_buf, 76); - sl->reg.rw2 = read_uint32(sl->q_buf, 80); - if (sl->verbose < 2) - return; - - DD(sl, "xpsr = 0x%08x\n", sl->reg.xpsr); - DD(sl, "main_sp = 0x%08x\n", sl->reg.main_sp); - DD(sl, "process_sp = 0x%08x\n", sl->reg.process_sp); - DD(sl, "rw = 0x%08x\n", sl->reg.rw); - DD(sl, "rw2 = 0x%08x\n", sl->reg.rw2); -} - -// Read an arm-core register, the index must be in the range 0..20. -// 0 | 1 | ... | 15 | 16 | 17 | 18 | 19 | 20 -// r0 | r1 | ... | r15 | xpsr | main_sp | process_sp | rw | rw2 -void stlink_read_reg(struct stlink *sl, int r_idx) { - D(sl, "\n*** stlink_read_reg"); - DD(sl, " (%d) ***\n", r_idx); - - if (r_idx > 20 || r_idx < 0) { - fprintf(stderr, "Error: register index must be in [0..20]\n"); - return; - } - clear_cdb(sl); - sl->cdb_cmd_blk[1] = STLINK_DEBUG_READREG; - sl->cdb_cmd_blk[2] = r_idx; - sl->q_len = 4; - sl->q_addr = 0; - stlink_q(sl); - // 0 | 1 | ... | 15 | 16 | 17 | 18 | 19 | 20 - // 0-3 | 4-7 | ... | 60-63 | 64-67 | 68-71 | 72-75 | 76-79 | 80-83 - // r0 | r1 | ... | r15 | xpsr | main_sp | process_sp | rw | rw2 - stlink_print_data(sl); - - uint32_t r = read_uint32(sl->q_buf, 0); - DD(sl, "r_idx (%2d) = 0x%08x\n", r_idx, r); - - switch (r_idx) { - case 16: - sl->reg.xpsr = r; - break; - case 17: - sl->reg.main_sp = r; - break; - case 18: - sl->reg.process_sp = r; - break; - case 19: - sl->reg.rw = r; //XXX ?(primask, basemask etc.) - break; - case 20: - sl->reg.rw2 = r; //XXX ?(primask, basemask etc.) - break; - default: - sl->reg.r[r_idx] = r; - } -} - -// Write an arm-core register. Index: -// 0 | 1 | ... | 15 | 16 | 17 | 18 | 19 | 20 -// r0 | r1 | ... | r15 | xpsr | main_sp | process_sp | rw | rw2 -void stlink_write_reg(struct stlink *sl, uint32_t reg, int idx) { - D(sl, "\n*** stlink_write_reg ***\n"); - clear_cdb(sl); - sl->cdb_cmd_blk[1] = STLINK_DEBUG_WRITEREG; - // 2: reg index - // 3-6: reg content - sl->cdb_cmd_blk[2] = idx; - write_uint32(sl->cdb_cmd_blk + 3, reg); - sl->q_len = 2; - sl->q_addr = 0; - stlink_q(sl); - stlink_stat(sl, "write reg"); -} - -// Write a register of the debug module of the core. -// XXX ?(atomic writes) -// TODO test -void stlink_write_dreg(struct stlink *sl, uint32_t reg, uint32_t addr) { - D(sl, "\n*** stlink_write_dreg ***\n"); - clear_cdb(sl); - sl->cdb_cmd_blk[1] = STLINK_DEBUG_WRITEDEBUGREG; - // 2-5: address of reg of the debug module - // 6-9: reg content - write_uint32(sl->cdb_cmd_blk + 2, addr); - write_uint32(sl->cdb_cmd_blk + 6, reg); - sl->q_len = 2; - sl->q_addr = addr; - stlink_q(sl); - stlink_stat(sl, "write debug reg"); -} - -// Force the core exit the debug mode. -void stlink_run(struct stlink *sl) { - D(sl, "\n*** stlink_run ***\n"); - clear_cdb(sl); - sl->cdb_cmd_blk[1] = STLINK_DEBUG_RUNCORE; - sl->q_len = 2; - sl->q_addr = 0; - stlink_q(sl); - stlink_stat(sl, "run core"); -} - -// same as above with entrypoint. -static unsigned int is_core_halted(struct stlink*); -void stlink_run_at(struct stlink *sl, stm32_addr_t addr) { - stlink_write_reg(sl, addr, 15); /* pc register */ - - stlink_run(sl); - - while (is_core_halted(sl) == 0) - usleep(3000000); -} - -// Step the arm-core. -void stlink_step(struct stlink *sl) { - D(sl, "\n*** stlink_step ***\n"); - clear_cdb(sl); - sl->cdb_cmd_blk[1] = STLINK_DEBUG_STEPCORE; - sl->q_len = 2; - sl->q_addr = 0; - stlink_q(sl); - stlink_stat(sl, "step core"); -} - -// TODO test -// see Cortex-M3 Technical Reference Manual -void stlink_set_hw_bp(struct stlink *sl, int fp_nr, uint32_t addr, int fp) { - D(sl, "\n*** stlink_set_hw_bp ***\n"); - clear_cdb(sl); - sl->cdb_cmd_blk[1] = STLINK_DEBUG_SETFP; - // 2:The number of the flash patch used to set the breakpoint - // 3-6: Address of the breakpoint (LSB) - // 7: FP_ALL (0x02) / FP_UPPER (0x01) / FP_LOWER (0x00) - sl->q_buf[2] = fp_nr; - write_uint32(sl->q_buf, addr); - sl->q_buf[7] = fp; - - sl->q_len = 2; - stlink_q(sl); - stlink_stat(sl, "set flash breakpoint"); -} - -// TODO test -void stlink_clr_hw_bp(struct stlink *sl, int fp_nr) { - D(sl, "\n*** stlink_clr_hw_bp ***\n"); - clear_cdb(sl); - sl->cdb_cmd_blk[1] = STLINK_DEBUG_CLEARFP; - sl->cdb_cmd_blk[2] = fp_nr; - - sl->q_len = 2; - stlink_q(sl); - stlink_stat(sl, "clear flash breakpoint"); -} - -// Read a "len" bytes to the sl->q_buf from the memory, max 6kB (6144 bytes) -void stlink_read_mem32(struct stlink *sl, uint32_t addr, uint16_t len) { - D(sl, "\n*** stlink_read_mem32 ***\n"); - if (len % 4 != 0) { // !!! never ever: fw gives just wrong values - fprintf( - stderr, - "Error: Data length doesn't have a 32 bit alignment: +%d byte.\n", - len % 4); - return; - } - clear_cdb(sl); - sl->cdb_cmd_blk[1] = STLINK_DEBUG_READMEM_32BIT; - // 2-5: addr - // 6-7: len - write_uint32(sl->cdb_cmd_blk + 2, addr); - write_uint16(sl->cdb_cmd_blk + 6, len); - - // data_in 0-0x40-len - // !!! len _and_ q_len must be max 6k, - // i.e. >1024 * 6 = 6144 -> aboard) - // !!! if len < q_len: 64*k, 1024*n, n=1..5 -> aboard - // (broken residue issue) - sl->q_len = len; - sl->q_addr = addr; - stlink_q(sl); - stlink_print_data(sl); -} - -// Write a "len" bytes from the sl->q_buf to the memory, max 64 Bytes. -void stlink_write_mem8(struct stlink *sl, uint32_t addr, uint16_t len) { - D(sl, "\n*** stlink_write_mem8 ***\n"); - clear_cdb(sl); - sl->cdb_cmd_blk[1] = STLINK_DEBUG_WRITEMEM_8BIT; - // 2-5: addr - // 6-7: len (>0x40 (64) -> aboard) - write_uint32(sl->cdb_cmd_blk + 2, addr); - write_uint16(sl->cdb_cmd_blk + 6, len); - - // data_out 0-len - sl->q_len = len; - sl->q_addr = addr; - sl->q_data_dir = Q_DATA_OUT; - stlink_q(sl); - stlink_print_data(sl); -} - -// Write a "len" bytes from the sl->q_buf to the memory, max Q_BUF_LEN bytes. -void stlink_write_mem32(struct stlink *sl, uint32_t addr, uint16_t len) { - D(sl, "\n*** stlink_write_mem32 ***\n"); - if (len % 4 != 0) { - fprintf( - stderr, - "Error: Data length doesn't have a 32 bit alignment: +%d byte.\n", - len % 4); - return; - } - clear_cdb(sl); - sl->cdb_cmd_blk[1] = STLINK_DEBUG_WRITEMEM_32BIT; - // 2-5: addr - // 6-7: len "unlimited" - write_uint32(sl->cdb_cmd_blk + 2, addr); - write_uint16(sl->cdb_cmd_blk + 6, len); - - // data_out 0-0x40-...-len - sl->q_len = len; - sl->q_addr = addr; - sl->q_data_dir = Q_DATA_OUT; - stlink_q(sl); - stlink_print_data(sl); -} - -/* FPEC flash controller interface, pm0063 manual - */ - -#define FLASH_REGS_ADDR 0x40022000 -#define FLASH_REGS_SIZE 0x28 - -#define FLASH_ACR (FLASH_REGS_ADDR + 0x00) -#define FLASH_KEYR (FLASH_REGS_ADDR + 0x04) -#define FLASH_SR (FLASH_REGS_ADDR + 0x0c) -#define FLASH_CR (FLASH_REGS_ADDR + 0x10) -#define FLASH_AR (FLASH_REGS_ADDR + 0x14) -#define FLASH_OBR (FLASH_REGS_ADDR + 0x1c) -#define FLASH_WRPR (FLASH_REGS_ADDR + 0x20) - -#define FLASH_RDPTR_KEY 0x00a5 -#define FLASH_KEY1 0x45670123 -#define FLASH_KEY2 0xcdef89ab - -#define FLASH_SR_BSY 0 -#define FLASH_SR_EOP 5 - -#define FLASH_CR_PG 0 -#define FLASH_CR_PER 1 -#define FLASH_CR_MER 2 -#define FLASH_CR_STRT 6 -#define FLASH_CR_LOCK 7 - -static uint32_t __attribute__((unused)) read_flash_rdp(struct stlink* sl) -{ - stlink_read_mem32(sl, FLASH_WRPR, sizeof(uint32_t)); - return (*(uint32_t*)sl->q_buf) & 0xff; -} - -static inline uint32_t read_flash_wrpr(struct stlink* sl) -{ - stlink_read_mem32(sl, FLASH_WRPR, sizeof(uint32_t)); - return *(uint32_t*)sl->q_buf; -} - -static inline uint32_t read_flash_obr(struct stlink* sl) -{ - stlink_read_mem32(sl, FLASH_OBR, sizeof(uint32_t)); - return *(uint32_t*)sl->q_buf; -} - -static inline uint32_t read_flash_cr(struct stlink* sl) -{ - stlink_read_mem32(sl, FLASH_CR, sizeof(uint32_t)); - return *(uint32_t*)sl->q_buf; -} - -static inline unsigned int is_flash_locked(struct stlink* sl) -{ - /* return non zero for true */ - return read_flash_cr(sl) & (1 << FLASH_CR_LOCK); -} - -static void unlock_flash(struct stlink* sl) -{ - /* the unlock sequence consists of 2 write cycles where - 2 key values are written to the FLASH_KEYR register. - an invalid sequence results in a definitive lock of - the FPEC block until next reset. - */ - - write_uint32(sl->q_buf, FLASH_KEY1); - stlink_write_mem32(sl, FLASH_KEYR, sizeof(uint32_t)); - - write_uint32(sl->q_buf, FLASH_KEY2); - stlink_write_mem32(sl, FLASH_KEYR, sizeof(uint32_t)); -} - -static int unlock_flash_if(struct stlink* sl) -{ - /* unlock flash if already locked */ - - if (is_flash_locked(sl)) - { - unlock_flash(sl); - if (is_flash_locked(sl)) - return -1; - } - - return 0; -} - -static void lock_flash(struct stlink* sl) -{ - /* write to 1 only. reset by hw at unlock sequence */ - - const uint32_t n = read_flash_cr(sl) | (1 << FLASH_CR_LOCK); - - write_uint32(sl->q_buf, n); - stlink_write_mem32(sl, FLASH_CR, sizeof(uint32_t)); -} - -static void set_flash_cr_pg(struct stlink* sl) -{ - const uint32_t n = 1 << FLASH_CR_PG; - write_uint32(sl->q_buf, n); - stlink_write_mem32(sl, FLASH_CR, sizeof(uint32_t)); -} - -static void __attribute__((unused)) clear_flash_cr_pg(struct stlink* sl) -{ - const uint32_t n = read_flash_cr(sl) & ~(1 << FLASH_CR_PG); - write_uint32(sl->q_buf, n); - stlink_write_mem32(sl, FLASH_CR, sizeof(uint32_t)); -} - -static void set_flash_cr_per(struct stlink* sl) -{ - const uint32_t n = 1 << FLASH_CR_PER; - write_uint32(sl->q_buf, n); - stlink_write_mem32(sl, FLASH_CR, sizeof(uint32_t)); -} - -static void __attribute__((unused)) clear_flash_cr_per(struct stlink* sl) -{ - const uint32_t n = read_flash_cr(sl) & ~(1 << FLASH_CR_PER); - write_uint32(sl->q_buf, n); - stlink_write_mem32(sl, FLASH_CR, sizeof(uint32_t)); -} - -static void set_flash_cr_mer(struct stlink* sl) -{ - const uint32_t n = 1 << FLASH_CR_MER; - write_uint32(sl->q_buf, n); - stlink_write_mem32(sl, FLASH_CR, sizeof(uint32_t)); -} - -static void __attribute__((unused)) clear_flash_cr_mer(struct stlink* sl) -{ - const uint32_t n = read_flash_cr(sl) & ~(1 << FLASH_CR_MER); - write_uint32(sl->q_buf, n); - stlink_write_mem32(sl, FLASH_CR, sizeof(uint32_t)); -} - -static void set_flash_cr_strt(struct stlink* sl) -{ - /* assume come on the flash_cr_per path */ - const uint32_t n = (1 << FLASH_CR_PER) | (1 << FLASH_CR_STRT); - write_uint32(sl->q_buf, n); - stlink_write_mem32(sl, FLASH_CR, sizeof(uint32_t)); -} - -static inline uint32_t read_flash_acr(struct stlink* sl) -{ - stlink_read_mem32(sl, FLASH_ACR, sizeof(uint32_t)); - return *(uint32_t*)sl->q_buf; -} - -static inline uint32_t read_flash_sr(struct stlink* sl) -{ - stlink_read_mem32(sl, FLASH_SR, sizeof(uint32_t)); - return *(uint32_t*)sl->q_buf; -} - -static inline unsigned int is_flash_busy(struct stlink* sl) -{ - return read_flash_sr(sl) & (1 << FLASH_SR_BSY); -} - -static void wait_flash_busy(struct stlink* sl) -{ - /* todo: add some delays here */ - while (is_flash_busy(sl)) - ; -} - -static inline unsigned int is_flash_eop(struct stlink* sl) -{ - return read_flash_sr(sl) & (1 << FLASH_SR_EOP); -} - -static void __attribute__((unused)) clear_flash_sr_eop(struct stlink* sl) -{ - const uint32_t n = read_flash_sr(sl) & ~(1 << FLASH_SR_EOP); - write_uint32(sl->q_buf, n); - stlink_write_mem32(sl, FLASH_SR, sizeof(uint32_t)); -} - -static void __attribute__((unused)) wait_flash_eop(struct stlink* sl) -{ - /* todo: add some delays here */ - while (is_flash_eop(sl) == 0) - ; -} - -static inline void write_flash_ar(struct stlink* sl, uint32_t n) -{ - write_uint32(sl->q_buf, n); - stlink_write_mem32(sl, FLASH_AR, sizeof(uint32_t)); -} - -#if 0 /* todo */ -static void disable_flash_read_protection(struct stlink* sl) -{ - /* erase the option byte area */ - /* rdp = 0x00a5; */ - /* reset */ -} -#endif /* todo */ - -#if 0 /* not working */ -static int write_flash_mem16 -(struct stlink* sl, uint32_t addr, uint16_t val) -{ - /* half word writes */ - if (addr % 2) return -1; - - /* unlock if locked */ - unlock_flash_if(sl); - - /* set flash programming chosen bit */ - set_flash_cr_pg(sl); - - write_uint16(sl->q_buf, val); - stlink_write_mem16(sl, addr, 2); - - /* wait for non business */ - wait_flash_busy(sl); - - lock_flash(sl); - - /* check the programmed value back */ - stlink_read_mem16(sl, addr, 2); - if (*(const uint16_t*)sl->q_buf != val) - { - /* values differ at i * sizeof(uint16_t) */ - return -1; - } - - /* success */ - return 0; -} -#endif /* not working */ - -int stlink_erase_flash_page(struct stlink* sl, stm32_addr_t page) -{ - /* page an addr in the page to erase */ - - /* wait for ongoing op to finish */ - wait_flash_busy(sl); - - /* unlock if locked */ - unlock_flash_if(sl); - - /* set the page erase bit */ - set_flash_cr_per(sl); - - /* select the page to erase */ - write_flash_ar(sl, page); - - /* start erase operation, reset by hw with bsy bit */ - set_flash_cr_strt(sl); - - /* wait for completion */ - wait_flash_busy(sl); - - /* relock the flash */ - lock_flash(sl); - - /* todo: verify the erased page */ - - return 0; -} - -int stlink_erase_flash_mass(struct stlink* sl) -{ - /* wait for ongoing op to finish */ - wait_flash_busy(sl); - - /* unlock if locked */ - unlock_flash_if(sl); - - /* set the mass erase bit */ - set_flash_cr_mer(sl); - - /* start erase operation, reset by hw with bsy bit */ - set_flash_cr_strt(sl); - - /* wait for completion */ - wait_flash_busy(sl); - - /* relock the flash */ - lock_flash(sl); - - /* todo: verify the erased memory */ - - return 0; -} - -static unsigned int is_core_halted(struct stlink* sl) -{ - /* return non zero if core is halted */ - stlink_status(sl); - return sl->q_buf[0] == STLINK_CORE_HALTED; -} - -static int write_loader_to_sram -(struct stlink* sl, stm32_addr_t* addr, size_t* size) -{ - /* from openocd, contrib/loaders/flash/stm32.s */ - static const uint8_t loader_code[] = - { - 0x08, 0x4c, /* ldr r4, STM32_FLASH_BASE */ - 0x1c, 0x44, /* add r4, r3 */ - /* write_half_word: */ - 0x01, 0x23, /* movs r3, #0x01 */ - 0x23, 0x61, /* str r3, [r4, #STM32_FLASH_CR_OFFSET] */ - 0x30, 0xf8, 0x02, 0x3b, /* ldrh r3, [r0], #0x02 */ - 0x21, 0xf8, 0x02, 0x3b, /* strh r3, [r1], #0x02 */ - /* busy: */ - 0xe3, 0x68, /* ldr r3, [r4, #STM32_FLASH_SR_OFFSET] */ - 0x13, 0xf0, 0x01, 0x0f, /* tst r3, #0x01 */ - 0xfb, 0xd0, /* beq busy */ - 0x13, 0xf0, 0x14, 0x0f, /* tst r3, #0x14 */ - 0x01, 0xd1, /* bne exit */ - 0x01, 0x3a, /* subs r2, r2, #0x01 */ - 0xf0, 0xd1, /* bne write_half_word */ - /* exit: */ - 0x00, 0xbe, /* bkpt #0x00 */ - 0x00, 0x20, 0x02, 0x40, /* STM32_FLASH_BASE: .word 0x40022000 */ - }; - - memcpy(sl->q_buf, loader_code, sizeof(loader_code)); - stlink_write_mem32(sl, sl->sram_base, sizeof(loader_code)); - - *addr = sl->sram_base; - *size = sizeof(loader_code); - - /* success */ - return 0; -} - -typedef struct flash_loader -{ - stm32_addr_t loader_addr; /* loader sram adddr */ - stm32_addr_t buf_addr; /* buffer sram address */ -} flash_loader_t; - -static int write_buffer_to_sram -(struct stlink* sl, flash_loader_t* fl, const uint8_t* buf, size_t size) -{ - /* write the buffer right after the loader */ - memcpy(sl->q_buf, buf, size); - stlink_write_mem8(sl, fl->buf_addr, size); - return 0; -} - -static int init_flash_loader -(struct stlink* sl, flash_loader_t* fl) -{ - size_t size; - - /* allocate the loader in sram */ - if (write_loader_to_sram(sl, &fl->loader_addr, &size) == -1) - { - fprintf(stderr, "write_loader_to_sram() == -1\n"); - return -1; - } - - /* allocate a one page buffer in sram right after loader */ - fl->buf_addr = fl->loader_addr + size; - - return 0; -} - -static int run_flash_loader -(struct stlink* sl, flash_loader_t* fl, stm32_addr_t target, const uint8_t* buf, size_t size) -{ - const size_t count = size / sizeof(uint16_t); - - if (write_buffer_to_sram(sl, fl, buf, size) == -1) - { - fprintf(stderr, "write_buffer_to_sram() == -1\n"); - return -1; - } - - /* setup core */ - stlink_write_reg(sl, fl->buf_addr, 0); /* source */ - stlink_write_reg(sl, target, 1); /* target */ - stlink_write_reg(sl, count, 2); /* count (16 bits half words) */ - stlink_write_reg(sl, 0, 3); /* flash bank 0 (input) */ - stlink_write_reg(sl, fl->loader_addr, 15); /* pc register */ - - /* unlock and set programming mode */ - unlock_flash_if(sl); - set_flash_cr_pg(sl); - - /* run loader */ - stlink_run(sl); - - while (is_core_halted(sl) == 0) - ; - - lock_flash(sl); - - /* not all bytes have been written */ - stlink_read_reg(sl, 2); - if (sl->reg.r[2] != 0) - { - fprintf(stderr, "write error, count == %u\n", sl->reg.r[2]); - return -1; - } - - return 0; -} - -/* memory mapped file */ - -typedef struct mapped_file -{ - uint8_t* base; - size_t len; -} mapped_file_t; - -#define MAPPED_FILE_INITIALIZER { NULL, 0 } - -static int map_file(mapped_file_t* mf, const char* path) -{ - int error = -1; - struct stat st; - - const int fd = open(path, O_RDONLY); - if (fd == -1) - { - fprintf(stderr, "open(%s) == -1\n", path); - return -1; - } - - if (fstat(fd, &st) == -1) - { - fprintf(stderr, "fstat() == -1\n"); - goto on_error; - } - - mf->base = (uint8_t*)mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); - if (mf->base == MAP_FAILED) - { - fprintf(stderr, "mmap() == MAP_FAILED\n"); - goto on_error; - } - - mf->len = st.st_size; - - /* success */ - error = 0; - - on_error: - close(fd); - - return error; -} - -static void unmap_file(mapped_file_t* mf) -{ - munmap((void*)mf->base, mf->len); - mf->base = (unsigned char*)MAP_FAILED; - mf->len = 0; -} - -static int check_file -(struct stlink* sl, mapped_file_t* mf, stm32_addr_t addr) -{ - size_t off; - - for (off = 0; off < mf->len; off += sl->flash_pgsz) - { - size_t aligned_size; - - /* adjust last page size */ - size_t cmp_size = sl->flash_pgsz; - if ((off + sl->flash_pgsz) > mf->len) - cmp_size = mf->len - off; - - aligned_size = cmp_size; - if (aligned_size & (4 - 1)) - aligned_size = (cmp_size + 4) & ~(4 - 1); - - stlink_read_mem32(sl, addr + off, aligned_size); - - if (memcmp(sl->q_buf, mf->base + off, cmp_size)) - return -1; - } - - return 0; -} - -static int stlink_fcheck_flash -(struct stlink* sl, const char* path, stm32_addr_t addr) -{ - /* check the contents of path are at addr */ - - int res; - mapped_file_t mf = MAPPED_FILE_INITIALIZER; - - if (map_file(&mf, path) == -1) - return -1; - - res = check_file(sl, &mf, addr); - - unmap_file(&mf); - - return res; -} - -// The stlink_fwrite_flash should not muck with mmapped files inside itself, -// and should use this function instead. (Hell, what's the reason behind mmap -// there?!) But, as it is not actually used anywhere, nobody cares. - -#define WRITE_BLOCK_SIZE 0x40 -int stlink_write_flash(struct stlink* sl, stm32_addr_t addr, uint8_t* base, unsigned len) { - int error = -1; - size_t off; - flash_loader_t fl; - - /* check addr range is inside the flash */ - if (addr < sl->flash_base) { - fprintf(stderr, "addr too low\n"); - return -1; - } else if ((addr + len) < addr) { - fprintf(stderr, "addr overruns\n"); - return -1; - } else if ((addr + len) > (sl->flash_base + sl->flash_size)) { - fprintf(stderr, "addr too high\n"); - return -1; - } else if ((addr & 1) || (len & 1)) { - fprintf(stderr, "unaligned addr or size\n"); - return -1; - } - - /* flash loader initialization */ - if (init_flash_loader(sl, &fl) == -1) { - fprintf(stderr, "init_flash_loader() == -1\n"); - return -1; - } - - /* write each page. above WRITE_BLOCK_SIZE fails? */ - for (off = 0; off < len; off += WRITE_BLOCK_SIZE) { - /* adjust last write size */ - size_t size = WRITE_BLOCK_SIZE; - if((off + WRITE_BLOCK_SIZE) > len) - size = len - off; - - if(run_flash_loader(sl, &fl, addr + off, base + off, size) == -1) { - fprintf(stderr, "run_flash_loader(0x%zx) == -1\n", addr + off); - return -1; - } - } - - for(off = 0; off < len; off += sl->flash_pgsz) { - size_t aligned_size; - - /* adjust last page size */ - size_t cmp_size = sl->flash_pgsz; - if ((off + sl->flash_pgsz) > len) - cmp_size = len - off; - - aligned_size = cmp_size; - if (aligned_size & (4 - 1)) - aligned_size = (cmp_size + 4) & ~(4 - 1); - - stlink_read_mem32(sl, addr + off, aligned_size); - - if (memcmp(sl->q_buf, base + off, cmp_size)) - return -1; - } - - return 0; -} - -static int stlink_fwrite_flash -(struct stlink* sl, const char* path, stm32_addr_t addr) -{ - /* write the file in flash at addr */ - - int error = -1; - size_t off; - mapped_file_t mf = MAPPED_FILE_INITIALIZER; - flash_loader_t fl; - - if (map_file(&mf, path) == -1) - { - fprintf(stderr, "map_file() == -1\n"); - return -1; - } - - /* check addr range is inside the flash */ - if (addr < sl->flash_base) - { - fprintf(stderr, "addr too low\n"); - goto on_error; - } - else if ((addr + mf.len) < addr) - { - fprintf(stderr, "addr overruns\n"); - goto on_error; - } - else if ((addr + mf.len) > (sl->flash_base + sl->flash_size)) - { - fprintf(stderr, "addr too high\n"); - goto on_error; - } - else if ((addr & 1) || (mf.len & 1)) - { - /* todo */ - fprintf(stderr, "unaligned addr or size\n"); - goto on_error; - } - - /* erase each page. todo: mass erase faster? */ - for (off = 0; off < mf.len; off += sl->flash_pgsz) - { - /* addr must be an addr inside the page */ - if (stlink_erase_flash_page(sl, addr + off) == -1) - { - fprintf(stderr, "erase_flash_page(0x%zx) == -1\n", addr + off); - goto on_error; - } - } - - /* flash loader initialization */ - if (init_flash_loader(sl, &fl) == -1) - { - fprintf(stderr, "init_flash_loader() == -1\n"); - goto on_error; - } - - /* write each page. above WRITE_BLOCK_SIZE fails? */ -#define WRITE_BLOCK_SIZE 0x40 - for (off = 0; off < mf.len; off += WRITE_BLOCK_SIZE) - { - /* adjust last write size */ - size_t size = WRITE_BLOCK_SIZE; - if ((off + WRITE_BLOCK_SIZE) > mf.len) - size = mf.len - off; - - if (run_flash_loader(sl, &fl, addr + off, mf.base + off, size) == -1) - { - fprintf(stderr, "run_flash_loader(0x%zx) == -1\n", addr + off); - goto on_error; - } - } - - /* check the file ha been written */ - if (check_file(sl, &mf, addr) == -1) - { - fprintf(stderr, "check_file() == -1\n"); - goto on_error; - } - - /* success */ - error = 0; - - on_error: - unmap_file(&mf); - return error; -} - -static int stlink_fwrite_sram -(struct stlink* sl, const char* path, stm32_addr_t addr) -{ - /* write the file in sram at addr */ - - int error = -1; - size_t off; - mapped_file_t mf = MAPPED_FILE_INITIALIZER; - - if (map_file(&mf, path) == -1) - { - fprintf(stderr, "map_file() == -1\n"); - return -1; - } - - /* check addr range is inside the sram */ - if (addr < sl->sram_base) - { - fprintf(stderr, "addr too low\n"); - goto on_error; - } - else if ((addr + mf.len) < addr) - { - fprintf(stderr, "addr overruns\n"); - goto on_error; - } - else if ((addr + mf.len) > (sl->sram_base + sl->sram_size)) - { - fprintf(stderr, "addr too high\n"); - goto on_error; - } - else if ((addr & 3) || (mf.len & 3)) - { - /* todo */ - fprintf(stderr, "unaligned addr or size\n"); - goto on_error; - } - - /* do the copy by 1k blocks */ - for (off = 0; off < mf.len; off += 1024) - { - size_t size = 1024; - if ((off + size) > mf.len) - size = mf.len - off; - - memcpy(sl->q_buf, mf.base + off, size); - - /* round size if needed */ - if (size & 3) - size += 2; - - stlink_write_mem32(sl, addr + off, size); - } - - /* check the file ha been written */ - if (check_file(sl, &mf, addr) == -1) - { - fprintf(stderr, "check_file() == -1\n"); - goto on_error; - } - - /* success */ - error = 0; - - on_error: - unmap_file(&mf); - return error; -} - - -static int stlink_fread -(struct stlink* sl, const char* path, stm32_addr_t addr, size_t size) -{ - /* read size bytes from addr to file */ - - int error = -1; - size_t off; - - const int fd = open(path, O_RDWR | O_TRUNC | O_CREAT, 00700); - if (fd == -1) - { - fprintf(stderr, "open(%s) == -1\n", path); - return -1; - } - - /* do the copy by 1k blocks */ - for (off = 0; off < size; off += 1024) - { - size_t read_size = 1024; - if ((off + read_size) > size) - read_size = off + read_size; - - /* round size if needed */ - if (read_size & 3) - read_size = (read_size + 4) & ~(3); - - stlink_read_mem32(sl, addr + off, read_size); - - if (write(fd, sl->q_buf, read_size) != (ssize_t)read_size) - { - fprintf(stderr, "write() != read_size\n"); - goto on_error; - } - } - - /* success */ - error = 0; - - on_error: - close(fd); - - return error; -} - -// 1) open a sg device, switch the stlink from dfu to mass mode -// 2) wait 5s until the kernel driver stops reseting the broken device -// 3) reopen the device -// 4) the device driver is now ready for a switch to jtag/swd mode -// TODO thinking, better error handling, wait until the kernel driver stops reseting the plugged-in device -struct stlink* stlink_quirk_open(const char *dev_name, const int verbose) { - struct stlink *sl = stlink_open(dev_name, verbose); - if (sl == NULL) { - fputs("Error: could not open stlink device\n", stderr); - return NULL; - } - - stlink_version(sl); - - if (sl->st_vid != USB_ST_VID || sl->stlink_pid != USB_STLINK_PID) { - fprintf(stderr, "Error: the device %s is not a stlink\n", - dev_name); - fprintf(stderr, " VID: got %04x expect %04x \n", - sl->st_vid, USB_ST_VID); - fprintf(stderr, " PID: got %04x expect %04x \n", - sl->stlink_pid, USB_STLINK_PID); - return NULL; - } - - D(sl, "\n*** stlink_force_open ***\n"); - switch (stlink_current_mode(sl)) { - case STLINK_DEV_MASS_MODE: - return sl; - case STLINK_DEV_DEBUG_MODE: - // TODO go to mass? - return sl; - } - DD(sl, "\n*** switch the stlink to mass mode ***\n"); - stlink_exit_dfu_mode(sl); - // exit the dfu mode -> the device is gone - DD(sl, "\n*** reopen the stlink device ***\n"); - delay(1000); - stlink_close(sl); - delay(5000); - - sl = stlink_open(dev_name, verbose); - if (sl == NULL) { - fputs("Error: could not open stlink device\n", stderr); - return NULL; - } - // re-query device info - stlink_version(sl); - return sl; -} - -static void __attribute__((unused)) mark_buf(struct stlink *sl) { - clear_buf(sl); - sl->q_buf[0] = 0x12; - sl->q_buf[1] = 0x34; - sl->q_buf[2] = 0x56; - sl->q_buf[3] = 0x78; - sl->q_buf[4] = 0x90; - sl->q_buf[15] = 0x42; - sl->q_buf[16] = 0x43; - sl->q_buf[63] = 0x42; - sl->q_buf[64] = 0x43; - sl->q_buf[1024 * 6 - 1] = 0x42; //6kB - sl->q_buf[1024 * 8 - 1] = 0x42; //8kB -} - -#if 0 -int main(int argc, char *argv[]) { - // set scpi lib debug level: 0 for no debug info, 10 for lots - const int scsi_verbose = 2; - char *dev_name; - - switch (argc) { - case 1: - fputs( - "\nUsage: stlink-access-test /dev/sg0, sg1, ...\n" - "\n*** Notice: The stlink firmware violates the USB standard.\n" - "*** If you plug-in the discovery's stlink, wait a several\n" - "*** minutes to let the kernel driver swallow the broken device.\n" - "*** Watch:\ntail -f /var/log/messages\n" - "*** This command sequence can shorten the waiting time and fix some issues.\n" - "*** Unplug the stlink and execute once as root:\n" - "modprobe -r usb-storage && modprobe usb-storage quirks=483:3744:lrwsro\n\n", - stderr); - return EXIT_FAILURE; - case 2: - dev_name = argv[1]; - break; - default: - return EXIT_FAILURE; - } - - fputs("*** stlink access test ***\n", stderr); - DD(sl, "Using sg_lib %s : scsi_pt %s\n", sg_lib_version(), - scsi_pt_version()); - - struct stlink *sl = stlink_force_open(dev_name, scsi_verbose); - if (sl == NULL) - return EXIT_FAILURE; - - // we are in mass mode, go to swd - stlink_enter_swd_mode(sl); - stlink_current_mode(sl); - stlink_core_id(sl); - //---------------------------------------------------------------------- - - stlink_status(sl); - //stlink_force_debug(sl); - stlink_reset(sl); - stlink_status(sl); -#if 0 - // core system control block - stlink_read_mem32(sl, 0xe000ed00, 4); - DD(sl, "cpu id base register: SCB_CPUID = got 0x%08x expect 0x411fc231", read_uint32(sl->q_buf, 0)); - // no MPU - stlink_read_mem32(sl, 0xe000ed90, 4); - DD(sl, "mpu type register: MPU_TYPER = got 0x%08x expect 0x0", read_uint32(sl->q_buf, 0)); - - stlink_read_mem32(sl, 0xe000edf0, 4); - DD(sl, "DHCSR = 0x%08x", read_uint32(sl->q_buf, 0)); - - stlink_read_mem32(sl, 0x4001100c, 4); - DD(sl, "GPIOC_ODR = 0x%08x", read_uint32(sl->q_buf, 0)); -#endif -#if 0 - // happy new year 2011: let blink all the leds - // see "RM0041 Reference manual - STM32F100xx advanced ARM-based 32-bit MCUs" - -#define GPIOC 0x40011000 // port C -#define GPIOC_CRH (GPIOC + 0x04) // port configuration register high -#define GPIOC_ODR (GPIOC + 0x0c) // port output data register -#define LED_BLUE (1<<8) // pin 8 -#define LED_GREEN (1<<9) // pin 9 - stlink_read_mem32(sl, GPIOC_CRH, 4); - uint32_t io_conf = read_uint32(sl->q_buf, 0); - DD(sl, "GPIOC_CRH = 0x%08x", io_conf); - - // set: general purpose output push-pull, output mode, max speed 10 MHz. - write_uint32(sl->q_buf, 0x44444411); - stlink_write_mem32(sl, GPIOC_CRH, 4); - - clear_buf(sl); - for (int i = 0; i < 100; i++) { - write_uint32(sl->q_buf, LED_BLUE | LED_GREEN); - stlink_write_mem32(sl, GPIOC_ODR, 4); - /* stlink_read_mem32(sl, 0x4001100c, 4); */ - /* DD(sl, "GPIOC_ODR = 0x%08x", read_uint32(sl->q_buf, 0)); */ - delay(100); - - clear_buf(sl); - stlink_write_mem32(sl, GPIOC_ODR, 4); // PC lo - delay(100); - } - write_uint32(sl->q_buf, io_conf); // set old state - -#endif -#if 0 - // TODO rtfm: stlink doesn't have flash write routines - // writing to the flash area confuses the fw for the next read access - - //stlink_read_mem32(sl, 0, 1024*6); - // flash 0x08000000 128kB - fputs("++++++++++ read a flash at 0x0800 0000\n", stderr); - stlink_read_mem32(sl, 0x08000000, 1024 * 6); //max 6kB - clear_buf(sl); - stlink_read_mem32(sl, 0x08000c00, 5); - stlink_read_mem32(sl, 0x08000c00, 4); - mark_buf(sl); - stlink_write_mem32(sl, 0x08000c00, 4); - stlink_read_mem32(sl, 0x08000c00, 256); - stlink_read_mem32(sl, 0x08000c00, 256); -#endif -#if 0 - // sram 0x20000000 8kB - fputs("\n++++++++++ read/write 8bit, sram at 0x2000 0000 ++++++++++++++++\n\n", stderr); - clear_buf(sl); - stlink_write_mem8(sl, 0x20000000, 16); - - mark_buf(sl); - stlink_write_mem8(sl, 0x20000000, 1); - stlink_write_mem8(sl, 0x20000001, 1); - stlink_write_mem8(sl, 0x2000000b, 3); - stlink_read_mem32(sl, 0x20000000, 16); -#endif -#if 0 - // a not aligned mem32 access doesn't work indeed - fputs("\n++++++++++ read/write 32bit, sram at 0x2000 0000 ++++++++++++++++\n\n", stderr); - clear_buf(sl); - stlink_write_mem8(sl, 0x20000000, 32); - - mark_buf(sl); - stlink_write_mem32(sl, 0x20000000, 1); - stlink_read_mem32(sl, 0x20000000, 16); - mark_buf(sl); - stlink_write_mem32(sl, 0x20000001, 1); - stlink_read_mem32(sl, 0x20000000, 16); - mark_buf(sl); - stlink_write_mem32(sl, 0x2000000b, 3); - stlink_read_mem32(sl, 0x20000000, 16); - - mark_buf(sl); - stlink_write_mem32(sl, 0x20000000, 17); - stlink_read_mem32(sl, 0x20000000, 32); -#endif -#if 0 - // sram 0x20000000 8kB - fputs("++++++++++ read/write 32bit, sram at 0x2000 0000 ++++++++++++\n", stderr); - mark_buf(sl); - stlink_write_mem8(sl, 0x20000000, 64); - stlink_read_mem32(sl, 0x20000000, 64); - - mark_buf(sl); - stlink_write_mem32(sl, 0x20000000, 1024 * 8); //8kB - stlink_read_mem32(sl, 0x20000000, 1024 * 6); - stlink_read_mem32(sl, 0x20000000 + 1024 * 6, 1024 * 2); -#endif -#if 0 - stlink_read_all_regs(sl); - stlink_step(sl); - fputs("++++++++++ write r0 = 0x12345678\n", stderr); - stlink_write_reg(sl, 0x12345678, 0); - stlink_read_reg(sl, 0); - stlink_read_all_regs(sl); -#endif -#if 0 - stlink_run(sl); - stlink_status(sl); - - stlink_force_debug(sl); - stlink_status(sl); -#endif -#if 1 /* read the system bootloader */ - fputs("\n++++++++++ reading bootloader ++++++++++++++++\n\n", stderr); - stlink_fread(sl, "/tmp/barfoo", sl->sys_base, sl->sys_size); -#endif -#if 0 /* read the flash memory */ - fputs("\n+++++++ read flash memory\n\n", stderr); - /* mark_buf(sl); */ - stlink_read_mem32(sl, 0x08000000, 4); -#endif -#if 0 /* flash programming */ - fputs("\n+++++++ program flash memory\n\n", stderr); - stlink_fwrite_flash(sl, "/tmp/foobar", 0x08000000); -#endif -#if 0 /* check file contents */ - fputs("\n+++++++ check flash memory\n\n", stderr); - { - const int res = stlink_fcheck_flash(sl, "/tmp/foobar", 0x08000000); - printf("_____ stlink_fcheck_flash() == %d\n", res); - } -#endif -#if 0 - fputs("\n+++++++ sram write and execute\n\n", stderr); - stlink_fwrite_sram(sl, "/tmp/foobar", sl->sram_base); - stlink_run_at(sl, sl->sram_base); -#endif - - stlink_run(sl); - stlink_status(sl); - //---------------------------------------------------------------------- - // back to mass mode, just in case ... - stlink_exit_debug_mode(sl); - stlink_current_mode(sl); - stlink_close(sl); - - //fflush(stderr); fflush(stdout); - return EXIT_SUCCESS; -} -#endif diff --git a/src/stlink-hw.h b/src/stlink-hw.h deleted file mode 100644 index dd17d20..0000000 --- a/src/stlink-hw.h +++ /dev/null @@ -1,203 +0,0 @@ -#ifndef _STLINK_HW_H_ -#define _STLINK_HW_H_ - -#include - -// device access -#define RDWR 0 -#define RO 1 -#define SG_TIMEOUT_SEC 1 // actually 1 is about 2 sec -// Each CDB can be a total of 6, 10, 12, or 16 bytes, later version -// of the SCSI standard also allow for variable-length CDBs (min. CDB is 6). -// the stlink needs max. 10 bytes. -#define CDB_6 6 -#define CDB_10 10 -#define CDB_12 12 -#define CDB_16 16 - -#define CDB_SL 10 - -// Query data flow direction. -#define Q_DATA_OUT 0 -#define Q_DATA_IN 1 - -// The SCSI Request Sense command is used to obtain sense data -// (error information) from a target device. -// http://en.wikipedia.org/wiki/SCSI_Request_Sense_Command -#define SENSE_BUF_LEN 32 - -// Max data transfer size. -// 6kB = max mem32_read block, 8kB sram -//#define Q_BUF_LEN 96 -#define Q_BUF_LEN 1024 * 100 - -// st-link vendor cmd's -#define USB_ST_VID 0x0483 -#define USB_STLINK_PID 0x3744 - -// STLINK_DEBUG_RESETSYS, etc: -#define STLINK_OK 0x80 -#define STLINK_FALSE 0x81 -#define STLINK_CORE_RUNNING 0x80 -#define STLINK_CORE_HALTED 0x81 -#define STLINK_CORE_STAT_UNKNOWN -1 - -#define STLINK_GET_VERSION 0xf1 -#define STLINK_GET_CURRENT_MODE 0xf5 - -#define STLINK_DEBUG_COMMAND 0xF2 -#define STLINK_DFU_COMMAND 0xF3 -#define STLINK_DFU_EXIT 0x07 - -// STLINK_GET_CURRENT_MODE -#define STLINK_DEV_DFU_MODE 0x00 -#define STLINK_DEV_MASS_MODE 0x01 -#define STLINK_DEV_DEBUG_MODE 0x02 -#define STLINK_DEV_UNKNOWN_MODE -1 - -// jtag mode cmds -#define STLINK_DEBUG_ENTER 0x20 -#define STLINK_DEBUG_EXIT 0x21 -#define STLINK_DEBUG_READCOREID 0x22 -#define STLINK_DEBUG_GETSTATUS 0x01 -#define STLINK_DEBUG_FORCEDEBUG 0x02 -#define STLINK_DEBUG_RESETSYS 0x03 -#define STLINK_DEBUG_READALLREGS 0x04 -#define STLINK_DEBUG_READREG 0x05 -#define STLINK_DEBUG_WRITEREG 0x06 -#define STLINK_DEBUG_READMEM_32BIT 0x07 -#define STLINK_DEBUG_WRITEMEM_32BIT 0x08 -#define STLINK_DEBUG_RUNCORE 0x09 -#define STLINK_DEBUG_STEPCORE 0x0a -#define STLINK_DEBUG_SETFP 0x0b -#define STLINK_DEBUG_WRITEMEM_8BIT 0x0d -#define STLINK_DEBUG_CLEARFP 0x0e -#define STLINK_DEBUG_WRITEDEBUGREG 0x0f -#define STLINK_DEBUG_ENTER_SWD 0xa3 -#define STLINK_DEBUG_ENTER_JTAG 0x00 - -typedef struct { - uint32_t r[16]; - uint32_t xpsr; - uint32_t main_sp; - uint32_t process_sp; - uint32_t rw; - uint32_t rw2; -} reg; - -typedef uint32_t stm32_addr_t; - -enum transport_type -{ - TRANSPORT_TYPE_ZERO = 0, -#if CONFIG_USE_LIBSG - TRANSPORT_TYPE_LIBSG, -#endif /* CONFIG_USE_LIBSG */ -#if CONFIG_USE_LIBUSB - TRANSPORT_TYPE_LIBUSB, -#endif /* CONFIG_USE_LIBUSB */ - TRANSPORT_TYPE_INVALID -}; - -struct stlink_libusb -{ - libusb_device_handle* usb_handle; - struct libusb_transfer* req_trans; - struct libusb_transfer* rep_trans; - unsigned int ep_req; - unsigned int ep_rep; -}; - -struct stlink_libsg { - int sg_fd; - int do_scsi_pt_err; - // sg layer verboseness: 0 for no debug info, 10 for lots - int verbose; - - unsigned char cdb_cmd_blk[CDB_SL]; - - // Data transferred from or to device - unsigned char q_buf[Q_BUF_LEN]; - int q_len; - int q_data_dir; // Q_DATA_IN, Q_DATA_OUT - // the start of the query data in the device memory space - uint32_t q_addr; - - // Sense (error information) data - unsigned char sense_buf[SENSE_BUF_LEN]; - - uint32_t st_vid; - uint32_t stlink_pid; - uint32_t stlink_v; - uint32_t jtag_v; - uint32_t swim_v; - uint32_t core_id; - - reg reg; - int core_stat; - - /* medium density stm32 flash settings */ -#define STM32_FLASH_BASE 0x08000000 -#define STM32_FLASH_SIZE (128 * 1024) -#define STM32_FLASH_PGSZ 1024 - stm32_addr_t flash_base; - size_t flash_size; - size_t flash_pgsz; - - /* in flash system memory */ -#define STM32_SYSTEM_BASE 0x1ffff000 -#define STM32_SYSTEM_SIZE (2 * 1024) - stm32_addr_t sys_base; - size_t sys_size; - - /* sram settings */ -#define STM32_SRAM_BASE 0x20000000 -#define STM32_SRAM_SIZE (8 * 1024) - stm32_addr_t sram_base; - size_t sram_size; -}; - -struct stlink -{ - enum transport_type tt; - union { -#if CONFIG_USE_LIBUSB - struct stlink_libusb *libusb; -#endif /* CONFIG_USE_LIBUSB */ -#if CONFIG_USE_LIBSG - struct stlink_libsg *libsg; -#endif /* CONFIG_USE_LIBSG */ - } transport; - - unsigned char q_buf[64]; - size_t q_len; - - /* layer independant */ - uint32_t core_id; -}; - - -struct stlink* stlink_quirk_open(enum transport_type tt, const char *dev_name, const int verbose); -int stlink_current_mode(struct stlink *sl); -void stlink_enter_swd_mode(struct stlink *sl); -void stlink_enter_jtag_mode(struct stlink *sl); -void stlink_exit_debug_mode(struct stlink *sl); -void stlink_core_id(struct stlink *sl); -void stlink_status(struct stlink *sl); -void stlink_force_debug(struct stlink *sl); -void stlink_reset(struct stlink *sl); -void stlink_run(struct stlink *sl); -void stlink_step(struct stlink *sl); -void stlink_read_all_regs(struct stlink *sl); -void stlink_read_reg(struct stlink *sl, int r_idx); -void stlink_write_reg(struct stlink *sl, uint32_t reg, int idx); -void stlink_read_mem32(struct stlink *sl, uint32_t addr, uint16_t len); -void stlink_write_mem8(struct stlink *sl, uint32_t addr, uint16_t len); -void stlink_write_mem32(struct stlink *sl, uint32_t addr, uint16_t len); -void stlink_close(struct stlink *sl); - -int stlink_erase_flash_page(struct stlink* sl, stm32_addr_t page); -int stlink_erase_flash_mass(struct stlink* sl); -int stlink_write_flash(struct stlink* sl, stm32_addr_t address, uint8_t* data, unsigned length); - -#endif diff --git a/src/stlink-sg.c b/src/stlink-sg.c new file mode 100644 index 0000000..a6e9d80 --- /dev/null +++ b/src/stlink-sg.c @@ -0,0 +1,1436 @@ +/* + Copyright (c) 2010 "Capt'ns Missing Link" Authors. All rights reserved. + Use of this source code is governed by a BSD-style + license that can be found in the LICENSE file. + + A linux stlink access demo. The purpose of this file is to mitigate the usual + "reinventing the wheel" force by incompatible licenses and give you an idea, + how to access the stlink device. That doesn't mean you should be a free-loader + and not contribute your improvements to this code. + + Author: Martin Capitanio + The stlink related constants kindly provided by Oliver Spencer (OpenOCD) + for use in a GPL compatible license. + + Code format ~ TAB = 8, K&R, linux kernel source, golang oriented + Tested compatibility: linux, gcc >= 4.3.3 + + The communication is based on standard USB mass storage device + BOT (Bulk Only Transfer) + - Endpoint 1: BULK_IN, 64 bytes max + - Endpoint 2: BULK_OUT, 64 bytes max + + All CBW transfers are ordered with the LSB (byte 0) first (little endian). + Any command must be answered before sending the next command. + Each USB transfer must complete in less than 1s. + + SB Device Class Definition for Mass Storage Devices: + www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf + + dt - Data Transfer (IN/OUT) + CBW - Command Block Wrapper + CSW - Command Status Wrapper + RFU - Reserved for Future Use + scsi_pt - SCSI pass-through + sg - SCSI generic + + * usb-storage.quirks + http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob_plain;f=Documentation/kernel-parameters.txt + Each entry has the form VID:PID:Flags where VID and PID are Vendor and Product + ID values (4-digit hex numbers) and Flags is a set of characters, each corresponding + to a common usb-storage quirk flag as follows: + + a = SANE_SENSE (collect more than 18 bytes of sense data); + b = BAD_SENSE (don't collect more than 18 bytes of sense data); + c = FIX_CAPACITY (decrease the reported device capacity by one sector); + h = CAPACITY_HEURISTICS (decrease the reported device capacity by one sector if the number is odd); + i = IGNORE_DEVICE (don't bind to this device); + l = NOT_LOCKABLE (don't try to lock and unlock ejectable media); + m = MAX_SECTORS_64 (don't transfer more than 64 sectors = 32 KB at a time); + o = CAPACITY_OK (accept the capacity reported by the device); + r = IGNORE_RESIDUE (the device reports bogus residue values); + s = SINGLE_LUN (the device has only one Logical Unit); + w = NO_WP_DETECT (don't test whether the medium is write-protected). + + Example: quirks=0419:aaf5:rl,0421:0433:rc + http://permalink.gmane.org/gmane.linux.usb.general/35053 + + modprobe -r usb-storage && modprobe usb-storage quirks=483:3744:l + + Equivalently, you can add a line saying + + options usb-storage quirks=483:3744:l + + to your /etc/modprobe.conf or /etc/modprobe.d/local.conf (or add the "quirks=..." + part to an existing options line for usb-storage). + */ + + +#define __USE_GNU +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// sgutils2 (apt-get install libsgutils2-dev) +#include +#include + +#include "stlink-common.h" +#include "stlink-sg.h" + + +// Suspends execution of the calling process for +// (at least) ms milliseconds. + +static void delay(int ms) { + //fprintf(stderr, "*** wait %d ms\n", ms); + usleep(1000 * ms); +} + +static void write_uint32(unsigned char* buf, uint32_t ui) { + if (!is_bigendian()) { // le -> le (don't swap) + buf[0] = ((unsigned char*) &ui)[0]; + buf[1] = ((unsigned char*) &ui)[1]; + buf[2] = ((unsigned char*) &ui)[2]; + buf[3] = ((unsigned char*) &ui)[3]; + } else { + buf[0] = ((unsigned char*) &ui)[3]; + buf[1] = ((unsigned char*) &ui)[2]; + buf[2] = ((unsigned char*) &ui)[1]; + buf[3] = ((unsigned char*) &ui)[0]; + } +} + +static void write_uint16(unsigned char* buf, uint16_t ui) { + if (!is_bigendian()) { // le -> le (don't swap) + buf[0] = ((unsigned char*) &ui)[0]; + buf[1] = ((unsigned char*) &ui)[1]; + } else { + buf[0] = ((unsigned char*) &ui)[1]; + buf[1] = ((unsigned char*) &ui)[0]; + } +} + +static uint32_t read_uint32(const unsigned char *c, const int pt) { + uint32_t ui; + char *p = (char *) &ui; + + if (!is_bigendian()) { // le -> le (don't swap) + p[0] = c[pt]; + p[1] = c[pt + 1]; + p[2] = c[pt + 2]; + p[3] = c[pt + 3]; + } else { + p[0] = c[pt + 3]; + p[1] = c[pt + 2]; + p[2] = c[pt + 1]; + p[3] = c[pt]; + } + return ui; +} + +static void clear_cdb(struct stlink_libsg *sl) { + for (int i = 0; i < sizeof (sl->cdb_cmd_blk); i++) + sl->cdb_cmd_blk[i] = 0; + // set default + sl->cdb_cmd_blk[0] = STLINK_DEBUG_COMMAND; + sl->q_data_dir = Q_DATA_IN; +} + +// E.g. make the valgrind happy. + +static void clear_buf(stlink_t *sl) { + DD(sl, "*** clear_buf ***\n"); + for (int i = 0; i < sizeof (sl->q_buf); i++) + sl->q_buf[i] = 0; + +} + +// close the device, free the allocated memory + +void _stlink_sg_close(stlink_t *sl) { + if (sl) { + struct stlink_libsg *slsg = sl->backend_data; + scsi_pt_close_device(slsg->sg_fd); + // CAUTION!? s this right? + free(slsg); + free(sl); + } +} + +// Exit the jtag or swd mode and enter the mass mode. + +void _stlink_sg_exit_debug_mode(stlink_t *stl) { + + if (stl) { + struct stlink_libsg* sl = stl->backend_data; + clear_cdb(sl); + sl->cdb_cmd_blk[1] = STLINK_DEBUG_EXIT; + stl->q_len = 0; // >0 -> aboard + stlink_q(stl); + } +} + + + +//TODO rewrite/cleanup, save the error in sl + +static void stlink_confirm_inq(stlink_t *stl, struct sg_pt_base *ptvp) { + struct stlink_libsg *sl = stl->backend_data; + const int e = sl->do_scsi_pt_err; + if (e < 0) { + fprintf(stderr, "scsi_pt error: pass through os error: %s\n", + safe_strerror(-e)); + return; + } else if (e == SCSI_PT_DO_BAD_PARAMS) { + fprintf(stderr, "scsi_pt error: bad pass through setup\n"); + return; + } else if (e == SCSI_PT_DO_TIMEOUT) { + fprintf(stderr, " pass through timeout\n"); + return; + } + const int duration = get_scsi_pt_duration_ms(ptvp); + if ((stl->verbose > 1) && (duration >= 0)) + DD(stl, " duration=%d ms\n", duration); + + // XXX stlink fw sends broken residue, so ignore it and use the known q_len + // "usb-storage quirks=483:3744:r" + // forces residue to be ignored and calculated, but this causes aboard if + // data_len = 0 and by some other data_len values. + + const int resid = get_scsi_pt_resid(ptvp); + const int dsize = stl->q_len - resid; + + const int cat = get_scsi_pt_result_category(ptvp); + char buf[512]; + unsigned int slen; + + switch (cat) { + case SCSI_PT_RESULT_GOOD: + if (stl->verbose && (resid > 0)) + DD(stl, " notice: requested %d bytes but " + "got %d bytes, ignore [broken] residue = %d\n", + stl->q_len, dsize, resid); + break; + case SCSI_PT_RESULT_STATUS: + if (stl->verbose) { + sg_get_scsi_status_str( + get_scsi_pt_status_response(ptvp), sizeof (buf), + buf); + DD(stl, " scsi status: %s\n", buf); + } + return; + case SCSI_PT_RESULT_SENSE: + slen = get_scsi_pt_sense_len(ptvp); + if (stl->verbose) { + sg_get_sense_str("", sl->sense_buf, slen, (stl->verbose + > 1), sizeof (buf), buf); + DD(stl, "%s", buf); + } + if (stl->verbose && (resid > 0)) { + if ((stl->verbose) || (stl->q_len > 0)) + DD(stl, " requested %d bytes but " + "got %d bytes\n", stl->q_len, dsize); + } + return; + case SCSI_PT_RESULT_TRANSPORT_ERR: + if (stl->verbose) { + get_scsi_pt_transport_err_str(ptvp, sizeof (buf), buf); + // http://tldp.org/HOWTO/SCSI-Generic-HOWTO/x291.html + // These codes potentially come from the firmware on a host adapter + // or from one of several hosts that an adapter driver controls. + // The 'host_status' field has the following values: + // [0x07] Internal error detected in the host adapter. + // This may not be fatal (and the command may have succeeded). + DD(stl, " transport: %s", buf); + } + return; + case SCSI_PT_RESULT_OS_ERR: + if (stl->verbose) { + get_scsi_pt_os_err_str(ptvp, sizeof (buf), buf); + DD(stl, " os: %s", buf); + } + return; + default: + fprintf(stderr, " unknown pass through result " + "category (%d)\n", cat); + } +} + +void stlink_q(stlink_t *stl) { + struct stlink_libsg* sl = stl->backend_data; + DD(stl, "CDB["); + for (int i = 0; i < CDB_SL; i++) + DD(stl, " 0x%02x", (unsigned int) sl->cdb_cmd_blk[i]); + DD(stl, "]\n"); + + // Get control command descriptor of scsi structure, + // (one object per command!!) + struct sg_pt_base *ptvp = construct_scsi_pt_obj(); + if (NULL == ptvp) { + fprintf(stderr, "construct_scsi_pt_obj: out of memory\n"); + return; + } + + set_scsi_pt_cdb(ptvp, sl->cdb_cmd_blk, sizeof (sl->cdb_cmd_blk)); + + // set buffer for sense (error information) data + set_scsi_pt_sense(ptvp, sl->sense_buf, sizeof (sl->sense_buf)); + + // Set a buffer to be used for data transferred from device + if (sl->q_data_dir == Q_DATA_IN) { + //clear_buf(sl); + set_scsi_pt_data_in(ptvp, stl->q_buf, stl->q_len); + } else { + set_scsi_pt_data_out(ptvp, stl->q_buf, stl->q_len); + } + // Executes SCSI command (or at least forwards it to lower layers). + sl->do_scsi_pt_err = do_scsi_pt(ptvp, sl->sg_fd, SG_TIMEOUT_SEC, + stl->verbose); + + // check for scsi errors + stlink_confirm_inq(stl, ptvp); + // TODO recycle: clear_scsi_pt_obj(struct sg_pt_base * objp); + destruct_scsi_pt_obj(ptvp); +} + +// TODO thinking, cleanup + +void stlink_parse_version(stlink_t *stl) { + struct stlink_libsg *sl = stl->backend_data; + sl->st_vid = 0; + sl->stlink_pid = 0; + if (stl->q_len <= 0) { + fprintf(stderr, "Error: could not parse the stlink version"); + return; + } + stlink_print_data(stl); + uint32_t b0 = stl->q_buf[0]; //lsb + uint32_t b1 = stl->q_buf[1]; + uint32_t b2 = stl->q_buf[2]; + uint32_t b3 = stl->q_buf[3]; + uint32_t b4 = stl->q_buf[4]; + uint32_t b5 = stl->q_buf[5]; //msb + + // b0 b1 || b2 b3 | b4 b5 + // 4b | 6b | 6b || 2B | 2B + // stlink_v | jtag_v | swim_v || st_vid | stlink_pid + + sl->stlink_v = (b0 & 0xf0) >> 4; + sl->jtag_v = ((b0 & 0x0f) << 2) | ((b1 & 0xc0) >> 6); + sl->swim_v = b1 & 0x3f; + sl->st_vid = (b3 << 8) | b2; + sl->stlink_pid = (b5 << 8) | b4; + + if (stl->verbose < 2) + return; + + DD(stl, "st vid = 0x%04x (expect 0x%04x)\n", + sl->st_vid, USB_ST_VID); + DD(stl, "stlink pid = 0x%04x (expect 0x%04x)\n", + sl->stlink_pid, USB_STLINK_PID); + DD(stl, "stlink version = 0x%x\n", sl->stlink_v); + DD(stl, "jtag version = 0x%x\n", sl->jtag_v); + DD(stl, "swim version = 0x%x\n", sl->swim_v); + if (sl->jtag_v == 0) + DD(stl, + " notice: the firmware doesn't support a jtag/swd interface\n"); + if (sl->swim_v == 0) + DD(stl, + " notice: the firmware doesn't support a swim interface\n"); + +} + +int stlink_mode(stlink_t *stl) { + if (stl->q_len <= 0) + return STLINK_DEV_UNKNOWN_MODE; + + stlink_print_data(stl); + + switch (stl->q_buf[0]) { + case STLINK_DEV_DFU_MODE: + DD(stl, "stlink mode: dfu\n"); + return STLINK_DEV_DFU_MODE; + case STLINK_DEV_DEBUG_MODE: + DD(stl, "stlink mode: debug (jtag or swd)\n"); + return STLINK_DEV_DEBUG_MODE; + case STLINK_DEV_MASS_MODE: + DD(stl, "stlink mode: mass\n"); + return STLINK_DEV_MASS_MODE; + } + return STLINK_DEV_UNKNOWN_MODE; +} + +void stlink_stat(stlink_t *stl, char *txt) { + if (stl->q_len <= 0) + return; + + stlink_print_data(stl); + + switch (stl->q_buf[0]) { + case STLINK_OK: + DD(stl, " %s: ok\n", txt); + return; + case STLINK_FALSE: + DD(stl, " %s: false\n", txt); + return; + default: + DD(stl, " %s: unknown\n", txt); + } +} + +void _stlink_sg_version(stlink_t *stl) { + struct stlink_libsg *sl = stl->backend_data; + D(stl, "\n*** stlink_version ***\n"); + clear_cdb(sl); + sl->cdb_cmd_blk[0] = STLINK_GET_VERSION; + stl->q_len = 6; + sl->q_addr = 0; + stlink_q(stl); + stlink_parse_version(stl); +} + +// Get stlink mode: +// STLINK_DEV_DFU_MODE || STLINK_DEV_MASS_MODE || STLINK_DEV_DEBUG_MODE +// usb dfu || usb mass || jtag or swd + +int stlink_current_mode(stlink_t *stl) { + struct stlink_libsg *sl = stl->backend_data; + D(stl, "\n*** stlink_current_mode ***\n"); + clear_cdb(sl); + sl->cdb_cmd_blk[0] = STLINK_GET_CURRENT_MODE; + stl->q_len = 2; + sl->q_addr = 0; + stlink_q(stl); + return stlink_mode(stl); +} + +// Exit the mass mode and enter the swd debug mode. + +void _stlink_sg_enter_swd_mode(stlink_t *sl) { + struct stlink_libsg *sg = sl->backend_data; + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_ENTER; + sg->cdb_cmd_blk[2] = STLINK_DEBUG_ENTER_SWD; + sl->q_len = 0; // >0 -> aboard + stlink_q(sl); +} + +// Exit the mass mode and enter the jtag debug mode. +// (jtag is disabled in the discovery's stlink firmware) + +void _stlink_sg_enter_jtag_mode(stlink_t *sl) { + struct stlink_libsg *sg = sl->backend_data; + D(sl, "\n*** stlink_enter_jtag_mode ***\n"); + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_ENTER; + sg->cdb_cmd_blk[2] = STLINK_DEBUG_ENTER_JTAG; + sl->q_len = 0; + stlink_q(sl); +} + +// XXX kernel driver performs reset, the device temporally disappears + +void _stlink_sg_exit_dfu_mode(stlink_t *sl) { + struct stlink_libsg *sg = sl->backend_data; + D(sl, "\n*** stlink_exit_dfu_mode ***\n"); + clear_cdb(sg); + sg->cdb_cmd_blk[0] = STLINK_DFU_COMMAND; + sg->cdb_cmd_blk[1] = STLINK_DFU_EXIT; + sl->q_len = 0; // ?? + stlink_q(sl); + /* + [135121.844564] sd 19:0:0:0: [sdb] Unhandled error code + [135121.844569] sd 19:0:0:0: [sdb] Result: hostbyte=DID_ERROR driverbyte=DRIVER_OK + [135121.844574] sd 19:0:0:0: [sdb] CDB: Read(10): 28 00 00 00 10 00 00 00 08 00 + [135121.844584] end_request: I/O error, dev sdb, sector 4096 + [135121.844590] Buffer I/O error on device sdb, logical block 512 + [135130.122567] usb 6-1: reset full speed USB device using uhci_hcd and address 7 + [135130.274551] usb 6-1: device firmware changed + [135130.274618] usb 6-1: USB disconnect, address 7 + [135130.275186] VFS: busy inodes on changed media or resized disk sdb + [135130.275424] VFS: busy inodes on changed media or resized disk sdb + [135130.286758] VFS: busy inodes on changed media or resized disk sdb + [135130.292796] VFS: busy inodes on changed media or resized disk sdb + [135130.301481] VFS: busy inodes on changed media or resized disk sdb + [135130.304316] VFS: busy inodes on changed media or resized disk sdb + [135130.431113] usb 6-1: new full speed USB device using uhci_hcd and address 8 + [135130.629444] usb-storage 6-1:1.0: Quirks match for vid 0483 pid 3744: 102a1 + [135130.629492] scsi20 : usb-storage 6-1:1.0 + [135131.625600] scsi 20:0:0:0: Direct-Access STM32 PQ: 0 ANSI: 0 + [135131.627010] sd 20:0:0:0: Attached scsi generic sg2 type 0 + [135131.633603] sd 20:0:0:0: [sdb] 64000 512-byte logical blocks: (32.7 MB/31.2 MiB) + [135131.633613] sd 20:0:0:0: [sdb] Assuming Write Enabled + [135131.633620] sd 20:0:0:0: [sdb] Assuming drive cache: write through + [135131.640584] sd 20:0:0:0: [sdb] Assuming Write Enabled + [135131.640592] sd 20:0:0:0: [sdb] Assuming drive cache: write through + [135131.640609] sdb: + [135131.652634] sd 20:0:0:0: [sdb] Assuming Write Enabled + [135131.652639] sd 20:0:0:0: [sdb] Assuming drive cache: write through + [135131.652645] sd 20:0:0:0: [sdb] Attached SCSI removable disk + [135131.671536] sd 20:0:0:0: [sdb] Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE + [135131.671548] sd 20:0:0:0: [sdb] Sense Key : Illegal Request [current] + [135131.671553] sd 20:0:0:0: [sdb] Add. Sense: Logical block address out of range + [135131.671560] sd 20:0:0:0: [sdb] CDB: Read(10): 28 00 00 00 f9 80 00 00 08 00 + [135131.671570] end_request: I/O error, dev sdb, sector 63872 + [135131.671575] Buffer I/O error on device sdb, logical block 7984 + [135131.678527] sd 20:0:0:0: [sdb] Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE + [135131.678532] sd 20:0:0:0: [sdb] Sense Key : Illegal Request [current] + [135131.678537] sd 20:0:0:0: [sdb] Add. Sense: Logical block address out of range + [135131.678542] sd 20:0:0:0: [sdb] CDB: Read(10): 28 00 00 00 f9 80 00 00 08 00 + [135131.678551] end_request: I/O error, dev sdb, sector 63872 + ... + [135131.853565] end_request: I/O error, dev sdb, sector 4096 + */ +} + +void _stlink_sg_core_id(stlink_t *sl) { + struct stlink_libsg *sg = sl->backend_data; + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_READCOREID; + sl->q_len = 4; + sg->q_addr = 0; + stlink_q(sl); + sl->core_id = read_uint32(sl->q_buf, 0); + if (sl->verbose < 2) + return; + stlink_print_data(sl); +} + +// Arm-core reset -> halted state. + +void _stlink_sg_reset(stlink_t *sl) { + struct stlink_libsg *sg = sl->backend_data; + D(sl, "\n*** stlink_reset ***\n"); + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_RESETSYS; + sl->q_len = 2; + sg->q_addr = 0; + stlink_q(sl); + stlink_stat(sl, "core reset"); +} + +// Arm-core status: halted or running. + +void _stlink_sg_status(struct stlink_libsg *sl) { + D(sl, "\n*** stlink_status ***\n"); + clear_cdb(sl); + sl->cdb_cmd_blk[1] = STLINK_DEBUG_GETSTATUS; + sl->q_len = 2; + sl->q_addr = 0; + stlink_q(sl); +} + +// Force the core into the debug mode -> halted state. + +void stlink_force_debug(struct stlink_libsg *sl) { + D(sl, "\n*** stlink_force_debug ***\n"); + clear_cdb(sl); + sl->cdb_cmd_blk[1] = STLINK_DEBUG_FORCEDEBUG; + sl->q_len = 2; + sl->q_addr = 0; + stlink_q(sl); + stlink_stat(sl, "force debug"); +} + +// Read all arm-core registers. + +void stlink_read_all_regs(struct stlink_libsg *sl) { + D(sl, "\n*** stlink_read_all_regs ***\n"); + clear_cdb(sl); + sl->cdb_cmd_blk[1] = STLINK_DEBUG_READALLREGS; + sl->q_len = 84; + sl->q_addr = 0; + stlink_q(sl); + stlink_print_data(sl); + + // 0-3 | 4-7 | ... | 60-63 | 64-67 | 68-71 | 72-75 | 76-79 | 80-83 + // r0 | r1 | ... | r15 | xpsr | main_sp | process_sp | rw | rw2 + for (int i = 0; i < 16; i++) { + sl->reg.r[i] = read_uint32(sl->q_buf, 4 * i); + if (sl->verbose > 1) + DD(sl, "r%2d = 0x%08x\n", i, sl->reg.r[i]); + } + sl->reg.xpsr = read_uint32(sl->q_buf, 64); + sl->reg.main_sp = read_uint32(sl->q_buf, 68); + sl->reg.process_sp = read_uint32(sl->q_buf, 72); + sl->reg.rw = read_uint32(sl->q_buf, 76); + sl->reg.rw2 = read_uint32(sl->q_buf, 80); + if (sl->verbose < 2) + return; + + DD(sl, "xpsr = 0x%08x\n", sl->reg.xpsr); + DD(sl, "main_sp = 0x%08x\n", sl->reg.main_sp); + DD(sl, "process_sp = 0x%08x\n", sl->reg.process_sp); + DD(sl, "rw = 0x%08x\n", sl->reg.rw); + DD(sl, "rw2 = 0x%08x\n", sl->reg.rw2); +} + +// Read an arm-core register, the index must be in the range 0..20. +// 0 | 1 | ... | 15 | 16 | 17 | 18 | 19 | 20 +// r0 | r1 | ... | r15 | xpsr | main_sp | process_sp | rw | rw2 + +void stlink_read_reg(struct stlink_libsg *sl, int r_idx) { + D(sl, "\n*** stlink_read_reg"); + DD(sl, " (%d) ***\n", r_idx); + + if (r_idx > 20 || r_idx < 0) { + fprintf(stderr, "Error: register index must be in [0..20]\n"); + return; + } + clear_cdb(sl); + sl->cdb_cmd_blk[1] = STLINK_DEBUG_READREG; + sl->cdb_cmd_blk[2] = r_idx; + sl->q_len = 4; + sl->q_addr = 0; + stlink_q(sl); + // 0 | 1 | ... | 15 | 16 | 17 | 18 | 19 | 20 + // 0-3 | 4-7 | ... | 60-63 | 64-67 | 68-71 | 72-75 | 76-79 | 80-83 + // r0 | r1 | ... | r15 | xpsr | main_sp | process_sp | rw | rw2 + stlink_print_data(sl); + + uint32_t r = read_uint32(sl->q_buf, 0); + DD(sl, "r_idx (%2d) = 0x%08x\n", r_idx, r); + + switch (r_idx) { + case 16: + sl->reg.xpsr = r; + break; + case 17: + sl->reg.main_sp = r; + break; + case 18: + sl->reg.process_sp = r; + break; + case 19: + sl->reg.rw = r; //XXX ?(primask, basemask etc.) + break; + case 20: + sl->reg.rw2 = r; //XXX ?(primask, basemask etc.) + break; + default: + sl->reg.r[r_idx] = r; + } +} + +// Write an arm-core register. Index: +// 0 | 1 | ... | 15 | 16 | 17 | 18 | 19 | 20 +// r0 | r1 | ... | r15 | xpsr | main_sp | process_sp | rw | rw2 + +void stlink_write_reg(struct stlink_libsg *sl, uint32_t reg, int idx) { + D(sl, "\n*** stlink_write_reg ***\n"); + clear_cdb(sl); + sl->cdb_cmd_blk[1] = STLINK_DEBUG_WRITEREG; + // 2: reg index + // 3-6: reg content + sl->cdb_cmd_blk[2] = idx; + write_uint32(sl->cdb_cmd_blk + 3, reg); + sl->q_len = 2; + sl->q_addr = 0; + stlink_q(sl); + stlink_stat(sl, "write reg"); +} + +// Write a register of the debug module of the core. +// XXX ?(atomic writes) +// TODO test + +void stlink_write_dreg(struct stlink_libsg *sl, uint32_t reg, uint32_t addr) { + D(sl, "\n*** stlink_write_dreg ***\n"); + clear_cdb(sl); + sl->cdb_cmd_blk[1] = STLINK_DEBUG_WRITEDEBUGREG; + // 2-5: address of reg of the debug module + // 6-9: reg content + write_uint32(sl->cdb_cmd_blk + 2, addr); + write_uint32(sl->cdb_cmd_blk + 6, reg); + sl->q_len = 2; + sl->q_addr = addr; + stlink_q(sl); + stlink_stat(sl, "write debug reg"); +} + +// Force the core exit the debug mode. + +void _stlink_sg_run(stlink_t *stl) { + struct stlink_libsg sl = stl->backend_data; + D(stl, "\n*** stlink_run ***\n"); + clear_cdb(sl); + sl->cdb_cmd_blk[1] = STLINK_DEBUG_RUNCORE; + sl->q_len = 2; + sl->q_addr = 0; + stlink_q(sl); + stlink_stat(sl, "run core"); +} + +// same as above with entrypoint. +static unsigned int is_core_halted(struct stlink_libsg*); + +void stlink_run_at(struct stlink *sl_libsg, stm32_addr_t addr) { + stlink_write_reg(sl, addr, 15); /* pc register */ + + stlink_run(sl); + + while (is_core_halted(sl) == 0) + usleep(3000000); +} + +// Step the arm-core. + +void stlink_step(struct stlink_libsg *sl) { + D(sl, "\n*** stlink_step ***\n"); + clear_cdb(sl); + sl->cdb_cmd_blk[1] = STLINK_DEBUG_STEPCORE; + sl->q_len = 2; + sl->q_addr = 0; + stlink_q(sl); + stlink_stat(sl, "step core"); +} + +// TODO test +// see Cortex-M3 Technical Reference Manual + +void stlink_set_hw_bp(struct stlink_libsg *sl, int fp_nr, uint32_t addr, int fp) { + D(sl, "\n*** stlink_set_hw_bp ***\n"); + clear_cdb(sl); + sl->cdb_cmd_blk[1] = STLINK_DEBUG_SETFP; + // 2:The number of the flash patch used to set the breakpoint + // 3-6: Address of the breakpoint (LSB) + // 7: FP_ALL (0x02) / FP_UPPER (0x01) / FP_LOWER (0x00) + sl->q_buf[2] = fp_nr; + write_uint32(sl->q_buf, addr); + sl->q_buf[7] = fp; + + sl->q_len = 2; + stlink_q(sl); + stlink_stat(sl, "set flash breakpoint"); +} + +// TODO test + +void stlink_clr_hw_bp(struct stlink_libsg *sl, int fp_nr) { + D(sl, "\n*** stlink_clr_hw_bp ***\n"); + clear_cdb(sl); + sl->cdb_cmd_blk[1] = STLINK_DEBUG_CLEARFP; + sl->cdb_cmd_blk[2] = fp_nr; + + sl->q_len = 2; + stlink_q(sl); + stlink_stat(sl, "clear flash breakpoint"); +} + +// Read a "len" bytes to the sl->q_buf from the memory, max 6kB (6144 bytes) + +void stlink_read_mem32(struct stlink_libsg *sl, uint32_t addr, uint16_t len) { + D(sl, "\n*** stlink_read_mem32 ***\n"); + if (len % 4 != 0) { // !!! never ever: fw gives just wrong values + fprintf( + stderr, + "Error: Data length doesn't have a 32 bit alignment: +%d byte.\n", + len % 4); + return; + } + clear_cdb(sl); + sl->cdb_cmd_blk[1] = STLINK_DEBUG_READMEM_32BIT; + // 2-5: addr + // 6-7: len + write_uint32(sl->cdb_cmd_blk + 2, addr); + write_uint16(sl->cdb_cmd_blk + 6, len); + + // data_in 0-0x40-len + // !!! len _and_ q_len must be max 6k, + // i.e. >1024 * 6 = 6144 -> aboard) + // !!! if len < q_len: 64*k, 1024*n, n=1..5 -> aboard + // (broken residue issue) + sl->q_len = len; + sl->q_addr = addr; + stlink_q(sl); + stlink_print_data(sl); +} + +// Write a "len" bytes from the sl->q_buf to the memory, max 64 Bytes. + +void _stlink_sg_write_mem8(struct stlink_libsg *sl, uint32_t addr, uint16_t len) { + D(sl, "\n*** stlink_write_mem8 ***\n"); + clear_cdb(sl); + sl->cdb_cmd_blk[1] = STLINK_DEBUG_WRITEMEM_8BIT; + // 2-5: addr + // 6-7: len (>0x40 (64) -> aboard) + write_uint32(sl->cdb_cmd_blk + 2, addr); + write_uint16(sl->cdb_cmd_blk + 6, len); + + // data_out 0-len + sl->q_len = len; + sl->q_addr = addr; + sl->q_data_dir = Q_DATA_OUT; + stlink_q(sl); + stlink_print_data(sl); +} + +// Write a "len" bytes from the sl->q_buf to the memory, max Q_BUF_LEN bytes. + +void _stlink_sg_write_mem32(struct stlink_libsg *sl, uint32_t addr, uint16_t len) { + clear_cdb(sl); + sl->cdb_cmd_blk[1] = STLINK_DEBUG_WRITEMEM_32BIT; + // 2-5: addr + // 6-7: len "unlimited" + write_uint32(sl->cdb_cmd_blk + 2, addr); + write_uint16(sl->cdb_cmd_blk + 6, len); + + // data_out 0-0x40-...-len + sl->q_len = len; + sl->q_addr = addr; + sl->q_data_dir = Q_DATA_OUT; + stlink_q(sl); + stlink_print_data(sl); +} + +/* FPEC flash controller interface, pm0063 manual + */ + +#define FLASH_REGS_ADDR 0x40022000 +#define FLASH_REGS_SIZE 0x28 + +#define FLASH_ACR (FLASH_REGS_ADDR + 0x00) +#define FLASH_KEYR (FLASH_REGS_ADDR + 0x04) +#define FLASH_SR (FLASH_REGS_ADDR + 0x0c) +#define FLASH_CR (FLASH_REGS_ADDR + 0x10) +#define FLASH_AR (FLASH_REGS_ADDR + 0x14) +#define FLASH_OBR (FLASH_REGS_ADDR + 0x1c) +#define FLASH_WRPR (FLASH_REGS_ADDR + 0x20) + +#define FLASH_RDPTR_KEY 0x00a5 +#define FLASH_KEY1 0x45670123 +#define FLASH_KEY2 0xcdef89ab + +#define FLASH_SR_BSY 0 +#define FLASH_SR_EOP 5 + +#define FLASH_CR_PG 0 +#define FLASH_CR_PER 1 +#define FLASH_CR_MER 2 +#define FLASH_CR_STRT 6 +#define FLASH_CR_LOCK 7 + +static uint32_t __attribute__((unused)) read_flash_rdp(struct stlink_libsg* sl) { + stlink_read_mem32(sl, FLASH_WRPR, sizeof (uint32_t)); + return (*(uint32_t*) sl->q_buf) & 0xff; +} + +static inline uint32_t read_flash_wrpr(struct stlink_libsg* sl) { + stlink_read_mem32(sl, FLASH_WRPR, sizeof (uint32_t)); + return *(uint32_t*) sl->q_buf; +} + +static inline uint32_t read_flash_obr(struct stlink_libsg* sl) { + stlink_read_mem32(sl, FLASH_OBR, sizeof (uint32_t)); + return *(uint32_t*) sl->q_buf; +} + +static inline uint32_t read_flash_cr(struct stlink_libsg* sl) { + stlink_read_mem32(sl, FLASH_CR, sizeof (uint32_t)); + return *(uint32_t*) sl->q_buf; +} + +static inline unsigned int is_flash_locked(struct stlink_libsg* sl) { + /* return non zero for true */ + return read_flash_cr(sl) & (1 << FLASH_CR_LOCK); +} + +static void unlock_flash(struct stlink_libsg* sl) { + /* the unlock sequence consists of 2 write cycles where + 2 key values are written to the FLASH_KEYR register. + an invalid sequence results in a definitive lock of + the FPEC block until next reset. + */ + + write_uint32(sl->q_buf, FLASH_KEY1); + stlink_write_mem32(sl, FLASH_KEYR, sizeof (uint32_t)); + + write_uint32(sl->q_buf, FLASH_KEY2); + stlink_write_mem32(sl, FLASH_KEYR, sizeof (uint32_t)); +} + +static int unlock_flash_if(struct stlink_libsg* sl) { + /* unlock flash if already locked */ + + if (is_flash_locked(sl)) { + unlock_flash(sl); + if (is_flash_locked(sl)) + return -1; + } + + return 0; +} + +static void lock_flash(struct stlink_libsg* sl) { + /* write to 1 only. reset by hw at unlock sequence */ + + const uint32_t n = read_flash_cr(sl) | (1 << FLASH_CR_LOCK); + + write_uint32(sl->q_buf, n); + stlink_write_mem32(sl, FLASH_CR, sizeof (uint32_t)); +} + +static void set_flash_cr_pg(struct stlink_libsg* sl) { + const uint32_t n = 1 << FLASH_CR_PG; + write_uint32(sl->q_buf, n); + stlink_write_mem32(sl, FLASH_CR, sizeof (uint32_t)); +} + +static void __attribute__((unused)) clear_flash_cr_pg(struct stlink_libsg* sl) { + const uint32_t n = read_flash_cr(sl) & ~(1 << FLASH_CR_PG); + write_uint32(sl->q_buf, n); + stlink_write_mem32(sl, FLASH_CR, sizeof (uint32_t)); +} + +static void set_flash_cr_per(struct stlink_libsg* sl) { + const uint32_t n = 1 << FLASH_CR_PER; + write_uint32(sl->q_buf, n); + stlink_write_mem32(sl, FLASH_CR, sizeof (uint32_t)); +} + +static void __attribute__((unused)) clear_flash_cr_per(struct stlink_libsg* sl) { + const uint32_t n = read_flash_cr(sl) & ~(1 << FLASH_CR_PER); + write_uint32(sl->q_buf, n); + stlink_write_mem32(sl, FLASH_CR, sizeof (uint32_t)); +} + +static void set_flash_cr_mer(struct stlink_libsg* sl) { + const uint32_t n = 1 << FLASH_CR_MER; + write_uint32(sl->q_buf, n); + stlink_write_mem32(sl, FLASH_CR, sizeof (uint32_t)); +} + +static void __attribute__((unused)) clear_flash_cr_mer(struct stlink_libsg* sl) { + const uint32_t n = read_flash_cr(sl) & ~(1 << FLASH_CR_MER); + write_uint32(sl->q_buf, n); + stlink_write_mem32(sl, FLASH_CR, sizeof (uint32_t)); +} + +static void set_flash_cr_strt(struct stlink_libsg* sl) { + /* assume come on the flash_cr_per path */ + const uint32_t n = (1 << FLASH_CR_PER) | (1 << FLASH_CR_STRT); + write_uint32(sl->q_buf, n); + stlink_write_mem32(sl, FLASH_CR, sizeof (uint32_t)); +} + +static inline uint32_t read_flash_acr(struct stlink_libsg* sl) { + stlink_read_mem32(sl, FLASH_ACR, sizeof (uint32_t)); + return *(uint32_t*) sl->q_buf; +} + +static inline uint32_t read_flash_sr(struct stlink_libsg* sl) { + stlink_read_mem32(sl, FLASH_SR, sizeof (uint32_t)); + return *(uint32_t*) sl->q_buf; +} + +static inline unsigned int is_flash_busy(struct stlink_libsg* sl) { + return read_flash_sr(sl) & (1 << FLASH_SR_BSY); +} + +static void wait_flash_busy(struct stlink_libsg* sl) { + /* todo: add some delays here */ + while (is_flash_busy(sl)) + ; +} + +static inline unsigned int is_flash_eop(struct stlink_libsg* sl) { + return read_flash_sr(sl) & (1 << FLASH_SR_EOP); +} + +static void __attribute__((unused)) clear_flash_sr_eop(struct stlink_libsg* sl) { + const uint32_t n = read_flash_sr(sl) & ~(1 << FLASH_SR_EOP); + write_uint32(sl->q_buf, n); + stlink_write_mem32(sl, FLASH_SR, sizeof (uint32_t)); +} + +static void __attribute__((unused)) wait_flash_eop(struct stlink_libsg* sl) { + /* todo: add some delays here */ + while (is_flash_eop(sl) == 0) + ; +} + +static inline void write_flash_ar(struct stlink_libsg* sl, uint32_t n) { + write_uint32(sl->q_buf, n); + stlink_write_mem32(sl, FLASH_AR, sizeof (uint32_t)); +} + +#if 0 /* todo */ + +static void disable_flash_read_protection(struct stlink* sl) { + /* erase the option byte area */ + /* rdp = 0x00a5; */ + /* reset */ +} +#endif /* todo */ + +#if 0 /* not working */ + +static int write_flash_mem16 +(struct stlink* sl, uint32_t addr, uint16_t val) { + /* half word writes */ + if (addr % 2) return -1; + + /* unlock if locked */ + unlock_flash_if(sl); + + /* set flash programming chosen bit */ + set_flash_cr_pg(sl); + + write_uint16(sl->q_buf, val); + stlink_write_mem16(sl, addr, 2); + + /* wait for non business */ + wait_flash_busy(sl); + + lock_flash(sl); + + /* check the programmed value back */ + stlink_read_mem16(sl, addr, 2); + if (*(const uint16_t*) sl->q_buf != val) { + /* values differ at i * sizeof(uint16_t) */ + return -1; + } + + /* success */ + return 0; +} +#endif /* not working */ + +int stlink_erase_flash_page(struct stlink_libsg* sl, stm32_addr_t page) { + /* page an addr in the page to erase */ + + /* wait for ongoing op to finish */ + wait_flash_busy(sl); + + /* unlock if locked */ + unlock_flash_if(sl); + + /* set the page erase bit */ + set_flash_cr_per(sl); + + /* select the page to erase */ + write_flash_ar(sl, page); + + /* start erase operation, reset by hw with bsy bit */ + set_flash_cr_strt(sl); + + /* wait for completion */ + wait_flash_busy(sl); + + /* relock the flash */ + lock_flash(sl); + + /* todo: verify the erased page */ + + return 0; +} + +int stlink_erase_flash_mass(struct stlink_libsg* sl) { + /* wait for ongoing op to finish */ + wait_flash_busy(sl); + + /* unlock if locked */ + unlock_flash_if(sl); + + /* set the mass erase bit */ + set_flash_cr_mer(sl); + + /* start erase operation, reset by hw with bsy bit */ + set_flash_cr_strt(sl); + + /* wait for completion */ + wait_flash_busy(sl); + + /* relock the flash */ + lock_flash(sl); + + /* todo: verify the erased memory */ + + return 0; +} + +static unsigned int is_core_halted(struct stlink_libsg* sl) { + /* return non zero if core is halted */ + stlink_status(sl); + return sl->q_buf[0] == STLINK_CORE_HALTED; +} + +static int write_loader_to_sram +(struct stlink* sl, stm32_addr_t* addr, size_t* size) { + /* from openocd, contrib/loaders/flash/stm32.s */ + static const uint8_t loader_code[] ={ + 0x08, 0x4c, /* ldr r4, STM32_FLASH_BASE */ + 0x1c, 0x44, /* add r4, r3 */ + /* write_half_word: */ + 0x01, 0x23, /* movs r3, #0x01 */ + 0x23, 0x61, /* str r3, [r4, #STM32_FLASH_CR_OFFSET] */ + 0x30, 0xf8, 0x02, 0x3b, /* ldrh r3, [r0], #0x02 */ + 0x21, 0xf8, 0x02, 0x3b, /* strh r3, [r1], #0x02 */ + /* busy: */ + 0xe3, 0x68, /* ldr r3, [r4, #STM32_FLASH_SR_OFFSET] */ + 0x13, 0xf0, 0x01, 0x0f, /* tst r3, #0x01 */ + 0xfb, 0xd0, /* beq busy */ + 0x13, 0xf0, 0x14, 0x0f, /* tst r3, #0x14 */ + 0x01, 0xd1, /* bne exit */ + 0x01, 0x3a, /* subs r2, r2, #0x01 */ + 0xf0, 0xd1, /* bne write_half_word */ + /* exit: */ + 0x00, 0xbe, /* bkpt #0x00 */ + 0x00, 0x20, 0x02, 0x40, /* STM32_FLASH_BASE: .word 0x40022000 */ + }; + + memcpy(sl->q_buf, loader_code, sizeof (loader_code)); + stlink_write_mem32(sl, sl->sram_base, sizeof (loader_code)); + + *addr = sl->sram_base; + *size = sizeof (loader_code); + + /* success */ + return 0; +} + + +int init_flash_loader(stlink_t *stl, flash_loader_t* fl) { + struct stlink_libsg* sl = stl->backend_data; + size_t size; + + /* allocate the loader in sram */ + if (write_loader_to_sram(sl, &fl->loader_addr, &size) == -1) { + fprintf(stderr, "write_loader_to_sram() == -1\n"); + return -1; + } + + /* allocate a one page buffer in sram right after loader */ + fl->buf_addr = fl->loader_addr + size; + + return 0; +} + +static int run_flash_loader +(stlink_t *stl, flash_loader_t* fl, stm32_addr_t target, const uint8_t* buf, size_t size) { + struct stlink_libsg* sl = stl->backend_data; + const size_t count = size / sizeof (uint16_t); + + if (write_buffer_to_sram(sl, fl, buf, size) == -1) { + fprintf(stderr, "write_buffer_to_sram() == -1\n"); + return -1; + } + + /* setup core */ + stlink_write_reg(sl, fl->buf_addr, 0); /* source */ + stlink_write_reg(sl, target, 1); /* target */ + stlink_write_reg(sl, count, 2); /* count (16 bits half words) */ + stlink_write_reg(sl, 0, 3); /* flash bank 0 (input) */ + stlink_write_reg(sl, fl->loader_addr, 15); /* pc register */ + + /* unlock and set programming mode */ + unlock_flash_if(sl); + set_flash_cr_pg(sl); + + /* run loader */ + stlink_run(sl); + + while (is_core_halted(sl) == 0) + ; + + lock_flash(sl); + + /* not all bytes have been written */ + stlink_read_reg(sl, 2); + if (sl->reg.r[2] != 0) { + fprintf(stderr, "write error, count == %u\n", sl->reg.r[2]); + return -1; + } + + return 0; +} + +static int stlink_fcheck_flash +(struct stlink_libsg* sl, const char* path, stm32_addr_t addr) { + /* check the contents of path are at addr */ + + int res; + mapped_file_t mf = MAPPED_FILE_INITIALIZER; + + if (map_file(&mf, path) == -1) + return -1; + + res = check_file(sl, &mf, addr); + + unmap_file(&mf); + + return res; +} + +// The stlink_fwrite_flash should not muck with mmapped files inside itself, +// and should use this function instead. (Hell, what's the reason behind mmap +// there?!) But, as it is not actually used anywhere, nobody cares. + +#define WRITE_BLOCK_SIZE 0x40 + +int stlink_write_flash(struct stlink_libsg* sl, stm32_addr_t addr, uint8_t* base, unsigned len) { + int error = -1; + size_t off; + flash_loader_t fl; + + /* check addr range is inside the flash */ + if (addr < sl->flash_base) { + fprintf(stderr, "addr too low\n"); + return -1; + } else if ((addr + len) < addr) { + fprintf(stderr, "addr overruns\n"); + return -1; + } else if ((addr + len) > (sl->flash_base + sl->flash_size)) { + fprintf(stderr, "addr too high\n"); + return -1; + } else if ((addr & 1) || (len & 1)) { + fprintf(stderr, "unaligned addr or size\n"); + return -1; + } + + /* flash loader initialization */ + if (init_flash_loader(sl, &fl) == -1) { + fprintf(stderr, "init_flash_loader() == -1\n"); + return -1; + } + + /* write each page. above WRITE_BLOCK_SIZE fails? */ + for (off = 0; off < len; off += WRITE_BLOCK_SIZE) { + /* adjust last write size */ + size_t size = WRITE_BLOCK_SIZE; + if ((off + WRITE_BLOCK_SIZE) > len) + size = len - off; + + if (run_flash_loader(sl, &fl, addr + off, base + off, size) == -1) { + fprintf(stderr, "run_flash_loader(0x%zx) == -1\n", addr + off); + return -1; + } + } + + for (off = 0; off < len; off += sl->flash_pgsz) { + size_t aligned_size; + + /* adjust last page size */ + size_t cmp_size = sl->flash_pgsz; + if ((off + sl->flash_pgsz) > len) + cmp_size = len - off; + + aligned_size = cmp_size; + if (aligned_size & (4 - 1)) + aligned_size = (cmp_size + 4) & ~(4 - 1); + + stlink_read_mem32(sl, addr + off, aligned_size); + + if (memcmp(sl->q_buf, base + off, cmp_size)) + return -1; + } + + return 0; +} + +int stlink_fwrite_flash +(stlink_t *sl, const char* path, stm32_addr_t addr) { + /* write the file in flash at addr */ + + int error = -1; + size_t off; + mapped_file_t mf = MAPPED_FILE_INITIALIZER; + flash_loader_t fl; + + if (map_file(&mf, path) == -1) { + fprintf(stderr, "map_file() == -1\n"); + return -1; + } + + /* check addr range is inside the flash */ + if (addr < sl->flash_base) { + fprintf(stderr, "addr too low\n"); + goto on_error; + } else if ((addr + mf.len) < addr) { + fprintf(stderr, "addr overruns\n"); + goto on_error; + } else if ((addr + mf.len) > (sl->flash_base + sl->flash_size)) { + fprintf(stderr, "addr too high\n"); + goto on_error; + } else if ((addr & 1) || (mf.len & 1)) { + /* todo */ + fprintf(stderr, "unaligned addr or size\n"); + goto on_error; + } + + /* erase each page. todo: mass erase faster? */ + for (off = 0; off < mf.len; off += sl->flash_pgsz) { + /* addr must be an addr inside the page */ + if (stlink_erase_flash_page(sl, addr + off) == -1) { + fprintf(stderr, "erase_flash_page(0x%zx) == -1\n", addr + off); + goto on_error; + } + } + + /* flash loader initialization */ + if (init_flash_loader(sl, &fl) == -1) { + fprintf(stderr, "init_flash_loader() == -1\n"); + goto on_error; + } + + /* write each page. above WRITE_BLOCK_SIZE fails? */ +#define WRITE_BLOCK_SIZE 0x40 + for (off = 0; off < mf.len; off += WRITE_BLOCK_SIZE) { + /* adjust last write size */ + size_t size = WRITE_BLOCK_SIZE; + if ((off + WRITE_BLOCK_SIZE) > mf.len) + size = mf.len - off; + + if (run_flash_loader(sl, &fl, addr + off, mf.base + off, size) == -1) { + fprintf(stderr, "run_flash_loader(0x%zx) == -1\n", addr + off); + goto on_error; + } + } + + /* check the file ha been written */ + if (check_file(sl, &mf, addr) == -1) { + fprintf(stderr, "check_file() == -1\n"); + goto on_error; + } + + /* success */ + error = 0; + +on_error: + unmap_file(&mf); + return error; +} + +// 1) open a sg device, switch the stlink from dfu to mass mode +// 2) wait 5s until the kernel driver stops reseting the broken device +// 3) reopen the device +// 4) the device driver is now ready for a switch to jtag/swd mode +// TODO thinking, better error handling, wait until the kernel driver stops reseting the plugged-in device + +stlink_backend_t _stlink_sg_backend = { + _stlink_sg_close, + _stlink_sg_exit_debug_mode, + _stlink_sg_enter_swd_mode, + _stlink_sg_enter_jtag_mode, + _stlink_sg_exit_dfu_mode, + _stlink_sg_core_id, + _stlink_sg_reset, + _stlink_sg_run, + _stlink_sg_status, + _stlink_sg_version, + _stlink_sg_write_mem32, + _stlink_sg_write_mem8 +}; + +stlink_t* stlink_open(const char *dev_name, const int verbose) { + fprintf(stderr, "\n*** stlink_open [%s] ***\n", dev_name); + int sg_fd = scsi_pt_open_device(dev_name, RDWR, verbose); + if (sg_fd < 0) { + fprintf(stderr, "error opening device: %s: %s\n", dev_name, + safe_strerror(-sg_fd)); + return NULL; + } + + stlink_t *sl = malloc(sizeof (stlink_t)); + struct stlink_libsg *slsg = malloc(sizeof (struct stlink_libsg)); + if (sl == NULL || slsg == NULL) { + fprintf(stderr, "Couldn't malloc stlink and stlink_sg structures out of memory!\n"); + return NULL; + } + sl->verbose = verbose; + sl->backend_data = slsg; + sl->backend = &_stlink_sg_backend; + + slsg->sg_fd = sg_fd; + sl->core_stat = STLINK_CORE_STAT_UNKNOWN; + slsg->core_id = 0; + slsg->q_addr = 0; + clear_buf(sl); + + /* flash memory settings */ + sl->flash_base = STM32_FLASH_BASE; + sl->flash_size = STM32_FLASH_SIZE; + sl->flash_pgsz = STM32_FLASH_PGSZ; + + /* system memory */ + sl->sys_base = STM32_SYSTEM_BASE; + sl->sys_size = STM32_SYSTEM_SIZE; + + /* sram memory settings */ + sl->sram_base = STM32_SRAM_BASE; + sl->sram_size = STM32_SRAM_SIZE; + + return sl; +} + + + +stlink_t* stlink_quirk_open(const char *dev_name, const int verbose) { + + stlink_t *sl = stlink_open(dev_name, verbose); + if (sl == NULL) { + fputs("Error: could not open stlink device\n", stderr); + return NULL; + } + + stlink_version(sl); + struct stlink_libsg *sg = sl->backend_data; + + if (sg->st_vid != USB_ST_VID || sg->stlink_pid != USB_STLINK_PID) { + fprintf(stderr, "Error: the device %s is not a stlink\n", + dev_name); + fprintf(stderr, " VID: got %04x expect %04x \n", + sg->st_vid, USB_ST_VID); + fprintf(stderr, " PID: got %04x expect %04x \n", + sg->stlink_pid, USB_STLINK_PID); + return NULL; + } + + D(sl, "\n*** stlink_force_open ***\n"); + switch (stlink_current_mode(sl)) { + case STLINK_DEV_MASS_MODE: + return sl; + case STLINK_DEV_DEBUG_MODE: + // TODO go to mass? + return sl; + } + DD(sl, "\n*** switch the stlink to mass mode ***\n"); + _stlink_sg_exit_dfu_mode(sl); + // exit the dfu mode -> the device is gone + DD(sl, "\n*** reopen the stlink device ***\n"); + delay(1000); + stlink_close(sl); + delay(5000); + + sl = stlink_open(dev_name, verbose); + if (sl == NULL) { + fputs("Error: could not open stlink device\n", stderr); + return NULL; + } + // re-query device info + stlink_version(sl); + return sl; +} + +static void __attribute__((unused)) mark_buf(stlink_t *sl) { + clear_buf(sl); + sl->q_buf[0] = 0x12; + sl->q_buf[1] = 0x34; + sl->q_buf[2] = 0x56; + sl->q_buf[3] = 0x78; + sl->q_buf[4] = 0x90; + sl->q_buf[15] = 0x42; + sl->q_buf[16] = 0x43; + sl->q_buf[63] = 0x42; + sl->q_buf[64] = 0x43; + sl->q_buf[1024 * 6 - 1] = 0x42; //6kB + sl->q_buf[1024 * 8 - 1] = 0x42; //8kB +} +#endif diff --git a/src/stlink-sg.h b/src/stlink-sg.h new file mode 100644 index 0000000..00b70d1 --- /dev/null +++ b/src/stlink-sg.h @@ -0,0 +1,75 @@ +/* + * File: stlink-sg.h + * Author: karl + * + * Created on October 1, 2011, 11:29 PM + */ + +#ifndef STLINK_SG_H +#define STLINK_SG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "stlink-common.h" + + // device access +#define RDWR 0 +#define RO 1 +#define SG_TIMEOUT_SEC 1 // actually 1 is about 2 sec + // Each CDB can be a total of 6, 10, 12, or 16 bytes, later version + // of the SCSI standard also allow for variable-length CDBs (min. CDB is 6). + // the stlink needs max. 10 bytes. +#define CDB_6 6 +#define CDB_10 10 +#define CDB_12 12 +#define CDB_16 16 + +#define CDB_SL 10 + + // Query data flow direction. +#define Q_DATA_OUT 0 +#define Q_DATA_IN 1 + + // The SCSI Request Sense command is used to obtain sense data + // (error information) from a target device. + // http://en.wikipedia.org/wiki/SCSI_Request_Sense_Command +#define SENSE_BUF_LEN 32 + + + +#if defined(CONFIG_USE_LIBUSB) + struct stlink_libsg { + int sg_fd; + int do_scsi_pt_err; + + unsigned char cdb_cmd_blk[CDB_SL]; + + int q_data_dir; // Q_DATA_IN, Q_DATA_OUT + // the start of the query data in the device memory space + uint32_t q_addr; + + // Sense (error information) data + unsigned char sense_buf[SENSE_BUF_LEN]; + + uint32_t st_vid; + uint32_t stlink_pid; + uint32_t stlink_v; + uint32_t jtag_v; + uint32_t swim_v; + uint32_t core_id; + + reg reg; + }; +#else + struct stlink_libsg {}; +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* STLINK_SG_H */ + diff --git a/src/stlink-usb.c b/src/stlink-usb.c new file mode 100644 index 0000000..d989633 --- /dev/null +++ b/src/stlink-usb.c @@ -0,0 +1,552 @@ +#include +#include +#include +#include +#include +#include +#include +#include "stlink-common.h" +#include "stlink-usb.h" + +/* endianess related */ +static inline unsigned int is_bigendian(void) { + static volatile const unsigned int i = 1; + return *(volatile const char*) &i == 0; +} + +void _stlink_usb_close(stlink_t* sl) { + struct stlink_libusb * const handle = sl->backend_data; + // maybe we couldn't even get the usb device? + if (handle != NULL) { + if (handle->req_trans != NULL) + libusb_free_transfer(handle->req_trans); + + if (handle->rep_trans != NULL) + libusb_free_transfer(handle->rep_trans); + + if (handle->usb_handle != NULL) + libusb_close(handle->usb_handle); + + libusb_exit(handle->libusb_ctx); + free(handle); + } +} + +static void write_uint32(unsigned char* buf, uint32_t ui) { + if (!is_bigendian()) { // le -> le (don't swap) + buf[0] = ((unsigned char*) &ui)[0]; + buf[1] = ((unsigned char*) &ui)[1]; + buf[2] = ((unsigned char*) &ui)[2]; + buf[3] = ((unsigned char*) &ui)[3]; + } else { + buf[0] = ((unsigned char*) &ui)[3]; + buf[1] = ((unsigned char*) &ui)[2]; + buf[2] = ((unsigned char*) &ui)[1]; + buf[3] = ((unsigned char*) &ui)[0]; + } +} + +static void write_uint16(unsigned char* buf, uint16_t ui) { + if (!is_bigendian()) { // le -> le (don't swap) + buf[0] = ((unsigned char*) &ui)[0]; + buf[1] = ((unsigned char*) &ui)[1]; + } else { + buf[0] = ((unsigned char*) &ui)[1]; + buf[1] = ((unsigned char*) &ui)[0]; + } +} + +static uint32_t read_uint32(const unsigned char *c, const int pt) { + uint32_t ui; + char *p = (char *) &ui; + + if (!is_bigendian()) { // le -> le (don't swap) + p[0] = c[pt]; + p[1] = c[pt + 1]; + p[2] = c[pt + 2]; + p[3] = c[pt + 3]; + } else { + p[0] = c[pt + 3]; + p[1] = c[pt + 2]; + p[2] = c[pt + 1]; + p[3] = c[pt]; + } + return ui; +} + + +struct trans_ctx { +#define TRANS_FLAGS_IS_DONE (1 << 0) +#define TRANS_FLAGS_HAS_ERROR (1 << 1) + volatile unsigned long flags; +}; + +static void on_trans_done(struct libusb_transfer * trans) { + struct trans_ctx * const ctx = trans->user_data; + + if (trans->status != LIBUSB_TRANSFER_COMPLETED) + ctx->flags |= TRANS_FLAGS_HAS_ERROR; + + ctx->flags |= TRANS_FLAGS_IS_DONE; +} + +int submit_wait(struct stlink_libusb *slu, struct libusb_transfer * trans) { + struct timeval start; + struct timeval now; + struct timeval diff; + struct trans_ctx trans_ctx; + enum libusb_error error; + + trans_ctx.flags = 0; + + /* brief intrusion inside the libusb interface */ + trans->callback = on_trans_done; + trans->user_data = &trans_ctx; + + if ((error = libusb_submit_transfer(trans))) { + printf("libusb_submit_transfer(%d)\n", error); + return -1; + } + + gettimeofday(&start, NULL); + + while (trans_ctx.flags == 0) { + struct timeval timeout; + timeout.tv_sec = 3; + timeout.tv_usec = 0; + if (libusb_handle_events_timeout(slu->libusb_ctx, &timeout)) { + printf("libusb_handle_events()\n"); + return -1; + } + + gettimeofday(&now, NULL); + timersub(&now, &start, &diff); + if (diff.tv_sec >= 3) { + printf("libusb_handle_events() timeout\n"); + return -1; + } + } + + if (trans_ctx.flags & TRANS_FLAGS_HAS_ERROR) { + printf("libusb_handle_events() | has_error\n"); + return -1; + } + + return 0; +} + +ssize_t send_recv(struct stlink_libusb* handle, + unsigned char* txbuf, size_t txsize, + unsigned char* rxbuf, size_t rxsize) { + /* note: txbuf and rxbuf can point to the same area */ + + libusb_fill_bulk_transfer(handle->req_trans, handle->usb_handle, + handle->ep_req, + txbuf, txsize, + NULL, NULL, + 0 + ); + + printf("submit_wait(req)\n"); + + if (submit_wait(handle, handle->req_trans)) return -1; + + /* send_only */ + if (rxsize == 0) return 0; + + /* read the response */ + + libusb_fill_bulk_transfer(handle->rep_trans, handle->usb_handle, + handle->ep_rep, rxbuf, rxsize, NULL, NULL, 0); + + printf("submit_wait(rep)\n"); + + if (submit_wait(handle, handle->rep_trans)) return -1; + + return handle->rep_trans->actual_length; +} + +static inline int send_only +(struct stlink_libusb* handle, unsigned char* txbuf, size_t txsize) { + return send_recv(handle, txbuf, txsize, NULL, 0); +} + + +// KARL - fixme, common code! (or, one per backend) +// candidate for common code... + + +static int is_stlink_device(libusb_device * dev) { + struct libusb_device_descriptor desc; + + if (libusb_get_device_descriptor(dev, &desc)) + return 0; + + printf("device: 0x%04x, 0x%04x\n", desc.idVendor, desc.idProduct); + + if (desc.idVendor != 0x0483) + return 0; + + if (desc.idProduct != 0x3748) + return 0; + + return 1; +} + +void _stlink_usb_version(stlink_t * sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const buf = sl->q_buf; + ssize_t size; + + memset(buf, 0, sizeof (sl->q_buf)); + buf[0] = STLINK_GET_VERSION; + buf[1] = 0x80; + + size = send_recv(slu, buf, 16, buf, sizeof (sl->q_buf)); + if (size == -1) { + printf("[!] send_recv\n"); + return; + }mkdir g + +#if 1 /* DEBUG */ + { + unsigned int i; + for (i = 0; i < size; ++i) printf("%02x", buf[i]); + printf("\n"); + } +#endif /* DEBUG */ +} + +void _stlink_usb_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { + D(sl, "oops! no write32 support yet ;)\n"); +} + +void _stlink_usb_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) { + D(sl, "oops! no write8 support yet ;)\n"); +} + + + +int stlink_current_mode(stlink_t * sl) { + int mode = -1; + + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const buf = sl->q_buf; + ssize_t size; + + memset(buf, 0, sizeof (sl->q_buf)); + + buf[0] = STLINK_GET_CURRENT_MODE; + + size = send_recv(slu, buf, 16, buf, sizeof (sl->q_buf)); + if (size == -1) { + printf("[!] send_recv\n"); + return -1; + } + + /* mode = (int)read_uint16(buf, 0); */ + mode = (int) buf[0]; + +#if 1 /* DEBUG */ + printf("mode == 0x%x\n", mode); +#endif /* DEBUG */ + + + return mode; +} + +void _stlink_usb_core_id(stlink_t * sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const buf = sl->q_buf; + ssize_t size; + + memset(buf, 0, sizeof (sl->q_buf)); + buf[0] = STLINK_DEBUG_COMMAND; + buf[1] = STLINK_DEBUG_READCOREID; + + size = send_recv(slu, buf, 16, buf, sizeof (sl->q_buf)); + if (size == -1) { + printf("[!] send_recv\n"); + return; + } + + sl->core_id = read_uint32(buf, 0); +} + +void _stlink_usb_status(stlink_t * sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const buf = sl->q_buf; + ssize_t size; + + memset(buf, 0, sizeof (sl->q_buf)); + + buf[0] = STLINK_DEBUG_COMMAND; + buf[1] = STLINK_DEBUG_GETSTATUS; + + size = send_recv(slu, buf, 16, buf, sizeof (sl->q_buf)); + if (size == -1) { + printf("[!] send_recv\n"); + return; + } + + /* todo: stlink_core_stat */ + + // FIXME - decode into sl->core_stat +#if 1 /* DEBUG */ + printf("status == 0x%x\n", buf[0]); +#endif /* DEBUG */ + +} + +void _stlink_enter_swd_mode(stlink_t * sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const buf = sl->q_buf; + ssize_t size; + + memset(buf, 0, sizeof (sl->q_buf)); + + buf[0] = STLINK_DEBUG_COMMAND; + buf[1] = 0x30; /* needed byte */ + buf[2] = STLINK_DEBUG_ENTER_JTAG; + + size = send_recv(slu, buf, 16, buf, sizeof (sl->q_buf)); + if (size == -1) { + printf("[!] send_recv\n"); + return; + } + +} + +void _stlink_usb_exit_dfu_mode(stlink_t* sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const buf = sl->q_buf; + ssize_t size; + + memset(buf, 0, sizeof (sl->q_buf)); + buf[0] = STLINK_DFU_COMMAND; + buf[1] = STLINK_DFU_EXIT; + + size = send_only(slu, buf, 16); + if (size == -1) { + printf("[!] send_recv\n"); + return; + } +} + +void _stlink_usb_reset(stlink_t * sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const buf = sl->q_buf; + ssize_t size; + + memset(buf, 0, sizeof (sl->q_buf)); + buf[0] = STLINK_DEBUG_COMMAND; + buf[1] = STLINK_DEBUG_RESETSYS; + + size = send_recv(slu, buf, 16, buf, sizeof (sl->q_buf)); + if (size == -1) { + printf("[!] send_recv\n"); + return; + } +} + + +void stlink_step(stlink_t* sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const buf = sl->q_buf; + ssize_t size; + + memset(buf, 0, sizeof (sl->q_buf)); + buf[0] = STLINK_DEBUG_COMMAND; + buf[1] = STLINK_DEBUG_STEPCORE; + + size = send_recv(slu, buf, 16, buf, sizeof (sl->q_buf)); + if (size == -1) { + printf("[!] send_recv\n"); + return; + } +} + +void _stlink_usb_run(stlink_t* sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const buf = sl->q_buf; + ssize_t size; + + memset(buf, 0, sizeof (sl->q_buf)); + buf[0] = STLINK_DEBUG_COMMAND; + buf[1] = STLINK_DEBUG_RUNCORE; + + size = send_recv(slu, buf, 16, buf, sizeof (sl->q_buf)); + if (size == -1) { + printf("[!] send_recv\n"); + return; + } + +} + +void _stlink_usb_exit_debug_mode(stlink_t *sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const buf = sl->q_buf; + ssize_t size; + + memset(buf, 0, sizeof (sl->q_buf)); + buf[0] = STLINK_DEBUG_COMMAND; + buf[1] = STLINK_DEBUG_EXIT; + + size = send_only(slu, buf, 16); + if (size == -1) { + printf("[!] send_only\n"); + return; + } +} + +void stlink_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const buf = sl->q_buf; + ssize_t size; + + /* assume len < sizeof(sl->q_buf) */ + + memset(buf, 0, sizeof (sl->q_buf)); + buf[0] = STLINK_DEBUG_COMMAND; + buf[1] = STLINK_DEBUG_READMEM_32BIT; + write_uint32(buf + 2, addr); + write_uint16(buf + 6, len); +#if 0 + /* windows usb logs show only one byte is used for length ... */ + buf[6] = (uint8_t) len; +#endif + + size = send_recv(slu, buf, 0x10, buf, sizeof (sl->q_buf)); + if (size == -1) { + printf("[!] send_recv\n"); + return; + } + + sl->q_len = (size_t) size; + + stlink_print_data(sl); +} + + +stlink_backend_t _stlink_usb_backend = { + _stlink_usb_close, + _stlink_usb_exit_debug_mode, + _stlink_enter_swd_mode, + NULL, + _stlink_usb_exit_dfu_mode, + _stlink_usb_core_id, + _stlink_usb_reset, + _stlink_usb_run, + _stlink_usb_status, + _stlink_usb_version, + _stlink_usb_write_mem32, + _stlink_usb_write_mem8 +}; + +stlink_t* stlink_open_usb(const char *dev_name, const int verbose) { + stlink_t* sl = NULL; + struct stlink_libusb* slu = NULL; + + sl = malloc(sizeof (stlink_t)); + slu = malloc(sizeof (struct stlink_libusb)); + if (sl == NULL) goto on_error; + if (slu == NULL) goto on_error; + + sl->verbose = verbose; + + if (slu->libusb_ctx != NULL) { + fprintf(stderr, "reopening with an existing context? undefined behaviour!\n"); + goto on_error; + } else { + if (libusb_init(&(slu->libusb_ctx))) { + fprintf(stderr, "failed to init libusb context, wrong version of libraries?\n"); + goto on_error; + } + } + + int error = -1; + + libusb_device** devs = NULL; + libusb_device* dev; + ssize_t i; + ssize_t count; + int config; + + count = libusb_get_device_list(slu->libusb_ctx, &devs); + if (count < 0) { + printf("libusb_get_device_list\n"); + goto on_libusb_error; + } + + for (i = 0; i < count; ++i) { + dev = devs[i]; + if (is_stlink_device(dev)) break; + } + if (i == count) return NULL; + + if (libusb_open(dev, &(slu->usb_handle))) { + printf("libusb_open()\n"); + goto on_libusb_error; + } + + if (libusb_get_configuration(slu->usb_handle, &config)) { + /* this may fail for a previous configured device */ + printf("libusb_get_configuration()\n"); + goto on_libusb_error; + } + + if (config != 1) { + printf("setting new configuration (%d -> 1)\n", config); + if (libusb_set_configuration(slu->usb_handle, 1)) { + /* this may fail for a previous configured device */ + printf("libusb_set_configuration()\n"); + goto on_libusb_error; + } + } + + if (libusb_claim_interface(slu->usb_handle, 0)) { + printf("libusb_claim_interface()\n"); + goto on_libusb_error; + } + + slu->req_trans = libusb_alloc_transfer(0); + if (slu->req_trans == NULL) { + printf("libusb_alloc_transfer\n"); + goto on_libusb_error; + } + + slu->rep_trans = libusb_alloc_transfer(0); + if (slu->rep_trans == NULL) { + printf("libusb_alloc_transfer\n"); + goto on_libusb_error; + } + + slu->ep_rep = 1 /* ep rep */ | LIBUSB_ENDPOINT_IN; + slu->ep_req = 2 /* ep req */ | LIBUSB_ENDPOINT_OUT; + + /* libusb_reset_device(slu->usb_handle); */ + + /* success */ + error = 0; + +on_libusb_error: + if (devs != NULL) { + libusb_free_device_list(devs, 1); + fprintf(stderr, "freed libusb device list\n"); + } + + if (error == -1) { + stlink_close(sl); + return NULL; + } + + sl->backend = &_stlink_usb_backend; + sl->backend_data = slu; + /* success */ + return sl; + +on_error: + if (sl != NULL) free(sl); + if (slu != NULL) free(slu); + return 0; +} \ No newline at end of file diff --git a/src/stlink-usb.h b/src/stlink-usb.h new file mode 100644 index 0000000..fe9645a --- /dev/null +++ b/src/stlink-usb.h @@ -0,0 +1,40 @@ +/* + * File: stlink-usb.h + * Author: karl + * + * Created on October 1, 2011, 11:29 PM + */ + +#ifndef STLINK_USB_H +#define STLINK_USB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#if defined(CONFIG_USE_LIBUSB) + struct stlink_libusb { + libusb_context* libusb_ctx; + libusb_device_handle* usb_handle; + struct libusb_transfer* req_trans; + struct libusb_transfer* rep_trans; + unsigned int ep_req; + unsigned int ep_rep; + }; +#else +#error "it's all bad!" + struct stlink_libusb {}; +#endif + + + stlink_t* stlink_open_usb(const char *dev_name, const int verbose); + + +#ifdef __cplusplus +} +#endif + +#endif /* STLINK_USB_H */ + diff --git a/src/test_sg.c b/src/test_sg.c new file mode 100644 index 0000000..8b86061 --- /dev/null +++ b/src/test_sg.c @@ -0,0 +1,213 @@ +/* + * File: test_main.c + * + * main() ripped out of old stlink-hw.c + */ + +#include +#include +#include "stlink-common.h" + +#if 0 +int main(int argc, char *argv[]) { + // set scpi lib debug level: 0 for no debug info, 10 for lots + const int scsi_verbose = 2; + char *dev_name; + + switch (argc) { + case 1: + fputs( + "\nUsage: stlink-access-test /dev/sg0, sg1, ...\n" + "\n*** Notice: The stlink firmware violates the USB standard.\n" + "*** If you plug-in the discovery's stlink, wait a several\n" + "*** minutes to let the kernel driver swallow the broken device.\n" + "*** Watch:\ntail -f /var/log/messages\n" + "*** This command sequence can shorten the waiting time and fix some issues.\n" + "*** Unplug the stlink and execute once as root:\n" + "modprobe -r usb-storage && modprobe usb-storage quirks=483:3744:lrwsro\n\n", + stderr); + return EXIT_FAILURE; + case 2: + dev_name = argv[1]; + break; + default: + return EXIT_FAILURE; + } + + fputs("*** stlink access test ***\n", stderr); + DD(sl, "Using sg_lib %s : scsi_pt %s\n", sg_lib_version(), + scsi_pt_version()); + + struct stlink *sl = stlink_force_open(dev_name, scsi_verbose); + if (sl == NULL) + return EXIT_FAILURE; + + // we are in mass mode, go to swd + stlink_enter_swd_mode(sl); + stlink_current_mode(sl); + stlink_core_id(sl); + //---------------------------------------------------------------------- + + stlink_status(sl); + //stlink_force_debug(sl); + stlink_reset(sl); + stlink_status(sl); +#if 0 + // core system control block + stlink_read_mem32(sl, 0xe000ed00, 4); + DD(sl, "cpu id base register: SCB_CPUID = got 0x%08x expect 0x411fc231", read_uint32(sl->q_buf, 0)); + // no MPU + stlink_read_mem32(sl, 0xe000ed90, 4); + DD(sl, "mpu type register: MPU_TYPER = got 0x%08x expect 0x0", read_uint32(sl->q_buf, 0)); + + stlink_read_mem32(sl, 0xe000edf0, 4); + DD(sl, "DHCSR = 0x%08x", read_uint32(sl->q_buf, 0)); + + stlink_read_mem32(sl, 0x4001100c, 4); + DD(sl, "GPIOC_ODR = 0x%08x", read_uint32(sl->q_buf, 0)); +#endif +#if 0 + // happy new year 2011: let blink all the leds + // see "RM0041 Reference manual - STM32F100xx advanced ARM-based 32-bit MCUs" + +#define GPIOC 0x40011000 // port C +#define GPIOC_CRH (GPIOC + 0x04) // port configuration register high +#define GPIOC_ODR (GPIOC + 0x0c) // port output data register +#define LED_BLUE (1<<8) // pin 8 +#define LED_GREEN (1<<9) // pin 9 + stlink_read_mem32(sl, GPIOC_CRH, 4); + uint32_t io_conf = read_uint32(sl->q_buf, 0); + DD(sl, "GPIOC_CRH = 0x%08x", io_conf); + + // set: general purpose output push-pull, output mode, max speed 10 MHz. + write_uint32(sl->q_buf, 0x44444411); + stlink_write_mem32(sl, GPIOC_CRH, 4); + + clear_buf(sl); + for (int i = 0; i < 100; i++) { + write_uint32(sl->q_buf, LED_BLUE | LED_GREEN); + stlink_write_mem32(sl, GPIOC_ODR, 4); + /* stlink_read_mem32(sl, 0x4001100c, 4); */ + /* DD(sl, "GPIOC_ODR = 0x%08x", read_uint32(sl->q_buf, 0)); */ + delay(100); + + clear_buf(sl); + stlink_write_mem32(sl, GPIOC_ODR, 4); // PC lo + delay(100); + } + write_uint32(sl->q_buf, io_conf); // set old state + +#endif +#if 0 + // TODO rtfm: stlink doesn't have flash write routines + // writing to the flash area confuses the fw for the next read access + + //stlink_read_mem32(sl, 0, 1024*6); + // flash 0x08000000 128kB + fputs("++++++++++ read a flash at 0x0800 0000\n", stderr); + stlink_read_mem32(sl, 0x08000000, 1024 * 6); //max 6kB + clear_buf(sl); + stlink_read_mem32(sl, 0x08000c00, 5); + stlink_read_mem32(sl, 0x08000c00, 4); + mark_buf(sl); + stlink_write_mem32(sl, 0x08000c00, 4); + stlink_read_mem32(sl, 0x08000c00, 256); + stlink_read_mem32(sl, 0x08000c00, 256); +#endif +#if 0 + // sram 0x20000000 8kB + fputs("\n++++++++++ read/write 8bit, sram at 0x2000 0000 ++++++++++++++++\n\n", stderr); + clear_buf(sl); + stlink_write_mem8(sl, 0x20000000, 16); + + mark_buf(sl); + stlink_write_mem8(sl, 0x20000000, 1); + stlink_write_mem8(sl, 0x20000001, 1); + stlink_write_mem8(sl, 0x2000000b, 3); + stlink_read_mem32(sl, 0x20000000, 16); +#endif +#if 0 + // a not aligned mem32 access doesn't work indeed + fputs("\n++++++++++ read/write 32bit, sram at 0x2000 0000 ++++++++++++++++\n\n", stderr); + clear_buf(sl); + stlink_write_mem8(sl, 0x20000000, 32); + + mark_buf(sl); + stlink_write_mem32(sl, 0x20000000, 1); + stlink_read_mem32(sl, 0x20000000, 16); + mark_buf(sl); + stlink_write_mem32(sl, 0x20000001, 1); + stlink_read_mem32(sl, 0x20000000, 16); + mark_buf(sl); + stlink_write_mem32(sl, 0x2000000b, 3); + stlink_read_mem32(sl, 0x20000000, 16); + + mark_buf(sl); + stlink_write_mem32(sl, 0x20000000, 17); + stlink_read_mem32(sl, 0x20000000, 32); +#endif +#if 0 + // sram 0x20000000 8kB + fputs("++++++++++ read/write 32bit, sram at 0x2000 0000 ++++++++++++\n", stderr); + mark_buf(sl); + stlink_write_mem8(sl, 0x20000000, 64); + stlink_read_mem32(sl, 0x20000000, 64); + + mark_buf(sl); + stlink_write_mem32(sl, 0x20000000, 1024 * 8); //8kB + stlink_read_mem32(sl, 0x20000000, 1024 * 6); + stlink_read_mem32(sl, 0x20000000 + 1024 * 6, 1024 * 2); +#endif +#if 0 + stlink_read_all_regs(sl); + stlink_step(sl); + fputs("++++++++++ write r0 = 0x12345678\n", stderr); + stlink_write_reg(sl, 0x12345678, 0); + stlink_read_reg(sl, 0); + stlink_read_all_regs(sl); +#endif +#if 0 + stlink_run(sl); + stlink_status(sl); + + stlink_force_debug(sl); + stlink_status(sl); +#endif +#if 1 /* read the system bootloader */ + fputs("\n++++++++++ reading bootloader ++++++++++++++++\n\n", stderr); + stlink_fread(sl, "/tmp/barfoo", sl->sys_base, sl->sys_size); +#endif +#if 0 /* read the flash memory */ + fputs("\n+++++++ read flash memory\n\n", stderr); + /* mark_buf(sl); */ + stlink_read_mem32(sl, 0x08000000, 4); +#endif +#if 0 /* flash programming */ + fputs("\n+++++++ program flash memory\n\n", stderr); + stlink_fwrite_flash(sl, "/tmp/foobar", 0x08000000); +#endif +#if 0 /* check file contents */ + fputs("\n+++++++ check flash memory\n\n", stderr); + { + const int res = stlink_fcheck_flash(sl, "/tmp/foobar", 0x08000000); + printf("_____ stlink_fcheck_flash() == %d\n", res); + } +#endif +#if 0 + fputs("\n+++++++ sram write and execute\n\n", stderr); + stlink_fwrite_sram(sl, "/tmp/foobar", sl->sram_base); + stlink_run_at(sl, sl->sram_base); +#endif + + stlink_run(sl); + stlink_status(sl); + //---------------------------------------------------------------------- + // back to mass mode, just in case ... + stlink_exit_debug_mode(sl); + stlink_current_mode(sl); + stlink_close(sl); + + //fflush(stderr); fflush(stdout); + return EXIT_SUCCESS; +} +#endif \ No newline at end of file diff --git a/src/test_usb.c b/src/test_usb.c new file mode 100644 index 0000000..18b4fe5 --- /dev/null +++ b/src/test_usb.c @@ -0,0 +1,62 @@ +#include +#include "stlink-common.h" + + +int main(int ac, char** av) { + stlink_t* sl; + + sl = stlink_open_usb(NULL, 10); + if (sl != NULL) { + printf("-- version\n"); + stlink_version(sl); + + if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) { + printf("-- exit_dfu_mode\n"); + stlink_exit_dfu_mode(sl); + } + + printf("-- enter_swd_mode\n"); + stlink_enter_swd_mode(sl); + + printf("-- current_mode\n"); + stlink_current_mode(sl); + + /* printf("-- core_id\n"); */ + /* stlink_core_id(sl); */ + + printf("-- read_sram\n"); + static const uint32_t sram_base = 0x8000000; + uint32_t off; + for (off = 0; off < 16; off += 4) + stlink_read_mem32(sl, sram_base + off, 4); + + printf("-- read_mem, cpuid\n"); + stlink_read_mem32(sl, 0xe000e008, 4); + /* stlink_read_mem32(sl, 0xe000ed90, 4); */ + /* stlink_read_mem32(sl, 0xe000edf0, 4); */ + /* stlink_read_mem32(sl, 0x4001100c, 4); */ + + printf("-- status\n"); + stlink_status(sl); + + printf("-- reset\n"); + stlink_reset(sl); + + printf("-- status\n"); + stlink_status(sl); + + printf("-- step\n"); + stlink_step(sl); + getchar(); + + printf("-- run\n"); + stlink_run(sl); + + printf("-- exit_debug_mode\n"); + stlink_exit_debug_mode(sl); + + stlink_close(sl); + } + + return 0; +}