Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pc-hosted semihosting #665

Merged
merged 1 commit into from
May 27, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
268 changes: 257 additions & 11 deletions src/target/cortexm.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
*
* Also supports Cortex-M0 / ARMv6-M
*/

#include "general.h"
#include "exception.h"
#include "adiv5.h"
Expand All @@ -37,6 +38,23 @@

#include <unistd.h>

#ifdef PC_HOSTED

/*
* pc-hosted semihosting does keyboard, file and screen i/o on the system
* where blackmagic_hosted runs, using linux system calls.
* semihosting in the probe does keyboard, file and screen i/o on the system
* where gdb runs, using gdb file i/o calls.
*/

#define TARGET_NULL ((target_addr)0)
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif

static const char cortexm_driver_str[] = "ARM Cortex-M";

static bool cortexm_vector_catch(target *t, int argc, char *argv[]);
Expand Down Expand Up @@ -71,9 +89,7 @@ static target_addr cortexm_check_watch(target *t);

static int cortexm_hostio_request(target *t);

#if !defined(PC_HOSTED)
static uint32_t time0_sec = UINT32_MAX; /* sys_clock time origin */
#endif

struct cortexm_priv {
ADIv5_AP_t *ap;
Expand Down Expand Up @@ -1013,6 +1029,17 @@ static bool cortexm_vector_catch(target *t, int argc, char *argv[])
#endif

/* Semihosting support */

/*
* If the target wants to read the special filename ":semihosting-features"
* to know what semihosting features are supported, it's easiest to create
* that file on the host in the directory where gdb runs,
* or, if using pc-hosted, where blackmagic_hosted runs.
*
* $ echo -e 'SHFB\x03' > ":semihosting-features"
* $ chmod 0444 ":semihosting-features"
*/

/* ARM Semihosting syscall numbers, from "Semihosting for AArch32 and AArch64 Version 3.0" */

#define SYS_CLOCK 0x10
Expand Down Expand Up @@ -1062,6 +1089,7 @@ static void probe_mem_write(target *t __attribute__((unused)), target_addr targe
return;
}
#endif

static int cortexm_hostio_request(target *t)
{
uint32_t arm_regs[t->regs_size];
Expand All @@ -1076,6 +1104,227 @@ static int cortexm_hostio_request(target *t)
DEBUG("syscall 0"PRIx32"%"PRIx32" (%"PRIx32" %"PRIx32" %"PRIx32" %"PRIx32")\n",
syscall, params[0], params[1], params[2], params[3]);
switch (syscall) {
#if defined(PC_HOSTED)

/* code that runs in pc-hosted process. use linux system calls. */

case SYS_OPEN:{ /* open */
target_addr fnam_taddr = params[0];
uint32_t fnam_len = params[2];
ret = -1;
if ((fnam_taddr == TARGET_NULL) || (fnam_len == 0)) break;

/* Translate stupid fopen modes to open flags.
* See DUI0471C, Table 8-3 */
const uint32_t flags[] = {
O_RDONLY, /* r, rb */
O_RDWR, /* r+, r+b */
O_WRONLY | O_CREAT | O_TRUNC,/*w*/
O_RDWR | O_CREAT | O_TRUNC,/*w+*/
O_WRONLY | O_CREAT | O_APPEND,/*a*/
O_RDWR | O_CREAT | O_APPEND,/*a+*/
};
uint32_t pflag = flags[params[1] >> 1];
char filename[4];

target_mem_read(t, filename, fnam_taddr, sizeof(filename));
/* handle requests for console i/o */
if (!strcmp(filename, ":tt")) {
if (pflag == TARGET_O_RDONLY)
ret = STDIN_FILENO;
else if (pflag & TARGET_O_TRUNC)
ret = STDOUT_FILENO;
else
ret = STDERR_FILENO;
ret++;
break;
}

char *fnam = malloc(fnam_len + 1);
if (fnam == NULL) break;
target_mem_read(t, fnam, fnam_taddr, fnam_len + 1);
if (target_check_error(t)) {free(fnam); break;}
fnam[fnam_len]='\0';
ret = open(fnam, pflag, 0644);
free(fnam);
if (ret != -1)
ret++;
break;
}

case SYS_CLOSE: /* close */
ret = close(params[0] - 1);
break;

case SYS_READ: { /* read */
ret = -1;
target_addr buf_taddr = params[1];
uint32_t buf_len = params[2];
if (buf_taddr == TARGET_NULL) break;
if (buf_len == 0) {ret = 0; break;}
uint8_t *buf = malloc(buf_len);
if (buf == NULL) break;
ssize_t rc = read(params[0] - 1, buf, buf_len);
if (rc >= 0)
rc = buf_len - rc;
target_mem_write(t, buf_taddr, buf, buf_len);
free(buf);
if (target_check_error(t)) break;
ret = rc;
break;
}

case SYS_WRITE: { /* write */
ret = -1;
target_addr buf_taddr = params[1];
uint32_t buf_len = params[2];
if (buf_taddr == TARGET_NULL) break;
if (buf_len == 0) {ret = 0; break;}
uint8_t *buf = malloc(buf_len);
if (buf == NULL) break;
target_mem_read(t, buf, buf_taddr, buf_len);
if (target_check_error(t)) {free(buf); break;}
ret = write(params[0] - 1, buf, buf_len);
free(buf);
if (ret >= 0)
ret = buf_len - ret;
break;
}

case SYS_WRITEC: { /* writec */
ret = -1;
uint8_t ch;
target_addr ch_taddr = arm_regs[1];
if (ch_taddr == TARGET_NULL) break;
ch = target_mem_read8(t, ch_taddr);
if (target_check_error(t)) break;
fputc(ch, stderr);
ret = 0;
break;
}

case SYS_WRITE0:{ /* write0 */
ret = -1;
uint8_t ch;
target_addr str = arm_regs[1];
if (str == TARGET_NULL) break;
while ((ch = target_mem_read8(t, str++)) != '\0') {
if (target_check_error(t)) break;
fputc(ch, stderr);
}
ret = 0;
break;
}

case SYS_ISTTY: /* isatty */
ret = isatty(params[0] - 1);
break;

case SYS_SEEK:{ /* lseek */
off_t pos = params[1];
if (lseek(params[0] - 1, pos, SEEK_SET) == (off_t)pos) ret = 0;
else ret = -1;
break;
}

case SYS_RENAME: { /* rename */
ret = -1;
target_addr fnam1_taddr = params[0];
uint32_t fnam1_len = params[1];
if (fnam1_taddr == TARGET_NULL) break;
if (fnam1_len == 0) break;
target_addr fnam2_taddr = params[2];
uint32_t fnam2_len = params[3];
if (fnam2_taddr == TARGET_NULL) break;
if (fnam2_len == 0) break;
char *fnam1 = malloc(fnam1_len + 1);
if (fnam1 == NULL) break;
target_mem_read(t, fnam1, fnam1_taddr, fnam1_len + 1);
if (target_check_error(t)) {free(fnam1); break;}
fnam1[fnam1_len]='\0';
char *fnam2 = malloc(fnam2_len + 1);
if (fnam2 == NULL) {free(fnam1); break;}
target_mem_read(t, fnam2, fnam2_taddr, fnam2_len + 1);
if (target_check_error(t)) {free(fnam1); free(fnam2); break;}
fnam2[fnam2_len]='\0';
ret = rename(fnam1, fnam2);
free(fnam1);
free(fnam2);
break;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these error handling conditions after successful malloc's fail to free the buffers before breaking. It looks like this is also an issue in some of the other commands

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right.

}

case SYS_REMOVE: { /* unlink */
ret = -1;
target_addr fnam_taddr = params[0];
if (fnam_taddr == TARGET_NULL) break;
uint32_t fnam_len = params[1];
if (fnam_len == 0) break;
char *fnam = malloc(fnam_len + 1);
if (fnam == NULL) break;
target_mem_read(t, fnam, fnam_taddr, fnam_len + 1);
if (target_check_error(t)) {free(fnam); break;}
fnam[fnam_len]='\0';
ret = remove(fnam);
free(fnam);
break;
}

case SYS_SYSTEM: { /* system */
ret = -1;
target_addr cmd_taddr = params[0];
if (cmd_taddr == TARGET_NULL) break;
uint32_t cmd_len = params[1];
if (cmd_len == 0) break;
char *cmd = malloc(cmd_len + 1);
if (cmd == NULL) break;
target_mem_read(t, cmd, cmd_taddr, cmd_len + 1);
if (target_check_error(t)) {free(cmd); break;}
cmd[cmd_len]='\0';
ret = system(cmd);
free(cmd);
break;
}

case SYS_FLEN: { /* file length */
ret = -1;
struct stat stat_buf;
if (fstat(params[0]-1, &stat_buf) != 0) break;
if (stat_buf.st_size > INT32_MAX) break;
ret = stat_buf.st_size;
break;
}

case SYS_CLOCK: { /* clock */
/* can't use clock() because that would give cpu time of pc-hosted process */
ret = -1;
struct timeval timeval_buf;
if(gettimeofday(&timeval_buf, NULL) != 0) break;
uint32_t sec = timeval_buf.tv_sec;
uint64_t usec = timeval_buf.tv_usec;
if (time0_sec > sec) time0_sec = sec;
sec -= time0_sec;
uint64_t csec64 = (sec * 1000000ull + usec)/10000ull;
uint32_t csec = csec64 & 0x7fffffff;
ret = csec;
break;
}

case SYS_TIME: /* time */
ret = time(NULL);
break;

case SYS_READC: /* readc */
ret = getchar();
break;

case SYS_ERRNO: /* errno */
ret = errno;
break;

#else

/* code that runs in probe. use gdb fileio calls. */

case SYS_OPEN:{ /* open */
/* Translate stupid fopen modes to open flags.
* See DUI0471C, Table 8-3 */
Expand All @@ -1102,13 +1351,13 @@ static int cortexm_hostio_request(target *t)
ret++;
break;
}
/* FIXME handle requests for special filename ':semihosting-features' */

ret = tc_open(t, params[0], params[2] + 1, pflag, 0644);
if (ret != -1)
ret++;
break;
}

case SYS_CLOSE: /* close */
ret = tc_close(t, params[0] - 1);
break;
Expand Down Expand Up @@ -1161,10 +1410,6 @@ static int cortexm_hostio_request(target *t)
break;

case SYS_FLEN:
#if defined(PC_HOSTED)
t->tc->errno_ = 0;
break;
#else
{ /* file length */
ret = -1;
uint32_t fio_stat[16]; /* same size as fio_stat in gdb/include/gdb/fileio.h */
Expand All @@ -1181,8 +1426,9 @@ static int cortexm_hostio_request(target *t)
if (rc) break; /* tc_fstat() failed */
uint32_t fst_size_msw = fio_stat[7]; /* most significant 32 bits of fst_size in fio_stat */
uint32_t fst_size_lsw = fio_stat[8]; /* least significant 32 bits of fst_size in fio_stat */
if (fst_size_msw != 0) break; /* file size too large for uint32_t return type */
if (fst_size_msw != 0) break; /* file size too large for int32_t return type */
ret = __builtin_bswap32(fst_size_lsw); /* convert from bigendian to target order */
if (ret < 0) ret = -1; /* file size too large for int32_t return type */
break;
}

Expand Down Expand Up @@ -1235,10 +1481,11 @@ static int cortexm_hostio_request(target *t)
else ret = -1;
break;
}
#endif

case SYS_ERRNO: /* Return last errno from GDB */
ret = t->tc->errno_;
break;
#endif

case SYS_EXIT: /* _exit() */
tc_printf(t, "_exit(0x%x)\n", params[0]);
Expand Down Expand Up @@ -1296,8 +1543,7 @@ static int cortexm_hostio_request(target *t)
break;

case SYS_TMPNAM: { /* tmpnam */
/* Given a target identifier between 0 and 255, returns a temporary name.
* FIXME: add directory prefix */
/* Given a target identifier between 0 and 255, returns a temporary name */
target_addr buf_ptr = params[0];
int target_id = params[1];
int buf_size = params[2];
Expand Down