From: Peter Zotov Date: Tue, 15 Feb 2011 02:15:28 +0000 (+0300) Subject: Add working GDB remote debug server. X-Git-Url: https://git.gag.com/?a=commitdiff_plain;h=a6c902d67b8fb7f538f1217ba7bd4ab69f393a5f;p=fw%2Fstlink Add working GDB remote debug server. --- diff --git a/AUTHORS b/AUTHORS index f0ec8e5..b7fbf6a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,3 +1,4 @@ Martin Capitanio Spencer Oliver Le Mentec Fabien +Peter Zotov diff --git a/README b/README index 9861697..77697c8 100644 --- a/README +++ b/README @@ -1,5 +1,18 @@ -cd build -make +HOWTO +===== -edit Makefile -make run +To run the gdb server, do (you do not need sudo if you have +set up permissions correctly): +$ make -C build && sudo ./build/st-util 1234 /dev/sg1 + +Then, in gdb: +(gdb) target remote :1234 + +Have fun! + +Caveats +======= + +`continue' GDB command does not work: target does not step at +all or steps with a turtle speed. Looks like there's something +wrong with SCSI requests. diff --git a/build/.gitignore b/build/.gitignore new file mode 100644 index 0000000..bc74c12 --- /dev/null +++ b/build/.gitignore @@ -0,0 +1,3 @@ +*.o +*.d +st-util diff --git a/build/Makefile b/build/Makefile index 34c25c6..076f974 100644 --- a/build/Makefile +++ b/build/Makefile @@ -1,5 +1,4 @@ -PRG := stlink-access-test -DEV := /dev/sg2 +PRG := st-util all: $(PRG) @@ -7,21 +6,16 @@ LIBS := \ -lsgutils2 OBJS += \ - stlink-access-test.o + stlink-hw.o gdb-remote.o gdb-server.o $(PRG): $(OBJS) - @echo 'Invoking: GCC C Linker' - gcc -o$(PRG) $(OBJS) $(LIBS) + gcc -o $(PRG) $(OBJS) $(LIBS) %.o: ../src/%.c - @echo 'Building file: $<' - gcc -O0 -g3 -Wall -c -fmessage-length=0 -std=gnu99 -MMD -MP \ + gcc -O3 -g3 -Wall -Werror -c -std=gnu99 -MMD -MP \ + -fno-strict-aliasing -Wno-unused \ -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)"\ - -o"$@" "$<" + -o "$@" "$<" clean: @rm -vf *.d *.o $(PRG) - -run: all - cp $(PRG) /tmp/ - sudo /tmp/$(PRG) $(DEV) diff --git a/src/gdb-remote.c b/src/gdb-remote.c new file mode 100644 index 0000000..8ed132f --- /dev/null +++ b/src/gdb-remote.c @@ -0,0 +1,145 @@ +/* -*- 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 + +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_size; +} + +int gdb_wait_for_interrupt(int fd) { + char c; + while(1) { + if(read(fd, &c, 1) != 1) + return -2; + + if(c == '\x03') // ^C + return 0; + } +} + diff --git a/src/gdb-remote.h b/src/gdb-remote.h new file mode 100644 index 0000000..d4c7d7a --- /dev/null +++ b/src/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_wait_for_interrupt(int fd); + +#endif diff --git a/src/gdb-server.c b/src/gdb-server.c new file mode 100644 index 0000000..cd44cc0 --- /dev/null +++ b/src/gdb-server.c @@ -0,0 +1,244 @@ +/* -*- 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 "gdb-remote.h" +#include "stlink-hw.h" + +static const char hex[] = "0123456789abcdef"; + +int main(int argc, char** argv) { + struct sockaddr_in serv_addr; + + if(argc != 3) { + fprintf(stderr, "Usage: %s /dev/sgX\n", argv[0]); + return 1; + } + + int port = atoi(argv[1]); + + int sock = socket(AF_INET, SOCK_STREAM, 0); + if(sock < 0) { + perror("socket"); + return 1; + } + + memset((char *) &serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + 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; + } + + 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); + + stlink_core_id(sl); + printf("Debugging ARM core %08x.\n", sl->core_id); + + stlink_force_debug(sl); + stlink_reset(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"); + + while(1) { + char* packet; + + int status = gdb_recv_packet(client, &packet); + if(status < 0) { + fprintf(stderr, "cannot recv: %d\n", status); + return 1; + } + + //printf("recv: %s\n", packet); + + char* reply = NULL; + + switch(packet[0]) { + case 'c': + stlink_run(sl); + + printf("Core running, waiting for interrupt.\n"); + + int status = gdb_wait_for_interrupt(client); + if(status < 0) { + fprintf(stderr, "cannot wait for int: %d\n", status); + return 1; + } + + stlink_force_debug(sl); + + reply = strdup("S05"); // TRAP + break; + + case 's': + stlink_step(sl); + + reply = strdup("S05"); // TRAP + break; + + case '?': + reply = strdup("S05"); // TRAP + 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; + } + + default: + reply = strdup(""); + } + + if(reply) { + //printf("send: %s\n", reply); + + 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-access-test.c b/src/stlink-access-test.c deleted file mode 100644 index 1fa6e5f..0000000 --- a/src/stlink-access-test.c +++ /dev/null @@ -1,1964 +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. - - Notes: - gcc -O0 -g3 -Wall -c -std=gnu99 -o stlink-access-test.o stlink-access-test.c - gcc -o stlink-access-test stlink-access-test.o -lsgutils2 - - 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 - -// sgutils2 (apt-get install libsgutils2-dev) -#include -#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_RUNNINIG 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; - -struct stlink { - 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; -}; - -static void D(struct stlink *sl, char *txt) { - if (sl->verbose > 1) - fputs(txt, stderr); -} - -// 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 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) { - fprintf(stderr, "*** 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)) - fprintf(stderr, " 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)) - fprintf(stderr, " 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); - fprintf(stderr, " 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); - fprintf(stderr, "%s", buf); - } - if (sl->verbose && (resid > 0)) { - if ((sl->verbose) || (sl->q_len > 0)) - fprintf(stderr, " 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). - fprintf(stderr, " transport: %s", buf); - } - return; - case SCSI_PT_RESULT_OS_ERR: - if (sl->verbose) { - get_scsi_pt_os_err_str(ptvp, sizeof(buf), buf); - fprintf(stderr, " os: %s", buf); - } - return; - default: - fprintf(stderr, " unknown pass through result " - "category (%d)\n", cat); - } -} - -static void stlink_q(struct stlink* sl) { - fputs("CDB[", stderr); - for (int i = 0; i < CDB_SL; i++) - fprintf(stderr, " 0x%02x", (unsigned int) sl->cdb_cmd_blk[i]); - fputs("]\n", stderr); - - // 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; - - fprintf(stderr, "st vid = 0x%04x (expect 0x%04x)\n", - sl->st_vid, USB_ST_VID); - fprintf(stderr, "stlink pid = 0x%04x (expect 0x%04x)\n", - sl->stlink_pid, USB_STLINK_PID); - fprintf(stderr, "stlink version = 0x%x\n", sl->stlink_v); - fprintf(stderr, "jtag version = 0x%x\n", sl->jtag_v); - fprintf(stderr, "swim version = 0x%x\n", sl->swim_v); - if (sl->jtag_v == 0) - fprintf(stderr, - " notice: the firmware doesn't support a jtag/swd interface\n"); - if (sl->swim_v == 0) - fprintf(stderr, - " 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: - fprintf(stderr, "stlink mode: dfu\n"); - return STLINK_DEV_DFU_MODE; - case STLINK_DEV_DEBUG_MODE: - fprintf(stderr, "stlink mode: debug (jtag or swd)\n"); - return STLINK_DEV_DEBUG_MODE; - case STLINK_DEV_MASS_MODE: - fprintf(stderr, "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: - fprintf(stderr, " %s: ok\n", txt); - return; - case STLINK_FALSE: - fprintf(stderr, " %s: false\n", txt); - return; - default: - fprintf(stderr, " %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_RUNNINIG: - sl->core_stat = STLINK_CORE_RUNNINIG; - fprintf(stderr, " core status: running\n"); - return; - case STLINK_CORE_HALTED: - sl->core_stat = STLINK_CORE_HALTED; - fprintf(stderr, " 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 - */ -} - -static 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); - fprintf(stderr, "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) - fprintf(stderr, "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; - - fprintf(stderr, "xpsr = 0x%08x\n", sl->reg.xpsr); - fprintf(stderr, "main_sp = 0x%08x\n", sl->reg.main_sp); - fprintf(stderr, "process_sp = 0x%08x\n", sl->reg.process_sp); - fprintf(stderr, "rw = 0x%08x\n", sl->reg.rw); - fprintf(stderr, "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"); - fprintf(stderr, " (%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); - fprintf(stderr, "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); -} - -void stlink_write_mem32(struct stlink *sl, uint32_t addr, uint16_t len); -void stlink_write_mem16(struct stlink *sl, uint32_t addr, uint16_t len) { - stlink_write_mem32(sl, addr, 4); -} - -// 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 */ - -static int 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; -} - -static int __attribute__((unused)) 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; -} - -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 (erase_flash_page(sl, addr + off) == -1) - { - fprintf(stderr, "erase_flash_page(0x%x) == -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%x) == -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_force_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; - } - fprintf(stderr, "\n*** switch the stlink to mass mode ***\n"); - stlink_exit_dfu_mode(sl); - // exit the dfu mode -> the device is gone - fprintf(stderr, "\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 -} - -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); - fprintf(stderr, "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); - fprintf(stderr, "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); - fprintf(stderr, "mpu type register: MPU_TYPER = got 0x%08x expect 0x0", read_uint32(sl->q_buf, 0)); - - stlink_read_mem32(sl, 0xe000edf0, 4); - fprintf(stderr, "DHCSR = 0x%08x", read_uint32(sl->q_buf, 0)); - - stlink_read_mem32(sl, 0x4001100c, 4); - fprintf(stderr, "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); - fprintf(stderr, "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); */ - /* fprintf(stderr, "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; -} diff --git a/src/stlink-hw.c b/src/stlink-hw.c new file mode 100644 index 0000000..441b692 --- /dev/null +++ b/src/stlink-hw.c @@ -0,0 +1,1850 @@ +/* + 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 */ + +static int 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; +} + +static int __attribute__((unused)) 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; +} + +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 (erase_flash_page(sl, addr + off) == -1) + { + fprintf(stderr, "erase_flash_page(0x%x) == -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%x) == -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 new file mode 100644 index 0000000..9ce085b --- /dev/null +++ b/src/stlink-hw.h @@ -0,0 +1,158 @@ +#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; + +struct stlink { + 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* stlink_quirk_open(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); + +#endif