Skip to content

Commit

Permalink
semihosting: split console_out into string and char versions
Browse files Browse the repository at this point in the history
This is ostensibly to avoid the weirdness of len looking like it might
come from a guest and sometimes being used. While we are at it fix up
the error checking for the arm-linux-user implementation of the API
which got flagged up by Coverity (CID 1401700).

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
  • Loading branch information
stsquad committed Jun 12, 2019
1 parent 3ace9be commit 78e2484
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 20 deletions.
34 changes: 25 additions & 9 deletions hw/semihosting/console.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,26 +36,24 @@ int qemu_semihosting_log_out(const char *s, int len)
/*
* A re-implementation of lock_user_string that we can use locally
* instead of relying on softmmu-semi. Hopefully we can deprecate that
* in time. We either copy len bytes if specified or until we find a NULL.
* in time. Copy string until we find a 0 or address error.
*/
static GString *copy_user_string(CPUArchState *env, target_ulong addr, int len)
static GString *copy_user_string(CPUArchState *env, target_ulong addr)
{
CPUState *cpu = env_cpu(env);
GString *s = g_string_sized_new(len ? len : 128);
GString *s = g_string_sized_new(128);
uint8_t c;
bool done;

do {
if (cpu_memory_rw_debug(cpu, addr++, &c, 1, 0) == 0) {
s = g_string_append_c(s, c);
done = len ? s->len == len : c == 0;
} else {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: passed inaccessible address " TARGET_FMT_lx,
__func__, addr);
done = true;
break;
}
} while (!done);
} while (c!=0);

return s;
}
Expand All @@ -68,9 +66,9 @@ static void semihosting_cb(CPUState *cs, target_ulong ret, target_ulong err)
}
}

int qemu_semihosting_console_out(CPUArchState *env, target_ulong addr, int len)
int qemu_semihosting_console_outs(CPUArchState *env, target_ulong addr)
{
GString *s = copy_user_string(env, addr, len);
GString *s = copy_user_string(env, addr);
int out = s->len;

if (use_gdb_syscalls()) {
Expand All @@ -82,3 +80,21 @@ int qemu_semihosting_console_out(CPUArchState *env, target_ulong addr, int len)
g_string_free(s, true);
return out;
}

void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr)
{
CPUState *cpu = env_cpu(env);
uint8_t c;

if (cpu_memory_rw_debug(cpu, addr, &c, 1, 0) == 0) {
if (use_gdb_syscalls()) {
gdb_do_syscall(semihosting_cb, "write,2,%x,%x", addr, 1);
} else {
qemu_semihosting_log_out((const char *) &c, 1);
}
} else {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: passed inaccessible address " TARGET_FMT_lx,
__func__, addr);
}
}
25 changes: 19 additions & 6 deletions include/hw/semihosting/console.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,30 @@
#define SEMIHOST_CONSOLE_H

/**
* qemu_semihosting_console_out:
* qemu_semihosting_console_outs:
* @env: CPUArchState
* @s: host address of guest string
* @len: length of string or 0 (string is null terminated)
* @s: host address of null terminated guest string
*
* Send a guest string to the debug console. This may be the remote
* gdb session if a softmmu guest is currently being debugged.
* Send a null terminated guest string to the debug console. This may
* be the remote gdb session if a softmmu guest is currently being
* debugged.
*
* Returns: number of bytes written.
*/
int qemu_semihosting_console_out(CPUArchState *env, target_ulong s, int len);
int qemu_semihosting_console_outs(CPUArchState *env, target_ulong s);

/**
* qemu_semihosting_console_outc:
* @env: CPUArchState
* @s: host address of null terminated guest string
*
* Send single character from guest memory to the debug console. This
* may be the remote gdb session if a softmmu guest is currently being
* debugged.
*
* Returns: nothing
*/
void qemu_semihosting_console_outc(CPUArchState *env, target_ulong c);

/**
* qemu_semihosting_log_out:
Expand Down
31 changes: 28 additions & 3 deletions linux-user/arm/semihost.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,35 @@
#include "hw/semihosting/console.h"
#include "qemu.h"

int qemu_semihosting_console_out(CPUArchState *env, target_ulong addr, int len)
int qemu_semihosting_console_outs(CPUArchState *env, target_ulong addr)
{
void *s = lock_user_string(addr);
len = write(STDERR_FILENO, s, len ? len : strlen(s));
int len = target_strlen(addr);
void *s;
if (len < 0){
qemu_log_mask(LOG_GUEST_ERROR,
"%s: passed inaccessible address " TARGET_FMT_lx,
__func__, addr);
return 0;
}
s = lock_user(VERIFY_READ, addr, (long)(len + 1), 1);
g_assert(s); /* target_strlen has already verified this will work */
len = write(STDERR_FILENO, s, len);
unlock_user(s, addr, 0);
return len;
}

void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr)
{
char c;

if (get_user_u8(c, addr)) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: passed inaccessible address " TARGET_FMT_lx,
__func__, addr);
} else {
if (write(STDERR_FILENO, &c, 1) != 1) {
qemu_log_mask(LOG_UNIMP, "%s: unexpected write to stdout failure",
__func__);
}
}
}
4 changes: 2 additions & 2 deletions target/arm/arm-semi.c
Original file line number Diff line number Diff line change
Expand Up @@ -314,10 +314,10 @@ target_ulong do_arm_semihosting(CPUARMState *env)
return set_swi_errno(ts, close(arg0));
}
case TARGET_SYS_WRITEC:
qemu_semihosting_console_out(env, args, 1);
qemu_semihosting_console_outc(env, args);
return 0xdeadbeef;
case TARGET_SYS_WRITE0:
return qemu_semihosting_console_out(env, args, 0);
return qemu_semihosting_console_outs(env, args);
case TARGET_SYS_WRITE:
GET_ARG(0);
GET_ARG(1);
Expand Down

0 comments on commit 78e2484

Please sign in to comment.