target/arm/arm-semi: Restrict use of TaskState*

The semihosting code needs accuss to the linux-user only
TaskState pointer so it can set the semihosting errno per-thread
for linux-user mode. At the moment we do this by having some
ifdefs so that we define a 'ts' local in do_arm_semihosting()
which is either a real TaskState * or just a CPUARMState *,
depending on which mode we're compiling for.

This is awkward if we want to refactor do_arm_semihosting()
into other functions which might need to be passed the TaskState.
Restrict usage of the TaskState local by:
 * making set_swi_errno() always take the CPUARMState pointer
   and (for the linux-user version) get TaskState from that
 * creating a new get_swi_errno() which reads the errno
 * having the two semihosting calls which need the TaskState
   for other purposes (SYS_GET_CMDLINE and SYS_HEAPINFO)
   define a variable with scope restricted to just that code

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20190916141544.17540-6-peter.maydell@linaro.org
This commit is contained in:
Peter Maydell 2019-09-16 15:15:34 +01:00
parent 35e9a0a8ce
commit 6ed6845532

View file

@ -213,26 +213,45 @@ static GuestFD *get_guestfd(int guestfd)
return gf; return gf;
} }
#ifdef CONFIG_USER_ONLY /*
static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code) * The semihosting API has no concept of its errno being thread-safe,
{ * as the API design predates SMP CPUs and was intended as a simple
if (code == (uint32_t)-1) * real-hardware set of debug functionality. For QEMU, we make the
ts->swi_errno = errno; * errno be per-thread in linux-user mode; in softmmu it is a simple
return code; * global, and we assume that the guest takes care of avoiding any races.
} */
#else #ifndef CONFIG_USER_ONLY
static target_ulong syscall_err; static target_ulong syscall_err;
#include "exec/softmmu-semi.h"
#endif
static inline uint32_t set_swi_errno(CPUARMState *env, uint32_t code) static inline uint32_t set_swi_errno(CPUARMState *env, uint32_t code)
{ {
if (code == (uint32_t)-1) { if (code == (uint32_t)-1) {
#ifdef CONFIG_USER_ONLY
CPUState *cs = env_cpu(env);
TaskState *ts = cs->opaque;
ts->swi_errno = errno;
#else
syscall_err = errno; syscall_err = errno;
#endif
} }
return code; return code;
} }
#include "exec/softmmu-semi.h" static inline uint32_t get_swi_errno(CPUARMState *env)
{
#ifdef CONFIG_USER_ONLY
CPUState *cs = env_cpu(env);
TaskState *ts = cs->opaque;
return ts->swi_errno;
#else
return syscall_err;
#endif #endif
}
static target_ulong arm_semi_syscall_len; static target_ulong arm_semi_syscall_len;
@ -379,12 +398,12 @@ static target_ulong arm_gdb_syscall(ARMCPU *cpu, gdb_syscall_complete_cb cb,
if (is_a64(env)) { \ if (is_a64(env)) { \
if (get_user_u64(arg ## n, args + (n) * 8)) { \ if (get_user_u64(arg ## n, args + (n) * 8)) { \
errno = EFAULT; \ errno = EFAULT; \
return set_swi_errno(ts, -1); \ return set_swi_errno(env, -1); \
} \ } \
} else { \ } else { \
if (get_user_u32(arg ## n, args + (n) * 4)) { \ if (get_user_u32(arg ## n, args + (n) * 4)) { \
errno = EFAULT; \ errno = EFAULT; \
return set_swi_errno(ts, -1); \ return set_swi_errno(env, -1); \
} \ } \
} \ } \
} while (0) } while (0)
@ -413,11 +432,6 @@ target_ulong do_arm_semihosting(CPUARMState *env)
int nr; int nr;
uint32_t ret; uint32_t ret;
uint32_t len; uint32_t len;
#ifdef CONFIG_USER_ONLY
TaskState *ts = cs->opaque;
#else
CPUARMState *ts = env;
#endif
GuestFD *gf; GuestFD *gf;
if (is_a64(env)) { if (is_a64(env)) {
@ -440,19 +454,19 @@ target_ulong do_arm_semihosting(CPUARMState *env)
s = lock_user_string(arg0); s = lock_user_string(arg0);
if (!s) { if (!s) {
errno = EFAULT; errno = EFAULT;
return set_swi_errno(ts, -1); return set_swi_errno(env, -1);
} }
if (arg1 >= 12) { if (arg1 >= 12) {
unlock_user(s, arg0, 0); unlock_user(s, arg0, 0);
errno = EINVAL; errno = EINVAL;
return set_swi_errno(ts, -1); return set_swi_errno(env, -1);
} }
guestfd = alloc_guestfd(); guestfd = alloc_guestfd();
if (guestfd < 0) { if (guestfd < 0) {
unlock_user(s, arg0, 0); unlock_user(s, arg0, 0);
errno = EMFILE; errno = EMFILE;
return set_swi_errno(ts, -1); return set_swi_errno(env, -1);
} }
if (strcmp(s, ":tt") == 0) { if (strcmp(s, ":tt") == 0) {
@ -466,7 +480,7 @@ target_ulong do_arm_semihosting(CPUARMState *env)
ret = arm_gdb_syscall(cpu, arm_semi_open_cb, "open,%s,%x,1a4", arg0, ret = arm_gdb_syscall(cpu, arm_semi_open_cb, "open,%s,%x,1a4", arg0,
(int)arg2+1, gdb_open_modeflags[arg1]); (int)arg2+1, gdb_open_modeflags[arg1]);
} else { } else {
ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644)); ret = set_swi_errno(env, open(s, open_modeflags[arg1], 0644));
if (ret == (uint32_t)-1) { if (ret == (uint32_t)-1) {
dealloc_guestfd(guestfd); dealloc_guestfd(guestfd);
} else { } else {
@ -483,13 +497,13 @@ target_ulong do_arm_semihosting(CPUARMState *env)
gf = get_guestfd(arg0); gf = get_guestfd(arg0);
if (!gf) { if (!gf) {
errno = EBADF; errno = EBADF;
return set_swi_errno(ts, -1); return set_swi_errno(env, -1);
} }
if (use_gdb_syscalls()) { if (use_gdb_syscalls()) {
ret = arm_gdb_syscall(cpu, arm_semi_cb, "close,%x", gf->hostfd); ret = arm_gdb_syscall(cpu, arm_semi_cb, "close,%x", gf->hostfd);
} else { } else {
ret = set_swi_errno(ts, close(gf->hostfd)); ret = set_swi_errno(env, close(gf->hostfd));
} }
dealloc_guestfd(arg0); dealloc_guestfd(arg0);
return ret; return ret;
@ -507,7 +521,7 @@ target_ulong do_arm_semihosting(CPUARMState *env)
gf = get_guestfd(arg0); gf = get_guestfd(arg0);
if (!gf) { if (!gf) {
errno = EBADF; errno = EBADF;
return set_swi_errno(ts, -1); return set_swi_errno(env, -1);
} }
if (use_gdb_syscalls()) { if (use_gdb_syscalls()) {
@ -520,7 +534,7 @@ target_ulong do_arm_semihosting(CPUARMState *env)
/* Return bytes not written on error */ /* Return bytes not written on error */
return len; return len;
} }
ret = set_swi_errno(ts, write(gf->hostfd, s, len)); ret = set_swi_errno(env, write(gf->hostfd, s, len));
unlock_user(s, arg1, 0); unlock_user(s, arg1, 0);
if (ret == (uint32_t)-1) { if (ret == (uint32_t)-1) {
ret = 0; ret = 0;
@ -537,7 +551,7 @@ target_ulong do_arm_semihosting(CPUARMState *env)
gf = get_guestfd(arg0); gf = get_guestfd(arg0);
if (!gf) { if (!gf) {
errno = EBADF; errno = EBADF;
return set_swi_errno(ts, -1); return set_swi_errno(env, -1);
} }
if (use_gdb_syscalls()) { if (use_gdb_syscalls()) {
@ -551,7 +565,7 @@ target_ulong do_arm_semihosting(CPUARMState *env)
return len; return len;
} }
do { do {
ret = set_swi_errno(ts, read(gf->hostfd, s, len)); ret = set_swi_errno(env, read(gf->hostfd, s, len));
} while (ret == -1 && errno == EINTR); } while (ret == -1 && errno == EINTR);
unlock_user(s, arg1, len); unlock_user(s, arg1, len);
if (ret == (uint32_t)-1) { if (ret == (uint32_t)-1) {
@ -569,7 +583,7 @@ target_ulong do_arm_semihosting(CPUARMState *env)
gf = get_guestfd(arg0); gf = get_guestfd(arg0);
if (!gf) { if (!gf) {
errno = EBADF; errno = EBADF;
return set_swi_errno(ts, -1); return set_swi_errno(env, -1);
} }
if (use_gdb_syscalls()) { if (use_gdb_syscalls()) {
@ -584,14 +598,14 @@ target_ulong do_arm_semihosting(CPUARMState *env)
gf = get_guestfd(arg0); gf = get_guestfd(arg0);
if (!gf) { if (!gf) {
errno = EBADF; errno = EBADF;
return set_swi_errno(ts, -1); return set_swi_errno(env, -1);
} }
if (use_gdb_syscalls()) { if (use_gdb_syscalls()) {
return arm_gdb_syscall(cpu, arm_semi_cb, "lseek,%x,%x,0", return arm_gdb_syscall(cpu, arm_semi_cb, "lseek,%x,%x,0",
gf->hostfd, arg1); gf->hostfd, arg1);
} else { } else {
ret = set_swi_errno(ts, lseek(gf->hostfd, arg1, SEEK_SET)); ret = set_swi_errno(env, lseek(gf->hostfd, arg1, SEEK_SET));
if (ret == (uint32_t)-1) if (ret == (uint32_t)-1)
return -1; return -1;
return 0; return 0;
@ -602,7 +616,7 @@ target_ulong do_arm_semihosting(CPUARMState *env)
gf = get_guestfd(arg0); gf = get_guestfd(arg0);
if (!gf) { if (!gf) {
errno = EBADF; errno = EBADF;
return set_swi_errno(ts, -1); return set_swi_errno(env, -1);
} }
if (use_gdb_syscalls()) { if (use_gdb_syscalls()) {
@ -610,7 +624,7 @@ target_ulong do_arm_semihosting(CPUARMState *env)
gf->hostfd, arm_flen_buf(cpu)); gf->hostfd, arm_flen_buf(cpu));
} else { } else {
struct stat buf; struct stat buf;
ret = set_swi_errno(ts, fstat(gf->hostfd, &buf)); ret = set_swi_errno(env, fstat(gf->hostfd, &buf));
if (ret == (uint32_t)-1) if (ret == (uint32_t)-1)
return -1; return -1;
return buf.st_size; return buf.st_size;
@ -628,9 +642,9 @@ target_ulong do_arm_semihosting(CPUARMState *env)
s = lock_user_string(arg0); s = lock_user_string(arg0);
if (!s) { if (!s) {
errno = EFAULT; errno = EFAULT;
return set_swi_errno(ts, -1); return set_swi_errno(env, -1);
} }
ret = set_swi_errno(ts, remove(s)); ret = set_swi_errno(env, remove(s));
unlock_user(s, arg0, 0); unlock_user(s, arg0, 0);
} }
return ret; return ret;
@ -648,9 +662,9 @@ target_ulong do_arm_semihosting(CPUARMState *env)
s2 = lock_user_string(arg2); s2 = lock_user_string(arg2);
if (!s || !s2) { if (!s || !s2) {
errno = EFAULT; errno = EFAULT;
ret = set_swi_errno(ts, -1); ret = set_swi_errno(env, -1);
} else { } else {
ret = set_swi_errno(ts, rename(s, s2)); ret = set_swi_errno(env, rename(s, s2));
} }
if (s2) if (s2)
unlock_user(s2, arg2, 0); unlock_user(s2, arg2, 0);
@ -661,7 +675,7 @@ target_ulong do_arm_semihosting(CPUARMState *env)
case TARGET_SYS_CLOCK: case TARGET_SYS_CLOCK:
return clock() / (CLOCKS_PER_SEC / 100); return clock() / (CLOCKS_PER_SEC / 100);
case TARGET_SYS_TIME: case TARGET_SYS_TIME:
return set_swi_errno(ts, time(NULL)); return set_swi_errno(env, time(NULL));
case TARGET_SYS_SYSTEM: case TARGET_SYS_SYSTEM:
GET_ARG(0); GET_ARG(0);
GET_ARG(1); GET_ARG(1);
@ -672,18 +686,14 @@ target_ulong do_arm_semihosting(CPUARMState *env)
s = lock_user_string(arg0); s = lock_user_string(arg0);
if (!s) { if (!s) {
errno = EFAULT; errno = EFAULT;
return set_swi_errno(ts, -1); return set_swi_errno(env, -1);
} }
ret = set_swi_errno(ts, system(s)); ret = set_swi_errno(env, system(s));
unlock_user(s, arg0, 0); unlock_user(s, arg0, 0);
return ret; return ret;
} }
case TARGET_SYS_ERRNO: case TARGET_SYS_ERRNO:
#ifdef CONFIG_USER_ONLY return get_swi_errno(env);
return ts->swi_errno;
#else
return syscall_err;
#endif
case TARGET_SYS_GET_CMDLINE: case TARGET_SYS_GET_CMDLINE:
{ {
/* Build a command-line from the original argv. /* Build a command-line from the original argv.
@ -706,6 +716,8 @@ target_ulong do_arm_semihosting(CPUARMState *env)
int status = 0; int status = 0;
#if !defined(CONFIG_USER_ONLY) #if !defined(CONFIG_USER_ONLY)
const char *cmdline; const char *cmdline;
#else
TaskState *ts = cs->opaque;
#endif #endif
GET_ARG(0); GET_ARG(0);
GET_ARG(1); GET_ARG(1);
@ -733,21 +745,21 @@ target_ulong do_arm_semihosting(CPUARMState *env)
if (output_size > input_size) { if (output_size > input_size) {
/* Not enough space to store command-line arguments. */ /* Not enough space to store command-line arguments. */
errno = E2BIG; errno = E2BIG;
return set_swi_errno(ts, -1); return set_swi_errno(env, -1);
} }
/* Adjust the command-line length. */ /* Adjust the command-line length. */
if (SET_ARG(1, output_size - 1)) { if (SET_ARG(1, output_size - 1)) {
/* Couldn't write back to argument block */ /* Couldn't write back to argument block */
errno = EFAULT; errno = EFAULT;
return set_swi_errno(ts, -1); return set_swi_errno(env, -1);
} }
/* Lock the buffer on the ARM side. */ /* Lock the buffer on the ARM side. */
output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0); output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0);
if (!output_buffer) { if (!output_buffer) {
errno = EFAULT; errno = EFAULT;
return set_swi_errno(ts, -1); return set_swi_errno(env, -1);
} }
/* Copy the command-line arguments. */ /* Copy the command-line arguments. */
@ -763,7 +775,7 @@ target_ulong do_arm_semihosting(CPUARMState *env)
if (copy_from_user(output_buffer, ts->info->arg_start, if (copy_from_user(output_buffer, ts->info->arg_start,
output_size)) { output_size)) {
errno = EFAULT; errno = EFAULT;
status = set_swi_errno(ts, -1); status = set_swi_errno(env, -1);
goto out; goto out;
} }
@ -785,6 +797,9 @@ target_ulong do_arm_semihosting(CPUARMState *env)
target_ulong retvals[4]; target_ulong retvals[4];
target_ulong limit; target_ulong limit;
int i; int i;
#ifdef CONFIG_USER_ONLY
TaskState *ts = cs->opaque;
#endif
GET_ARG(0); GET_ARG(0);
@ -834,7 +849,7 @@ target_ulong do_arm_semihosting(CPUARMState *env)
if (fail) { if (fail) {
/* Couldn't write back to argument block */ /* Couldn't write back to argument block */
errno = EFAULT; errno = EFAULT;
return set_swi_errno(ts, -1); return set_swi_errno(env, -1);
} }
} }
return 0; return 0;