diff options
author | Stefan Hajnoczi <stefanha@redhat.com> | 2022-09-17 10:30:33 -0400 |
---|---|---|
committer | Stefan Hajnoczi <stefanha@redhat.com> | 2022-09-17 10:30:34 -0400 |
commit | 123a32f9b38c3bd22d47688d0bf0ab955d4152bc (patch) | |
tree | 4da85597c7685c95ee752c2e5dff4b1d185fe458 /target | |
parent | 50eac424c74bddd2d73cee47080be94c1d04893e (diff) | |
parent | 7d7fb11615809839ff858328134c6a0abad27ea4 (diff) | |
download | qemu-123a32f9b38c3bd22d47688d0bf0ab955d4152bc.zip qemu-123a32f9b38c3bd22d47688d0bf0ab955d4152bc.tar.gz qemu-123a32f9b38c3bd22d47688d0bf0ab955d4152bc.tar.bz2 |
Merge tag 'pull-semi-20220914' of https://gitlab.com/rth7680/qemu into staging
Convert m68k to semihosting/syscalls.h.
Convert nios2 to semihosting/syscalls.h.
Allow optional use of semihosting from userspace.
# -----BEGIN PGP SIGNATURE-----
#
# iQFRBAABCgA7FiEEekgeeIaLTbaoWgXAZN846K9+IV8FAmMh1W8dHHJpY2hhcmQu
# aGVuZGVyc29uQGxpbmFyby5vcmcACgkQZN846K9+IV8ptggAimuNN6IiD19Huu5F
# PMjzDqFPvWFOf82O16WTBM1xN0lwVH8+02PYRL3AhOIw9ZTgxezOo9/KXZpr8a8Z
# gocr4Ge/J7zHzHahYuqcyOqqkur2dM4lFiK9rfDD6vdNBMbi0kQZVuaNlQK6rV6Z
# 2LHEwKKh64MXJVfwGzK7OLMv4pu0wpWcuCTH2/6U4E1325SOKmEos1VzIePxY1bw
# +AMNnairGEdBX1b3JlzZfrLSaOapJcgl0HZdrg6Mflm6ttTuuykGGtjkWBfcu3Nw
# utNI1zmUYfD/iJbnbsCNpZSLv6LVOQ2l5S6dOWV+JJ1HukVTZu3DoyfTr8t95kwK
# UuUoqA==
# =W7Yh
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 14 Sep 2022 09:21:51 EDT
# gpg: using RSA key 7A481E78868B4DB6A85A05C064DF38E8AF7E215F
# gpg: issuer "richard.henderson@linaro.org"
# gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>" [full]
# Primary key fingerprint: 7A48 1E78 868B 4DB6 A85A 05C0 64DF 38E8 AF7E 215F
* tag 'pull-semi-20220914' of https://gitlab.com/rth7680/qemu:
target/riscv: Honour -semihosting-config userspace=on and enable=on
target/xtensa: Honour -semihosting-config userspace=on
target/nios2: Honour -semihosting-config userspace=on
target/mips: Honour -semihosting-config userspace=on
target/m68k: Honour -semihosting-config userspace=on
target/arm: Honour -semihosting-config userspace=on
semihosting: Allow optional use of semihosting from userspace
target/m68k: Convert semihosting errno to gdb remote errno
target/m68k: Use semihosting/syscalls.h
target/nios2: Convert semihosting errno to gdb remote errno
target/nios2: Use semihosting/syscalls.h
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'target')
-rw-r--r-- | target/arm/translate-a64.c | 12 | ||||
-rw-r--r-- | target/arm/translate.c | 16 | ||||
-rw-r--r-- | target/m68k/m68k-semi.c | 306 | ||||
-rw-r--r-- | target/m68k/op_helper.c | 3 | ||||
-rw-r--r-- | target/mips/tcg/micromips_translate.c.inc | 6 | ||||
-rw-r--r-- | target/mips/tcg/mips16e_translate.c.inc | 2 | ||||
-rw-r--r-- | target/mips/tcg/nanomips_translate.c.inc | 4 | ||||
-rw-r--r-- | target/mips/tcg/translate.c | 9 | ||||
-rw-r--r-- | target/nios2/nios2-semi.c | 321 | ||||
-rw-r--r-- | target/nios2/translate.c | 3 | ||||
-rw-r--r-- | target/riscv/cpu_helper.c | 9 | ||||
-rw-r--r-- | target/riscv/insn_trans/trans_privileged.c.inc | 3 | ||||
-rw-r--r-- | target/riscv/translate.c | 1 | ||||
-rw-r--r-- | target/xtensa/translate.c | 7 |
14 files changed, 182 insertions, 520 deletions
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 163df8c..9bed336 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -2219,17 +2219,7 @@ static void disas_exc(DisasContext *s, uint32_t insn) * it is required for halting debug disabled: it will UNDEF. * Secondly, "HLT 0xf000" is the A64 semihosting syscall instruction. */ - if (semihosting_enabled() && imm16 == 0xf000) { -#ifndef CONFIG_USER_ONLY - /* In system mode, don't allow userspace access to semihosting, - * to provide some semblance of security (and for consistency - * with our 32-bit semihosting). - */ - if (s->current_el == 0) { - unallocated_encoding(s); - break; - } -#endif + if (semihosting_enabled(s->current_el == 0) && imm16 == 0xf000) { gen_exception_internal_insn(s, s->pc_curr, EXCP_SEMIHOST); } else { unallocated_encoding(s); diff --git a/target/arm/translate.c b/target/arm/translate.c index 9474e4b..5aaccbb 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -1169,10 +1169,7 @@ static inline void gen_hlt(DisasContext *s, int imm) * semihosting, to provide some semblance of security * (and for consistency with our 32-bit semihosting). */ - if (semihosting_enabled() && -#ifndef CONFIG_USER_ONLY - s->current_el != 0 && -#endif + if (semihosting_enabled(s->current_el != 0) && (imm == (s->thumb ? 0x3c : 0xf000))) { gen_exception_internal_insn(s, s->pc_curr, EXCP_SEMIHOST); return; @@ -6556,10 +6553,7 @@ static bool trans_BKPT(DisasContext *s, arg_BKPT *a) /* BKPT is OK with ECI set and leaves it untouched */ s->eci_handled = true; if (arm_dc_feature(s, ARM_FEATURE_M) && - semihosting_enabled() && -#ifndef CONFIG_USER_ONLY - !IS_USER(s) && -#endif + semihosting_enabled(s->current_el == 0) && (a->imm == 0xab)) { gen_exception_internal_insn(s, s->pc_curr, EXCP_SEMIHOST); } else { @@ -8764,10 +8758,8 @@ static bool trans_SVC(DisasContext *s, arg_SVC *a) { const uint32_t semihost_imm = s->thumb ? 0xab : 0x123456; - if (!arm_dc_feature(s, ARM_FEATURE_M) && semihosting_enabled() && -#ifndef CONFIG_USER_ONLY - !IS_USER(s) && -#endif + if (!arm_dc_feature(s, ARM_FEATURE_M) && + semihosting_enabled(s->current_el == 0) && (a->imm == semihost_imm)) { gen_exception_internal_insn(s, s->pc_curr, EXCP_SEMIHOST); } else { diff --git a/target/m68k/m68k-semi.c b/target/m68k/m68k-semi.c index d0697dd..87b1314 100644 --- a/target/m68k/m68k-semi.c +++ b/target/m68k/m68k-semi.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" +#include "semihosting/syscalls.h" #include "semihosting/softmmu-uaccess.h" #include "hw/boards.h" #include "qemu/log.h" @@ -40,54 +41,33 @@ #define HOSTED_ISATTY 12 #define HOSTED_SYSTEM 13 -static int translate_openflags(int flags) +static int host_to_gdb_errno(int err) { - int hf; - - if (flags & GDB_O_WRONLY) - hf = O_WRONLY; - else if (flags & GDB_O_RDWR) - hf = O_RDWR; - else - hf = O_RDONLY; - - if (flags & GDB_O_APPEND) hf |= O_APPEND; - if (flags & GDB_O_CREAT) hf |= O_CREAT; - if (flags & GDB_O_TRUNC) hf |= O_TRUNC; - if (flags & GDB_O_EXCL) hf |= O_EXCL; - - return hf; -} - -static void translate_stat(CPUM68KState *env, target_ulong addr, struct stat *s) -{ - struct gdb_stat *p; - - p = lock_user(VERIFY_WRITE, addr, sizeof(struct gdb_stat), 0); - if (!p) { - /* FIXME - should this return an error code? */ - return; +#define E(X) case E##X: return GDB_E##X + switch (err) { + E(PERM); + E(NOENT); + E(INTR); + E(BADF); + E(ACCES); + E(FAULT); + E(BUSY); + E(EXIST); + E(NODEV); + E(NOTDIR); + E(ISDIR); + E(INVAL); + E(NFILE); + E(MFILE); + E(FBIG); + E(NOSPC); + E(SPIPE); + E(ROFS); + E(NAMETOOLONG); + default: + return GDB_EUNKNOWN; } - p->gdb_st_dev = cpu_to_be32(s->st_dev); - p->gdb_st_ino = cpu_to_be32(s->st_ino); - p->gdb_st_mode = cpu_to_be32(s->st_mode); - p->gdb_st_nlink = cpu_to_be32(s->st_nlink); - p->gdb_st_uid = cpu_to_be32(s->st_uid); - p->gdb_st_gid = cpu_to_be32(s->st_gid); - p->gdb_st_rdev = cpu_to_be32(s->st_rdev); - p->gdb_st_size = cpu_to_be64(s->st_size); -#ifdef _WIN32 - /* Windows stat is missing some fields. */ - p->gdb_st_blksize = 0; - p->gdb_st_blocks = 0; -#else - p->gdb_st_blksize = cpu_to_be64(s->st_blksize); - p->gdb_st_blocks = cpu_to_be64(s->st_blocks); -#endif - p->gdb_st_atime = cpu_to_be32(s->st_atime); - p->gdb_st_mtime = cpu_to_be32(s->st_mtime); - p->gdb_st_ctime = cpu_to_be32(s->st_ctime); - unlock_user(p, addr, sizeof(struct gdb_stat)); +#undef E } static void m68k_semi_u32_cb(CPUState *cs, uint64_t ret, int err) @@ -97,7 +77,7 @@ static void m68k_semi_u32_cb(CPUState *cs, uint64_t ret, int err) target_ulong args = env->dregs[1]; if (put_user_u32(ret, args) || - put_user_u32(err, args + 4)) { + put_user_u32(host_to_gdb_errno(err), args + 4)) { /* * The m68k semihosting ABI does not provide any way to report this * error to the guest, so the best we can do is log it in qemu. @@ -116,7 +96,7 @@ static void m68k_semi_u64_cb(CPUState *cs, uint64_t ret, int err) target_ulong args = env->dregs[1]; if (put_user_u32(ret >> 32, args) || put_user_u32(ret, args + 4) || - put_user_u32(err, args + 8)) { + put_user_u32(host_to_gdb_errno(err), args + 8)) { /* No way to report this via m68k semihosting ABI; just log it */ qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value " "discarded because argument block not writable\n"); @@ -129,248 +109,109 @@ static void m68k_semi_u64_cb(CPUState *cs, uint64_t ret, int err) */ #define GET_ARG(n) do { \ if (get_user_ual(arg ## n, args + (n) * 4)) { \ - result = -1; \ - errno = EFAULT; \ goto failed; \ } \ } while (0) +#define GET_ARG64(n) do { \ + if (get_user_ual(arg ## n, args + (n) * 4)) { \ + goto failed64; \ + } \ +} while (0) + + void do_m68k_semihosting(CPUM68KState *env, int nr) { CPUState *cs = env_cpu(env); uint32_t args; target_ulong arg0, arg1, arg2, arg3; - void *p; - void *q; - uint32_t len; - uint32_t result; args = env->dregs[1]; switch (nr) { case HOSTED_EXIT: gdb_exit(env->dregs[0]); exit(env->dregs[0]); + case HOSTED_OPEN: GET_ARG(0); GET_ARG(1); GET_ARG(2); GET_ARG(3); - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_u32_cb, "open,%s,%x,%x", arg0, (int)arg1, - arg2, arg3); - return; - } else { - p = lock_user_string(arg0); - if (!p) { - /* FIXME - check error code? */ - result = -1; - } else { - result = open(p, translate_openflags(arg2), arg3); - unlock_user(p, arg0, 0); - } - } + semihost_sys_open(cs, m68k_semi_u32_cb, arg0, arg1, arg2, arg3); break; + case HOSTED_CLOSE: - { - /* Ignore attempts to close stdin/out/err. */ - GET_ARG(0); - int fd = arg0; - if (fd > 2) { - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_u32_cb, "close,%x", arg0); - return; - } else { - result = close(fd); - } - } else { - result = 0; - } - break; - } + GET_ARG(0); + semihost_sys_close(cs, m68k_semi_u32_cb, arg0); + break; + case HOSTED_READ: GET_ARG(0); GET_ARG(1); GET_ARG(2); - len = arg2; - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_u32_cb, "read,%x,%x,%x", - arg0, arg1, len); - return; - } else { - p = lock_user(VERIFY_WRITE, arg1, len, 0); - if (!p) { - /* FIXME - check error code? */ - result = -1; - } else { - result = read(arg0, p, len); - unlock_user(p, arg1, len); - } - } + semihost_sys_read(cs, m68k_semi_u32_cb, arg0, arg1, arg2); break; + case HOSTED_WRITE: GET_ARG(0); GET_ARG(1); GET_ARG(2); - len = arg2; - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_u32_cb, "write,%x,%x,%x", - arg0, arg1, len); - return; - } else { - p = lock_user(VERIFY_READ, arg1, len, 1); - if (!p) { - /* FIXME - check error code? */ - result = -1; - } else { - result = write(arg0, p, len); - unlock_user(p, arg0, 0); - } - } + semihost_sys_write(cs, m68k_semi_u32_cb, arg0, arg1, arg2); break; + case HOSTED_LSEEK: - { - uint64_t off; - GET_ARG(0); - GET_ARG(1); - GET_ARG(2); - GET_ARG(3); - off = (uint32_t)arg2 | ((uint64_t)arg1 << 32); - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_u64_cb, "fseek,%x,%lx,%x", - arg0, off, arg3); - } else { - off = lseek(arg0, off, arg3); - m68k_semi_u64_cb(cs, off, errno); - } - return; - } + GET_ARG64(0); + GET_ARG64(1); + GET_ARG64(2); + GET_ARG64(3); + semihost_sys_lseek(cs, m68k_semi_u64_cb, arg0, + deposit64(arg2, arg1, 32, 32), arg3); + break; + case HOSTED_RENAME: GET_ARG(0); GET_ARG(1); GET_ARG(2); GET_ARG(3); - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_u32_cb, "rename,%s,%s", - arg0, (int)arg1, arg2, (int)arg3); - return; - } else { - p = lock_user_string(arg0); - q = lock_user_string(arg2); - if (!p || !q) { - /* FIXME - check error code? */ - result = -1; - } else { - result = rename(p, q); - } - unlock_user(p, arg0, 0); - unlock_user(q, arg2, 0); - } + semihost_sys_rename(cs, m68k_semi_u32_cb, arg0, arg1, arg2, arg3); break; + case HOSTED_UNLINK: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_u32_cb, "unlink,%s", - arg0, (int)arg1); - return; - } else { - p = lock_user_string(arg0); - if (!p) { - /* FIXME - check error code? */ - result = -1; - } else { - result = unlink(p); - unlock_user(p, arg0, 0); - } - } + semihost_sys_remove(cs, m68k_semi_u32_cb, arg0, arg1); break; + case HOSTED_STAT: GET_ARG(0); GET_ARG(1); GET_ARG(2); - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_u32_cb, "stat,%s,%x", - arg0, (int)arg1, arg2); - return; - } else { - struct stat s; - p = lock_user_string(arg0); - if (!p) { - /* FIXME - check error code? */ - result = -1; - } else { - result = stat(p, &s); - unlock_user(p, arg0, 0); - } - if (result == 0) { - translate_stat(env, arg2, &s); - } - } + semihost_sys_stat(cs, m68k_semi_u32_cb, arg0, arg1, arg2); break; + case HOSTED_FSTAT: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_u32_cb, "fstat,%x,%x", - arg0, arg1); - return; - } else { - struct stat s; - result = fstat(arg0, &s); - if (result == 0) { - translate_stat(env, arg1, &s); - } - } + semihost_sys_fstat(cs, m68k_semi_u32_cb, arg0, arg1); break; + case HOSTED_GETTIMEOFDAY: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_u32_cb, "gettimeofday,%x,%x", - arg0, arg1); - return; - } else { - struct gdb_timeval *p; - int64_t rt = g_get_real_time(); - p = lock_user(VERIFY_WRITE, arg0, sizeof(struct gdb_timeval), 0); - if (!p) { - /* FIXME - check error code? */ - result = -1; - } else { - result = 0; - p->tv_sec = cpu_to_be32(rt / G_USEC_PER_SEC); - p->tv_usec = cpu_to_be64(rt % G_USEC_PER_SEC); - unlock_user(p, arg0, sizeof(struct gdb_timeval)); - } - } + semihost_sys_gettimeofday(cs, m68k_semi_u32_cb, arg0, arg1); break; + case HOSTED_ISATTY: GET_ARG(0); - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_u32_cb, "isatty,%x", arg0); - return; - } else { - result = isatty(arg0); - } + semihost_sys_isatty(cs, m68k_semi_u32_cb, arg0); break; + case HOSTED_SYSTEM: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_u32_cb, "system,%s", - arg0, (int)arg1); - return; - } else { - p = lock_user_string(arg0); - if (!p) { - /* FIXME - check error code? */ - result = -1; - } else { - result = system(p); - unlock_user(p, arg0, 0); - } - } + semihost_sys_system(cs, m68k_semi_u32_cb, arg0, arg1); break; + case HOSTED_INIT_SIM: /* * FIXME: This is wrong for boards where RAM does not start at @@ -379,10 +220,15 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) env->dregs[1] = current_machine->ram_size; env->aregs[7] = current_machine->ram_size; return; + default: cpu_abort(env_cpu(env), "Unsupported semihosting syscall %d\n", nr); - result = 0; + + failed: + m68k_semi_u32_cb(cs, -1, EFAULT); + break; + failed64: + m68k_semi_u64_cb(cs, -1, EFAULT); + break; } -failed: - m68k_semi_u32_cb(cs, result, errno); } diff --git a/target/m68k/op_helper.c b/target/m68k/op_helper.c index d9937ca..a96a034 100644 --- a/target/m68k/op_helper.c +++ b/target/m68k/op_helper.c @@ -203,8 +203,7 @@ static void cf_interrupt_all(CPUM68KState *env, int is_hw) cf_rte(env); return; case EXCP_HALT_INSN: - if (semihosting_enabled() - && (env->sr & SR_S) != 0 + if (semihosting_enabled((env->sr & SR_S) == 0) && (env->pc & 3) == 0 && cpu_lduw_code(env, env->pc - 4) == 0x4e71 && cpu_ldl_code(env, env->pc) == 0x4e7bf000) { diff --git a/target/mips/tcg/micromips_translate.c.inc b/target/mips/tcg/micromips_translate.c.inc index b2c696f..632895c 100644 --- a/target/mips/tcg/micromips_translate.c.inc +++ b/target/mips/tcg/micromips_translate.c.inc @@ -825,7 +825,7 @@ static void gen_pool16c_insn(DisasContext *ctx) generate_exception_break(ctx, extract32(ctx->opcode, 0, 4)); break; case SDBBP16: - if (is_uhi(extract32(ctx->opcode, 0, 4))) { + if (is_uhi(ctx, extract32(ctx->opcode, 0, 4))) { ctx->base.is_jmp = DISAS_SEMIHOST; } else { /* @@ -941,7 +941,7 @@ static void gen_pool16c_r6_insn(DisasContext *ctx) break; case R6_SDBBP16: /* SDBBP16 */ - if (is_uhi(extract32(ctx->opcode, 6, 4))) { + if (is_uhi(ctx, extract32(ctx->opcode, 6, 4))) { ctx->base.is_jmp = DISAS_SEMIHOST; } else { if (ctx->hflags & MIPS_HFLAG_SBRI) { @@ -1310,7 +1310,7 @@ static void gen_pool32axf(CPUMIPSState *env, DisasContext *ctx, int rt, int rs) generate_exception_end(ctx, EXCP_SYSCALL); break; case SDBBP: - if (is_uhi(extract32(ctx->opcode, 16, 10))) { + if (is_uhi(ctx, extract32(ctx->opcode, 16, 10))) { ctx->base.is_jmp = DISAS_SEMIHOST; } else { check_insn(ctx, ISA_MIPS_R1); diff --git a/target/mips/tcg/mips16e_translate.c.inc b/target/mips/tcg/mips16e_translate.c.inc index 7568933..918b15d 100644 --- a/target/mips/tcg/mips16e_translate.c.inc +++ b/target/mips/tcg/mips16e_translate.c.inc @@ -951,7 +951,7 @@ static int decode_ase_mips16e(CPUMIPSState *env, DisasContext *ctx) } break; case RR_SDBBP: - if (is_uhi(extract32(ctx->opcode, 5, 6))) { + if (is_uhi(ctx, extract32(ctx->opcode, 5, 6))) { ctx->base.is_jmp = DISAS_SEMIHOST; } else { /* diff --git a/target/mips/tcg/nanomips_translate.c.inc b/target/mips/tcg/nanomips_translate.c.inc index b3aff22..812c111 100644 --- a/target/mips/tcg/nanomips_translate.c.inc +++ b/target/mips/tcg/nanomips_translate.c.inc @@ -3694,7 +3694,7 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) generate_exception_end(ctx, EXCP_BREAK); break; case NM_SDBBP: - if (is_uhi(extract32(ctx->opcode, 0, 19))) { + if (is_uhi(ctx, extract32(ctx->opcode, 0, 19))) { ctx->base.is_jmp = DISAS_SEMIHOST; } else { if (ctx->hflags & MIPS_HFLAG_SBRI) { @@ -4633,7 +4633,7 @@ static int decode_isa_nanomips(CPUMIPSState *env, DisasContext *ctx) generate_exception_end(ctx, EXCP_BREAK); break; case NM_SDBBP16: - if (is_uhi(extract32(ctx->opcode, 0, 3))) { + if (is_uhi(ctx, extract32(ctx->opcode, 0, 3))) { ctx->base.is_jmp = DISAS_SEMIHOST; } else { if (ctx->hflags & MIPS_HFLAG_SBRI) { diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 0d936e2..c3f92ea 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -12082,12 +12082,13 @@ static void gen_cache_operation(DisasContext *ctx, uint32_t op, int base, tcg_temp_free_i32(t0); } -static inline bool is_uhi(int sdbbp_code) +static inline bool is_uhi(DisasContext *ctx, int sdbbp_code) { #ifdef CONFIG_USER_ONLY return false; #else - return semihosting_enabled() && sdbbp_code == 1; + bool is_user = (ctx->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_UM; + return semihosting_enabled(is_user) && sdbbp_code == 1; #endif } @@ -13898,7 +13899,7 @@ static void decode_opc_special_r6(CPUMIPSState *env, DisasContext *ctx) } break; case R6_OPC_SDBBP: - if (is_uhi(extract32(ctx->opcode, 6, 20))) { + if (is_uhi(ctx, extract32(ctx->opcode, 6, 20))) { ctx->base.is_jmp = DISAS_SEMIHOST; } else { if (ctx->hflags & MIPS_HFLAG_SBRI) { @@ -14310,7 +14311,7 @@ static void decode_opc_special2_legacy(CPUMIPSState *env, DisasContext *ctx) gen_cl(ctx, op1, rd, rs); break; case OPC_SDBBP: - if (is_uhi(extract32(ctx->opcode, 6, 20))) { + if (is_uhi(ctx, extract32(ctx->opcode, 6, 20))) { ctx->base.is_jmp = DISAS_SEMIHOST; } else { /* diff --git a/target/nios2/nios2-semi.c b/target/nios2/nios2-semi.c index 55061bb..f76e858 100644 --- a/target/nios2/nios2-semi.c +++ b/target/nios2/nios2-semi.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/gdbstub.h" +#include "semihosting/syscalls.h" #include "semihosting/softmmu-uaccess.h" #include "qemu/log.h" @@ -42,65 +43,33 @@ #define HOSTED_ISATTY 12 #define HOSTED_SYSTEM 13 -static int translate_openflags(int flags) +static int host_to_gdb_errno(int err) { - int hf; - - if (flags & GDB_O_WRONLY) { - hf = O_WRONLY; - } else if (flags & GDB_O_RDWR) { - hf = O_RDWR; - } else { - hf = O_RDONLY; - } - - if (flags & GDB_O_APPEND) { - hf |= O_APPEND; - } - if (flags & GDB_O_CREAT) { - hf |= O_CREAT; - } - if (flags & GDB_O_TRUNC) { - hf |= O_TRUNC; - } - if (flags & GDB_O_EXCL) { - hf |= O_EXCL; - } - - return hf; -} - -static bool translate_stat(CPUNios2State *env, target_ulong addr, - struct stat *s) -{ - struct gdb_stat *p; - - p = lock_user(VERIFY_WRITE, addr, sizeof(struct gdb_stat), 0); - - if (!p) { - return false; +#define E(X) case E##X: return GDB_E##X + switch (err) { + E(PERM); + E(NOENT); + E(INTR); + E(BADF); + E(ACCES); + E(FAULT); + E(BUSY); + E(EXIST); + E(NODEV); + E(NOTDIR); + E(ISDIR); + E(INVAL); + E(NFILE); + E(MFILE); + E(FBIG); + E(NOSPC); + E(SPIPE); + E(ROFS); + E(NAMETOOLONG); + default: + return GDB_EUNKNOWN; } - p->gdb_st_dev = cpu_to_be32(s->st_dev); - p->gdb_st_ino = cpu_to_be32(s->st_ino); - p->gdb_st_mode = cpu_to_be32(s->st_mode); - p->gdb_st_nlink = cpu_to_be32(s->st_nlink); - p->gdb_st_uid = cpu_to_be32(s->st_uid); - p->gdb_st_gid = cpu_to_be32(s->st_gid); - p->gdb_st_rdev = cpu_to_be32(s->st_rdev); - p->gdb_st_size = cpu_to_be64(s->st_size); -#ifdef _WIN32 - /* Windows stat is missing some fields. */ - p->gdb_st_blksize = 0; - p->gdb_st_blocks = 0; -#else - p->gdb_st_blksize = cpu_to_be64(s->st_blksize); - p->gdb_st_blocks = cpu_to_be64(s->st_blocks); -#endif - p->gdb_st_atime = cpu_to_be32(s->st_atime); - p->gdb_st_mtime = cpu_to_be32(s->st_mtime); - p->gdb_st_ctime = cpu_to_be32(s->st_ctime); - unlock_user(p, addr, sizeof(struct gdb_stat)); - return true; +#undef E } static void nios2_semi_u32_cb(CPUState *cs, uint64_t ret, int err) @@ -110,7 +79,7 @@ static void nios2_semi_u32_cb(CPUState *cs, uint64_t ret, int err) target_ulong args = env->regs[R_ARG1]; if (put_user_u32(ret, args) || - put_user_u32(err, args + 4)) { + put_user_u32(host_to_gdb_errno(err), args + 4)) { /* * The nios2 semihosting ABI does not provide any way to report this * error to the guest, so the best we can do is log it in qemu. @@ -129,7 +98,7 @@ static void nios2_semi_u64_cb(CPUState *cs, uint64_t ret, int err) if (put_user_u32(ret >> 32, args) || put_user_u32(ret, args + 4) || - put_user_u32(err, args + 8)) { + put_user_u32(host_to_gdb_errno(err), args + 8)) { /* No way to report this via nios2 semihosting ABI; just log it */ qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value " "discarded because argument block not writable\n"); @@ -142,22 +111,22 @@ static void nios2_semi_u64_cb(CPUState *cs, uint64_t ret, int err) */ #define GET_ARG(n) do { \ if (get_user_ual(arg ## n, args + (n) * 4)) { \ - result = -1; \ - errno = EFAULT; \ goto failed; \ } \ } while (0) +#define GET_ARG64(n) do { \ + if (get_user_ual(arg ## n, args + (n) * 4)) { \ + goto failed64; \ + } \ +} while (0) + void do_nios2_semihosting(CPUNios2State *env) { CPUState *cs = env_cpu(env); int nr; uint32_t args; target_ulong arg0, arg1, arg2, arg3; - void *p; - void *q; - uint32_t len; - uint32_t result; nr = env->regs[R_ARG0]; args = env->regs[R_ARG1]; @@ -165,234 +134,98 @@ void do_nios2_semihosting(CPUNios2State *env) case HOSTED_EXIT: gdb_exit(env->regs[R_ARG0]); exit(env->regs[R_ARG0]); + case HOSTED_OPEN: GET_ARG(0); GET_ARG(1); GET_ARG(2); GET_ARG(3); - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_u32_cb, "open,%s,%x,%x", arg0, (int)arg1, - arg2, arg3); - return; - } else { - p = lock_user_string(arg0); - if (!p) { - result = -1; - errno = EFAULT; - } else { - result = open(p, translate_openflags(arg2), arg3); - unlock_user(p, arg0, 0); - } - } + semihost_sys_open(cs, nios2_semi_u32_cb, arg0, arg1, arg2, arg3); break; + case HOSTED_CLOSE: - { - /* Ignore attempts to close stdin/out/err. */ - GET_ARG(0); - int fd = arg0; - if (fd > 2) { - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_u32_cb, "close,%x", arg0); - return; - } else { - result = close(fd); - } - } else { - result = 0; - } - break; - } + GET_ARG(0); + semihost_sys_close(cs, nios2_semi_u32_cb, arg0); + break; + case HOSTED_READ: GET_ARG(0); GET_ARG(1); GET_ARG(2); - len = arg2; - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_u32_cb, "read,%x,%x,%x", - arg0, arg1, len); - return; - } else { - p = lock_user(VERIFY_WRITE, arg1, len, 0); - if (!p) { - result = -1; - errno = EFAULT; - } else { - result = read(arg0, p, len); - unlock_user(p, arg1, len); - } - } + semihost_sys_read(cs, nios2_semi_u32_cb, arg0, arg1, arg2); break; + case HOSTED_WRITE: GET_ARG(0); GET_ARG(1); GET_ARG(2); - len = arg2; - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_u32_cb, "write,%x,%x,%x", - arg0, arg1, len); - return; - } else { - p = lock_user(VERIFY_READ, arg1, len, 1); - if (!p) { - result = -1; - errno = EFAULT; - } else { - result = write(arg0, p, len); - unlock_user(p, arg0, 0); - } - } + semihost_sys_write(cs, nios2_semi_u32_cb, arg0, arg1, arg2); break; + case HOSTED_LSEEK: - { - uint64_t off; - GET_ARG(0); - GET_ARG(1); - GET_ARG(2); - GET_ARG(3); - off = (uint32_t)arg2 | ((uint64_t)arg1 << 32); - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_u64_cb, "lseek,%x,%lx,%x", - arg0, off, arg3); - } else { - off = lseek(arg0, off, arg3); - nios2_semi_u64_cb(cs, off, errno); - } - return; - } + GET_ARG64(0); + GET_ARG64(1); + GET_ARG64(2); + GET_ARG64(3); + semihost_sys_lseek(cs, nios2_semi_u64_cb, arg0, + deposit64(arg2, arg1, 32, 32), arg3); + break; + case HOSTED_RENAME: GET_ARG(0); GET_ARG(1); GET_ARG(2); GET_ARG(3); - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_u32_cb, "rename,%s,%s", - arg0, (int)arg1, arg2, (int)arg3); - return; - } else { - p = lock_user_string(arg0); - q = lock_user_string(arg2); - if (!p || !q) { - result = -1; - errno = EFAULT; - } else { - result = rename(p, q); - } - unlock_user(p, arg0, 0); - unlock_user(q, arg2, 0); - } + semihost_sys_rename(cs, nios2_semi_u32_cb, arg0, arg1, arg2, arg3); break; + case HOSTED_UNLINK: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_u32_cb, "unlink,%s", - arg0, (int)arg1); - return; - } else { - p = lock_user_string(arg0); - if (!p) { - result = -1; - errno = EFAULT; - } else { - result = unlink(p); - unlock_user(p, arg0, 0); - } - } + semihost_sys_remove(cs, nios2_semi_u32_cb, arg0, arg1); break; + case HOSTED_STAT: GET_ARG(0); GET_ARG(1); GET_ARG(2); - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_u32_cb, "stat,%s,%x", - arg0, (int)arg1, arg2); - return; - } else { - struct stat s; - p = lock_user_string(arg0); - if (!p) { - result = -1; - errno = EFAULT; - } else { - result = stat(p, &s); - unlock_user(p, arg0, 0); - } - if (result == 0 && !translate_stat(env, arg2, &s)) { - result = -1; - errno = EFAULT; - } - } + semihost_sys_stat(cs, nios2_semi_u32_cb, arg0, arg1, arg2); break; + case HOSTED_FSTAT: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_u32_cb, "fstat,%x,%x", - arg0, arg1); - return; - } else { - struct stat s; - result = fstat(arg0, &s); - if (result == 0 && !translate_stat(env, arg1, &s)) { - result = -1; - errno = EFAULT; - } - } + semihost_sys_fstat(cs, nios2_semi_u32_cb, arg0, arg1); break; + case HOSTED_GETTIMEOFDAY: - /* Only the tv parameter is used. tz is assumed NULL. */ GET_ARG(0); - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_u32_cb, "gettimeofday,%x,%x", - arg0, 0); - return; - } else { - struct gdb_timeval *p; - int64_t rt = g_get_real_time(); - p = lock_user(VERIFY_WRITE, arg0, sizeof(struct gdb_timeval), 0); - if (!p) { - result = -1; - errno = EFAULT; - } else { - result = 0; - p->tv_sec = cpu_to_be32(rt / G_USEC_PER_SEC); - p->tv_usec = cpu_to_be64(rt % G_USEC_PER_SEC); - unlock_user(p, arg0, sizeof(struct gdb_timeval)); - } - } + GET_ARG(1); + semihost_sys_gettimeofday(cs, nios2_semi_u32_cb, arg0, arg1); break; + case HOSTED_ISATTY: GET_ARG(0); - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_u32_cb, "isatty,%x", arg0); - return; - } else { - result = isatty(arg0); - } + semihost_sys_isatty(cs, nios2_semi_u32_cb, arg0); break; + case HOSTED_SYSTEM: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_u32_cb, "system,%s", - arg0, (int)arg1); - return; - } else { - p = lock_user_string(arg0); - if (!p) { - result = -1; - errno = EFAULT; - } else { - result = system(p); - unlock_user(p, arg0, 0); - } - } + semihost_sys_system(cs, nios2_semi_u32_cb, arg0, arg1); break; + default: qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: unsupported " "semihosting syscall %d\n", nr); - result = 0; + nios2_semi_u32_cb(cs, -1, ENOSYS); + break; + + failed: + nios2_semi_u32_cb(cs, -1, EFAULT); + break; + failed64: + nios2_semi_u64_cb(cs, -1, EFAULT); + break; } -failed: - nios2_semi_u32_cb(cs, result, errno); } diff --git a/target/nios2/translate.c b/target/nios2/translate.c index c588e8e..8dc0a32 100644 --- a/target/nios2/translate.c +++ b/target/nios2/translate.c @@ -817,8 +817,9 @@ static void gen_break(DisasContext *dc, uint32_t code, uint32_t flags) { #ifndef CONFIG_USER_ONLY /* The semihosting instruction is "break 1". */ + bool is_user = FIELD_EX32(dc->tb_flags, TBFLAGS, U); R_TYPE(instr, code); - if (semihosting_enabled() && instr.imm5 == 1) { + if (semihosting_enabled(is_user) && instr.imm5 == 1) { t_gen_helper_raise_exception(dc, EXCP_SEMIHOST); return; } diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 67e4c0e..278d163 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -1589,12 +1589,9 @@ void riscv_cpu_do_interrupt(CPUState *cs) target_ulong mtval2 = 0; if (cause == RISCV_EXCP_SEMIHOST) { - if (env->priv >= PRV_S) { - do_common_semihosting(cs); - env->pc += 4; - return; - } - cause = RISCV_EXCP_BREAKPOINT; + do_common_semihosting(cs); + env->pc += 4; + return; } if (!async) { diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc index 46f96ad..3281408 100644 --- a/target/riscv/insn_trans/trans_privileged.c.inc +++ b/target/riscv/insn_trans/trans_privileged.c.inc @@ -52,7 +52,8 @@ static bool trans_ebreak(DisasContext *ctx, arg_ebreak *a) * that no exception will be raised when fetching them. */ - if ((pre_addr & TARGET_PAGE_MASK) == (post_addr & TARGET_PAGE_MASK)) { + if (semihosting_enabled(ctx->mem_idx < PRV_S) && + (pre_addr & TARGET_PAGE_MASK) == (post_addr & TARGET_PAGE_MASK)) { pre = opcode_at(&ctx->base, pre_addr); ebreak = opcode_at(&ctx->base, ebreak_addr); post = opcode_at(&ctx->base, post_addr); diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 8925a44..db123da 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -28,6 +28,7 @@ #include "exec/translator.h" #include "exec/log.h" +#include "semihosting/semihost.h" #include "instmap.h" #include "internals.h" diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 8b864ef..bdd4690 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -2362,13 +2362,14 @@ static uint32_t test_exceptions_simcall(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { + bool is_semi = semihosting_enabled(dc->cring != 0); #ifdef CONFIG_USER_ONLY bool ill = true; #else /* Between RE.2 and RE.3 simcall opcode's become nop for the hardware. */ - bool ill = dc->config->hw_version <= 250002 && !semihosting_enabled(); + bool ill = dc->config->hw_version <= 250002 && !is_semi; #endif - if (ill || !semihosting_enabled()) { + if (ill || !is_semi) { qemu_log_mask(LOG_GUEST_ERROR, "SIMCALL but semihosting is disabled\n"); } return ill ? XTENSA_OP_ILL : 0; @@ -2378,7 +2379,7 @@ static void translate_simcall(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { #ifndef CONFIG_USER_ONLY - if (semihosting_enabled()) { + if (semihosting_enabled(dc->cring != 0)) { gen_helper_simcall(cpu_env); } #endif |