From a6c902d67b8fb7f538f1217ba7bd4ab69f393a5f Mon Sep 17 00:00:00 2001 From: Peter Zotov Date: Tue, 15 Feb 2011 05:15:28 +0300 Subject: [PATCH] Add working GDB remote debug server. --- AUTHORS | 1 + README | 21 +- build/.gitignore | 3 + build/Makefile | 18 +- src/gdb-remote.c | 145 ++++++++++++ src/gdb-remote.h | 8 + src/gdb-server.c | 244 +++++++++++++++++++ src/{stlink-access-test.c => stlink-hw.c} | 270 +++++++--------------- src/stlink-hw.h | 158 +++++++++++++ 9 files changed, 660 insertions(+), 208 deletions(-) create mode 100644 build/.gitignore create mode 100644 src/gdb-remote.c create mode 100644 src/gdb-remote.h create mode 100644 src/gdb-server.c rename src/{stlink-access-test.c => stlink-hw.c} (88%) create mode 100644 src/stlink-hw.h 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-hw.c similarity index 88% rename from src/stlink-access-test.c rename to src/stlink-hw.c index 1fa6e5f..441b692 100644 --- a/src/stlink-access-test.c +++ b/src/stlink-hw.c @@ -12,10 +12,6 @@ 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 @@ -72,6 +68,7 @@ #define __USE_GNU #include #include +#include #include #include #include @@ -83,148 +80,26 @@ #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; -}; +#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); + //fprintf(stderr, "*** wait %d ms\n", ms); usleep(1000 * ms); } @@ -279,6 +154,20 @@ static uint32_t read_uint32(const unsigned char *c, const int 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; @@ -289,7 +178,7 @@ static void clear_cdb(struct stlink *sl) { // E.g. make the valgrind happy. static void clear_buf(struct stlink *sl) { - fprintf(stderr, "*** clear_buf ***\n"); + DD(sl, "*** clear_buf ***\n"); for (int i = 0; i < sizeof(sl->q_buf); i++) sl->q_buf[i] = 0; @@ -358,7 +247,7 @@ static void stlink_confirm_inq(struct stlink *sl, struct sg_pt_base *ptvp) { } const int duration = get_scsi_pt_duration_ms(ptvp); if ((sl->verbose > 1) && (duration >= 0)) - fprintf(stderr, " duration=%d ms\n", duration); + 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" @@ -375,7 +264,7 @@ static void stlink_confirm_inq(struct stlink *sl, struct sg_pt_base *ptvp) { switch (cat) { case SCSI_PT_RESULT_GOOD: if (sl->verbose && (resid > 0)) - fprintf(stderr, " notice: requested %d bytes but " + DD(sl, " notice: requested %d bytes but " "got %d bytes, ignore [broken] residue = %d\n", sl->q_len, dsize, resid); break; @@ -384,7 +273,7 @@ static void stlink_confirm_inq(struct stlink *sl, struct sg_pt_base *ptvp) { sg_get_scsi_status_str( get_scsi_pt_status_response(ptvp), sizeof(buf), buf); - fprintf(stderr, " scsi status: %s\n", buf); + DD(sl, " scsi status: %s\n", buf); } return; case SCSI_PT_RESULT_SENSE: @@ -392,11 +281,11 @@ static void stlink_confirm_inq(struct stlink *sl, struct sg_pt_base *ptvp) { if (sl->verbose) { sg_get_sense_str("", sl->sense_buf, slen, (sl->verbose > 1), sizeof(buf), buf); - fprintf(stderr, "%s", buf); + DD(sl, "%s", buf); } if (sl->verbose && (resid > 0)) { if ((sl->verbose) || (sl->q_len > 0)) - fprintf(stderr, " requested %d bytes but " + DD(sl, " requested %d bytes but " "got %d bytes\n", sl->q_len, dsize); } return; @@ -409,13 +298,13 @@ static void stlink_confirm_inq(struct stlink *sl, struct sg_pt_base *ptvp) { // 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); + 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); - fprintf(stderr, " os: %s", buf); + DD(sl, " os: %s", buf); } return; default: @@ -425,10 +314,10 @@ static void stlink_confirm_inq(struct stlink *sl, struct sg_pt_base *ptvp) { } static void stlink_q(struct stlink* sl) { - fputs("CDB[", stderr); + DD(sl, "CDB["); for (int i = 0; i < CDB_SL; i++) - fprintf(stderr, " 0x%02x", (unsigned int) sl->cdb_cmd_blk[i]); - fputs("]\n", stderr); + 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!!) @@ -507,18 +396,18 @@ static void stlink_parse_version(struct stlink *sl) { if (sl->verbose < 2) return; - fprintf(stderr, "st vid = 0x%04x (expect 0x%04x)\n", + DD(sl, "st vid = 0x%04x (expect 0x%04x)\n", sl->st_vid, USB_ST_VID); - fprintf(stderr, "stlink pid = 0x%04x (expect 0x%04x)\n", + DD(sl, "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); + 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) - fprintf(stderr, + DD(sl, " notice: the firmware doesn't support a jtag/swd interface\n"); if (sl->swim_v == 0) - fprintf(stderr, + DD(sl, " notice: the firmware doesn't support a swim interface\n"); } @@ -531,13 +420,13 @@ static int stlink_mode(struct stlink *sl) { switch (sl->q_buf[0]) { case STLINK_DEV_DFU_MODE: - fprintf(stderr, "stlink mode: dfu\n"); + DD(sl, "stlink mode: dfu\n"); return STLINK_DEV_DFU_MODE; case STLINK_DEV_DEBUG_MODE: - fprintf(stderr, "stlink mode: debug (jtag or swd)\n"); + DD(sl, "stlink mode: debug (jtag or swd)\n"); return STLINK_DEV_DEBUG_MODE; case STLINK_DEV_MASS_MODE: - fprintf(stderr, "stlink mode: mass\n"); + DD(sl, "stlink mode: mass\n"); return STLINK_DEV_MASS_MODE; } return STLINK_DEV_UNKNOWN_MODE; @@ -551,13 +440,13 @@ static void stlink_stat(struct stlink *sl, char *txt) { switch (sl->q_buf[0]) { case STLINK_OK: - fprintf(stderr, " %s: ok\n", txt); + DD(sl, " %s: ok\n", txt); return; case STLINK_FALSE: - fprintf(stderr, " %s: false\n", txt); + DD(sl, " %s: false\n", txt); return; default: - fprintf(stderr, " %s: unknown\n", txt); + DD(sl, " %s: unknown\n", txt); } } @@ -568,13 +457,13 @@ static void stlink_core_stat(struct stlink *sl) { 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"); + 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; - fprintf(stderr, " core status: halted\n"); + DD(sl, " core status: halted\n"); return; default: sl->core_stat = STLINK_CORE_STAT_UNKNOWN; @@ -688,7 +577,7 @@ static void stlink_exit_dfu_mode(struct stlink *sl) { */ } -static void stlink_core_id(struct stlink *sl) { +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; @@ -699,7 +588,7 @@ static void stlink_core_id(struct stlink *sl) { if (sl->verbose < 2) return; stlink_print_data(sl); - fprintf(stderr, "core_id = 0x%08x\n", sl->core_id); + DD(sl, "core_id = 0x%08x\n", sl->core_id); } // Arm-core reset -> halted state. @@ -750,7 +639,7 @@ void stlink_read_all_regs(struct stlink *sl) { 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]); + 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); @@ -760,11 +649,11 @@ void stlink_read_all_regs(struct stlink *sl) { 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); + 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. @@ -772,7 +661,7 @@ void stlink_read_all_regs(struct stlink *sl) { // 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); + DD(sl, " (%d) ***\n", r_idx); if (r_idx > 20 || r_idx < 0) { fprintf(stderr, "Error: register index must be in [0..20]\n"); @@ -790,7 +679,7 @@ void stlink_read_reg(struct stlink *sl, int r_idx) { stlink_print_data(sl); uint32_t r = read_uint32(sl->q_buf, 0); - fprintf(stderr, "r_idx (%2d) = 0x%08x\n", r_idx, r); + DD(sl, "r_idx (%2d) = 0x%08x\n", r_idx, r); switch (r_idx) { case 16: @@ -956,11 +845,6 @@ void stlink_write_mem8(struct stlink *sl, uint32_t addr, uint16_t len) { 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"); @@ -1158,7 +1042,7 @@ static void wait_flash_busy(struct stlink* sl) static inline unsigned int is_flash_eop(struct stlink* sl) { - return read_flash_sr(sl) & (1 << FLASH_SR_EOP); + return read_flash_sr(sl) & (1 << FLASH_SR_EOP); } static void __attribute__((unused)) clear_flash_sr_eop(struct stlink* sl) @@ -1379,7 +1263,7 @@ static int run_flash_loader /* run loader */ stlink_run(sl); - + while (is_core_halted(sl) == 0) ; @@ -1701,7 +1585,7 @@ static int stlink_fread // 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* 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); @@ -1728,10 +1612,10 @@ struct stlink* stlink_force_open(const char *dev_name, const int verbose) { // TODO go to mass? return sl; } - fprintf(stderr, "\n*** switch the stlink to mass mode ***\n"); + DD(sl, "\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"); + DD(sl, "\n*** reopen the stlink device ***\n"); delay(1000); stlink_close(sl); delay(5000); @@ -1761,6 +1645,7 @@ static void __attribute__((unused)) mark_buf(struct stlink *sl) { 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; @@ -1787,7 +1672,7 @@ int main(int argc, char *argv[]) { } fputs("*** stlink access test ***\n", stderr); - fprintf(stderr, "Using sg_lib %s : scsi_pt %s\n", sg_lib_version(), + 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); @@ -1807,16 +1692,16 @@ int main(int argc, char *argv[]) { #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)); + 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); - fprintf(stderr, "mpu type register: MPU_TYPER = got 0x%08x expect 0x0", read_uint32(sl->q_buf, 0)); + DD(sl, "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)); + DD(sl, "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)); + DD(sl, "GPIOC_ODR = 0x%08x", read_uint32(sl->q_buf, 0)); #endif #if 0 // happy new year 2011: let blink all the leds @@ -1829,7 +1714,7 @@ int main(int argc, char *argv[]) { #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); + 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); @@ -1840,7 +1725,7 @@ int main(int argc, char *argv[]) { 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)); */ + /* DD(sl, "GPIOC_ODR = 0x%08x", read_uint32(sl->q_buf, 0)); */ delay(100); clear_buf(sl); @@ -1962,3 +1847,4 @@ int main(int argc, char *argv[]) { //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 -- 2.30.2