From 4550c661bfa3fd4aba2d5dec7b732546124d8958 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Mon, 24 Jan 2022 01:33:02 -0700 Subject: bsd-user: Complete FreeBSD siginfo Fill in the missing FreeBSD siginfo fields, and add some comments. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/freebsd/target_os_siginfo.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/bsd-user/freebsd/target_os_siginfo.h b/bsd-user/freebsd/target_os_siginfo.h index 84944fa..d50a303 100644 --- a/bsd-user/freebsd/target_os_siginfo.h +++ b/bsd-user/freebsd/target_os_siginfo.h @@ -71,12 +71,25 @@ typedef struct target_siginfo { int32_t _mqd; } _mesgp; - /* SIGPOLL */ + /* SIGPOLL -- Not really genreated in FreeBSD ??? */ struct { int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ } _poll; struct { + int _mqd; + } _mesgq; + + struct { + /* + * Syscall number for signals delivered as a result of system calls + * denied by Capsicum. + */ + int _syscall; + } _capsicum; + + /* Spare for future growth */ + struct { abi_long __spare1__; int32_t __spare2_[7]; } __spare__; -- cgit v1.1 From 2373a62ae9600aef57923fdba0518e916cc8d28c Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Wed, 19 Jan 2022 11:46:05 -0700 Subject: bsd-user: Create setup_sigframe_arch to setup sigframe context Define setup_sigframe_arch whose job it is to setup the mcontext for the sigframe. Implement for x86 to just call mcontext. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/freebsd/target_os_signal.h | 3 +++ bsd-user/i386/signal.c | 13 +++++++++++++ bsd-user/x86_64/signal.c | 13 +++++++++++++ 3 files changed, 29 insertions(+) diff --git a/bsd-user/freebsd/target_os_signal.h b/bsd-user/freebsd/target_os_signal.h index 3ed454e..43700d0 100644 --- a/bsd-user/freebsd/target_os_signal.h +++ b/bsd-user/freebsd/target_os_signal.h @@ -4,6 +4,9 @@ #include "target_os_siginfo.h" #include "target_arch_signal.h" +abi_long setup_sigframe_arch(CPUArchState *env, abi_ulong frame_addr, + struct target_sigframe *frame, int flags); + /* Compare to sys/signal.h */ #define TARGET_SIGHUP 1 /* hangup */ #define TARGET_SIGINT 2 /* interrupt */ diff --git a/bsd-user/i386/signal.c b/bsd-user/i386/signal.c index 2939d32..5dd975c 100644 --- a/bsd-user/i386/signal.c +++ b/bsd-user/i386/signal.c @@ -32,6 +32,19 @@ abi_long set_sigtramp_args(CPUX86State *env, int sig, return 0; } +/* + * Compare to i386/i386/exec_machdep.c sendsig() + * Assumes that the memory is locked if frame points to user memory. + */ +abi_long setup_sigframe_arch(CPUX86State *env, abi_ulong frame_addr, + struct target_sigframe *frame, int flags) +{ + target_mcontext_t *mcp = &frame->sf_uc.uc_mcontext; + + get_mcontext(env, mcp, flags); + return 0; +} + /* Compare to i386/i386/machdep.c get_mcontext() */ abi_long get_mcontext(CPUX86State *regs, target_mcontext_t *mcp, int flags) { diff --git a/bsd-user/x86_64/signal.c b/bsd-user/x86_64/signal.c index 8885152..c3875bc 100644 --- a/bsd-user/x86_64/signal.c +++ b/bsd-user/x86_64/signal.c @@ -30,6 +30,19 @@ abi_long set_sigtramp_args(CPUX86State *regs, return 0; } +/* + * Compare to amd64/amd64/exec_machdep.c sendsig() + * Assumes that the memory is locked if frame points to user memory. + */ +abi_long setup_sigframe_arch(CPUX86State *env, abi_ulong frame_addr, + struct target_sigframe *frame, int flags) +{ + target_mcontext_t *mcp = &frame->sf_uc.uc_mcontext; + + get_mcontext(env, mcp, flags); + return 0; +} + /* Compare to amd64/amd64/machdep.c get_mcontext() */ abi_long get_mcontext(CPUX86State *regs, target_mcontext_t *mcp, int flags) -- cgit v1.1 From 224474622e61f24c4b991fca03e32113eaac91cb Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Wed, 19 Jan 2022 11:49:05 -0700 Subject: bsd-user/arm/signal.c: Implement setup_sigframe_arch for arm Fix the broken context setting for arm. FreeBSD's get_mcontext does not fill in the vfp info. It's filled in in sigframe(). This corresponds to the new setup_sigframe_arch which fills in mcontext, then adjusts it to point to the vfp context in the sigframe and fills in that context as well. Add pointer to where this code is done. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/arm/signal.c | 50 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/bsd-user/arm/signal.c b/bsd-user/arm/signal.c index 1478f00..9026343 100644 --- a/bsd-user/arm/signal.c +++ b/bsd-user/arm/signal.c @@ -59,19 +59,31 @@ abi_long set_sigtramp_args(CPUARMState *env, int sig, return 0; } +static abi_long get_vfpcontext(CPUARMState *env, abi_ulong frame_addr, + struct target_sigframe *frame) +{ + /* see sendsig and get_vfpcontext in sys/arm/arm/exec_machdep.c */ + target_mcontext_vfp_t *vfp = &frame->sf_vfp; + target_mcontext_t *mcp = &frame->sf_uc.uc_mcontext; + + /* Assumes that mcp and vfp are locked */ + for (int i = 0; i < 32; i++) { + vfp->mcv_reg[i] = tswap64(*aa32_vfp_dreg(env, i)); + } + vfp->mcv_fpscr = tswap32(vfp_get_fpscr(env)); + mcp->mc_vfp_size = tswap32(sizeof(*vfp)); + mcp->mc_vfp_ptr = tswap32(frame_addr + ((uintptr_t)vfp - (uintptr_t)frame)); + return 0; +} + /* - * Compare to arm/arm/machdep.c get_mcontext() + * Compare to arm/arm/exec_machdep.c get_mcontext() * Assumes that the memory is locked if mcp points to user memory. */ abi_long get_mcontext(CPUARMState *env, target_mcontext_t *mcp, int flags) { - int err = 0; uint32_t *gr = mcp->__gregs; - if (mcp->mc_vfp_size != 0 && mcp->mc_vfp_size != sizeof(target_mcontext_vfp_t)) { - return -TARGET_EINVAL; - } - gr[TARGET_REG_CPSR] = tswap32(cpsr_read(env)); if (flags & TARGET_MC_GET_CLEAR_RET) { gr[TARGET_REG_R0] = 0; @@ -97,17 +109,21 @@ abi_long get_mcontext(CPUARMState *env, target_mcontext_t *mcp, int flags) gr[TARGET_REG_LR] = tswap32(env->regs[14]); gr[TARGET_REG_PC] = tswap32(env->regs[15]); - if (mcp->mc_vfp_size != 0 && mcp->mc_vfp_ptr != 0) { - /* see get_vfpcontext in sys/arm/arm/exec_machdep.c */ - target_mcontext_vfp_t *vfp; - vfp = lock_user(VERIFY_WRITE, mcp->mc_vfp_ptr, sizeof(*vfp), 0); - for (int i = 0; i < 32; i++) { - vfp->mcv_reg[i] = tswap64(*aa32_vfp_dreg(env, i)); - } - vfp->mcv_fpscr = tswap32(vfp_get_fpscr(env)); - unlock_user(vfp, mcp->mc_vfp_ptr, sizeof(*vfp)); - } - return err; + return 0; +} + +/* + * Compare to arm/arm/exec_machdep.c sendsig() + * Assumes that the memory is locked if frame points to user memory. + */ +abi_long setup_sigframe_arch(CPUARMState *env, abi_ulong frame_addr, + struct target_sigframe *frame, int flags) +{ + target_mcontext_t *mcp = &frame->sf_uc.uc_mcontext; + + get_mcontext(env, mcp, flags); + get_vfpcontext(env, frame_addr, frame); + return 0; } /* Compare to arm/arm/exec_machdep.c set_mcontext() */ -- cgit v1.1 From 7f96d0a93c9f252fc65b0ad49121a62889ec560e Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Mon, 24 Jan 2022 01:29:53 -0700 Subject: bsd-user/arm/signal.c: get_mcontext should zero vfp data FreeBSD's get_mcontext doesn't return any vfp data. Instead, it zeros out the vfp feilds (and all the spare fields). Impelement this behavior. We're still missing the sysarch(ARM_GET_VFPCONTEXT) syscall, though. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/arm/signal.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/bsd-user/arm/signal.c b/bsd-user/arm/signal.c index 9026343..2b1dd74 100644 --- a/bsd-user/arm/signal.c +++ b/bsd-user/arm/signal.c @@ -109,6 +109,15 @@ abi_long get_mcontext(CPUARMState *env, target_mcontext_t *mcp, int flags) gr[TARGET_REG_LR] = tswap32(env->regs[14]); gr[TARGET_REG_PC] = tswap32(env->regs[15]); + /* + * FreeBSD's get_mcontext doesn't save VFP info, but sets the pointer and + * size to zero. Applications that need the VFP state use + * sysarch(ARM_GET_VFPSTATE) and are expected to adjust mcontext after that. + */ + mcp->mc_vfp_size = 0; + mcp->mc_vfp_ptr = 0; + memset(&mcp->mc_spare, 0, sizeof(mcp->mc_spare)); + return 0; } -- cgit v1.1 From b46d4ad7d135d43eb6141e298b3fed9236f4caeb Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sun, 16 Jan 2022 16:14:18 -0700 Subject: bsd-user: Remove vestiges of signal queueing code bsd-user was copied from linux-user at a time when it queued signals. Remove those vestiges of thse code. Retain the init function, even though it's now empty since other stuff will likely be added there. Make it static since it's not called from outside of main.c Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/main.c | 9 +-------- bsd-user/qemu.h | 13 +------------ 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index cb5ea40..29cf4e1 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -215,15 +215,8 @@ void qemu_cpu_kick(CPUState *cpu) } /* Assumes contents are already zeroed. */ -void init_task_state(TaskState *ts) +static void init_task_state(TaskState *ts) { - int i; - - ts->first_free = ts->sigqueue_table; - for (i = 0; i < MAX_SIGQUEUE_SIZE - 1; i++) { - ts->sigqueue_table[i].next = &ts->sigqueue_table[i + 1]; - } - ts->sigqueue_table[i].next = NULL; } void gemu_log(const char *fmt, ...) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 1b3b974..4dd209e 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -70,17 +70,9 @@ struct image_info { uint32_t elf_flags; }; -#define MAX_SIGQUEUE_SIZE 1024 - -struct qemu_sigqueue { - struct qemu_sigqueue *next; - target_siginfo_t info; -}; - struct emulated_sigtable { int pending; /* true if signal is pending */ - struct qemu_sigqueue *first; - struct qemu_sigqueue info; /* Put first signal info here */ + target_siginfo_t info; }; /* @@ -94,14 +86,11 @@ typedef struct TaskState { struct image_info *info; struct emulated_sigtable sigtab[TARGET_NSIG]; - struct qemu_sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */ - struct qemu_sigqueue *first_free; /* first free siginfo queue entry */ int signal_pending; /* non zero if a signal may be pending */ uint8_t stack[]; } __attribute__((aligned(16))) TaskState; -void init_task_state(TaskState *ts); void stop_all_tasks(void); extern const char *qemu_uname_release; -- cgit v1.1 From 4804722593bd1735ce810e380247788200bcb961 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sun, 16 Jan 2022 16:33:21 -0700 Subject: bsd-user: Bring in docs from linux-user for signal_pending This is currently unused, so no code adjustments are needed. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/qemu.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 4dd209e..671b26f 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -86,7 +86,14 @@ typedef struct TaskState { struct image_info *info; struct emulated_sigtable sigtab[TARGET_NSIG]; - int signal_pending; /* non zero if a signal may be pending */ + /* + * Nonzero if process_pending_signals() needs to do something (either + * handle a pending signal or unblock signals). + * This flag is written from a signal handler so should be accessed via + * the qatomic_read() and qatomic_set() functions. (It is not accessed + * from multiple threads.) + */ + int signal_pending; uint8_t stack[]; } __attribute__((aligned(16))) TaskState; -- cgit v1.1 From c0d2691ccce7828ade341a263df1d51ce1dfe9ff Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 15:41:10 -0700 Subject: bsd-user/arm/target_arch_cpu.h: Move EXCP_ATOMIC to match linux-user Move the EXCP_ATOMIC case to match linux-user/arm/cpu_loop.c:cpu_loop ordering. Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/arm/target_arch_cpu.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bsd-user/arm/target_arch_cpu.h b/bsd-user/arm/target_arch_cpu.h index c675419..c526fc7 100644 --- a/bsd-user/arm/target_arch_cpu.h +++ b/bsd-user/arm/target_arch_cpu.h @@ -180,12 +180,12 @@ static inline void target_cpu_loop(CPUARMState *env) queue_signal(env, info.si_signo, &info); } break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; case EXCP_YIELD: /* nothing to do here for user-mode, just resume guest code */ break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; default: fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); -- cgit v1.1 From 0ef599897345e0a43b3741a9990866c92a33d6e9 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 15:58:34 -0700 Subject: bsd-user/signal.c: implement force_sig_fault Start to implement the force_sig_fault code. This currently just calls queue_signal(). The bsd-user fork version of that will handle this the synchronous nature of this call. Add signal-common.h to hold signal helper functions like force_sig_fault. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/signal-common.h | 14 ++++++++++++++ bsd-user/signal.c | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 bsd-user/signal-common.h diff --git a/bsd-user/signal-common.h b/bsd-user/signal-common.h new file mode 100644 index 0000000..6207417 --- /dev/null +++ b/bsd-user/signal-common.h @@ -0,0 +1,14 @@ +/* + * Emulation of BSD signals + * + * Copyright (c) 2013 Stacey Son + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SIGNAL_COMMON_H +#define SIGNAL_COMMON_H + +void force_sig_fault(int sig, int code, abi_ulong addr); + +#endif diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 05b277c..1206d0d 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "qemu.h" +#include "signal-common.h" /* * Stubbed out routines until we merge signal support from bsd-user @@ -34,6 +35,23 @@ void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info) qemu_log_mask(LOG_UNIMP, "No signal queueing, dropping signal %d\n", sig); } +/* + * Force a synchronously taken QEMU_SI_FAULT signal. For QEMU the + * 'force' part is handled in process_pending_signals(). + */ +void force_sig_fault(int sig, int code, abi_ulong addr) +{ + CPUState *cpu = thread_cpu; + CPUArchState *env = cpu->env_ptr; + target_siginfo_t info = {}; + + info.si_signo = sig; + info.si_errno = 0; + info.si_code = code; + info.si_addr = addr; + queue_signal(env, sig, &info); +} + void signal_init(void) { } -- cgit v1.1 From 2bd010c4bfdaecee33f3ba4a785ccaaf84df25c1 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Mon, 24 Jan 2022 16:11:46 -0700 Subject: bsd-user/signal-common.h: Move signal functions prototypes to here Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/arm/target_arch_cpu.h | 1 + bsd-user/i386/target_arch_cpu.h | 1 + bsd-user/qemu.h | 8 -------- bsd-user/signal-common.h | 6 ++++++ bsd-user/x86_64/target_arch_cpu.h | 1 + 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/bsd-user/arm/target_arch_cpu.h b/bsd-user/arm/target_arch_cpu.h index c526fc7..b7f728f 100644 --- a/bsd-user/arm/target_arch_cpu.h +++ b/bsd-user/arm/target_arch_cpu.h @@ -21,6 +21,7 @@ #define _TARGET_ARCH_CPU_H_ #include "target_arch.h" +#include "signal-common.h" #define TARGET_DEFAULT_CPU_MODEL "any" diff --git a/bsd-user/i386/target_arch_cpu.h b/bsd-user/i386/target_arch_cpu.h index b28602a..472a966 100644 --- a/bsd-user/i386/target_arch_cpu.h +++ b/bsd-user/i386/target_arch_cpu.h @@ -20,6 +20,7 @@ #define _TARGET_ARCH_CPU_H_ #include "target_arch.h" +#include "signal-common.h" #define TARGET_DEFAULT_CPU_MODEL "qemu32" diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 671b26f..99c37fc 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -199,14 +199,6 @@ print_openbsd_syscall(int num, void print_openbsd_syscall_ret(int num, abi_long ret); extern int do_strace; -/* signal.c */ -void process_pending_signals(CPUArchState *cpu_env); -void signal_init(void); -long do_sigreturn(CPUArchState *env); -long do_rt_sigreturn(CPUArchState *env); -void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info); -abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp); - /* mmap.c */ int target_mprotect(abi_ulong start, abi_ulong len, int prot); abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, diff --git a/bsd-user/signal-common.h b/bsd-user/signal-common.h index 6207417..f9a9d1e 100644 --- a/bsd-user/signal-common.h +++ b/bsd-user/signal-common.h @@ -9,6 +9,12 @@ #ifndef SIGNAL_COMMON_H #define SIGNAL_COMMON_H +long do_rt_sigreturn(CPUArchState *env); +abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp); +long do_sigreturn(CPUArchState *env); void force_sig_fault(int sig, int code, abi_ulong addr); +void process_pending_signals(CPUArchState *env); +void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info); +void signal_init(void); #endif diff --git a/bsd-user/x86_64/target_arch_cpu.h b/bsd-user/x86_64/target_arch_cpu.h index 5172b23..14def48 100644 --- a/bsd-user/x86_64/target_arch_cpu.h +++ b/bsd-user/x86_64/target_arch_cpu.h @@ -20,6 +20,7 @@ #define _TARGET_ARCH_CPU_H_ #include "target_arch.h" +#include "signal-common.h" #define TARGET_DEFAULT_CPU_MODEL "qemu64" -- cgit v1.1 From fc9f9bdd3a6111d0bb419282657f89eea7d8de88 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 16:03:51 -0700 Subject: bsd-user/signal.c: Implement cpu_loop_exit_sigsegv First attempt at implementing cpu_loop_exit_sigsegv, mostly copied from linux-user version of this function. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/signal.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 1206d0d..12de0e2 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "signal-common.h" +#include "hw/core/tcg-cpu-ops.h" /* * Stubbed out routines until we merge signal support from bsd-user @@ -63,9 +64,17 @@ void process_pending_signals(CPUArchState *cpu_env) void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, MMUAccessType access_type, bool maperr, uintptr_t ra) { - qemu_log_mask(LOG_UNIMP, "No signal support for SIGSEGV\n"); - /* unreachable */ - abort(); + const struct TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; + + if (tcg_ops->record_sigsegv) { + tcg_ops->record_sigsegv(cpu, addr, access_type, maperr, ra); + } + + force_sig_fault(TARGET_SIGSEGV, + maperr ? TARGET_SEGV_MAPERR : TARGET_SEGV_ACCERR, + addr); + cpu->exception_index = EXCP_INTERRUPT; + cpu_loop_exit_restore(cpu, ra); } void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, -- cgit v1.1 From cfdee273c4e41d0b485bc82966a82d6ab6f37a1d Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 16:06:33 -0700 Subject: bsd-user/signal.c: implement cpu_loop_exit_sigbus First attempt at implementing cpu_loop_exit_sigbus, mostly copied from linux-user version of this function. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/signal.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 12de0e2..844dfa1 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -80,7 +80,13 @@ void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, MMUAccessType access_type, uintptr_t ra) { - qemu_log_mask(LOG_UNIMP, "No signal support for SIGBUS\n"); - /* unreachable */ - abort(); + const struct TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; + + if (tcg_ops->record_sigbus) { + tcg_ops->record_sigbus(cpu, addr, access_type, ra); + } + + force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN, addr); + cpu->exception_index = EXCP_INTERRUPT; + cpu_loop_exit_restore(cpu, ra); } -- cgit v1.1 From a3ed97cee57279620d934642c6da1eb0c5ae9df2 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 16:11:15 -0700 Subject: bsd-user/arm/arget_arch_cpu.h: Move EXCP_DEBUG and EXCP_BKPT together Implement EXCP_DEBUG and EXCP_BKPT the same, as is done in linux-user. The prior adjustment of register 15 isn't needed, so remove that. Remove a redunant comment (that code in FreeBSD never handled break points). It's unclear why BKPT was an alias for system calls, but FreeBSD doesn't do that today. Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/arm/target_arch_cpu.h | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/bsd-user/arm/target_arch_cpu.h b/bsd-user/arm/target_arch_cpu.h index b7f728f..05b19ce 100644 --- a/bsd-user/arm/target_arch_cpu.h +++ b/bsd-user/arm/target_arch_cpu.h @@ -65,19 +65,7 @@ static inline void target_cpu_loop(CPUARMState *env) } break; case EXCP_SWI: - case EXCP_BKPT: { - /* - * system call - * See arm/arm/trap.c cpu_fetch_syscall_args() - */ - if (trapnr == EXCP_BKPT) { - if (env->thumb) { - env->regs[15] += 2; - } else { - env->regs[15] += 4; - } - } n = env->regs[7]; if (bsd_type == target_freebsd) { int ret; @@ -172,14 +160,8 @@ static inline void target_cpu_loop(CPUARMState *env) queue_signal(env, info.si_signo, &info); break; case EXCP_DEBUG: - { - - info.si_signo = TARGET_SIGTRAP; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - info.si_addr = env->exception.vaddress; - queue_signal(env, info.si_signo, &info); - } + case EXCP_BKPT: + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->regs[15]); break; case EXCP_YIELD: /* nothing to do here for user-mode, just resume guest code */ -- cgit v1.1 From c0b93df35248fcb842173abf583fe59d2b2692a5 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 16:14:04 -0700 Subject: bsd-user/arm/target_arch_cpu.h: Correct code pointer The code has moved in FreeBSD since the emulator was started, update the comment to reflect that change. Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/arm/target_arch_cpu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bsd-user/arm/target_arch_cpu.h b/bsd-user/arm/target_arch_cpu.h index 05b19ce..905f13a 100644 --- a/bsd-user/arm/target_arch_cpu.h +++ b/bsd-user/arm/target_arch_cpu.h @@ -73,7 +73,7 @@ static inline void target_cpu_loop(CPUARMState *env) int32_t syscall_nr = n; int32_t arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8; - /* See arm/arm/trap.c cpu_fetch_syscall_args() */ + /* See arm/arm/syscall.c cpu_fetch_syscall_args() */ if (syscall_nr == TARGET_FREEBSD_NR_syscall) { syscall_nr = env->regs[0]; arg1 = env->regs[1]; -- cgit v1.1 From 5e02ded157a7db45c3f06bd8c9d60f62d5bdeb1c Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 16:22:16 -0700 Subject: bsd-user/arm/target_arch_cpu.h: Use force_sig_fault for EXCP_UDEF Use force_sig_fault to implement unknown opcode. This just uninlines that function, so simplify things by using it. Fold in EXCP_NOCP and EXCP_INVSTATE, as is done in linux-user. Make a note about slight differences with FreeBSD in case any of them turn out to be important later. Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/arm/target_arch_cpu.h | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/bsd-user/arm/target_arch_cpu.h b/bsd-user/arm/target_arch_cpu.h index 905f13a..9d79017 100644 --- a/bsd-user/arm/target_arch_cpu.h +++ b/bsd-user/arm/target_arch_cpu.h @@ -51,18 +51,19 @@ static inline void target_cpu_loop(CPUARMState *env) process_queued_cpu_work(cs); switch (trapnr) { case EXCP_UDEF: - { - /* See arm/arm/undefined.c undefinedinstruction(); */ - info.si_addr = env->regs[15]; - - /* illegal instruction */ - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPC; - queue_signal(env, info.si_signo, &info); - - /* TODO: What about instruction emulation? */ - } + case EXCP_NOCP: + case EXCP_INVSTATE: + /* + * See arm/arm/undefined.c undefinedinstruction(); + * + * A number of details aren't emulated (they likely don't matter): + * o Misaligned PC generates ILL_ILLADR (these can't come from qemu) + * o Thumb-2 instructions generate ILLADR + * o Both modes implement coprocessor instructions, which we don't + * do here. FreeBSD just implements them for the VFP coprocessor + * and special kernel breakpoints, trace points, dtrace, etc. + */ + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->regs[15]); break; case EXCP_SWI: { -- cgit v1.1 From 67ccbe798fef0912da54ecfddcf6ef5f0a02020b Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 16:26:55 -0700 Subject: bsd-user/arm/target_arch_cpu.h: Implement data faults Update for the richer set of data faults that are now possible. Copied largely from linux-user/arm/cpu_loop.c, with minor typo fixes. Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/arm/target_arch_cpu.h | 45 ++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/bsd-user/arm/target_arch_cpu.h b/bsd-user/arm/target_arch_cpu.h index 9d79017..2b395d5 100644 --- a/bsd-user/arm/target_arch_cpu.h +++ b/bsd-user/arm/target_arch_cpu.h @@ -39,8 +39,7 @@ static inline void target_cpu_init(CPUARMState *env, static inline void target_cpu_loop(CPUARMState *env) { - int trapnr; - target_siginfo_t info; + int trapnr, si_signo, si_code; unsigned int n; CPUState *cs = env_cpu(env); @@ -150,15 +149,41 @@ static inline void target_cpu_loop(CPUARMState *env) /* just indicate that signals should be handled asap */ break; case EXCP_PREFETCH_ABORT: - /* See arm/arm/trap.c prefetch_abort_handler() */ case EXCP_DATA_ABORT: - /* See arm/arm/trap.c data_abort_handler() */ - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = 0; - info.si_addr = env->exception.vaddress; - queue_signal(env, info.si_signo, &info); + /* + * See arm/arm/trap-v6.c prefetch_abort_handler() and + * data_abort_handler() + * + * However, FreeBSD maps these to a generic value and then uses that + * to maybe fault in pages in vm/vm_fault.c:vm_fault_trap(). I + * believe that the indirection maps the same as Linux, but haven't + * chased down every single possible indirection. + */ + + /* For user-only we don't set TTBCR_EAE, so look at the FSR. */ + switch (env->exception.fsr & 0x1f) { + case 0x1: /* Alignment */ + si_signo = TARGET_SIGBUS; + si_code = TARGET_BUS_ADRALN; + break; + case 0x3: /* Access flag fault, level 1 */ + case 0x6: /* Access flag fault, level 2 */ + case 0x9: /* Domain fault, level 1 */ + case 0xb: /* Domain fault, level 2 */ + case 0xd: /* Permission fault, level 1 */ + case 0xf: /* Permission fault, level 2 */ + si_signo = TARGET_SIGSEGV; + si_code = TARGET_SEGV_ACCERR; + break; + case 0x5: /* Translation fault, level 1 */ + case 0x7: /* Translation fault, level 2 */ + si_signo = TARGET_SIGSEGV; + si_code = TARGET_SEGV_MAPERR; + break; + default: + g_assert_not_reached(); + } + force_sig_fault(si_signo, si_code, env->exception.vaddress); break; case EXCP_DEBUG: case EXCP_BKPT: -- cgit v1.1 From 1366ef817a151f8f89a561494ea24204ad7917c7 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 16:48:03 -0700 Subject: bsd-user/signal.c: implement abstract target / host signal translation Implement host_to_target_signal and target_to_host_signal. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/signal-common.h | 2 ++ bsd-user/signal.c | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/bsd-user/signal-common.h b/bsd-user/signal-common.h index f9a9d1e..efed23d 100644 --- a/bsd-user/signal-common.h +++ b/bsd-user/signal-common.h @@ -13,8 +13,10 @@ long do_rt_sigreturn(CPUArchState *env); abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp); long do_sigreturn(CPUArchState *env); void force_sig_fault(int sig, int code, abi_ulong addr); +int host_to_target_signal(int sig); void process_pending_signals(CPUArchState *env); void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info); void signal_init(void); +int target_to_host_signal(int sig); #endif diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 844dfa1..1313bae 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -2,6 +2,7 @@ * Emulation of BSD signals * * Copyright (c) 2003 - 2008 Fabrice Bellard + * Copyright (c) 2013 Stacey Son * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,6 +29,21 @@ */ /* + * The BSD ABIs use the same singal numbers across all the CPU architectures, so + * (unlike Linux) these functions are just the identity mapping. This might not + * be true for XyzBSD running on AbcBSD, which doesn't currently work. + */ +int host_to_target_signal(int sig) +{ + return sig; +} + +int target_to_host_signal(int sig) +{ + return sig; +} + +/* * Queue a signal so that it will be send to the virtual CPU as soon as * possible. */ -- cgit v1.1 From 149076ade7b8250fa62a6b1e7462f8d2c340b27e Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 16:57:31 -0700 Subject: bsd-user/signal.c: Implement signal_init() Initialize the signal state for the emulator. Setup a set of sane default signal handlers, mirroring the host's signals. For fatal signals (those that exit by default), establish our own set of signal handlers. Stub out the actual signal handler we use for the moment. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson XXX SIGPROF PENDING --- bsd-user/qemu.h | 7 ++++++ bsd-user/signal.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 99c37fc..49f0193 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -94,6 +94,13 @@ typedef struct TaskState { * from multiple threads.) */ int signal_pending; + /* + * This thread's signal mask, as requested by the guest program. + * The actual signal mask of this thread may differ: + * + we don't let SIGSEGV and SIGBUS be blocked while running guest code + * + sometimes we block all signals to avoid races + */ + sigset_t signal_mask; uint8_t stack[]; } __attribute__((aligned(16))) TaskState; diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 1313bae..3ef7cf5 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -28,6 +28,9 @@ * fork. */ +static struct target_sigaction sigact_table[TARGET_NSIG]; +static void host_signal_handler(int host_sig, siginfo_t *info, void *puc); + /* * The BSD ABIs use the same singal numbers across all the CPU architectures, so * (unlike Linux) these functions are just the identity mapping. This might not @@ -52,6 +55,28 @@ void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info) qemu_log_mask(LOG_UNIMP, "No signal queueing, dropping signal %d\n", sig); } +static int fatal_signal(int sig) +{ + + switch (sig) { + case TARGET_SIGCHLD: + case TARGET_SIGURG: + case TARGET_SIGWINCH: + case TARGET_SIGINFO: + /* Ignored by default. */ + return 0; + case TARGET_SIGCONT: + case TARGET_SIGSTOP: + case TARGET_SIGTSTP: + case TARGET_SIGTTIN: + case TARGET_SIGTTOU: + /* Job control signals. */ + return 0; + default: + return 1; + } +} + /* * Force a synchronously taken QEMU_SI_FAULT signal. For QEMU the * 'force' part is handled in process_pending_signals(). @@ -69,8 +94,50 @@ void force_sig_fault(int sig, int code, abi_ulong addr) queue_signal(env, sig, &info); } +static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) +{ +} + void signal_init(void) { + TaskState *ts = (TaskState *)thread_cpu->opaque; + struct sigaction act; + struct sigaction oact; + int i; + int host_sig; + + /* Set the signal mask from the host mask. */ + sigprocmask(0, 0, &ts->signal_mask); + + sigfillset(&act.sa_mask); + act.sa_sigaction = host_signal_handler; + act.sa_flags = SA_SIGINFO; + + for (i = 1; i <= TARGET_NSIG; i++) { +#ifdef CONFIG_GPROF + if (i == TARGET_SIGPROF) { + continue; + } +#endif + host_sig = target_to_host_signal(i); + sigaction(host_sig, NULL, &oact); + if (oact.sa_sigaction == (void *)SIG_IGN) { + sigact_table[i - 1]._sa_handler = TARGET_SIG_IGN; + } else if (oact.sa_sigaction == (void *)SIG_DFL) { + sigact_table[i - 1]._sa_handler = TARGET_SIG_DFL; + } + /* + * If there's already a handler installed then something has + * gone horribly wrong, so don't even try to handle that case. + * Install some handlers for our own use. We need at least + * SIGSEGV and SIGBUS, to detect exceptions. We can not just + * trap all signals because it affects syscall interrupt + * behavior. But do trap all default-fatal signals. + */ + if (fatal_signal(i)) { + sigaction(host_sig, &act, NULL); + } + } } void process_pending_signals(CPUArchState *cpu_env) -- cgit v1.1 From e32a63010ff221f7e161a592972076d2976c5eae Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sun, 16 Jan 2022 09:28:59 -0700 Subject: bsd-user/signal.c: Add si_type argument to queue_signal Mirror the linux-user practice and add a si_type argument to queue signal. This will be transported as the upper 8 bits in the si_type element of siginfo so that we know what bits of the structure are valid and so we can properly implement host_to_target_siginfo_noswap and tswap_siginfo. Adapt the one caller of queue_signal to the new interface. Use all the same names as Linux (except _RT which we don't treat differently, unlike Linux), though some are unused. Place this into signal-common.h since that's a better place given bsd-user's structure. Move prototype of queue_signal to signal-common.h to mirror linux-user's location. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal-common.h | 26 +++++++++++++++++++++++++- bsd-user/signal.c | 5 +++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/bsd-user/signal-common.h b/bsd-user/signal-common.h index efed23d..80e9503 100644 --- a/bsd-user/signal-common.h +++ b/bsd-user/signal-common.h @@ -15,8 +15,32 @@ long do_sigreturn(CPUArchState *env); void force_sig_fault(int sig, int code, abi_ulong addr); int host_to_target_signal(int sig); void process_pending_signals(CPUArchState *env); -void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info); +void queue_signal(CPUArchState *env, int sig, int si_type, + target_siginfo_t *info); void signal_init(void); int target_to_host_signal(int sig); +/* + * Within QEMU the top 8 bits of si_code indicate which of the parts of the + * union in target_siginfo is valid. This only applies between + * host_to_target_siginfo_noswap() and tswap_siginfo(); it does not appear + * either within host siginfo_t or in target_siginfo structures which we get + * from the guest userspace program. Linux kenrels use this internally, but BSD + * kernels don't do this, but its a useful abstraction. + * + * The linux-user version of this uses the top 16 bits, but FreeBSD's SI_USER + * and other signal indepenent SI_ codes have bit 16 set, so we only use the top + * byte instead. + * + * For FreeBSD, we have si_pid, si_uid, si_status, and si_addr always. Linux and + * {Open,Net}BSD have a different approach (where their reason field is larger, + * but whose siginfo has fewer fields always). + */ +#define QEMU_SI_NOINFO 0 /* nothing other than si_signo valid */ +#define QEMU_SI_FAULT 1 /* _fault is valid in _reason */ +#define QEMU_SI_TIMER 2 /* _timer is valid in _reason */ +#define QEMU_SI_MESGQ 3 /* _mesgq is valid in _reason */ +#define QEMU_SI_POLL 4 /* _poll is valid in _reason */ +#define QEMU_SI_CAPSICUM 5 /* _capsicum is valid in _reason */ + #endif diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 3ef7cf5..ad8437a 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -50,7 +50,8 @@ int target_to_host_signal(int sig) * Queue a signal so that it will be send to the virtual CPU as soon as * possible. */ -void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info) +void queue_signal(CPUArchState *env, int sig, int si_type, + target_siginfo_t *info) { qemu_log_mask(LOG_UNIMP, "No signal queueing, dropping signal %d\n", sig); } @@ -91,7 +92,7 @@ void force_sig_fault(int sig, int code, abi_ulong addr) info.si_errno = 0; info.si_code = code; info.si_addr = addr; - queue_signal(env, sig, &info); + queue_signal(env, sig, QEMU_SI_FAULT, &info); } static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) -- cgit v1.1 From 6e0bc06e210cbd25006c3a39e9a8325784d0be78 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 17:14:12 -0700 Subject: bsd-user/host/arm/host-signal.h: Implement host_signal_* Implement host_signal_pc, host_signal_set_pc and host_signal_write for arm. Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/host/arm/host-signal.h | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 bsd-user/host/arm/host-signal.h diff --git a/bsd-user/host/arm/host-signal.h b/bsd-user/host/arm/host-signal.h new file mode 100644 index 0000000..56679bd --- /dev/null +++ b/bsd-user/host/arm/host-signal.h @@ -0,0 +1,35 @@ +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2021 Warner Losh + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ARM_HOST_SIGNAL_H +#define ARM_HOST_SIGNAL_H + +#include + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.__gregs[_REG_PC]; +} + +static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +{ + uc->uc_mcontext.__gregs[_REG_PC] = pc; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + /* + * In the FSR, bit 11 is WnR. FreeBSD returns this as part of the + * si_info.si_trapno. + */ + uint32_t fsr = info->si_trapno; + + return extract32(fsr, 11, 1); +} + +#endif -- cgit v1.1 From 220f8606c8d48e5d6d4145abccebb0fa8518c507 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 17:15:19 -0700 Subject: bsd-user/host/i386/host-signal.h: Implement host_signal_* Implement host_signal_pc, host_signal_set_pc and host_signal_write for i386. Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/host/i386/host-signal.h | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 bsd-user/host/i386/host-signal.h diff --git a/bsd-user/host/i386/host-signal.h b/bsd-user/host/i386/host-signal.h new file mode 100644 index 0000000..169e61b --- /dev/null +++ b/bsd-user/host/i386/host-signal.h @@ -0,0 +1,37 @@ +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2021 Warner Losh + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef I386_HOST_SIGNAL_H +#define I386_HOST_SIGNAL_H + +#include +#include +#include +#include + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.mc_eip; +} + +static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +{ + uc->uc_mcontext.mc_eip = pc; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + /* + * Look in sys/i386/i386/trap.c. NOTE: mc_err == tr_err due to type punning + * between a trapframe and mcontext on FreeBSD/i386. + */ + return uc->uc_mcontext.mc_trapno == T_PAGEFLT && + uc->uc_mcontext.mc_err & PGEX_W; +} + +#endif -- cgit v1.1 From b375158801e804cfbf2ff45edab1bd7590fdad30 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 17:15:57 -0700 Subject: bsd-user/host/x86_64/host-signal.h: Implement host_signal_* Implement host_signal_pc, host_signal_set_pc and host_signal_write for x86_64. Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/host/x86_64/host-signal.h | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 bsd-user/host/x86_64/host-signal.h diff --git a/bsd-user/host/x86_64/host-signal.h b/bsd-user/host/x86_64/host-signal.h new file mode 100644 index 0000000..47ca19f --- /dev/null +++ b/bsd-user/host/x86_64/host-signal.h @@ -0,0 +1,37 @@ +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2021 Warner Losh + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef X86_64_HOST_SIGNAL_H +#define X86_64_HOST_SIGNAL_H + +#include +#include +#include +#include + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.mc_rip; +} + +static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +{ + uc->uc_mcontext.mc_rip = pc; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + /* + * Look in sys/amd64/amd64/trap.c. NOTE: mc_err == tr_err due to type + * punning between a trapframe and mcontext on FreeBSD/amd64. + */ + return uc->uc_mcontext.mc_trapno == T_PAGEFLT && + uc->uc_mcontext.mc_err & PGEX_W; +} + +#endif -- cgit v1.1 From 85fc1b5dbf893254471809eef8ec773bb29d4f48 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 17:27:34 -0700 Subject: bsd-user: Add host signals to the build Start to add the host signal functionality to the build. Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/signal.c | 1 + meson.build | 1 + 2 files changed, 2 insertions(+) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index ad8437a..f3e020e 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -22,6 +22,7 @@ #include "qemu.h" #include "signal-common.h" #include "hw/core/tcg-cpu-ops.h" +#include "host-signal.h" /* * Stubbed out routines until we merge signal support from bsd-user diff --git a/meson.build b/meson.build index 5dbc9a7..155403d 100644 --- a/meson.build +++ b/meson.build @@ -2947,6 +2947,7 @@ foreach target : target_dirs if 'CONFIG_BSD_USER' in config_target base_dir = 'bsd-user' target_inc += include_directories('bsd-user/' / targetos) + target_inc += include_directories('bsd-user/host/' / host_arch) dir = base_dir / abi arch_srcs += files(dir / 'signal.c', dir / 'target_arch_cpu.c') endif -- cgit v1.1 From 6ddc1abe0fd1099f807b27306b518752ea3f40e0 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 17:37:23 -0700 Subject: bsd-user: Add trace events for bsd-user Add the bsd-user specific events and infrastructure. Only include the linux-user trace events for linux-user, not bsd-user. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/signal.c | 1 + bsd-user/trace-events | 11 +++++++++++ bsd-user/trace.h | 1 + meson.build | 5 ++++- 4 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 bsd-user/trace-events create mode 100644 bsd-user/trace.h diff --git a/bsd-user/signal.c b/bsd-user/signal.c index f3e020e..cb0036a 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "signal-common.h" +#include "trace.h" #include "hw/core/tcg-cpu-ops.h" #include "host-signal.h" diff --git a/bsd-user/trace-events b/bsd-user/trace-events new file mode 100644 index 0000000..843896f --- /dev/null +++ b/bsd-user/trace-events @@ -0,0 +1,11 @@ +# See docs/tracing.txt for syntax documentation. + +# bsd-user/signal.c +user_setup_frame(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64 +user_setup_rt_frame(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64 +user_do_rt_sigreturn(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64 +user_do_sigreturn(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64 +user_dump_core_and_abort(void *env, int target_sig, int host_sig) "env=%p signal %d (host %d)" +user_handle_signal(void *env, int target_sig) "env=%p signal %d" +user_host_signal(void *env, int host_sig, int target_sig) "env=%p signal %d (target %d(" +user_queue_signal(void *env, int target_sig) "env=%p signal %d" diff --git a/bsd-user/trace.h b/bsd-user/trace.h new file mode 100644 index 0000000..593c020 --- /dev/null +++ b/bsd-user/trace.h @@ -0,0 +1 @@ +#include "trace/trace-bsd_user.h" diff --git a/meson.build b/meson.build index 155403d..5f43355 100644 --- a/meson.build +++ b/meson.build @@ -2458,9 +2458,12 @@ trace_events_subdirs = [ 'monitor', 'util', ] -if have_user +if have_linux_user trace_events_subdirs += [ 'linux-user' ] endif +if have_bsd_user + trace_events_subdirs += [ 'bsd-user' ] +endif if have_block trace_events_subdirs += [ 'authz', -- cgit v1.1 From c34f2aaff645cbda1d69f71b8fa9173fec5b1a8d Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 17:50:38 -0700 Subject: bsd-user/signal.c: host_to_target_siginfo_noswap Implement conversion of host to target siginfo. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index cb0036a..db8cf0a 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -48,6 +48,119 @@ int target_to_host_signal(int sig) return sig; } +static bool has_trapno(int tsig) +{ + return tsig == TARGET_SIGILL || + tsig == TARGET_SIGFPE || + tsig == TARGET_SIGSEGV || + tsig == TARGET_SIGBUS || + tsig == TARGET_SIGTRAP; +} + + +/* Siginfo conversion. */ + +/* + * Populate tinfo w/o swapping based on guessing which fields are valid. + */ +static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, + const siginfo_t *info) +{ + int sig = host_to_target_signal(info->si_signo); + int si_code = info->si_code; + int si_type; + + /* + * Make sure we that the variable portion of the target siginfo is zeroed + * out so we don't leak anything into that. + */ + memset(&tinfo->_reason, 0, sizeof(tinfo->_reason)); + + /* + * This is awkward, because we have to use a combination of the si_code and + * si_signo to figure out which of the union's members are valid.o We + * therefore make our best guess. + * + * Once we have made our guess, we record it in the top 16 bits of + * the si_code, so that tswap_siginfo() later can use it. + * tswap_siginfo() will strip these top bits out before writing + * si_code to the guest (sign-extending the lower bits). + */ + tinfo->si_signo = sig; + tinfo->si_errno = info->si_errno; + tinfo->si_code = info->si_code; + tinfo->si_pid = info->si_pid; + tinfo->si_uid = info->si_uid; + tinfo->si_status = info->si_status; + tinfo->si_addr = (abi_ulong)(unsigned long)info->si_addr; + /* + * si_value is opaque to kernel. On all FreeBSD platforms, + * sizeof(sival_ptr) >= sizeof(sival_int) so the following + * always will copy the larger element. + */ + tinfo->si_value.sival_ptr = + (abi_ulong)(unsigned long)info->si_value.sival_ptr; + + switch (si_code) { + /* + * All the SI_xxx codes that are defined here are global to + * all the signals (they have values that none of the other, + * more specific signal info will set). + */ + case SI_USER: + case SI_LWP: + case SI_KERNEL: + case SI_QUEUE: + case SI_ASYNCIO: + /* + * Only the fixed parts are valid (though FreeBSD doesn't always + * set all the fields to non-zero values. + */ + si_type = QEMU_SI_NOINFO; + break; + case SI_TIMER: + tinfo->_reason._timer._timerid = info->_reason._timer._timerid; + tinfo->_reason._timer._overrun = info->_reason._timer._overrun; + si_type = QEMU_SI_TIMER; + break; + case SI_MESGQ: + tinfo->_reason._mesgq._mqd = info->_reason._mesgq._mqd; + si_type = QEMU_SI_MESGQ; + break; + default: + /* + * We have to go based on the signal number now to figure out + * what's valid. + */ + if (has_trapno(sig)) { + tinfo->_reason._fault._trapno = info->_reason._fault._trapno; + si_type = QEMU_SI_FAULT; + } +#ifdef TARGET_SIGPOLL + /* + * FreeBSD never had SIGPOLL, but emulates it for Linux so there's + * a chance it may popup in the future. + */ + if (sig == TARGET_SIGPOLL) { + tinfo->_reason._poll._band = info->_reason._poll._band; + si_type = QEMU_SI_POLL; + } +#endif + /* + * Unsure that this can actually be generated, and our support for + * capsicum is somewhere between weak and non-existant, but if we get + * one, then we know what to save. + */ + if (sig == TARGET_SIGTRAP) { + tinfo->_reason._capsicum._syscall = + info->_reason._capsicum._syscall; + si_type = QEMU_SI_CAPSICUM; + } + break; + } + tinfo->si_code = deposit32(si_code, 24, 8, si_type); +} + /* * Queue a signal so that it will be send to the virtual CPU as soon as * possible. -- cgit v1.1 From aae57ac37a8803cdd39a732491718b6ee772bb3d Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 17:55:56 -0700 Subject: bsd-user/signal.c: Implement rewind_if_in_safe_syscall Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/qemu.h | 2 ++ bsd-user/signal.c | 13 ++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 49f0193..8ed1bfb 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -446,4 +446,6 @@ static inline void *lock_user_string(abi_ulong guest_addr) #include +#include "user/safe-syscall.h" + #endif /* QEMU_H */ diff --git a/bsd-user/signal.c b/bsd-user/signal.c index db8cf0a..454aef2 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -48,6 +48,18 @@ int target_to_host_signal(int sig) return sig; } +/* Adjust the signal context to rewind out of safe-syscall if we're in it */ +static inline void rewind_if_in_safe_syscall(void *puc) +{ + ucontext_t *uc = (ucontext_t *)puc; + uintptr_t pcreg = host_signal_pc(uc); + + if (pcreg > (uintptr_t)safe_syscall_start + && pcreg < (uintptr_t)safe_syscall_end) { + host_signal_set_pc(uc, (uintptr_t)safe_syscall_start); + } +} + static bool has_trapno(int tsig) { return tsig == TARGET_SIGILL || @@ -57,7 +69,6 @@ static bool has_trapno(int tsig) tsig == TARGET_SIGTRAP; } - /* Siginfo conversion. */ /* -- cgit v1.1 From e625c7ef5c07431a708f9fb0d98cbfeea1ad3ccc Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 17:57:34 -0700 Subject: bsd-user/signal.c: Implement host_signal_handler Implement host_signal_handler to handle signals generated by the host and to do safe system calls. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 454aef2..24cf4b1 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -223,6 +223,111 @@ void force_sig_fault(int sig, int code, abi_ulong addr) static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) { + CPUArchState *env = thread_cpu->env_ptr; + CPUState *cpu = env_cpu(env); + TaskState *ts = cpu->opaque; + target_siginfo_t tinfo; + ucontext_t *uc = puc; + struct emulated_sigtable *k; + int guest_sig; + uintptr_t pc = 0; + bool sync_sig = false; + + /* + * Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special + * handling wrt signal blocking and unwinding. + */ + if ((host_sig == SIGSEGV || host_sig == SIGBUS) && info->si_code > 0) { + MMUAccessType access_type; + uintptr_t host_addr; + abi_ptr guest_addr; + bool is_write; + + host_addr = (uintptr_t)info->si_addr; + + /* + * Convert forcefully to guest address space: addresses outside + * reserved_va are still valid to report via SEGV_MAPERR. + */ + guest_addr = h2g_nocheck(host_addr); + + pc = host_signal_pc(uc); + is_write = host_signal_write(info, uc); + access_type = adjust_signal_pc(&pc, is_write); + + if (host_sig == SIGSEGV) { + bool maperr = true; + + if (info->si_code == SEGV_ACCERR && h2g_valid(host_addr)) { + /* If this was a write to a TB protected page, restart. */ + if (is_write && + handle_sigsegv_accerr_write(cpu, &uc->uc_sigmask, + pc, guest_addr)) { + return; + } + + /* + * With reserved_va, the whole address space is PROT_NONE, + * which means that we may get ACCERR when we want MAPERR. + */ + if (page_get_flags(guest_addr) & PAGE_VALID) { + maperr = false; + } else { + info->si_code = SEGV_MAPERR; + } + } + + sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL); + cpu_loop_exit_sigsegv(cpu, guest_addr, access_type, maperr, pc); + } else { + sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL); + if (info->si_code == BUS_ADRALN) { + cpu_loop_exit_sigbus(cpu, guest_addr, access_type, pc); + } + } + + sync_sig = true; + } + + /* Get the target signal number. */ + guest_sig = host_to_target_signal(host_sig); + if (guest_sig < 1 || guest_sig > TARGET_NSIG) { + return; + } + trace_user_host_signal(cpu, host_sig, guest_sig); + + host_to_target_siginfo_noswap(&tinfo, info); + + k = &ts->sigtab[guest_sig - 1]; + k->info = tinfo; + k->pending = guest_sig; + ts->signal_pending = 1; + + /* + * For synchronous signals, unwind the cpu state to the faulting + * insn and then exit back to the main loop so that the signal + * is delivered immediately. + */ + if (sync_sig) { + cpu->exception_index = EXCP_INTERRUPT; + cpu_loop_exit_restore(cpu, pc); + } + + rewind_if_in_safe_syscall(puc); + + /* + * Block host signals until target signal handler entered. We + * can't block SIGSEGV or SIGBUS while we're executing guest + * code in case the guest code provokes one in the window between + * now and it getting out to the main loop. Signals will be + * unblocked again in process_pending_signals(). + */ + sigfillset(&uc->uc_sigmask); + sigdelset(&uc->uc_sigmask, SIGSEGV); + sigdelset(&uc->uc_sigmask, SIGBUS); + + /* Interrupt the virtual CPU as soon as possible. */ + cpu_exit(thread_cpu); } void signal_init(void) -- cgit v1.1 From fd5bec9ad28eaa454feaad46e68a76b9eeedb3ff Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 20:04:18 -0700 Subject: bsd-user/strace.c: print_taken_signal print_taken_signal() prints signals when we're tracing signals. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/qemu.h | 10 ++++++ bsd-user/strace.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 8ed1bfb..a796477 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -204,6 +204,16 @@ print_openbsd_syscall(int num, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6); void print_openbsd_syscall_ret(int num, abi_long ret); +/** + * print_taken_signal: + * @target_signum: target signal being taken + * @tinfo: target_siginfo_t which will be passed to the guest for the signal + * + * Print strace output indicating that this signal is being taken by the guest, + * in a format similar to: + * --- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} --- + */ +void print_taken_signal(int target_signum, const target_siginfo_t *tinfo); extern int do_strace; /* mmap.c */ diff --git a/bsd-user/strace.c b/bsd-user/strace.c index be40b8a..a77d10d 100644 --- a/bsd-user/strace.c +++ b/bsd-user/strace.c @@ -31,6 +31,24 @@ int do_strace; /* * Utility functions */ +static const char * +get_comma(int last) +{ + return (last) ? "" : ","; +} + +/* + * Prints out raw parameter using given format. Caller needs + * to do byte swapping if needed. + */ +static void +print_raw_param(const char *fmt, abi_long param, int last) +{ + char format[64]; + + (void)snprintf(format, sizeof(format), "%s%s", fmt, get_comma(last)); + gemu_log(format, param); +} static void print_sysctl(const struct syscallname *name, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, @@ -239,3 +257,82 @@ void print_openbsd_syscall_ret(int num, abi_long ret) print_syscall_ret(num, ret, openbsd_scnames, ARRAY_SIZE(openbsd_scnames)); } + +static void +print_signal(abi_ulong arg, int last) +{ + const char *signal_name = NULL; + switch (arg) { + case TARGET_SIGHUP: + signal_name = "SIGHUP"; + break; + case TARGET_SIGINT: + signal_name = "SIGINT"; + break; + case TARGET_SIGQUIT: + signal_name = "SIGQUIT"; + break; + case TARGET_SIGILL: + signal_name = "SIGILL"; + break; + case TARGET_SIGABRT: + signal_name = "SIGABRT"; + break; + case TARGET_SIGFPE: + signal_name = "SIGFPE"; + break; + case TARGET_SIGKILL: + signal_name = "SIGKILL"; + break; + case TARGET_SIGSEGV: + signal_name = "SIGSEGV"; + break; + case TARGET_SIGPIPE: + signal_name = "SIGPIPE"; + break; + case TARGET_SIGALRM: + signal_name = "SIGALRM"; + break; + case TARGET_SIGTERM: + signal_name = "SIGTERM"; + break; + case TARGET_SIGUSR1: + signal_name = "SIGUSR1"; + break; + case TARGET_SIGUSR2: + signal_name = "SIGUSR2"; + break; + case TARGET_SIGCHLD: + signal_name = "SIGCHLD"; + break; + case TARGET_SIGCONT: + signal_name = "SIGCONT"; + break; + case TARGET_SIGSTOP: + signal_name = "SIGSTOP"; + break; + case TARGET_SIGTTIN: + signal_name = "SIGTTIN"; + break; + case TARGET_SIGTTOU: + signal_name = "SIGTTOU"; + break; + } + if (signal_name == NULL) { + print_raw_param("%ld", arg, last); + return; + } + gemu_log("%s%s", signal_name, get_comma(last)); +} + +void print_taken_signal(int target_signum, const target_siginfo_t *tinfo) +{ + /* + * Print the strace output for a signal being taken: + * --- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} --- + */ + gemu_log("%d ", getpid()); + gemu_log("--- "); + print_signal(target_signum, 1); + gemu_log(" ---\n"); +} -- cgit v1.1 From 377145478339917491a850643bb920548907d956 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 21:05:20 -0700 Subject: bsd-user/signal.c: Implement dump_core_and_abort Force delivering a signal and generating a core file. It's a global function for the moment... Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ bsd-user/syscall_defs.h | 1 + 2 files changed, 77 insertions(+) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 24cf4b1..ccda7ad 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -172,6 +172,82 @@ static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, tinfo->si_code = deposit32(si_code, 24, 8, si_type); } +/* Returns 1 if given signal should dump core if not handled. */ +static int core_dump_signal(int sig) +{ + switch (sig) { + case TARGET_SIGABRT: + case TARGET_SIGFPE: + case TARGET_SIGILL: + case TARGET_SIGQUIT: + case TARGET_SIGSEGV: + case TARGET_SIGTRAP: + case TARGET_SIGBUS: + return 1; + default: + return 0; + } +} + +/* Abort execution with signal. */ +static void QEMU_NORETURN dump_core_and_abort(int target_sig) +{ + CPUArchState *env = thread_cpu->env_ptr; + CPUState *cpu = env_cpu(env); + TaskState *ts = cpu->opaque; + int core_dumped = 0; + int host_sig; + struct sigaction act; + + host_sig = target_to_host_signal(target_sig); + gdb_signalled(env, target_sig); + + /* Dump core if supported by target binary format */ + if (core_dump_signal(target_sig) && (ts->bprm->core_dump != NULL)) { + stop_all_tasks(); + core_dumped = + ((*ts->bprm->core_dump)(target_sig, env) == 0); + } + if (core_dumped) { + struct rlimit nodump; + + /* + * We already dumped the core of target process, we don't want + * a coredump of qemu itself. + */ + getrlimit(RLIMIT_CORE, &nodump); + nodump.rlim_cur = 0; + setrlimit(RLIMIT_CORE, &nodump); + (void) fprintf(stderr, "qemu: uncaught target signal %d (%s) " + "- %s\n", target_sig, strsignal(host_sig), "core dumped"); + } + + /* + * The proper exit code for dying from an uncaught signal is + * -. The kernel doesn't allow exit() or _exit() to pass + * a negative value. To get the proper exit code we need to + * actually die from an uncaught signal. Here the default signal + * handler is installed, we send ourself a signal and we wait for + * it to arrive. + */ + memset(&act, 0, sizeof(act)); + sigfillset(&act.sa_mask); + act.sa_handler = SIG_DFL; + sigaction(host_sig, &act, NULL); + + kill(getpid(), host_sig); + + /* + * Make sure the signal isn't masked (just reuse the mask inside + * of act). + */ + sigdelset(&act.sa_mask, host_sig); + sigsuspend(&act.sa_mask); + + /* unreachable */ + abort(); +} + /* * Queue a signal so that it will be send to the virtual CPU as soon as * possible. diff --git a/bsd-user/syscall_defs.h b/bsd-user/syscall_defs.h index 04a1a88..62b472b 100644 --- a/bsd-user/syscall_defs.h +++ b/bsd-user/syscall_defs.h @@ -21,6 +21,7 @@ #define _SYSCALL_DEFS_H_ #include +#include #include "errno_defs.h" -- cgit v1.1 From 38be620c950dcf629ba3217c6a183fee0e790fa8 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 21:15:11 -0700 Subject: bsd-user/signal.c: Fill in queue_signal Fill in queue signal implementation, as well as routines allocate and delete elements of the signal queue. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/qemu.h | 5 +++++ bsd-user/signal.c | 13 ++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index a796477..1648a50 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -85,6 +85,11 @@ typedef struct TaskState { struct bsd_binprm *bprm; struct image_info *info; + struct emulated_sigtable sync_signal; + /* + * TODO: Since we block all signals while returning to the main CPU + * loop, this needn't be an array + */ struct emulated_sigtable sigtab[TARGET_NSIG]; /* * Nonzero if process_pending_signals() needs to do something (either diff --git a/bsd-user/signal.c b/bsd-user/signal.c index ccda7ad..34663f7 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -255,7 +255,18 @@ static void QEMU_NORETURN dump_core_and_abort(int target_sig) void queue_signal(CPUArchState *env, int sig, int si_type, target_siginfo_t *info) { - qemu_log_mask(LOG_UNIMP, "No signal queueing, dropping signal %d\n", sig); + CPUState *cpu = env_cpu(env); + TaskState *ts = cpu->opaque; + + trace_user_queue_signal(env, sig); + + info->si_code = deposit32(info->si_code, 24, 8, si_type); + + ts->sync_signal.info = *info; + ts->sync_signal.pending = sig; + /* Signal that a new signal is pending. */ + qatomic_set(&ts->signal_pending, 1); + return; } static int fatal_signal(int sig) -- cgit v1.1 From c93cbac1f4aab40c3fd4ac7488c7e3365ec5c894 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 21:24:18 -0700 Subject: bsd-user/signal.c: sigset manipulation routines. target_sigemptyset: resets a set to having no bits set target_sigaddset: adds a signal to a set target_sigismember: returns true when signal is a member host_to_target_sigset_internal: convert host sigset to target host_to_target_sigset: convert host sigset to target target_to_host_sigset_internal: convert target sigset to host target_to_host_sigset: convert target sigset to host Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal-common.h | 2 ++ bsd-user/signal.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/bsd-user/signal-common.h b/bsd-user/signal-common.h index 80e9503..ee81926 100644 --- a/bsd-user/signal-common.h +++ b/bsd-user/signal-common.h @@ -14,11 +14,13 @@ abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp); long do_sigreturn(CPUArchState *env); void force_sig_fault(int sig, int code, abi_ulong addr); int host_to_target_signal(int sig); +void host_to_target_sigset(target_sigset_t *d, const sigset_t *s); void process_pending_signals(CPUArchState *env); void queue_signal(CPUArchState *env, int sig, int si_type, target_siginfo_t *info); void signal_init(void); int target_to_host_signal(int sig); +void target_to_host_sigset(sigset_t *d, const target_sigset_t *s); /* * Within QEMU the top 8 bits of si_code indicate which of the parts of the diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 34663f7..84dafa4 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -32,6 +32,9 @@ static struct target_sigaction sigact_table[TARGET_NSIG]; static void host_signal_handler(int host_sig, siginfo_t *info, void *puc); +static void target_to_host_sigset_internal(sigset_t *d, + const target_sigset_t *s); + /* * The BSD ABIs use the same singal numbers across all the CPU architectures, so @@ -48,6 +51,25 @@ int target_to_host_signal(int sig) return sig; } +static inline void target_sigemptyset(target_sigset_t *set) +{ + memset(set, 0, sizeof(*set)); +} + +static inline void target_sigaddset(target_sigset_t *set, int signum) +{ + signum--; + uint32_t mask = (uint32_t)1 << (signum % TARGET_NSIG_BPW); + set->__bits[signum / TARGET_NSIG_BPW] |= mask; +} + +static inline int target_sigismember(const target_sigset_t *set, int signum) +{ + signum--; + abi_ulong mask = (abi_ulong)1 << (signum % TARGET_NSIG_BPW); + return (set->__bits[signum / TARGET_NSIG_BPW] & mask) != 0; +} + /* Adjust the signal context to rewind out of safe-syscall if we're in it */ static inline void rewind_if_in_safe_syscall(void *puc) { @@ -60,6 +82,58 @@ static inline void rewind_if_in_safe_syscall(void *puc) } } +/* + * Note: The following take advantage of the BSD signal property that all + * signals are available on all architectures. + */ +static void host_to_target_sigset_internal(target_sigset_t *d, + const sigset_t *s) +{ + int i; + + target_sigemptyset(d); + for (i = 1; i <= NSIG; i++) { + if (sigismember(s, i)) { + target_sigaddset(d, host_to_target_signal(i)); + } + } +} + +void host_to_target_sigset(target_sigset_t *d, const sigset_t *s) +{ + target_sigset_t d1; + int i; + + host_to_target_sigset_internal(&d1, s); + for (i = 0; i < _SIG_WORDS; i++) { + d->__bits[i] = tswap32(d1.__bits[i]); + } +} + +static void target_to_host_sigset_internal(sigset_t *d, + const target_sigset_t *s) +{ + int i; + + sigemptyset(d); + for (i = 1; i <= TARGET_NSIG; i++) { + if (target_sigismember(s, i)) { + sigaddset(d, target_to_host_signal(i)); + } + } +} + +void target_to_host_sigset(sigset_t *d, const target_sigset_t *s) +{ + target_sigset_t s1; + int i; + + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + s1.__bits[i] = tswap32(s->__bits[i]); + } + target_to_host_sigset_internal(d, &s1); +} + static bool has_trapno(int tsig) { return tsig == TARGET_SIGILL || -- cgit v1.1 From 46f4f76d332d8c2b4eb24c8e6f91ac8bdc205733 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 21:40:28 -0700 Subject: bsd-user/signal.c: setup_frame setup_frame sets up a signalled stack frame. Associated routines to extract the pointer to the stack frame and to support alternate stacks. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/main.c | 5 ++++ bsd-user/qemu.h | 3 +- bsd-user/signal.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 1 deletion(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index 29cf4e1..f1d58e9 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -217,6 +217,11 @@ void qemu_cpu_kick(CPUState *cpu) /* Assumes contents are already zeroed. */ static void init_task_state(TaskState *ts) { + ts->sigaltstack_used = (struct target_sigaltstack) { + .ss_sp = 0, + .ss_size = 0, + .ss_flags = TARGET_SS_DISABLE, + }; } void gemu_log(const char *fmt, ...) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 1648a50..de20650 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -107,7 +107,8 @@ typedef struct TaskState { */ sigset_t signal_mask; - uint8_t stack[]; + /* This thread's sigaltstack, if it has one */ + struct target_sigaltstack sigaltstack_used; } __attribute__((aligned(16))) TaskState; void stop_all_tasks(void); diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 84dafa4..dbc1373 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -35,6 +35,16 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc); static void target_to_host_sigset_internal(sigset_t *d, const target_sigset_t *s); +static inline int on_sig_stack(TaskState *ts, unsigned long sp) +{ + return sp - ts->sigaltstack_used.ss_sp < ts->sigaltstack_used.ss_size; +} + +static inline int sas_ss_flags(TaskState *ts, unsigned long sp) +{ + return ts->sigaltstack_used.ss_size == 0 ? SS_DISABLE : + on_sig_stack(ts, sp) ? SS_ONSTACK : 0; +} /* * The BSD ABIs use the same singal numbers across all the CPU architectures, so @@ -491,6 +501,79 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) cpu_exit(thread_cpu); } +static inline abi_ulong get_sigframe(struct target_sigaction *ka, + CPUArchState *env, size_t frame_size) +{ + TaskState *ts = (TaskState *)thread_cpu->opaque; + abi_ulong sp; + + /* Use default user stack */ + sp = get_sp_from_cpustate(env); + + if ((ka->sa_flags & TARGET_SA_ONSTACK) && sas_ss_flags(ts, sp) == 0) { + sp = ts->sigaltstack_used.ss_sp + ts->sigaltstack_used.ss_size; + } + +/* TODO: make this a target_arch function / define */ +#if defined(TARGET_ARM) + return (sp - frame_size) & ~7; +#elif defined(TARGET_AARCH64) + return (sp - frame_size) & ~15; +#else + return sp - frame_size; +#endif +} + +/* compare to $M/$M/exec_machdep.c sendsig and sys/kern/kern_sig.c sigexit */ + +static void setup_frame(int sig, int code, struct target_sigaction *ka, + target_sigset_t *set, target_siginfo_t *tinfo, CPUArchState *env) +{ + struct target_sigframe *frame; + abi_ulong frame_addr; + int i; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + unlock_user_struct(frame, frame_addr, 1); + dump_core_and_abort(TARGET_SIGILL); + return; + } + + memset(frame, 0, sizeof(*frame)); + setup_sigframe_arch(env, frame_addr, frame, 0); + + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->__bits[i], &frame->sf_uc.uc_sigmask.__bits[i]); + } + + if (tinfo) { + frame->sf_si.si_signo = tinfo->si_signo; + frame->sf_si.si_errno = tinfo->si_errno; + frame->sf_si.si_code = tinfo->si_code; + frame->sf_si.si_pid = tinfo->si_pid; + frame->sf_si.si_uid = tinfo->si_uid; + frame->sf_si.si_status = tinfo->si_status; + frame->sf_si.si_addr = tinfo->si_addr; + /* see host_to_target_siginfo_noswap() for more details */ + frame->sf_si.si_value.sival_ptr = tinfo->si_value.sival_ptr; + /* + * At this point, whatever is in the _reason union is complete + * and in target order, so just copy the whole thing over, even + * if it's too large for this specific signal. + * host_to_target_siginfo_noswap() and tswap_siginfo() have ensured + * that's so. + */ + memcpy(&frame->sf_si._reason, &tinfo->_reason, + sizeof(tinfo->_reason)); + } + + set_sigtramp_args(env, sig, frame, frame_addr, ka); + + unlock_user_struct(frame, frame_addr, 1); +} + void signal_init(void) { TaskState *ts = (TaskState *)thread_cpu->opaque; -- cgit v1.1 From 6c6d4b5616b391934851f049f41a7cbde12140d9 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 21:46:07 -0700 Subject: bsd-user/signal.c: handle_pending_signal Handle a queued signal. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/qemu.h | 7 +++++ bsd-user/signal.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index de20650..02921ac 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -99,6 +99,8 @@ typedef struct TaskState { * from multiple threads.) */ int signal_pending; + /* True if we're leaving a sigsuspend and sigsuspend_mask is valid. */ + bool in_sigsuspend; /* * This thread's signal mask, as requested by the guest program. * The actual signal mask of this thread may differ: @@ -106,6 +108,11 @@ typedef struct TaskState { * + sometimes we block all signals to avoid races */ sigset_t signal_mask; + /* + * The signal mask imposed by a guest sigsuspend syscall, if we are + * currently in the middle of such a syscall + */ + sigset_t sigsuspend_mask; /* This thread's sigaltstack, if it has one */ struct target_sigaltstack sigaltstack_used; diff --git a/bsd-user/signal.c b/bsd-user/signal.c index dbc1373..366e047 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -616,6 +616,93 @@ void signal_init(void) } } +static void handle_pending_signal(CPUArchState *env, int sig, + struct emulated_sigtable *k) +{ + CPUState *cpu = env_cpu(env); + TaskState *ts = cpu->opaque; + struct target_sigaction *sa; + int code; + sigset_t set; + abi_ulong handler; + target_siginfo_t tinfo; + target_sigset_t target_old_set; + + trace_user_handle_signal(env, sig); + + k->pending = 0; + + sig = gdb_handlesig(cpu, sig); + if (!sig) { + sa = NULL; + handler = TARGET_SIG_IGN; + } else { + sa = &sigact_table[sig - 1]; + handler = sa->_sa_handler; + } + + if (do_strace) { + print_taken_signal(sig, &k->info); + } + + if (handler == TARGET_SIG_DFL) { + /* + * default handler : ignore some signal. The other are job + * control or fatal. + */ + if (sig == TARGET_SIGTSTP || sig == TARGET_SIGTTIN || + sig == TARGET_SIGTTOU) { + kill(getpid(), SIGSTOP); + } else if (sig != TARGET_SIGCHLD && sig != TARGET_SIGURG && + sig != TARGET_SIGINFO && sig != TARGET_SIGWINCH && + sig != TARGET_SIGCONT) { + dump_core_and_abort(sig); + } + } else if (handler == TARGET_SIG_IGN) { + /* ignore sig */ + } else if (handler == TARGET_SIG_ERR) { + dump_core_and_abort(sig); + } else { + /* compute the blocked signals during the handler execution */ + sigset_t *blocked_set; + + target_to_host_sigset(&set, &sa->sa_mask); + /* + * SA_NODEFER indicates that the current signal should not be + * blocked during the handler. + */ + if (!(sa->sa_flags & TARGET_SA_NODEFER)) { + sigaddset(&set, target_to_host_signal(sig)); + } + + /* + * Save the previous blocked signal state to restore it at the + * end of the signal execution (see do_sigreturn). + */ + host_to_target_sigset_internal(&target_old_set, &ts->signal_mask); + + blocked_set = ts->in_sigsuspend ? + &ts->sigsuspend_mask : &ts->signal_mask; + sigorset(&ts->signal_mask, blocked_set, &set); + ts->in_sigsuspend = false; + sigprocmask(SIG_SETMASK, &ts->signal_mask, NULL); + + /* XXX VM86 on x86 ??? */ + + code = k->info.si_code; /* From host, so no si_type */ + /* prepare the stack frame of the virtual CPU */ + if (sa->sa_flags & TARGET_SA_SIGINFO) { + tswap_siginfo(&tinfo, &k->info); + setup_frame(sig, code, sa, &target_old_set, &tinfo, env); + } else { + setup_frame(sig, code, sa, &target_old_set, NULL, env); + } + if (sa->sa_flags & TARGET_SA_RESETHAND) { + sa->_sa_handler = TARGET_SIG_DFL; + } + } +} + void process_pending_signals(CPUArchState *cpu_env) { } -- cgit v1.1 From 08eb66d5d837c2db9d5d57553da8448fd8e36571 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 21:48:25 -0700 Subject: bsd-user/signal.c: tswap_siginfo Convert siginfo from targer to host. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 366e047..34e8c81 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -256,6 +256,59 @@ static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, tinfo->si_code = deposit32(si_code, 24, 8, si_type); } +static void tswap_siginfo(target_siginfo_t *tinfo, const target_siginfo_t *info) +{ + int si_type = extract32(info->si_code, 24, 8); + int si_code = sextract32(info->si_code, 0, 24); + + __put_user(info->si_signo, &tinfo->si_signo); + __put_user(info->si_errno, &tinfo->si_errno); + __put_user(si_code, &tinfo->si_code); /* Zero out si_type, it's internal */ + __put_user(info->si_pid, &tinfo->si_pid); + __put_user(info->si_uid, &tinfo->si_uid); + __put_user(info->si_status, &tinfo->si_status); + __put_user(info->si_addr, &tinfo->si_addr); + /* + * Unswapped, because we passed it through mostly untouched. si_value is + * opaque to the kernel, so we didn't bother with potentially wasting cycles + * to swap it into host byte order. + */ + tinfo->si_value.sival_ptr = info->si_value.sival_ptr; + + /* + * We can use our internal marker of which fields in the structure + * are valid, rather than duplicating the guesswork of + * host_to_target_siginfo_noswap() here. + */ + switch (si_type) { + case QEMU_SI_NOINFO: /* No additional info */ + break; + case QEMU_SI_FAULT: + __put_user(info->_reason._fault._trapno, + &tinfo->_reason._fault._trapno); + break; + case QEMU_SI_TIMER: + __put_user(info->_reason._timer._timerid, + &tinfo->_reason._timer._timerid); + __put_user(info->_reason._timer._overrun, + &tinfo->_reason._timer._overrun); + break; + case QEMU_SI_MESGQ: + __put_user(info->_reason._mesgq._mqd, &tinfo->_reason._mesgq._mqd); + break; + case QEMU_SI_POLL: + /* Note: Not generated on FreeBSD */ + __put_user(info->_reason._poll._band, &tinfo->_reason._poll._band); + break; + case QEMU_SI_CAPSICUM: + __put_user(info->_reason._capsicum._syscall, + &tinfo->_reason._capsicum._syscall); + break; + default: + g_assert_not_reached(); + } +} + /* Returns 1 if given signal should dump core if not handled. */ static int core_dump_signal(int sig) { -- cgit v1.1 From d7acd31780bea1c192854a8617255ad992b19c4d Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 21:52:26 -0700 Subject: bsd-user/signal.c: process_pending_signals Process the currently queued signals. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 34e8c81..4b39874 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -756,8 +756,62 @@ static void handle_pending_signal(CPUArchState *env, int sig, } } -void process_pending_signals(CPUArchState *cpu_env) +void process_pending_signals(CPUArchState *env) { + CPUState *cpu = env_cpu(env); + int sig; + sigset_t *blocked_set, set; + struct emulated_sigtable *k; + TaskState *ts = cpu->opaque; + + while (qatomic_read(&ts->signal_pending)) { + sigfillset(&set); + sigprocmask(SIG_SETMASK, &set, 0); + + restart_scan: + sig = ts->sync_signal.pending; + if (sig) { + /* + * Synchronous signals are forced by the emulated CPU in some way. + * If they are set to ignore, restore the default handler (see + * sys/kern_sig.c trapsignal() and execsigs() for this behavior) + * though maybe this is done only when forcing exit for non SIGCHLD. + */ + if (sigismember(&ts->signal_mask, target_to_host_signal(sig)) || + sigact_table[sig - 1]._sa_handler == TARGET_SIG_IGN) { + sigdelset(&ts->signal_mask, target_to_host_signal(sig)); + sigact_table[sig - 1]._sa_handler = TARGET_SIG_DFL; + } + handle_pending_signal(env, sig, &ts->sync_signal); + } + + k = ts->sigtab; + for (sig = 1; sig <= TARGET_NSIG; sig++, k++) { + blocked_set = ts->in_sigsuspend ? + &ts->sigsuspend_mask : &ts->signal_mask; + if (k->pending && + !sigismember(blocked_set, target_to_host_signal(sig))) { + handle_pending_signal(env, sig, k); + /* + * Restart scan from the beginning, as handle_pending_signal + * might have resulted in a new synchronous signal (eg SIGSEGV). + */ + goto restart_scan; + } + } + + /* + * Unblock signals and check one more time. Unblocking signals may cause + * us to take another host signal, which will set signal_pending again. + */ + qatomic_set(&ts->signal_pending, 0); + ts->in_sigsuspend = false; + set = ts->signal_mask; + sigdelset(&set, SIGSEGV); + sigdelset(&set, SIGBUS); + sigprocmask(SIG_SETMASK, &set, 0); + } + ts->in_sigsuspend = false; } void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, -- cgit v1.1 From c885ae0e4ebf207c861bf651dcf9282677281c06 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 23:48:12 -0700 Subject: bsd-user/signal.c: implement do_sigreturn Implements the meat of a sigreturn(2) system call via do_sigreturn, and helper reset_signal_mask. Fix the prototype of do_sigreturn in qemu.h and remove do_rt_sigreturn since it's linux only. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal-common.h | 2 +- bsd-user/signal.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/bsd-user/signal-common.h b/bsd-user/signal-common.h index ee81926..786ec59 100644 --- a/bsd-user/signal-common.h +++ b/bsd-user/signal-common.h @@ -11,7 +11,7 @@ long do_rt_sigreturn(CPUArchState *env); abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp); -long do_sigreturn(CPUArchState *env); +long do_sigreturn(CPUArchState *env, abi_ulong addr); void force_sig_fault(int sig, int code, abi_ulong addr); int host_to_target_signal(int sig); void host_to_target_sigset(target_sigset_t *d, const sigset_t *s); diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 4b39874..150262a 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -627,6 +627,62 @@ static void setup_frame(int sig, int code, struct target_sigaction *ka, unlock_user_struct(frame, frame_addr, 1); } +static int reset_signal_mask(target_ucontext_t *ucontext) +{ + int i; + sigset_t blocked; + target_sigset_t target_set; + TaskState *ts = (TaskState *)thread_cpu->opaque; + + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + if (__get_user(target_set.__bits[i], + &ucontext->uc_sigmask.__bits[i])) { + return -TARGET_EFAULT; + } + } + target_to_host_sigset_internal(&blocked, &target_set); + ts->signal_mask = blocked; + + return 0; +} + +/* See sys/$M/$M/exec_machdep.c sigreturn() */ +long do_sigreturn(CPUArchState *env, abi_ulong addr) +{ + long ret; + abi_ulong target_ucontext; + target_ucontext_t *ucontext = NULL; + + /* Get the target ucontext address from the stack frame */ + ret = get_ucontext_sigreturn(env, addr, &target_ucontext); + if (is_error(ret)) { + return ret; + } + trace_user_do_sigreturn(env, addr); + if (!lock_user_struct(VERIFY_READ, ucontext, target_ucontext, 0)) { + goto badframe; + } + + /* Set the register state back to before the signal. */ + if (set_mcontext(env, &ucontext->uc_mcontext, 1)) { + goto badframe; + } + + /* And reset the signal mask. */ + if (reset_signal_mask(ucontext)) { + goto badframe; + } + + unlock_user_struct(ucontext, target_ucontext, 0); + return -TARGET_EJUSTRETURN; + +badframe: + if (ucontext != NULL) { + unlock_user_struct(ucontext, target_ucontext, 0); + } + return -TARGET_EFAULT; +} + void signal_init(void) { TaskState *ts = (TaskState *)thread_cpu->opaque; -- cgit v1.1 From 394cf694273caf8ab8838588954d0fc2909ae2fa Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 23:59:42 -0700 Subject: bsd-user/signal.c: implement do_sigaction Implement the meat of the sigaction(2) system call with do_sigaction and helper routiner block_signals (which is also used to implemement signal masking so it's global). Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal-common.h | 22 +++++++++++++ bsd-user/signal.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/bsd-user/signal-common.h b/bsd-user/signal-common.h index 786ec59..7ff8e8f 100644 --- a/bsd-user/signal-common.h +++ b/bsd-user/signal-common.h @@ -9,7 +9,29 @@ #ifndef SIGNAL_COMMON_H #define SIGNAL_COMMON_H +/** + * block_signals: block all signals while handling this guest syscall + * + * Block all signals, and arrange that the signal mask is returned to + * its correct value for the guest before we resume execution of guest code. + * If this function returns non-zero, then the caller should immediately + * return -TARGET_ERESTARTSYS to the main loop, which will take the pending + * signal and restart execution of the syscall. + * If block_signals() returns zero, then the caller can continue with + * emulation of the system call knowing that no signals can be taken + * (and therefore that no race conditions will result). + * This should only be called once, because if it is called a second time + * it will always return non-zero. (Think of it like a mutex that can't + * be recursively locked.) + * Signals will be unblocked again by process_pending_signals(). + * + * Return value: non-zero if there was a pending signal, zero if not. + */ +int block_signals(void); /* Returns non zero if signal pending */ + long do_rt_sigreturn(CPUArchState *env); +int do_sigaction(int sig, const struct target_sigaction *act, + struct target_sigaction *oact); abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp); long do_sigreturn(CPUArchState *env, abi_ulong addr); void force_sig_fault(int sig, int code, abi_ulong addr); diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 150262a..5c94bd0 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -309,6 +309,25 @@ static void tswap_siginfo(target_siginfo_t *tinfo, const target_siginfo_t *info) } } +int block_signals(void) +{ + TaskState *ts = (TaskState *)thread_cpu->opaque; + sigset_t set; + + /* + * It's OK to block everything including SIGSEGV, because we won't run any + * further guest code before unblocking signals in + * process_pending_signals(). We depend on the FreeBSD behaivor here where + * this will only affect this thread's signal mask. We don't use + * pthread_sigmask which might seem more correct because that routine also + * does odd things with SIGCANCEL to implement pthread_cancel(). + */ + sigfillset(&set); + sigprocmask(SIG_SETMASK, &set, 0); + + return qatomic_xchg(&ts->signal_pending, 1); +} + /* Returns 1 if given signal should dump core if not handled. */ static int core_dump_signal(int sig) { @@ -554,6 +573,69 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) cpu_exit(thread_cpu); } +/* do_sigaction() return host values and errnos */ +int do_sigaction(int sig, const struct target_sigaction *act, + struct target_sigaction *oact) +{ + struct target_sigaction *k; + struct sigaction act1; + int host_sig; + int ret = 0; + + if (sig < 1 || sig > TARGET_NSIG) { + return -TARGET_EINVAL; + } + + if ((sig == TARGET_SIGKILL || sig == TARGET_SIGSTOP) && + act != NULL && act->_sa_handler != TARGET_SIG_DFL) { + return -TARGET_EINVAL; + } + + if (block_signals()) { + return -TARGET_ERESTART; + } + + k = &sigact_table[sig - 1]; + if (oact) { + oact->_sa_handler = tswapal(k->_sa_handler); + oact->sa_flags = tswap32(k->sa_flags); + oact->sa_mask = k->sa_mask; + } + if (act) { + k->_sa_handler = tswapal(act->_sa_handler); + k->sa_flags = tswap32(act->sa_flags); + k->sa_mask = act->sa_mask; + + /* Update the host signal state. */ + host_sig = target_to_host_signal(sig); + if (host_sig != SIGSEGV && host_sig != SIGBUS) { + memset(&act1, 0, sizeof(struct sigaction)); + sigfillset(&act1.sa_mask); + act1.sa_flags = SA_SIGINFO; + if (k->sa_flags & TARGET_SA_RESTART) { + act1.sa_flags |= SA_RESTART; + } + /* + * Note: It is important to update the host kernel signal mask to + * avoid getting unexpected interrupted system calls. + */ + if (k->_sa_handler == TARGET_SIG_IGN) { + act1.sa_sigaction = (void *)SIG_IGN; + } else if (k->_sa_handler == TARGET_SIG_DFL) { + if (fatal_signal(sig)) { + act1.sa_sigaction = host_signal_handler; + } else { + act1.sa_sigaction = (void *)SIG_DFL; + } + } else { + act1.sa_sigaction = host_signal_handler; + } + ret = sigaction(host_sig, &act1, NULL); + } + } + return ret; +} + static inline abi_ulong get_sigframe(struct target_sigaction *ka, CPUArchState *env, size_t frame_size) { -- cgit v1.1 From 43ed4267845899871890589c96b1302a1696525d Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sun, 9 Jan 2022 00:06:56 -0700 Subject: bsd-user/signal.c: do_sigaltstack Implement the meat of the sigaltstack(2) system call with do_sigaltstack. With that, all the stubbed out routines are complete, so remove now-incorrect comment. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 5c94bd0..ad22ba9 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -25,11 +25,6 @@ #include "hw/core/tcg-cpu-ops.h" #include "host-signal.h" -/* - * Stubbed out routines until we merge signal support from bsd-user - * fork. - */ - static struct target_sigaction sigact_table[TARGET_NSIG]; static void host_signal_handler(int host_sig, siginfo_t *info, void *puc); static void target_to_host_sigset_internal(sigset_t *d, @@ -573,6 +568,73 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) cpu_exit(thread_cpu); } +/* do_sigaltstack() returns target values and errnos. */ +/* compare to kern/kern_sig.c sys_sigaltstack() and kern_sigaltstack() */ +abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp) +{ + TaskState *ts = (TaskState *)thread_cpu->opaque; + int ret; + target_stack_t oss; + + if (uoss_addr) { + /* Save current signal stack params */ + oss.ss_sp = tswapl(ts->sigaltstack_used.ss_sp); + oss.ss_size = tswapl(ts->sigaltstack_used.ss_size); + oss.ss_flags = tswapl(sas_ss_flags(ts, sp)); + } + + if (uss_addr) { + target_stack_t *uss; + target_stack_t ss; + size_t minstacksize = TARGET_MINSIGSTKSZ; + + ret = -TARGET_EFAULT; + if (!lock_user_struct(VERIFY_READ, uss, uss_addr, 1)) { + goto out; + } + __get_user(ss.ss_sp, &uss->ss_sp); + __get_user(ss.ss_size, &uss->ss_size); + __get_user(ss.ss_flags, &uss->ss_flags); + unlock_user_struct(uss, uss_addr, 0); + + ret = -TARGET_EPERM; + if (on_sig_stack(ts, sp)) { + goto out; + } + + ret = -TARGET_EINVAL; + if (ss.ss_flags != TARGET_SS_DISABLE + && ss.ss_flags != TARGET_SS_ONSTACK + && ss.ss_flags != 0) { + goto out; + } + + if (ss.ss_flags == TARGET_SS_DISABLE) { + ss.ss_size = 0; + ss.ss_sp = 0; + } else { + ret = -TARGET_ENOMEM; + if (ss.ss_size < minstacksize) { + goto out; + } + } + + ts->sigaltstack_used.ss_sp = ss.ss_sp; + ts->sigaltstack_used.ss_size = ss.ss_size; + } + + if (uoss_addr) { + ret = -TARGET_EFAULT; + if (copy_to_user(uoss_addr, &oss, sizeof(oss))) { + goto out; + } + } + + ret = 0; +out: + return ret; +} + /* do_sigaction() return host values and errnos */ int do_sigaction(int sig, const struct target_sigaction *act, struct target_sigaction *oact) -- cgit v1.1 From adbae40fa1c378d68b2d329620d16bbb8e222eb7 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sun, 9 Jan 2022 11:50:24 -0700 Subject: MAINTAINERS: Add tests/vm/*bsd to the list to get reviews on tests/vm/*bsd (especailly tests/vm/freebsd) are adjacent to the bsd-user stuff and we're keen on keeping them working as well. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index e4b3a4b..b7487f9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3181,6 +3181,7 @@ R: Kyle Evans S: Maintained F: bsd-user/ F: configs/targets/*-bsd-user.mak +F: tests/vm/*bsd T: git https://github.com/qemu-bsd-user/qemu-bsd-user bsd-user-rebase-3.1 Linux user -- cgit v1.1 From bab6ccc53d4dcfc7a713efbcce9ec60ada9e29b9 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Mon, 24 Jan 2022 15:14:04 -0700 Subject: bsd-user: Rename arg name for target_cpu_reset to env Rename the parameter name for target_cpu_reset's CPUArchState * arg from cpu to env. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/arm/target_arch_cpu.h | 2 +- bsd-user/i386/target_arch_cpu.h | 4 ++-- bsd-user/x86_64/target_arch_cpu.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bsd-user/arm/target_arch_cpu.h b/bsd-user/arm/target_arch_cpu.h index 2b395d5..b087db4 100644 --- a/bsd-user/arm/target_arch_cpu.h +++ b/bsd-user/arm/target_arch_cpu.h @@ -213,7 +213,7 @@ static inline void target_cpu_clone_regs(CPUARMState *env, target_ulong newsp) env->regs[0] = 0; } -static inline void target_cpu_reset(CPUArchState *cpu) +static inline void target_cpu_reset(CPUArchState *env) { } diff --git a/bsd-user/i386/target_arch_cpu.h b/bsd-user/i386/target_arch_cpu.h index 472a966..3cbf69d 100644 --- a/bsd-user/i386/target_arch_cpu.h +++ b/bsd-user/i386/target_arch_cpu.h @@ -200,9 +200,9 @@ static inline void target_cpu_clone_regs(CPUX86State *env, target_ulong newsp) env->regs[R_EAX] = 0; } -static inline void target_cpu_reset(CPUArchState *cpu) +static inline void target_cpu_reset(CPUArchState *env) { - cpu_reset(env_cpu(cpu)); + cpu_reset(env_cpu(env)); } #endif /* ! _TARGET_ARCH_CPU_H_ */ diff --git a/bsd-user/x86_64/target_arch_cpu.h b/bsd-user/x86_64/target_arch_cpu.h index 14def48..0a9c0f0 100644 --- a/bsd-user/x86_64/target_arch_cpu.h +++ b/bsd-user/x86_64/target_arch_cpu.h @@ -238,9 +238,9 @@ static inline void target_cpu_clone_regs(CPUX86State *env, target_ulong newsp) env->regs[R_EAX] = 0; } -static inline void target_cpu_reset(CPUArchState *cpu) +static inline void target_cpu_reset(CPUArchState *env) { - cpu_reset(env_cpu(cpu)); + cpu_reset(env_cpu(env)); } #endif /* ! _TARGET_ARCH_CPU_H_ */ -- cgit v1.1 From 1103d59caaa82c94b4223a5429c31895d2f05217 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Mon, 24 Jan 2022 15:23:13 -0700 Subject: bsd-user/freebsd/target_os_ucontext.h: Prefer env as arg name for CPUArchState args Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/freebsd/target_os_ucontext.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bsd-user/freebsd/target_os_ucontext.h b/bsd-user/freebsd/target_os_ucontext.h index 41b28b2..b196b1c 100644 --- a/bsd-user/freebsd/target_os_ucontext.h +++ b/bsd-user/freebsd/target_os_ucontext.h @@ -36,9 +36,9 @@ abi_long set_sigtramp_args(CPUArchState *env, int sig, struct target_sigframe *frame, abi_ulong frame_addr, struct target_sigaction *ka); -abi_long get_mcontext(CPUArchState *regs, target_mcontext_t *mcp, int flags); -abi_long set_mcontext(CPUArchState *regs, target_mcontext_t *mcp, int srflag); -abi_long get_ucontext_sigreturn(CPUArchState *regs, abi_ulong target_sf, +abi_long get_mcontext(CPUArchState *env, target_mcontext_t *mcp, int flags); +abi_long set_mcontext(CPUArchState *env, target_mcontext_t *mcp, int srflag); +abi_long get_ucontext_sigreturn(CPUArchState *env, abi_ulong target_sf, abi_ulong *target_uc); #endif /* TARGET_OS_UCONTEXT_H */ -- cgit v1.1