Basic semihosting support
Implement bare minimum necessary to support console IO. This works with standard newlib builds and is based on the reference documentation: http://infocenter.arm.com/help/topic/com.arm.doc.dui0471c/CHDJHHDI.html Tested using gcc-arm-none-eabi-4_7-2013q1-20130313 for both stm32f1-based BMP and stm32f0 target. Signed-off-by: Paul Fertser <fercerpav@gmail.com>
This commit is contained in:
parent
feaf626673
commit
aca421d0bb
12
README
12
README
|
@ -45,6 +45,18 @@ your '.gdbinit'. That will allow you to access addresses outside of
|
||||||
the memory map. It will treat anything outside of the memory map as
|
the memory map. It will treat anything outside of the memory map as
|
||||||
RAM.
|
RAM.
|
||||||
|
|
||||||
|
Semihosting support
|
||||||
|
===================
|
||||||
|
Basic armv6m/armv7m semihosting is supported, i.e. you can build your
|
||||||
|
application in a special way to use host's stdin and stdout right
|
||||||
|
inside gdb simply by calling printf(), scanf() and other input-output
|
||||||
|
functions. To make use of it, add --specs=rdimon.specs and -lrdimon to
|
||||||
|
the linker flags for your firmware.
|
||||||
|
|
||||||
|
All writes are treated as writes to the stdout, all reads are done
|
||||||
|
from stdin; SYS_ISTTY always returns true, SYS_ERRNO -- EINTR;
|
||||||
|
SYS_OPEN and SYS_FLEN return dummy non-zero values.
|
||||||
|
|
||||||
Project layout
|
Project layout
|
||||||
==============
|
==============
|
||||||
driver/ - Windows drivers for the Black Magic probe hardware.
|
driver/ - Windows drivers for the Black Magic probe hardware.
|
||||||
|
|
|
@ -75,6 +75,8 @@ gdb_main(void)
|
||||||
{
|
{
|
||||||
int size;
|
int size;
|
||||||
bool single_step = false;
|
bool single_step = false;
|
||||||
|
char last_activity = 0;
|
||||||
|
uint32_t semihost_read_bytes = 0;
|
||||||
|
|
||||||
DEBUG("Entring GDB protocol main loop\n");
|
DEBUG("Entring GDB protocol main loop\n");
|
||||||
/* GDB protocol main loop */
|
/* GDB protocol main loop */
|
||||||
|
@ -82,6 +84,7 @@ gdb_main(void)
|
||||||
SET_IDLE_STATE(1);
|
SET_IDLE_STATE(1);
|
||||||
size = gdb_getpacket(pbuf, BUF_SIZE);
|
size = gdb_getpacket(pbuf, BUF_SIZE);
|
||||||
SET_IDLE_STATE(0);
|
SET_IDLE_STATE(0);
|
||||||
|
continue_activity:
|
||||||
switch(pbuf[0]) {
|
switch(pbuf[0]) {
|
||||||
/* Implementation of these is mandatory! */
|
/* Implementation of these is mandatory! */
|
||||||
case 'g': { /* 'g': Read general registers */
|
case 'g': { /* 'g': Read general registers */
|
||||||
|
@ -163,9 +166,48 @@ gdb_main(void)
|
||||||
unsigned char c = gdb_if_getchar_to(0);
|
unsigned char c = gdb_if_getchar_to(0);
|
||||||
if((c == '\x03') || (c == '\x04')) {
|
if((c == '\x03') || (c == '\x04')) {
|
||||||
target_halt_request(cur_target);
|
target_halt_request(cur_target);
|
||||||
|
last_activity = 's';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SET_RUN_STATE(0);
|
SET_RUN_STATE(0);
|
||||||
|
|
||||||
|
uint32_t arm_regs[cur_target->regs_size];
|
||||||
|
uint32_t semihost_buf[4];
|
||||||
|
target_regs_read(cur_target, arm_regs);
|
||||||
|
target_mem_read_bytes(cur_target, (uint8_t *)semihost_buf, arm_regs[15], 2);
|
||||||
|
/* Is this a semihosting breakpoint? */
|
||||||
|
if ((semihost_buf[0] & 0xFFFF) == 0xBEAB) {
|
||||||
|
last_activity = pbuf[0];
|
||||||
|
semihost_read_bytes = 0;
|
||||||
|
target_mem_read_words(cur_target, semihost_buf, arm_regs[1], sizeof(semihost_buf));
|
||||||
|
|
||||||
|
switch (arm_regs[0]) {
|
||||||
|
case 0x09: /* SYS_ISTTY */
|
||||||
|
arm_regs[0] = 1; /* it's a tty */
|
||||||
|
target_regs_write(cur_target, arm_regs);
|
||||||
|
/* fall-through */
|
||||||
|
case 0x01: /* SYS_OPEN */
|
||||||
|
case 0x0C: /* SYS_FLEN */
|
||||||
|
/* pretend it's successful, r0 is non-zero already */
|
||||||
|
goto continue_activity;
|
||||||
|
case 0x13: /* SYS_ERRNO */
|
||||||
|
arm_regs[0] = 4; /* EINTR */
|
||||||
|
target_regs_write(cur_target, arm_regs);
|
||||||
|
goto continue_activity;
|
||||||
|
case 0x05: /* SYS_WRITE */
|
||||||
|
gdb_putpacket_f("Fwrite,1,%08X,%08X",
|
||||||
|
semihost_buf[1], semihost_buf[2]);
|
||||||
|
arm_regs[0] = 0; /* pretend it's always successful */
|
||||||
|
target_regs_write(cur_target, arm_regs);
|
||||||
|
continue;
|
||||||
|
case 0x06: /* SYS_READ */
|
||||||
|
gdb_putpacket_f("Fread,0,%08X,%08X",
|
||||||
|
semihost_buf[1], semihost_buf[2]);
|
||||||
|
semihost_read_bytes = semihost_buf[2];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Report reason for halt */
|
/* Report reason for halt */
|
||||||
if(target_check_hw_wp(cur_target, &watch_addr)) {
|
if(target_check_hw_wp(cur_target, &watch_addr)) {
|
||||||
/* Watchpoint hit */
|
/* Watchpoint hit */
|
||||||
|
@ -175,6 +217,34 @@ gdb_main(void)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'F': { /* Semihosting call finished */
|
||||||
|
int bytes, errcode, items;
|
||||||
|
char c, *p;
|
||||||
|
if (pbuf[1] == '-')
|
||||||
|
p = &pbuf[2];
|
||||||
|
else
|
||||||
|
p = &pbuf[1];
|
||||||
|
items = sscanf(p, "%x,%x,%c", &bytes, &errcode, &c);
|
||||||
|
|
||||||
|
if (semihost_read_bytes) {
|
||||||
|
uint32_t arm_regs[cur_target->regs_size];
|
||||||
|
target_regs_read(cur_target, arm_regs);
|
||||||
|
if (items == 3 && c == 'C')
|
||||||
|
arm_regs[0] = -1;
|
||||||
|
else
|
||||||
|
arm_regs[0] = semihost_read_bytes - bytes;
|
||||||
|
target_regs_write(cur_target, arm_regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if break is requested */
|
||||||
|
if (items == 3 && c == 'C') {
|
||||||
|
gdb_putpacketz("T02");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pbuf[0] = last_activity;
|
||||||
|
goto continue_activity;
|
||||||
|
}
|
||||||
|
|
||||||
/* Optional GDB packet support */
|
/* Optional GDB packet support */
|
||||||
case '!': /* Enable Extended GDB Protocol. */
|
case '!': /* Enable Extended GDB Protocol. */
|
||||||
|
|
Loading…
Reference in New Issue