From a71f48db1055dd7cbd73260e089a63e86afaebb8 Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Sun, 8 Jul 2012 21:04:30 -0400 Subject: [PATCH 1/3] Read Cortex M4F floating point registers Since the ST-LINK does not seem to support reading these registers, I have implemented functions that will manually request these registers and add them to the reg struct. As of now, these functions are just backend and are not integrated into anything, however I have verified that they work with the STM32F407 DISCOVERY board. --- src/stlink-common.c | 17 +++++++++++++++++ src/stlink-common.h | 6 ++++++ src/stlink-usb.c | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/src/stlink-common.c b/src/stlink-common.c index a4e2a53..359d39a 100644 --- a/src/stlink-common.c +++ b/src/stlink-common.c @@ -577,6 +577,11 @@ void stlink_read_all_regs(stlink_t *sl, reg *regp) { sl->backend->read_all_regs(sl, regp); } +void stlink_read_all_unsupported_regs(stlink_t *sl, reg *regp) { + DLOG("*** stlink_read_all_unsupported_regs ***\n"); + sl->backend->read_all_unsupported_regs(sl, regp); +} + void stlink_write_reg(stlink_t *sl, uint32_t reg, int idx) { DLOG("*** stlink_write_reg\n"); sl->backend->write_reg(sl, reg, idx); @@ -594,6 +599,18 @@ void stlink_read_reg(stlink_t *sl, int r_idx, reg *regp) { sl->backend->read_reg(sl, r_idx, regp); } +void stlink_read_unsupported_reg(stlink_t *sl, int r_idx, reg *regp) { + DLOG("*** stlink_read_unsupported_reg\n"); + DLOG(" (%d) ***\n", r_idx); + + if (r_idx != 0x21 || r_idx < 0x40 || r_idx > 0x5f) { + fprintf(stderr, "Error: register address must be in [0x21, 0x40..0x5f]\n"); + return; + } + + sl->backend->read_unsupported_reg(sl, r_idx, regp); +} + unsigned int is_core_halted(stlink_t *sl) { /* return non zero if core is halted */ stlink_status(sl); diff --git a/src/stlink-common.h b/src/stlink-common.h index ee59094..c982b3d 100644 --- a/src/stlink-common.h +++ b/src/stlink-common.h @@ -238,11 +238,13 @@ extern "C" { typedef struct { uint32_t r[16]; + uint32_t s[32]; uint32_t xpsr; uint32_t main_sp; uint32_t process_sp; uint32_t rw; uint32_t rw2; + uint32_t fpscr; } reg; typedef uint32_t stm32_addr_t; @@ -295,6 +297,8 @@ extern "C" { void (*write_mem8) (stlink_t *sl, uint32_t addr, uint16_t len); void (*read_all_regs) (stlink_t *sl, reg * regp); void (*read_reg) (stlink_t *sl, int r_idx, reg * regp); + void (*read_all_unsupported_regs) (stlink_t *sl, reg *regp); + void (*read_unsupported_reg) (stlink_t *sl, int r_idx, reg *regp); void (*write_reg) (stlink_t *sl, uint32_t reg, int idx); void (*step) (stlink_t * stl); int (*current_mode) (stlink_t * stl); @@ -360,7 +364,9 @@ extern "C" { void stlink_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len); void stlink_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len); void stlink_read_all_regs(stlink_t *sl, reg *regp); + void stlink_read_all_unsupported_regs(stlink_t *sl, reg *regp); void stlink_read_reg(stlink_t *sl, int r_idx, reg *regp); + void stlink_read_unsupported_reg(stlink_t *sl, int r_idx, reg *regp); void stlink_write_reg(stlink_t *sl, uint32_t reg, int idx); void stlink_step(stlink_t *sl); int stlink_current_mode(stlink_t *sl); diff --git a/src/stlink-usb.c b/src/stlink-usb.c index a25f7ec..397723c 100644 --- a/src/stlink-usb.c +++ b/src/stlink-usb.c @@ -576,6 +576,38 @@ void _stlink_usb_read_reg(stlink_t *sl, int r_idx, reg *regp) { } } +void _stlink_usb_read_unsupported_reg(stlink_t *sl, int r_idx, reg *regp) { + uint32_t r; + + sl->q_buf[0] = (unsigned char) r_idx; + for (int i = 1; i < 4; i++) { + sl->q_buf[i] = 0; + } + + _stlink_usb_write_mem32(sl, 0xE000EDF4, 4); + _stlink_usb_read_mem32(sl, 0xE000EDF8, 4); + + r = read_uint32(sl->q_buf, 0); + DLOG("r_idx (%2d) = 0x%08x\n", r_idx, r); + + switch (r_idx) { + case 0x21: + regp->fpscr = r; + break; + default: + regp->s[r_idx - 0x40] = r; + break; + } +} + +void _stlink_usb_read_all_unsupported_regs(stlink_t *sl, reg *regp) { + _stlink_usb_read_unsupported_reg(sl, 0x21, regp); + + for (int i = 0; i < 32; i++) { + _stlink_usb_read_unsupported_reg(sl, 0x40+i, regp); + } +} + void _stlink_usb_write_reg(stlink_t *sl, uint32_t reg, int idx) { struct stlink_libusb * const slu = sl->backend_data; unsigned char* const data = sl->q_buf; @@ -616,6 +648,8 @@ stlink_backend_t _stlink_usb_backend = { _stlink_usb_write_mem8, _stlink_usb_read_all_regs, _stlink_usb_read_reg, + _stlink_usb_read_all_unsupported_regs, + _stlink_usb_read_unsupported_reg, _stlink_usb_write_reg, _stlink_usb_step, _stlink_usb_current_mode, From b1e65ea367a450e8a37584cdee334562149b2a7c Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Sun, 8 Jul 2012 23:04:35 -0400 Subject: [PATCH 2/3] Extra registers integration with GDB The support for extra registers has been added to GDB. Now all core registers can be read from GDB. Write support has not yet been added. --- gdbserver/gdb-server.c | 99 +++++++++++++++++++++++++++++++++++++++++- src/stlink-common.c | 15 +++++-- src/stlink-common.h | 4 ++ src/stlink-usb.c | 7 +++ 4 files changed, 121 insertions(+), 4 deletions(-) diff --git a/gdbserver/gdb-server.c b/gdbserver/gdb-server.c index f60aadc..789b609 100644 --- a/gdbserver/gdb-server.c +++ b/gdbserver/gdb-server.c @@ -192,6 +192,71 @@ winsock_error: return 0; } +static const char* const target_description_F4 = + "" + "" + "" + " arm" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + static const char* const memory_map_template_F4 = "" "chip_id==STM32_CHIPID_F4) { + reply = strdup("PacketSize=3fff;qXfer:memory-map:read+;qXfer:features:read+"); + } + else { + reply = strdup("PacketSize=3fff;qXfer:memory-map:read+"); + } } else if(!strcmp(queryName, "Xfer")) { char *type, *op, *__s_addr, *s_length; char *tok = params; @@ -674,6 +744,9 @@ int serve(stlink_t *sl, int port) { if(!strcmp(type, "memory-map") && !strcmp(op, "read")) data = current_memory_map; + if(!strcmp(type, "features") && !strcmp(op, "read")) + data = target_description_F4; + if(data) { unsigned data_length = strlen(data); if(addr + length > data_length) @@ -881,6 +954,30 @@ int serve(stlink_t *sl, int port) { } else if(id == 0x19) { stlink_read_reg(sl, 16, ®p); myreg = htonl(regp.xpsr); + } else if(id == 0x1A) { + stlink_read_reg(sl, 17, ®p); + myreg = htonl(regp.main_sp); + } else if(id == 0x1B) { + stlink_read_reg(sl, 18, ®p); + myreg = htonl(regp.process_sp); + } else if(id == 0x1C) { + stlink_read_unsupported_reg(sl, id, ®p); + myreg = htonl(regp.control); + } else if(id == 0x1D) { + stlink_read_unsupported_reg(sl, id, ®p); + myreg = htonl(regp.faultmask); + } else if(id == 0x1E) { + stlink_read_unsupported_reg(sl, id, ®p); + myreg = htonl(regp.basepri); + } else if(id == 0x1F) { + stlink_read_unsupported_reg(sl, id, ®p); + myreg = htonl(regp.primask); + } else if(id >= 0x20 && id < 0x40) { + stlink_read_unsupported_reg(sl, id, ®p); + myreg = htonl(regp.s[id-0x20]); + } else if(id == 0x40) { + stlink_read_unsupported_reg(sl, id, ®p); + myreg = htonl(regp.fpscr); } else { reply = strdup("E00"); } diff --git a/src/stlink-common.c b/src/stlink-common.c index 359d39a..1e9c011 100644 --- a/src/stlink-common.c +++ b/src/stlink-common.c @@ -600,15 +600,24 @@ void stlink_read_reg(stlink_t *sl, int r_idx, reg *regp) { } void stlink_read_unsupported_reg(stlink_t *sl, int r_idx, reg *regp) { + int r_convert; + DLOG("*** stlink_read_unsupported_reg\n"); DLOG(" (%d) ***\n", r_idx); - if (r_idx != 0x21 || r_idx < 0x40 || r_idx > 0x5f) { - fprintf(stderr, "Error: register address must be in [0x21, 0x40..0x5f]\n"); + /* Convert to values used by DCRSR */ + if (r_idx >= 0x1C && r_idx <= 0x1F) { /* primask, basepri, faultmask, or control */ + r_convert = 0x14; + } else if (r_idx == 0x40) { /* FPSCR */ + r_convert = 0x21; + } else if (r_idx >= 0x20 && r_idx < 0x40) { + r_convert = 0x40 + (r_idx - 0x20); + } else { + fprintf(stderr, "Error: register address must be in [0x1C..0x40]\n"); return; } - sl->backend->read_unsupported_reg(sl, r_idx, regp); + sl->backend->read_unsupported_reg(sl, r_convert, regp); } unsigned int is_core_halted(stlink_t *sl) { diff --git a/src/stlink-common.h b/src/stlink-common.h index c982b3d..5a82e1f 100644 --- a/src/stlink-common.h +++ b/src/stlink-common.h @@ -244,6 +244,10 @@ extern "C" { uint32_t process_sp; uint32_t rw; uint32_t rw2; + uint8_t control; + uint8_t faultmask; + uint8_t basepri; + uint8_t primask; uint32_t fpscr; } reg; diff --git a/src/stlink-usb.c b/src/stlink-usb.c index 397723c..fdd636b 100644 --- a/src/stlink-usb.c +++ b/src/stlink-usb.c @@ -576,6 +576,7 @@ void _stlink_usb_read_reg(stlink_t *sl, int r_idx, reg *regp) { } } +/* See section C1.6 of the ARMv7-M Architecture Reference Manual */ void _stlink_usb_read_unsupported_reg(stlink_t *sl, int r_idx, reg *regp) { uint32_t r; @@ -591,6 +592,12 @@ void _stlink_usb_read_unsupported_reg(stlink_t *sl, int r_idx, reg *regp) { DLOG("r_idx (%2d) = 0x%08x\n", r_idx, r); switch (r_idx) { + case 0x14: + regp->primask = (uint8_t) (r & 0xFF); + regp->basepri = (uint8_t) ((r>>8) & 0xFF); + regp->faultmask = (uint8_t) ((r>>16) & 0xFF); + regp->control = (uint8_t) ((r>>24) & 0xFF); + break; case 0x21: regp->fpscr = r; break; From c6274f026b7848cf45029eccc327f8215738a743 Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Mon, 9 Jul 2012 01:00:53 -0400 Subject: [PATCH 3/3] Write to extra (FP, etc) registers from GDB The extra registers added in my previous commit can now be modified from within GDB. Since the ST-LINK does not support accessing these registers, a workaround was used from reading an writing to them. That is, the Debug Core Register Selector Register (DCRSR) can be written with the register requested, and it will be read/written to/from the Debug Core Register Data Register (DCRDR). The standard ST-LINK memory access functions are used to make these accesses. A target descriptor XML file is sent to GDB from the server, which tells GDB which registers exist on the target. This is only supported for the STM32F4, and has only been tested on the STM32F4DISCOVERY. I tested st-util on an STM32L-DISCOVERY and my changes did not seem to interfere with its operation. --- AUTHORS | 3 ++- gdbserver/gdb-server.c | 16 +++++++++++++++ src/stlink-common.c | 21 ++++++++++++++++++++ src/stlink-common.h | 4 ++++ src/stlink-usb.c | 44 ++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 85 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index 1096c17..5c52c64 100644 --- a/AUTHORS +++ b/AUTHORS @@ -14,4 +14,5 @@ bravikov@gmail.com jnosky - codegrinder69@hotmail.com marpe@mimuw.edu.pl marco.cassinerio@gmail.com -jserv@0xlab.org \ No newline at end of file +jserv@0xlab.org +michael@pratt.im diff --git a/gdbserver/gdb-server.c b/gdbserver/gdb-server.c index 789b609..ae06952 100644 --- a/gdbserver/gdb-server.c +++ b/gdbserver/gdb-server.c @@ -999,6 +999,22 @@ int serve(stlink_t *sl, int port) { stlink_write_reg(sl, ntohl(value), reg); } else if(reg == 0x19) { stlink_write_reg(sl, ntohl(value), 16); + } else if(reg == 0x1A) { + stlink_write_reg(sl, ntohl(value), 17); + } else if(reg == 0x1B) { + stlink_write_reg(sl, ntohl(value), 18); + } else if(reg == 0x1C) { + stlink_write_unsupported_reg(sl, ntohl(value), reg, ®p); + } else if(reg == 0x1D) { + stlink_write_unsupported_reg(sl, ntohl(value), reg, ®p); + } else if(reg == 0x1E) { + stlink_write_unsupported_reg(sl, ntohl(value), reg, ®p); + } else if(reg == 0x1F) { + stlink_write_unsupported_reg(sl, ntohl(value), reg, ®p); + } else if(reg >= 0x20 && reg < 0x40) { + stlink_write_unsupported_reg(sl, ntohl(value), reg, ®p); + } else if(reg == 0x40) { + stlink_write_unsupported_reg(sl, ntohl(value), reg, ®p); } else { reply = strdup("E00"); } diff --git a/src/stlink-common.c b/src/stlink-common.c index 1e9c011..a86d985 100644 --- a/src/stlink-common.c +++ b/src/stlink-common.c @@ -620,6 +620,27 @@ void stlink_read_unsupported_reg(stlink_t *sl, int r_idx, reg *regp) { sl->backend->read_unsupported_reg(sl, r_convert, regp); } +void stlink_write_unsupported_reg(stlink_t *sl, uint32_t val, int r_idx, reg *regp) { + int r_convert; + + DLOG("*** stlink_write_unsupported_reg\n"); + DLOG(" (%d) ***\n", r_idx); + + /* Convert to values used by DCRSR */ + if (r_idx >= 0x1C && r_idx <= 0x1F) { /* primask, basepri, faultmask, or control */ + r_convert = r_idx; /* The backend function handles this */ + } else if (r_idx == 0x40) { /* FPSCR */ + r_convert = 0x21; + } else if (r_idx >= 0x20 && r_idx < 0x40) { + r_convert = 0x40 + (r_idx - 0x20); + } else { + fprintf(stderr, "Error: register address must be in [0x1C..0x40]\n"); + return; + } + + sl->backend->write_unsupported_reg(sl, val, r_convert, regp); +} + unsigned int is_core_halted(stlink_t *sl) { /* return non zero if core is halted */ stlink_status(sl); diff --git a/src/stlink-common.h b/src/stlink-common.h index 5a82e1f..2e30e71 100644 --- a/src/stlink-common.h +++ b/src/stlink-common.h @@ -112,6 +112,8 @@ extern "C" { /* Cortex™-M3 Technical Reference Manual */ /* Debug Halting Control and Status Register */ #define DHCSR 0xe000edf0 +#define DCRSR 0xe000edf4 +#define DCRDR 0xe000edf8 #define DBGKEY 0xa05f0000 /* Enough space to hold both a V2 command or a V1 command packaged as generic scsi*/ @@ -303,6 +305,7 @@ extern "C" { void (*read_reg) (stlink_t *sl, int r_idx, reg * regp); void (*read_all_unsupported_regs) (stlink_t *sl, reg *regp); void (*read_unsupported_reg) (stlink_t *sl, int r_idx, reg *regp); + void (*write_unsupported_reg) (stlink_t *sl, uint32_t value, int idx, reg *regp); void (*write_reg) (stlink_t *sl, uint32_t reg, int idx); void (*step) (stlink_t * stl); int (*current_mode) (stlink_t * stl); @@ -371,6 +374,7 @@ extern "C" { void stlink_read_all_unsupported_regs(stlink_t *sl, reg *regp); void stlink_read_reg(stlink_t *sl, int r_idx, reg *regp); void stlink_read_unsupported_reg(stlink_t *sl, int r_idx, reg *regp); + void stlink_write_unsupported_reg(stlink_t *sl, uint32_t value, int r_idx, reg *regp); void stlink_write_reg(stlink_t *sl, uint32_t reg, int idx); void stlink_step(stlink_t *sl); int stlink_current_mode(stlink_t *sl); diff --git a/src/stlink-usb.c b/src/stlink-usb.c index fdd636b..277431a 100644 --- a/src/stlink-usb.c +++ b/src/stlink-usb.c @@ -585,8 +585,8 @@ void _stlink_usb_read_unsupported_reg(stlink_t *sl, int r_idx, reg *regp) { sl->q_buf[i] = 0; } - _stlink_usb_write_mem32(sl, 0xE000EDF4, 4); - _stlink_usb_read_mem32(sl, 0xE000EDF8, 4); + _stlink_usb_write_mem32(sl, DCRSR, 4); + _stlink_usb_read_mem32(sl, DCRDR, 4); r = read_uint32(sl->q_buf, 0); DLOG("r_idx (%2d) = 0x%08x\n", r_idx, r); @@ -608,6 +608,7 @@ void _stlink_usb_read_unsupported_reg(stlink_t *sl, int r_idx, reg *regp) { } void _stlink_usb_read_all_unsupported_regs(stlink_t *sl, reg *regp) { + _stlink_usb_read_unsupported_reg(sl, 0x14, regp); _stlink_usb_read_unsupported_reg(sl, 0x21, regp); for (int i = 0; i < 32; i++) { @@ -615,6 +616,44 @@ void _stlink_usb_read_all_unsupported_regs(stlink_t *sl, reg *regp) { } } +/* See section C1.6 of the ARMv7-M Architecture Reference Manual */ +void _stlink_usb_write_unsupported_reg(stlink_t *sl, uint32_t val, int r_idx, reg *regp) { + if (r_idx >= 0x1C && r_idx <= 0x1F) { /* primask, basepri, faultmask, or control */ + /* These are held in the same register */ + _stlink_usb_read_unsupported_reg(sl, 0x14, regp); + + val = (uint8_t) (val>>24); + + switch (r_idx) { + case 0x1C: /* control */ + val = (((uint32_t) val) << 24) | (((uint32_t) regp->faultmask) << 16) | (((uint32_t) regp->basepri) << 8) | ((uint32_t) regp->primask); + break; + case 0x1D: /* faultmask */ + val = (((uint32_t) regp->control) << 24) | (((uint32_t) val) << 16) | (((uint32_t) regp->basepri) << 8) | ((uint32_t) regp->primask); + break; + case 0x1E: /* basepri */ + val = (((uint32_t) regp->control) << 24) | (((uint32_t) regp->faultmask) << 16) | (((uint32_t) val) << 8) | ((uint32_t) regp->primask); + break; + case 0x1F: /* primask */ + val = (((uint32_t) regp->control) << 24) | (((uint32_t) regp->faultmask) << 16) | (((uint32_t) regp->basepri) << 8) | ((uint32_t) val); + break; + } + + r_idx = 0x14; + } + + write_uint32(sl->q_buf, val); + + _stlink_usb_write_mem32(sl, DCRDR, 4); + + sl->q_buf[0] = (unsigned char) r_idx; + sl->q_buf[1] = 0; + sl->q_buf[2] = 0x01; + sl->q_buf[3] = 0; + + _stlink_usb_write_mem32(sl, DCRSR, 4); +} + void _stlink_usb_write_reg(stlink_t *sl, uint32_t reg, int idx) { struct stlink_libusb * const slu = sl->backend_data; unsigned char* const data = sl->q_buf; @@ -657,6 +696,7 @@ stlink_backend_t _stlink_usb_backend = { _stlink_usb_read_reg, _stlink_usb_read_all_unsupported_regs, _stlink_usb_read_unsupported_reg, + _stlink_usb_write_unsupported_reg, _stlink_usb_write_reg, _stlink_usb_step, _stlink_usb_current_mode,