From c89a14ad2c2be24f786d80d33362279f5de61c74 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Apr 2022 20:48:41 -0700 Subject: [PATCH 01/60] semihosting: Move exec/softmmu-semi.h to semihosting/softmmu-uaccess.h We have a subdirectory for semihosting; move this file out of exec. Rename to emphasize the contents are a replacement for the functions in linux-user/bsd-user uaccess.c. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- .../{exec/softmmu-semi.h => semihosting/softmmu-uaccess.h} | 6 +++--- semihosting/arm-compat-semi.c | 2 +- target/m68k/m68k-semi.c | 2 +- target/mips/tcg/sysemu/mips-semi.c | 2 +- target/nios2/nios2-semi.c | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) rename include/{exec/softmmu-semi.h => semihosting/softmmu-uaccess.h} (95%) diff --git a/include/exec/softmmu-semi.h b/include/semihosting/softmmu-uaccess.h similarity index 95% rename from include/exec/softmmu-semi.h rename to include/semihosting/softmmu-uaccess.h index fbcae88f4b..e69e3c8548 100644 --- a/include/exec/softmmu-semi.h +++ b/include/semihosting/softmmu-uaccess.h @@ -7,8 +7,8 @@ * This code is licensed under the GPL */ -#ifndef SOFTMMU_SEMI_H -#define SOFTMMU_SEMI_H +#ifndef SEMIHOSTING_SOFTMMU_UACCESS_H +#define SEMIHOSTING_SOFTMMU_UACCESS_H #include "cpu.h" @@ -98,4 +98,4 @@ static void softmmu_unlock_user(CPUArchState *env, void *p, target_ulong addr, } #define unlock_user(s, args, len) softmmu_unlock_user(env, s, args, len) -#endif +#endif /* SEMIHOSTING_SOFTMMU_UACCESS_H */ diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index b6ddaf863a..1033e751ef 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -370,7 +370,7 @@ static GuestFD *get_guestfd(int guestfd) #ifndef CONFIG_USER_ONLY static target_ulong syscall_err; -#include "exec/softmmu-semi.h" +#include "semihosting/softmmu-uaccess.h" #endif static inline uint32_t set_swi_errno(CPUState *cs, uint32_t code) diff --git a/target/m68k/m68k-semi.c b/target/m68k/m68k-semi.c index 37343d47e2..a31db38fc3 100644 --- a/target/m68k/m68k-semi.c +++ b/target/m68k/m68k-semi.c @@ -25,7 +25,7 @@ #include "qemu.h" #define SEMIHOSTING_HEAP_SIZE (128 * 1024 * 1024) #else -#include "exec/softmmu-semi.h" +#include "semihosting/softmmu-uaccess.h" #include "hw/boards.h" #endif #include "qemu/log.h" diff --git a/target/mips/tcg/sysemu/mips-semi.c b/target/mips/tcg/sysemu/mips-semi.c index b4a383ae90..6d6296e709 100644 --- a/target/mips/tcg/sysemu/mips-semi.c +++ b/target/mips/tcg/sysemu/mips-semi.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "qemu/log.h" #include "exec/helper-proto.h" -#include "exec/softmmu-semi.h" +#include "semihosting/softmmu-uaccess.h" #include "semihosting/semihost.h" #include "semihosting/console.h" diff --git a/target/nios2/nios2-semi.c b/target/nios2/nios2-semi.c index ec88474a73..373e6b9436 100644 --- a/target/nios2/nios2-semi.c +++ b/target/nios2/nios2-semi.c @@ -28,7 +28,7 @@ #if defined(CONFIG_USER_ONLY) #include "qemu.h" #else -#include "exec/softmmu-semi.h" +#include "semihosting/softmmu-uaccess.h" #endif #include "qemu/log.h" From 8ce5c64499d3468495c1fa251bf37bf369db2574 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Apr 2022 20:01:24 -0700 Subject: [PATCH 02/60] semihosting: Return failure from softmmu-uaccess.h functions We were reporting unconditional success for these functions; pass on any failure from cpu_memory_rw_debug. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- include/semihosting/softmmu-uaccess.h | 91 ++++++++++++--------------- 1 file changed, 39 insertions(+), 52 deletions(-) diff --git a/include/semihosting/softmmu-uaccess.h b/include/semihosting/softmmu-uaccess.h index e69e3c8548..5246a91570 100644 --- a/include/semihosting/softmmu-uaccess.h +++ b/include/semihosting/softmmu-uaccess.h @@ -12,82 +12,69 @@ #include "cpu.h" -static inline uint64_t softmmu_tget64(CPUArchState *env, target_ulong addr) -{ - uint64_t val; +#define get_user_u64(val, addr) \ + ({ uint64_t val_ = 0; \ + int ret_ = cpu_memory_rw_debug(env_cpu(env), (addr), \ + &val_, sizeof(val_), 0); \ + (val) = tswap64(val_); ret_; }) - cpu_memory_rw_debug(env_cpu(env), addr, (uint8_t *)&val, 8, 0); - return tswap64(val); -} +#define get_user_u32(val, addr) \ + ({ uint32_t val_ = 0; \ + int ret_ = cpu_memory_rw_debug(env_cpu(env), (addr), \ + &val_, sizeof(val_), 0); \ + (val) = tswap32(val_); ret_; }) -static inline uint32_t softmmu_tget32(CPUArchState *env, target_ulong addr) -{ - uint32_t val; +#define get_user_u8(val, addr) \ + ({ uint8_t val_ = 0; \ + int ret_ = cpu_memory_rw_debug(env_cpu(env), (addr), \ + &val_, sizeof(val_), 0); \ + (val) = val_; ret_; }) - cpu_memory_rw_debug(env_cpu(env), addr, (uint8_t *)&val, 4, 0); - return tswap32(val); -} - -static inline uint32_t softmmu_tget8(CPUArchState *env, target_ulong addr) -{ - uint8_t val; - - cpu_memory_rw_debug(env_cpu(env), addr, &val, 1, 0); - return val; -} - -#define get_user_u64(arg, p) ({ arg = softmmu_tget64(env, p); 0; }) -#define get_user_u32(arg, p) ({ arg = softmmu_tget32(env, p) ; 0; }) -#define get_user_u8(arg, p) ({ arg = softmmu_tget8(env, p) ; 0; }) #define get_user_ual(arg, p) get_user_u32(arg, p) -static inline void softmmu_tput64(CPUArchState *env, - target_ulong addr, uint64_t val) -{ - val = tswap64(val); - cpu_memory_rw_debug(env_cpu(env), addr, (uint8_t *)&val, 8, 1); -} +#define put_user_u64(val, addr) \ + ({ uint64_t val_ = tswap64(val); \ + cpu_memory_rw_debug(env_cpu(env), (addr), &val_, sizeof(val_), 1); }) + +#define put_user_u32(val, addr) \ + ({ uint32_t val_ = tswap32(val); \ + cpu_memory_rw_debug(env_cpu(env), (addr), &val_, sizeof(val_), 1); }) -static inline void softmmu_tput32(CPUArchState *env, - target_ulong addr, uint32_t val) -{ - val = tswap32(val); - cpu_memory_rw_debug(env_cpu(env), addr, (uint8_t *)&val, 4, 1); -} -#define put_user_u64(arg, p) ({ softmmu_tput64(env, p, arg) ; 0; }) -#define put_user_u32(arg, p) ({ softmmu_tput32(env, p, arg) ; 0; }) #define put_user_ual(arg, p) put_user_u32(arg, p) -static void *softmmu_lock_user(CPUArchState *env, - target_ulong addr, target_ulong len, int copy) +static void *softmmu_lock_user(CPUArchState *env, target_ulong addr, + target_ulong len, bool copy) { - uint8_t *p; - /* TODO: Make this something that isn't fixed size. */ - p = malloc(len); + void *p = malloc(len); if (p && copy) { - cpu_memory_rw_debug(env_cpu(env), addr, p, len, 0); + if (cpu_memory_rw_debug(env_cpu(env), addr, p, len, 0)) { + free(p); + p = NULL; + } } return p; } #define lock_user(type, p, len, copy) softmmu_lock_user(env, p, len, copy) + static char *softmmu_lock_user_string(CPUArchState *env, target_ulong addr) { - char *p; - char *s; - uint8_t c; /* TODO: Make this something that isn't fixed size. */ - s = p = malloc(1024); + char *s = malloc(1024); + size_t len = 0; + if (!s) { return NULL; } do { - cpu_memory_rw_debug(env_cpu(env), addr, &c, 1, 0); - addr++; - *(p++) = c; - } while (c); + if (cpu_memory_rw_debug(env_cpu(env), addr++, s + len, 1, 0)) { + free(s); + return NULL; + } + } while (s[len++]); return s; } #define lock_user_string(p) softmmu_lock_user_string(env, p) + static void softmmu_unlock_user(CPUArchState *env, void *p, target_ulong addr, target_ulong len) { From 259739ce74300ae9e5df7c8c3a8a672341af5546 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Apr 2022 18:32:35 -0700 Subject: [PATCH 03/60] semihosting: Improve condition for config.c and console.c While CONFIG_SEMIHOSTING is currently only set for softmmu, this will not continue to be true. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- semihosting/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/semihosting/meson.build b/semihosting/meson.build index ea8090abe3..4344e43fb9 100644 --- a/semihosting/meson.build +++ b/semihosting/meson.build @@ -1,4 +1,4 @@ -specific_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( +specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SOFTMMU'], if_true: files( 'config.c', 'console.c', )) From 0a9221810ce08fd5b0c4cff6055e640d4bd6876d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Apr 2022 20:44:17 -0700 Subject: [PATCH 04/60] semihosting: Move softmmu-uaccess.h functions out of line Rather that static (and not even inline) functions within a header, move the functions to semihosting/uaccess.c. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- include/semihosting/softmmu-uaccess.h | 42 +++------------------- semihosting/meson.build | 1 + semihosting/uaccess.c | 51 +++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 37 deletions(-) create mode 100644 semihosting/uaccess.c diff --git a/include/semihosting/softmmu-uaccess.h b/include/semihosting/softmmu-uaccess.h index 5246a91570..03300376d3 100644 --- a/include/semihosting/softmmu-uaccess.h +++ b/include/semihosting/softmmu-uaccess.h @@ -42,47 +42,15 @@ #define put_user_ual(arg, p) put_user_u32(arg, p) -static void *softmmu_lock_user(CPUArchState *env, target_ulong addr, - target_ulong len, bool copy) -{ - void *p = malloc(len); - if (p && copy) { - if (cpu_memory_rw_debug(env_cpu(env), addr, p, len, 0)) { - free(p); - p = NULL; - } - } - return p; -} +void *softmmu_lock_user(CPUArchState *env, target_ulong addr, + target_ulong len, bool copy); #define lock_user(type, p, len, copy) softmmu_lock_user(env, p, len, copy) -static char *softmmu_lock_user_string(CPUArchState *env, target_ulong addr) -{ - /* TODO: Make this something that isn't fixed size. */ - char *s = malloc(1024); - size_t len = 0; - - if (!s) { - return NULL; - } - do { - if (cpu_memory_rw_debug(env_cpu(env), addr++, s + len, 1, 0)) { - free(s); - return NULL; - } - } while (s[len++]); - return s; -} +char *softmmu_lock_user_string(CPUArchState *env, target_ulong addr); #define lock_user_string(p) softmmu_lock_user_string(env, p) -static void softmmu_unlock_user(CPUArchState *env, void *p, target_ulong addr, - target_ulong len) -{ - if (len) { - cpu_memory_rw_debug(env_cpu(env), addr, p, len, 1); - } - free(p); -} +void softmmu_unlock_user(CPUArchState *env, void *p, + target_ulong addr, target_ulong len); #define unlock_user(s, args, len) softmmu_unlock_user(env, s, args, len) #endif /* SEMIHOSTING_SOFTMMU_UACCESS_H */ diff --git a/semihosting/meson.build b/semihosting/meson.build index 4344e43fb9..10b3b99921 100644 --- a/semihosting/meson.build +++ b/semihosting/meson.build @@ -1,6 +1,7 @@ specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SOFTMMU'], if_true: files( 'config.c', 'console.c', + 'uaccess.c', )) specific_ss.add(when: ['CONFIG_ARM_COMPATIBLE_SEMIHOSTING'], diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c new file mode 100644 index 0000000000..0d3b32b75d --- /dev/null +++ b/semihosting/uaccess.c @@ -0,0 +1,51 @@ +/* + * Helper routines to provide target memory access for semihosting + * syscalls in system emulation mode. + * + * Copyright (c) 2007 CodeSourcery. + * + * This code is licensed under the GPL + */ + +#include "qemu/osdep.h" +#include "semihosting/softmmu-uaccess.h" + +void *softmmu_lock_user(CPUArchState *env, target_ulong addr, + target_ulong len, bool copy) +{ + void *p = malloc(len); + if (p && copy) { + if (cpu_memory_rw_debug(env_cpu(env), addr, p, len, 0)) { + free(p); + p = NULL; + } + } + return p; +} + +char *softmmu_lock_user_string(CPUArchState *env, target_ulong addr) +{ + /* TODO: Make this something that isn't fixed size. */ + char *s = malloc(1024); + size_t len = 0; + + if (!s) { + return NULL; + } + do { + if (cpu_memory_rw_debug(env_cpu(env), addr++, s + len, 1, 0)) { + free(s); + return NULL; + } + } while (s[len++]); + return s; +} + +void softmmu_unlock_user(CPUArchState *env, void *p, + target_ulong addr, target_ulong len) +{ + if (len) { + cpu_memory_rw_debug(env_cpu(env), addr, p, len, 1); + } + free(p); +} From b89350e83044ee6e6e6628dd99845f3d1f53bd52 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 20 May 2022 22:12:08 -0700 Subject: [PATCH 05/60] accel/stubs: Add tcg stub for probe_access_flags Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- accel/stubs/tcg-stub.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c index ea4a0dd2fb..6ce8a34228 100644 --- a/accel/stubs/tcg-stub.c +++ b/accel/stubs/tcg-stub.c @@ -21,6 +21,13 @@ void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) { } +int probe_access_flags(CPUArchState *env, target_ulong addr, + MMUAccessType access_type, int mmu_idx, + bool nonfault, void **phost, uintptr_t retaddr) +{ + g_assert_not_reached(); +} + void *probe_access(CPUArchState *env, target_ulong addr, int size, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { From 5f9ca6f3c5111fadb0b1e76755ceaf738a98db4c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Apr 2022 21:03:12 -0700 Subject: [PATCH 06/60] semihosting: Add target_strlen for softmmu-uaccess.h Mirror the interface of the user-only function of the same name. Use probe_access_flags for the common case of ram, and cpu_memory_rw_debug for the uncommon case of mmio. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- v3: Use probe_access_flags (pmm) --- include/semihosting/softmmu-uaccess.h | 3 ++ semihosting/uaccess.c | 49 +++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/include/semihosting/softmmu-uaccess.h b/include/semihosting/softmmu-uaccess.h index 03300376d3..4f08dfc098 100644 --- a/include/semihosting/softmmu-uaccess.h +++ b/include/semihosting/softmmu-uaccess.h @@ -53,4 +53,7 @@ void softmmu_unlock_user(CPUArchState *env, void *p, target_ulong addr, target_ulong len); #define unlock_user(s, args, len) softmmu_unlock_user(env, s, args, len) +ssize_t softmmu_strlen_user(CPUArchState *env, target_ulong addr); +#define target_strlen(p) softmmu_strlen_user(env, p) + #endif /* SEMIHOSTING_SOFTMMU_UACCESS_H */ diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c index 0d3b32b75d..d6997e3c65 100644 --- a/semihosting/uaccess.c +++ b/semihosting/uaccess.c @@ -8,6 +8,7 @@ */ #include "qemu/osdep.h" +#include "exec/exec-all.h" #include "semihosting/softmmu-uaccess.h" void *softmmu_lock_user(CPUArchState *env, target_ulong addr, @@ -23,6 +24,54 @@ void *softmmu_lock_user(CPUArchState *env, target_ulong addr, return p; } +ssize_t softmmu_strlen_user(CPUArchState *env, target_ulong addr) +{ + int mmu_idx = cpu_mmu_index(env, false); + size_t len = 0; + + while (1) { + size_t left_in_page; + int flags; + void *h; + + /* Find the number of bytes remaining in the page. */ + left_in_page = TARGET_PAGE_SIZE - (addr & ~TARGET_PAGE_MASK); + + flags = probe_access_flags(env, addr, MMU_DATA_LOAD, + mmu_idx, true, &h, 0); + if (flags & TLB_INVALID_MASK) { + return -1; + } + if (flags & TLB_MMIO) { + do { + uint8_t c; + if (cpu_memory_rw_debug(env_cpu(env), addr, &c, 1, 0)) { + return -1; + } + if (c == 0) { + return len; + } + addr++; + len++; + if (len > INT32_MAX) { + return -1; + } + } while (--left_in_page != 0); + } else { + char *p = memchr(h, 0, left_in_page); + if (p) { + len += p - (char *)h; + return len <= INT32_MAX ? (ssize_t)len : -1; + } + addr += left_in_page; + len += left_in_page; + if (len > INT32_MAX) { + return -1; + } + } + } +} + char *softmmu_lock_user_string(CPUArchState *env, target_ulong addr) { /* TODO: Make this something that isn't fixed size. */ From 3d5e2b4f26e077e9a8fd94659a1ce2dd49c134b7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Apr 2022 21:06:58 -0700 Subject: [PATCH 07/60] semihosting: Simplify softmmu_lock_user_string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are not currently bounding the search to the 1024 bytes that we allocated, possibly overrunning the buffer. Use softmmu_strlen_user to find the length and allocate the correct size from the beginning. Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- semihosting/uaccess.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c index d6997e3c65..8018828069 100644 --- a/semihosting/uaccess.c +++ b/semihosting/uaccess.c @@ -74,20 +74,11 @@ ssize_t softmmu_strlen_user(CPUArchState *env, target_ulong addr) char *softmmu_lock_user_string(CPUArchState *env, target_ulong addr) { - /* TODO: Make this something that isn't fixed size. */ - char *s = malloc(1024); - size_t len = 0; - - if (!s) { + ssize_t len = softmmu_strlen_user(env, addr); + if (len < 0) { return NULL; } - do { - if (cpu_memory_rw_debug(env_cpu(env), addr++, s + len, 1, 0)) { - free(s); - return NULL; - } - } while (s[len++]); - return s; + return softmmu_lock_user(env, addr, len + 1, true); } void softmmu_unlock_user(CPUArchState *env, void *p, From 1c6ff7205bff49870dc3511f237b3ad90da5f5f7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Apr 2022 21:38:02 -0700 Subject: [PATCH 08/60] semihosting: Split out guestfd.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In arm-compat-semi.c, we have more advanced treatment of guest file descriptors than we do in other implementations. Split out GuestFD and related functions to a new file so that they can be shared. Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- configs/targets/aarch64-linux-user.mak | 1 + configs/targets/aarch64_be-linux-user.mak | 1 + configs/targets/arm-linux-user.mak | 1 + configs/targets/armeb-linux-user.mak | 1 + configs/targets/riscv32-linux-user.mak | 1 + configs/targets/riscv64-linux-user.mak | 1 + include/semihosting/guestfd.h | 83 +++++++++++ semihosting/arm-compat-semi.c | 164 +++------------------- semihosting/guestfd.c | 118 ++++++++++++++++ semihosting/meson.build | 4 + 10 files changed, 233 insertions(+), 142 deletions(-) create mode 100644 include/semihosting/guestfd.h create mode 100644 semihosting/guestfd.c diff --git a/configs/targets/aarch64-linux-user.mak b/configs/targets/aarch64-linux-user.mak index d0c603c54e..db552f1839 100644 --- a/configs/targets/aarch64-linux-user.mak +++ b/configs/targets/aarch64-linux-user.mak @@ -2,4 +2,5 @@ TARGET_ARCH=aarch64 TARGET_BASE_ARCH=arm TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml TARGET_HAS_BFLT=y +CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/aarch64_be-linux-user.mak b/configs/targets/aarch64_be-linux-user.mak index 7794424745..dc78044fb1 100644 --- a/configs/targets/aarch64_be-linux-user.mak +++ b/configs/targets/aarch64_be-linux-user.mak @@ -3,4 +3,5 @@ TARGET_BASE_ARCH=arm TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml TARGET_HAS_BFLT=y +CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/arm-linux-user.mak b/configs/targets/arm-linux-user.mak index 3e10d6b15d..7f5d65794c 100644 --- a/configs/targets/arm-linux-user.mak +++ b/configs/targets/arm-linux-user.mak @@ -3,4 +3,5 @@ TARGET_SYSTBL_ABI=common,oabi TARGET_SYSTBL=syscall.tbl TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml TARGET_HAS_BFLT=y +CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/armeb-linux-user.mak b/configs/targets/armeb-linux-user.mak index a249cc2e29..943d0d87bf 100644 --- a/configs/targets/armeb-linux-user.mak +++ b/configs/targets/armeb-linux-user.mak @@ -4,4 +4,5 @@ TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml TARGET_HAS_BFLT=y +CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/riscv32-linux-user.mak b/configs/targets/riscv32-linux-user.mak index bd2f1fd497..9761618e67 100644 --- a/configs/targets/riscv32-linux-user.mak +++ b/configs/targets/riscv32-linux-user.mak @@ -2,4 +2,5 @@ TARGET_ARCH=riscv32 TARGET_BASE_ARCH=riscv TARGET_ABI_DIR=riscv TARGET_XML_FILES= gdb-xml/riscv-32bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-32bit-virtual.xml +CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/riscv64-linux-user.mak b/configs/targets/riscv64-linux-user.mak index 4aca7662ce..cfd1fd382f 100644 --- a/configs/targets/riscv64-linux-user.mak +++ b/configs/targets/riscv64-linux-user.mak @@ -2,4 +2,5 @@ TARGET_ARCH=riscv64 TARGET_BASE_ARCH=riscv TARGET_ABI_DIR=riscv TARGET_XML_FILES= gdb-xml/riscv-64bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-64bit-virtual.xml +CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/include/semihosting/guestfd.h b/include/semihosting/guestfd.h new file mode 100644 index 0000000000..ef268abe85 --- /dev/null +++ b/include/semihosting/guestfd.h @@ -0,0 +1,83 @@ +/* + * Hosted file support for semihosting syscalls. + * + * Copyright (c) 2005, 2007 CodeSourcery. + * Copyright (c) 2019 Linaro + * Copyright © 2020 by Keith Packard + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SEMIHOSTING_GUESTFD_H +#define SEMIHOSTING_GUESTFD_H + +typedef enum GuestFDType { + GuestFDUnused = 0, + GuestFDHost = 1, + GuestFDGDB = 2, + GuestFDStatic = 3, +} GuestFDType; + +/* + * Guest file descriptors are integer indexes into an array of + * these structures (we will dynamically resize as necessary). + */ +typedef struct GuestFD { + GuestFDType type; + union { + int hostfd; + struct { + const uint8_t *data; + size_t len; + size_t off; + } staticfile; + }; +} GuestFD; + +/** + * alloc_guestfd: + * + * Allocate an unused GuestFD index. The associated guestfd index + * will still be GuestFDUnused until it is initialized. + */ +int alloc_guestfd(void); + +/** + * dealloc_guestfd: + * @guestfd: GuestFD index + * + * Deallocate a GuestFD index. The associated GuestFD structure + * will be recycled for a subsequent allocation. + */ +void dealloc_guestfd(int guestfd); + +/** + * get_guestfd: + * @guestfd: GuestFD index + * + * Return the GuestFD structure associated with an initialized @guestfd, + * or NULL if it has not been allocated, or hasn't been initialized. + */ +GuestFD *get_guestfd(int guestfd); + +/** + * associate_guestfd: + * @guestfd: GuestFD index + * @hostfd: host file descriptor + * + * Initialize the GuestFD for @guestfd to GuestFDHost using @hostfd. + */ +void associate_guestfd(int guestfd, int hostfd); + +/** + * staticfile_guestfd: + * @guestfd: GuestFD index + * @data: data to be read + * @len: length of @data + * + * Initialize the GuestFD for @guestfd to GuestFDStatic. + * The @len bytes at @data will be returned to the guest on reads. + */ +void staticfile_guestfd(int guestfd, const uint8_t *data, size_t len); + +#endif /* SEMIHOSTING_GUESTFD_H */ diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 1033e751ef..2fa7f23d8b 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -32,12 +32,13 @@ */ #include "qemu/osdep.h" - #include "semihosting/semihost.h" #include "semihosting/console.h" #include "semihosting/common-semi.h" +#include "semihosting/guestfd.h" #include "qemu/timer.h" #include "exec/gdbstub.h" + #ifdef CONFIG_USER_ONLY #include "qemu.h" @@ -123,27 +124,6 @@ static int open_modeflags[12] = { O_RDWR | O_CREAT | O_APPEND | O_BINARY }; -typedef enum GuestFDType { - GuestFDUnused = 0, - GuestFDHost = 1, - GuestFDGDB = 2, - GuestFDFeatureFile = 3, -} GuestFDType; - -/* - * Guest file descriptors are integer indexes into an array of - * these structures (we will dynamically resize as necessary). - */ -typedef struct GuestFD { - GuestFDType type; - union { - int hostfd; - target_ulong featurefile_offset; - }; -} GuestFD; - -static GArray *guestfd_array; - #ifndef CONFIG_USER_ONLY /** @@ -268,98 +248,6 @@ common_semi_sys_exit_extended(CPUState *cs, int nr) #endif -/* - * Allocate a new guest file descriptor and return it; if we - * couldn't allocate a new fd then return -1. - * This is a fairly simplistic implementation because we don't - * expect that most semihosting guest programs will make very - * heavy use of opening and closing fds. - */ -static int alloc_guestfd(void) -{ - guint i; - - if (!guestfd_array) { - /* New entries zero-initialized, i.e. type GuestFDUnused */ - guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD)); - } - - /* SYS_OPEN should return nonzero handle on success. Start guestfd from 1 */ - for (i = 1; i < guestfd_array->len; i++) { - GuestFD *gf = &g_array_index(guestfd_array, GuestFD, i); - - if (gf->type == GuestFDUnused) { - return i; - } - } - - /* All elements already in use: expand the array */ - g_array_set_size(guestfd_array, i + 1); - return i; -} - -/* - * Look up the guestfd in the data structure; return NULL - * for out of bounds, but don't check whether the slot is unused. - * This is used internally by the other guestfd functions. - */ -static GuestFD *do_get_guestfd(int guestfd) -{ - if (!guestfd_array) { - return NULL; - } - - if (guestfd <= 0 || guestfd >= guestfd_array->len) { - return NULL; - } - - return &g_array_index(guestfd_array, GuestFD, guestfd); -} - -/* - * Associate the specified guest fd (which must have been - * allocated via alloc_fd() and not previously used) with - * the specified host/gdb fd. - */ -static void associate_guestfd(int guestfd, int hostfd) -{ - GuestFD *gf = do_get_guestfd(guestfd); - - assert(gf); - gf->type = use_gdb_syscalls() ? GuestFDGDB : GuestFDHost; - gf->hostfd = hostfd; -} - -/* - * Deallocate the specified guest file descriptor. This doesn't - * close the host fd, it merely undoes the work of alloc_fd(). - */ -static void dealloc_guestfd(int guestfd) -{ - GuestFD *gf = do_get_guestfd(guestfd); - - assert(gf); - gf->type = GuestFDUnused; -} - -/* - * Given a guest file descriptor, get the associated struct. - * If the fd is not valid, return NULL. This is the function - * used by the various semihosting calls to validate a handle - * from the guest. - * Note: calling alloc_guestfd() or dealloc_guestfd() will - * invalidate any GuestFD* obtained by calling this function. - */ -static GuestFD *get_guestfd(int guestfd) -{ - GuestFD *gf = do_get_guestfd(guestfd); - - if (!gf || gf->type == GuestFDUnused) { - return NULL; - } - return gf; -} - /* * 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 @@ -665,22 +553,13 @@ static const uint8_t featurefile_data[] = { SH_EXT_EXIT_EXTENDED | SH_EXT_STDOUT_STDERR, /* Feature byte 0 */ }; -static void init_featurefile_guestfd(int guestfd) -{ - GuestFD *gf = do_get_guestfd(guestfd); - - assert(gf); - gf->type = GuestFDFeatureFile; - gf->featurefile_offset = 0; -} - -static uint32_t featurefile_closefn(CPUState *cs, GuestFD *gf) +static uint32_t staticfile_closefn(CPUState *cs, GuestFD *gf) { /* Nothing to do */ return 0; } -static uint32_t featurefile_writefn(CPUState *cs, GuestFD *gf, +static uint32_t staticfile_writefn(CPUState *cs, GuestFD *gf, target_ulong buf, uint32_t len) { /* This fd can never be open for writing */ @@ -689,7 +568,7 @@ static uint32_t featurefile_writefn(CPUState *cs, GuestFD *gf, return set_swi_errno(cs, -1); } -static uint32_t featurefile_readfn(CPUState *cs, GuestFD *gf, +static uint32_t staticfile_readfn(CPUState *cs, GuestFD *gf, target_ulong buf, uint32_t len) { CPUArchState *env = cs->env_ptr; @@ -703,11 +582,11 @@ static uint32_t featurefile_readfn(CPUState *cs, GuestFD *gf, } for (i = 0; i < len; i++) { - if (gf->featurefile_offset >= sizeof(featurefile_data)) { + if (gf->staticfile.off >= gf->staticfile.len) { break; } - s[i] = featurefile_data[gf->featurefile_offset]; - gf->featurefile_offset++; + s[i] = gf->staticfile.data[gf->staticfile.off]; + gf->staticfile.off++; } unlock_user(s, buf, len); @@ -716,21 +595,21 @@ static uint32_t featurefile_readfn(CPUState *cs, GuestFD *gf, return len - i; } -static uint32_t featurefile_isattyfn(CPUState *cs, GuestFD *gf) +static uint32_t staticfile_isattyfn(CPUState *cs, GuestFD *gf) { return 0; } -static uint32_t featurefile_seekfn(CPUState *cs, GuestFD *gf, +static uint32_t staticfile_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset) { - gf->featurefile_offset = offset; + gf->staticfile.off = offset; return 0; } -static uint32_t featurefile_flenfn(CPUState *cs, GuestFD *gf) +static uint32_t staticfile_flenfn(CPUState *cs, GuestFD *gf) { - return sizeof(featurefile_data); + return gf->staticfile.len; } typedef struct GuestFDFunctions { @@ -759,13 +638,13 @@ static const GuestFDFunctions guestfd_fns[] = { .seekfn = gdb_seekfn, .flenfn = gdb_flenfn, }, - [GuestFDFeatureFile] = { - .closefn = featurefile_closefn, - .writefn = featurefile_writefn, - .readfn = featurefile_readfn, - .isattyfn = featurefile_isattyfn, - .seekfn = featurefile_seekfn, - .flenfn = featurefile_flenfn, + [GuestFDStatic] = { + .closefn = staticfile_closefn, + .writefn = staticfile_writefn, + .readfn = staticfile_readfn, + .isattyfn = staticfile_isattyfn, + .seekfn = staticfile_seekfn, + .flenfn = staticfile_flenfn, }, }; @@ -886,7 +765,8 @@ target_ulong do_common_semihosting(CPUState *cs) errno = EACCES; return set_swi_errno(cs, -1); } - init_featurefile_guestfd(guestfd); + staticfile_guestfd(guestfd, featurefile_data, + sizeof(featurefile_data)); return guestfd; } diff --git a/semihosting/guestfd.c b/semihosting/guestfd.c new file mode 100644 index 0000000000..b6405f5663 --- /dev/null +++ b/semihosting/guestfd.c @@ -0,0 +1,118 @@ +/* + * Hosted file support for semihosting syscalls. + * + * Copyright (c) 2005, 2007 CodeSourcery. + * Copyright (c) 2019 Linaro + * Copyright © 2020 by Keith Packard + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "exec/gdbstub.h" +#include "semihosting/guestfd.h" + +static GArray *guestfd_array; + +/* + * Allocate a new guest file descriptor and return it; if we + * couldn't allocate a new fd then return -1. + * This is a fairly simplistic implementation because we don't + * expect that most semihosting guest programs will make very + * heavy use of opening and closing fds. + */ +int alloc_guestfd(void) +{ + guint i; + + if (!guestfd_array) { + /* New entries zero-initialized, i.e. type GuestFDUnused */ + guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD)); + } + + /* SYS_OPEN should return nonzero handle on success. Start guestfd from 1 */ + for (i = 1; i < guestfd_array->len; i++) { + GuestFD *gf = &g_array_index(guestfd_array, GuestFD, i); + + if (gf->type == GuestFDUnused) { + return i; + } + } + + /* All elements already in use: expand the array */ + g_array_set_size(guestfd_array, i + 1); + return i; +} + +/* + * Look up the guestfd in the data structure; return NULL + * for out of bounds, but don't check whether the slot is unused. + * This is used internally by the other guestfd functions. + */ +static GuestFD *do_get_guestfd(int guestfd) +{ + if (!guestfd_array) { + return NULL; + } + + if (guestfd <= 0 || guestfd >= guestfd_array->len) { + return NULL; + } + + return &g_array_index(guestfd_array, GuestFD, guestfd); +} + +/* + * Given a guest file descriptor, get the associated struct. + * If the fd is not valid, return NULL. This is the function + * used by the various semihosting calls to validate a handle + * from the guest. + * Note: calling alloc_guestfd() or dealloc_guestfd() will + * invalidate any GuestFD* obtained by calling this function. + */ +GuestFD *get_guestfd(int guestfd) +{ + GuestFD *gf = do_get_guestfd(guestfd); + + if (!gf || gf->type == GuestFDUnused) { + return NULL; + } + return gf; +} + +/* + * Associate the specified guest fd (which must have been + * allocated via alloc_fd() and not previously used) with + * the specified host/gdb fd. + */ +void associate_guestfd(int guestfd, int hostfd) +{ + GuestFD *gf = do_get_guestfd(guestfd); + + assert(gf); + gf->type = use_gdb_syscalls() ? GuestFDGDB : GuestFDHost; + gf->hostfd = hostfd; +} + +void staticfile_guestfd(int guestfd, const uint8_t *data, size_t len) +{ + GuestFD *gf = do_get_guestfd(guestfd); + + assert(gf); + gf->type = GuestFDStatic; + gf->staticfile.data = data; + gf->staticfile.len = len; + gf->staticfile.off = 0; +} + +/* + * Deallocate the specified guest file descriptor. This doesn't + * close the host fd, it merely undoes the work of alloc_fd(). + */ +void dealloc_guestfd(int guestfd) +{ + GuestFD *gf = do_get_guestfd(guestfd); + + assert(gf); + gf->type = GuestFDUnused; +} diff --git a/semihosting/meson.build b/semihosting/meson.build index 10b3b99921..d2c1c37bfd 100644 --- a/semihosting/meson.build +++ b/semihosting/meson.build @@ -1,3 +1,7 @@ +specific_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( + 'guestfd.c', +)) + specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SOFTMMU'], if_true: files( 'config.c', 'console.c', From 5aadd1829905aace2a1201ddb8ac9b7f18d104fb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 16 May 2022 19:25:19 -0700 Subject: [PATCH 09/60] semihosting: Inline set_swi_errno into common_semi_cb Do not store 'err' into errno only to read it back immediately. Use 'ret' for the return value, not 'reg0'. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- semihosting/arm-compat-semi.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 2fa7f23d8b..9d1f13ea8b 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -290,28 +290,29 @@ static target_ulong common_semi_syscall_len; static void common_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) { - target_ulong reg0 = common_semi_arg(cs, 0); - if (ret == (target_ulong)-1) { - errno = err; - set_swi_errno(cs, -1); - reg0 = ret; +#ifdef CONFIG_USER_ONLY + TaskState *ts = cs->opaque; + ts->swi_errno = err; +#else + syscall_err = err; +#endif } else { /* Fixup syscalls that use nonstardard return conventions. */ + target_ulong reg0 = common_semi_arg(cs, 0); switch (reg0) { case TARGET_SYS_WRITE: case TARGET_SYS_READ: - reg0 = common_semi_syscall_len - ret; + ret = common_semi_syscall_len - ret; break; case TARGET_SYS_SEEK: - reg0 = 0; + ret = 0; break; default: - reg0 = ret; break; } } - common_semi_set_ret(cs, reg0); + common_semi_set_ret(cs, ret); } static target_ulong common_semi_flen_buf(CPUState *cs) From 709fe27b189aa86c801b9bd655f9267fec17d0d0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Apr 2022 12:32:24 -0700 Subject: [PATCH 10/60] semihosting: Adjust error checking in common_semi_cb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The err parameter is non-zero if and only if an error occured. Use this instead of ret == -1 for determining if we need to update the saved errno. This fixes the errno setting of SYS_ISTTY, which returns 0 on error, not -1. Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- semihosting/arm-compat-semi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 9d1f13ea8b..88d6bdeaf2 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -290,7 +290,7 @@ static target_ulong common_semi_syscall_len; static void common_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) { - if (ret == (target_ulong)-1) { + if (err) { #ifdef CONFIG_USER_ONLY TaskState *ts = cs->opaque; ts->swi_errno = err; From 84ca0dfd1e8c0a23fe8aeb22f4141e0b14da2812 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 16 May 2022 19:34:06 -0700 Subject: [PATCH 11/60] semihosting: Clean up common_semi_flen_cb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not read from the gdb struct stat buffer if the callback is reporting an error. Use common_semi_cb to finish returning results. Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- semihosting/arm-compat-semi.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 88d6bdeaf2..cc13fcb0ef 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -346,15 +346,17 @@ static target_ulong common_semi_flen_buf(CPUState *cs) static void common_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err) { - /* The size is always stored in big-endian order, extract - the value. We assume the size always fit in 32 bits. */ - uint32_t size; - cpu_memory_rw_debug(cs, common_semi_flen_buf(cs) + 32, - (uint8_t *)&size, 4, 0); - size = be32_to_cpu(size); - common_semi_set_ret(cs, size); - errno = err; - set_swi_errno(cs, -1); + if (!err) { + /* + * The size is always stored in big-endian order, extract + * the value. We assume the size always fit in 32 bits. + */ + uint32_t size; + cpu_memory_rw_debug(cs, common_semi_flen_buf(cs) + 32, + (uint8_t *)&size, 4, 0); + ret = be32_to_cpu(size); + } + common_semi_cb(cs, ret, err); } static int common_semi_open_guestfd; From 4cfeff4ac16bf5e3e1df44d5561b83e3bd3aab6c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 16 May 2022 19:37:14 -0700 Subject: [PATCH 12/60] semihosting: Clean up common_semi_open_cb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use common_semi_cb to return results instead of calling set_swi_errno and common_semi_set_ret directly. Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- semihosting/arm-compat-semi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index cc13fcb0ef..6414caa749 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -364,15 +364,13 @@ static int common_semi_open_guestfd; static void common_semi_open_cb(CPUState *cs, target_ulong ret, target_ulong err) { - if (ret == (target_ulong)-1) { - errno = err; - set_swi_errno(cs, -1); + if (err) { dealloc_guestfd(common_semi_open_guestfd); } else { associate_guestfd(common_semi_open_guestfd, ret); ret = common_semi_open_guestfd; } - common_semi_set_ret(cs, ret); + common_semi_cb(cs, ret, err); } static target_ulong From ed3a06b10a6abb53589d794ed23accf21be05633 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Apr 2022 01:10:55 -0700 Subject: [PATCH 13/60] semihosting: Return void from do_common_semihosting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Perform the cleanup in the FIXME comment in common_semi_gdb_syscall. Do not modify guest registers until the syscall is complete, which in the gdbstub case is asynchronous. In the synchronous non-gdbstub case, use common_semi_set_ret to set the result. Merge set_swi_errno into common_semi_cb. Rely on the latter for combined return value / errno setting. Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- linux-user/aarch64/cpu_loop.c | 2 +- linux-user/arm/cpu_loop.c | 2 +- linux-user/riscv/cpu_loop.c | 2 +- semihosting/arm-compat-semi.c | 543 ++++++++++++++++------------------ semihosting/common-semi.h | 2 +- target/arm/helper.c | 4 +- target/arm/m_helper.c | 2 +- target/riscv/cpu_helper.c | 2 +- 8 files changed, 264 insertions(+), 295 deletions(-) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 3b273f6299..f7ef36cd9f 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -154,7 +154,7 @@ void cpu_loop(CPUARMState *env) force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); break; case EXCP_SEMIHOST: - env->xregs[0] = do_common_semihosting(cs); + do_common_semihosting(cs); env->pc += 4; break; case EXCP_YIELD: diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index d950409d5b..c0790f3246 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -449,7 +449,7 @@ void cpu_loop(CPUARMState *env) } break; case EXCP_SEMIHOST: - env->regs[0] = do_common_semihosting(cs); + do_common_semihosting(cs); env->regs[15] += env->thumb ? 2 : 4; break; case EXCP_INTERRUPT: diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index 29084c1421..bffca7db12 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -81,7 +81,7 @@ void cpu_loop(CPURISCVState *env) force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); break; case RISCV_EXCP_SEMIHOST: - env->gpr[xA0] = do_common_semihosting(cs); + do_common_semihosting(cs); env->pc += 4; break; default: diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 6414caa749..cebbad2355 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -261,20 +261,6 @@ static target_ulong syscall_err; #include "semihosting/softmmu-uaccess.h" #endif -static inline uint32_t set_swi_errno(CPUState *cs, uint32_t code) -{ - if (code == (uint32_t)-1) { -#ifdef CONFIG_USER_ONLY - TaskState *ts = cs->opaque; - - ts->swi_errno = errno; -#else - syscall_err = errno; -#endif - } - return code; -} - static inline uint32_t get_swi_errno(CPUState *cs) { #ifdef CONFIG_USER_ONLY @@ -373,54 +359,24 @@ common_semi_open_cb(CPUState *cs, target_ulong ret, target_ulong err) common_semi_cb(cs, ret, err); } -static target_ulong -common_semi_gdb_syscall(CPUState *cs, gdb_syscall_complete_cb cb, - const char *fmt, ...) -{ - va_list va; - - va_start(va, fmt); - gdb_do_syscallv(cb, fmt, va); - va_end(va); - - /* - * FIXME: in softmmu mode, the gdbstub will schedule our callback - * to occur, but will not actually call it to complete the syscall - * until after this function has returned and we are back in the - * CPU main loop. Therefore callers to this function must not - * do anything with its return value, because it is not necessarily - * the result of the syscall, but could just be the old value of X0. - * The only thing safe to do with this is that the callers of - * do_common_semihosting() will write it straight back into X0. - * (In linux-user mode, the callback will have happened before - * gdb_do_syscallv() returns.) - * - * We should tidy this up so neither this function nor - * do_common_semihosting() return a value, so the mistake of - * doing something with the return value is not possible to make. - */ - - return common_semi_arg(cs, 0); -} - /* * Types for functions implementing various semihosting calls * for specific types of guest file descriptor. These must all - * do the work and return the required return value for the guest, - * setting the guest errno if appropriate. + * do the work and return the required return value to the guest + * via common_semi_cb. */ -typedef uint32_t sys_closefn(CPUState *cs, GuestFD *gf); -typedef uint32_t sys_writefn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len); -typedef uint32_t sys_readfn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len); -typedef uint32_t sys_isattyfn(CPUState *cs, GuestFD *gf); -typedef uint32_t sys_seekfn(CPUState *cs, GuestFD *gf, - target_ulong offset); -typedef uint32_t sys_flenfn(CPUState *cs, GuestFD *gf); +typedef void sys_closefn(CPUState *cs, GuestFD *gf); +typedef void sys_writefn(CPUState *cs, GuestFD *gf, + target_ulong buf, uint32_t len); +typedef void sys_readfn(CPUState *cs, GuestFD *gf, + target_ulong buf, uint32_t len); +typedef void sys_isattyfn(CPUState *cs, GuestFD *gf); +typedef void sys_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset); +typedef void sys_flenfn(CPUState *cs, GuestFD *gf); -static uint32_t host_closefn(CPUState *cs, GuestFD *gf) +static void host_closefn(CPUState *cs, GuestFD *gf) { + int ret; /* * Only close the underlying host fd if it's one we opened on behalf * of the guest in SYS_OPEN. @@ -428,113 +384,106 @@ static uint32_t host_closefn(CPUState *cs, GuestFD *gf) if (gf->hostfd == STDIN_FILENO || gf->hostfd == STDOUT_FILENO || gf->hostfd == STDERR_FILENO) { - return 0; + ret = 0; + } else { + ret = close(gf->hostfd); } - return set_swi_errno(cs, close(gf->hostfd)); + common_semi_cb(cs, ret, ret ? errno : 0); } -static uint32_t host_writefn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) +static void host_writefn(CPUState *cs, GuestFD *gf, + target_ulong buf, uint32_t len) { CPUArchState *env = cs->env_ptr; - uint32_t ret; + uint32_t ret = 0; char *s = lock_user(VERIFY_READ, buf, len, 1); (void) env; /* Used in arm softmmu lock_user implicitly */ - if (!s) { - /* Return bytes not written on error */ - return len; + if (s) { + ret = write(gf->hostfd, s, len); + unlock_user(s, buf, 0); + if (ret == (uint32_t)-1) { + ret = 0; + } } - ret = set_swi_errno(cs, write(gf->hostfd, s, len)); - unlock_user(s, buf, 0); - if (ret == (uint32_t)-1) { - ret = 0; - } - /* Return bytes not written */ - return len - ret; + /* Return bytes not written, on error as well. */ + common_semi_cb(cs, len - ret, 0); } -static uint32_t host_readfn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) +static void host_readfn(CPUState *cs, GuestFD *gf, + target_ulong buf, uint32_t len) { CPUArchState *env = cs->env_ptr; - uint32_t ret; + uint32_t ret = 0; char *s = lock_user(VERIFY_WRITE, buf, len, 0); (void) env; /* Used in arm softmmu lock_user implicitly */ - if (!s) { - /* return bytes not read */ - return len; + if (s) { + do { + ret = read(gf->hostfd, s, len); + } while (ret == -1 && errno == EINTR); + unlock_user(s, buf, len); + if (ret == (uint32_t)-1) { + ret = 0; + } } - do { - ret = set_swi_errno(cs, read(gf->hostfd, s, len)); - } while (ret == -1 && errno == EINTR); - unlock_user(s, buf, len); - if (ret == (uint32_t)-1) { - ret = 0; - } - /* Return bytes not read */ - return len - ret; + /* Return bytes not read, on error as well. */ + common_semi_cb(cs, len - ret, 0); } -static uint32_t host_isattyfn(CPUState *cs, GuestFD *gf) +static void host_isattyfn(CPUState *cs, GuestFD *gf) { - return isatty(gf->hostfd); + common_semi_cb(cs, isatty(gf->hostfd), 0); } -static uint32_t host_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset) +static void host_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset) { - uint32_t ret = set_swi_errno(cs, lseek(gf->hostfd, offset, SEEK_SET)); - if (ret == (uint32_t)-1) { - return -1; - } - return 0; + off_t ret = lseek(gf->hostfd, offset, SEEK_SET); + common_semi_cb(cs, ret, ret == -1 ? errno : 0); } -static uint32_t host_flenfn(CPUState *cs, GuestFD *gf) +static void host_flenfn(CPUState *cs, GuestFD *gf) { struct stat buf; - uint32_t ret = set_swi_errno(cs, fstat(gf->hostfd, &buf)); - if (ret == (uint32_t)-1) { - return -1; + + if (fstat(gf->hostfd, &buf)) { + common_semi_cb(cs, -1, errno); + } else { + common_semi_cb(cs, buf.st_size, 0); } - return buf.st_size; } -static uint32_t gdb_closefn(CPUState *cs, GuestFD *gf) +static void gdb_closefn(CPUState *cs, GuestFD *gf) { - return common_semi_gdb_syscall(cs, common_semi_cb, "close,%x", gf->hostfd); + gdb_do_syscall(common_semi_cb, "close,%x", gf->hostfd); } -static uint32_t gdb_writefn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) +static void gdb_writefn(CPUState *cs, GuestFD *gf, + target_ulong buf, uint32_t len) { common_semi_syscall_len = len; - return common_semi_gdb_syscall(cs, common_semi_cb, "write,%x,%x,%x", - gf->hostfd, buf, len); + gdb_do_syscall(common_semi_cb, "write,%x,%x,%x", gf->hostfd, buf, len); } -static uint32_t gdb_readfn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) +static void gdb_readfn(CPUState *cs, GuestFD *gf, + target_ulong buf, uint32_t len) { common_semi_syscall_len = len; - return common_semi_gdb_syscall(cs, common_semi_cb, "read,%x,%x,%x", - gf->hostfd, buf, len); + gdb_do_syscall(common_semi_cb, "read,%x,%x,%x", gf->hostfd, buf, len); } -static uint32_t gdb_isattyfn(CPUState *cs, GuestFD *gf) +static void gdb_isattyfn(CPUState *cs, GuestFD *gf) { - return common_semi_gdb_syscall(cs, common_semi_cb, "isatty,%x", gf->hostfd); + gdb_do_syscall(common_semi_cb, "isatty,%x", gf->hostfd); } -static uint32_t gdb_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset) +static void gdb_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset) { - return common_semi_gdb_syscall(cs, common_semi_cb, "lseek,%x,%x,0", - gf->hostfd, offset); + gdb_do_syscall(common_semi_cb, "lseek,%x,%x,0", gf->hostfd, offset); } -static uint32_t gdb_flenfn(CPUState *cs, GuestFD *gf) +static void gdb_flenfn(CPUState *cs, GuestFD *gf) { - return common_semi_gdb_syscall(cs, common_semi_flen_cb, "fstat,%x,%x", - gf->hostfd, common_semi_flen_buf(cs)); + gdb_do_syscall(common_semi_flen_cb, "fstat,%x,%x", + gf->hostfd, common_semi_flen_buf(cs)); } #define SHFB_MAGIC_0 0x53 @@ -554,63 +503,57 @@ static const uint8_t featurefile_data[] = { SH_EXT_EXIT_EXTENDED | SH_EXT_STDOUT_STDERR, /* Feature byte 0 */ }; -static uint32_t staticfile_closefn(CPUState *cs, GuestFD *gf) +static void staticfile_closefn(CPUState *cs, GuestFD *gf) { /* Nothing to do */ - return 0; + common_semi_cb(cs, 0, 0); } -static uint32_t staticfile_writefn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) +static void staticfile_writefn(CPUState *cs, GuestFD *gf, + target_ulong buf, uint32_t len) { /* This fd can never be open for writing */ - - errno = EBADF; - return set_swi_errno(cs, -1); + common_semi_cb(cs, -1, EBADF); } -static uint32_t staticfile_readfn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) +static void staticfile_readfn(CPUState *cs, GuestFD *gf, + target_ulong buf, uint32_t len) { CPUArchState *env = cs->env_ptr; - uint32_t i; + uint32_t i = 0; char *s; (void) env; /* Used in arm softmmu lock_user implicitly */ s = lock_user(VERIFY_WRITE, buf, len, 0); - if (!s) { - return len; - } - - for (i = 0; i < len; i++) { - if (gf->staticfile.off >= gf->staticfile.len) { - break; + if (s) { + for (i = 0; i < len; i++) { + if (gf->staticfile.off >= gf->staticfile.len) { + break; + } + s[i] = gf->staticfile.data[gf->staticfile.off]; + gf->staticfile.off++; } - s[i] = gf->staticfile.data[gf->staticfile.off]; - gf->staticfile.off++; + unlock_user(s, buf, len); } - unlock_user(s, buf, len); - /* Return number of bytes not read */ - return len - i; + common_semi_cb(cs, len - i, 0); } -static uint32_t staticfile_isattyfn(CPUState *cs, GuestFD *gf) +static void staticfile_isattyfn(CPUState *cs, GuestFD *gf) { - return 0; + common_semi_cb(cs, 0, 0); } -static uint32_t staticfile_seekfn(CPUState *cs, GuestFD *gf, - target_ulong offset) +static void staticfile_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset) { gf->staticfile.off = offset; - return 0; + common_semi_cb(cs, 0, 0); } -static uint32_t staticfile_flenfn(CPUState *cs, GuestFD *gf) +static void staticfile_flenfn(CPUState *cs, GuestFD *gf) { - return gf->staticfile.len; + common_semi_cb(cs, gf->staticfile.len, 0); } typedef struct GuestFDFunctions { @@ -669,13 +612,11 @@ static inline bool is_64bit_semihosting(CPUArchState *env) #define GET_ARG(n) do { \ if (is_64bit_semihosting(env)) { \ if (get_user_u64(arg ## n, args + (n) * 8)) { \ - errno = EFAULT; \ - return set_swi_errno(cs, -1); \ + goto do_fault; \ } \ } else { \ if (get_user_u32(arg ## n, args + (n) * 4)) { \ - errno = EFAULT; \ - return set_swi_errno(cs, -1); \ + goto do_fault; \ } \ } \ } while (0) @@ -695,7 +636,7 @@ static inline bool is_64bit_semihosting(CPUArchState *env) * leave the register unchanged. We use 0xdeadbeef as the return value * when there isn't a defined return value for the call. */ -target_ulong do_common_semihosting(CPUState *cs) +void do_common_semihosting(CPUState *cs) { CPUArchState *env = cs->env_ptr; target_ulong args; @@ -715,32 +656,23 @@ target_ulong do_common_semihosting(CPUState *cs) switch (nr) { case TARGET_SYS_OPEN: { - int guestfd; + int ret, err = 0; + int hostfd; GET_ARG(0); GET_ARG(1); GET_ARG(2); s = lock_user_string(arg0); if (!s) { - errno = EFAULT; - return set_swi_errno(cs, -1); + goto do_fault; } if (arg1 >= 12) { unlock_user(s, arg0, 0); - errno = EINVAL; - return set_swi_errno(cs, -1); - } - - guestfd = alloc_guestfd(); - if (guestfd < 0) { - unlock_user(s, arg0, 0); - errno = EMFILE; - return set_swi_errno(cs, -1); + common_semi_cb(cs, -1, EINVAL); + break; } if (strcmp(s, ":tt") == 0) { - int result_fileno; - /* * We implement SH_EXT_STDOUT_STDERR, so: * open for read == stdin @@ -748,63 +680,67 @@ target_ulong do_common_semihosting(CPUState *cs) * open for append == stderr */ if (arg1 < 4) { - result_fileno = STDIN_FILENO; + hostfd = STDIN_FILENO; } else if (arg1 < 8) { - result_fileno = STDOUT_FILENO; + hostfd = STDOUT_FILENO; } else { - result_fileno = STDERR_FILENO; + hostfd = STDERR_FILENO; } - associate_guestfd(guestfd, result_fileno); - unlock_user(s, arg0, 0); - return guestfd; - } - if (strcmp(s, ":semihosting-features") == 0) { - unlock_user(s, arg0, 0); + ret = alloc_guestfd(); + associate_guestfd(ret, hostfd); + } else if (strcmp(s, ":semihosting-features") == 0) { /* We must fail opens for modes other than 0 ('r') or 1 ('rb') */ if (arg1 != 0 && arg1 != 1) { - dealloc_guestfd(guestfd); - errno = EACCES; - return set_swi_errno(cs, -1); - } - staticfile_guestfd(guestfd, featurefile_data, - sizeof(featurefile_data)); - return guestfd; - } - - if (use_gdb_syscalls()) { - common_semi_open_guestfd = guestfd; - ret = common_semi_gdb_syscall(cs, common_semi_open_cb, - "open,%s,%x,1a4", arg0, (int)arg2 + 1, - gdb_open_modeflags[arg1]); - } else { - ret = set_swi_errno(cs, open(s, open_modeflags[arg1], 0644)); - if (ret == (uint32_t)-1) { - dealloc_guestfd(guestfd); + ret = -1; + err = EACCES; } else { - associate_guestfd(guestfd, ret); - ret = guestfd; + ret = alloc_guestfd(); + staticfile_guestfd(ret, featurefile_data, + sizeof(featurefile_data)); + } + } else if (use_gdb_syscalls()) { + unlock_user(s, arg0, 0); + common_semi_open_guestfd = alloc_guestfd(); + gdb_do_syscall(common_semi_open_cb, + "open,%s,%x,1a4", arg0, (int)arg2 + 1, + gdb_open_modeflags[arg1]); + break; + } else { + hostfd = open(s, open_modeflags[arg1], 0644); + if (hostfd < 0) { + ret = -1; + err = errno; + } else { + ret = alloc_guestfd(); + associate_guestfd(ret, hostfd); } } unlock_user(s, arg0, 0); - return ret; + common_semi_cb(cs, ret, err); + break; } + case TARGET_SYS_CLOSE: GET_ARG(0); gf = get_guestfd(arg0); if (!gf) { - errno = EBADF; - return set_swi_errno(cs, -1); + goto do_badf; } - - ret = guestfd_fns[gf->type].closefn(cs, gf); + guestfd_fns[gf->type].closefn(cs, gf); dealloc_guestfd(arg0); - return ret; + break; + case TARGET_SYS_WRITEC: qemu_semihosting_console_outc(cs->env_ptr, args); - return 0xdeadbeef; + common_semi_set_ret(cs, 0xdeadbeef); + break; + case TARGET_SYS_WRITE0: - return qemu_semihosting_console_outs(cs->env_ptr, args); + ret = qemu_semihosting_console_outs(cs->env_ptr, args); + common_semi_set_ret(cs, ret); + break; + case TARGET_SYS_WRITE: GET_ARG(0); GET_ARG(1); @@ -813,11 +749,11 @@ target_ulong do_common_semihosting(CPUState *cs) gf = get_guestfd(arg0); if (!gf) { - errno = EBADF; - return set_swi_errno(cs, -1); + goto do_badf; } + guestfd_fns[gf->type].writefn(cs, gf, arg1, len); + break; - return guestfd_fns[gf->type].writefn(cs, gf, arg1, len); case TARGET_SYS_READ: GET_ARG(0); GET_ARG(1); @@ -826,129 +762,150 @@ target_ulong do_common_semihosting(CPUState *cs) gf = get_guestfd(arg0); if (!gf) { - errno = EBADF; - return set_swi_errno(cs, -1); + goto do_badf; } + guestfd_fns[gf->type].readfn(cs, gf, arg1, len); + break; - return guestfd_fns[gf->type].readfn(cs, gf, arg1, len); case TARGET_SYS_READC: - return qemu_semihosting_console_inc(cs->env_ptr); + ret = qemu_semihosting_console_inc(cs->env_ptr); + common_semi_set_ret(cs, ret); + break; + case TARGET_SYS_ISERROR: GET_ARG(0); - return (target_long) arg0 < 0 ? 1 : 0; + common_semi_set_ret(cs, (target_long)arg0 < 0); + break; + case TARGET_SYS_ISTTY: GET_ARG(0); gf = get_guestfd(arg0); if (!gf) { - errno = EBADF; - return set_swi_errno(cs, -1); + goto do_badf; } + guestfd_fns[gf->type].isattyfn(cs, gf); + break; - return guestfd_fns[gf->type].isattyfn(cs, gf); case TARGET_SYS_SEEK: GET_ARG(0); GET_ARG(1); gf = get_guestfd(arg0); if (!gf) { - errno = EBADF; - return set_swi_errno(cs, -1); + goto do_badf; } + guestfd_fns[gf->type].seekfn(cs, gf, arg1); + break; - return guestfd_fns[gf->type].seekfn(cs, gf, arg1); case TARGET_SYS_FLEN: GET_ARG(0); gf = get_guestfd(arg0); if (!gf) { - errno = EBADF; - return set_swi_errno(cs, -1); + goto do_badf; } + guestfd_fns[gf->type].flenfn(cs, gf); + break; - return guestfd_fns[gf->type].flenfn(cs, gf); case TARGET_SYS_TMPNAM: + { + int len; + char *p; + GET_ARG(0); GET_ARG(1); GET_ARG(2); - if (asprintf(&s, "/tmp/qemu-%x%02x", getpid(), - (int) (arg1 & 0xff)) < 0) { - return -1; - } - ul_ret = (target_ulong) -1; - + len = asprintf(&s, "/tmp/qemu-%x%02x", getpid(), (int)arg1 & 0xff); /* Make sure there's enough space in the buffer */ - if (strlen(s) < arg2) { - char *output = lock_user(VERIFY_WRITE, arg0, arg2, 0); - strcpy(output, s); - unlock_user(output, arg0, arg2); - ul_ret = 0; + if (len < 0 || len >= arg2) { + common_semi_set_ret(cs, -1); + break; } + p = lock_user(VERIFY_WRITE, arg0, len, 0); + if (!p) { + goto do_fault; + } + memcpy(p, s, len + 1); + unlock_user(p, arg0, len); free(s); - return ul_ret; + common_semi_set_ret(cs, 0); + break; + } + case TARGET_SYS_REMOVE: GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - ret = common_semi_gdb_syscall(cs, common_semi_cb, "unlink,%s", - arg0, (int)arg1 + 1); - } else { - s = lock_user_string(arg0); - if (!s) { - errno = EFAULT; - return set_swi_errno(cs, -1); - } - ret = set_swi_errno(cs, remove(s)); - unlock_user(s, arg0, 0); + gdb_do_syscall(common_semi_cb, "unlink,%s", + arg0, (int)arg1 + 1); + break; } - return ret; + s = lock_user_string(arg0); + if (!s) { + goto do_fault; + } + ret = remove(s); + unlock_user(s, arg0, 0); + common_semi_cb(cs, ret, ret ? errno : 0); + break; + case TARGET_SYS_RENAME: GET_ARG(0); GET_ARG(1); GET_ARG(2); GET_ARG(3); if (use_gdb_syscalls()) { - return common_semi_gdb_syscall(cs, common_semi_cb, "rename,%s,%s", - arg0, (int)arg1 + 1, arg2, - (int)arg3 + 1); + gdb_do_syscall(common_semi_cb, "rename,%s,%s", + arg0, (int)arg1 + 1, arg2, (int)arg3 + 1); } else { char *s2; + s = lock_user_string(arg0); - s2 = lock_user_string(arg2); - if (!s || !s2) { - errno = EFAULT; - ret = set_swi_errno(cs, -1); - } else { - ret = set_swi_errno(cs, rename(s, s2)); + if (!s) { + goto do_fault; } - if (s2) - unlock_user(s2, arg2, 0); - if (s) + s2 = lock_user_string(arg2); + if (!s2) { unlock_user(s, arg0, 0); - return ret; + goto do_fault; + } + ret = rename(s, s2); + unlock_user(s2, arg2, 0); + unlock_user(s, arg0, 0); + common_semi_cb(cs, ret, ret ? errno : 0); } + break; + case TARGET_SYS_CLOCK: - return clock() / (CLOCKS_PER_SEC / 100); + common_semi_set_ret(cs, clock() / (CLOCKS_PER_SEC / 100)); + break; + case TARGET_SYS_TIME: - return set_swi_errno(cs, time(NULL)); + ul_ret = time(NULL); + common_semi_cb(cs, ul_ret, ul_ret == -1 ? errno : 0); + break; + case TARGET_SYS_SYSTEM: GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - return common_semi_gdb_syscall(cs, common_semi_cb, "system,%s", - arg0, (int)arg1 + 1); - } else { - s = lock_user_string(arg0); - if (!s) { - errno = EFAULT; - return set_swi_errno(cs, -1); - } - ret = set_swi_errno(cs, system(s)); - unlock_user(s, arg0, 0); - return ret; + gdb_do_syscall(common_semi_cb, "system,%s", arg0, (int)arg1 + 1); + break; } + s = lock_user_string(arg0); + if (!s) { + goto do_fault; + } + ret = system(s); + unlock_user(s, arg0, 0); + common_semi_cb(cs, ret, ret == -1 ? errno : 0); + break; + case TARGET_SYS_ERRNO: - return get_swi_errno(cs); + common_semi_set_ret(cs, get_swi_errno(cs)); + break; + case TARGET_SYS_GET_CMDLINE: { /* Build a command-line from the original argv. @@ -999,22 +956,20 @@ target_ulong do_common_semihosting(CPUState *cs) if (output_size > input_size) { /* Not enough space to store command-line arguments. */ - errno = E2BIG; - return set_swi_errno(cs, -1); + common_semi_cb(cs, -1, E2BIG); + break; } /* Adjust the command-line length. */ if (SET_ARG(1, output_size - 1)) { /* Couldn't write back to argument block */ - errno = EFAULT; - return set_swi_errno(cs, -1); + goto do_fault; } /* Lock the buffer on the ARM side. */ output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0); if (!output_buffer) { - errno = EFAULT; - return set_swi_errno(cs, -1); + goto do_fault; } /* Copy the command-line arguments. */ @@ -1029,9 +984,8 @@ target_ulong do_common_semihosting(CPUState *cs) if (copy_from_user(output_buffer, ts->info->arg_strings, output_size)) { - errno = EFAULT; - status = set_swi_errno(cs, -1); - goto out; + unlock_user(output_buffer, arg0, 0); + goto do_fault; } /* Separate arguments by white spaces. */ @@ -1044,9 +998,10 @@ target_ulong do_common_semihosting(CPUState *cs) #endif /* Unlock the buffer on the ARM side. */ unlock_user(output_buffer, arg0, output_size); - - return status; + common_semi_cb(cs, status, 0); } + break; + case TARGET_SYS_HEAPINFO: { target_ulong retvals[4]; @@ -1103,12 +1058,13 @@ target_ulong do_common_semihosting(CPUState *cs) if (fail) { /* Couldn't write back to argument block */ - errno = EFAULT; - return set_swi_errno(cs, -1); + goto do_fault; } } - return 0; + common_semi_set_ret(cs, 0); } + break; + case TARGET_SYS_EXIT: case TARGET_SYS_EXIT_EXTENDED: if (common_semi_sys_exit_extended(cs, nr)) { @@ -1138,6 +1094,7 @@ target_ulong do_common_semihosting(CPUState *cs) } gdb_exit(ret); exit(ret); + case TARGET_SYS_ELAPSED: elapsed = get_clock() - clock_start; if (sizeof(target_ulong) == 8) { @@ -1146,10 +1103,14 @@ target_ulong do_common_semihosting(CPUState *cs) SET_ARG(0, (uint32_t) elapsed); SET_ARG(1, (uint32_t) (elapsed >> 32)); } - return 0; + common_semi_set_ret(cs, 0); + break; + case TARGET_SYS_TICKFREQ: /* qemu always uses nsec */ - return 1000000000; + common_semi_set_ret(cs, 1000000000); + break; + case TARGET_SYS_SYNCCACHE: /* * Clean the D-cache and invalidate the I-cache for the specified @@ -1158,16 +1119,24 @@ target_ulong do_common_semihosting(CPUState *cs) */ #ifdef TARGET_ARM if (is_a64(cs->env_ptr)) { - return 0; + common_semi_set_ret(cs, 0); + break; } #endif #ifdef TARGET_RISCV - return 0; + common_semi_set_ret(cs, 0); #endif /* fall through -- invalid for A32/T32 */ default: fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr); cpu_dump_state(cs, stderr, 0); abort(); + + do_badf: + common_semi_cb(cs, -1, EBADF); + break; + do_fault: + common_semi_cb(cs, -1, EFAULT); + break; } } diff --git a/semihosting/common-semi.h b/semihosting/common-semi.h index 0bfab1c669..0a91db7c41 100644 --- a/semihosting/common-semi.h +++ b/semihosting/common-semi.h @@ -34,6 +34,6 @@ #ifndef COMMON_SEMI_H #define COMMON_SEMI_H -target_ulong do_common_semihosting(CPUState *cs); +void do_common_semihosting(CPUState *cs); #endif /* COMMON_SEMI_H */ diff --git a/target/arm/helper.c b/target/arm/helper.c index d2886a123a..f6dcb1a115 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -10515,13 +10515,13 @@ static void handle_semihosting(CPUState *cs) qemu_log_mask(CPU_LOG_INT, "...handling as semihosting call 0x%" PRIx64 "\n", env->xregs[0]); - env->xregs[0] = do_common_semihosting(cs); + do_common_semihosting(cs); env->pc += 4; } else { qemu_log_mask(CPU_LOG_INT, "...handling as semihosting call 0x%x\n", env->regs[0]); - env->regs[0] = do_common_semihosting(cs); + do_common_semihosting(cs); env->regs[15] += env->thumb ? 2 : 4; } } diff --git a/target/arm/m_helper.c b/target/arm/m_helper.c index a740c3e160..308610f6b4 100644 --- a/target/arm/m_helper.c +++ b/target/arm/m_helper.c @@ -2373,7 +2373,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) "...handling as semihosting call 0x%x\n", env->regs[0]); #ifdef CONFIG_TCG - env->regs[0] = do_common_semihosting(cs); + do_common_semihosting(cs); #else g_assert_not_reached(); #endif diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 4a6700c890..be28615e23 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -1347,7 +1347,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) if (cause == RISCV_EXCP_SEMIHOST) { if (env->priv >= PRV_S) { - env->gpr[xA0] = do_common_semihosting(cs); + do_common_semihosting(cs); env->pc += 4; return; } From bb3b8821a326d18294ce31bdd350bb27a139940e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Apr 2022 01:14:02 -0700 Subject: [PATCH 14/60] semihosting: Move common-semi.h to include/semihosting/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This header is not private to the top-level semihosting directory, so place it in the public include directory. Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- {semihosting => include/semihosting}/common-semi.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {semihosting => include/semihosting}/common-semi.h (100%) diff --git a/semihosting/common-semi.h b/include/semihosting/common-semi.h similarity index 100% rename from semihosting/common-semi.h rename to include/semihosting/common-semi.h From a1a2a3e609f619857fcdd8302fe1e832490216a7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jun 2022 10:50:43 -0700 Subject: [PATCH 15/60] semihosting: Remove GDB_O_BINARY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The value is zero, and gdb always opens files in binary mode. Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- semihosting/arm-compat-semi.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index cebbad2355..92c1375b15 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -92,21 +92,20 @@ #define GDB_O_APPEND 0x008 #define GDB_O_CREAT 0x200 #define GDB_O_TRUNC 0x400 -#define GDB_O_BINARY 0 static int gdb_open_modeflags[12] = { GDB_O_RDONLY, - GDB_O_RDONLY | GDB_O_BINARY, + GDB_O_RDONLY, + GDB_O_RDWR, GDB_O_RDWR, - GDB_O_RDWR | GDB_O_BINARY, GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC, - GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY, + GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC, + GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC, GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC, - GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY, GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND, - GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY, + GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND, + GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND, GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND, - GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY }; static int open_modeflags[12] = { From 94b14fe08f9f4f1f0e7aba639fc98e65a13e5235 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Apr 2022 01:29:54 -0700 Subject: [PATCH 16/60] include/exec: Move gdb open flags to gdbstub.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There were 3 copies of these flags. Place them in the file with gdb_do_syscall, with which they belong. Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- include/exec/gdbstub.h | 9 +++++++++ semihosting/arm-compat-semi.c | 7 ------- target/m68k/m68k-semi.c | 8 -------- target/nios2/nios2-semi.c | 8 -------- 4 files changed, 9 insertions(+), 23 deletions(-) diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index c35d7334b4..603e22ae80 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -10,6 +10,15 @@ #define GDB_WATCHPOINT_READ 3 #define GDB_WATCHPOINT_ACCESS 4 +/* For gdb file i/o remote protocol open flags. */ +#define GDB_O_RDONLY 0 +#define GDB_O_WRONLY 1 +#define GDB_O_RDWR 2 +#define GDB_O_APPEND 8 +#define GDB_O_CREAT 0x200 +#define GDB_O_TRUNC 0x400 +#define GDB_O_EXCL 0x800 + #ifdef NEED_CPU_H #include "cpu.h" diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 92c1375b15..abf543ce91 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -86,13 +86,6 @@ #define O_BINARY 0 #endif -#define GDB_O_RDONLY 0x000 -#define GDB_O_WRONLY 0x001 -#define GDB_O_RDWR 0x002 -#define GDB_O_APPEND 0x008 -#define GDB_O_CREAT 0x200 -#define GDB_O_TRUNC 0x400 - static int gdb_open_modeflags[12] = { GDB_O_RDONLY, GDB_O_RDONLY, diff --git a/target/m68k/m68k-semi.c b/target/m68k/m68k-semi.c index a31db38fc3..475a6b13b7 100644 --- a/target/m68k/m68k-semi.c +++ b/target/m68k/m68k-semi.c @@ -69,14 +69,6 @@ struct gdb_timeval { uint64_t tv_usec; /* microsecond */ } QEMU_PACKED; -#define GDB_O_RDONLY 0x0 -#define GDB_O_WRONLY 0x1 -#define GDB_O_RDWR 0x2 -#define GDB_O_APPEND 0x8 -#define GDB_O_CREAT 0x200 -#define GDB_O_TRUNC 0x400 -#define GDB_O_EXCL 0x800 - static int translate_openflags(int flags) { int hf; diff --git a/target/nios2/nios2-semi.c b/target/nios2/nios2-semi.c index 373e6b9436..0eec1f9a1c 100644 --- a/target/nios2/nios2-semi.c +++ b/target/nios2/nios2-semi.c @@ -71,14 +71,6 @@ struct gdb_timeval { uint64_t tv_usec; /* microsecond */ } QEMU_PACKED; -#define GDB_O_RDONLY 0x0 -#define GDB_O_WRONLY 0x1 -#define GDB_O_RDWR 0x2 -#define GDB_O_APPEND 0x8 -#define GDB_O_CREAT 0x200 -#define GDB_O_TRUNC 0x400 -#define GDB_O_EXCL 0x800 - static int translate_openflags(int flags) { int hf; From 7c56c2d3da2d25cb202e6b47ad7348a42b950b76 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Apr 2022 14:41:37 -0700 Subject: [PATCH 17/60] include/exec: Move gdb_stat and gdb_timeval to gdbstub.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have two copies of these structures, and require them in semihosting/ going forward. Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- include/exec/gdbstub.h | 25 +++++++++++++++++++++++++ target/m68k/m68k-semi.c | 32 +++++--------------------------- target/nios2/nios2-semi.c | 30 +++--------------------------- 3 files changed, 33 insertions(+), 54 deletions(-) diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index 603e22ae80..7413ffeba2 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -19,6 +19,31 @@ #define GDB_O_TRUNC 0x400 #define GDB_O_EXCL 0x800 +/* For gdb file i/o stat/fstat. */ +typedef uint32_t gdb_mode_t; +typedef uint32_t gdb_time_t; + +struct gdb_stat { + uint32_t gdb_st_dev; /* device */ + uint32_t gdb_st_ino; /* inode */ + gdb_mode_t gdb_st_mode; /* protection */ + uint32_t gdb_st_nlink; /* number of hard links */ + uint32_t gdb_st_uid; /* user ID of owner */ + uint32_t gdb_st_gid; /* group ID of owner */ + uint32_t gdb_st_rdev; /* device type (if inode device) */ + uint64_t gdb_st_size; /* total size, in bytes */ + uint64_t gdb_st_blksize; /* blocksize for filesystem I/O */ + uint64_t gdb_st_blocks; /* number of blocks allocated */ + gdb_time_t gdb_st_atime; /* time of last access */ + gdb_time_t gdb_st_mtime; /* time of last modification */ + gdb_time_t gdb_st_ctime; /* time of last change */ +} QEMU_PACKED; + +struct gdb_timeval { + gdb_time_t tv_sec; /* second */ + uint64_t tv_usec; /* microsecond */ +} QEMU_PACKED; + #ifdef NEED_CPU_H #include "cpu.h" diff --git a/target/m68k/m68k-semi.c b/target/m68k/m68k-semi.c index 475a6b13b7..b886ebf714 100644 --- a/target/m68k/m68k-semi.c +++ b/target/m68k/m68k-semi.c @@ -45,30 +45,6 @@ #define HOSTED_ISATTY 12 #define HOSTED_SYSTEM 13 -typedef uint32_t gdb_mode_t; -typedef uint32_t gdb_time_t; - -struct m68k_gdb_stat { - uint32_t gdb_st_dev; /* device */ - uint32_t gdb_st_ino; /* inode */ - gdb_mode_t gdb_st_mode; /* protection */ - uint32_t gdb_st_nlink; /* number of hard links */ - uint32_t gdb_st_uid; /* user ID of owner */ - uint32_t gdb_st_gid; /* group ID of owner */ - uint32_t gdb_st_rdev; /* device type (if inode device) */ - uint64_t gdb_st_size; /* total size, in bytes */ - uint64_t gdb_st_blksize; /* blocksize for filesystem I/O */ - uint64_t gdb_st_blocks; /* number of blocks allocated */ - gdb_time_t gdb_st_atime; /* time of last access */ - gdb_time_t gdb_st_mtime; /* time of last modification */ - gdb_time_t gdb_st_ctime; /* time of last change */ -} QEMU_PACKED; - -struct gdb_timeval { - gdb_time_t tv_sec; /* second */ - uint64_t tv_usec; /* microsecond */ -} QEMU_PACKED; - static int translate_openflags(int flags) { int hf; @@ -90,11 +66,13 @@ static int translate_openflags(int flags) static void translate_stat(CPUM68KState *env, target_ulong addr, struct stat *s) { - struct m68k_gdb_stat *p; + struct gdb_stat *p; - if (!(p = lock_user(VERIFY_WRITE, addr, sizeof(struct m68k_gdb_stat), 0))) + p = lock_user(VERIFY_WRITE, addr, sizeof(struct gdb_stat), 0); + if (!p) { /* FIXME - should this return an error code? */ return; + } 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); @@ -114,7 +92,7 @@ static void translate_stat(CPUM68KState *env, target_ulong addr, struct stat *s) 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 m68k_gdb_stat)); + unlock_user(p, addr, sizeof(struct gdb_stat)); } static void m68k_semi_return_u32(CPUM68KState *env, uint32_t ret, uint32_t err) diff --git a/target/nios2/nios2-semi.c b/target/nios2/nios2-semi.c index 0eec1f9a1c..3e504a6c5f 100644 --- a/target/nios2/nios2-semi.c +++ b/target/nios2/nios2-semi.c @@ -47,30 +47,6 @@ #define HOSTED_ISATTY 12 #define HOSTED_SYSTEM 13 -typedef uint32_t gdb_mode_t; -typedef uint32_t gdb_time_t; - -struct nios2_gdb_stat { - uint32_t gdb_st_dev; /* device */ - uint32_t gdb_st_ino; /* inode */ - gdb_mode_t gdb_st_mode; /* protection */ - uint32_t gdb_st_nlink; /* number of hard links */ - uint32_t gdb_st_uid; /* user ID of owner */ - uint32_t gdb_st_gid; /* group ID of owner */ - uint32_t gdb_st_rdev; /* device type (if inode device) */ - uint64_t gdb_st_size; /* total size, in bytes */ - uint64_t gdb_st_blksize; /* blocksize for filesystem I/O */ - uint64_t gdb_st_blocks; /* number of blocks allocated */ - gdb_time_t gdb_st_atime; /* time of last access */ - gdb_time_t gdb_st_mtime; /* time of last modification */ - gdb_time_t gdb_st_ctime; /* time of last change */ -} QEMU_PACKED; - -struct gdb_timeval { - gdb_time_t tv_sec; /* second */ - uint64_t tv_usec; /* microsecond */ -} QEMU_PACKED; - static int translate_openflags(int flags) { int hf; @@ -102,9 +78,9 @@ static int translate_openflags(int flags) static bool translate_stat(CPUNios2State *env, target_ulong addr, struct stat *s) { - struct nios2_gdb_stat *p; + struct gdb_stat *p; - p = lock_user(VERIFY_WRITE, addr, sizeof(struct nios2_gdb_stat), 0); + p = lock_user(VERIFY_WRITE, addr, sizeof(struct gdb_stat), 0); if (!p) { return false; @@ -128,7 +104,7 @@ static bool translate_stat(CPUNios2State *env, target_ulong addr, 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 nios2_gdb_stat)); + unlock_user(p, addr, sizeof(struct gdb_stat)); return true; } From 9814483d6346fb697c9f852f3a1edab0b6910486 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 May 2022 18:35:18 -0700 Subject: [PATCH 18/60] include/exec: Define errno values in gdbstub.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define constants for the errno values defined by the gdb remote fileio protocol. Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- include/exec/gdbstub.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index 7413ffeba2..95a8b7b056 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -19,6 +19,28 @@ #define GDB_O_TRUNC 0x400 #define GDB_O_EXCL 0x800 +/* For gdb file i/o remote protocol errno values */ +#define GDB_EPERM 1 +#define GDB_ENOENT 2 +#define GDB_EINTR 4 +#define GDB_EBADF 9 +#define GDB_EACCES 13 +#define GDB_EFAULT 14 +#define GDB_EBUSY 16 +#define GDB_EEXIST 17 +#define GDB_ENODEV 19 +#define GDB_ENOTDIR 20 +#define GDB_EISDIR 21 +#define GDB_EINVAL 22 +#define GDB_ENFILE 23 +#define GDB_EMFILE 24 +#define GDB_EFBIG 27 +#define GDB_ENOSPC 28 +#define GDB_ESPIPE 29 +#define GDB_EROFS 30 +#define GDB_ENAMETOOLONG 91 +#define GDB_EUNKNOWN 9999 + /* For gdb file i/o stat/fstat. */ typedef uint32_t gdb_mode_t; typedef uint32_t gdb_time_t; From c805e118754a2c90c29219985ba389829d4a53a4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jun 2022 12:38:26 -0700 Subject: [PATCH 19/60] gdbstub: Convert GDB error numbers to host error numbers Provide the callback with consistent state -- always use host error numbers. The individual callback can then decide if the errno requires conversion for the guest. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- gdbstub.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/gdbstub.c b/gdbstub.c index 88a34c8f52..f3a4664453 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1886,6 +1886,37 @@ static void handle_file_io(GArray *params, void *user_ctx) } else { err = 0; } + + /* Convert GDB error numbers back to host error numbers. */ +#define E(X) case GDB_E##X: err = E##X; break + switch (err) { + case 0: + break; + 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: + err = EINVAL; + break; + } +#undef E + gdbserver_state.current_syscall_cb(gdbserver_state.c_cpu, ret, err); gdbserver_state.current_syscall_cb = NULL; } From cd7f29e335a88c2803bfb42ee3cef60417c43274 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Apr 2022 14:55:21 -0700 Subject: [PATCH 20/60] semihosting: Use struct gdb_stat in common_semi_flen_cb Load the entire 64-bit size value. While we're at it, use offsetof instead of an integer constant. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- semihosting/arm-compat-semi.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index abf543ce91..a9e488886a 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -325,14 +325,12 @@ static void common_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err) { if (!err) { - /* - * The size is always stored in big-endian order, extract - * the value. We assume the size always fit in 32 bits. - */ - uint32_t size; - cpu_memory_rw_debug(cs, common_semi_flen_buf(cs) + 32, - (uint8_t *)&size, 4, 0); - ret = be32_to_cpu(size); + /* The size is always stored in big-endian order, extract the value. */ + uint64_t size; + cpu_memory_rw_debug(cs, common_semi_flen_buf(cs) + + offsetof(struct gdb_stat, gdb_st_size), + &size, 8, 0); + ret = be64_to_cpu(size); } common_semi_cb(cs, ret, err); } From ef9c5ea85d83459361449259418726a5d4d0a751 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Apr 2022 11:31:04 -0700 Subject: [PATCH 21/60] semihosting: Split is_64bit_semihosting per target We already have some larger ifdef blocks for ARM and RISCV; split the function into multiple implementations per arch. Reviewed-by: Peter Maydell Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson --- semihosting/arm-compat-semi.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index a9e488886a..d2ce214078 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -213,6 +213,10 @@ common_semi_sys_exit_extended(CPUState *cs, int nr) return (nr == TARGET_SYS_EXIT_EXTENDED || is_a64(cs->env_ptr)); } +static inline bool is_64bit_semihosting(CPUArchState *env) +{ + return is_a64(env); +} #endif /* TARGET_ARM */ #ifdef TARGET_RISCV @@ -238,6 +242,10 @@ common_semi_sys_exit_extended(CPUState *cs, int nr) return (nr == TARGET_SYS_EXIT_EXTENDED || sizeof(target_ulong) == 8); } +static inline bool is_64bit_semihosting(CPUArchState *env) +{ + return riscv_cpu_mxl(env) != MXL_RV32; +} #endif /* @@ -587,17 +595,6 @@ static const GuestFDFunctions guestfd_fns[] = { * call if the memory read fails. Eventually we could use a generic * CPUState helper function here. */ -static inline bool is_64bit_semihosting(CPUArchState *env) -{ -#if defined(TARGET_ARM) - return is_a64(env); -#elif defined(TARGET_RISCV) - return riscv_cpu_mxl(env) != MXL_RV32; -#else -#error un-handled architecture -#endif -} - #define GET_ARG(n) do { \ if (is_64bit_semihosting(env)) { \ From 3c820ddc1b92f17fea85fdaed2928feaa9c238d7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Apr 2022 13:59:52 -0700 Subject: [PATCH 22/60] semihosting: Split common_semi_flen_buf per target We already have some larger ifdef blocks for ARM and RISCV; split out common_semi_stack_bottom per target. Reviewed-by: Peter Maydell Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson --- semihosting/arm-compat-semi.c | 44 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index d2ce214078..7550dce622 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -217,6 +217,13 @@ static inline bool is_64bit_semihosting(CPUArchState *env) { return is_a64(env); } + +static inline target_ulong common_semi_stack_bottom(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + return is_a64(env) ? env->xregs[31] : env->regs[13]; +} #endif /* TARGET_ARM */ #ifdef TARGET_RISCV @@ -246,6 +253,13 @@ static inline bool is_64bit_semihosting(CPUArchState *env) { return riscv_cpu_mxl(env) != MXL_RV32; } + +static inline target_ulong common_semi_stack_bottom(CPUState *cs) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + return env->gpr[xSP]; +} #endif /* @@ -301,31 +315,15 @@ static void common_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) common_semi_set_ret(cs, ret); } +/* + * Return an address in target memory of 64 bytes where the remote + * gdb should write its stat struct. (The format of this structure + * is defined by GDB's remote protocol and is not target-specific.) + * We put this on the guest's stack just below SP. + */ static target_ulong common_semi_flen_buf(CPUState *cs) { - target_ulong sp; -#ifdef TARGET_ARM - /* Return an address in target memory of 64 bytes where the remote - * gdb should write its stat struct. (The format of this structure - * is defined by GDB's remote protocol and is not target-specific.) - * We put this on the guest's stack just below SP. - */ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - - if (is_a64(env)) { - sp = env->xregs[31]; - } else { - sp = env->regs[13]; - } -#endif -#ifdef TARGET_RISCV - RISCVCPU *cpu = RISCV_CPU(cs); - CPURISCVState *env = &cpu->env; - - sp = env->gpr[xSP]; -#endif - + target_ulong sp = common_semi_stack_bottom(cs); return sp - 64; } From a1df4bab432fc62a0d14abc192ce063c432afd2e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Apr 2022 14:16:01 -0700 Subject: [PATCH 23/60] semihosting: Split out common_semi_has_synccache We already have some larger ifdef blocks for ARM and RISCV; split out a boolean test for SYS_SYNCCACHE. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson --- semihosting/arm-compat-semi.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 7550dce622..50f40a2a1a 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -224,6 +224,12 @@ static inline target_ulong common_semi_stack_bottom(CPUState *cs) CPUARMState *env = &cpu->env; return is_a64(env) ? env->xregs[31] : env->regs[13]; } + +static inline bool common_semi_has_synccache(CPUArchState *env) +{ + /* Ok for A64, invalid for A32/T32. */ + return is_a64(env); +} #endif /* TARGET_ARM */ #ifdef TARGET_RISCV @@ -260,6 +266,11 @@ static inline target_ulong common_semi_stack_bottom(CPUState *cs) CPURISCVState *env = &cpu->env; return env->gpr[xSP]; } + +static inline bool common_semi_has_synccache(CPUArchState *env) +{ + return true; +} #endif /* @@ -1102,16 +1113,11 @@ void do_common_semihosting(CPUState *cs) * virtual address range. This is a nop for us since we don't * implement caches. This is only present on A64. */ -#ifdef TARGET_ARM - if (is_a64(cs->env_ptr)) { + if (common_semi_has_synccache(env)) { common_semi_set_ret(cs, 0); break; } -#endif -#ifdef TARGET_RISCV - common_semi_set_ret(cs, 0); -#endif - /* fall through -- invalid for A32/T32 */ + /* fall through */ default: fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr); cpu_dump_state(cs, stderr, 0); From 1b3b7693b7c3f94bd66a8425201a4bb7de5388e4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jun 2022 10:31:22 -0700 Subject: [PATCH 24/60] semihosting: Split out common-semi-target.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the ARM and RISCV specific helpers into their own header file. Reviewed-by: Alex Bennée Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- semihosting/arm-compat-semi.c | 94 +------------------------------ target/arm/common-semi-target.h | 62 ++++++++++++++++++++ target/riscv/common-semi-target.h | 50 ++++++++++++++++ 3 files changed, 113 insertions(+), 93 deletions(-) create mode 100644 target/arm/common-semi-target.h create mode 100644 target/riscv/common-semi-target.h diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 50f40a2a1a..5e442e549d 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -46,9 +46,6 @@ #else #include "qemu/cutils.h" #include "hw/loader.h" -#ifdef TARGET_ARM -#include "hw/arm/boot.h" -#endif #include "hw/boards.h" #endif @@ -182,96 +179,7 @@ static LayoutInfo common_semi_find_bases(CPUState *cs) #endif -#ifdef TARGET_ARM -static inline target_ulong -common_semi_arg(CPUState *cs, int argno) -{ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - if (is_a64(env)) { - return env->xregs[argno]; - } else { - return env->regs[argno]; - } -} - -static inline void -common_semi_set_ret(CPUState *cs, target_ulong ret) -{ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - if (is_a64(env)) { - env->xregs[0] = ret; - } else { - env->regs[0] = ret; - } -} - -static inline bool -common_semi_sys_exit_extended(CPUState *cs, int nr) -{ - return (nr == TARGET_SYS_EXIT_EXTENDED || is_a64(cs->env_ptr)); -} - -static inline bool is_64bit_semihosting(CPUArchState *env) -{ - return is_a64(env); -} - -static inline target_ulong common_semi_stack_bottom(CPUState *cs) -{ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - return is_a64(env) ? env->xregs[31] : env->regs[13]; -} - -static inline bool common_semi_has_synccache(CPUArchState *env) -{ - /* Ok for A64, invalid for A32/T32. */ - return is_a64(env); -} -#endif /* TARGET_ARM */ - -#ifdef TARGET_RISCV -static inline target_ulong -common_semi_arg(CPUState *cs, int argno) -{ - RISCVCPU *cpu = RISCV_CPU(cs); - CPURISCVState *env = &cpu->env; - return env->gpr[xA0 + argno]; -} - -static inline void -common_semi_set_ret(CPUState *cs, target_ulong ret) -{ - RISCVCPU *cpu = RISCV_CPU(cs); - CPURISCVState *env = &cpu->env; - env->gpr[xA0] = ret; -} - -static inline bool -common_semi_sys_exit_extended(CPUState *cs, int nr) -{ - return (nr == TARGET_SYS_EXIT_EXTENDED || sizeof(target_ulong) == 8); -} - -static inline bool is_64bit_semihosting(CPUArchState *env) -{ - return riscv_cpu_mxl(env) != MXL_RV32; -} - -static inline target_ulong common_semi_stack_bottom(CPUState *cs) -{ - RISCVCPU *cpu = RISCV_CPU(cs); - CPURISCVState *env = &cpu->env; - return env->gpr[xSP]; -} - -static inline bool common_semi_has_synccache(CPUArchState *env) -{ - return true; -} -#endif +#include "common-semi-target.h" /* * The semihosting API has no concept of its errno being thread-safe, diff --git a/target/arm/common-semi-target.h b/target/arm/common-semi-target.h new file mode 100644 index 0000000000..629d75ca5a --- /dev/null +++ b/target/arm/common-semi-target.h @@ -0,0 +1,62 @@ +/* + * Target-specific parts of semihosting/arm-compat-semi.c. + * + * Copyright (c) 2005, 2007 CodeSourcery. + * Copyright (c) 2019, 2022 Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TARGET_ARM_COMMON_SEMI_TARGET_H +#define TARGET_ARM_COMMON_SEMI_TARGET_H + +#ifndef CONFIG_USER_ONLY +#include "hw/arm/boot.h" +#endif + +static inline target_ulong common_semi_arg(CPUState *cs, int argno) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + if (is_a64(env)) { + return env->xregs[argno]; + } else { + return env->regs[argno]; + } +} + +static inline void common_semi_set_ret(CPUState *cs, target_ulong ret) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + if (is_a64(env)) { + env->xregs[0] = ret; + } else { + env->regs[0] = ret; + } +} + +static inline bool common_semi_sys_exit_extended(CPUState *cs, int nr) +{ + return (nr == TARGET_SYS_EXIT_EXTENDED || is_a64(cs->env_ptr)); +} + +static inline bool is_64bit_semihosting(CPUArchState *env) +{ + return is_a64(env); +} + +static inline target_ulong common_semi_stack_bottom(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + return is_a64(env) ? env->xregs[31] : env->regs[13]; +} + +static inline bool common_semi_has_synccache(CPUArchState *env) +{ + /* Ok for A64, invalid for A32/T32 */ + return is_a64(env); +} + +#endif diff --git a/target/riscv/common-semi-target.h b/target/riscv/common-semi-target.h new file mode 100644 index 0000000000..7c8a59e0cc --- /dev/null +++ b/target/riscv/common-semi-target.h @@ -0,0 +1,50 @@ +/* + * Target-specific parts of semihosting/arm-compat-semi.c. + * + * Copyright (c) 2005, 2007 CodeSourcery. + * Copyright (c) 2019, 2022 Linaro + * Copyright © 2020 by Keith Packard + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TARGET_RISCV_COMMON_SEMI_TARGET_H +#define TARGET_RISCV_COMMON_SEMI_TARGET_H + +static inline target_ulong common_semi_arg(CPUState *cs, int argno) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + return env->gpr[xA0 + argno]; +} + +static inline void common_semi_set_ret(CPUState *cs, target_ulong ret) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + env->gpr[xA0] = ret; +} + +static inline bool common_semi_sys_exit_extended(CPUState *cs, int nr) +{ + return (nr == TARGET_SYS_EXIT_EXTENDED || sizeof(target_ulong) == 8); +} + +static inline bool is_64bit_semihosting(CPUArchState *env) +{ + return riscv_cpu_mxl(env) != MXL_RV32; +} + +static inline target_ulong common_semi_stack_bottom(CPUState *cs) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + return env->gpr[xSP]; +} + +static inline bool common_semi_has_synccache(CPUArchState *env) +{ + return true; +} + +#endif From 189878ae237d443571250f76655161d91c018889 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Apr 2022 14:18:40 -0700 Subject: [PATCH 25/60] semihosting: Use env more often in do_common_semihosting We've already loaded cs->env_ptr into a local variable; use it. Since env is unconditionally used, we don't need a dummy use. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- semihosting/arm-compat-semi.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 5e442e549d..adb4e5b581 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -553,7 +553,6 @@ void do_common_semihosting(CPUState *cs) GuestFD *gf; int64_t elapsed; - (void) env; /* Used implicitly by arm lock_user macro */ nr = common_semi_arg(cs, 0) & 0xffffffffU; args = common_semi_arg(cs, 1); @@ -636,12 +635,12 @@ void do_common_semihosting(CPUState *cs) break; case TARGET_SYS_WRITEC: - qemu_semihosting_console_outc(cs->env_ptr, args); + qemu_semihosting_console_outc(env, args); common_semi_set_ret(cs, 0xdeadbeef); break; case TARGET_SYS_WRITE0: - ret = qemu_semihosting_console_outs(cs->env_ptr, args); + ret = qemu_semihosting_console_outs(env, args); common_semi_set_ret(cs, ret); break; @@ -672,7 +671,7 @@ void do_common_semihosting(CPUState *cs) break; case TARGET_SYS_READC: - ret = qemu_semihosting_console_inc(cs->env_ptr); + ret = qemu_semihosting_console_inc(env); common_semi_set_ret(cs, ret); break; From 3753b00e5747068882c7f0302dcf9b87402993ab Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Apr 2022 11:40:14 -0700 Subject: [PATCH 26/60] semihosting: Move GET_ARG/SET_ARG earlier in the file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moving this to be useful for another function besides do_common_semihosting. Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- semihosting/arm-compat-semi.c | 48 +++++++++++++++++------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index adb4e5b581..72a1350512 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -181,6 +181,30 @@ static LayoutInfo common_semi_find_bases(CPUState *cs) #include "common-semi-target.h" +/* + * Read the input value from the argument block; fail the semihosting + * call if the memory read fails. Eventually we could use a generic + * CPUState helper function here. + */ + +#define GET_ARG(n) do { \ + if (is_64bit_semihosting(env)) { \ + if (get_user_u64(arg ## n, args + (n) * 8)) { \ + goto do_fault; \ + } \ + } else { \ + if (get_user_u32(arg ## n, args + (n) * 4)) { \ + goto do_fault; \ + } \ + } \ +} while (0) + +#define SET_ARG(n, val) \ + (is_64bit_semihosting(env) ? \ + put_user_u64(val, args + (n) * 8) : \ + put_user_u32(val, args + (n) * 4)) + + /* * 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 @@ -507,30 +531,6 @@ static const GuestFDFunctions guestfd_fns[] = { }, }; -/* - * Read the input value from the argument block; fail the semihosting - * call if the memory read fails. Eventually we could use a generic - * CPUState helper function here. - */ - -#define GET_ARG(n) do { \ - if (is_64bit_semihosting(env)) { \ - if (get_user_u64(arg ## n, args + (n) * 8)) { \ - goto do_fault; \ - } \ - } else { \ - if (get_user_u32(arg ## n, args + (n) * 4)) { \ - goto do_fault; \ - } \ - } \ -} while (0) - -#define SET_ARG(n, val) \ - (is_64bit_semihosting(env) ? \ - put_user_u64(val, args + (n) * 8) : \ - put_user_u32(val, args + (n) * 4)) - - /* * Do a semihosting call. * From 5b3f39cb04fda32226e84502f858bab06d83e5c1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Apr 2022 01:44:28 -0700 Subject: [PATCH 27/60] semihosting: Split out semihost_sys_open Split out the non-ARM specific portions of SYS_OPEN to a reusable function. This handles gdb and host file i/o. Add helpers to validate the length of the filename string. Prepare for usage by other semihosting by allowing the filename length parameter to be 0, and calling strlen. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- include/semihosting/syscalls.h | 25 ++++++ semihosting/arm-compat-semi.c | 53 ++--------- semihosting/guestfd.c | 5 ++ semihosting/meson.build | 1 + semihosting/syscalls.c | 156 +++++++++++++++++++++++++++++++++ 5 files changed, 194 insertions(+), 46 deletions(-) create mode 100644 include/semihosting/syscalls.h create mode 100644 semihosting/syscalls.c diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h new file mode 100644 index 0000000000..991658bf79 --- /dev/null +++ b/include/semihosting/syscalls.h @@ -0,0 +1,25 @@ +/* + * Syscall implementations for semihosting. + * + * Copyright (c) 2022 Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SEMIHOSTING_SYSCALLS_H +#define SEMIHOSTING_SYSCALLS_H + +/* + * Argument loading from the guest is performed by the caller; + * results are returned via the 'complete' callback. + * + * String operands are in address/len pairs. The len argument may be 0 + * (when the semihosting abi does not already provide the length), + * or non-zero (where it should include the terminating zero). + */ + +void semihost_sys_open(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + int gdb_flags, int mode); + +#endif /* SEMIHOSTING_SYSCALLS_H */ diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 72a1350512..07960658d8 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -32,12 +32,13 @@ */ #include "qemu/osdep.h" +#include "qemu/timer.h" +#include "exec/gdbstub.h" #include "semihosting/semihost.h" #include "semihosting/console.h" #include "semihosting/common-semi.h" #include "semihosting/guestfd.h" -#include "qemu/timer.h" -#include "exec/gdbstub.h" +#include "semihosting/syscalls.h" #ifdef CONFIG_USER_ONLY #include "qemu.h" @@ -98,21 +99,6 @@ static int gdb_open_modeflags[12] = { GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND, }; -static int open_modeflags[12] = { - O_RDONLY, - O_RDONLY | O_BINARY, - O_RDWR, - O_RDWR | O_BINARY, - O_WRONLY | O_CREAT | O_TRUNC, - O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, - O_RDWR | O_CREAT | O_TRUNC, - O_RDWR | O_CREAT | O_TRUNC | O_BINARY, - O_WRONLY | O_CREAT | O_APPEND, - O_WRONLY | O_CREAT | O_APPEND | O_BINARY, - O_RDWR | O_CREAT | O_APPEND, - O_RDWR | O_CREAT | O_APPEND | O_BINARY -}; - #ifndef CONFIG_USER_ONLY /** @@ -284,20 +270,6 @@ common_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err) common_semi_cb(cs, ret, err); } -static int common_semi_open_guestfd; - -static void -common_semi_open_cb(CPUState *cs, target_ulong ret, target_ulong err) -{ - if (err) { - dealloc_guestfd(common_semi_open_guestfd); - } else { - associate_guestfd(common_semi_open_guestfd, ret); - ret = common_semi_open_guestfd; - } - common_semi_cb(cs, ret, err); -} - /* * Types for functions implementing various semihosting calls * for specific types of guest file descriptor. These must all @@ -601,22 +573,11 @@ void do_common_semihosting(CPUState *cs) staticfile_guestfd(ret, featurefile_data, sizeof(featurefile_data)); } - } else if (use_gdb_syscalls()) { - unlock_user(s, arg0, 0); - common_semi_open_guestfd = alloc_guestfd(); - gdb_do_syscall(common_semi_open_cb, - "open,%s,%x,1a4", arg0, (int)arg2 + 1, - gdb_open_modeflags[arg1]); - break; } else { - hostfd = open(s, open_modeflags[arg1], 0644); - if (hostfd < 0) { - ret = -1; - err = errno; - } else { - ret = alloc_guestfd(); - associate_guestfd(ret, hostfd); - } + unlock_user(s, arg0, 0); + semihost_sys_open(cs, common_semi_cb, arg0, arg2 + 1, + gdb_open_modeflags[arg1], 0644); + break; } unlock_user(s, arg0, 0); common_semi_cb(cs, ret, err); diff --git a/semihosting/guestfd.c b/semihosting/guestfd.c index b6405f5663..7ac2e147a8 100644 --- a/semihosting/guestfd.c +++ b/semihosting/guestfd.c @@ -11,6 +11,11 @@ #include "qemu/osdep.h" #include "exec/gdbstub.h" #include "semihosting/guestfd.h" +#ifdef CONFIG_USER_ONLY +#include "qemu.h" +#else +#include "semihosting/softmmu-uaccess.h" +#endif static GArray *guestfd_array; diff --git a/semihosting/meson.build b/semihosting/meson.build index d2c1c37bfd..8057db5494 100644 --- a/semihosting/meson.build +++ b/semihosting/meson.build @@ -1,5 +1,6 @@ specific_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( 'guestfd.c', + 'syscalls.c', )) specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SOFTMMU'], if_true: files( diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c new file mode 100644 index 0000000000..9f9d19a59a --- /dev/null +++ b/semihosting/syscalls.c @@ -0,0 +1,156 @@ +/* + * Syscall implementations for semihosting. + * + * Copyright (c) 2022 Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "exec/gdbstub.h" +#include "semihosting/guestfd.h" +#include "semihosting/syscalls.h" +#ifdef CONFIG_USER_ONLY +#include "qemu.h" +#else +#include "semihosting/softmmu-uaccess.h" +#endif + + +/* + * Validate or compute the length of the string (including terminator). + */ +static int validate_strlen(CPUState *cs, target_ulong str, target_ulong tlen) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + char c; + + if (tlen == 0) { + ssize_t slen = target_strlen(str); + + if (slen < 0) { + return -EFAULT; + } + if (slen >= INT32_MAX) { + return -ENAMETOOLONG; + } + return slen + 1; + } + if (tlen > INT32_MAX) { + return -ENAMETOOLONG; + } + if (get_user_u8(c, str + tlen - 1)) { + return -EFAULT; + } + if (c != 0) { + return -EINVAL; + } + return tlen; +} + +static int validate_lock_user_string(char **pstr, CPUState *cs, + target_ulong tstr, target_ulong tlen) +{ + int ret = validate_strlen(cs, tstr, tlen); + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + char *str = NULL; + + if (ret > 0) { + str = lock_user(VERIFY_READ, tstr, ret, true); + ret = str ? 0 : -EFAULT; + } + *pstr = str; + return ret; +} + +/* + * GDB semihosting syscall implementations. + */ + +static gdb_syscall_complete_cb gdb_open_complete; + +static void gdb_open_cb(CPUState *cs, target_ulong ret, target_ulong err) +{ + if (!err) { + int guestfd = alloc_guestfd(); + associate_guestfd(guestfd, ret); + ret = guestfd; + } + gdb_open_complete(cs, ret, err); +} + +static void gdb_open(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + int gdb_flags, int mode) +{ + int len = validate_strlen(cs, fname, fname_len); + if (len < 0) { + complete(cs, -1, -len); + return; + } + + gdb_open_complete = complete; + gdb_do_syscall(gdb_open_cb, "open,%s,%x,%x", + fname, len, (target_ulong)gdb_flags, (target_ulong)mode); +} + +/* + * Host semihosting syscall implementations. + */ + +static void host_open(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + int gdb_flags, int mode) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + char *p; + int ret, host_flags; + + ret = validate_lock_user_string(&p, cs, fname, fname_len); + if (ret < 0) { + complete(cs, -1, -ret); + return; + } + + if (gdb_flags & GDB_O_WRONLY) { + host_flags = O_WRONLY; + } else if (gdb_flags & GDB_O_RDWR) { + host_flags = O_RDWR; + } else { + host_flags = O_RDONLY; + } + if (gdb_flags & GDB_O_CREAT) { + host_flags |= O_CREAT; + } + if (gdb_flags & GDB_O_TRUNC) { + host_flags |= O_TRUNC; + } + if (gdb_flags & GDB_O_EXCL) { + host_flags |= O_EXCL; + } + + ret = open(p, host_flags, mode); + if (ret < 0) { + complete(cs, -1, errno); + } else { + int guestfd = alloc_guestfd(); + associate_guestfd(guestfd, ret); + complete(cs, guestfd, 0); + } + unlock_user(p, fname, 0); +} + +/* + * Syscall entry points. + */ + +void semihost_sys_open(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + int gdb_flags, int mode) +{ + if (use_gdb_syscalls()) { + gdb_open(cs, complete, fname, fname_len, gdb_flags, mode); + } else { + host_open(cs, complete, fname, fname_len, gdb_flags, mode); + } +} From 5eadbbfca6232710036ccb9f5cf1481be537c0e7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Apr 2022 09:22:18 -0700 Subject: [PATCH 28/60] semihosting: Split out semihost_sys_close Split out the non-ARM specific portions of SYS_CLOSE to a reusable function. This handles all GuestFD. Note that gdb_do_syscall %x reads target_ulong, not int. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- include/semihosting/syscalls.h | 3 +++ semihosting/arm-compat-semi.c | 41 +---------------------------- semihosting/guestfd.c | 7 ++++- semihosting/syscalls.c | 47 ++++++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 41 deletions(-) diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h index 991658bf79..00e718f11d 100644 --- a/include/semihosting/syscalls.h +++ b/include/semihosting/syscalls.h @@ -22,4 +22,7 @@ void semihost_sys_open(CPUState *cs, gdb_syscall_complete_cb complete, target_ulong fname, target_ulong fname_len, int gdb_flags, int mode); +void semihost_sys_close(CPUState *cs, gdb_syscall_complete_cb complete, + int fd); + #endif /* SEMIHOSTING_SYSCALLS_H */ diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 07960658d8..0cb3db2a1a 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -276,7 +276,6 @@ common_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err) * do the work and return the required return value to the guest * via common_semi_cb. */ -typedef void sys_closefn(CPUState *cs, GuestFD *gf); typedef void sys_writefn(CPUState *cs, GuestFD *gf, target_ulong buf, uint32_t len); typedef void sys_readfn(CPUState *cs, GuestFD *gf, @@ -285,23 +284,6 @@ typedef void sys_isattyfn(CPUState *cs, GuestFD *gf); typedef void sys_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset); typedef void sys_flenfn(CPUState *cs, GuestFD *gf); -static void host_closefn(CPUState *cs, GuestFD *gf) -{ - int ret; - /* - * Only close the underlying host fd if it's one we opened on behalf - * of the guest in SYS_OPEN. - */ - if (gf->hostfd == STDIN_FILENO || - gf->hostfd == STDOUT_FILENO || - gf->hostfd == STDERR_FILENO) { - ret = 0; - } else { - ret = close(gf->hostfd); - } - common_semi_cb(cs, ret, ret ? errno : 0); -} - static void host_writefn(CPUState *cs, GuestFD *gf, target_ulong buf, uint32_t len) { @@ -362,11 +344,6 @@ static void host_flenfn(CPUState *cs, GuestFD *gf) } } -static void gdb_closefn(CPUState *cs, GuestFD *gf) -{ - gdb_do_syscall(common_semi_cb, "close,%x", gf->hostfd); -} - static void gdb_writefn(CPUState *cs, GuestFD *gf, target_ulong buf, uint32_t len) { @@ -414,12 +391,6 @@ static const uint8_t featurefile_data[] = { SH_EXT_EXIT_EXTENDED | SH_EXT_STDOUT_STDERR, /* Feature byte 0 */ }; -static void staticfile_closefn(CPUState *cs, GuestFD *gf) -{ - /* Nothing to do */ - common_semi_cb(cs, 0, 0); -} - static void staticfile_writefn(CPUState *cs, GuestFD *gf, target_ulong buf, uint32_t len) { @@ -468,7 +439,6 @@ static void staticfile_flenfn(CPUState *cs, GuestFD *gf) } typedef struct GuestFDFunctions { - sys_closefn *closefn; sys_writefn *writefn; sys_readfn *readfn; sys_isattyfn *isattyfn; @@ -478,7 +448,6 @@ typedef struct GuestFDFunctions { static const GuestFDFunctions guestfd_fns[] = { [GuestFDHost] = { - .closefn = host_closefn, .writefn = host_writefn, .readfn = host_readfn, .isattyfn = host_isattyfn, @@ -486,7 +455,6 @@ static const GuestFDFunctions guestfd_fns[] = { .flenfn = host_flenfn, }, [GuestFDGDB] = { - .closefn = gdb_closefn, .writefn = gdb_writefn, .readfn = gdb_readfn, .isattyfn = gdb_isattyfn, @@ -494,7 +462,6 @@ static const GuestFDFunctions guestfd_fns[] = { .flenfn = gdb_flenfn, }, [GuestFDStatic] = { - .closefn = staticfile_closefn, .writefn = staticfile_writefn, .readfn = staticfile_readfn, .isattyfn = staticfile_isattyfn, @@ -586,13 +553,7 @@ void do_common_semihosting(CPUState *cs) case TARGET_SYS_CLOSE: GET_ARG(0); - - gf = get_guestfd(arg0); - if (!gf) { - goto do_badf; - } - guestfd_fns[gf->type].closefn(cs, gf); - dealloc_guestfd(arg0); + semihost_sys_close(cs, common_semi_cb, arg0); break; case TARGET_SYS_WRITEC: diff --git a/semihosting/guestfd.c b/semihosting/guestfd.c index 7ac2e147a8..e3122ebba9 100644 --- a/semihosting/guestfd.c +++ b/semihosting/guestfd.c @@ -49,6 +49,11 @@ int alloc_guestfd(void) return i; } +static void do_dealloc_guestfd(GuestFD *gf) +{ + gf->type = GuestFDUnused; +} + /* * Look up the guestfd in the data structure; return NULL * for out of bounds, but don't check whether the slot is unused. @@ -119,5 +124,5 @@ void dealloc_guestfd(int guestfd) GuestFD *gf = do_get_guestfd(guestfd); assert(gf); - gf->type = GuestFDUnused; + do_dealloc_guestfd(gf); } diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index 9f9d19a59a..3648b9dd49 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -94,6 +94,12 @@ static void gdb_open(CPUState *cs, gdb_syscall_complete_cb complete, fname, len, (target_ulong)gdb_flags, (target_ulong)mode); } +static void gdb_close(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf) +{ + gdb_do_syscall(complete, "close,%x", (target_ulong)gf->hostfd); +} + /* * Host semihosting syscall implementations. */ @@ -140,6 +146,23 @@ static void host_open(CPUState *cs, gdb_syscall_complete_cb complete, unlock_user(p, fname, 0); } +static void host_close(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf) +{ + /* + * Only close the underlying host fd if it's one we opened on behalf + * of the guest in SYS_OPEN. + */ + if (gf->hostfd != STDIN_FILENO && + gf->hostfd != STDOUT_FILENO && + gf->hostfd != STDERR_FILENO && + close(gf->hostfd) < 0) { + complete(cs, -1, errno); + } else { + complete(cs, 0, 0); + } +} + /* * Syscall entry points. */ @@ -154,3 +177,27 @@ void semihost_sys_open(CPUState *cs, gdb_syscall_complete_cb complete, host_open(cs, complete, fname, fname_len, gdb_flags, mode); } } + +void semihost_sys_close(CPUState *cs, gdb_syscall_complete_cb complete, int fd) +{ + GuestFD *gf = get_guestfd(fd); + + if (!gf) { + complete(cs, -1, EBADF); + return; + } + switch (gf->type) { + case GuestFDGDB: + gdb_close(cs, complete, gf); + break; + case GuestFDHost: + host_close(cs, complete, gf); + break; + case GuestFDStatic: + complete(cs, 0, 0); + break; + default: + g_assert_not_reached(); + } + dealloc_guestfd(fd); +} From af0484b5025f8b7c951428a00b5bb3f172a2da8d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Apr 2022 11:40:41 -0700 Subject: [PATCH 29/60] semihosting: Split out semihost_sys_read Split out the non-ARM specific portions of SYS_READ to a reusable function. This handles all GuestFD. Isolate the curious ARM-specific return value processing to a new callback, common_semi_rw_cb. Note that gdb_do_syscall %x reads target_ulong, not int. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- include/semihosting/syscalls.h | 8 ++++ semihosting/arm-compat-semi.c | 85 ++++++++-------------------------- semihosting/syscalls.c | 85 ++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 65 deletions(-) diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h index 00e718f11d..20da8138b0 100644 --- a/include/semihosting/syscalls.h +++ b/include/semihosting/syscalls.h @@ -18,6 +18,8 @@ * or non-zero (where it should include the terminating zero). */ +typedef struct GuestFD GuestFD; + void semihost_sys_open(CPUState *cs, gdb_syscall_complete_cb complete, target_ulong fname, target_ulong fname_len, int gdb_flags, int mode); @@ -25,4 +27,10 @@ void semihost_sys_open(CPUState *cs, gdb_syscall_complete_cb complete, void semihost_sys_close(CPUState *cs, gdb_syscall_complete_cb complete, int fd); +void semihost_sys_read(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, target_ulong buf, target_ulong len); + +void semihost_sys_read_gf(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len); + #endif /* SEMIHOSTING_SYSCALLS_H */ diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 0cb3db2a1a..8da31d8507 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -231,7 +231,6 @@ static void common_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) target_ulong reg0 = common_semi_arg(cs, 0); switch (reg0) { case TARGET_SYS_WRITE: - case TARGET_SYS_READ: ret = common_semi_syscall_len - ret; break; case TARGET_SYS_SEEK: @@ -244,6 +243,25 @@ static void common_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) common_semi_set_ret(cs, ret); } +/* + * SYS_READ and SYS_WRITE always return the number of bytes not read/written. + * There is no error condition, other than returning the original length. + */ +static void common_semi_rw_cb(CPUState *cs, target_ulong ret, target_ulong err) +{ + /* Recover the original length from the third argument. */ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + target_ulong args = common_semi_arg(cs, 1); + target_ulong arg2; + GET_ARG(2); + + if (err) { + do_fault: + ret = 0; /* error: no bytes transmitted */ + } + common_semi_set_ret(cs, arg2 - ret); +} + /* * Return an address in target memory of 64 bytes where the remote * gdb should write its stat struct. (The format of this structure @@ -278,8 +296,6 @@ common_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err) */ typedef void sys_writefn(CPUState *cs, GuestFD *gf, target_ulong buf, uint32_t len); -typedef void sys_readfn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len); typedef void sys_isattyfn(CPUState *cs, GuestFD *gf); typedef void sys_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset); typedef void sys_flenfn(CPUState *cs, GuestFD *gf); @@ -302,26 +318,6 @@ static void host_writefn(CPUState *cs, GuestFD *gf, common_semi_cb(cs, len - ret, 0); } -static void host_readfn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) -{ - CPUArchState *env = cs->env_ptr; - uint32_t ret = 0; - char *s = lock_user(VERIFY_WRITE, buf, len, 0); - (void) env; /* Used in arm softmmu lock_user implicitly */ - if (s) { - do { - ret = read(gf->hostfd, s, len); - } while (ret == -1 && errno == EINTR); - unlock_user(s, buf, len); - if (ret == (uint32_t)-1) { - ret = 0; - } - } - /* Return bytes not read, on error as well. */ - common_semi_cb(cs, len - ret, 0); -} - static void host_isattyfn(CPUState *cs, GuestFD *gf) { common_semi_cb(cs, isatty(gf->hostfd), 0); @@ -351,13 +347,6 @@ static void gdb_writefn(CPUState *cs, GuestFD *gf, gdb_do_syscall(common_semi_cb, "write,%x,%x,%x", gf->hostfd, buf, len); } -static void gdb_readfn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) -{ - common_semi_syscall_len = len; - gdb_do_syscall(common_semi_cb, "read,%x,%x,%x", gf->hostfd, buf, len); -} - static void gdb_isattyfn(CPUState *cs, GuestFD *gf) { gdb_do_syscall(common_semi_cb, "isatty,%x", gf->hostfd); @@ -398,30 +387,6 @@ static void staticfile_writefn(CPUState *cs, GuestFD *gf, common_semi_cb(cs, -1, EBADF); } -static void staticfile_readfn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) -{ - CPUArchState *env = cs->env_ptr; - uint32_t i = 0; - char *s; - - (void) env; /* Used in arm softmmu lock_user implicitly */ - s = lock_user(VERIFY_WRITE, buf, len, 0); - if (s) { - for (i = 0; i < len; i++) { - if (gf->staticfile.off >= gf->staticfile.len) { - break; - } - s[i] = gf->staticfile.data[gf->staticfile.off]; - gf->staticfile.off++; - } - unlock_user(s, buf, len); - } - - /* Return number of bytes not read */ - common_semi_cb(cs, len - i, 0); -} - static void staticfile_isattyfn(CPUState *cs, GuestFD *gf) { common_semi_cb(cs, 0, 0); @@ -440,7 +405,6 @@ static void staticfile_flenfn(CPUState *cs, GuestFD *gf) typedef struct GuestFDFunctions { sys_writefn *writefn; - sys_readfn *readfn; sys_isattyfn *isattyfn; sys_seekfn *seekfn; sys_flenfn *flenfn; @@ -449,21 +413,18 @@ typedef struct GuestFDFunctions { static const GuestFDFunctions guestfd_fns[] = { [GuestFDHost] = { .writefn = host_writefn, - .readfn = host_readfn, .isattyfn = host_isattyfn, .seekfn = host_seekfn, .flenfn = host_flenfn, }, [GuestFDGDB] = { .writefn = gdb_writefn, - .readfn = gdb_readfn, .isattyfn = gdb_isattyfn, .seekfn = gdb_seekfn, .flenfn = gdb_flenfn, }, [GuestFDStatic] = { .writefn = staticfile_writefn, - .readfn = staticfile_readfn, .isattyfn = staticfile_isattyfn, .seekfn = staticfile_seekfn, .flenfn = staticfile_flenfn, @@ -583,13 +544,7 @@ void do_common_semihosting(CPUState *cs) GET_ARG(0); GET_ARG(1); GET_ARG(2); - len = arg2; - - gf = get_guestfd(arg0); - if (!gf) { - goto do_badf; - } - guestfd_fns[gf->type].readfn(cs, gf, arg1, len); + semihost_sys_read(cs, common_semi_rw_cb, arg0, arg1, arg2); break; case TARGET_SYS_READC: diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index 3648b9dd49..d42a190746 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -100,6 +100,13 @@ static void gdb_close(CPUState *cs, gdb_syscall_complete_cb complete, gdb_do_syscall(complete, "close,%x", (target_ulong)gf->hostfd); } +static void gdb_read(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + gdb_do_syscall(complete, "read,%x,%x,%x", + (target_ulong)gf->hostfd, buf, len); +} + /* * Host semihosting syscall implementations. */ @@ -163,6 +170,54 @@ static void host_close(CPUState *cs, gdb_syscall_complete_cb complete, } } +static void host_read(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + void *ptr = lock_user(VERIFY_WRITE, buf, len, 0); + ssize_t ret; + + if (!ptr) { + complete(cs, -1, EFAULT); + return; + } + do { + ret = read(gf->hostfd, ptr, len); + } while (ret == -1 && errno == EINTR); + if (ret == -1) { + complete(cs, -1, errno); + unlock_user(ptr, buf, 0); + } else { + complete(cs, ret, 0); + unlock_user(ptr, buf, ret); + } +} + +/* + * Static file semihosting syscall implementations. + */ + +static void staticfile_read(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + target_ulong rest = gf->staticfile.len - gf->staticfile.off; + void *ptr; + + if (len > rest) { + len = rest; + } + ptr = lock_user(VERIFY_WRITE, buf, len, 0); + if (!ptr) { + complete(cs, -1, EFAULT); + return; + } + memcpy(ptr, gf->staticfile.data + gf->staticfile.off, len); + gf->staticfile.off += len; + complete(cs, len, 0); + unlock_user(ptr, buf, len); +} + /* * Syscall entry points. */ @@ -201,3 +256,33 @@ void semihost_sys_close(CPUState *cs, gdb_syscall_complete_cb complete, int fd) } dealloc_guestfd(fd); } + +void semihost_sys_read_gf(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + switch (gf->type) { + case GuestFDGDB: + gdb_read(cs, complete, gf, buf, len); + break; + case GuestFDHost: + host_read(cs, complete, gf, buf, len); + break; + case GuestFDStatic: + staticfile_read(cs, complete, gf, buf, len); + break; + default: + g_assert_not_reached(); + } +} + +void semihost_sys_read(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, target_ulong buf, target_ulong len) +{ + GuestFD *gf = get_guestfd(fd); + + if (gf) { + semihost_sys_read_gf(cs, complete, gf, buf, len); + } else { + complete(cs, -1, EBADF); + } +} From aa915bd0a67d6c0a214b45372ed841521c5cd07a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Apr 2022 11:49:47 -0700 Subject: [PATCH 30/60] semihosting: Split out semihost_sys_write Split out the non-ARM specific portions of SYS_WRITE to a reusable function. This handles all GuestFD. This removes the last use of common_semi_syscall_len. Note that gdb_do_syscall %x reads target_ulong, not int. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- include/semihosting/syscalls.h | 6 ++++ semihosting/arm-compat-semi.c | 52 +------------------------------- semihosting/syscalls.c | 54 ++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 51 deletions(-) diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h index 20da8138b0..2464467579 100644 --- a/include/semihosting/syscalls.h +++ b/include/semihosting/syscalls.h @@ -33,4 +33,10 @@ void semihost_sys_read(CPUState *cs, gdb_syscall_complete_cb complete, void semihost_sys_read_gf(CPUState *cs, gdb_syscall_complete_cb complete, GuestFD *gf, target_ulong buf, target_ulong len); +void semihost_sys_write(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, target_ulong buf, target_ulong len); + +void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len); + #endif /* SEMIHOSTING_SYSCALLS_H */ diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 8da31d8507..d591fcd7c2 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -215,8 +215,6 @@ static inline uint32_t get_swi_errno(CPUState *cs) #endif } -static target_ulong common_semi_syscall_len; - static void common_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) { if (err) { @@ -230,9 +228,6 @@ static void common_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) /* Fixup syscalls that use nonstardard return conventions. */ target_ulong reg0 = common_semi_arg(cs, 0); switch (reg0) { - case TARGET_SYS_WRITE: - ret = common_semi_syscall_len - ret; - break; case TARGET_SYS_SEEK: ret = 0; break; @@ -294,30 +289,10 @@ common_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err) * do the work and return the required return value to the guest * via common_semi_cb. */ -typedef void sys_writefn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len); typedef void sys_isattyfn(CPUState *cs, GuestFD *gf); typedef void sys_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset); typedef void sys_flenfn(CPUState *cs, GuestFD *gf); -static void host_writefn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) -{ - CPUArchState *env = cs->env_ptr; - uint32_t ret = 0; - char *s = lock_user(VERIFY_READ, buf, len, 1); - (void) env; /* Used in arm softmmu lock_user implicitly */ - if (s) { - ret = write(gf->hostfd, s, len); - unlock_user(s, buf, 0); - if (ret == (uint32_t)-1) { - ret = 0; - } - } - /* Return bytes not written, on error as well. */ - common_semi_cb(cs, len - ret, 0); -} - static void host_isattyfn(CPUState *cs, GuestFD *gf) { common_semi_cb(cs, isatty(gf->hostfd), 0); @@ -340,13 +315,6 @@ static void host_flenfn(CPUState *cs, GuestFD *gf) } } -static void gdb_writefn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) -{ - common_semi_syscall_len = len; - gdb_do_syscall(common_semi_cb, "write,%x,%x,%x", gf->hostfd, buf, len); -} - static void gdb_isattyfn(CPUState *cs, GuestFD *gf) { gdb_do_syscall(common_semi_cb, "isatty,%x", gf->hostfd); @@ -380,13 +348,6 @@ static const uint8_t featurefile_data[] = { SH_EXT_EXIT_EXTENDED | SH_EXT_STDOUT_STDERR, /* Feature byte 0 */ }; -static void staticfile_writefn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) -{ - /* This fd can never be open for writing */ - common_semi_cb(cs, -1, EBADF); -} - static void staticfile_isattyfn(CPUState *cs, GuestFD *gf) { common_semi_cb(cs, 0, 0); @@ -404,7 +365,6 @@ static void staticfile_flenfn(CPUState *cs, GuestFD *gf) } typedef struct GuestFDFunctions { - sys_writefn *writefn; sys_isattyfn *isattyfn; sys_seekfn *seekfn; sys_flenfn *flenfn; @@ -412,19 +372,16 @@ typedef struct GuestFDFunctions { static const GuestFDFunctions guestfd_fns[] = { [GuestFDHost] = { - .writefn = host_writefn, .isattyfn = host_isattyfn, .seekfn = host_seekfn, .flenfn = host_flenfn, }, [GuestFDGDB] = { - .writefn = gdb_writefn, .isattyfn = gdb_isattyfn, .seekfn = gdb_seekfn, .flenfn = gdb_flenfn, }, [GuestFDStatic] = { - .writefn = staticfile_writefn, .isattyfn = staticfile_isattyfn, .seekfn = staticfile_seekfn, .flenfn = staticfile_flenfn, @@ -449,7 +406,6 @@ void do_common_semihosting(CPUState *cs) char * s; int nr; uint32_t ret; - uint32_t len; GuestFD *gf; int64_t elapsed; @@ -531,13 +487,7 @@ void do_common_semihosting(CPUState *cs) GET_ARG(0); GET_ARG(1); GET_ARG(2); - len = arg2; - - gf = get_guestfd(arg0); - if (!gf) { - goto do_badf; - } - guestfd_fns[gf->type].writefn(cs, gf, arg1, len); + semihost_sys_write(cs, common_semi_rw_cb, arg0, arg1, arg2); break; case TARGET_SYS_READ: diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index d42a190746..5cb12d6adc 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -107,6 +107,13 @@ static void gdb_read(CPUState *cs, gdb_syscall_complete_cb complete, (target_ulong)gf->hostfd, buf, len); } +static void gdb_write(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + gdb_do_syscall(complete, "write,%x,%x,%x", + (target_ulong)gf->hostfd, buf, len); +} + /* * Host semihosting syscall implementations. */ @@ -193,6 +200,22 @@ static void host_read(CPUState *cs, gdb_syscall_complete_cb complete, } } +static void host_write(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + void *ptr = lock_user(VERIFY_READ, buf, len, 1); + ssize_t ret; + + if (!ptr) { + complete(cs, -1, EFAULT); + return; + } + ret = write(gf->hostfd, ptr, len); + complete(cs, ret, ret == -1 ? errno : 0); + unlock_user(ptr, buf, 0); +} + /* * Static file semihosting syscall implementations. */ @@ -286,3 +309,34 @@ void semihost_sys_read(CPUState *cs, gdb_syscall_complete_cb complete, complete(cs, -1, EBADF); } } + +void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + switch (gf->type) { + case GuestFDGDB: + gdb_write(cs, complete, gf, buf, len); + break; + case GuestFDHost: + host_write(cs, complete, gf, buf, len); + break; + case GuestFDStatic: + /* Static files are never open for writing: EBADF. */ + complete(cs, -1, EBADF); + break; + default: + g_assert_not_reached(); + } +} + +void semihost_sys_write(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, target_ulong buf, target_ulong len) +{ + GuestFD *gf = get_guestfd(fd); + + if (gf) { + semihost_sys_write_gf(cs, complete, gf, buf, len); + } else { + complete(cs, -1, EBADF); + } +} From 40f1219a8b2f95808ed5a18798dbce1b57fef211 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Apr 2022 12:15:14 -0700 Subject: [PATCH 31/60] semihosting: Bound length for semihost_sys_{read,write} Fixes a minor bug in which a 64-bit guest on a 32-bit host could truncate the length. This would only ever cause a problem if there were no bits set in the low 32, so that it truncates to 0. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- semihosting/syscalls.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index 5cb12d6adc..eefbae74f1 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -283,6 +283,14 @@ void semihost_sys_close(CPUState *cs, gdb_syscall_complete_cb complete, int fd) void semihost_sys_read_gf(CPUState *cs, gdb_syscall_complete_cb complete, GuestFD *gf, target_ulong buf, target_ulong len) { + /* + * Bound length for 64-bit guests on 32-bit hosts, not overlowing ssize_t. + * Note the Linux kernel does this with MAX_RW_COUNT, so it's not a bad + * idea to do this unconditionally. + */ + if (len > INT32_MAX) { + len = INT32_MAX; + } switch (gf->type) { case GuestFDGDB: gdb_read(cs, complete, gf, buf, len); @@ -313,6 +321,14 @@ void semihost_sys_read(CPUState *cs, gdb_syscall_complete_cb complete, void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete, GuestFD *gf, target_ulong buf, target_ulong len) { + /* + * Bound length for 64-bit guests on 32-bit hosts, not overlowing ssize_t. + * Note the Linux kernel does this with MAX_RW_COUNT, so it's not a bad + * idea to do this unconditionally. + */ + if (len > INT32_MAX) { + len = INT32_MAX; + } switch (gf->type) { case GuestFDGDB: gdb_write(cs, complete, gf, buf, len); From 9a89470449a5171eb1bd06f681361e0888d15cf7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Apr 2022 12:04:44 -0700 Subject: [PATCH 32/60] semihosting: Split out semihost_sys_lseek Split out the non-ARM specific portions of SYS_SEEK to a reusable function. This handles all GuestFD. Isolate the curious ARM-specific return value processing to a new callback, common_semi_seek_cb. Expand the internal type of the offset to int64_t, and provide the whence argument, which will be required by m68k and nios2 semihosting. Note that gdb_do_syscall %x reads target_ulong, not int. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- include/exec/gdbstub.h | 5 +++ include/semihosting/syscalls.h | 3 ++ semihosting/arm-compat-semi.c | 51 ++++++--------------- semihosting/syscalls.c | 81 ++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+), 38 deletions(-) diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index 95a8b7b056..b588c306cc 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -41,6 +41,11 @@ #define GDB_ENAMETOOLONG 91 #define GDB_EUNKNOWN 9999 +/* For gdb file i/o remote protocol lseek whence. */ +#define GDB_SEEK_SET 0 +#define GDB_SEEK_CUR 1 +#define GDB_SEEK_END 2 + /* For gdb file i/o stat/fstat. */ typedef uint32_t gdb_mode_t; typedef uint32_t gdb_time_t; diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h index 2464467579..841a93d25b 100644 --- a/include/semihosting/syscalls.h +++ b/include/semihosting/syscalls.h @@ -39,4 +39,7 @@ void semihost_sys_write(CPUState *cs, gdb_syscall_complete_cb complete, void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete, GuestFD *gf, target_ulong buf, target_ulong len); +void semihost_sys_lseek(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, int64_t off, int gdb_whence); + #endif /* SEMIHOSTING_SYSCALLS_H */ diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index d591fcd7c2..a117d180bc 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -224,16 +224,6 @@ static void common_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) #else syscall_err = err; #endif - } else { - /* Fixup syscalls that use nonstardard return conventions. */ - target_ulong reg0 = common_semi_arg(cs, 0); - switch (reg0) { - case TARGET_SYS_SEEK: - ret = 0; - break; - default: - break; - } } common_semi_set_ret(cs, ret); } @@ -257,6 +247,18 @@ static void common_semi_rw_cb(CPUState *cs, target_ulong ret, target_ulong err) common_semi_set_ret(cs, arg2 - ret); } +/* + * SYS_SEEK returns 0 on success, not the resulting offset. + */ +static void common_semi_seek_cb(CPUState *cs, target_ulong ret, + target_ulong err) +{ + if (!err) { + ret = 0; + } + common_semi_cb(cs, ret, err); +} + /* * Return an address in target memory of 64 bytes where the remote * gdb should write its stat struct. (The format of this structure @@ -290,7 +292,6 @@ common_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err) * via common_semi_cb. */ typedef void sys_isattyfn(CPUState *cs, GuestFD *gf); -typedef void sys_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset); typedef void sys_flenfn(CPUState *cs, GuestFD *gf); static void host_isattyfn(CPUState *cs, GuestFD *gf) @@ -298,12 +299,6 @@ static void host_isattyfn(CPUState *cs, GuestFD *gf) common_semi_cb(cs, isatty(gf->hostfd), 0); } -static void host_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset) -{ - off_t ret = lseek(gf->hostfd, offset, SEEK_SET); - common_semi_cb(cs, ret, ret == -1 ? errno : 0); -} - static void host_flenfn(CPUState *cs, GuestFD *gf) { struct stat buf; @@ -320,11 +315,6 @@ static void gdb_isattyfn(CPUState *cs, GuestFD *gf) gdb_do_syscall(common_semi_cb, "isatty,%x", gf->hostfd); } -static void gdb_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset) -{ - gdb_do_syscall(common_semi_cb, "lseek,%x,%x,0", gf->hostfd, offset); -} - static void gdb_flenfn(CPUState *cs, GuestFD *gf) { gdb_do_syscall(common_semi_flen_cb, "fstat,%x,%x", @@ -353,12 +343,6 @@ static void staticfile_isattyfn(CPUState *cs, GuestFD *gf) common_semi_cb(cs, 0, 0); } -static void staticfile_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset) -{ - gf->staticfile.off = offset; - common_semi_cb(cs, 0, 0); -} - static void staticfile_flenfn(CPUState *cs, GuestFD *gf) { common_semi_cb(cs, gf->staticfile.len, 0); @@ -366,24 +350,20 @@ static void staticfile_flenfn(CPUState *cs, GuestFD *gf) typedef struct GuestFDFunctions { sys_isattyfn *isattyfn; - sys_seekfn *seekfn; sys_flenfn *flenfn; } GuestFDFunctions; static const GuestFDFunctions guestfd_fns[] = { [GuestFDHost] = { .isattyfn = host_isattyfn, - .seekfn = host_seekfn, .flenfn = host_flenfn, }, [GuestFDGDB] = { .isattyfn = gdb_isattyfn, - .seekfn = gdb_seekfn, .flenfn = gdb_flenfn, }, [GuestFDStatic] = { .isattyfn = staticfile_isattyfn, - .seekfn = staticfile_seekfn, .flenfn = staticfile_flenfn, }, }; @@ -520,12 +500,7 @@ void do_common_semihosting(CPUState *cs) case TARGET_SYS_SEEK: GET_ARG(0); GET_ARG(1); - - gf = get_guestfd(arg0); - if (!gf) { - goto do_badf; - } - guestfd_fns[gf->type].seekfn(cs, gf, arg1); + semihost_sys_lseek(cs, common_semi_seek_cb, arg0, arg1, GDB_SEEK_SET); break; case TARGET_SYS_FLEN: diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index eefbae74f1..9e3eb464b5 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -114,6 +114,13 @@ static void gdb_write(CPUState *cs, gdb_syscall_complete_cb complete, (target_ulong)gf->hostfd, buf, len); } +static void gdb_lseek(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, int64_t off, int gdb_whence) +{ + gdb_do_syscall(complete, "lseek,%x,%lx,%x", + (target_ulong)gf->hostfd, off, (target_ulong)gdb_whence); +} + /* * Host semihosting syscall implementations. */ @@ -216,6 +223,29 @@ static void host_write(CPUState *cs, gdb_syscall_complete_cb complete, unlock_user(ptr, buf, 0); } +static void host_lseek(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, int64_t off, int whence) +{ + /* So far, all hosts use the same values. */ + QEMU_BUILD_BUG_ON(GDB_SEEK_SET != SEEK_SET); + QEMU_BUILD_BUG_ON(GDB_SEEK_CUR != SEEK_CUR); + QEMU_BUILD_BUG_ON(GDB_SEEK_END != SEEK_END); + + off_t ret = off; + int err = 0; + + if (ret == off) { + ret = lseek(gf->hostfd, ret, whence); + if (ret == -1) { + err = errno; + } + } else { + ret = -1; + err = EINVAL; + } + complete(cs, ret, err); +} + /* * Static file semihosting syscall implementations. */ @@ -241,6 +271,33 @@ static void staticfile_read(CPUState *cs, gdb_syscall_complete_cb complete, unlock_user(ptr, buf, len); } +static void staticfile_lseek(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, int64_t off, int gdb_whence) +{ + int64_t ret; + + switch (gdb_whence) { + case GDB_SEEK_SET: + ret = off; + break; + case GDB_SEEK_CUR: + ret = gf->staticfile.off + off; + break; + case GDB_SEEK_END: + ret = gf->staticfile.len + off; + break; + default: + ret = -1; + break; + } + if (ret >= 0 && ret <= gf->staticfile.len) { + gf->staticfile.off = ret; + complete(cs, ret, 0); + } else { + complete(cs, -1, EINVAL); + } +} + /* * Syscall entry points. */ @@ -356,3 +413,27 @@ void semihost_sys_write(CPUState *cs, gdb_syscall_complete_cb complete, complete(cs, -1, EBADF); } } + +void semihost_sys_lseek(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, int64_t off, int gdb_whence) +{ + GuestFD *gf = get_guestfd(fd); + + if (!gf) { + complete(cs, -1, EBADF); + return; + } + switch (gf->type) { + case GuestFDGDB: + gdb_lseek(cs, complete, gf, off, gdb_whence); + return; + case GuestFDHost: + host_lseek(cs, complete, gf, off, gdb_whence); + break; + case GuestFDStatic: + staticfile_lseek(cs, complete, gf, off, gdb_whence); + break; + default: + g_assert_not_reached(); + } +} From a2212474301bc354bea46e3bdc6c21e33e0b5b2b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Apr 2022 12:31:25 -0700 Subject: [PATCH 33/60] semihosting: Split out semihost_sys_isatty Split out the non-ARM specific portions of SYS_ISTTY to a reusable function. This handles all GuestFD. Add a common_semi_istty_cb helper to translate the Posix error return, 0+ENOTTY, to the Arm semihosting not-a-file success result. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- include/semihosting/syscalls.h | 3 +++ semihosting/arm-compat-semi.c | 40 ++++++++++++---------------------- semihosting/syscalls.c | 36 ++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 26 deletions(-) diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h index 841a93d25b..c60ebafb85 100644 --- a/include/semihosting/syscalls.h +++ b/include/semihosting/syscalls.h @@ -42,4 +42,7 @@ void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete, void semihost_sys_lseek(CPUState *cs, gdb_syscall_complete_cb complete, int fd, int64_t off, int gdb_whence); +void semihost_sys_isatty(CPUState *cs, gdb_syscall_complete_cb complete, + int fd); + #endif /* SEMIHOSTING_SYSCALLS_H */ diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index a117d180bc..3cdc2b6efc 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -247,6 +247,19 @@ static void common_semi_rw_cb(CPUState *cs, target_ulong ret, target_ulong err) common_semi_set_ret(cs, arg2 - ret); } +/* + * Convert from Posix ret+errno to Arm SYS_ISTTY return values. + * With gdbstub, err is only ever set for protocol errors to EIO. + */ +static void common_semi_istty_cb(CPUState *cs, target_ulong ret, + target_ulong err) +{ + if (err) { + ret = (err == ENOTTY ? 0 : -1); + } + common_semi_cb(cs, ret, err); +} + /* * SYS_SEEK returns 0 on success, not the resulting offset. */ @@ -291,14 +304,8 @@ common_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err) * do the work and return the required return value to the guest * via common_semi_cb. */ -typedef void sys_isattyfn(CPUState *cs, GuestFD *gf); typedef void sys_flenfn(CPUState *cs, GuestFD *gf); -static void host_isattyfn(CPUState *cs, GuestFD *gf) -{ - common_semi_cb(cs, isatty(gf->hostfd), 0); -} - static void host_flenfn(CPUState *cs, GuestFD *gf) { struct stat buf; @@ -310,11 +317,6 @@ static void host_flenfn(CPUState *cs, GuestFD *gf) } } -static void gdb_isattyfn(CPUState *cs, GuestFD *gf) -{ - gdb_do_syscall(common_semi_cb, "isatty,%x", gf->hostfd); -} - static void gdb_flenfn(CPUState *cs, GuestFD *gf) { gdb_do_syscall(common_semi_flen_cb, "fstat,%x,%x", @@ -338,32 +340,23 @@ static const uint8_t featurefile_data[] = { SH_EXT_EXIT_EXTENDED | SH_EXT_STDOUT_STDERR, /* Feature byte 0 */ }; -static void staticfile_isattyfn(CPUState *cs, GuestFD *gf) -{ - common_semi_cb(cs, 0, 0); -} - static void staticfile_flenfn(CPUState *cs, GuestFD *gf) { common_semi_cb(cs, gf->staticfile.len, 0); } typedef struct GuestFDFunctions { - sys_isattyfn *isattyfn; sys_flenfn *flenfn; } GuestFDFunctions; static const GuestFDFunctions guestfd_fns[] = { [GuestFDHost] = { - .isattyfn = host_isattyfn, .flenfn = host_flenfn, }, [GuestFDGDB] = { - .isattyfn = gdb_isattyfn, .flenfn = gdb_flenfn, }, [GuestFDStatic] = { - .isattyfn = staticfile_isattyfn, .flenfn = staticfile_flenfn, }, }; @@ -489,12 +482,7 @@ void do_common_semihosting(CPUState *cs) case TARGET_SYS_ISTTY: GET_ARG(0); - - gf = get_guestfd(arg0); - if (!gf) { - goto do_badf; - } - guestfd_fns[gf->type].isattyfn(cs, gf); + semihost_sys_isatty(cs, common_semi_istty_cb, arg0); break; case TARGET_SYS_SEEK: diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index 9e3eb464b5..1f1baf7e2d 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -121,6 +121,12 @@ static void gdb_lseek(CPUState *cs, gdb_syscall_complete_cb complete, (target_ulong)gf->hostfd, off, (target_ulong)gdb_whence); } +static void gdb_isatty(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf) +{ + gdb_do_syscall(complete, "isatty,%x", (target_ulong)gf->hostfd); +} + /* * Host semihosting syscall implementations. */ @@ -246,6 +252,13 @@ static void host_lseek(CPUState *cs, gdb_syscall_complete_cb complete, complete(cs, ret, err); } +static void host_isatty(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf) +{ + int ret = isatty(gf->hostfd); + complete(cs, ret, ret ? 0 : errno); +} + /* * Static file semihosting syscall implementations. */ @@ -437,3 +450,26 @@ void semihost_sys_lseek(CPUState *cs, gdb_syscall_complete_cb complete, g_assert_not_reached(); } } + +void semihost_sys_isatty(CPUState *cs, gdb_syscall_complete_cb complete, int fd) +{ + GuestFD *gf = get_guestfd(fd); + + if (!gf) { + complete(cs, 0, EBADF); + return; + } + switch (gf->type) { + case GuestFDGDB: + gdb_isatty(cs, complete, gf); + break; + case GuestFDHost: + host_isatty(cs, complete, gf); + break; + case GuestFDStatic: + complete(cs, 0, ENOTTY); + break; + default: + g_assert_not_reached(); + } +} From a6300ed6b7ecd19eb5ae0fd8a3eb876681cb8e6e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Apr 2022 09:05:36 -0700 Subject: [PATCH 34/60] semihosting: Split out semihost_sys_flen The ARM-specific SYS_FLEN isn't really something that can be reused by other semihosting apis, but there are parts that can reused for the implementation of semihost_sys_fstat. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- include/semihosting/syscalls.h | 4 ++ semihosting/arm-compat-semi.c | 74 ++++++---------------------------- semihosting/syscalls.c | 49 ++++++++++++++++++++++ 3 files changed, 66 insertions(+), 61 deletions(-) diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h index c60ebafb85..1ae5ba6716 100644 --- a/include/semihosting/syscalls.h +++ b/include/semihosting/syscalls.h @@ -45,4 +45,8 @@ void semihost_sys_lseek(CPUState *cs, gdb_syscall_complete_cb complete, void semihost_sys_isatty(CPUState *cs, gdb_syscall_complete_cb complete, int fd); +void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb, + gdb_syscall_complete_cb flen_cb, + int fd, target_ulong fstat_addr); + #endif /* SEMIHOSTING_SYSCALLS_H */ diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 3cdc2b6efc..68e13d9077 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -285,44 +285,25 @@ static target_ulong common_semi_flen_buf(CPUState *cs) } static void -common_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err) +common_semi_flen_fstat_cb(CPUState *cs, target_ulong ret, target_ulong err) { if (!err) { /* The size is always stored in big-endian order, extract the value. */ uint64_t size; - cpu_memory_rw_debug(cs, common_semi_flen_buf(cs) + - offsetof(struct gdb_stat, gdb_st_size), - &size, 8, 0); - ret = be64_to_cpu(size); + if (cpu_memory_rw_debug(cs, common_semi_flen_buf(cs) + + offsetof(struct gdb_stat, gdb_st_size), + &size, 8, 0)) { + ret = -1, err = EFAULT; + } else { + size = be64_to_cpu(size); + if (ret != size) { + ret = -1, err = EOVERFLOW; + } + } } common_semi_cb(cs, ret, err); } -/* - * Types for functions implementing various semihosting calls - * for specific types of guest file descriptor. These must all - * do the work and return the required return value to the guest - * via common_semi_cb. - */ -typedef void sys_flenfn(CPUState *cs, GuestFD *gf); - -static void host_flenfn(CPUState *cs, GuestFD *gf) -{ - struct stat buf; - - if (fstat(gf->hostfd, &buf)) { - common_semi_cb(cs, -1, errno); - } else { - common_semi_cb(cs, buf.st_size, 0); - } -} - -static void gdb_flenfn(CPUState *cs, GuestFD *gf) -{ - gdb_do_syscall(common_semi_flen_cb, "fstat,%x,%x", - gf->hostfd, common_semi_flen_buf(cs)); -} - #define SHFB_MAGIC_0 0x53 #define SHFB_MAGIC_1 0x48 #define SHFB_MAGIC_2 0x46 @@ -340,27 +321,6 @@ static const uint8_t featurefile_data[] = { SH_EXT_EXIT_EXTENDED | SH_EXT_STDOUT_STDERR, /* Feature byte 0 */ }; -static void staticfile_flenfn(CPUState *cs, GuestFD *gf) -{ - common_semi_cb(cs, gf->staticfile.len, 0); -} - -typedef struct GuestFDFunctions { - sys_flenfn *flenfn; -} GuestFDFunctions; - -static const GuestFDFunctions guestfd_fns[] = { - [GuestFDHost] = { - .flenfn = host_flenfn, - }, - [GuestFDGDB] = { - .flenfn = gdb_flenfn, - }, - [GuestFDStatic] = { - .flenfn = staticfile_flenfn, - }, -}; - /* * Do a semihosting call. * @@ -379,7 +339,6 @@ void do_common_semihosting(CPUState *cs) char * s; int nr; uint32_t ret; - GuestFD *gf; int64_t elapsed; nr = common_semi_arg(cs, 0) & 0xffffffffU; @@ -493,12 +452,8 @@ void do_common_semihosting(CPUState *cs) case TARGET_SYS_FLEN: GET_ARG(0); - - gf = get_guestfd(arg0); - if (!gf) { - goto do_badf; - } - guestfd_fns[gf->type].flenfn(cs, gf); + semihost_sys_flen(cs, common_semi_flen_fstat_cb, common_semi_cb, + arg0, common_semi_flen_buf(cs)); break; case TARGET_SYS_TMPNAM: @@ -820,9 +775,6 @@ void do_common_semihosting(CPUState *cs) cpu_dump_state(cs, stderr, 0); abort(); - do_badf: - common_semi_cb(cs, -1, EBADF); - break; do_fault: common_semi_cb(cs, -1, EFAULT); break; diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index 1f1baf7e2d..fff9550c89 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -127,6 +127,12 @@ static void gdb_isatty(CPUState *cs, gdb_syscall_complete_cb complete, gdb_do_syscall(complete, "isatty,%x", (target_ulong)gf->hostfd); } +static void gdb_fstat(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong addr) +{ + gdb_do_syscall(complete, "fstat,%x,%x", (target_ulong)gf->hostfd, addr); +} + /* * Host semihosting syscall implementations. */ @@ -259,6 +265,18 @@ static void host_isatty(CPUState *cs, gdb_syscall_complete_cb complete, complete(cs, ret, ret ? 0 : errno); } +static void host_flen(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf) +{ + struct stat buf; + + if (fstat(gf->hostfd, &buf) < 0) { + complete(cs, -1, errno); + } else { + complete(cs, buf.st_size, 0); + } +} + /* * Static file semihosting syscall implementations. */ @@ -311,6 +329,12 @@ static void staticfile_lseek(CPUState *cs, gdb_syscall_complete_cb complete, } } +static void staticfile_flen(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf) +{ + complete(cs, gf->staticfile.len, 0); +} + /* * Syscall entry points. */ @@ -473,3 +497,28 @@ void semihost_sys_isatty(CPUState *cs, gdb_syscall_complete_cb complete, int fd) g_assert_not_reached(); } } + +void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb, + gdb_syscall_complete_cb flen_cb, int fd, + target_ulong fstat_addr) +{ + GuestFD *gf = get_guestfd(fd); + + if (!gf) { + flen_cb(cs, -1, EBADF); + return; + } + switch (gf->type) { + case GuestFDGDB: + gdb_fstat(cs, fstat_cb, gf, fstat_addr); + break; + case GuestFDHost: + host_flen(cs, flen_cb, gf); + break; + case GuestFDStatic: + staticfile_flen(cs, flen_cb, gf); + break; + default: + g_assert_not_reached(); + } +} From d49e79b8e276b56817509114a86b036358246af8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Apr 2022 09:29:27 -0700 Subject: [PATCH 35/60] semihosting: Split out semihost_sys_remove Split out the non-ARM specific portions of SYS_REMOVE to a reusable function. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- include/semihosting/syscalls.h | 3 +++ semihosting/arm-compat-semi.c | 13 +---------- semihosting/syscalls.c | 40 ++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h index 1ae5ba6716..748a4b5e47 100644 --- a/include/semihosting/syscalls.h +++ b/include/semihosting/syscalls.h @@ -49,4 +49,7 @@ void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb, gdb_syscall_complete_cb flen_cb, int fd, target_ulong fstat_addr); +void semihost_sys_remove(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len); + #endif /* SEMIHOSTING_SYSCALLS_H */ diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 68e13d9077..f4ce3851a4 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -484,18 +484,7 @@ void do_common_semihosting(CPUState *cs) case TARGET_SYS_REMOVE: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - gdb_do_syscall(common_semi_cb, "unlink,%s", - arg0, (int)arg1 + 1); - break; - } - s = lock_user_string(arg0); - if (!s) { - goto do_fault; - } - ret = remove(s); - unlock_user(s, arg0, 0); - common_semi_cb(cs, ret, ret ? errno : 0); + semihost_sys_remove(cs, common_semi_cb, arg0, arg1 + 1); break; case TARGET_SYS_RENAME: diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index fff9550c89..5ec4e8f827 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -133,6 +133,18 @@ static void gdb_fstat(CPUState *cs, gdb_syscall_complete_cb complete, gdb_do_syscall(complete, "fstat,%x,%x", (target_ulong)gf->hostfd, addr); } +static void gdb_remove(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len) +{ + int len = validate_strlen(cs, fname, fname_len); + if (len < 0) { + complete(cs, -1, -len); + return; + } + + gdb_do_syscall(complete, "unlink,%s", fname, len); +} + /* * Host semihosting syscall implementations. */ @@ -277,6 +289,24 @@ static void host_flen(CPUState *cs, gdb_syscall_complete_cb complete, } } +static void host_remove(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + char *p; + int ret; + + ret = validate_lock_user_string(&p, cs, fname, fname_len); + if (ret < 0) { + complete(cs, -1, -ret); + return; + } + + ret = remove(p); + complete(cs, ret, ret ? errno : 0); + unlock_user(p, fname, 0); +} + /* * Static file semihosting syscall implementations. */ @@ -522,3 +552,13 @@ void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb, g_assert_not_reached(); } } + +void semihost_sys_remove(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len) +{ + if (use_gdb_syscalls()) { + gdb_remove(cs, complete, fname, fname_len); + } else { + host_remove(cs, complete, fname, fname_len); + } +} From 25a95da0beeb854570f974028b77cd35826433b7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Apr 2022 10:11:31 -0700 Subject: [PATCH 36/60] semihosting: Split out semihost_sys_rename Split out the non-ARM specific portions of SYS_RENAME to a reusable function. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- include/semihosting/syscalls.h | 4 +++ semihosting/arm-compat-semi.c | 21 +------------ semihosting/syscalls.c | 57 ++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 20 deletions(-) diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h index 748a4b5e47..21430aa0ef 100644 --- a/include/semihosting/syscalls.h +++ b/include/semihosting/syscalls.h @@ -52,4 +52,8 @@ void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb, void semihost_sys_remove(CPUState *cs, gdb_syscall_complete_cb complete, target_ulong fname, target_ulong fname_len); +void semihost_sys_rename(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong oname, target_ulong oname_len, + target_ulong nname, target_ulong nname_len); + #endif /* SEMIHOSTING_SYSCALLS_H */ diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index f4ce3851a4..14d37ac9da 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -492,26 +492,7 @@ void do_common_semihosting(CPUState *cs) GET_ARG(1); GET_ARG(2); GET_ARG(3); - if (use_gdb_syscalls()) { - gdb_do_syscall(common_semi_cb, "rename,%s,%s", - arg0, (int)arg1 + 1, arg2, (int)arg3 + 1); - } else { - char *s2; - - s = lock_user_string(arg0); - if (!s) { - goto do_fault; - } - s2 = lock_user_string(arg2); - if (!s2) { - unlock_user(s, arg0, 0); - goto do_fault; - } - ret = rename(s, s2); - unlock_user(s2, arg2, 0); - unlock_user(s, arg0, 0); - common_semi_cb(cs, ret, ret ? errno : 0); - } + semihost_sys_rename(cs, common_semi_cb, arg0, arg1 + 1, arg2, arg3 + 1); break; case TARGET_SYS_CLOCK: diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index 5ec4e8f827..223916b110 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -145,6 +145,26 @@ static void gdb_remove(CPUState *cs, gdb_syscall_complete_cb complete, gdb_do_syscall(complete, "unlink,%s", fname, len); } +static void gdb_rename(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong oname, target_ulong oname_len, + target_ulong nname, target_ulong nname_len) +{ + int olen, nlen; + + olen = validate_strlen(cs, oname, oname_len); + if (olen < 0) { + complete(cs, -1, -olen); + return; + } + nlen = validate_strlen(cs, nname, nname_len); + if (nlen < 0) { + complete(cs, -1, -nlen); + return; + } + + gdb_do_syscall(complete, "rename,%s,%s", oname, olen, nname, nlen); +} + /* * Host semihosting syscall implementations. */ @@ -307,6 +327,32 @@ static void host_remove(CPUState *cs, gdb_syscall_complete_cb complete, unlock_user(p, fname, 0); } +static void host_rename(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong oname, target_ulong oname_len, + target_ulong nname, target_ulong nname_len) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + char *ostr, *nstr; + int ret; + + ret = validate_lock_user_string(&ostr, cs, oname, oname_len); + if (ret < 0) { + complete(cs, -1, -ret); + return; + } + ret = validate_lock_user_string(&nstr, cs, nname, nname_len); + if (ret < 0) { + unlock_user(ostr, oname, 0); + complete(cs, -1, -ret); + return; + } + + ret = rename(ostr, nstr); + complete(cs, ret, ret ? errno : 0); + unlock_user(ostr, oname, 0); + unlock_user(nstr, nname, 0); +} + /* * Static file semihosting syscall implementations. */ @@ -562,3 +608,14 @@ void semihost_sys_remove(CPUState *cs, gdb_syscall_complete_cb complete, host_remove(cs, complete, fname, fname_len); } } + +void semihost_sys_rename(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong oname, target_ulong oname_len, + target_ulong nname, target_ulong nname_len) +{ + if (use_gdb_syscalls()) { + gdb_rename(cs, complete, oname, oname_len, nname, nname_len); + } else { + host_rename(cs, complete, oname, oname_len, nname, nname_len); + } +} From 90d8e0b09c4a0085bb12e9659eb33ea3773d7ccc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Apr 2022 13:57:19 -0700 Subject: [PATCH 37/60] semihosting: Split out semihost_sys_system Split out the non-ARM specific portions of SYS_SYSTEM to a reusable function. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- include/semihosting/syscalls.h | 3 +++ semihosting/arm-compat-semi.c | 12 +--------- semihosting/syscalls.c | 40 ++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h index 21430aa0ef..c9f9e66be1 100644 --- a/include/semihosting/syscalls.h +++ b/include/semihosting/syscalls.h @@ -56,4 +56,7 @@ void semihost_sys_rename(CPUState *cs, gdb_syscall_complete_cb complete, target_ulong oname, target_ulong oname_len, target_ulong nname, target_ulong nname_len); +void semihost_sys_system(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong cmd, target_ulong cmd_len); + #endif /* SEMIHOSTING_SYSCALLS_H */ diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 14d37ac9da..7ab6afd7fc 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -507,17 +507,7 @@ void do_common_semihosting(CPUState *cs) case TARGET_SYS_SYSTEM: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - gdb_do_syscall(common_semi_cb, "system,%s", arg0, (int)arg1 + 1); - break; - } - s = lock_user_string(arg0); - if (!s) { - goto do_fault; - } - ret = system(s); - unlock_user(s, arg0, 0); - common_semi_cb(cs, ret, ret == -1 ? errno : 0); + semihost_sys_system(cs, common_semi_cb, arg0, arg1 + 1); break; case TARGET_SYS_ERRNO: diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index 223916b110..de846ced32 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -165,6 +165,18 @@ static void gdb_rename(CPUState *cs, gdb_syscall_complete_cb complete, gdb_do_syscall(complete, "rename,%s,%s", oname, olen, nname, nlen); } +static void gdb_system(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong cmd, target_ulong cmd_len) +{ + int len = validate_strlen(cs, cmd, cmd_len); + if (len < 0) { + complete(cs, -1, -len); + return; + } + + gdb_do_syscall(complete, "system,%s", cmd, len); +} + /* * Host semihosting syscall implementations. */ @@ -353,6 +365,24 @@ static void host_rename(CPUState *cs, gdb_syscall_complete_cb complete, unlock_user(nstr, nname, 0); } +static void host_system(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong cmd, target_ulong cmd_len) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + char *p; + int ret; + + ret = validate_lock_user_string(&p, cs, cmd, cmd_len); + if (ret < 0) { + complete(cs, -1, -ret); + return; + } + + ret = system(p); + complete(cs, ret, ret == -1 ? errno : 0); + unlock_user(p, cmd, 0); +} + /* * Static file semihosting syscall implementations. */ @@ -619,3 +649,13 @@ void semihost_sys_rename(CPUState *cs, gdb_syscall_complete_cb complete, host_rename(cs, complete, oname, oname_len, nname, nname_len); } } + +void semihost_sys_system(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong cmd, target_ulong cmd_len) +{ + if (use_gdb_syscalls()) { + gdb_system(cs, complete, cmd, cmd_len); + } else { + host_system(cs, complete, cmd, cmd_len); + } +} From dffeb7756674af4577e77b4510f1cea5ac585ca0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Apr 2022 15:45:25 -0700 Subject: [PATCH 38/60] semihosting: Create semihost_sys_{stat,fstat} These syscalls will be used by m68k and nios2 semihosting. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- include/semihosting/syscalls.h | 7 ++ semihosting/syscalls.c | 137 +++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h index c9f9e66be1..ecc97751a9 100644 --- a/include/semihosting/syscalls.h +++ b/include/semihosting/syscalls.h @@ -49,6 +49,13 @@ void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb, gdb_syscall_complete_cb flen_cb, int fd, target_ulong fstat_addr); +void semihost_sys_fstat(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, target_ulong addr); + +void semihost_sys_stat(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + target_ulong addr); + void semihost_sys_remove(CPUState *cs, gdb_syscall_complete_cb complete, target_ulong fname, target_ulong fname_len); diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index de846ced32..d21064716d 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -63,6 +63,52 @@ static int validate_lock_user_string(char **pstr, CPUState *cs, return ret; } +/* + * TODO: Note that gdb always stores the stat structure big-endian. + * So far, that's ok, as the only two targets using this are also + * big-endian. Until we do something with gdb, also produce the + * same big-endian result from the host. + */ +static int copy_stat_to_user(CPUState *cs, target_ulong addr, + const struct stat *s) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + struct gdb_stat *p; + + if (s->st_dev != (uint32_t)s->st_dev || + s->st_ino != (uint32_t)s->st_ino) { + return -EOVERFLOW; + } + + p = lock_user(VERIFY_WRITE, addr, sizeof(struct gdb_stat), 0); + if (!p) { + return -EFAULT; + } + + 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 0; +} + /* * GDB semihosting syscall implementations. */ @@ -133,6 +179,19 @@ static void gdb_fstat(CPUState *cs, gdb_syscall_complete_cb complete, gdb_do_syscall(complete, "fstat,%x,%x", (target_ulong)gf->hostfd, addr); } +static void gdb_stat(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + target_ulong addr) +{ + int len = validate_strlen(cs, fname, fname_len); + if (len < 0) { + complete(cs, -1, -len); + return; + } + + gdb_do_syscall(complete, "stat,%s,%x", fname, len, addr); +} + static void gdb_remove(CPUState *cs, gdb_syscall_complete_cb complete, target_ulong fname, target_ulong fname_len) { @@ -321,6 +380,51 @@ static void host_flen(CPUState *cs, gdb_syscall_complete_cb complete, } } +static void host_fstat(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong addr) +{ + struct stat buf; + int ret; + + ret = fstat(gf->hostfd, &buf); + if (ret) { + complete(cs, -1, errno); + return; + } + ret = copy_stat_to_user(cs, addr, &buf); + complete(cs, ret ? -1 : 0, ret ? -ret : 0); +} + +static void host_stat(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + target_ulong addr) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + struct stat buf; + char *name; + int ret, err; + + ret = validate_lock_user_string(&name, cs, fname, fname_len); + if (ret < 0) { + complete(cs, -1, -ret); + return; + } + + ret = stat(name, &buf); + if (ret) { + err = errno; + } else { + ret = copy_stat_to_user(cs, addr, &buf); + err = 0; + if (ret < 0) { + err = -ret; + ret = -1; + } + } + complete(cs, ret, err); + unlock_user(name, fname, 0); +} + static void host_remove(CPUState *cs, gdb_syscall_complete_cb complete, target_ulong fname, target_ulong fname_len) { @@ -629,6 +733,39 @@ void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb, } } +void semihost_sys_fstat(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, target_ulong addr) +{ + GuestFD *gf = get_guestfd(fd); + + if (!gf) { + complete(cs, -1, EBADF); + return; + } + switch (gf->type) { + case GuestFDGDB: + gdb_fstat(cs, complete, gf, addr); + break; + case GuestFDHost: + host_fstat(cs, complete, gf, addr); + break; + case GuestFDStatic: + default: + g_assert_not_reached(); + } +} + +void semihost_sys_stat(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + target_ulong addr) +{ + if (use_gdb_syscalls()) { + gdb_stat(cs, complete, fname, fname_len, addr); + } else { + host_stat(cs, complete, fname, fname_len, addr); + } +} + void semihost_sys_remove(CPUState *cs, gdb_syscall_complete_cb complete, target_ulong fname, target_ulong fname_len) { From 1875dab0eea908b2ceaf74d1204f1e72c69d3a73 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Apr 2022 16:05:49 -0700 Subject: [PATCH 39/60] semihosting: Create semihost_sys_gettimeofday This syscall will be used by m68k and nios2 semihosting. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- include/semihosting/syscalls.h | 3 +++ semihosting/syscalls.c | 42 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h index ecc97751a9..347200cb9f 100644 --- a/include/semihosting/syscalls.h +++ b/include/semihosting/syscalls.h @@ -66,4 +66,7 @@ void semihost_sys_rename(CPUState *cs, gdb_syscall_complete_cb complete, void semihost_sys_system(CPUState *cs, gdb_syscall_complete_cb complete, target_ulong cmd, target_ulong cmd_len); +void semihost_sys_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong tv_addr, target_ulong tz_addr); + #endif /* SEMIHOSTING_SYSCALLS_H */ diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index d21064716d..db1e9f6cc9 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -236,6 +236,12 @@ static void gdb_system(CPUState *cs, gdb_syscall_complete_cb complete, gdb_do_syscall(complete, "system,%s", cmd, len); } +static void gdb_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong tv_addr, target_ulong tz_addr) +{ + gdb_do_syscall(complete, "gettimeofday,%x,%x", tv_addr, tz_addr); +} + /* * Host semihosting syscall implementations. */ @@ -487,6 +493,32 @@ static void host_system(CPUState *cs, gdb_syscall_complete_cb complete, unlock_user(p, cmd, 0); } +static void host_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong tv_addr, target_ulong tz_addr) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + struct gdb_timeval *p; + int64_t rt; + + /* GDB fails on non-null TZ, so be consistent. */ + if (tz_addr != 0) { + complete(cs, -1, EINVAL); + return; + } + + p = lock_user(VERIFY_WRITE, tv_addr, sizeof(struct gdb_timeval), 0); + if (!p) { + complete(cs, -1, EFAULT); + return; + } + + /* TODO: Like stat, gdb always produces big-endian results; match it. */ + rt = g_get_real_time(); + 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, tv_addr, sizeof(struct gdb_timeval)); +} + /* * Static file semihosting syscall implementations. */ @@ -796,3 +828,13 @@ void semihost_sys_system(CPUState *cs, gdb_syscall_complete_cb complete, host_system(cs, complete, cmd, cmd_len); } } + +void semihost_sys_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong tv_addr, target_ulong tz_addr) +{ + if (use_gdb_syscalls()) { + gdb_gettimeofday(cs, complete, tv_addr, tz_addr); + } else { + host_gettimeofday(cs, complete, tv_addr, tz_addr); + } +} From 64c8c6a99221877f0e92f973c66e0e66a10ab2ff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Apr 2022 16:21:43 -0700 Subject: [PATCH 40/60] gdbstub: Adjust gdb_syscall_complete_cb declaration Change 'ret' to uint64_t. This resolves a FIXME in the m68k and nios2 semihosting that we've lost data. Change 'err' to int. There is nothing target-specific about the width of the errno value. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- gdbstub.c | 7 ++++--- include/exec/gdbstub.h | 3 +-- semihosting/arm-compat-semi.c | 12 +++++------- semihosting/console.c | 7 +++---- semihosting/syscalls.c | 2 +- target/m68k/m68k-semi.c | 10 +++------- target/nios2/nios2-semi.c | 8 +++----- 7 files changed, 20 insertions(+), 29 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index f3a4664453..cf869b10e3 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1878,11 +1878,12 @@ static void handle_read_all_regs(GArray *params, void *user_ctx) static void handle_file_io(GArray *params, void *user_ctx) { if (params->len >= 1 && gdbserver_state.current_syscall_cb) { - target_ulong ret, err; + uint64_t ret; + int err; - ret = (target_ulong)get_param(params, 0)->val_ull; + ret = get_param(params, 0)->val_ull; if (params->len >= 2) { - err = (target_ulong)get_param(params, 1)->val_ull; + err = get_param(params, 1)->val_ull; } else { err = 0; } diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index b588c306cc..f667014888 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -74,8 +74,7 @@ struct gdb_timeval { #ifdef NEED_CPU_H #include "cpu.h" -typedef void (*gdb_syscall_complete_cb)(CPUState *cpu, - target_ulong ret, target_ulong err); +typedef void (*gdb_syscall_complete_cb)(CPUState *cpu, uint64_t ret, int err); /** * gdb_do_syscall: diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 7ab6afd7fc..1b0505987a 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -215,7 +215,7 @@ static inline uint32_t get_swi_errno(CPUState *cs) #endif } -static void common_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) +static void common_semi_cb(CPUState *cs, uint64_t ret, int err) { if (err) { #ifdef CONFIG_USER_ONLY @@ -232,7 +232,7 @@ static void common_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) * SYS_READ and SYS_WRITE always return the number of bytes not read/written. * There is no error condition, other than returning the original length. */ -static void common_semi_rw_cb(CPUState *cs, target_ulong ret, target_ulong err) +static void common_semi_rw_cb(CPUState *cs, uint64_t ret, int err) { /* Recover the original length from the third argument. */ CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; @@ -251,8 +251,7 @@ static void common_semi_rw_cb(CPUState *cs, target_ulong ret, target_ulong err) * Convert from Posix ret+errno to Arm SYS_ISTTY return values. * With gdbstub, err is only ever set for protocol errors to EIO. */ -static void common_semi_istty_cb(CPUState *cs, target_ulong ret, - target_ulong err) +static void common_semi_istty_cb(CPUState *cs, uint64_t ret, int err) { if (err) { ret = (err == ENOTTY ? 0 : -1); @@ -263,8 +262,7 @@ static void common_semi_istty_cb(CPUState *cs, target_ulong ret, /* * SYS_SEEK returns 0 on success, not the resulting offset. */ -static void common_semi_seek_cb(CPUState *cs, target_ulong ret, - target_ulong err) +static void common_semi_seek_cb(CPUState *cs, uint64_t ret, int err) { if (!err) { ret = 0; @@ -285,7 +283,7 @@ static target_ulong common_semi_flen_buf(CPUState *cs) } static void -common_semi_flen_fstat_cb(CPUState *cs, target_ulong ret, target_ulong err) +common_semi_flen_fstat_cb(CPUState *cs, uint64_t ret, int err) { if (!err) { /* The size is always stored in big-endian order, extract the value. */ diff --git a/semihosting/console.c b/semihosting/console.c index ef6958d844..4e49202b2a 100644 --- a/semihosting/console.c +++ b/semihosting/console.c @@ -64,11 +64,10 @@ static GString *copy_user_string(CPUArchState *env, target_ulong addr) return s; } -static void semihosting_cb(CPUState *cs, target_ulong ret, target_ulong err) +static void semihosting_cb(CPUState *cs, uint64_t ret, int err) { - if (ret == (target_ulong) -1) { - qemu_log("%s: gdb console output failed ("TARGET_FMT_ld")", - __func__, err); + if (err) { + qemu_log("%s: gdb console output failed (%d)\n", __func__, err); } } diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index db1e9f6cc9..13a9bdeda6 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -115,7 +115,7 @@ static int copy_stat_to_user(CPUState *cs, target_ulong addr, static gdb_syscall_complete_cb gdb_open_complete; -static void gdb_open_cb(CPUState *cs, target_ulong ret, target_ulong err) +static void gdb_open_cb(CPUState *cs, uint64_t ret, int err) { if (!err) { int guestfd = alloc_guestfd(); diff --git a/target/m68k/m68k-semi.c b/target/m68k/m68k-semi.c index b886ebf714..8c186c0e9f 100644 --- a/target/m68k/m68k-semi.c +++ b/target/m68k/m68k-semi.c @@ -95,7 +95,7 @@ static void translate_stat(CPUM68KState *env, target_ulong addr, struct stat *s) unlock_user(p, addr, sizeof(struct gdb_stat)); } -static void m68k_semi_return_u32(CPUM68KState *env, uint32_t ret, uint32_t err) +static void m68k_semi_return_u32(CPUM68KState *env, uint32_t ret, int err) { target_ulong args = env->dregs[1]; if (put_user_u32(ret, args) || @@ -110,7 +110,7 @@ static void m68k_semi_return_u32(CPUM68KState *env, uint32_t ret, uint32_t err) } } -static void m68k_semi_return_u64(CPUM68KState *env, uint64_t ret, uint32_t err) +static void m68k_semi_return_u64(CPUM68KState *env, uint64_t ret, int err) { target_ulong args = env->dregs[1]; if (put_user_u32(ret >> 32, args) || @@ -124,16 +124,12 @@ static void m68k_semi_return_u64(CPUM68KState *env, uint64_t ret, uint32_t err) static int m68k_semi_is_fseek; -static void m68k_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) +static void m68k_semi_cb(CPUState *cs, uint64_t ret, int err) { M68kCPU *cpu = M68K_CPU(cs); CPUM68KState *env = &cpu->env; if (m68k_semi_is_fseek) { - /* - * FIXME: We've already lost the high bits of the fseek - * return value. - */ m68k_semi_return_u64(env, ret, err); m68k_semi_is_fseek = 0; } else { diff --git a/target/nios2/nios2-semi.c b/target/nios2/nios2-semi.c index 3e504a6c5f..4d02789d26 100644 --- a/target/nios2/nios2-semi.c +++ b/target/nios2/nios2-semi.c @@ -108,8 +108,7 @@ static bool translate_stat(CPUNios2State *env, target_ulong addr, return true; } -static void nios2_semi_return_u32(CPUNios2State *env, uint32_t ret, - uint32_t err) +static void nios2_semi_return_u32(CPUNios2State *env, uint32_t ret, int err) { target_ulong args = env->regs[R_ARG1]; if (put_user_u32(ret, args) || @@ -124,8 +123,7 @@ static void nios2_semi_return_u32(CPUNios2State *env, uint32_t ret, } } -static void nios2_semi_return_u64(CPUNios2State *env, uint64_t ret, - uint32_t err) +static void nios2_semi_return_u64(CPUNios2State *env, uint64_t ret, int err) { target_ulong args = env->regs[R_ARG1]; if (put_user_u32(ret >> 32, args) || @@ -139,7 +137,7 @@ static void nios2_semi_return_u64(CPUNios2State *env, uint64_t ret, static int nios2_semi_is_lseek; -static void nios2_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) +static void nios2_semi_cb(CPUState *cs, uint64_t ret, int err) { Nios2CPU *cpu = NIOS2_CPU(cs); CPUNios2State *env = &cpu->env; From 675f702fd725f07af50d99318c11bf2512b774d7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 May 2022 11:58:58 -0700 Subject: [PATCH 41/60] semihosting: Fix docs comment for qemu_semihosting_console_inc The implementation of qemu_semihosting_console_inc does not defer to gdbstub, but only reads from the fifo in console.c. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- include/semihosting/console.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/include/semihosting/console.h b/include/semihosting/console.h index 0238f540f4..4f6217bf10 100644 --- a/include/semihosting/console.h +++ b/include/semihosting/console.h @@ -41,11 +41,10 @@ void qemu_semihosting_console_outc(CPUArchState *env, target_ulong c); * qemu_semihosting_console_inc: * @env: CPUArchState * - * Receive single character from debug console. This may be the remote - * gdb session if a softmmu guest is currently being debugged. As this - * call may block if no data is available we suspend the CPU and will - * re-execute the instruction when data is there. Therefore two - * conditions must be met: + * Receive single character from debug console. As this call may block + * if no data is available we suspend the CPU and will re-execute the + * instruction when data is there. Therefore two conditions must be met: + * * - CPUState is synchronized before calling this function * - pc is only updated once the character is successfully returned * From 3367d452b001e91547634756e32246610701df5c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 May 2022 12:21:19 -0700 Subject: [PATCH 42/60] semihosting: Pass CPUState to qemu_semihosting_console_inc We don't need CPUArchState, and we do want the CPUState of the thread performing the operation -- use this instead of current_cpu. Reviewed-by: Luc Michel Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- include/semihosting/console.h | 4 ++-- linux-user/semihost.c | 2 +- semihosting/arm-compat-semi.c | 2 +- semihosting/console.c | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/semihosting/console.h b/include/semihosting/console.h index 4f6217bf10..27f8e9ae2e 100644 --- a/include/semihosting/console.h +++ b/include/semihosting/console.h @@ -39,7 +39,7 @@ void qemu_semihosting_console_outc(CPUArchState *env, target_ulong c); /** * qemu_semihosting_console_inc: - * @env: CPUArchState + * @cs: CPUState * * Receive single character from debug console. As this call may block * if no data is available we suspend the CPU and will re-execute the @@ -50,7 +50,7 @@ void qemu_semihosting_console_outc(CPUArchState *env, target_ulong c); * * Returns: character read OR cpu_loop_exit! */ -target_ulong qemu_semihosting_console_inc(CPUArchState *env); +target_ulong qemu_semihosting_console_inc(CPUState *cs); /** * qemu_semihosting_log_out: diff --git a/linux-user/semihost.c b/linux-user/semihost.c index 17f074ac56..f14c6ae21d 100644 --- a/linux-user/semihost.c +++ b/linux-user/semihost.c @@ -56,7 +56,7 @@ void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr) * program is expecting more normal behaviour. This is slow but * nothing using semihosting console reading is expecting to be fast. */ -target_ulong qemu_semihosting_console_inc(CPUArchState *env) +target_ulong qemu_semihosting_console_inc(CPUState *cs) { uint8_t c; struct termios old_tio, new_tio; diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 1b0505987a..40f3730778 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -428,7 +428,7 @@ void do_common_semihosting(CPUState *cs) break; case TARGET_SYS_READC: - ret = qemu_semihosting_console_inc(env); + ret = qemu_semihosting_console_inc(cs); common_semi_set_ret(cs, ret); break; diff --git a/semihosting/console.c b/semihosting/console.c index 4e49202b2a..17ece6bdca 100644 --- a/semihosting/console.c +++ b/semihosting/console.c @@ -144,17 +144,17 @@ static void console_read(void *opaque, const uint8_t *buf, int size) c->sleeping_cpus = NULL; } -target_ulong qemu_semihosting_console_inc(CPUArchState *env) +target_ulong qemu_semihosting_console_inc(CPUState *cs) { uint8_t ch; SemihostingConsole *c = &console; + g_assert(qemu_mutex_iothread_locked()); - g_assert(current_cpu); if (fifo8_is_empty(&c->fifo)) { - c->sleeping_cpus = g_slist_prepend(c->sleeping_cpus, current_cpu); - current_cpu->halted = 1; - current_cpu->exception_index = EXCP_HALTED; - cpu_loop_exit(current_cpu); + c->sleeping_cpus = g_slist_prepend(c->sleeping_cpus, cs); + cs->halted = 1; + cs->exception_index = EXCP_HALTED; + cpu_loop_exit(cs); /* never returns */ } ch = fifo8_pop(&c->fifo); From e7fb6f320548c1b0c25d291466a0249ee80d91b6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 May 2022 12:31:08 -0700 Subject: [PATCH 43/60] semihosting: Expand qemu_semihosting_console_inc to read Allow more than one character to be read at one time. Will be used by m68k and nios2 semihosting for stdio. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- include/semihosting/console.h | 12 +++++++----- linux-user/semihost.c | 10 ++++++---- semihosting/arm-compat-semi.c | 11 +++++++++-- semihosting/console.c | 16 ++++++++++++---- 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/include/semihosting/console.h b/include/semihosting/console.h index 27f8e9ae2e..39dbf1b062 100644 --- a/include/semihosting/console.h +++ b/include/semihosting/console.h @@ -38,19 +38,21 @@ int qemu_semihosting_console_outs(CPUArchState *env, target_ulong s); void qemu_semihosting_console_outc(CPUArchState *env, target_ulong c); /** - * qemu_semihosting_console_inc: + * qemu_semihosting_console_read: * @cs: CPUState + * @buf: host buffer + * @len: buffer size * - * Receive single character from debug console. As this call may block - * if no data is available we suspend the CPU and will re-execute the + * Receive at least one character from debug console. As this call may + * block if no data is available we suspend the CPU and will re-execute the * instruction when data is there. Therefore two conditions must be met: * * - CPUState is synchronized before calling this function * - pc is only updated once the character is successfully returned * - * Returns: character read OR cpu_loop_exit! + * Returns: number of characters read, OR cpu_loop_exit! */ -target_ulong qemu_semihosting_console_inc(CPUState *cs); +int qemu_semihosting_console_read(CPUState *cs, void *buf, int len); /** * qemu_semihosting_log_out: diff --git a/linux-user/semihost.c b/linux-user/semihost.c index f14c6ae21d..2029fb674c 100644 --- a/linux-user/semihost.c +++ b/linux-user/semihost.c @@ -56,21 +56,23 @@ void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr) * program is expecting more normal behaviour. This is slow but * nothing using semihosting console reading is expecting to be fast. */ -target_ulong qemu_semihosting_console_inc(CPUState *cs) +int qemu_semihosting_console_read(CPUState *cs, void *buf, int len) { - uint8_t c; + int ret; struct termios old_tio, new_tio; /* Disable line-buffering and echo */ tcgetattr(STDIN_FILENO, &old_tio); new_tio = old_tio; new_tio.c_lflag &= (~ICANON & ~ECHO); + new_tio.c_cc[VMIN] = 1; + new_tio.c_cc[VTIME] = 0; tcsetattr(STDIN_FILENO, TCSANOW, &new_tio); - c = getchar(); + ret = fread(buf, 1, len, stdin); /* restore config */ tcsetattr(STDIN_FILENO, TCSANOW, &old_tio); - return (target_ulong) c; + return ret; } diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 40f3730778..fdb143ace8 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -428,8 +428,15 @@ void do_common_semihosting(CPUState *cs) break; case TARGET_SYS_READC: - ret = qemu_semihosting_console_inc(cs); - common_semi_set_ret(cs, ret); + { + uint8_t ch; + int ret = qemu_semihosting_console_read(cs, &ch, 1); + if (ret == 1) { + common_semi_cb(cs, ch, 0); + } else { + common_semi_cb(cs, -1, EIO); + } + } break; case TARGET_SYS_ISERROR: diff --git a/semihosting/console.c b/semihosting/console.c index 17ece6bdca..e5ac3f20ba 100644 --- a/semihosting/console.c +++ b/semihosting/console.c @@ -144,12 +144,14 @@ static void console_read(void *opaque, const uint8_t *buf, int size) c->sleeping_cpus = NULL; } -target_ulong qemu_semihosting_console_inc(CPUState *cs) +int qemu_semihosting_console_read(CPUState *cs, void *buf, int len) { - uint8_t ch; SemihostingConsole *c = &console; + int ret = 0; g_assert(qemu_mutex_iothread_locked()); + + /* Block if the fifo is completely empty. */ if (fifo8_is_empty(&c->fifo)) { c->sleeping_cpus = g_slist_prepend(c->sleeping_cpus, cs); cs->halted = 1; @@ -157,8 +159,14 @@ target_ulong qemu_semihosting_console_inc(CPUState *cs) cpu_loop_exit(cs); /* never returns */ } - ch = fifo8_pop(&c->fifo); - return (target_ulong) ch; + + /* Read until buffer full or fifo exhausted. */ + do { + *(char *)(buf + ret) = fifo8_pop(&c->fifo); + ret++; + } while (ret < len && !fifo8_is_empty(&c->fifo)); + + return ret; } void qemu_semihosting_console_init(void) From fb08790b35174a98301ecbac4d5234d0cbfebea0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 May 2022 16:59:06 -0700 Subject: [PATCH 44/60] semihosting: Cleanup chardev init Rename qemu_semihosting_connect_chardevs to qemu_semihosting_chardev_init; pass the result directly to qemu_semihosting_console_init. Store the chardev in SemihostingConsole instead of SemihostingConfig, which lets us drop semihosting_get_chardev. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- include/semihosting/semihost.h | 13 ++----------- semihosting/config.c | 17 +++++++---------- semihosting/console.c | 31 +++++++++++++++---------------- softmmu/vl.c | 3 +-- stubs/semihost.c | 6 +----- 5 files changed, 26 insertions(+), 44 deletions(-) diff --git a/include/semihosting/semihost.h b/include/semihosting/semihost.h index 0c55ade3ac..5b36a76f08 100644 --- a/include/semihosting/semihost.h +++ b/include/semihosting/semihost.h @@ -51,14 +51,6 @@ static inline const char *semihosting_get_cmdline(void) { return NULL; } - -static inline Chardev *semihosting_get_chardev(void) -{ - return NULL; -} -static inline void qemu_semihosting_console_init(void) -{ -} #else /* !CONFIG_USER_ONLY */ bool semihosting_enabled(void); SemihostingTarget semihosting_get_target(void); @@ -66,12 +58,11 @@ const char *semihosting_get_arg(int i); int semihosting_get_argc(void); const char *semihosting_get_cmdline(void); void semihosting_arg_fallback(const char *file, const char *cmd); -Chardev *semihosting_get_chardev(void); /* for vl.c hooks */ void qemu_semihosting_enable(void); int qemu_semihosting_config_options(const char *opt); -void qemu_semihosting_connect_chardevs(void); -void qemu_semihosting_console_init(void); +void qemu_semihosting_chardev_init(void); +void qemu_semihosting_console_init(Chardev *); #endif /* CONFIG_USER_ONLY */ #endif /* SEMIHOST_H */ diff --git a/semihosting/config.c b/semihosting/config.c index 3afacf54ab..e171d4d6bc 100644 --- a/semihosting/config.c +++ b/semihosting/config.c @@ -51,7 +51,6 @@ QemuOptsList qemu_semihosting_config_opts = { typedef struct SemihostingConfig { bool enabled; SemihostingTarget target; - Chardev *chardev; char **argv; int argc; const char *cmdline; /* concatenated argv */ @@ -122,11 +121,6 @@ void semihosting_arg_fallback(const char *file, const char *cmd) } } -Chardev *semihosting_get_chardev(void) -{ - return semihosting.chardev; -} - void qemu_semihosting_enable(void) { semihosting.enabled = true; @@ -172,16 +166,19 @@ int qemu_semihosting_config_options(const char *optarg) return 0; } -void qemu_semihosting_connect_chardevs(void) +/* We had to defer this until chardevs were created */ +void qemu_semihosting_chardev_init(void) { - /* We had to defer this until chardevs were created */ + Chardev *chr = NULL; + if (semihost_chardev) { - Chardev *chr = qemu_chr_find(semihost_chardev); + chr = qemu_chr_find(semihost_chardev); if (chr == NULL) { error_report("semihosting chardev '%s' not found", semihost_chardev); exit(1); } - semihosting.chardev = chr; } + + qemu_semihosting_console_init(chr); } diff --git a/semihosting/console.c b/semihosting/console.c index e5ac3f20ba..1d16a290c4 100644 --- a/semihosting/console.c +++ b/semihosting/console.c @@ -27,11 +27,21 @@ #include "qapi/error.h" #include "qemu/fifo8.h" +/* Access to this structure is protected by the BQL */ +typedef struct SemihostingConsole { + CharBackend backend; + Chardev *chr; + GSList *sleeping_cpus; + bool got; + Fifo8 fifo; +} SemihostingConsole; + +static SemihostingConsole console; + int qemu_semihosting_log_out(const char *s, int len) { - Chardev *chardev = semihosting_get_chardev(); - if (chardev) { - return qemu_chr_write_all(chardev, (uint8_t *) s, len); + if (console.chr) { + return qemu_chr_write_all(console.chr, (uint8_t *) s, len); } else { return write(STDERR_FILENO, s, len); } @@ -106,16 +116,6 @@ void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr) #define FIFO_SIZE 1024 -/* Access to this structure is protected by the BQL */ -typedef struct SemihostingConsole { - CharBackend backend; - GSList *sleeping_cpus; - bool got; - Fifo8 fifo; -} SemihostingConsole; - -static SemihostingConsole console; - static int console_can_read(void *opaque) { SemihostingConsole *c = opaque; @@ -169,10 +169,9 @@ int qemu_semihosting_console_read(CPUState *cs, void *buf, int len) return ret; } -void qemu_semihosting_console_init(void) +void qemu_semihosting_console_init(Chardev *chr) { - Chardev *chr = semihosting_get_chardev(); - + console.chr = chr; if (chr) { fifo8_create(&console.fifo, FIFO_SIZE); qemu_chr_fe_init(&console.backend, chr, &error_abort); diff --git a/softmmu/vl.c b/softmmu/vl.c index 3dca5936c7..b24772841d 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -1917,8 +1917,7 @@ static void qemu_create_late_backends(void) exit(1); /* now chardevs have been created we may have semihosting to connect */ - qemu_semihosting_connect_chardevs(); - qemu_semihosting_console_init(); + qemu_semihosting_chardev_init(); } static void qemu_resolve_machine_memdev(void) diff --git a/stubs/semihost.c b/stubs/semihost.c index 4bf2cf71b9..f486651afb 100644 --- a/stubs/semihost.c +++ b/stubs/semihost.c @@ -65,10 +65,6 @@ void semihosting_arg_fallback(const char *file, const char *cmd) { } -void qemu_semihosting_connect_chardevs(void) -{ -} - -void qemu_semihosting_console_init(void) +void qemu_semihosting_chardev_init(void) { } From cd66f20f614bb492e4e5be11e4b65d58b4a046ca Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 May 2022 12:42:37 -0700 Subject: [PATCH 45/60] semihosting: Create qemu_semihosting_console_write Will replace qemu_semihosting_console_{outs,outc}, but we need more plumbing first. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- include/semihosting/console.h | 12 ++++++++++++ linux-user/semihost.c | 5 +++++ semihosting/console.c | 9 +++++++++ 3 files changed, 26 insertions(+) diff --git a/include/semihosting/console.h b/include/semihosting/console.h index 39dbf1b062..6994f23c82 100644 --- a/include/semihosting/console.h +++ b/include/semihosting/console.h @@ -54,6 +54,18 @@ void qemu_semihosting_console_outc(CPUArchState *env, target_ulong c); */ int qemu_semihosting_console_read(CPUState *cs, void *buf, int len); +/** + * qemu_semihosting_console_write: + * @buf: host buffer + * @len: buffer size + * + * Write len bytes from buf to the debug console. + * + * Returns: number of bytes written -- this should only ever be short + * on some sort of i/o error. + */ +int qemu_semihosting_console_write(void *buf, int len); + /** * qemu_semihosting_log_out: * @s: pointer to string diff --git a/linux-user/semihost.c b/linux-user/semihost.c index 2029fb674c..871edf993a 100644 --- a/linux-user/semihost.c +++ b/linux-user/semihost.c @@ -76,3 +76,8 @@ int qemu_semihosting_console_read(CPUState *cs, void *buf, int len) return ret; } + +int qemu_semihosting_console_write(void *buf, int len) +{ + return fwrite(buf, 1, len, stderr); +} diff --git a/semihosting/console.c b/semihosting/console.c index 1d16a290c4..540aa0cd4b 100644 --- a/semihosting/console.c +++ b/semihosting/console.c @@ -169,6 +169,15 @@ int qemu_semihosting_console_read(CPUState *cs, void *buf, int len) return ret; } +int qemu_semihosting_console_write(void *buf, int len) +{ + if (console.chr) { + return qemu_chr_write_all(console.chr, (uint8_t *)buf, len); + } else { + return fwrite(buf, 1, len, stderr); + } +} + void qemu_semihosting_console_init(Chardev *chr) { console.chr = chr; From 008e147572863a7a54c54403e626aaed3e50574f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 May 2022 13:11:45 -0700 Subject: [PATCH 46/60] semihosting: Add GuestFDConsole MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a GuestFDType for connecting to the semihosting console. Hook up to read, write, isatty, and fstat syscalls. Note that the arm-specific syscall flen cannot be applied to the console, because the console is not a descriptor exposed to the guest. Reviewed-by: Luc Michel Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- include/semihosting/guestfd.h | 7 ++-- semihosting/syscalls.c | 68 +++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/include/semihosting/guestfd.h b/include/semihosting/guestfd.h index ef268abe85..a7ea1041ea 100644 --- a/include/semihosting/guestfd.h +++ b/include/semihosting/guestfd.h @@ -13,9 +13,10 @@ typedef enum GuestFDType { GuestFDUnused = 0, - GuestFDHost = 1, - GuestFDGDB = 2, - GuestFDStatic = 3, + GuestFDHost, + GuestFDGDB, + GuestFDStatic, + GuestFDConsole, } GuestFDType; /* diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index 13a9bdeda6..9e499b1751 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -10,6 +10,7 @@ #include "exec/gdbstub.h" #include "semihosting/guestfd.h" #include "semihosting/syscalls.h" +#include "semihosting/console.h" #ifdef CONFIG_USER_ONLY #include "qemu.h" #else @@ -577,6 +578,56 @@ static void staticfile_flen(CPUState *cs, gdb_syscall_complete_cb complete, complete(cs, gf->staticfile.len, 0); } +/* + * Console semihosting syscall implementations. + */ + +static void console_read(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + char *ptr; + int ret; + + ptr = lock_user(VERIFY_WRITE, buf, len, 0); + if (!ptr) { + complete(cs, -1, EFAULT); + return; + } + ret = qemu_semihosting_console_read(cs, ptr, len); + complete(cs, ret, 0); + unlock_user(ptr, buf, ret); +} + +static void console_write(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + char *ptr = lock_user(VERIFY_READ, buf, len, 1); + int ret; + + if (!ptr) { + complete(cs, -1, EFAULT); + return; + } + ret = qemu_semihosting_console_write(ptr, len); + complete(cs, ret ? ret : -1, ret ? 0 : EIO); + unlock_user(ptr, buf, ret); +} + +static void console_fstat(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong addr) +{ + static const struct stat tty_buf = { + .st_mode = 020666, /* S_IFCHR, ugo+rw */ + .st_rdev = 5, /* makedev(5, 0) -- linux /dev/tty */ + }; + int ret; + + ret = copy_stat_to_user(cs, addr, &tty_buf); + complete(cs, ret ? -1 : 0, ret ? -ret : 0); +} + /* * Syscall entry points. */ @@ -608,6 +659,7 @@ void semihost_sys_close(CPUState *cs, gdb_syscall_complete_cb complete, int fd) host_close(cs, complete, gf); break; case GuestFDStatic: + case GuestFDConsole: complete(cs, 0, 0); break; default: @@ -637,6 +689,9 @@ void semihost_sys_read_gf(CPUState *cs, gdb_syscall_complete_cb complete, case GuestFDStatic: staticfile_read(cs, complete, gf, buf, len); break; + case GuestFDConsole: + console_read(cs, complete, gf, buf, len); + break; default: g_assert_not_reached(); } @@ -672,6 +727,9 @@ void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete, case GuestFDHost: host_write(cs, complete, gf, buf, len); break; + case GuestFDConsole: + console_write(cs, complete, gf, buf, len); + break; case GuestFDStatic: /* Static files are never open for writing: EBADF. */ complete(cs, -1, EBADF); @@ -712,6 +770,9 @@ void semihost_sys_lseek(CPUState *cs, gdb_syscall_complete_cb complete, case GuestFDStatic: staticfile_lseek(cs, complete, gf, off, gdb_whence); break; + case GuestFDConsole: + complete(cs, -1, ESPIPE); + break; default: g_assert_not_reached(); } @@ -735,6 +796,9 @@ void semihost_sys_isatty(CPUState *cs, gdb_syscall_complete_cb complete, int fd) case GuestFDStatic: complete(cs, 0, ENOTTY); break; + case GuestFDConsole: + complete(cs, 1, 0); + break; default: g_assert_not_reached(); } @@ -760,6 +824,7 @@ void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb, case GuestFDStatic: staticfile_flen(cs, flen_cb, gf); break; + case GuestFDConsole: default: g_assert_not_reached(); } @@ -781,6 +846,9 @@ void semihost_sys_fstat(CPUState *cs, gdb_syscall_complete_cb complete, case GuestFDHost: host_fstat(cs, complete, gf, addr); break; + case GuestFDConsole: + console_fstat(cs, complete, gf, addr); + break; case GuestFDStatic: default: g_assert_not_reached(); From e4a4aaa51b4c71914a6f30ca504ab78e8f695aee Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 May 2022 17:21:00 -0700 Subject: [PATCH 47/60] semihosting: Create qemu_semihosting_guestfd_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For arm-compat, initialize console_{in,out}_gf; otherwise, initialize stdio file descriptors. This will go some way to cleaning up arm-compat, and will allow other semihosting to use normal stdio. Reviewed-by: Luc Michel Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- include/semihosting/guestfd.h | 7 +++++ include/semihosting/semihost.h | 1 + linux-user/main.c | 9 ++++++ semihosting/console.c | 2 ++ semihosting/guestfd.c | 52 +++++++++++++++++++++++++++------- 5 files changed, 61 insertions(+), 10 deletions(-) diff --git a/include/semihosting/guestfd.h b/include/semihosting/guestfd.h index a7ea1041ea..3d426fedab 100644 --- a/include/semihosting/guestfd.h +++ b/include/semihosting/guestfd.h @@ -35,6 +35,13 @@ typedef struct GuestFD { }; } GuestFD; +/* + * For ARM semihosting, we have a separate structure for routing + * data for the console which is outside the guest fd address space. + */ +extern GuestFD console_in_gf; +extern GuestFD console_out_gf; + /** * alloc_guestfd: * diff --git a/include/semihosting/semihost.h b/include/semihosting/semihost.h index 5b36a76f08..93a3c21b44 100644 --- a/include/semihosting/semihost.h +++ b/include/semihosting/semihost.h @@ -64,5 +64,6 @@ int qemu_semihosting_config_options(const char *opt); void qemu_semihosting_chardev_init(void); void qemu_semihosting_console_init(Chardev *); #endif /* CONFIG_USER_ONLY */ +void qemu_semihosting_guestfd_init(void); #endif /* SEMIHOST_H */ diff --git a/linux-user/main.c b/linux-user/main.c index 651e32f5f2..e44bdb17b8 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -54,6 +54,10 @@ #include "loader.h" #include "user-mmap.h" +#ifdef CONFIG_SEMIHOSTING +#include "semihosting/semihost.h" +#endif + #ifndef AT_FLAGS_PRESERVE_ARGV0 #define AT_FLAGS_PRESERVE_ARGV0_BIT 0 #define AT_FLAGS_PRESERVE_ARGV0 (1 << AT_FLAGS_PRESERVE_ARGV0_BIT) @@ -906,6 +910,11 @@ int main(int argc, char **argv, char **envp) } gdb_handlesig(cpu, 0); } + +#ifdef CONFIG_SEMIHOSTING + qemu_semihosting_guestfd_init(); +#endif + cpu_loop(env); /* never exits */ return 0; diff --git a/semihosting/console.c b/semihosting/console.c index 540aa0cd4b..955880514e 100644 --- a/semihosting/console.c +++ b/semihosting/console.c @@ -190,4 +190,6 @@ void qemu_semihosting_console_init(Chardev *chr) NULL, NULL, &console, NULL, true); } + + qemu_semihosting_guestfd_init(); } diff --git a/semihosting/guestfd.c b/semihosting/guestfd.c index e3122ebba9..b05c52f26f 100644 --- a/semihosting/guestfd.c +++ b/semihosting/guestfd.c @@ -10,15 +10,56 @@ #include "qemu/osdep.h" #include "exec/gdbstub.h" +#include "semihosting/semihost.h" #include "semihosting/guestfd.h" #ifdef CONFIG_USER_ONLY #include "qemu.h" #else #include "semihosting/softmmu-uaccess.h" +#include CONFIG_DEVICES #endif static GArray *guestfd_array; +#ifdef CONFIG_ARM_COMPATIBLE_SEMIHOSTING +GuestFD console_in_gf; +GuestFD console_out_gf; +#endif + +void qemu_semihosting_guestfd_init(void) +{ + /* New entries zero-initialized, i.e. type GuestFDUnused */ + guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD)); + +#ifdef CONFIG_ARM_COMPATIBLE_SEMIHOSTING + /* For ARM-compat, the console is in a separate namespace. */ + if (use_gdb_syscalls()) { + console_in_gf.type = GuestFDGDB; + console_in_gf.hostfd = 0; + console_out_gf.type = GuestFDGDB; + console_out_gf.hostfd = 2; + } else { + console_in_gf.type = GuestFDConsole; + console_out_gf.type = GuestFDConsole; + } +#else + /* Otherwise, the stdio file descriptors apply. */ + guestfd_array = g_array_set_size(guestfd_array, 3); +#ifndef CONFIG_USER_ONLY + if (!use_gdb_syscalls()) { + GuestFD *gf = &g_array_index(guestfd_array, GuestFD, 0); + gf[0].type = GuestFDConsole; + gf[1].type = GuestFDConsole; + gf[2].type = GuestFDConsole; + return; + } +#endif + associate_guestfd(0, 0); + associate_guestfd(1, 1); + associate_guestfd(2, 2); +#endif +} + /* * Allocate a new guest file descriptor and return it; if we * couldn't allocate a new fd then return -1. @@ -30,11 +71,6 @@ int alloc_guestfd(void) { guint i; - if (!guestfd_array) { - /* New entries zero-initialized, i.e. type GuestFDUnused */ - guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD)); - } - /* SYS_OPEN should return nonzero handle on success. Start guestfd from 1 */ for (i = 1; i < guestfd_array->len; i++) { GuestFD *gf = &g_array_index(guestfd_array, GuestFD, i); @@ -61,11 +97,7 @@ static void do_dealloc_guestfd(GuestFD *gf) */ static GuestFD *do_get_guestfd(int guestfd) { - if (!guestfd_array) { - return NULL; - } - - if (guestfd <= 0 || guestfd >= guestfd_array->len) { + if (guestfd < 0 || guestfd >= guestfd_array->len) { return NULL; } From 1577eec0fca6fd67bdc0727d10de4bdc3f8afa95 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 May 2022 17:42:43 -0700 Subject: [PATCH 48/60] semihosting: Use console_in_gf for SYS_READC Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- semihosting/arm-compat-semi.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index fdb143ace8..9d4d6d2812 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -302,6 +302,22 @@ common_semi_flen_fstat_cb(CPUState *cs, uint64_t ret, int err) common_semi_cb(cs, ret, err); } +static void +common_semi_readc_cb(CPUState *cs, uint64_t ret, int err) +{ + if (!err) { + CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + uint8_t ch; + + if (get_user_u8(ch, common_semi_stack_bottom(cs) - 1)) { + ret = -1, err = EFAULT; + } else { + ret = ch; + } + } + common_semi_cb(cs, ret, err); +} + #define SHFB_MAGIC_0 0x53 #define SHFB_MAGIC_1 0x48 #define SHFB_MAGIC_2 0x46 @@ -428,15 +444,8 @@ void do_common_semihosting(CPUState *cs) break; case TARGET_SYS_READC: - { - uint8_t ch; - int ret = qemu_semihosting_console_read(cs, &ch, 1); - if (ret == 1) { - common_semi_cb(cs, ch, 0); - } else { - common_semi_cb(cs, -1, EIO); - } - } + semihost_sys_read_gf(cs, common_semi_readc_cb, &console_in_gf, + common_semi_stack_bottom(cs) - 1, 1); break; case TARGET_SYS_ISERROR: From 5d77289dac9917db89d56f558bcf7c3a82332222 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 May 2022 17:55:20 -0700 Subject: [PATCH 49/60] semihosting: Use console_out_gf for SYS_WRITEC Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- semihosting/arm-compat-semi.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 9d4d6d2812..d61b773f98 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -228,6 +228,15 @@ static void common_semi_cb(CPUState *cs, uint64_t ret, int err) common_semi_set_ret(cs, ret); } +/* + * Use 0xdeadbeef as the return value when there isn't a defined + * return value for the call. + */ +static void common_semi_dead_cb(CPUState *cs, uint64_t ret, int err) +{ + common_semi_set_ret(cs, 0xdeadbeef); +} + /* * SYS_READ and SYS_WRITE always return the number of bytes not read/written. * There is no error condition, other than returning the original length. @@ -341,8 +350,7 @@ static const uint8_t featurefile_data[] = { * The specification always says that the "return register" either * returns a specific value or is corrupted, so we don't need to * report to our caller whether we are returning a value or trying to - * leave the register unchanged. We use 0xdeadbeef as the return value - * when there isn't a defined return value for the call. + * leave the register unchanged. */ void do_common_semihosting(CPUState *cs) { @@ -420,8 +428,12 @@ void do_common_semihosting(CPUState *cs) break; case TARGET_SYS_WRITEC: - qemu_semihosting_console_outc(env, args); - common_semi_set_ret(cs, 0xdeadbeef); + /* + * FIXME: the byte to be written is in a target_ulong slot, + * which means this is wrong for a big-endian guest. + */ + semihost_sys_write_gf(cs, common_semi_dead_cb, + &console_out_gf, args, 1); break; case TARGET_SYS_WRITE0: From 004d2abe3f2f856bd6f70fa3d8933d5f6d620142 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 May 2022 17:57:22 -0700 Subject: [PATCH 50/60] semihosting: Remove qemu_semihosting_console_outc This function has been replaced by *_write. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- include/semihosting/console.h | 13 ------------- linux-user/semihost.c | 16 ---------------- semihosting/console.c | 18 ------------------ 3 files changed, 47 deletions(-) diff --git a/include/semihosting/console.h b/include/semihosting/console.h index 6994f23c82..d6c1cc58ab 100644 --- a/include/semihosting/console.h +++ b/include/semihosting/console.h @@ -24,19 +24,6 @@ */ 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_console_read: * @cs: CPUState diff --git a/linux-user/semihost.c b/linux-user/semihost.c index 871edf993a..f8bc8889f3 100644 --- a/linux-user/semihost.c +++ b/linux-user/semihost.c @@ -33,22 +33,6 @@ int qemu_semihosting_console_outs(CPUArchState *env, target_ulong addr) 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__); - } - } -} - /* * For linux-user we can safely block. However as we want to return as * soon as a character is read we need to tweak the termio to disable diff --git a/semihosting/console.c b/semihosting/console.c index 955880514e..fe7ee85137 100644 --- a/semihosting/console.c +++ b/semihosting/console.c @@ -96,24 +96,6 @@ int qemu_semihosting_console_outs(CPUArchState *env, target_ulong addr) 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); - } -} - #define FIFO_SIZE 1024 static int console_can_read(void *opaque) From 7281550cfb30738f0d4bc5113e92780b8a38ec78 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 May 2022 18:02:53 -0700 Subject: [PATCH 51/60] semihosting: Use console_out_gf for SYS_WRITE0 Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- semihosting/arm-compat-semi.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index d61b773f98..1a1e2a6960 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -437,8 +437,15 @@ void do_common_semihosting(CPUState *cs) break; case TARGET_SYS_WRITE0: - ret = qemu_semihosting_console_outs(env, args); - common_semi_set_ret(cs, ret); + { + ssize_t len = target_strlen(args); + if (len < 0) { + common_semi_dead_cb(cs, -1, EFAULT); + } else { + semihost_sys_write_gf(cs, common_semi_dead_cb, + &console_out_gf, args, len); + } + } break; case TARGET_SYS_WRITE: From 2d010c2719da360d44a5c44d279d49eca21c5de8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 May 2022 18:04:27 -0700 Subject: [PATCH 52/60] semihosting: Remove qemu_semihosting_console_outs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function has been replaced by *_write. Reviewed-by: Luc Michel Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- include/semihosting/console.h | 13 ---------- linux-user/semihost.c | 17 ------------ semihosting/console.c | 49 ----------------------------------- 3 files changed, 79 deletions(-) diff --git a/include/semihosting/console.h b/include/semihosting/console.h index d6c1cc58ab..20c31d89d4 100644 --- a/include/semihosting/console.h +++ b/include/semihosting/console.h @@ -11,19 +11,6 @@ #include "cpu.h" -/** - * qemu_semihosting_console_outs: - * @env: CPUArchState - * @s: host address of null terminated guest string - * - * 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_outs(CPUArchState *env, target_ulong s); - /** * qemu_semihosting_console_read: * @cs: CPUState diff --git a/linux-user/semihost.c b/linux-user/semihost.c index f8bc8889f3..cee62a365c 100644 --- a/linux-user/semihost.c +++ b/linux-user/semihost.c @@ -16,23 +16,6 @@ #include "user-internals.h" #include -int qemu_semihosting_console_outs(CPUArchState *env, target_ulong addr) -{ - 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; -} - /* * For linux-user we can safely block. However as we want to return as * soon as a character is read we need to tweak the termio to disable diff --git a/semihosting/console.c b/semihosting/console.c index fe7ee85137..c84ab97ab6 100644 --- a/semihosting/console.c +++ b/semihosting/console.c @@ -47,55 +47,6 @@ 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. Copy string until we find a 0 or address error. - */ -static GString *copy_user_string(CPUArchState *env, target_ulong addr) -{ - CPUState *cpu = env_cpu(env); - GString *s = g_string_sized_new(128); - uint8_t c; - - do { - if (cpu_memory_rw_debug(cpu, addr++, &c, 1, 0) == 0) { - if (c) { - s = g_string_append_c(s, c); - } - } else { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: passed inaccessible address " TARGET_FMT_lx, - __func__, addr); - break; - } - } while (c!=0); - - return s; -} - -static void semihosting_cb(CPUState *cs, uint64_t ret, int err) -{ - if (err) { - qemu_log("%s: gdb console output failed (%d)\n", __func__, err); - } -} - -int qemu_semihosting_console_outs(CPUArchState *env, target_ulong addr) -{ - GString *s = copy_user_string(env, addr); - int out = s->len; - - if (use_gdb_syscalls()) { - gdb_do_syscall(semihosting_cb, "write,2,%x,%x", addr, s->len); - } else { - out = qemu_semihosting_log_out(s->str, s->len); - } - - g_string_free(s, true); - return out; -} - #define FIFO_SIZE 1024 static int console_can_read(void *opaque) From 1b9177f7495086f1595d7c989c810013f1c9eb5a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 2 May 2022 11:15:40 -0700 Subject: [PATCH 53/60] semihosting: Create semihost_sys_poll_one This will be used for implementing the xtensa select_one system call. Choose "poll" over "select" so that we can reuse Glib's g_poll constants and to avoid struct timeval. Reviewed-by: Luc Michel Signed-off-by: Richard Henderson --- include/semihosting/console.h | 16 ++++++++ include/semihosting/syscalls.h | 3 ++ semihosting/console.c | 19 ++++++++- semihosting/syscalls.c | 70 ++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 2 deletions(-) diff --git a/include/semihosting/console.h b/include/semihosting/console.h index 20c31d89d4..61b0cb3a94 100644 --- a/include/semihosting/console.h +++ b/include/semihosting/console.h @@ -53,4 +53,20 @@ int qemu_semihosting_console_write(void *buf, int len); */ int qemu_semihosting_log_out(const char *s, int len); +/* + * qemu_semihosting_console_block_until_ready: + * @cs: CPUState + * + * If no data is available we suspend the CPU and will re-execute the + * instruction when data is available. + */ +void qemu_semihosting_console_block_until_ready(CPUState *cs); + +/** + * qemu_semihosting_console_ready: + * + * Return true if characters are available for read; does not block. + */ +bool qemu_semihosting_console_ready(void); + #endif /* SEMIHOST_CONSOLE_H */ diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h index 347200cb9f..3a5ec229eb 100644 --- a/include/semihosting/syscalls.h +++ b/include/semihosting/syscalls.h @@ -69,4 +69,7 @@ void semihost_sys_system(CPUState *cs, gdb_syscall_complete_cb complete, void semihost_sys_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, target_ulong tv_addr, target_ulong tz_addr); +void semihost_sys_poll_one(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, GIOCondition cond, int timeout); + #endif /* SEMIHOSTING_SYSCALLS_H */ diff --git a/semihosting/console.c b/semihosting/console.c index c84ab97ab6..cda7cf1905 100644 --- a/semihosting/console.c +++ b/semihosting/console.c @@ -77,10 +77,17 @@ static void console_read(void *opaque, const uint8_t *buf, int size) c->sleeping_cpus = NULL; } -int qemu_semihosting_console_read(CPUState *cs, void *buf, int len) +bool qemu_semihosting_console_ready(void) +{ + SemihostingConsole *c = &console; + + g_assert(qemu_mutex_iothread_locked()); + return !fifo8_is_empty(&c->fifo); +} + +void qemu_semihosting_console_block_until_ready(CPUState *cs) { SemihostingConsole *c = &console; - int ret = 0; g_assert(qemu_mutex_iothread_locked()); @@ -92,6 +99,14 @@ int qemu_semihosting_console_read(CPUState *cs, void *buf, int len) cpu_loop_exit(cs); /* never returns */ } +} + +int qemu_semihosting_console_read(CPUState *cs, void *buf, int len) +{ + SemihostingConsole *c = &console; + int ret = 0; + + qemu_semihosting_console_block_until_ready(cs); /* Read until buffer full or fifo exhausted. */ do { diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index 9e499b1751..4847f66c02 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -520,6 +520,21 @@ static void host_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, unlock_user(p, tv_addr, sizeof(struct gdb_timeval)); } +#ifndef CONFIG_USER_ONLY +static void host_poll_one(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, GIOCondition cond, int timeout) +{ + /* + * Since this is only used by xtensa in system mode, and stdio is + * handled through GuestFDConsole, and there are no semihosting + * system calls for sockets and the like, that means this descriptor + * must be a normal file. Normal files never block and are thus + * always ready. + */ + complete(cs, cond & (G_IO_IN | G_IO_OUT), 0); +} +#endif + /* * Static file semihosting syscall implementations. */ @@ -628,6 +643,34 @@ static void console_fstat(CPUState *cs, gdb_syscall_complete_cb complete, complete(cs, ret ? -1 : 0, ret ? -ret : 0); } +#ifndef CONFIG_USER_ONLY +static void console_poll_one(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, GIOCondition cond, int timeout) +{ + /* The semihosting console does not support urgent data or errors. */ + cond &= G_IO_IN | G_IO_OUT; + + /* + * Since qemu_semihosting_console_write never blocks, we can + * consider output always ready -- leave G_IO_OUT alone. + * All that remains is to conditionally signal input ready. + * Since output ready causes an immediate return, only block + * for G_IO_IN alone. + * + * TODO: Implement proper timeout. For now, only support + * indefinite wait or immediate poll. + */ + if (cond == G_IO_IN && timeout < 0) { + qemu_semihosting_console_block_until_ready(cs); + /* We returned -- input must be ready. */ + } else if ((cond & G_IO_IN) && !qemu_semihosting_console_ready()) { + cond &= ~G_IO_IN; + } + + complete(cs, cond, 0); +} +#endif + /* * Syscall entry points. */ @@ -906,3 +949,30 @@ void semihost_sys_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, host_gettimeofday(cs, complete, tv_addr, tz_addr); } } + +#ifndef CONFIG_USER_ONLY +void semihost_sys_poll_one(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, GIOCondition cond, int timeout) +{ + GuestFD *gf = get_guestfd(fd); + + if (!gf) { + complete(cs, G_IO_NVAL, 1); + return; + } + switch (gf->type) { + case GuestFDGDB: + complete(cs, G_IO_NVAL, 1); + break; + case GuestFDHost: + host_poll_one(cs, complete, gf, cond, timeout); + break; + case GuestFDConsole: + console_poll_one(cs, complete, gf, cond, timeout); + break; + case GuestFDStatic: + default: + g_assert_not_reached(); + } +} +#endif From ab294b6c3adfc8a9241f2aaff0709c51acf0370b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Apr 2022 16:44:44 -0700 Subject: [PATCH 54/60] target/m68k: Eliminate m68k_semi_is_fseek Reorg m68k_semi_return_* to gdb_syscall_complete_cb. Use the 32-bit version normally, and the 64-bit version for HOSTED_LSEEK. Reviewed-by: Laurent Vivier Signed-off-by: Richard Henderson --- target/m68k/m68k-semi.c | 55 +++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/target/m68k/m68k-semi.c b/target/m68k/m68k-semi.c index 8c186c0e9f..37b409b0b9 100644 --- a/target/m68k/m68k-semi.c +++ b/target/m68k/m68k-semi.c @@ -95,8 +95,11 @@ static void translate_stat(CPUM68KState *env, target_ulong addr, struct stat *s) unlock_user(p, addr, sizeof(struct gdb_stat)); } -static void m68k_semi_return_u32(CPUM68KState *env, uint32_t ret, int err) +static void m68k_semi_u32_cb(CPUState *cs, uint64_t ret, int err) { + M68kCPU *cpu = M68K_CPU(cs); + CPUM68KState *env = &cpu->env; + target_ulong args = env->dregs[1]; if (put_user_u32(ret, args) || put_user_u32(err, args + 4)) { @@ -110,8 +113,11 @@ static void m68k_semi_return_u32(CPUM68KState *env, uint32_t ret, int err) } } -static void m68k_semi_return_u64(CPUM68KState *env, uint64_t ret, int err) +static void m68k_semi_u64_cb(CPUState *cs, uint64_t ret, int err) { + M68kCPU *cpu = M68K_CPU(cs); + CPUM68KState *env = &cpu->env; + target_ulong args = env->dregs[1]; if (put_user_u32(ret >> 32, args) || put_user_u32(ret, args + 4) || @@ -122,21 +128,6 @@ static void m68k_semi_return_u64(CPUM68KState *env, uint64_t ret, int err) } } -static int m68k_semi_is_fseek; - -static void m68k_semi_cb(CPUState *cs, uint64_t ret, int err) -{ - M68kCPU *cpu = M68K_CPU(cs); - CPUM68KState *env = &cpu->env; - - if (m68k_semi_is_fseek) { - m68k_semi_return_u64(env, ret, err); - m68k_semi_is_fseek = 0; - } else { - m68k_semi_return_u32(env, ret, err); - } -} - /* * Read the input value from the argument block; fail the semihosting * call if the memory read fails. @@ -151,6 +142,7 @@ static void m68k_semi_cb(CPUState *cs, uint64_t ret, int err) void do_m68k_semihosting(CPUM68KState *env, int nr) { + CPUState *cs = env_cpu(env); uint32_t args; target_ulong arg0, arg1, arg2, arg3; void *p; @@ -169,7 +161,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(2); GET_ARG(3); if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "open,%s,%x,%x", arg0, (int)arg1, + gdb_do_syscall(m68k_semi_u32_cb, "open,%s,%x,%x", arg0, (int)arg1, arg2, arg3); return; } else { @@ -190,7 +182,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) int fd = arg0; if (fd > 2) { if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "close,%x", arg0); + gdb_do_syscall(m68k_semi_u32_cb, "close,%x", arg0); return; } else { result = close(fd); @@ -206,7 +198,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(2); len = arg2; if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "read,%x,%x,%x", + gdb_do_syscall(m68k_semi_u32_cb, "read,%x,%x,%x", arg0, arg1, len); return; } else { @@ -226,7 +218,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(2); len = arg2; if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "write,%x,%x,%x", + gdb_do_syscall(m68k_semi_u32_cb, "write,%x,%x,%x", arg0, arg1, len); return; } else { @@ -249,12 +241,11 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(3); off = (uint32_t)arg2 | ((uint64_t)arg1 << 32); if (use_gdb_syscalls()) { - m68k_semi_is_fseek = 1; - gdb_do_syscall(m68k_semi_cb, "fseek,%x,%lx,%x", + gdb_do_syscall(m68k_semi_u64_cb, "fseek,%x,%lx,%x", arg0, off, arg3); } else { off = lseek(arg0, off, arg3); - m68k_semi_return_u64(env, off, errno); + m68k_semi_u64_cb(cs, off, errno); } return; } @@ -264,7 +255,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(2); GET_ARG(3); if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "rename,%s,%s", + gdb_do_syscall(m68k_semi_u32_cb, "rename,%s,%s", arg0, (int)arg1, arg2, (int)arg3); return; } else { @@ -284,7 +275,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "unlink,%s", + gdb_do_syscall(m68k_semi_u32_cb, "unlink,%s", arg0, (int)arg1); return; } else { @@ -303,7 +294,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(1); GET_ARG(2); if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "stat,%s,%x", + gdb_do_syscall(m68k_semi_u32_cb, "stat,%s,%x", arg0, (int)arg1, arg2); return; } else { @@ -325,7 +316,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "fstat,%x,%x", + gdb_do_syscall(m68k_semi_u32_cb, "fstat,%x,%x", arg0, arg1); return; } else { @@ -340,7 +331,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "gettimeofday,%x,%x", + gdb_do_syscall(m68k_semi_u32_cb, "gettimeofday,%x,%x", arg0, arg1); return; } else { @@ -361,7 +352,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) case HOSTED_ISATTY: GET_ARG(0); if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "isatty,%x", arg0); + gdb_do_syscall(m68k_semi_u32_cb, "isatty,%x", arg0); return; } else { result = isatty(arg0); @@ -371,7 +362,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "system,%s", + gdb_do_syscall(m68k_semi_u32_cb, "system,%s", arg0, (int)arg1); return; } else { @@ -429,5 +420,5 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) result = 0; } failed: - m68k_semi_return_u32(env, result, errno); + m68k_semi_u32_cb(cs, result, errno); } From a638af09b6c6b1259803a377a53ef242c5af6af5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Apr 2022 20:16:53 -0700 Subject: [PATCH 55/60] target/m68k: Make semihosting system only While we had a call to do_m68k_semihosting in linux-user, it wasn't actually reachable. We don't include DISAS_INSN(halt) as an instruction unless system mode. Reviewed-by: Laurent Vivier Signed-off-by: Richard Henderson --- linux-user/m68k/cpu_loop.c | 5 ----- target/m68k/m68k-semi.c | 36 ------------------------------------ target/m68k/meson.build | 6 ++++-- 3 files changed, 4 insertions(+), 43 deletions(-) diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c index 3d3033155f..caead1cb74 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -36,11 +36,6 @@ void cpu_loop(CPUM68KState *env) process_queued_cpu_work(cs); switch(trapnr) { - case EXCP_HALT_INSN: - /* Semihosing syscall. */ - env->pc += 4; - do_m68k_semihosting(env, env->dregs[0]); - break; case EXCP_ILLEGAL: case EXCP_LINEA: case EXCP_LINEF: diff --git a/target/m68k/m68k-semi.c b/target/m68k/m68k-semi.c index 37b409b0b9..d0697ddbd1 100644 --- a/target/m68k/m68k-semi.c +++ b/target/m68k/m68k-semi.c @@ -21,13 +21,8 @@ #include "cpu.h" #include "exec/gdbstub.h" -#if defined(CONFIG_USER_ONLY) -#include "qemu.h" -#define SEMIHOSTING_HEAP_SIZE (128 * 1024 * 1024) -#else #include "semihosting/softmmu-uaccess.h" #include "hw/boards.h" -#endif #include "qemu/log.h" #define HOSTED_EXIT 0 @@ -377,43 +372,12 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) } break; case HOSTED_INIT_SIM: -#if defined(CONFIG_USER_ONLY) - { - CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; - /* Allocate the heap using sbrk. */ - if (!ts->heap_limit) { - abi_ulong ret; - uint32_t size; - uint32_t base; - - base = do_brk(0); - size = SEMIHOSTING_HEAP_SIZE; - /* Try a big heap, and reduce the size if that fails. */ - for (;;) { - ret = do_brk(base + size); - if (ret >= (base + size)) { - break; - } - size >>= 1; - } - ts->heap_limit = base + size; - } - /* - * This call may happen before we have writable memory, so return - * values directly in registers. - */ - env->dregs[1] = ts->heap_limit; - env->aregs[7] = ts->stack_base; - } -#else /* * FIXME: This is wrong for boards where RAM does not start at * address zero. */ env->dregs[1] = current_machine->ram_size; env->aregs[7] = current_machine->ram_size; -#endif return; default: cpu_abort(env_cpu(env), "Unsupported semihosting syscall %d\n", nr); diff --git a/target/m68k/meson.build b/target/m68k/meson.build index 05cd9fbd1e..27d2d7ba87 100644 --- a/target/m68k/meson.build +++ b/target/m68k/meson.build @@ -4,14 +4,16 @@ m68k_ss.add(files( 'fpu_helper.c', 'gdbstub.c', 'helper.c', - 'm68k-semi.c', 'op_helper.c', 'softfloat.c', 'translate.c', )) m68k_softmmu_ss = ss.source_set() -m68k_softmmu_ss.add(files('monitor.c')) +m68k_softmmu_ss.add(files( + 'm68k-semi.c', + 'monitor.c' +)) target_arch += {'m68k': m68k_ss} target_softmmu_arch += {'m68k': m68k_softmmu_ss} From 8ec7e3c53d48c61e31dd251b84a2b8190a14542d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 2 May 2022 00:11:25 -0700 Subject: [PATCH 56/60] target/mips: Use an exception for semihosting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Within do_interrupt, we hold the iothread lock, which is required for Chardev access for the console, and for the round trip for use_gdb_syscalls(). Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/mips/cpu.h | 3 ++- target/mips/tcg/exception.c | 1 + target/mips/tcg/micromips_translate.c.inc | 6 +++--- target/mips/tcg/mips16e_translate.c.inc | 2 +- target/mips/tcg/nanomips_translate.c.inc | 4 ++-- target/mips/tcg/sysemu/mips-semi.c | 4 ++-- target/mips/tcg/sysemu/tlb_helper.c | 4 ++++ target/mips/tcg/sysemu_helper.h.inc | 2 -- target/mips/tcg/tcg-internal.h | 2 ++ target/mips/tcg/translate.c | 12 ++---------- 10 files changed, 19 insertions(+), 21 deletions(-) diff --git a/target/mips/cpu.h b/target/mips/cpu.h index 42efa989e4..0a085643a3 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -1252,8 +1252,9 @@ enum { EXCP_MSAFPE, EXCP_TLBXI, EXCP_TLBRI, + EXCP_SEMIHOST, - EXCP_LAST = EXCP_TLBRI, + EXCP_LAST = EXCP_SEMIHOST, }; /* diff --git a/target/mips/tcg/exception.c b/target/mips/tcg/exception.c index 0b21e0872b..2bd77a61de 100644 --- a/target/mips/tcg/exception.c +++ b/target/mips/tcg/exception.c @@ -125,6 +125,7 @@ static const char * const excp_names[EXCP_LAST + 1] = { [EXCP_TLBRI] = "TLB read-inhibit", [EXCP_MSADIS] = "MSA disabled", [EXCP_MSAFPE] = "MSA floating point", + [EXCP_SEMIHOST] = "Semihosting", }; const char *mips_exception_name(int32_t exception) diff --git a/target/mips/tcg/micromips_translate.c.inc b/target/mips/tcg/micromips_translate.c.inc index fc6ede75b8..274caf2c3c 100644 --- a/target/mips/tcg/micromips_translate.c.inc +++ b/target/mips/tcg/micromips_translate.c.inc @@ -826,7 +826,7 @@ static void gen_pool16c_insn(DisasContext *ctx) break; case SDBBP16: if (is_uhi(extract32(ctx->opcode, 0, 4))) { - gen_helper_do_semihosting(cpu_env); + generate_exception_end(ctx, EXCP_SEMIHOST); } else { /* * XXX: not clear which exception should be raised @@ -942,7 +942,7 @@ static void gen_pool16c_r6_insn(DisasContext *ctx) case R6_SDBBP16: /* SDBBP16 */ if (is_uhi(extract32(ctx->opcode, 6, 4))) { - gen_helper_do_semihosting(cpu_env); + generate_exception_end(ctx, EXCP_SEMIHOST); } else { if (ctx->hflags & MIPS_HFLAG_SBRI) { generate_exception(ctx, EXCP_RI); @@ -1311,7 +1311,7 @@ static void gen_pool32axf(CPUMIPSState *env, DisasContext *ctx, int rt, int rs) break; case SDBBP: if (is_uhi(extract32(ctx->opcode, 16, 10))) { - gen_helper_do_semihosting(cpu_env); + generate_exception_end(ctx, EXCP_SEMIHOST); } else { check_insn(ctx, ISA_MIPS_R1); if (ctx->hflags & MIPS_HFLAG_SBRI) { diff --git a/target/mips/tcg/mips16e_translate.c.inc b/target/mips/tcg/mips16e_translate.c.inc index f57e0a5f2a..0a3ba252e4 100644 --- a/target/mips/tcg/mips16e_translate.c.inc +++ b/target/mips/tcg/mips16e_translate.c.inc @@ -952,7 +952,7 @@ static int decode_ase_mips16e(CPUMIPSState *env, DisasContext *ctx) break; case RR_SDBBP: if (is_uhi(extract32(ctx->opcode, 5, 6))) { - gen_helper_do_semihosting(cpu_env); + generate_exception_end(ctx, EXCP_SEMIHOST); } else { /* * XXX: not clear which exception should be raised diff --git a/target/mips/tcg/nanomips_translate.c.inc b/target/mips/tcg/nanomips_translate.c.inc index c0ba2bf1b1..ecb0ebed57 100644 --- a/target/mips/tcg/nanomips_translate.c.inc +++ b/target/mips/tcg/nanomips_translate.c.inc @@ -3695,7 +3695,7 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) break; case NM_SDBBP: if (is_uhi(extract32(ctx->opcode, 0, 19))) { - gen_helper_do_semihosting(cpu_env); + generate_exception_end(ctx, EXCP_SEMIHOST); } else { if (ctx->hflags & MIPS_HFLAG_SBRI) { gen_reserved_instruction(ctx); @@ -4634,7 +4634,7 @@ static int decode_isa_nanomips(CPUMIPSState *env, DisasContext *ctx) break; case NM_SDBBP16: if (is_uhi(extract32(ctx->opcode, 0, 3))) { - gen_helper_do_semihosting(cpu_env); + generate_exception_end(ctx, EXCP_SEMIHOST); } else { if (ctx->hflags & MIPS_HFLAG_SBRI) { gen_reserved_instruction(ctx); diff --git a/target/mips/tcg/sysemu/mips-semi.c b/target/mips/tcg/sysemu/mips-semi.c index 6d6296e709..ac12c802a3 100644 --- a/target/mips/tcg/sysemu/mips-semi.c +++ b/target/mips/tcg/sysemu/mips-semi.c @@ -20,10 +20,10 @@ #include "qemu/osdep.h" #include "cpu.h" #include "qemu/log.h" -#include "exec/helper-proto.h" #include "semihosting/softmmu-uaccess.h" #include "semihosting/semihost.h" #include "semihosting/console.h" +#include "internal.h" typedef enum UHIOp { UHI_exit = 1, @@ -238,7 +238,7 @@ static int copy_argn_to_target(CPUMIPSState *env, int arg_num, unlock_user(p, gpr, 0); \ } while (0) -void helper_do_semihosting(CPUMIPSState *env) +void mips_semihosting(CPUMIPSState *env) { target_ulong *gpr = env->active_tc.gpr; const UHIOp op = gpr[25]; diff --git a/target/mips/tcg/sysemu/tlb_helper.c b/target/mips/tcg/sysemu/tlb_helper.c index 73254d1929..57ffad2902 100644 --- a/target/mips/tcg/sysemu/tlb_helper.c +++ b/target/mips/tcg/sysemu/tlb_helper.c @@ -1053,6 +1053,10 @@ void mips_cpu_do_interrupt(CPUState *cs) } offset = 0x180; switch (cs->exception_index) { + case EXCP_SEMIHOST: + cs->exception_index = EXCP_NONE; + mips_semihosting(env); + return; case EXCP_DSS: env->CP0_Debug |= 1 << CP0DB_DSS; /* diff --git a/target/mips/tcg/sysemu_helper.h.inc b/target/mips/tcg/sysemu_helper.h.inc index 4353a966f9..af585b5d9c 100644 --- a/target/mips/tcg/sysemu_helper.h.inc +++ b/target/mips/tcg/sysemu_helper.h.inc @@ -9,8 +9,6 @@ * SPDX-License-Identifier: LGPL-2.1-or-later */ -DEF_HELPER_1(do_semihosting, void, env) - /* CP0 helpers */ DEF_HELPER_1(mfc0_mvpcontrol, tl, env) DEF_HELPER_1(mfc0_mvpconf0, tl, env) diff --git a/target/mips/tcg/tcg-internal.h b/target/mips/tcg/tcg-internal.h index 993720b00c..1d27fa2ff9 100644 --- a/target/mips/tcg/tcg-internal.h +++ b/target/mips/tcg/tcg-internal.h @@ -62,6 +62,8 @@ bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); +void mips_semihosting(CPUMIPSState *env); + #endif /* !CONFIG_USER_ONLY */ #endif diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 5f460fb687..d9d7692765 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -12094,14 +12094,6 @@ static inline bool is_uhi(int sdbbp_code) #endif } -#ifdef CONFIG_USER_ONLY -/* The above should dead-code away any calls to this..*/ -static inline void gen_helper_do_semihosting(void *env) -{ - g_assert_not_reached(); -} -#endif - void gen_ldxs(DisasContext *ctx, int base, int index, int rd) { TCGv t0 = tcg_temp_new(); @@ -13910,7 +13902,7 @@ static void decode_opc_special_r6(CPUMIPSState *env, DisasContext *ctx) break; case R6_OPC_SDBBP: if (is_uhi(extract32(ctx->opcode, 6, 20))) { - gen_helper_do_semihosting(cpu_env); + generate_exception_end(ctx, EXCP_SEMIHOST); } else { if (ctx->hflags & MIPS_HFLAG_SBRI) { gen_reserved_instruction(ctx); @@ -14322,7 +14314,7 @@ static void decode_opc_special2_legacy(CPUMIPSState *env, DisasContext *ctx) break; case OPC_SDBBP: if (is_uhi(extract32(ctx->opcode, 6, 20))) { - gen_helper_do_semihosting(cpu_env); + generate_exception_end(ctx, EXCP_SEMIHOST); } else { /* * XXX: not clear which exception should be raised From 7ba6e53a9ddf9b4ae04ad5fc658105440889cf17 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 1 May 2022 20:03:01 -0700 Subject: [PATCH 57/60] target/mips: Add UHI errno values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the Unified Hosting Interface, MD01069 Reference Manual, version 1.1.6, 06 July 2015. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/mips/tcg/sysemu/mips-semi.c | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/target/mips/tcg/sysemu/mips-semi.c b/target/mips/tcg/sysemu/mips-semi.c index ac12c802a3..2a039baf4c 100644 --- a/target/mips/tcg/sysemu/mips-semi.c +++ b/target/mips/tcg/sysemu/mips-semi.c @@ -74,6 +74,46 @@ enum UHIOpenFlags { UHIOpen_EXCL = 0x800 }; +enum UHIErrno { + UHI_EACCESS = 13, + UHI_EAGAIN = 11, + UHI_EBADF = 9, + UHI_EBADMSG = 77, + UHI_EBUSY = 16, + UHI_ECONNRESET = 104, + UHI_EEXIST = 17, + UHI_EFBIG = 27, + UHI_EINTR = 4, + UHI_EINVAL = 22, + UHI_EIO = 5, + UHI_EISDIR = 21, + UHI_ELOOP = 92, + UHI_EMFILE = 24, + UHI_EMLINK = 31, + UHI_ENAMETOOLONG = 91, + UHI_ENETDOWN = 115, + UHI_ENETUNREACH = 114, + UHI_ENFILE = 23, + UHI_ENOBUFS = 105, + UHI_ENOENT = 2, + UHI_ENOMEM = 12, + UHI_ENOSPC = 28, + UHI_ENOSR = 63, + UHI_ENOTCONN = 128, + UHI_ENOTDIR = 20, + UHI_ENXIO = 6, + UHI_EOVERFLOW = 139, + UHI_EPERM = 1, + UHI_EPIPE = 32, + UHI_ERANGE = 34, + UHI_EROFS = 30, + UHI_ESPIPE = 29, + UHI_ETIMEDOUT = 116, + UHI_ETXTBSY = 26, + UHI_EWOULDBLOCK = 11, + UHI_EXDEV = 18, +}; + static int errno_mips(int host_errno) { /* Errno values taken from asm-mips/errno.h */ From 6863e92d04c9ba23cbf7c0c1498e977c646e1faf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 2 May 2022 22:52:19 -0700 Subject: [PATCH 58/60] target/mips: Drop pread and pwrite syscalls from semihosting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't implement it with _WIN32 hosts, and the syscalls are missing from the gdb remote file i/o interface. Since we can't implement them universally, drop them. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/mips/tcg/sysemu/mips-semi.c | 39 ++++++------------------------ 1 file changed, 7 insertions(+), 32 deletions(-) diff --git a/target/mips/tcg/sysemu/mips-semi.c b/target/mips/tcg/sysemu/mips-semi.c index 2a039baf4c..67c35fe7f9 100644 --- a/target/mips/tcg/sysemu/mips-semi.c +++ b/target/mips/tcg/sysemu/mips-semi.c @@ -182,8 +182,8 @@ static int get_open_flags(target_ulong target_flags) return open_flags; } -static int write_to_file(CPUMIPSState *env, target_ulong fd, target_ulong vaddr, - target_ulong len, target_ulong offset) +static int write_to_file(CPUMIPSState *env, target_ulong fd, + target_ulong vaddr, target_ulong len) { int num_of_bytes; void *dst = lock_user(VERIFY_READ, vaddr, len, 1); @@ -192,23 +192,14 @@ static int write_to_file(CPUMIPSState *env, target_ulong fd, target_ulong vaddr, return -1; } - if (offset) { -#ifdef _WIN32 - num_of_bytes = 0; -#else - num_of_bytes = pwrite(fd, dst, len, offset); -#endif - } else { - num_of_bytes = write(fd, dst, len); - } + num_of_bytes = write(fd, dst, len); unlock_user(dst, vaddr, 0); return num_of_bytes; } static int read_from_file(CPUMIPSState *env, target_ulong fd, - target_ulong vaddr, target_ulong len, - target_ulong offset) + target_ulong vaddr, target_ulong len) { int num_of_bytes; void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0); @@ -217,15 +208,7 @@ static int read_from_file(CPUMIPSState *env, target_ulong fd, return -1; } - if (offset) { -#ifdef _WIN32 - num_of_bytes = 0; -#else - num_of_bytes = pread(fd, dst, len, offset); -#endif - } else { - num_of_bytes = read(fd, dst, len); - } + num_of_bytes = read(fd, dst, len); unlock_user(dst, vaddr, len); return num_of_bytes; @@ -312,11 +295,11 @@ void mips_semihosting(CPUMIPSState *env) gpr[3] = errno_mips(errno); break; case UHI_read: - gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], 0); + gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6]); gpr[3] = errno_mips(errno); break; case UHI_write: - gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], 0); + gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6]); gpr[3] = errno_mips(errno); break; case UHI_lseek: @@ -382,14 +365,6 @@ void mips_semihosting(CPUMIPSState *env) FREE_TARGET_STRING(p, gpr[4]); abort(); break; - case UHI_pread: - gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], gpr[7]); - gpr[3] = errno_mips(errno); - break; - case UHI_pwrite: - gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], gpr[7]); - gpr[3] = errno_mips(errno); - break; #ifndef _WIN32 case UHI_link: GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]); From 79cc9724c28c1327deaa5cb8888f44b80640c516 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Apr 2022 16:49:20 -0700 Subject: [PATCH 59/60] target/nios2: Eliminate nios2_semi_is_lseek MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reorg nios2_semi_return_* to gdb_syscall_complete_cb. Use the 32-bit version normally, and the 64-bit version for HOSTED_LSEEK. Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/nios2/nios2-semi.c | 59 +++++++++++++++------------------------ 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/target/nios2/nios2-semi.c b/target/nios2/nios2-semi.c index 4d02789d26..bdf8849689 100644 --- a/target/nios2/nios2-semi.c +++ b/target/nios2/nios2-semi.c @@ -108,9 +108,12 @@ static bool translate_stat(CPUNios2State *env, target_ulong addr, return true; } -static void nios2_semi_return_u32(CPUNios2State *env, uint32_t ret, int err) +static void nios2_semi_u32_cb(CPUState *cs, uint64_t ret, int err) { + Nios2CPU *cpu = NIOS2_CPU(cs); + CPUNios2State *env = &cpu->env; target_ulong args = env->regs[R_ARG1]; + if (put_user_u32(ret, args) || put_user_u32(err, args + 4)) { /* @@ -123,9 +126,12 @@ static void nios2_semi_return_u32(CPUNios2State *env, uint32_t ret, int err) } } -static void nios2_semi_return_u64(CPUNios2State *env, uint64_t ret, int err) +static void nios2_semi_u64_cb(CPUState *cs, uint64_t ret, int err) { + Nios2CPU *cpu = NIOS2_CPU(cs); + CPUNios2State *env = &cpu->env; target_ulong args = env->regs[R_ARG1]; + if (put_user_u32(ret >> 32, args) || put_user_u32(ret, args + 4) || put_user_u32(err, args + 8)) { @@ -135,25 +141,6 @@ static void nios2_semi_return_u64(CPUNios2State *env, uint64_t ret, int err) } } -static int nios2_semi_is_lseek; - -static void nios2_semi_cb(CPUState *cs, uint64_t ret, int err) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - - if (nios2_semi_is_lseek) { - /* - * FIXME: We've already lost the high bits of the lseek - * return value. - */ - nios2_semi_return_u64(env, ret, err); - nios2_semi_is_lseek = 0; - } else { - nios2_semi_return_u32(env, ret, err); - } -} - /* * Read the input value from the argument block; fail the semihosting * call if the memory read fails. @@ -168,6 +155,7 @@ static void nios2_semi_cb(CPUState *cs, uint64_t ret, int err) void do_nios2_semihosting(CPUNios2State *env) { + CPUState *cs = env_cpu(env); int nr; uint32_t args; target_ulong arg0, arg1, arg2, arg3; @@ -188,7 +176,7 @@ void do_nios2_semihosting(CPUNios2State *env) GET_ARG(2); GET_ARG(3); if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "open,%s,%x,%x", arg0, (int)arg1, + gdb_do_syscall(nios2_semi_u32_cb, "open,%s,%x,%x", arg0, (int)arg1, arg2, arg3); return; } else { @@ -209,7 +197,7 @@ void do_nios2_semihosting(CPUNios2State *env) int fd = arg0; if (fd > 2) { if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "close,%x", arg0); + gdb_do_syscall(nios2_semi_u32_cb, "close,%x", arg0); return; } else { result = close(fd); @@ -225,7 +213,7 @@ void do_nios2_semihosting(CPUNios2State *env) GET_ARG(2); len = arg2; if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "read,%x,%x,%x", + gdb_do_syscall(nios2_semi_u32_cb, "read,%x,%x,%x", arg0, arg1, len); return; } else { @@ -245,7 +233,7 @@ void do_nios2_semihosting(CPUNios2State *env) GET_ARG(2); len = arg2; if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "write,%x,%x,%x", + gdb_do_syscall(nios2_semi_u32_cb, "write,%x,%x,%x", arg0, arg1, len); return; } else { @@ -268,12 +256,11 @@ void do_nios2_semihosting(CPUNios2State *env) GET_ARG(3); off = (uint32_t)arg2 | ((uint64_t)arg1 << 32); if (use_gdb_syscalls()) { - nios2_semi_is_lseek = 1; - gdb_do_syscall(nios2_semi_cb, "lseek,%x,%lx,%x", + gdb_do_syscall(nios2_semi_u64_cb, "lseek,%x,%lx,%x", arg0, off, arg3); } else { off = lseek(arg0, off, arg3); - nios2_semi_return_u64(env, off, errno); + nios2_semi_u64_cb(cs, off, errno); } return; } @@ -283,7 +270,7 @@ void do_nios2_semihosting(CPUNios2State *env) GET_ARG(2); GET_ARG(3); if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "rename,%s,%s", + gdb_do_syscall(nios2_semi_u32_cb, "rename,%s,%s", arg0, (int)arg1, arg2, (int)arg3); return; } else { @@ -303,7 +290,7 @@ void do_nios2_semihosting(CPUNios2State *env) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "unlink,%s", + gdb_do_syscall(nios2_semi_u32_cb, "unlink,%s", arg0, (int)arg1); return; } else { @@ -322,7 +309,7 @@ void do_nios2_semihosting(CPUNios2State *env) GET_ARG(1); GET_ARG(2); if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "stat,%s,%x", + gdb_do_syscall(nios2_semi_u32_cb, "stat,%s,%x", arg0, (int)arg1, arg2); return; } else { @@ -345,7 +332,7 @@ void do_nios2_semihosting(CPUNios2State *env) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "fstat,%x,%x", + gdb_do_syscall(nios2_semi_u32_cb, "fstat,%x,%x", arg0, arg1); return; } else { @@ -361,7 +348,7 @@ void do_nios2_semihosting(CPUNios2State *env) /* Only the tv parameter is used. tz is assumed NULL. */ GET_ARG(0); if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "gettimeofday,%x,%x", + gdb_do_syscall(nios2_semi_u32_cb, "gettimeofday,%x,%x", arg0, 0); return; } else { @@ -382,7 +369,7 @@ void do_nios2_semihosting(CPUNios2State *env) case HOSTED_ISATTY: GET_ARG(0); if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "isatty,%x", arg0); + gdb_do_syscall(nios2_semi_u32_cb, "isatty,%x", arg0); return; } else { result = isatty(arg0); @@ -392,7 +379,7 @@ void do_nios2_semihosting(CPUNios2State *env) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "system,%s", + gdb_do_syscall(nios2_semi_u32_cb, "system,%s", arg0, (int)arg1); return; } else { @@ -412,5 +399,5 @@ void do_nios2_semihosting(CPUNios2State *env) result = 0; } failed: - nios2_semi_return_u32(env, result, errno); + nios2_semi_u32_cb(cs, result, errno); } From ca97e0ef99045ce650b842f3bc8c89d76daaafae Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Apr 2022 19:50:02 -0700 Subject: [PATCH 60/60] target/nios2: Move nios2-semi.c to nios2_softmmu_ss MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Semihosting is not enabled for nios2-linux-user. Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/nios2/meson.build | 4 ++-- target/nios2/nios2-semi.c | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/target/nios2/meson.build b/target/nios2/meson.build index 2bd60ba306..c6e2243cc3 100644 --- a/target/nios2/meson.build +++ b/target/nios2/meson.build @@ -1,7 +1,6 @@ nios2_ss = ss.source_set() nios2_ss.add(files( 'cpu.c', - 'nios2-semi.c', 'op_helper.c', 'translate.c', )) @@ -10,7 +9,8 @@ nios2_softmmu_ss = ss.source_set() nios2_softmmu_ss.add(files( 'helper.c', 'monitor.c', - 'mmu.c' + 'mmu.c', + 'nios2-semi.c', )) target_arch += {'nios2': nios2_ss} diff --git a/target/nios2/nios2-semi.c b/target/nios2/nios2-semi.c index bdf8849689..55061bb2dc 100644 --- a/target/nios2/nios2-semi.c +++ b/target/nios2/nios2-semi.c @@ -22,14 +22,9 @@ */ #include "qemu/osdep.h" - #include "cpu.h" #include "exec/gdbstub.h" -#if defined(CONFIG_USER_ONLY) -#include "qemu.h" -#else #include "semihosting/softmmu-uaccess.h" -#endif #include "qemu/log.h" #define HOSTED_EXIT 0