aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Henderson <richard.henderson@linaro.org>2021-11-02 13:44:52 -0400
committerRichard Henderson <richard.henderson@linaro.org>2021-11-02 13:44:53 -0400
commit102f39b32dd32711fdb6236f17fb63a3cc63f8a7 (patch)
tree2d7868d58472ea136503b038a27aceeefe7b1a5c
parent91e8394415f9bc9cd81c02bfafe02012855d4f98 (diff)
parent742f07628c0a0bd847b47ee0a0b20c44531e0ba5 (diff)
Merge remote-tracking branch 'remotes/rth/tags/pull-tcg-20211102' into staging
- Split out host signal handing from accel/tcg/user-exec.c to linux-user/host/arch/host-signal.h - Replace TCGCPUOps.tlb_fill with TCGCPUOps.record_sigsegv for user-only - Add TCGCPUOps.record_sigbus for user-only - Remove a lot of target-specific cpu_loop handling for signals, now accomplished with generic code. # gpg: Signature made Tue 02 Nov 2021 07:06:14 AM EDT # gpg: using RSA key 7A481E78868B4DB6A85A05C064DF38E8AF7E215F # gpg: issuer "richard.henderson@linaro.org" # gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>" [ultimate] * remotes/rth/tags/pull-tcg-20211102: (60 commits) linux-user: Handle BUS_ADRALN in host_signal_handler tcg: Add helper_unaligned_{ld,st} for user-only sigbus accel/tcg: Report unaligned load/store for user-only accel/tcg: Report unaligned atomics for user-only target/sparc: Set fault address in sparc_cpu_do_unaligned_access target/sparc: Split out build_sfsr target/sparc: Remove DEBUG_UNALIGNED target/sh4: Set fault address in superh_cpu_do_unaligned_access target/s390x: Implement s390x_cpu_record_sigbus linux-user/ppc: Remove POWERPC_EXCP_ALIGN handling target/ppc: Restrict ppc_cpu_do_unaligned_access to sysemu target/ppc: Set fault address in ppc_cpu_do_unaligned_access target/ppc: Move SPR_DSISR setting to powerpc_excp target/microblaze: Do not set MO_ALIGN for user-only linux-user/hppa: Remove EXCP_UNALIGN handling target/arm: Implement arm_cpu_record_sigbus target/alpha: Implement alpha_cpu_record_sigbus linux-user: Add cpu_loop_exit_sigbus hw/core: Add TCGCPUOps.record_sigbus accel/tcg: Restrict TCGCPUOps::tlb_fill() to sysemu ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
-rw-r--r--accel/tcg/cpu-exec.c3
-rw-r--r--accel/tcg/user-exec.c859
-rwxr-xr-xconfigure8
-rw-r--r--include/exec/exec-all.h55
-rw-r--r--include/hw/core/tcg-cpu-ops.h71
-rw-r--r--include/tcg/tcg-ldst.h5
-rw-r--r--linux-user/aarch64/cpu_loop.c12
-rw-r--r--linux-user/alpha/cpu_loop.c15
-rw-r--r--linux-user/arm/cpu_loop.c30
-rw-r--r--linux-user/cris/cpu_loop.c10
-rw-r--r--linux-user/hexagon/cpu_loop.c24
-rw-r--r--linux-user/host/aarch64/host-signal.h74
-rw-r--r--linux-user/host/alpha/host-signal.h42
-rw-r--r--linux-user/host/arm/host-signal.h30
-rw-r--r--linux-user/host/i386/host-signal.h25
-rw-r--r--linux-user/host/mips/host-signal.h62
-rw-r--r--linux-user/host/ppc/host-signal.h25
-rw-r--r--linux-user/host/ppc64/host-signal.h1
-rw-r--r--linux-user/host/riscv/host-signal.h58
-rw-r--r--linux-user/host/riscv/hostdep.h (renamed from linux-user/host/riscv64/hostdep.h)4
-rw-r--r--linux-user/host/riscv/safe-syscall.inc.S (renamed from linux-user/host/riscv64/safe-syscall.inc.S)0
-rw-r--r--linux-user/host/riscv32/hostdep.h11
-rw-r--r--linux-user/host/s390/host-signal.h93
-rw-r--r--linux-user/host/s390x/host-signal.h1
-rw-r--r--linux-user/host/sparc/host-signal.h54
-rw-r--r--linux-user/host/sparc64/host-signal.h1
-rw-r--r--linux-user/host/x32/host-signal.h1
-rw-r--r--linux-user/host/x86_64/host-signal.h24
-rw-r--r--linux-user/hppa/cpu_loop.c23
-rw-r--r--linux-user/m68k/cpu_loop.c10
-rw-r--r--linux-user/microblaze/cpu_loop.c10
-rw-r--r--linux-user/mips/cpu_loop.c11
-rw-r--r--linux-user/openrisc/cpu_loop.c25
-rw-r--r--linux-user/ppc/cpu_loop.c8
-rw-r--r--linux-user/riscv/cpu_loop.c7
-rw-r--r--linux-user/s390x/cpu_loop.c13
-rw-r--r--linux-user/sh4/cpu_loop.c8
-rw-r--r--linux-user/signal.c129
-rw-r--r--linux-user/sparc/cpu_loop.c25
-rw-r--r--linux-user/xtensa/cpu_loop.c9
-rw-r--r--meson.build4
-rw-r--r--target/alpha/cpu.c7
-rw-r--r--target/alpha/cpu.h21
-rw-r--r--target/alpha/helper.c39
-rw-r--r--target/alpha/mem_helper.c30
-rw-r--r--target/arm/cpu.c7
-rw-r--r--target/arm/cpu_tcg.c7
-rw-r--r--target/arm/internals.h8
-rw-r--r--target/arm/mte_helper.c6
-rw-r--r--target/arm/sve_helper.c2
-rw-r--r--target/arm/tlb_helper.c42
-rw-r--r--target/cris/cpu.c4
-rw-r--r--target/cris/cpu.h8
-rw-r--r--target/cris/helper.c18
-rw-r--r--target/cris/meson.build7
-rw-r--r--target/hexagon/cpu.c23
-rw-r--r--target/hppa/cpu.c2
-rw-r--r--target/hppa/cpu.h2
-rw-r--r--target/hppa/mem_helper.c15
-rw-r--r--target/hppa/meson.build6
-rw-r--r--target/i386/tcg/helper-tcg.h6
-rw-r--r--target/i386/tcg/tcg-cpu.c3
-rw-r--r--target/i386/tcg/user/excp_helper.c23
-rw-r--r--target/m68k/cpu.c2
-rw-r--r--target/m68k/helper.c6
-rw-r--r--target/microblaze/cpu.c2
-rw-r--r--target/microblaze/cpu.h8
-rw-r--r--target/microblaze/helper.c13
-rw-r--r--target/microblaze/translate.c16
-rw-r--r--target/mips/cpu.c2
-rw-r--r--target/mips/tcg/meson.build3
-rw-r--r--target/mips/tcg/tcg-internal.h7
-rw-r--r--target/mips/tcg/user/meson.build3
-rw-r--r--target/mips/tcg/user/tlb_helper.c59
-rw-r--r--target/nios2/cpu.c6
-rw-r--r--target/nios2/cpu.h6
-rw-r--r--target/nios2/helper.c7
-rw-r--r--target/openrisc/cpu.c2
-rw-r--r--target/openrisc/cpu.h7
-rw-r--r--target/openrisc/meson.build2
-rw-r--r--target/openrisc/mmu.c9
-rw-r--r--target/ppc/cpu.h3
-rw-r--r--target/ppc/cpu_init.c6
-rw-r--r--target/ppc/excp_helper.c41
-rw-r--r--target/ppc/internal.h17
-rw-r--r--target/ppc/user_only_helper.c15
-rw-r--r--target/riscv/cpu.c2
-rw-r--r--target/riscv/cpu_helper.c21
-rw-r--r--target/s390x/cpu.c7
-rw-r--r--target/s390x/s390x-internal.h9
-rw-r--r--target/s390x/tcg/excp_helper.c45
-rw-r--r--target/s390x/tcg/mem_helper.c18
-rw-r--r--target/sh4/cpu.c2
-rw-r--r--target/sh4/cpu.h6
-rw-r--r--target/sh4/helper.c9
-rw-r--r--target/sh4/op_helper.c5
-rw-r--r--target/sparc/cpu.c2
-rw-r--r--target/sparc/ldst_helper.c22
-rw-r--r--target/sparc/meson.build2
-rw-r--r--target/sparc/mmu_helper.c115
-rw-r--r--target/xtensa/cpu.c2
-rw-r--r--target/xtensa/cpu.h2
-rw-r--r--target/xtensa/helper.c22
103 files changed, 1266 insertions, 1433 deletions
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index c9764c1325..bba4672632 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -462,6 +462,7 @@ void cpu_exec_step_atomic(CPUState *cpu)
* memory.
*/
#ifndef CONFIG_SOFTMMU
+ clear_helper_retaddr();
tcg_debug_assert(!have_mmap_lock());
#endif
if (qemu_mutex_iothread_locked()) {
@@ -471,7 +472,6 @@ void cpu_exec_step_atomic(CPUState *cpu)
qemu_plugin_disable_mem_helpers(cpu);
}
-
/*
* As we start the exclusive region before codegen we must still
* be in the region if we longjump out of either the codegen or
@@ -916,6 +916,7 @@ int cpu_exec(CPUState *cpu)
#endif
#ifndef CONFIG_SOFTMMU
+ clear_helper_retaddr();
tcg_debug_assert(!have_mmap_lock());
#endif
if (qemu_mutex_iothread_locked()) {
diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c
index e6bb29b42d..1528a21fad 100644
--- a/accel/tcg/user-exec.c
+++ b/accel/tcg/user-exec.c
@@ -27,48 +27,18 @@
#include "exec/helper-proto.h"
#include "qemu/atomic128.h"
#include "trace/trace-root.h"
+#include "tcg/tcg-ldst.h"
#include "internal.h"
-#undef EAX
-#undef ECX
-#undef EDX
-#undef EBX
-#undef ESP
-#undef EBP
-#undef ESI
-#undef EDI
-#undef EIP
-#ifdef __linux__
-#include <sys/ucontext.h>
-#endif
-
__thread uintptr_t helper_retaddr;
//#define DEBUG_SIGNAL
-/* exit the current TB from a signal handler. The host registers are
- restored in a state compatible with the CPU emulator
+/*
+ * Adjust the pc to pass to cpu_restore_state; return the memop type.
*/
-static void QEMU_NORETURN cpu_exit_tb_from_sighandler(CPUState *cpu,
- sigset_t *old_set)
-{
- /* XXX: use siglongjmp ? */
- sigprocmask(SIG_SETMASK, old_set, NULL);
- cpu_loop_exit_noexc(cpu);
-}
-
-/* 'pc' is the host PC at which the exception was raised. 'address' is
- the effective address of the memory exception. 'is_write' is 1 if a
- write caused the exception and otherwise 0'. 'old_set' is the
- signal set which should be restored */
-static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
- int is_write, sigset_t *old_set)
+MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write)
{
- CPUState *cpu = current_cpu;
- CPUClass *cc;
- unsigned long address = (unsigned long)info->si_addr;
- MMUAccessType access_type = is_write ? MMU_DATA_STORE : MMU_DATA_LOAD;
-
switch (helper_retaddr) {
default:
/*
@@ -77,7 +47,7 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
* pointer into the generated code that will unwind to the
* correct guest pc.
*/
- pc = helper_retaddr;
+ *pc = helper_retaddr;
break;
case 0:
@@ -97,7 +67,7 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
* Therefore, adjust to compensate for what will be done later
* by cpu_restore_state_from_tb.
*/
- pc += GETPC_ADJ;
+ *pc += GETPC_ADJ;
break;
case 1:
@@ -113,118 +83,97 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
*
* Like tb_gen_code, release the memory lock before cpu_loop_exit.
*/
- pc = 0;
- access_type = MMU_INST_FETCH;
mmap_unlock();
- break;
+ *pc = 0;
+ return MMU_INST_FETCH;
}
- /* For synchronous signals we expect to be coming from the vCPU
- * thread (so current_cpu should be valid) and either from running
- * code or during translation which can fault as we cross pages.
- *
- * If neither is true then something has gone wrong and we should
- * abort rather than try and restart the vCPU execution.
- */
- if (!cpu || !cpu->running) {
- printf("qemu:%s received signal outside vCPU context @ pc=0x%"
- PRIxPTR "\n", __func__, pc);
- abort();
- }
+ return is_write ? MMU_DATA_STORE : MMU_DATA_LOAD;
+}
-#if defined(DEBUG_SIGNAL)
- printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
- pc, address, is_write, *(unsigned long *)old_set);
-#endif
- /* XXX: locking issue */
- /* Note that it is important that we don't call page_unprotect() unless
- * this is really a "write to nonwriteable page" fault, because
- * page_unprotect() assumes that if it is called for an access to
- * a page that's writeable this means we had two threads racing and
- * another thread got there first and already made the page writeable;
- * so we will retry the access. If we were to call page_unprotect()
- * for some other kind of fault that should really be passed to the
- * guest, we'd end up in an infinite loop of retrying the faulting
- * access.
- */
- if (is_write && info->si_signo == SIGSEGV && info->si_code == SEGV_ACCERR &&
- h2g_valid(address)) {
- switch (page_unprotect(h2g(address), pc)) {
- case 0:
- /* Fault not caused by a page marked unwritable to protect
- * cached translations, must be the guest binary's problem.
- */
- break;
- case 1:
- /* Fault caused by protection of cached translation; TBs
- * invalidated, so resume execution. Retain helper_retaddr
- * for a possible second fault.
- */
- return 1;
- case 2:
- /* Fault caused by protection of cached translation, and the
- * currently executing TB was modified and must be exited
- * immediately. Clear helper_retaddr for next execution.
- */
- clear_helper_retaddr();
- cpu_exit_tb_from_sighandler(cpu, old_set);
- /* NORETURN */
-
- default:
- g_assert_not_reached();
- }
+/**
+ * handle_sigsegv_accerr_write:
+ * @cpu: the cpu context
+ * @old_set: the sigset_t from the signal ucontext_t
+ * @host_pc: the host pc, adjusted for the signal
+ * @guest_addr: the guest address of the fault
+ *
+ * Return true if the write fault has been handled, and should be re-tried.
+ *
+ * Note that it is important that we don't call page_unprotect() unless
+ * this is really a "write to nonwriteable page" fault, because
+ * page_unprotect() assumes that if it is called for an access to
+ * a page that's writeable this means we had two threads racing and
+ * another thread got there first and already made the page writeable;
+ * so we will retry the access. If we were to call page_unprotect()
+ * for some other kind of fault that should really be passed to the
+ * guest, we'd end up in an infinite loop of retrying the faulting access.
+ */
+bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set,
+ uintptr_t host_pc, abi_ptr guest_addr)
+{
+ switch (page_unprotect(guest_addr, host_pc)) {
+ case 0:
+ /*
+ * Fault not caused by a page marked unwritable to protect
+ * cached translations, must be the guest binary's problem.
+ */
+ return false;
+ case 1:
+ /*
+ * Fault caused by protection of cached translation; TBs
+ * invalidated, so resume execution.
+ */
+ return true;
+ case 2:
+ /*
+ * Fault caused by protection of cached translation, and the
+ * currently executing TB was modified and must be exited immediately.
+ */
+ sigprocmask(SIG_SETMASK, old_set, NULL);
+ cpu_loop_exit_noexc(cpu);
+ /* NORETURN */
+ default:
+ g_assert_not_reached();
}
-
- /* Convert forcefully to guest address space, invalid addresses
- are still valid segv ones */
- address = h2g_nocheck(address);
-
- /*
- * There is no way the target can handle this other than raising
- * an exception. Undo signal and retaddr state prior to longjmp.
- */
- sigprocmask(SIG_SETMASK, old_set, NULL);
- clear_helper_retaddr();
-
- cc = CPU_GET_CLASS(cpu);
- cc->tcg_ops->tlb_fill(cpu, address, 0, access_type,
- MMU_USER_IDX, false, pc);
- g_assert_not_reached();
}
static int probe_access_internal(CPUArchState *env, target_ulong addr,
int fault_size, MMUAccessType access_type,
bool nonfault, uintptr_t ra)
{
- int flags;
+ int acc_flag;
+ bool maperr;
switch (access_type) {
case MMU_DATA_STORE:
- flags = PAGE_WRITE;
+ acc_flag = PAGE_WRITE_ORG;
break;
case MMU_DATA_LOAD:
- flags = PAGE_READ;
+ acc_flag = PAGE_READ;
break;
case MMU_INST_FETCH:
- flags = PAGE_EXEC;
+ acc_flag = PAGE_EXEC;
break;
default:
g_assert_not_reached();
}
- if (!guest_addr_valid_untagged(addr) ||
- page_check_range(addr, 1, flags) < 0) {
- if (nonfault) {
- return TLB_INVALID_MASK;
- } else {
- CPUState *cpu = env_cpu(env);
- CPUClass *cc = CPU_GET_CLASS(cpu);
- cc->tcg_ops->tlb_fill(cpu, addr, fault_size, access_type,
- MMU_USER_IDX, false, ra);
- g_assert_not_reached();
+ if (guest_addr_valid_untagged(addr)) {
+ int page_flags = page_get_flags(addr);
+ if (page_flags & acc_flag) {
+ return 0; /* success */
}
+ maperr = !(page_flags & PAGE_VALID);
+ } else {
+ maperr = true;
}
- return 0;
+
+ if (nonfault) {
+ return TLB_INVALID_MASK;
+ }
+
+ cpu_loop_exit_sigsegv(env_cpu(env), addr, access_type, maperr, ra);
}
int probe_access_flags(CPUArchState *env, target_ulong addr,
@@ -250,640 +199,6 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size,
return size ? g2h(env_cpu(env), addr) : NULL;
}
-#if defined(__i386__)
-
-#if defined(__NetBSD__)
-#include <ucontext.h>
-#include <machine/trap.h>
-
-#define EIP_sig(context) ((context)->uc_mcontext.__gregs[_REG_EIP])
-#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO])
-#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR])
-#define MASK_sig(context) ((context)->uc_sigmask)
-#define PAGE_FAULT_TRAP T_PAGEFLT
-#elif defined(__FreeBSD__) || defined(__DragonFly__)
-#include <ucontext.h>
-#include <machine/trap.h>
-
-#define EIP_sig(context) (*((unsigned long *)&(context)->uc_mcontext.mc_eip))
-#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno)
-#define ERROR_sig(context) ((context)->uc_mcontext.mc_err)
-#define MASK_sig(context) ((context)->uc_sigmask)
-#define PAGE_FAULT_TRAP T_PAGEFLT
-#elif defined(__OpenBSD__)
-#include <machine/trap.h>
-#define EIP_sig(context) ((context)->sc_eip)
-#define TRAP_sig(context) ((context)->sc_trapno)
-#define ERROR_sig(context) ((context)->sc_err)
-#define MASK_sig(context) ((context)->sc_mask)
-#define PAGE_FAULT_TRAP T_PAGEFLT
-#else
-#define EIP_sig(context) ((context)->uc_mcontext.gregs[REG_EIP])
-#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO])
-#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR])
-#define MASK_sig(context) ((context)->uc_sigmask)
-#define PAGE_FAULT_TRAP 0xe
-#endif
-
-int cpu_signal_handler(int host_signum, void *pinfo,
- void *puc)
-{
- siginfo_t *info = pinfo;
-#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
- ucontext_t *uc = puc;
-#elif defined(__OpenBSD__)
- struct sigcontext *uc = puc;
-#else
- ucontext_t *uc = puc;
-#endif
- unsigned long pc;
- int trapno;
-
-#ifndef REG_EIP
-/* for glibc 2.1 */
-#define REG_EIP EIP
-#define REG_ERR ERR
-#define REG_TRAPNO TRAPNO
-#endif
- pc = EIP_sig(uc);
- trapno = TRAP_sig(uc);
- return handle_cpu_signal(pc, info,
- trapno == PAGE_FAULT_TRAP ?
- (ERROR_sig(uc) >> 1) & 1 : 0,
- &MASK_sig(uc));
-}
-
-#elif defined(__x86_64__)
-
-#ifdef __NetBSD__
-#include <machine/trap.h>
-#define PC_sig(context) _UC_MACHINE_PC(context)
-#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO])
-#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR])
-#define MASK_sig(context) ((context)->uc_sigmask)
-#define PAGE_FAULT_TRAP T_PAGEFLT
-#elif defined(__OpenBSD__)
-#include <machine/trap.h>
-#define PC_sig(context) ((context)->sc_rip)
-#define TRAP_sig(context) ((context)->sc_trapno)
-#define ERROR_sig(context) ((context)->sc_err)
-#define MASK_sig(context) ((context)->sc_mask)
-#define PAGE_FAULT_TRAP T_PAGEFLT
-#elif defined(__FreeBSD__) || defined(__DragonFly__)
-#include <ucontext.h>
-#include <machine/trap.h>
-
-#define PC_sig(context) (*((unsigned long *)&(context)->uc_mcontext.mc_rip))
-#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno)
-#define ERROR_sig(context) ((context)->uc_mcontext.mc_err)
-#define MASK_sig(context) ((context)->uc_sigmask)
-#define PAGE_FAULT_TRAP T_PAGEFLT
-#else
-#define PC_sig(context) ((context)->uc_mcontext.gregs[REG_RIP])
-#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO])
-#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR])
-#define MASK_sig(context) ((context)->uc_sigmask)
-#define PAGE_FAULT_TRAP 0xe
-#endif
-
-int cpu_signal_handler(int host_signum, void *pinfo,
- void *puc)
-{
- siginfo_t *info = pinfo;
- unsigned long pc;
-#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
- ucontext_t *uc = puc;
-#elif defined(__OpenBSD__)
- struct sigcontext *uc = puc;
-#else
- ucontext_t *uc = puc;
-#endif
-
- pc = PC_sig(uc);
- return handle_cpu_signal(pc, info,
- TRAP_sig(uc) == PAGE_FAULT_TRAP ?
- (ERROR_sig(uc) >> 1) & 1 : 0,
- &MASK_sig(uc));
-}
-
-#elif defined(_ARCH_PPC)
-
-/***********************************************************************
- * signal context platform-specific definitions
- * From Wine
- */
-#ifdef linux
-/* All Registers access - only for local access */
-#define REG_sig(reg_name, context) \
- ((context)->uc_mcontext.regs->reg_name)
-/* Gpr Registers access */
-#define GPR_sig(reg_num, context) REG_sig(gpr[reg_num], context)
-/* Program counter */
-#define IAR_sig(context) REG_sig(nip, context)
-/* Machine State Register (Supervisor) */
-#define MSR_sig(context) REG_sig(msr, context)
-/* Count register */
-#define CTR_sig(context) REG_sig(ctr, context)
-/* User's integer exception register */
-#define XER_sig(context) REG_sig(xer, context)
-/* Link register */
-#define LR_sig(context) REG_sig(link, context)
-/* Condition register */
-#define CR_sig(context) REG_sig(ccr, context)
-
-/* Float Registers access */
-#define FLOAT_sig(reg_num, context) \
- (((double *)((char *)((context)->uc_mcontext.regs + 48 * 4)))[reg_num])
-#define FPSCR_sig(context) \
- (*(int *)((char *)((context)->uc_mcontext.regs + (48 + 32 * 2) * 4)))
-/* Exception Registers access */
-#define DAR_sig(context) REG_sig(dar, context)
-#define DSISR_sig(context) REG_sig(dsisr, context)
-#define TRAP_sig(context) REG_sig(trap, context)
-#endif /* linux */
-
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-#include <ucontext.h>
-#define IAR_sig(context) ((context)->uc_mcontext.mc_srr0)
-#define MSR_sig(context) ((context)->uc_mcontext.mc_srr1)
-#define CTR_sig(context) ((context)->uc_mcontext.mc_ctr)
-#define XER_sig(context) ((context)->uc_mcontext.mc_xer)
-#define LR_sig(context) ((context)->uc_mcontext.mc_lr)
-#define CR_sig(context) ((context)->uc_mcontext.mc_cr)
-/* Exception Registers access */
-#define DAR_sig(context) ((context)->uc_mcontext.mc_dar)
-#define DSISR_sig(context) ((context)->uc_mcontext.mc_dsisr)
-#define TRAP_sig(context) ((context)->uc_mcontext.mc_exc)
-#endif /* __FreeBSD__|| __FreeBSD_kernel__ */
-
-int cpu_signal_handler(int host_signum, void *pinfo,
- void *puc)
-{
- siginfo_t *info = pinfo;
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
- ucontext_t *uc = puc;
-#else
- ucontext_t *uc = puc;
-#endif
- unsigned long pc;
- int is_write;
-
- pc = IAR_sig(uc);
- is_write = 0;
-#if 0
- /* ppc 4xx case */
- if (DSISR_sig(uc) & 0x00800000) {
- is_write = 1;
- }
-#else
- if (TRAP_sig(uc) != 0x400 && (DSISR_sig(uc) & 0x02000000)) {
- is_write = 1;
- }
-#endif
- return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
-}
-
-#elif defined(__alpha__)
-
-int cpu_signal_handler(int host_signum, void *pinfo,
- void *puc)
-{
- siginfo_t *info = pinfo;
- ucontext_t *uc = puc;
- uint32_t *pc = uc->uc_mcontext.sc_pc;
- uint32_t insn = *pc;
- int is_write = 0;
-
- /* XXX: need kernel patch to get write flag faster */
- switch (insn >> 26) {
- case 0x0d: /* stw */
- case 0x0e: /* stb */
- case 0x0f: /* stq_u */
- case 0x24: /* stf */
- case 0x25: /* stg */
- case 0x26: /* sts */
- case 0x27: /* stt */
- case 0x2c: /* stl */
- case 0x2d: /* stq */
- case 0x2e: /* stl_c */
- case 0x2f: /* stq_c */
- is_write = 1;
- }
-
- return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
-}
-#elif defined(__sparc__)
-
-int cpu_signal_handler(int host_signum, void *pinfo,
- void *puc)
-{
- siginfo_t *info = pinfo;
- int is_write;
- uint32_t insn;
-#if !defined(__arch64__) || defined(CONFIG_SOLARIS)
- uint32_t *regs = (uint32_t *)(info + 1);
- void *sigmask = (regs + 20);
- /* XXX: is there a standard glibc define ? */
- unsigned long pc = regs[1];
-#else
-#ifdef __linux__
- struct sigcontext *sc = puc;
- unsigned long pc = sc->sigc_regs.tpc;
- void *sigmask = (void *)sc->sigc_mask;
-#elif defined(__OpenBSD__)
- struct sigcontext *uc = puc;
- unsigned long pc = uc->sc_pc;
- void *sigmask = (void *)(long)uc->sc_mask;
-#elif defined(__NetBSD__)
- ucontext_t *uc = puc;
- unsigned long pc = _UC_MACHINE_PC(uc);
- void *sigmask = (void *)&uc->uc_sigmask;
-#endif
-#endif
-
- /* XXX: need kernel patch to get write flag faster */
- is_write = 0;
- insn = *(uint32_t *)pc;
- if ((insn >> 30) == 3) {
- switch ((insn >> 19) & 0x3f) {
- case 0x05: /* stb */
- case 0x15: /* stba */
- case 0x06: /* sth */
- case 0x16: /* stha */
- case 0x04: /* st */
- case 0x14: /* sta */
- case 0x07: /* std */
- case 0x17: /* stda */
- case 0x0e: /* stx */
- case 0x1e: /* stxa */
- case 0x24: /* stf */
- case 0x34: /* stfa */
- case 0x27: /* stdf */
- case 0x37: /* stdfa */
- case 0x26: /* stqf */
- case 0x36: /* stqfa */
- case 0x25: /* stfsr */
- case 0x3c: /* casa */
- case 0x3e: /* casxa */
- is_write = 1;
- break;
- }
- }
- return handle_cpu_signal(pc, info, is_write, sigmask);
-}
-
-#elif defined(__arm__)
-
-#if defined(__NetBSD__)
-#include <ucontext.h>
-#include <sys/siginfo.h>
-#endif
-
-int cpu_signal_handler(int host_signum, void *pinfo,
- void *puc)
-{
- siginfo_t *info = pinfo;
-#if defined(__NetBSD__)
- ucontext_t *uc = puc;
- siginfo_t *si = pinfo;
-#else
- ucontext_t *uc = puc;
-#endif
- unsigned long pc;
- uint32_t fsr;
- int is_write;
-
-#if defined(__NetBSD__)
- pc = uc->uc_mcontext.__gregs[_REG_R15];
-#elif defined(__GLIBC__) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
- pc = uc->uc_mcontext.gregs[R15];
-#else
- pc = uc->uc_mcontext.arm_pc;
-#endif
-
-#ifdef __NetBSD__
- fsr = si->si_trap;
-#else
- fsr = uc->uc_mcontext.error_code;
-#endif
- /*
- * In the FSR, bit 11 is WnR, assuming a v6 or
- * later processor. On v5 we will always report
- * this as a read, which will fail later.
- */
- is_write = extract32(fsr, 11, 1);
- return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
-}
-
-#elif defined(__aarch64__)
-
-#if defined(__NetBSD__)
-
-#include <ucontext.h>
-#include <sys/siginfo.h>
-
-int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
-{
- ucontext_t *uc = puc;
- siginfo_t *si = pinfo;
- unsigned long pc;
- int is_write;
- uint32_t esr;
-
- pc = uc->uc_mcontext.__gregs[_REG_PC];
- esr = si->si_trap;
-
- /*
- * siginfo_t::si_trap is the ESR value, for data aborts ESR.EC
- * is 0b10010x: then bit 6 is the WnR bit
- */
- is_write = extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1;
- return handle_cpu_signal(pc, si, is_write, &uc->uc_sigmask);
-}
-
-#else
-
-#ifndef ESR_MAGIC
-/* Pre-3.16 kernel headers don't have these, so provide fallback definitions */
-#define ESR_MAGIC 0x45535201
-struct esr_context {
- struct _aarch64_ctx head;
- uint64_t esr;
-};
-#endif
-
-static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc)
-{
- return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved;
-}
-
-static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr)
-{
- return (struct _aarch64_ctx *)((char *)hdr + hdr->size);
-}
-
-int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
-{
- siginfo_t *info = pinfo;
- ucontext_t *uc = puc;
- uintptr_t pc = uc->uc_mcontext.pc;
- bool is_write;
- struct _aarch64_ctx *hdr;
- struct esr_context const *esrctx = NULL;
-
- /* Find the esr_context, which has the WnR bit in it */
- for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) {
- if (hdr->magic == ESR_MAGIC) {
- esrctx = (struct esr_context const *)hdr;
- break;
- }
- }
-
- if (esrctx) {
- /* For data aborts ESR.EC is 0b10010x: then bit 6 is the WnR bit */
- uint64_t esr = esrctx->esr;
- is_write = extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1;
- } else {
- /*
- * Fall back to parsing instructions; will only be needed
- * for really ancient (pre-3.16) kernels.
- */
- uint32_t insn = *(uint32_t *)pc;
-
- is_write = ((insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */
- || (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */
- || (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */
- || (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */
- || (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */
- || (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */
- || (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */
- /* Ignore bits 10, 11 & 21, controlling indexing. */
- || (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */
- || (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */
- /* Ignore bits 23 & 24, controlling indexing. */
- || (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */
- }
- return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
-}
-#endif
-
-#elif defined(__s390__)
-
-int cpu_signal_handler(int host_signum, void *pinfo,
- void *puc)
-{
- siginfo_t *info = pinfo;
- ucontext_t *uc = puc;
- unsigned long pc;
- uint16_t *pinsn;
- int is_write = 0;
-
- pc = uc->uc_mcontext.psw.addr;
-
- /*
- * ??? On linux, the non-rt signal handler has 4 (!) arguments instead
- * of the normal 2 arguments. The 4th argument contains the "Translation-
- * Exception Identification for DAT Exceptions" from the hardware (aka
- * "int_parm_long"), which does in fact contain the is_write value.
- * The rt signal handler, as far as I can tell, does not give this value
- * at all. Not that we could get to it from here even if it were.
- * So fall back to parsing instructions. Treat read-modify-write ones as
- * writes, which is not fully correct, but for tracking self-modifying code
- * this is better than treating them as reads. Checking si_addr page flags
- * might be a viable improvement, albeit a racy one.
- */
- /* ??? This is not even close to complete. */
- pinsn = (uint16_t *)pc;
- switch (pinsn[0] >> 8) {
- case 0x50: /* ST */
- case 0x42: /* STC */
- case 0x40: /* STH */
- case 0xba: /* CS */
- case 0xbb: /* CDS */
- is_write = 1;
- break;
- case 0xc4: /* RIL format insns */
- switch (pinsn[0] & 0xf) {
- case 0xf: /* STRL */
- case 0xb: /* STGRL */
- case 0x7: /* STHRL */
- is_write = 1;
- }
- break;
- case 0xc8: /* SSF format insns */
- switch (pinsn[0] & 0xf) {
- case 0x2: /* CSST */
- is_write = 1;
- }
- break;
- case 0xe3: /* RXY format insns */
- switch (pinsn[2] & 0xff) {
- case 0x50: /* STY */
- case 0x24: /* STG */
- case 0x72: /* STCY */
- case 0x70: /* STHY */
- case 0x8e: /* STPQ */
- case 0x3f: /* STRVH */
- case 0x3e: /* STRV */
- case 0x2f: /* STRVG */
- is_write = 1;
- }
- break;
- case 0xeb: /* RSY format insns */
- switch (pinsn[2] & 0xff) {
- case 0x14: /* CSY */
- case 0x30: /* CSG */
- case 0x31: /* CDSY */
- case 0x3e: /* CDSG */
- case 0xe4: /* LANG */
- case 0xe6: /* LAOG */
- case 0xe7: /* LAXG */
- case 0xe8: /* LAAG */
- case 0xea: /* LAALG */
- case 0xf4: /* LAN */
- case 0xf6: /* LAO */
- case 0xf7: /* LAX */
- case 0xfa: /* LAAL */
- case 0xf8: /* LAA */
- is_write = 1;
- }
- break;
- }
-
- return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
-}
-
-#elif defined(__mips__)
-
-#if defined(__misp16) || defined(__mips_micromips)
-#error "Unsupported encoding"
-#endif
-
-int cpu_signal_handler(int host_signum, void *pinfo,
- void *puc)
-{
- siginfo_t *info = pinfo;
- ucontext_t *uc = puc;
- uintptr_t pc = uc->uc_mcontext.pc;
- uint32_t insn = *(uint32_t *)pc;
- int is_write = 0;
-
- /* Detect all store instructions at program counter. */
- switch((insn >> 26) & 077) {
- case 050: /* SB */
- case 051: /* SH */
- case 052: /* SWL */
- case 053: /* SW */
- case 054: /* SDL */
- case 055: /* SDR */
- case 056: /* SWR */
- case 070: /* SC */
- case 071: /* SWC1 */
- case 074: /* SCD */
- case 075: /* SDC1 */
- case 077: /* SD */
-#if !defined(__mips_isa_rev) || __mips_isa_rev < 6
- case 072: /* SWC2 */
- case 076: /* SDC2 */
-#endif
- is_write = 1;
- break;
- case 023: /* COP1X */
- /* Required in all versions of MIPS64 since
- MIPS64r1 and subsequent versions of MIPS32r2. */
- switch (insn & 077) {
- case 010: /* SWXC1 */
- case 011: /* SDXC1 */
- case 015: /* SUXC1 */
- is_write = 1;
- }
- break;
- }
-
- return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
-}
-
-#elif defined(__riscv)
-
-int cpu_signal_handler(int host_signum, void *pinfo,
- void *puc)
-{
- siginfo_t *info = pinfo;
- ucontext_t *uc = puc;
- greg_t pc = uc->uc_mcontext.__gregs[REG_PC];
- uint32_t insn = *(uint32_t *)pc;
- int is_write = 0;
-
- /* Detect store by reading the instruction at the program
- counter. Note: we currently only generate 32-bit
- instructions so we thus only detect 32-bit stores */
- switch (((insn >> 0) & 0b11)) {
- case 3:
- switch (((insn >> 2) & 0b11111)) {
- case 8:
- switch (((insn >> 12) & 0b111)) {
- case 0: /* sb */
- case 1: /* sh */
- case 2: /* sw */
- case 3: /* sd */
- case 4: /* sq */
- is_write = 1;
- break;
- default:
- break;
- }
- break;
- case 9:
- switch (((insn >> 12) & 0b111)) {
- case 2: /* fsw */
- case 3: /* fsd */
- case 4: /* fsq */
- is_write = 1;
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
- }
-
- /* Check for compressed instructions */
- switch (((insn >> 13) & 0b111)) {
- case 7:
- switch (insn & 0b11) {
- case 0: /*c.sd */
- case 2: /* c.sdsp */
- is_write = 1;
- break;
- default:
- break;
- }
- break;
- case 6:
- switch (insn & 0b11) {
- case 0: /* c.sw */
- case 3: /* c.swsp */
- is_write = 1;
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
-
- return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
-}
-
-#else
-
-#error host CPU specific signal handler needed
-
-#endif
-
/* The softmmu versions of these helpers are in cputlb.c. */
/*
@@ -901,12 +216,27 @@ static void validate_memop(MemOpIdx oi, MemOp expected)
#endif
}
+void helper_unaligned_ld(CPUArchState *env, target_ulong addr)
+{
+ cpu_loop_exit_sigbus(env_cpu(env), addr, MMU_DATA_LOAD, GETPC());
+}
+
+void helper_unaligned_st(CPUArchState *env, target_ulong addr)
+{
+ cpu_loop_exit_sigbus(env_cpu(env), addr, MMU_DATA_STORE, GETPC());
+}
+
static void *cpu_mmu_lookup(CPUArchState *env, target_ulong addr,
MemOpIdx oi, uintptr_t ra, MMUAccessType type)
{
+ MemOp mop = get_memop(oi);
+ int a_bits = get_alignment_bits(mop);
void *ret;
- /* TODO: Enforce guest required alignment. */
+ /* Enforce guest required alignment. */
+ if (unlikely(addr & ((1 << a_bits) - 1))) {
+ cpu_loop_exit_sigbus(env_cpu(env), addr, type, ra);
+ }
ret = g2h(env_cpu(env), addr);
set_helper_retaddr(ra);
@@ -1160,11 +490,22 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
MemOpIdx oi, int size, int prot,
uintptr_t retaddr)
{
+ MemOp mop = get_memop(oi);
+ int a_bits = get_alignment_bits(mop);
+ void *ret;
+
+ /* Enforce guest required alignment. */
+ if (unlikely(addr & ((1 << a_bits) - 1))) {
+ MMUAccessType t = prot == PAGE_READ ? MMU_DATA_LOAD : MMU_DATA_STORE;
+ cpu_loop_exit_sigbus(env_cpu(env), addr, t, retaddr);
+ }
+
/* Enforce qemu required alignment. */
if (unlikely(addr & (size - 1))) {
cpu_loop_exit_atomic(env_cpu(env), retaddr);
}
- void *ret = g2h(env_cpu(env), addr);
+
+ ret = g2h(env_cpu(env), addr);
set_helper_retaddr(retaddr);
return ret;
}
diff --git a/configure b/configure
index 039467c04b..d57ad58342 100755
--- a/configure
+++ b/configure
@@ -570,11 +570,7 @@ elif check_define __s390__ ; then
cpu="s390"
fi
elif check_define __riscv ; then
- if check_define _LP64 ; then
- cpu="riscv64"
- else
- cpu="riscv32"
- fi
+ cpu="riscv"
elif check_define __arm__ ; then
cpu="arm"
elif check_define __aarch64__ ; then
@@ -587,7 +583,7 @@ ARCH=
# Normalise host CPU name and set ARCH.
# Note that this case should only have supported host CPUs, not guests.
case "$cpu" in
- ppc|ppc64|s390x|sparc64|x32|riscv32|riscv64)
+ ppc|ppc64|s390x|sparc64|x32|riscv)
;;
ppc64le)
ARCH="ppc64"
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index 9d5987ba04..6bb2a0f7ec 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -664,16 +664,55 @@ static inline tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env,
}
/**
- * cpu_signal_handler
- * @signum: host signal number
- * @pinfo: host siginfo_t
- * @puc: host ucontext_t
+ * adjust_signal_pc:
+ * @pc: raw pc from the host signal ucontext_t.
+ * @is_write: host memory operation was write, or read-modify-write.
*
- * To be called from the SIGBUS and SIGSEGV signal handler to inform the
- * virtual cpu of exceptions. Returns true if the signal was handled by
- * the virtual CPU.
+ * Alter @pc as required for unwinding. Return the type of the
+ * guest memory access -- host reads may be for guest execution.
*/
-int cpu_signal_handler(int signum, void *pinfo, void *puc);
+MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write);
+
+/**
+ * handle_sigsegv_accerr_write:
+ * @cpu: the cpu context
+ * @old_set: the sigset_t from the signal ucontext_t
+ * @host_pc: the host pc, adjusted for the signal
+ * @host_addr: the host address of the fault
+ *
+ * Return true if the write fault has been handled, and should be re-tried.
+ */
+bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set,
+ uintptr_t host_pc, abi_ptr guest_addr);
+
+/**
+ * cpu_loop_exit_sigsegv:
+ * @cpu: the cpu context
+ * @addr: the guest address of the fault
+ * @access_type: access was read/write/execute
+ * @maperr: true for invalid page, false for permission fault
+ * @ra: host pc for unwinding
+ *
+ * Use the TCGCPUOps hook to record cpu state, do guest operating system
+ * specific things to raise SIGSEGV, and jump to the main cpu loop.
+ */
+void QEMU_NORETURN cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr,
+ MMUAccessType access_type,
+ bool maperr, uintptr_t ra);
+
+/**
+ * cpu_loop_exit_sigbus:
+ * @cpu: the cpu context
+ * @addr: the guest address of the alignment fault
+ * @access_type: access was read/write/execute
+ * @ra: host pc for unwinding
+ *
+ * Use the TCGCPUOps hook to record cpu state, do guest operating system
+ * specific things to raise SIGBUS, and jump to the main cpu loop.
+ */
+void QEMU_NORETURN cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr,
+ MMUAccessType access_type,
+ uintptr_t ra);
#else
static inline void mmap_lock(void) {}
diff --git a/include/hw/core/tcg-cpu-ops.h b/include/hw/core/tcg-cpu-ops.h
index 6cbe17f2e6..e13898553a 100644
--- a/include/hw/core/tcg-cpu-ops.h
+++ b/include/hw/core/tcg-cpu-ops.h
@@ -35,18 +35,6 @@ struct TCGCPUOps {
void (*cpu_exec_enter)(CPUState *cpu);
/** @cpu_exec_exit: Callback for cpu_exec cleanup */
void (*cpu_exec_exit)(CPUState *cpu);
- /**
- * @tlb_fill: Handle a softmmu tlb miss or user-only address fault
- *
- * For system mode, if the access is valid, call tlb_set_page
- * and return true; if the access is invalid, and probe is
- * true, return false; otherwise raise an exception and do
- * not return. For user-only mode, always raise an exception
- * and do not return.
- */
- bool (*tlb_fill)(CPUState *cpu, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr);
/** @debug_excp_handler: Callback for handling debug exceptions */
void (*debug_excp_handler)(CPUState *cpu);
@@ -69,6 +57,16 @@ struct TCGCPUOps {
/** @cpu_exec_interrupt: Callback for processing interrupts in cpu_exec */
bool (*cpu_exec_interrupt)(CPUState *cpu, int interrupt_request);
/**
+ * @tlb_fill: Handle a softmmu tlb miss
+ *
+ * If the access is valid, call tlb_set_page and return true;
+ * if the access is invalid and probe is true, return false;
+ * otherwise raise an exception and do not return.
+ */
+ bool (*tlb_fill)(CPUState *cpu, vaddr address, int size,
+ MMUAccessType access_type, int mmu_idx,
+ bool probe, uintptr_t retaddr);
+ /**
* @do_transaction_failed: Callback for handling failed memory transactions
* (ie bus faults or external aborts; not MMU faults)
*/
@@ -111,6 +109,55 @@ struct TCGCPUOps {
*/
bool (*io_recompile_replay_branch)(CPUState *cpu,
const TranslationBlock *tb);
+#else
+ /**
+ * record_sigsegv:
+ * @cpu: cpu context
+ * @addr: faulting guest address
+ * @access_type: access was read/write/execute
+ * @maperr: true for invalid page, false for permission fault
+ * @ra: host pc for unwinding
+ *
+ * We are about to raise SIGSEGV with si_code set for @maperr,
+ * and si_addr set for @addr. Record anything further needed
+ * for the signal ucontext_t.
+ *
+ * If the emulated kernel does not provide anything to the signal
+ * handler with anything besides the user context registers, and
+ * the siginfo_t, then this hook need do nothing and may be omitted.
+ * Otherwise, record the data and return; the caller will raise
+ * the signal, unwind the cpu state, and return to the main loop.
+ *
+ * If it is simpler to re-use the sysemu tlb_fill code, @ra is provided
+ * so that a "normal" cpu exception can be raised. In this case,
+ * the signal must be raised by the architecture cpu_loop.
+ */
+ void (*record_sigsegv)(CPUState *cpu, vaddr addr,
+ MMUAccessType access_type,
+ bool maperr, uintptr_t ra);
+ /**
+ * record_sigbus:
+ * @cpu: cpu context
+ * @addr: misaligned guest address
+ * @access_type: access was read/write/execute
+ * @ra: host pc for unwinding
+ *
+ * We are about to raise SIGBUS with si_code BUS_ADRALN,
+ * and si_addr set for @addr. Record anything further needed
+ * for the signal ucontext_t.
+ *
+ * If the emulated kernel does not provide the signal handler with
+ * anything besides the user context registers, and the siginfo_t,
+ * then this hook need do nothing and may be omitted.
+ * Otherwise, record the data and return; the caller will raise
+ * the signal, unwind the cpu state, and return to the main loop.
+ *
+ * If it is simpler to re-use the sysemu do_unaligned_access code,
+ * @ra is provided so that a "normal" cpu exception can be raised.
+ * In this case, the signal must be raised by the architecture cpu_loop.
+ */
+ void (*record_sigbus)(CPUState *cpu, vaddr addr,
+ MMUAccessType access_type, uintptr_t ra);
#endif /* CONFIG_SOFTMMU */
#endif /* NEED_CPU_H */
diff --git a/include/tcg/tcg-ldst.h b/include/tcg/tcg-ldst.h
index 8c86365611..bf40942de4 100644
--- a/include/tcg/tcg-ldst.h
+++ b/include/tcg/tcg-ldst.h
@@ -70,5 +70,10 @@ void helper_be_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val,
void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val,
MemOpIdx oi, uintptr_t retaddr);
+#else
+
+void QEMU_NORETURN helper_unaligned_ld(CPUArchState *env, target_ulong addr);
+void QEMU_NORETURN helper_unaligned_st(CPUArchState *env, target_ulong addr);
+
#endif /* CONFIG_SOFTMMU */
#endif /* TCG_LDST_H */
diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c
index 034b737435..97e0728b67 100644
--- a/linux-user/aarch64/cpu_loop.c
+++ b/linux-user/aarch64/cpu_loop.c
@@ -79,7 +79,7 @@
void cpu_loop(CPUARMState *env)
{
CPUState *cs = env_cpu(env);
- int trapnr, ec, fsc, si_code;
+ int trapnr, ec, fsc, si_code, si_signo;
abi_long ret;
for (;;) {
@@ -121,20 +121,26 @@ void cpu_loop(CPUARMState *env)
fsc = extract32(env->exception.syndrome, 0, 6);
switch (fsc) {
case 0x04 ... 0x07: /* Translation fault, level {0-3} */
+ si_signo = TARGET_SIGSEGV;
si_code = TARGET_SEGV_MAPERR;
break;
case 0x09 ... 0x0b: /* Access flag fault, level {1-3} */
case 0x0d ... 0x0f: /* Permission fault, level {1-3} */
+ si_signo = TARGET_SIGSEGV;
si_code = TARGET_SEGV_ACCERR;
break;
case 0x11: /* Synchronous Tag Check Fault */
+ si_signo = TARGET_SIGSEGV;
si_code = TARGET_SEGV_MTESERR;
break;
+ case 0x21: /* Alignment fault */
+ si_signo = TARGET_SIGBUS;
+ si_code = TARGET_BUS_ADRALN;
+ break;
default:
g_assert_not_reached();
}
-
- force_sig_fault(TARGET_SIGSEGV, si_code, env->exception.vaddress);
+ force_sig_fault(si_signo, si_code, env->exception.vaddress);
break;
case EXCP_DEBUG:
case EXCP_BKPT:
diff --git a/linux-user/alpha/cpu_loop.c b/linux-user/alpha/cpu_loop.c
index 1b00a81385..4029849d5c 100644
--- a/linux-user/alpha/cpu_loop.c
+++ b/linux-user/alpha/cpu_loop.c
@@ -54,21 +54,6 @@ void cpu_loop(CPUAlphaState *env)
fprintf(stderr, "External interrupt. Exit\n");
exit(EXIT_FAILURE);
break;
- case EXCP_MMFAULT:
- info.si_signo = TARGET_SIGSEGV;
- info.si_errno = 0;
- info.si_code = (page_get_flags(env->trap_arg0) & PAGE_VALID
- ? TARGET_SEGV_ACCERR : TARGET_SEGV_MAPERR);
- info._sifields._sigfault._addr = env->trap_arg0;
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- break;
- case EXCP_UNALIGN:
- info.si_signo = TARGET_SIGBUS;
- info.si_errno = 0;
- info.si_code = TARGET_BUS_ADRALN;
- info._sifields._sigfault._addr = env->trap_arg0;
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- break;
case EXCP_OPCDEC:
do_sigill:
info.si_signo = TARGET_SIGILL;
diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c
index ae09adcb95..01cb6eb534 100644
--- a/linux-user/arm/cpu_loop.c
+++ b/linux-user/arm/cpu_loop.c
@@ -25,6 +25,7 @@
#include "cpu_loop-common.h"
#include "signal-common.h"
#include "semihosting/common-semi.h"
+#include "target/arm/syndrome.h"
#define get_user_code_u32(x, gaddr, env) \
({ abi_long __r = get_user_u32((x), (gaddr)); \
@@ -280,7 +281,7 @@ static bool emulate_arm_fpa11(CPUARMState *env, uint32_t opcode)
void cpu_loop(CPUARMState *env)
{
CPUState *cs = env_cpu(env);
- int trapnr;
+ int trapnr, si_signo, si_code;
unsigned int n, insn;
abi_ulong ret;
@@ -423,9 +424,30 @@ void cpu_loop(CPUARMState *env)
break;
case EXCP_PREFETCH_ABORT:
case EXCP_DATA_ABORT:
- /* XXX: check env->error_code */
- force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR,
- env->exception.vaddress);
+ /* 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: /* Permision fault, level 1 */
+ case 0xf: /* Permision 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:
diff --git a/linux-user/cris/cpu_loop.c b/linux-user/cris/cpu_loop.c
index b9085619c4..0d5d268609 100644
--- a/linux-user/cris/cpu_loop.c
+++ b/linux-user/cris/cpu_loop.c
@@ -37,16 +37,6 @@ void cpu_loop(CPUCRISState *env)
process_queued_cpu_work(cs);
switch (trapnr) {
- case 0xaa:
- {
- info.si_signo = TARGET_SIGSEGV;
- info.si_errno = 0;
- /* XXX: check env->error_code */
- info.si_code = TARGET_SEGV_MAPERR;
- info._sifields._sigfault._addr = env->pregs[PR_EDA];
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- }
- break;
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
break;
diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c
index bee2a9e4ea..6b24cbaba9 100644
--- a/linux-user/hexagon/cpu_loop.c
+++ b/linux-user/hexagon/cpu_loop.c
@@ -28,8 +28,7 @@
void cpu_loop(CPUHexagonState *env)
{
CPUState *cs = env_cpu(env);
- int trapnr, signum, sigcode;
- target_ulong sigaddr;
+ int trapnr;
target_ulong syscallnum;
target_ulong ret;
@@ -39,10 +38,6 @@ void cpu_loop(CPUHexagonState *env)
cpu_exec_end(cs);
process_queued_cpu_work(cs);
- signum = 0;
- sigcode = 0;
- sigaddr = 0;
-
switch (trapnr) {
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
@@ -65,12 +60,6 @@ void cpu_loop(CPUHexagonState *env)
env->gpr[0] = ret;
}
break;
- case HEX_EXCP_FETCH_NO_UPAGE:
- case HEX_EXCP_PRIV_NO_UREAD:
- case HEX_EXCP_PRIV_NO_UWRITE:
- signum = TARGET_SIGSEGV;
- sigcode = TARGET_SEGV_MAPERR;
- break;
case EXCP_ATOMIC:
cpu_exec_step_atomic(cs);
break;
@@ -79,17 +68,6 @@ void cpu_loop(CPUHexagonState *env)
trapnr);
exit(EXIT_FAILURE);
}
-
- if (signum) {
- target_siginfo_t info = {
- .si_signo = signum,
- .si_errno = 0,
- .si_code = sigcode,
- ._sifields._sigfault._addr = sigaddr
- };
- queue_signal(env, info.si_signo, QEMU_SI_KILL, &info);
- }
-
process_pending_signals(env);
}
}
diff --git a/linux-user/host/aarch64/host-signal.h b/linux-user/host/aarch64/host-signal.h
new file mode 100644
index 0000000000..0c0b08383a
--- /dev/null
+++ b/linux-user/host/aarch64/host-signal.h
@@ -0,0 +1,74 @@
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef AARCH64_HOST_SIGNAL_H
+#define AARCH64_HOST_SIGNAL_H
+
+/* Pre-3.16 kernel headers don't have these, so provide fallback definitions */
+#ifndef ESR_MAGIC
+#define ESR_MAGIC 0x45535201
+struct esr_context {
+ struct _aarch64_ctx head;
+ uint64_t esr;
+};
+#endif
+
+static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc)
+{
+ return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved;
+}
+
+static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr)
+{
+ return (struct _aarch64_ctx *)((char *)hdr + hdr->size);
+}
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.pc;
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ struct _aarch64_ctx *hdr;
+ uint32_t insn;
+
+ /* Find the esr_context, which has the WnR bit in it */
+ for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) {
+ if (hdr->magic == ESR_MAGIC) {
+ struct esr_context const *ec = (struct esr_context const *)hdr;
+ uint64_t esr = ec->esr;
+
+ /* For data aborts ESR.EC is 0b10010x: then bit 6 is the WnR bit */
+ return extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1;
+ }
+ }
+
+ /*
+ * Fall back to parsing instructions; will only be needed
+ * for really ancient (pre-3.16) kernels.
+ */
+ insn = *(uint32_t *)host_signal_pc(uc);
+
+ return (insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */
+ || (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */
+ || (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */
+ || (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */
+ || (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */
+ || (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */
+ || (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */
+ /* Ignore bits 10, 11 & 21, controlling indexing. */
+ || (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */
+ || (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */
+ /* Ignore bits 23 & 24, controlling indexing. */
+ || (insn & 0x3a400000) == 0x28000000; /* C3.3.7,14-16 */
+}
+
+#endif
diff --git a/linux-user/host/alpha/host-signal.h b/linux-user/host/alpha/host-signal.h
new file mode 100644
index 0000000000..e080be412f
--- /dev/null
+++ b/linux-user/host/alpha/host-signal.h
@@ -0,0 +1,42 @@
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef ALPHA_HOST_SIGNAL_H
+#define ALPHA_HOST_SIGNAL_H
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.sc_pc;
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ uint32_t *pc = (uint32_t *)host_signal_pc(uc);
+ uint32_t insn = *pc;
+
+ /* XXX: need kernel patch to get write flag faster */
+ switch (insn >> 26) {
+ case 0x0d: /* stw */
+ case 0x0e: /* stb */
+ case 0x0f: /* stq_u */
+ case 0x24: /* stf */
+ case 0x25: /* stg */
+ case 0x26: /* sts */
+ case 0x27: /* stt */
+ case 0x2c: /* stl */
+ case 0x2d: /* stq */
+ case 0x2e: /* stl_c */
+ case 0x2f: /* stq_c */
+ return true;
+ }
+ return false;
+}
+
+#endif
diff --git a/linux-user/host/arm/host-signal.h b/linux-user/host/arm/host-signal.h
new file mode 100644
index 0000000000..efb165c0c5
--- /dev/null
+++ b/linux-user/host/arm/host-signal.h
@@ -0,0 +1,30 @@
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef ARM_HOST_SIGNAL_H
+#define ARM_HOST_SIGNAL_H
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.arm_pc;
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ /*
+ * In the FSR, bit 11 is WnR, assuming a v6 or
+ * later processor. On v5 we will always report
+ * this as a read, which will fail later.
+ */
+ uint32_t fsr = uc->uc_mcontext.error_code;
+ return extract32(fsr, 11, 1);
+}
+
+#endif
diff --git a/linux-user/host/i386/host-signal.h b/linux-user/host/i386/host-signal.h
new file mode 100644
index 0000000000..4c8eef99ce
--- /dev/null
+++ b/linux-user/host/i386/host-signal.h
@@ -0,0 +1,25 @@
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef I386_HOST_SIGNAL_H
+#define I386_HOST_SIGNAL_H
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.gregs[REG_EIP];
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ return uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe
+ && (uc->uc_mcontext.gregs[REG_ERR] & 0x2);
+}
+
+#endif
diff --git a/linux-user/host/mips/host-signal.h b/linux-user/host/mips/host-signal.h
new file mode 100644
index 0000000000..ef341f7c20
--- /dev/null
+++ b/linux-user/host/mips/host-signal.h
@@ -0,0 +1,62 @@
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef MIPS_HOST_SIGNAL_H
+#define MIPS_HOST_SIGNAL_H
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.pc;
+}
+
+#if defined(__misp16) || defined(__mips_micromips)
+#error "Unsupported encoding"
+#endif
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ uint32_t insn = *(uint32_t *)host_signal_pc(uc);
+
+ /* Detect all store instructions at program counter. */
+ switch ((insn >> 26) & 077) {
+ case 050: /* SB */
+ case 051: /* SH */
+ case 052: /* SWL */
+ case 053: /* SW */
+ case 054: /* SDL */
+ case 055: /* SDR */
+ case 056: /* SWR */
+ case 070: /* SC */
+ case 071: /* SWC1 */
+ case 074: /* SCD */
+ case 075: /* SDC1 */
+ case 077: /* SD */
+#if !defined(__mips_isa_rev) || __mips_isa_rev < 6
+ case 072: /* SWC2 */
+ case 076: /* SDC2 */
+#endif
+ return true;
+ case 023: /* COP1X */
+ /*
+ * Required in all versions of MIPS64 since
+ * MIPS64r1 and subsequent versions of MIPS32r2.
+ */
+ switch (insn & 077) {
+ case 010: /* SWXC1 */
+ case 011: /* SDXC1 */
+ case 015: /* SUXC1 */
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+#endif
diff --git a/linux-user/host/ppc/host-signal.h b/linux-user/host/ppc/host-signal.h
new file mode 100644
index 0000000000..a491c413dc
--- /dev/null
+++ b/linux-user/host/ppc/host-signal.h
@@ -0,0 +1,25 @@
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef PPC_HOST_SIGNAL_H
+#define PPC_HOST_SIGNAL_H
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.regs->nip;
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ return uc->uc_mcontext.regs->trap != 0x400
+ && (uc->uc_mcontext.regs->dsisr & 0x02000000);
+}
+
+#endif
diff --git a/linux-user/host/ppc64/host-signal.h b/linux-user/host/ppc64/host-signal.h
new file mode 100644
index 0000000000..a353c22a90
--- /dev/null
+++ b/linux-user/host/ppc64/host-signal.h
@@ -0,0 +1 @@
+#include "../ppc/host-signal.h"
diff --git a/linux-user/host/riscv/host-signal.h b/linux-user/host/riscv/host-signal.h
new file mode 100644
index 0000000000..3b168cb58b
--- /dev/null
+++ b/linux-user/host/riscv/host-signal.h
@@ -0,0 +1,58 @@
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef RISCV_HOST_SIGNAL_H
+#define RISCV_HOST_SIGNAL_H
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.__gregs[REG_PC];
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ /*
+ * Detect store by reading the instruction at the program counter.
+ * Do not read more than 16 bits, because we have not yet determined
+ * the size of the instruction.
+ */
+ const uint16_t *pinsn = (const uint16_t *)host_signal_pc(uc);
+ uint16_t insn = pinsn[0];
+
+ /* 16-bit instructions */
+ switch (insn & 0xe003) {
+ case 0xa000: /* c.fsd */
+ case 0xc000: /* c.sw */
+ case 0xe000: /* c.sd (rv64) / c.fsw (rv32) */
+ case 0xa002: /* c.fsdsp */
+ case 0xc002: /* c.swsp */
+ case 0xe002: /* c.sdsp (rv64) / c.fswsp (rv32) */
+ return true;
+ }
+
+ /* 32-bit instructions, major opcodes */
+ switch (insn & 0x7f) {
+ case 0x23: /* store */
+ case 0x27: /* store-fp */
+ return true;
+ case 0x2f: /* amo */
+ /*
+ * The AMO function code is in bits 25-31, unread as yet.
+ * The AMO functions are LR (read), SC (write), and the
+ * rest are all read-modify-write.
+ */
+ insn = pinsn[1];
+ return (insn >> 11) != 2; /* LR */
+ }
+
+ return false;
+}
+
+#endif
diff --git a/linux-user/host/riscv64/hostdep.h b/linux-user/host/riscv/hostdep.h
index 865f0fb9ff..2ba07456ae 100644
--- a/linux-user/host/riscv64/hostdep.h
+++ b/linux-user/host/riscv/hostdep.h
@@ -5,8 +5,8 @@
* See the COPYING file in the top-level directory.
*/
-#ifndef RISCV64_HOSTDEP_H
-#define RISCV64_HOSTDEP_H
+#ifndef RISCV_HOSTDEP_H
+#define RISCV_HOSTDEP_H
/* We have a safe-syscall.inc.S */
#define HAVE_SAFE_SYSCALL
diff --git a/linux-user/host/riscv64/safe-syscall.inc.S b/linux-user/host/riscv/safe-syscall.inc.S
index 9ca3fbfd1e..9ca3fbfd1e 100644
--- a/linux-user/host/riscv64/safe-syscall.inc.S
+++ b/linux-user/host/riscv/safe-syscall.inc.S
diff --git a/linux-user/host/riscv32/hostdep.h b/linux-user/host/riscv32/hostdep.h
deleted file mode 100644
index adf9edbf2d..0000000000
--- a/linux-user/host/riscv32/hostdep.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * hostdep.h : things which are dependent on the host architecture
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef RISCV32_HOSTDEP_H
-#define RISCV32_HOSTDEP_H
-
-#endif
diff --git a/linux-user/host/s390/host-signal.h b/linux-user/host/s390/host-signal.h
new file mode 100644
index 0000000000..26990e4893
--- /dev/null
+++ b/linux-user/host/s390/host-signal.h
@@ -0,0 +1,93 @@
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef S390_HOST_SIGNAL_H
+#define S390_HOST_SIGNAL_H
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.psw.addr;
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ uint16_t *pinsn = (uint16_t *)host_signal_pc(uc);
+
+ /*
+ * ??? On linux, the non-rt signal handler has 4 (!) arguments instead
+ * of the normal 2 arguments. The 4th argument contains the "Translation-
+ * Exception Identification for DAT Exceptions" from the hardware (aka
+ * "int_parm_long"), which does in fact contain the is_write value.
+ * The rt signal handler, as far as I can tell, does not give this value
+ * at all. Not that we could get to it from here even if it were.
+ * So fall back to parsing instructions. Treat read-modify-write ones as
+ * writes, which is not fully correct, but for tracking self-modifying code
+ * this is better than treating them as reads. Checking si_addr page flags
+ * might be a viable improvement, albeit a racy one.
+ */
+ /* ??? This is not even close to complete. */
+ switch (pinsn[0] >> 8) {
+ case 0x50: /* ST */
+ case 0x42: /* STC */
+ case 0x40: /* STH */
+ case 0xba: /* CS */
+ case 0xbb: /* CDS */
+ return true;
+ case 0xc4: /* RIL format insns */
+ switch (pinsn[0] & 0xf) {
+ case 0xf: /* STRL */
+ case 0xb: /* STGRL */
+ case 0x7: /* STHRL */
+ return true;
+ }
+ break;
+ case 0xc8: /* SSF format insns */
+ switch (pinsn[0] & 0xf) {
+ case 0x2: /* CSST */
+ return true;
+ }
+ break;
+ case 0xe3: /* RXY format insns */
+ switch (pinsn[2] & 0xff) {
+ case 0x50: /* STY */
+ case 0x24: /* STG */
+ case 0x72: /* STCY */
+ case 0x70: /* STHY */
+ case 0x8e: /* STPQ */
+ case 0x3f: /* STRVH */
+ case 0x3e: /* STRV */
+ case 0x2f: /* STRVG */
+ return true;
+ }
+ break;
+ case 0xeb: /* RSY format insns */
+ switch (pinsn[2] & 0xff) {
+ case 0x14: /* CSY */
+ case 0x30: /* CSG */
+ case 0x31: /* CDSY */
+ case 0x3e: /* CDSG */
+ case 0xe4: /* LANG */
+ case 0xe6: /* LAOG */
+ case 0xe7: /* LAXG */
+ case 0xe8: /* LAAG */
+ case 0xea: /* LAALG */
+ case 0xf4: /* LAN */
+ case 0xf6: /* LAO */
+ case 0xf7: /* LAX */
+ case 0xfa: /* LAAL */
+ case 0xf8: /* LAA */
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+#endif
diff --git a/linux-user/host/s390x/host-signal.h b/linux-user/host/s390x/host-signal.h
new file mode 100644
index 0000000000..0e83f9358d
--- /dev/null
+++ b/linux-user/host/s390x/host-signal.h
@@ -0,0 +1 @@
+#include "../s390/host-signal.h"
diff --git a/linux-user/host/sparc/host-signal.h b/linux-user/host/sparc/host-signal.h
new file mode 100644
index 0000000000..5e71d33f8e
--- /dev/null
+++ b/linux-user/host/sparc/host-signal.h
@@ -0,0 +1,54 @@
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef SPARC_HOST_SIGNAL_H
+#define SPARC_HOST_SIGNAL_H
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+#ifdef __arch64__
+ return uc->uc_mcontext.mc_gregs[MC_PC];
+#else
+ return uc->uc_mcontext.gregs[REG_PC];
+#endif
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ uint32_t insn = *(uint32_t *)host_signal_pc(uc);
+
+ if ((insn >> 30) == 3) {
+ switch ((insn >> 19) & 0x3f) {
+ case 0x05: /* stb */
+ case 0x15: /* stba */
+ case 0x06: /* sth */
+ case 0x16: /* stha */
+ case 0x04: /* st */
+ case 0x14: /* sta */
+ case 0x07: /* std */
+ case 0x17: /* stda */
+ case 0x0e: /* stx */
+ case 0x1e: /* stxa */
+ case 0x24: /* stf */
+ case 0x34: /* stfa */
+ case 0x27: /* stdf */
+ case 0x37: /* stdfa */
+ case 0x26: /* stqf */
+ case 0x36: /* stqfa */
+ case 0x25: /* stfsr */
+ case 0x3c: /* casa */
+ case 0x3e: /* casxa */
+ return true;
+ }
+ }
+ return false;
+}
+
+#endif
diff --git a/linux-user/host/sparc64/host-signal.h b/linux-user/host/sparc64/host-signal.h
new file mode 100644
index 0000000000..1191fe2d40
--- /dev/null
+++ b/linux-user/host/sparc64/host-signal.h
@@ -0,0 +1 @@
+#include "../sparc/host-signal.h"
diff --git a/linux-user/host/x32/host-signal.h b/linux-user/host/x32/host-signal.h
new file mode 100644
index 0000000000..26800591d3
--- /dev/null
+++ b/linux-user/host/x32/host-signal.h
@@ -0,0 +1 @@
+#include "../x86_64/host-signal.h"
diff --git a/linux-user/host/x86_64/host-signal.h b/linux-user/host/x86_64/host-signal.h
new file mode 100644
index 0000000000..883d2fcf65
--- /dev/null
+++ b/linux-user/host/x86_64/host-signal.h
@@ -0,0 +1,24 @@
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (C) 2021 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef X86_64_HOST_SIGNAL_H
+#define X86_64_HOST_SIGNAL_H
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.gregs[REG_RIP];
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ return uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe
+ && (uc->uc_mcontext.gregs[REG_ERR] & 0x2);
+}
+
+#endif
diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c
index 81607a9b27..375576c8f0 100644
--- a/linux-user/hppa/cpu_loop.c
+++ b/linux-user/hppa/cpu_loop.c
@@ -144,29 +144,6 @@ void cpu_loop(CPUHPPAState *env)
env->iaoq_f = env->gr[31];
env->iaoq_b = env->gr[31] + 4;
break;
- case EXCP_ITLB_MISS:
- case EXCP_DTLB_MISS:
- case EXCP_NA_ITLB_MISS:
- case EXCP_NA_DTLB_MISS:
- case EXCP_IMP:
- case EXCP_DMP:
- case EXCP_DMB:
- case EXCP_PAGE_REF:
- case EXCP_DMAR:
- case EXCP_DMPI:
- info.si_signo = TARGET_SIGSEGV;
- info.si_errno = 0;
- info.si_code = TARGET_SEGV_ACCERR;
- info._sifields._sigfault._addr = env->cr[CR_IOR];
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- break;
- case EXCP_UNALIGN:
- info.si_signo = TARGET_SIGBUS;
- info.si_errno = 0;
- info.si_code = 0;
- info._sifields._sigfault._addr = env->cr[CR_IOR];
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- break;
case EXCP_ILL:
case EXCP_PRIV_OPR:
case EXCP_PRIV_REG:
diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c
index ebf32be78f..790bd558c3 100644
--- a/linux-user/m68k/cpu_loop.c
+++ b/linux-user/m68k/cpu_loop.c
@@ -90,16 +90,6 @@ void cpu_loop(CPUM68KState *env)
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
break;
- case EXCP_ACCESS:
- {
- info.si_signo = TARGET_SIGSEGV;
- info.si_errno = 0;
- /* XXX: check env->error_code */
- info.si_code = TARGET_SEGV_MAPERR;
- info._sifields._sigfault._addr = env->mmu.ar;
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- }
- break;
case EXCP_DEBUG:
info.si_signo = TARGET_SIGTRAP;
info.si_errno = 0;
diff --git a/linux-user/microblaze/cpu_loop.c b/linux-user/microblaze/cpu_loop.c
index 52222eb93f..a94467dd2d 100644
--- a/linux-user/microblaze/cpu_loop.c
+++ b/linux-user/microblaze/cpu_loop.c
@@ -37,16 +37,6 @@ void cpu_loop(CPUMBState *env)
process_queued_cpu_work(cs);
switch (trapnr) {
- case 0xaa:
- {
- info.si_signo = TARGET_SIGSEGV;
- info.si_errno = 0;
- /* XXX: check env->error_code */
- info.si_code = TARGET_SEGV_MAPERR;
- info._sifields._sigfault._addr = 0;
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- }
- break;
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
break;
diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c
index cb03fb066b..b735c99a24 100644
--- a/linux-user/mips/cpu_loop.c
+++ b/linux-user/mips/cpu_loop.c
@@ -158,17 +158,6 @@ done_syscall:
}
env->active_tc.gpr[2] = ret;
break;
- case EXCP_TLBL:
- case EXCP_TLBS:
- case EXCP_AdEL:
- case EXCP_AdES:
- info.si_signo = TARGET_SIGSEGV;
- info.si_errno = 0;
- /* XXX: check env->error_code */
- info.si_code = TARGET_SEGV_MAPERR;
- info._sifields._sigfault._addr = env->CP0_BadVAddr;
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- break;
case EXCP_CpU:
case EXCP_RI:
info.si_signo = TARGET_SIGILL;
diff --git a/linux-user/openrisc/cpu_loop.c b/linux-user/openrisc/cpu_loop.c
index f6360db47c..3cfdbbf037 100644
--- a/linux-user/openrisc/cpu_loop.c
+++ b/linux-user/openrisc/cpu_loop.c
@@ -54,15 +54,6 @@ void cpu_loop(CPUOpenRISCState *env)
cpu_set_gpr(env, 11, ret);
}
break;
- case EXCP_DPF:
- case EXCP_IPF:
- case EXCP_RANGE:
- info.si_signo = TARGET_SIGSEGV;
- info.si_errno = 0;
- info.si_code = TARGET_SEGV_MAPERR;
- info._sifields._sigfault._addr = env->pc;
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- break;
case EXCP_ALIGN:
info.si_signo = TARGET_SIGBUS;
info.si_errno = 0;
@@ -77,13 +68,6 @@ void cpu_loop(CPUOpenRISCState *env)
info._sifields._sigfault._addr = env->pc;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
- case EXCP_FPE:
- info.si_signo = TARGET_SIGFPE;
- info.si_errno = 0;
- info.si_code = 0;
- info._sifields._sigfault._addr = env->pc;
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- break;
case EXCP_INTERRUPT:
/* We processed the pending cpu work above. */
break;
@@ -96,6 +80,15 @@ void cpu_loop(CPUOpenRISCState *env)
case EXCP_ATOMIC:
cpu_exec_step_atomic(cs);
break;
+ case EXCP_RANGE:
+ /* Requires SR.OVE set, which linux-user won't do. */
+ cpu_abort(cs, "Unexpected RANGE exception");
+ case EXCP_FPE:
+ /*
+ * Requires FPSCR.FPEE set. Writes to FPSCR from usermode not
+ * yet enabled in kernel ABI, so linux-user does not either.
+ */
+ cpu_abort(cs, "Unexpected FPE exception");
default:
g_assert_not_reached();
}
diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c
index 840b23736b..483e669300 100644
--- a/linux-user/ppc/cpu_loop.c
+++ b/linux-user/ppc/cpu_loop.c
@@ -162,14 +162,6 @@ void cpu_loop(CPUPPCState *env)
cpu_abort(cs, "External interrupt while in user mode. "
"Aborting\n");
break;
- case POWERPC_EXCP_ALIGN: /* Alignment exception */
- /* XXX: check this */
- info.si_signo = TARGET_SIGBUS;
- info.si_errno = 0;
- info.si_code = TARGET_BUS_ADRALN;
- info._sifields._sigfault._addr = env->nip;
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- break;
case POWERPC_EXCP_PROGRAM: /* Program exception */
case POWERPC_EXCP_HV_EMU: /* HV emulation */
/* XXX: check this */
diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c
index e5bb6d908a..b301dac802 100644
--- a/linux-user/riscv/cpu_loop.c
+++ b/linux-user/riscv/cpu_loop.c
@@ -87,13 +87,6 @@ void cpu_loop(CPURISCVState *env)
sigcode = TARGET_TRAP_BRKPT;
sigaddr = env->pc;
break;
- case RISCV_EXCP_INST_PAGE_FAULT:
- case RISCV_EXCP_LOAD_PAGE_FAULT:
- case RISCV_EXCP_STORE_PAGE_FAULT:
- signum = TARGET_SIGSEGV;
- sigcode = TARGET_SEGV_MAPERR;
- sigaddr = env->badaddr;
- break;
case RISCV_EXCP_SEMIHOST:
env->gpr[xA0] = do_common_semihosting(cs);
env->pc += 4;
diff --git a/linux-user/s390x/cpu_loop.c b/linux-user/s390x/cpu_loop.c
index 69b69981f6..d089c8417e 100644
--- a/linux-user/s390x/cpu_loop.c
+++ b/linux-user/s390x/cpu_loop.c
@@ -24,8 +24,6 @@
#include "cpu_loop-common.h"
#include "signal-common.h"
-/* s390x masks the fault address it reports in si_addr for SIGSEGV and SIGBUS */
-#define S390X_FAIL_ADDR_MASK -4096LL
static int get_pgm_data_si_code(int dxc_code)
{
@@ -111,12 +109,13 @@ void cpu_loop(CPUS390XState *env)
n = TARGET_ILL_ILLOPC;
goto do_signal_pc;
case PGM_PROTECTION:
+ force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_ACCERR,
+ env->__excp_addr);
+ break;
case PGM_ADDRESSING:
- sig = TARGET_SIGSEGV;
- /* XXX: check env->error_code */
- n = TARGET_SEGV_MAPERR;
- addr = env->__excp_addr & S390X_FAIL_ADDR_MASK;
- goto do_signal;
+ force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR,
+ env->__excp_addr);
+ break;
case PGM_EXECUTE:
case PGM_SPECIFICATION:
case PGM_SPECIAL_OP:
diff --git a/linux-user/sh4/cpu_loop.c b/linux-user/sh4/cpu_loop.c
index 65b8972e3c..ac9b01840c 100644
--- a/linux-user/sh4/cpu_loop.c
+++ b/linux-user/sh4/cpu_loop.c
@@ -65,14 +65,6 @@ void cpu_loop(CPUSH4State *env)
info.si_code = TARGET_TRAP_BRKPT;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
- case 0xa0:
- case 0xc0:
- info.si_signo = TARGET_SIGSEGV;
- info.si_errno = 0;
- info.si_code = TARGET_SEGV_MAPERR;
- info._sifields._sigfault._addr = env->tea;
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- break;
case EXCP_ATOMIC:
cpu_exec_step_atomic(cs);
arch_interrupt = false;
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 14d8fdfde1..81c45bfce9 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -19,6 +19,7 @@
#include "qemu/osdep.h"
#include "qemu/bitops.h"
#include "exec/gdbstub.h"
+#include "hw/core/tcg-cpu-ops.h"
#include <sys/ucontext.h>
#include <sys/resource.h>
@@ -29,6 +30,7 @@
#include "loader.h"
#include "trace.h"
#include "signal-common.h"
+#include "host-signal.h"
static struct target_sigaction sigact_table[TARGET_NSIG];
@@ -686,9 +688,38 @@ void force_sigsegv(int oldsig)
}
force_sig(TARGET_SIGSEGV);
}
-
#endif
+void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr,
+ MMUAccessType access_type, bool maperr, uintptr_t ra)
+{
+ 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,
+ MMUAccessType access_type, uintptr_t ra)
+{
+ 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);
+}
+
/* abort execution with signal */
static void QEMU_NORETURN dump_core_and_abort(int target_sig)
{
@@ -769,41 +800,101 @@ static inline void rewind_if_in_safe_syscall(void *puc)
}
#endif
-static void host_signal_handler(int host_signum, siginfo_t *info,
- void *puc)
+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;
-
- int sig;
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);
- /* the CPU emulator uses some host signals to detect exceptions,
- we forward to it some signals */
- if ((host_signum == SIGSEGV || host_signum == SIGBUS)
- && info->si_code > 0) {
- if (cpu_signal_handler(host_signum, info, puc))
- return;
+ 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 target signal number */
- sig = host_to_target_signal(host_signum);
- if (sig < 1 || sig > TARGET_NSIG)
+ guest_sig = host_to_target_signal(host_sig);
+ if (guest_sig < 1 || guest_sig > TARGET_NSIG) {
return;
- trace_user_host_signal(env, host_signum, sig);
-
- rewind_if_in_safe_syscall(puc);
+ }
+ trace_user_host_signal(env, host_sig, guest_sig);
host_to_target_siginfo_noswap(&tinfo, info);
- k = &ts->sigtab[sig - 1];
+ k = &ts->sigtab[guest_sig - 1];
k->info = tinfo;
- k->pending = sig;
+ k->pending = guest_sig;
ts->signal_pending = 1;
- /* Block host signals until target signal handler entered. We
+ /*
+ * 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
diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c
index ad29b4eb6a..0ba65e431c 100644
--- a/linux-user/sparc/cpu_loop.c
+++ b/linux-user/sparc/cpu_loop.c
@@ -219,17 +219,6 @@ void cpu_loop (CPUSPARCState *env)
case TT_WIN_UNF: /* window underflow */
restore_window(env);
break;
- case TT_TFAULT:
- case TT_DFAULT:
- {
- info.si_signo = TARGET_SIGSEGV;
- info.si_errno = 0;
- /* XXX: check env->error_code */
- info.si_code = TARGET_SEGV_MAPERR;
- info._sifields._sigfault._addr = env->mmuregs[4];
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- }
- break;
#else
case TT_SPILL: /* window overflow */
save_window(env);
@@ -237,20 +226,6 @@ void cpu_loop (CPUSPARCState *env)
case TT_FILL: /* window underflow */
restore_window(env);
break;
- case TT_TFAULT:
- case TT_DFAULT:
- {
- info.si_signo = TARGET_SIGSEGV;
- info.si_errno = 0;
- /* XXX: check env->error_code */
- info.si_code = TARGET_SEGV_MAPERR;
- if (trapnr == TT_DFAULT)
- info._sifields._sigfault._addr = env->dmmu.mmuregs[4];
- else
- info._sifields._sigfault._addr = cpu_tsptr(env)->tpc;
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- }
- break;
#ifndef TARGET_ABI32
case 0x16e:
flush_windows(env);
diff --git a/linux-user/xtensa/cpu_loop.c b/linux-user/xtensa/cpu_loop.c
index 622afbcd34..a83490ab35 100644
--- a/linux-user/xtensa/cpu_loop.c
+++ b/linux-user/xtensa/cpu_loop.c
@@ -226,15 +226,6 @@ void cpu_loop(CPUXtensaState *env)
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
- case LOAD_PROHIBITED_CAUSE:
- case STORE_PROHIBITED_CAUSE:
- info.si_signo = TARGET_SIGSEGV;
- info.si_errno = 0;
- info.si_code = TARGET_SEGV_ACCERR;
- info._sifields._sigfault._addr = env->sregs[EXCVADDR];
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- break;
-
default:
fprintf(stderr, "exccause = %d\n", env->sregs[EXCCAUSE]);
g_assert_not_reached();
diff --git a/meson.build b/meson.build
index 85f1e43dfe..bc918fbe58 100644
--- a/meson.build
+++ b/meson.build
@@ -55,7 +55,7 @@ have_block = have_system or have_tools
python = import('python').find_installation()
supported_oses = ['windows', 'freebsd', 'netbsd', 'openbsd', 'darwin', 'sunos', 'linux']
-supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv32', 'riscv64', 'x86', 'x86_64',
+supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv', 'x86', 'x86_64',
'arm', 'aarch64', 'mips', 'mips64', 'sparc', 'sparc64']
cpu = host_machine.cpu_family()
@@ -351,8 +351,6 @@ if not get_option('tcg').disabled()
tcg_arch = 'i386'
elif config_host['ARCH'] == 'ppc64'
tcg_arch = 'ppc'
- elif config_host['ARCH'] in ['riscv32', 'riscv64']
- tcg_arch = 'riscv'
endif
add_project_arguments('-iquote', meson.current_source_dir() / 'tcg' / tcg_arch,
language: ['c', 'cpp', 'objc'])
diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c
index 93e16a2ffb..a8990d401b 100644
--- a/target/alpha/cpu.c
+++ b/target/alpha/cpu.c
@@ -218,9 +218,12 @@ static const struct SysemuCPUOps alpha_sysemu_ops = {
static const struct TCGCPUOps alpha_tcg_ops = {
.initialize = alpha_translate_init,
- .tlb_fill = alpha_cpu_tlb_fill,
-#ifndef CONFIG_USER_ONLY
+#ifdef CONFIG_USER_ONLY
+ .record_sigsegv = alpha_cpu_record_sigsegv,
+ .record_sigbus = alpha_cpu_record_sigbus,
+#else
+ .tlb_fill = alpha_cpu_tlb_fill,
.cpu_exec_interrupt = alpha_cpu_exec_interrupt,
.do_interrupt = alpha_cpu_do_interrupt,
.do_transaction_failed = alpha_cpu_do_transaction_failed,
diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h
index 772828cc26..afd975c878 100644
--- a/target/alpha/cpu.h
+++ b/target/alpha/cpu.h
@@ -282,9 +282,6 @@ void alpha_cpu_dump_state(CPUState *cs, FILE *f, int flags);
hwaddr alpha_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
int alpha_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
int alpha_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
-void alpha_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
- MMUAccessType access_type, int mmu_idx,
- uintptr_t retaddr) QEMU_NORETURN;
#define cpu_list alpha_cpu_list
@@ -439,9 +436,6 @@ void alpha_translate_init(void);
#define CPU_RESOLVING_TYPE TYPE_ALPHA_CPU
void alpha_cpu_list(void);
-bool alpha_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr);
void QEMU_NORETURN dynamic_excp(CPUAlphaState *, uintptr_t, int, int);
void QEMU_NORETURN arith_excp(CPUAlphaState *, uintptr_t, int, uint64_t);
@@ -449,7 +443,20 @@ uint64_t cpu_alpha_load_fpcr (CPUAlphaState *env);
void cpu_alpha_store_fpcr (CPUAlphaState *env, uint64_t val);
uint64_t cpu_alpha_load_gr(CPUAlphaState *env, unsigned reg);
void cpu_alpha_store_gr(CPUAlphaState *env, unsigned reg, uint64_t val);
-#ifndef CONFIG_USER_ONLY
+
+#ifdef CONFIG_USER_ONLY
+void alpha_cpu_record_sigsegv(CPUState *cs, vaddr address,
+ MMUAccessType access_type,
+ bool maperr, uintptr_t retaddr);
+void alpha_cpu_record_sigbus(CPUState *cs, vaddr address,
+ MMUAccessType access_type, uintptr_t retaddr);
+#else
+bool alpha_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
+ MMUAccessType access_type, int mmu_idx,
+ bool probe, uintptr_t retaddr);
+void alpha_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
+ MMUAccessType access_type, int mmu_idx,
+ uintptr_t retaddr) QEMU_NORETURN;
void alpha_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
vaddr addr, unsigned size,
MMUAccessType access_type,
diff --git a/target/alpha/helper.c b/target/alpha/helper.c
index 81550d9e2f..b7e7f73b15 100644
--- a/target/alpha/helper.c
+++ b/target/alpha/helper.c
@@ -120,15 +120,44 @@ void cpu_alpha_store_gr(CPUAlphaState *env, unsigned reg, uint64_t val)
}
#if defined(CONFIG_USER_ONLY)
-bool alpha_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr)
+void alpha_cpu_record_sigsegv(CPUState *cs, vaddr address,
+ MMUAccessType access_type,
+ bool maperr, uintptr_t retaddr)
{
AlphaCPU *cpu = ALPHA_CPU(cs);
+ target_ulong mmcsr, cause;
+
+ /* Assuming !maperr, infer the missing protection. */
+ switch (access_type) {
+ case MMU_DATA_LOAD:
+ mmcsr = MM_K_FOR;
+ cause = 0;
+ break;
+ case MMU_DATA_STORE:
+ mmcsr = MM_K_FOW;
+ cause = 1;
+ break;
+ case MMU_INST_FETCH:
+ mmcsr = MM_K_FOE;
+ cause = -1;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ if (maperr) {
+ if (address < BIT_ULL(TARGET_VIRT_ADDR_SPACE_BITS - 1)) {
+ /* Userspace address, therefore page not mapped. */
+ mmcsr = MM_K_TNV;
+ } else {
+ /* Kernel or invalid address. */
+ mmcsr = MM_K_ACV;
+ }
+ }
- cs->exception_index = EXCP_MMFAULT;
+ /* Record the arguments that PALcode would give to the kernel. */
cpu->env.trap_arg0 = address;
- cpu_loop_exit_restore(cs, retaddr);
+ cpu->env.trap_arg1 = mmcsr;
+ cpu->env.trap_arg2 = cause;
}
#else
/* Returns the OSF/1 entMM failure indication, or -1 on success. */
diff --git a/target/alpha/mem_helper.c b/target/alpha/mem_helper.c
index 75e72bc337..47283a0612 100644
--- a/target/alpha/mem_helper.c
+++ b/target/alpha/mem_helper.c
@@ -23,18 +23,12 @@
#include "exec/exec-all.h"
#include "exec/cpu_ldst.h"
-/* Softmmu support */
-#ifndef CONFIG_USER_ONLY
-void alpha_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
- MMUAccessType access_type,
- int mmu_idx, uintptr_t retaddr)
+static void do_unaligned_access(CPUAlphaState *env, vaddr addr, uintptr_t retaddr)
{
- AlphaCPU *cpu = ALPHA_CPU(cs);
- CPUAlphaState *env = &cpu->env;
uint64_t pc;
uint32_t insn;
- cpu_restore_state(cs, retaddr, true);
+ cpu_restore_state(env_cpu(env), retaddr, true);
pc = env->pc;
insn = cpu_ldl_code(env, pc);
@@ -42,6 +36,26 @@ void alpha_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
env->trap_arg0 = addr;
env->trap_arg1 = insn >> 26; /* opcode */
env->trap_arg2 = (insn >> 21) & 31; /* dest regno */
+}
+
+#ifdef CONFIG_USER_ONLY
+void alpha_cpu_record_sigbus(CPUState *cs, vaddr addr,
+ MMUAccessType access_type, uintptr_t retaddr)
+{
+ AlphaCPU *cpu = ALPHA_CPU(cs);
+ CPUAlphaState *env = &cpu->env;
+
+ do_unaligned_access(env, addr, retaddr);
+}
+#else
+void alpha_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
+ MMUAccessType access_type,
+ int mmu_idx, uintptr_t retaddr)
+{
+ AlphaCPU *cpu = ALPHA_CPU(cs);
+ CPUAlphaState *env = &cpu->env;
+
+ do_unaligned_access(env, addr, retaddr);
cs->exception_index = EXCP_UNALIGN;
env->error_code = 0;
cpu_loop_exit(cs);
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 641a8c2d3d..a211804fd3 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -2031,10 +2031,13 @@ static const struct SysemuCPUOps arm_sysemu_ops = {
static const struct TCGCPUOps arm_tcg_ops = {
.initialize = arm_translate_init,
.synchronize_from_tb = arm_cpu_synchronize_from_tb,
- .tlb_fill = arm_cpu_tlb_fill,
.debug_excp_handler = arm_debug_excp_handler,
-#if !defined(CONFIG_USER_ONLY)
+#ifdef CONFIG_USER_ONLY
+ .record_sigsegv = arm_cpu_record_sigsegv,
+ .record_sigbus = arm_cpu_record_sigbus,
+#else
+ .tlb_fill = arm_cpu_tlb_fill,
.cpu_exec_interrupt = arm_cpu_exec_interrupt,
.do_interrupt = arm_cpu_do_interrupt,
.do_transaction_failed = arm_cpu_do_transaction_failed,
diff --git a/target/arm/cpu_tcg.c b/target/arm/cpu_tcg.c
index 0d5adccf1a..13d0e9b195 100644
--- a/target/arm/cpu_tcg.c
+++ b/target/arm/cpu_tcg.c
@@ -898,10 +898,13 @@ static void pxa270c5_initfn(Object *obj)
static const struct TCGCPUOps arm_v7m_tcg_ops = {
.initialize = arm_translate_init,
.synchronize_from_tb = arm_cpu_synchronize_from_tb,
- .tlb_fill = arm_cpu_tlb_fill,
.debug_excp_handler = arm_debug_excp_handler,
-#if !defined(CONFIG_USER_ONLY)
+#ifdef CONFIG_USER_ONLY
+ .record_sigsegv = arm_cpu_record_sigsegv,
+ .record_sigbus = arm_cpu_record_sigbus,
+#else
+ .tlb_fill = arm_cpu_tlb_fill,
.cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt,
.do_interrupt = arm_v7m_cpu_do_interrupt,
.do_transaction_failed = arm_cpu_do_transaction_failed,
diff --git a/target/arm/internals.h b/target/arm/internals.h
index 3612107ab2..89f7610ebc 100644
--- a/target/arm/internals.h
+++ b/target/arm/internals.h
@@ -544,9 +544,17 @@ static inline bool arm_extabort_type(MemTxResult result)
return result != MEMTX_DECODE_ERROR;
}
+#ifdef CONFIG_USER_ONLY
+void arm_cpu_record_sigsegv(CPUState *cpu, vaddr addr,
+ MMUAccessType access_type,
+ bool maperr, uintptr_t ra);
+void arm_cpu_record_sigbus(CPUState *cpu, vaddr addr,
+ MMUAccessType access_type, uintptr_t ra);
+#else
bool arm_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr);
+#endif
static inline int arm_to_core_mmu_idx(ARMMMUIdx mmu_idx)
{
diff --git a/target/arm/mte_helper.c b/target/arm/mte_helper.c
index 724175210b..e09b7e46a2 100644
--- a/target/arm/mte_helper.c
+++ b/target/arm/mte_helper.c
@@ -84,10 +84,8 @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx,
uintptr_t index;
if (!(flags & (ptr_access == MMU_DATA_STORE ? PAGE_WRITE_ORG : PAGE_READ))) {
- /* SIGSEGV */
- arm_cpu_tlb_fill(env_cpu(env), ptr, ptr_size, ptr_access,
- ptr_mmu_idx, false, ra);
- g_assert_not_reached();
+ cpu_loop_exit_sigsegv(env_cpu(env), ptr, ptr_access,
+ !(flags & PAGE_VALID), ra);
}
/* Require both MAP_ANON and PROT_MTE for the page. */
diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c
index dab5f1d1cd..07be55b7e1 100644
--- a/target/arm/sve_helper.c
+++ b/target/arm/sve_helper.c
@@ -6118,7 +6118,7 @@ DO_LDN_2(4, dd, MO_64)
* linux-user/ in its get_user/put_user macros.
*
* TODO: Construct some helpers, written in assembly, that interact with
- * handle_cpu_signal to produce memory ops which can properly report errors
+ * host_signal_handler to produce memory ops which can properly report errors
* without racing.
*/
diff --git a/target/arm/tlb_helper.c b/target/arm/tlb_helper.c
index 3107f9823e..12a934e924 100644
--- a/target/arm/tlb_helper.c
+++ b/target/arm/tlb_helper.c
@@ -147,28 +147,12 @@ void arm_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
arm_deliver_fault(cpu, addr, access_type, mmu_idx, &fi);
}
-#endif /* !defined(CONFIG_USER_ONLY) */
-
bool arm_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr)
{
ARMCPU *cpu = ARM_CPU(cs);
ARMMMUFaultInfo fi = {};
-
-#ifdef CONFIG_USER_ONLY
- int flags = page_get_flags(useronly_clean_ptr(address));
- if (flags & PAGE_VALID) {
- fi.type = ARMFault_Permission;
- } else {
- fi.type = ARMFault_Translation;
- }
- fi.level = 3;
-
- /* now we have a real cpu fault */
- cpu_restore_state(cs, retaddr, true);
- arm_deliver_fault(cpu, address, access_type, mmu_idx, &fi);
-#else
hwaddr phys_addr;
target_ulong page_size;
int prot, ret;
@@ -210,5 +194,29 @@ bool arm_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
cpu_restore_state(cs, retaddr, true);
arm_deliver_fault(cpu, address, access_type, mmu_idx, &fi);
}
-#endif
}
+#else
+void arm_cpu_record_sigsegv(CPUState *cs, vaddr addr,
+ MMUAccessType access_type,
+ bool maperr, uintptr_t ra)
+{
+ ARMMMUFaultInfo fi = {
+ .type = maperr ? ARMFault_Translation : ARMFault_Permission,
+ .level = 3,
+ };
+ ARMCPU *cpu = ARM_CPU(cs);
+
+ /*
+ * We report both ESR and FAR to signal handlers.
+ * For now, it's easiest to deliver the fault normally.
+ */
+ cpu_restore_state(cs, ra, true);
+ arm_deliver_fault(cpu, addr, access_type, MMU_USER_IDX, &fi);
+}
+
+void arm_cpu_record_sigbus(CPUState *cs, vaddr addr,
+ MMUAccessType access_type, uintptr_t ra)
+{
+ arm_cpu_do_unaligned_access(cs, addr, access_type, MMU_USER_IDX, ra);
+}
+#endif /* !defined(CONFIG_USER_ONLY) */
diff --git a/target/cris/cpu.c b/target/cris/cpu.c
index c2e7483f5b..ed6c781342 100644
--- a/target/cris/cpu.c
+++ b/target/cris/cpu.c
@@ -205,9 +205,9 @@ static const struct SysemuCPUOps cris_sysemu_ops = {
static const struct TCGCPUOps crisv10_tcg_ops = {
.initialize = cris_initialize_crisv10_tcg,
- .tlb_fill = cris_cpu_tlb_fill,
#ifndef CONFIG_USER_ONLY
+ .tlb_fill = cris_cpu_tlb_fill,
.cpu_exec_interrupt = cris_cpu_exec_interrupt,
.do_interrupt = crisv10_cpu_do_interrupt,
#endif /* !CONFIG_USER_ONLY */
@@ -215,9 +215,9 @@ static const struct TCGCPUOps crisv10_tcg_ops = {
static const struct TCGCPUOps crisv32_tcg_ops = {
.initialize = cris_initialize_tcg,
- .tlb_fill = cris_cpu_tlb_fill,
#ifndef CONFIG_USER_ONLY
+ .tlb_fill = cris_cpu_tlb_fill,
.cpu_exec_interrupt = cris_cpu_exec_interrupt,
.do_interrupt = cris_cpu_do_interrupt,
#endif /* !CONFIG_USER_ONLY */
diff --git a/target/cris/cpu.h b/target/cris/cpu.h
index 6603565f83..b445b194ea 100644
--- a/target/cris/cpu.h
+++ b/target/cris/cpu.h
@@ -189,6 +189,10 @@ extern const VMStateDescription vmstate_cris_cpu;
void cris_cpu_do_interrupt(CPUState *cpu);
void crisv10_cpu_do_interrupt(CPUState *cpu);
bool cris_cpu_exec_interrupt(CPUState *cpu, int int_req);
+
+bool cris_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
+ MMUAccessType access_type, int mmu_idx,
+ bool probe, uintptr_t retaddr);
#endif
void cris_cpu_dump_state(CPUState *cs, FILE *f, int flags);
@@ -251,10 +255,6 @@ static inline int cpu_mmu_index (CPUCRISState *env, bool ifetch)
return !!(env->pregs[PR_CCS] & U_FLAG);
}
-bool cris_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr);
-
/* Support function regs. */
#define SFR_RW_GC_CFG 0][0
#define SFR_RW_MM_CFG env->pregs[PR_SRS]][0
diff --git a/target/cris/helper.c b/target/cris/helper.c
index 36926faf32..a0d6ecdcd3 100644
--- a/target/cris/helper.c
+++ b/target/cris/helper.c
@@ -39,22 +39,6 @@
#define D_LOG(...) do { } while (0)
#endif
-#if defined(CONFIG_USER_ONLY)
-
-bool cris_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr)
-{
- CRISCPU *cpu = CRIS_CPU(cs);
-
- cs->exception_index = 0xaa;
- cpu->env.pregs[PR_EDA] = address;
- cpu_loop_exit_restore(cs, retaddr);
-}
-
-#else /* !CONFIG_USER_ONLY */
-
-
static void cris_shift_ccs(CPUCRISState *env)
{
uint32_t ccs;
@@ -304,5 +288,3 @@ bool cris_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
return ret;
}
-
-#endif /* !CONFIG_USER_ONLY */
diff --git a/target/cris/meson.build b/target/cris/meson.build
index 67c3793c85..c1e326d950 100644
--- a/target/cris/meson.build
+++ b/target/cris/meson.build
@@ -2,13 +2,16 @@ cris_ss = ss.source_set()
cris_ss.add(files(
'cpu.c',
'gdbstub.c',
- 'helper.c',
'op_helper.c',
'translate.c',
))
cris_softmmu_ss = ss.source_set()
-cris_softmmu_ss.add(files('mmu.c', 'machine.c'))
+cris_softmmu_ss.add(files(
+ 'helper.c',
+ 'machine.c',
+ 'mmu.c',
+))
target_arch += {'cris': cris_ss}
target_softmmu_arch += {'cris': cris_softmmu_ss}
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index 3338365c16..160a46a3d5 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -245,34 +245,11 @@ static void hexagon_cpu_init(Object *obj)
qdev_property_add_static(DEVICE(obj), &hexagon_lldb_stack_adjust_property);
}
-static bool hexagon_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr)
-{
-#ifdef CONFIG_USER_ONLY
- switch (access_type) {
- case MMU_INST_FETCH:
- cs->exception_index = HEX_EXCP_FETCH_NO_UPAGE;
- break;
- case MMU_DATA_LOAD:
- cs->exception_index = HEX_EXCP_PRIV_NO_UREAD;
- break;
- case MMU_DATA_STORE:
- cs->exception_index = HEX_EXCP_PRIV_NO_UWRITE;
- break;
- }
- cpu_loop_exit_restore(cs, retaddr);
-#else
-#error System mode not implemented for Hexagon
-#endif
-}
-
#include "hw/core/tcg-cpu-ops.h"
static const struct TCGCPUOps hexagon_tcg_ops = {
.initialize = hexagon_translate_init,
.synchronize_from_tb = hexagon_cpu_synchronize_from_tb,
- .tlb_fill = hexagon_tlb_fill,
};
static void hexagon_cpu_class_init(ObjectClass *c, void *data)
diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c
index 89cba9d7a2..23eb254228 100644
--- a/target/hppa/cpu.c
+++ b/target/hppa/cpu.c
@@ -145,9 +145,9 @@ static const struct SysemuCPUOps hppa_sysemu_ops = {
static const struct TCGCPUOps hppa_tcg_ops = {
.initialize = hppa_translate_init,
.synchronize_from_tb = hppa_cpu_synchronize_from_tb,
- .tlb_fill = hppa_cpu_tlb_fill,
#ifndef CONFIG_USER_ONLY
+ .tlb_fill = hppa_cpu_tlb_fill,
.cpu_exec_interrupt = hppa_cpu_exec_interrupt,
.do_interrupt = hppa_cpu_do_interrupt,
.do_unaligned_access = hppa_cpu_do_unaligned_access,
diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h
index d3cb7a279f..294fd7297f 100644
--- a/target/hppa/cpu.h
+++ b/target/hppa/cpu.h
@@ -323,10 +323,10 @@ hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr);
int hppa_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
int hppa_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
void hppa_cpu_dump_state(CPUState *cs, FILE *f, int);
+#ifndef CONFIG_USER_ONLY
bool hppa_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr);
-#ifndef CONFIG_USER_ONLY
void hppa_cpu_do_interrupt(CPUState *cpu);
bool hppa_cpu_exec_interrupt(CPUState *cpu, int int_req);
int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c
index afc5b56c3e..bf07445cd1 100644
--- a/target/hppa/mem_helper.c
+++ b/target/hppa/mem_helper.c
@@ -24,20 +24,6 @@
#include "hw/core/cpu.h"
#include "trace.h"
-#ifdef CONFIG_USER_ONLY
-bool hppa_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr)
-{
- HPPACPU *cpu = HPPA_CPU(cs);
-
- /* ??? Test between data page fault and data memory protection trap,
- which would affect si_code. */
- cs->exception_index = EXCP_DMP;
- cpu->env.cr[CR_IOR] = address;
- cpu_loop_exit_restore(cs, retaddr);
-}
-#else
static hppa_tlb_entry *hppa_find_tlb(CPUHPPAState *env, vaddr addr)
{
int i;
@@ -392,4 +378,3 @@ int hppa_artype_for_page(CPUHPPAState *env, target_ulong vaddr)
hppa_tlb_entry *ent = hppa_find_tlb(env, vaddr);
return ent ? ent->ar_type : -1;
}
-#endif /* CONFIG_USER_ONLY */
diff --git a/target/hppa/meson.build b/target/hppa/meson.build
index 8a7ff82efc..021e42a2d0 100644
--- a/target/hppa/meson.build
+++ b/target/hppa/meson.build
@@ -7,13 +7,15 @@ hppa_ss.add(files(
'gdbstub.c',
'helper.c',
'int_helper.c',
- 'mem_helper.c',
'op_helper.c',
'translate.c',
))
hppa_softmmu_ss = ss.source_set()
-hppa_softmmu_ss.add(files('machine.c'))
+hppa_softmmu_ss.add(files(
+ 'machine.c',
+ 'mem_helper.c',
+))
target_arch += {'hppa': hppa_ss}
target_softmmu_arch += {'hppa': hppa_softmmu_ss}
diff --git a/target/i386/tcg/helper-tcg.h b/target/i386/tcg/helper-tcg.h
index 60ca09e95e..0a4401e917 100644
--- a/target/i386/tcg/helper-tcg.h
+++ b/target/i386/tcg/helper-tcg.h
@@ -43,9 +43,15 @@ bool x86_cpu_exec_interrupt(CPUState *cpu, int int_req);
#endif
/* helper.c */
+#ifdef CONFIG_USER_ONLY
+void x86_cpu_record_sigsegv(CPUState *cs, vaddr addr,
+ MMUAccessType access_type,
+ bool maperr, uintptr_t ra);
+#else
bool x86_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr);
+#endif
void breakpoint_handler(CPUState *cs);
diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c
index 3ecfae34cb..6fdfdf9598 100644
--- a/target/i386/tcg/tcg-cpu.c
+++ b/target/i386/tcg/tcg-cpu.c
@@ -72,10 +72,11 @@ static const struct TCGCPUOps x86_tcg_ops = {
.synchronize_from_tb = x86_cpu_synchronize_from_tb,
.cpu_exec_enter = x86_cpu_exec_enter,
.cpu_exec_exit = x86_cpu_exec_exit,
- .tlb_fill = x86_cpu_tlb_fill,
#ifdef CONFIG_USER_ONLY
.fake_user_interrupt = x86_cpu_do_interrupt,
+ .record_sigsegv = x86_cpu_record_sigsegv,
#else
+ .tlb_fill = x86_cpu_tlb_fill,
.do_interrupt = x86_cpu_do_interrupt,
.cpu_exec_interrupt = x86_cpu_exec_interrupt,
.debug_excp_handler = breakpoint_handler,
diff --git a/target/i386/tcg/user/excp_helper.c b/target/i386/tcg/user/excp_helper.c
index a89b5228fd..cd507e2a1b 100644
--- a/target/i386/tcg/user/excp_helper.c
+++ b/target/i386/tcg/user/excp_helper.c
@@ -22,18 +22,29 @@
#include "exec/exec-all.h"
#include "tcg/helper-tcg.h"
-bool x86_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr)
+void x86_cpu_record_sigsegv(CPUState *cs, vaddr addr,
+ MMUAccessType access_type,
+ bool maperr, uintptr_t ra)
{
X86CPU *cpu = X86_CPU(cs);
CPUX86State *env = &cpu->env;
+ /*
+ * The error_code that hw reports as part of the exception frame
+ * is copied to linux sigcontext.err. The exception_index is
+ * copied to linux sigcontext.trapno. Short of inventing a new
+ * place to store the trapno, we cannot let our caller raise the
+ * signal and set exception_index to EXCP_INTERRUPT.
+ */
env->cr[2] = addr;
- env->error_code = (access_type == MMU_DATA_STORE) << PG_ERROR_W_BIT;
- env->error_code |= PG_ERROR_U_MASK;
+ env->error_code = ((access_type == MMU_DATA_STORE) << PG_ERROR_W_BIT)
+ | (maperr ? 0 : PG_ERROR_P_MASK)
+ | PG_ERROR_U_MASK;
cs->exception_index = EXCP0E_PAGE;
+
+ /* Disable do_interrupt_user. */
env->exception_is_int = 0;
env->exception_next_eip = -1;
- cpu_loop_exit_restore(cs, retaddr);
+
+ cpu_loop_exit_restore(cs, ra);
}
diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c
index 66d22d1189..c7aeb7da9c 100644
--- a/target/m68k/cpu.c
+++ b/target/m68k/cpu.c
@@ -515,9 +515,9 @@ static const struct SysemuCPUOps m68k_sysemu_ops = {
static const struct TCGCPUOps m68k_tcg_ops = {
.initialize = m68k_tcg_init,
- .tlb_fill = m68k_cpu_tlb_fill,
#ifndef CONFIG_USER_ONLY
+ .tlb_fill = m68k_cpu_tlb_fill,
.cpu_exec_interrupt = m68k_cpu_exec_interrupt,
.do_interrupt = m68k_cpu_do_interrupt,
.do_transaction_failed = m68k_cpu_transaction_failed,
diff --git a/target/m68k/helper.c b/target/m68k/helper.c
index 137a3e1a3d..5728e48585 100644
--- a/target/m68k/helper.c
+++ b/target/m68k/helper.c
@@ -978,16 +978,12 @@ void m68k_set_irq_level(M68kCPU *cpu, int level, uint8_t vector)
}
}
-#endif
-
bool m68k_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType qemu_access_type, int mmu_idx,
bool probe, uintptr_t retaddr)
{
M68kCPU *cpu = M68K_CPU(cs);
CPUM68KState *env = &cpu->env;
-
-#ifndef CONFIG_USER_ONLY
hwaddr physical;
int prot;
int access_type;
@@ -1051,12 +1047,12 @@ bool m68k_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
if (!(access_type & ACCESS_STORE)) {
env->mmu.ssw |= M68K_RW_040;
}
-#endif
cs->exception_index = EXCP_ACCESS;
env->mmu.ar = address;
cpu_loop_exit_restore(cs, retaddr);
}
+#endif /* !CONFIG_USER_ONLY */
uint32_t HELPER(bitrev)(uint32_t x)
{
diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c
index 15db277925..b9c888b87e 100644
--- a/target/microblaze/cpu.c
+++ b/target/microblaze/cpu.c
@@ -365,9 +365,9 @@ static const struct SysemuCPUOps mb_sysemu_ops = {
static const struct TCGCPUOps mb_tcg_ops = {
.initialize = mb_tcg_init,
.synchronize_from_tb = mb_cpu_synchronize_from_tb,
- .tlb_fill = mb_cpu_tlb_fill,
#ifndef CONFIG_USER_ONLY
+ .tlb_fill = mb_cpu_tlb_fill,
.cpu_exec_interrupt = mb_cpu_exec_interrupt,
.do_interrupt = mb_cpu_do_interrupt,
.do_transaction_failed = mb_cpu_transaction_failed,
diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h
index b7a848bbae..e9cd0b88de 100644
--- a/target/microblaze/cpu.h
+++ b/target/microblaze/cpu.h
@@ -394,10 +394,6 @@ void mb_tcg_init(void);
#define MMU_USER_IDX 2
/* See NB_MMU_MODES further up the file. */
-bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr);
-
typedef CPUMBState CPUArchState;
typedef MicroBlazeCPU ArchCPU;
@@ -415,6 +411,10 @@ static inline void cpu_get_tb_cpu_state(CPUMBState *env, target_ulong *pc,
}
#if !defined(CONFIG_USER_ONLY)
+bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
+ MMUAccessType access_type, int mmu_idx,
+ bool probe, uintptr_t retaddr);
+
void mb_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
unsigned size, MMUAccessType access_type,
int mmu_idx, MemTxAttrs attrs,
diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c
index dd2aecd1d5..a607fe68e5 100644
--- a/target/microblaze/helper.c
+++ b/target/microblaze/helper.c
@@ -24,18 +24,7 @@
#include "qemu/host-utils.h"
#include "exec/log.h"
-#if defined(CONFIG_USER_ONLY)
-
-bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr)
-{
- cs->exception_index = 0xaa;
- cpu_loop_exit_restore(cs, retaddr);
-}
-
-#else /* !CONFIG_USER_ONLY */
-
+#ifndef CONFIG_USER_ONLY
static bool mb_cpu_access_is_secure(MicroBlazeCPU *cpu,
MMUAccessType access_type)
{
diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c
index 437bbed6d6..2561b904b9 100644
--- a/target/microblaze/translate.c
+++ b/target/microblaze/translate.c
@@ -722,6 +722,7 @@ static TCGv compute_ldst_addr_ea(DisasContext *dc, int ra, int rb)
}
#endif
+#ifndef CONFIG_USER_ONLY
static void record_unaligned_ess(DisasContext *dc, int rd,
MemOp size, bool store)
{
@@ -734,6 +735,7 @@ static void record_unaligned_ess(DisasContext *dc, int rd,
tcg_set_insn_start_param(dc->insn_start, 1, iflags);
}
+#endif
static bool do_load(DisasContext *dc, int rd, TCGv addr, MemOp mop,
int mem_index, bool rev)
@@ -755,12 +757,19 @@ static bool do_load(DisasContext *dc, int rd, TCGv addr, MemOp mop,
}
}
+ /*
+ * For system mode, enforce alignment if the cpu configuration
+ * requires it. For user-mode, the Linux kernel will have fixed up
+ * any unaligned access, so emulate that by *not* setting MO_ALIGN.
+ */
+#ifndef CONFIG_USER_ONLY
if (size > MO_8 &&
(dc->tb_flags & MSR_EE) &&
dc->cfg->unaligned_exceptions) {
record_unaligned_ess(dc, rd, size, false);
mop |= MO_ALIGN;
}
+#endif
tcg_gen_qemu_ld_i32(reg_for_write(dc, rd), addr, mem_index, mop);
@@ -901,12 +910,19 @@ static bool do_store(DisasContext *dc, int rd, TCGv addr, MemOp mop,
}
}
+ /*
+ * For system mode, enforce alignment if the cpu configuration
+ * requires it. For user-mode, the Linux kernel will have fixed up
+ * any unaligned access, so emulate that by *not* setting MO_ALIGN.
+ */
+#ifndef CONFIG_USER_ONLY
if (size > MO_8 &&
(dc->tb_flags & MSR_EE) &&
dc->cfg->unaligned_exceptions) {
record_unaligned_ess(dc, rd, size, true);
mop |= MO_ALIGN;
}
+#endif
tcg_gen_qemu_st_i32(reg_for_read(dc, rd), addr, mem_index, mop);
diff --git a/target/mips/cpu.c b/target/mips/cpu.c
index 00e0c55d0e..4aae23934b 100644
--- a/target/mips/cpu.c
+++ b/target/mips/cpu.c
@@ -539,9 +539,9 @@ static const struct SysemuCPUOps mips_sysemu_ops = {
static const struct TCGCPUOps mips_tcg_ops = {
.initialize = mips_tcg_init,
.synchronize_from_tb = mips_cpu_synchronize_from_tb,
- .tlb_fill = mips_cpu_tlb_fill,
#if !defined(CONFIG_USER_ONLY)
+ .tlb_fill = mips_cpu_tlb_fill,
.cpu_exec_interrupt = mips_cpu_exec_interrupt,
.do_interrupt = mips_cpu_do_interrupt,
.do_transaction_failed = mips_cpu_do_transaction_failed,
diff --git a/target/mips/tcg/meson.build b/target/mips/tcg/meson.build
index 8f6f7508b6..98003779ae 100644
--- a/target/mips/tcg/meson.build
+++ b/target/mips/tcg/meson.build
@@ -28,9 +28,6 @@ mips_ss.add(when: 'TARGET_MIPS64', if_true: files(
'mxu_translate.c',
))
-if have_user
- subdir('user')
-endif
if have_system
subdir('sysemu')
endif
diff --git a/target/mips/tcg/tcg-internal.h b/target/mips/tcg/tcg-internal.h
index bad3deb611..466768aec4 100644
--- a/target/mips/tcg/tcg-internal.h
+++ b/target/mips/tcg/tcg-internal.h
@@ -18,9 +18,6 @@
void mips_tcg_init(void);
void mips_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb);
-bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr);
void mips_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
MMUAccessType access_type, int mmu_idx,
uintptr_t retaddr) QEMU_NORETURN;
@@ -60,6 +57,10 @@ void mips_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
MemTxResult response, uintptr_t retaddr);
void cpu_mips_tlb_flush(CPUMIPSState *env);
+bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
+ MMUAccessType access_type, int mmu_idx,
+ bool probe, uintptr_t retaddr);
+
#endif /* !CONFIG_USER_ONLY */
#endif
diff --git a/target/mips/tcg/user/meson.build b/target/mips/tcg/user/meson.build
deleted file mode 100644
index 79badcd321..0000000000
--- a/target/mips/tcg/user/meson.build
+++ /dev/null
@@ -1,3 +0,0 @@
-mips_user_ss.add(files(
- 'tlb_helper.c',
-))
diff --git a/target/mips/tcg/user/tlb_helper.c b/target/mips/tcg/user/tlb_helper.c
deleted file mode 100644
index 210c6d529e..0000000000
--- a/target/mips/tcg/user/tlb_helper.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * MIPS TLB (Translation lookaside buffer) helpers.
- *
- * Copyright (c) 2004-2005 Jocelyn Mayer
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-#include "qemu/osdep.h"
-
-#include "cpu.h"
-#include "exec/exec-all.h"
-#include "internal.h"
-
-static void raise_mmu_exception(CPUMIPSState *env, target_ulong address,
- MMUAccessType access_type)
-{
- CPUState *cs = env_cpu(env);
-
- env->error_code = 0;
- if (access_type == MMU_INST_FETCH) {
- env->error_code |= EXCP_INST_NOTAVAIL;
- }
-
- /* Reference to kernel address from user mode or supervisor mode */
- /* Reference to supervisor address from user mode */
- if (access_type == MMU_DATA_STORE) {
- cs->exception_index = EXCP_AdES;
- } else {
- cs->exception_index = EXCP_AdEL;
- }
-
- /* Raise exception */
- if (!(env->hflags & MIPS_HFLAG_DM)) {
- env->CP0_BadVAddr = address;
- }
-}
-
-bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr)
-{
- MIPSCPU *cpu = MIPS_CPU(cs);
- CPUMIPSState *env = &cpu->env;
-
- /* data access */
- raise_mmu_exception(env, address, access_type);
- do_raise_exception_err(env, cs->exception_index, env->error_code, retaddr);
-}
diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c
index 58ecd27d75..4cade61e93 100644
--- a/target/nios2/cpu.c
+++ b/target/nios2/cpu.c
@@ -216,9 +216,11 @@ static const struct SysemuCPUOps nios2_sysemu_ops = {
static const struct TCGCPUOps nios2_tcg_ops = {
.initialize = nios2_tcg_init,
- .tlb_fill = nios2_cpu_tlb_fill,
-#ifndef CONFIG_USER_ONLY
+#ifdef CONFIG_USER_ONLY
+ .record_sigsegv = nios2_cpu_record_sigsegv,
+#else
+ .tlb_fill = nios2_cpu_tlb_fill,
.cpu_exec_interrupt = nios2_cpu_exec_interrupt,
.do_interrupt = nios2_cpu_do_interrupt,
.do_unaligned_access = nios2_cpu_do_unaligned_access,
diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h
index a80587338a..1a69ed7a49 100644
--- a/target/nios2/cpu.h
+++ b/target/nios2/cpu.h
@@ -218,9 +218,15 @@ static inline int cpu_mmu_index(CPUNios2State *env, bool ifetch)
MMU_SUPERVISOR_IDX;
}
+#ifdef CONFIG_USER_ONLY
+void nios2_cpu_record_sigsegv(CPUState *cpu, vaddr addr,
+ MMUAccessType access_type,
+ bool maperr, uintptr_t ra);
+#else
bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr);
+#endif
static inline int cpu_interrupts_enabled(CPUNios2State *env)
{
diff --git a/target/nios2/helper.c b/target/nios2/helper.c
index 53be8398e9..e5c98650e1 100644
--- a/target/nios2/helper.c
+++ b/target/nios2/helper.c
@@ -38,10 +38,11 @@ void nios2_cpu_do_interrupt(CPUState *cs)
env->regs[R_EA] = env->regs[R_PC] + 4;
}
-bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr)
+void nios2_cpu_record_sigsegv(CPUState *cs, vaddr addr,
+ MMUAccessType access_type,
+ bool maperr, uintptr_t retaddr)
{
+ /* FIXME: Disentangle kuser page from linux-user sigsegv handling. */
cs->exception_index = 0xaa;
cpu_loop_exit_restore(cs, retaddr);
}
diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c
index 27cb04152f..dfbafc5236 100644
--- a/target/openrisc/cpu.c
+++ b/target/openrisc/cpu.c
@@ -186,9 +186,9 @@ static const struct SysemuCPUOps openrisc_sysemu_ops = {
static const struct TCGCPUOps openrisc_tcg_ops = {
.initialize = openrisc_translate_init,
- .tlb_fill = openrisc_cpu_tlb_fill,
#ifndef CONFIG_USER_ONLY
+ .tlb_fill = openrisc_cpu_tlb_fill,
.cpu_exec_interrupt = openrisc_cpu_exec_interrupt,
.do_interrupt = openrisc_cpu_do_interrupt,
#endif /* !CONFIG_USER_ONLY */
diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h
index 187a4a114e..ee069b080c 100644
--- a/target/openrisc/cpu.h
+++ b/target/openrisc/cpu.h
@@ -317,14 +317,15 @@ hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
int openrisc_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
int openrisc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
void openrisc_translate_init(void);
-bool openrisc_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr);
int print_insn_or1k(bfd_vma addr, disassemble_info *info);
#define cpu_list cpu_openrisc_list
#ifndef CONFIG_USER_ONLY
+bool openrisc_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
+ MMUAccessType access_type, int mmu_idx,
+ bool probe, uintptr_t retaddr);
+
extern const VMStateDescription vmstate_openrisc_cpu;
void openrisc_cpu_do_interrupt(CPUState *cpu);
diff --git a/target/openrisc/meson.build b/target/openrisc/meson.build
index e445dec4a0..84322086ec 100644
--- a/target/openrisc/meson.build
+++ b/target/openrisc/meson.build
@@ -10,7 +10,6 @@ openrisc_ss.add(files(
'fpu_helper.c',
'gdbstub.c',
'interrupt_helper.c',
- 'mmu.c',
'sys_helper.c',
'translate.c',
))
@@ -19,6 +18,7 @@ openrisc_softmmu_ss = ss.source_set()
openrisc_softmmu_ss.add(files(
'interrupt.c',
'machine.c',
+ 'mmu.c',
))
target_arch += {'openrisc': openrisc_ss}
diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c
index 94df8c7bef..e561ef245b 100644
--- a/target/openrisc/mmu.c
+++ b/target/openrisc/mmu.c
@@ -23,11 +23,8 @@
#include "exec/exec-all.h"
#include "exec/gdbstub.h"
#include "qemu/host-utils.h"
-#ifndef CONFIG_USER_ONLY
#include "hw/loader.h"
-#endif
-#ifndef CONFIG_USER_ONLY
static inline void get_phys_nommu(hwaddr *phys_addr, int *prot,
target_ulong address)
{
@@ -94,7 +91,6 @@ static int get_phys_mmu(OpenRISCCPU *cpu, hwaddr *phys_addr, int *prot,
return need & PAGE_EXEC ? EXCP_ITLBMISS : EXCP_DTLBMISS;
}
}
-#endif
static void raise_mmu_exception(OpenRISCCPU *cpu, target_ulong address,
int exception)
@@ -112,8 +108,6 @@ bool openrisc_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
{
OpenRISCCPU *cpu = OPENRISC_CPU(cs);
int excp = EXCP_DPF;
-
-#ifndef CONFIG_USER_ONLY
int prot;
hwaddr phys_addr;
@@ -138,13 +132,11 @@ bool openrisc_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
if (probe) {
return false;
}
-#endif
raise_mmu_exception(cpu, addr, excp);
cpu_loop_exit_restore(cs, retaddr);
}
-#ifndef CONFIG_USER_ONLY
hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
{
OpenRISCCPU *cpu = OPENRISC_CPU(cs);
@@ -177,4 +169,3 @@ hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
return phys_addr;
}
}
-#endif
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index 0472ec9154..e946da5f3a 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -1302,9 +1302,6 @@ extern const VMStateDescription vmstate_ppc_cpu;
/*****************************************************************************/
void ppc_translate_init(void);
-bool ppc_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr);
#if !defined(CONFIG_USER_ONLY)
void ppc_store_sdr1(CPUPPCState *env, target_ulong value);
diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c
index 65545ba9ca..1c7a7b4b38 100644
--- a/target/ppc/cpu_init.c
+++ b/target/ppc/cpu_init.c
@@ -9014,9 +9014,11 @@ static const struct SysemuCPUOps ppc_sysemu_ops = {
static const struct TCGCPUOps ppc_tcg_ops = {
.initialize = ppc_translate_init,
- .tlb_fill = ppc_cpu_tlb_fill,
-#ifndef CONFIG_USER_ONLY
+#ifdef CONFIG_USER_ONLY
+ .record_sigsegv = ppc_cpu_record_sigsegv,
+#else
+ .tlb_fill = ppc_cpu_tlb_fill,
.cpu_exec_interrupt = ppc_cpu_exec_interrupt,
.do_interrupt = ppc_cpu_do_interrupt,
.cpu_exec_enter = ppc_cpu_exec_enter,
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
index b7d1767920..17607adbe4 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -454,13 +454,15 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
break;
}
case POWERPC_EXCP_ALIGN: /* Alignment exception */
- /* Get rS/rD and rA from faulting opcode */
/*
- * Note: the opcode fields will not be set properly for a
- * direct store load/store, but nobody cares as nobody
- * actually uses direct store segments.
+ * Get rS/rD and rA from faulting opcode.
+ * Note: We will only invoke ALIGN for atomic operations,
+ * so all instructions are X-form.
*/
- env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16;
+ {
+ uint32_t insn = cpu_ldl_code(env, env->nip);
+ env->spr[SPR_DSISR] |= (insn & 0x03FF0000) >> 16;
+ }
break;
case POWERPC_EXCP_PROGRAM: /* Program exception */
switch (env->error_code & ~0xF) {
@@ -1452,24 +1454,31 @@ void helper_book3s_msgsndp(CPUPPCState *env, target_ulong rb)
book3s_msgsnd_common(pir, PPC_INTERRUPT_DOORBELL);
}
-#endif
-#endif /* CONFIG_TCG */
-#endif
+#endif /* TARGET_PPC64 */
-#ifdef CONFIG_TCG
void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr,
MMUAccessType access_type,
int mmu_idx, uintptr_t retaddr)
{
CPUPPCState *env = cs->env_ptr;
- uint32_t insn;
- /* Restore state and reload the insn we executed, for filling in DSISR. */
- cpu_restore_state(cs, retaddr, true);
- insn = cpu_ldl_code(env, env->nip);
+ switch (env->mmu_model) {
+ case POWERPC_MMU_SOFT_4xx:
+ case POWERPC_MMU_SOFT_4xx_Z:
+ env->spr[SPR_40x_DEAR] = vaddr;
+ break;
+ case POWERPC_MMU_BOOKE:
+ case POWERPC_MMU_BOOKE206:
+ env->spr[SPR_BOOKE_DEAR] = vaddr;
+ break;
+ default:
+ env->spr[SPR_DAR] = vaddr;
+ break;
+ }
cs->exception_index = POWERPC_EXCP_ALIGN;
- env->error_code = insn & 0x03FF0000;
- cpu_loop_exit(cs);
+ env->error_code = 0;
+ cpu_loop_exit_restore(cs, retaddr);
}
-#endif
+#endif /* CONFIG_TCG */
+#endif /* !CONFIG_USER_ONLY */
diff --git a/target/ppc/internal.h b/target/ppc/internal.h
index 55284369f5..6aa9484f34 100644
--- a/target/ppc/internal.h
+++ b/target/ppc/internal.h
@@ -211,11 +211,6 @@ void helper_compute_fprf_float16(CPUPPCState *env, float16 arg);
void helper_compute_fprf_float32(CPUPPCState *env, float32 arg);
void helper_compute_fprf_float128(CPUPPCState *env, float128 arg);
-/* Raise a data fault alignment exception for the specified virtual address */
-void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
- MMUAccessType access_type, int mmu_idx,
- uintptr_t retaddr) QEMU_NORETURN;
-
/* translate.c */
int ppc_fixup_cpu(PowerPCCPU *cpu);
@@ -283,5 +278,17 @@ static inline void pte_invalidate(target_ulong *pte0)
#define PTE_PTEM_MASK 0x7FFFFFBF
#define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B)
+#ifdef CONFIG_USER_ONLY
+void ppc_cpu_record_sigsegv(CPUState *cs, vaddr addr,
+ MMUAccessType access_type,
+ bool maperr, uintptr_t ra);
+#else
+bool ppc_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
+ MMUAccessType access_type, int mmu_idx,
+ bool probe, uintptr_t retaddr);
+void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
+ MMUAccessType access_type, int mmu_idx,
+ uintptr_t retaddr) QEMU_NORETURN;
+#endif
#endif /* PPC_INTERNAL_H */
diff --git a/target/ppc/user_only_helper.c b/target/ppc/user_only_helper.c
index aa3f867596..7ff76f7a06 100644
--- a/target/ppc/user_only_helper.c
+++ b/target/ppc/user_only_helper.c
@@ -21,16 +21,23 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/exec-all.h"
+#include "internal.h"
-
-bool ppc_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr)
+void ppc_cpu_record_sigsegv(CPUState *cs, vaddr address,
+ MMUAccessType access_type,
+ bool maperr, uintptr_t retaddr)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);
CPUPPCState *env = &cpu->env;
int exception, error_code;
+ /*
+ * Both DSISR and the "trap number" (exception vector offset,
+ * looked up from exception_index) are present in the linux-user
+ * signal frame.
+ * FIXME: we don't actually populate the trap number properly.
+ * It would be easiest to fill in an env->trap value now.
+ */
if (access_type == MMU_INST_FETCH) {
exception = POWERPC_EXCP_ISI;
error_code = 0x40000000;
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 7d53125dbc..f812998123 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -694,9 +694,9 @@ static const struct SysemuCPUOps riscv_sysemu_ops = {
static const struct TCGCPUOps riscv_tcg_ops = {
.initialize = riscv_translate_init,
.synchronize_from_tb = riscv_cpu_synchronize_from_tb,
- .tlb_fill = riscv_cpu_tlb_fill,
#ifndef CONFIG_USER_ONLY
+ .tlb_fill = riscv_cpu_tlb_fill,
.cpu_exec_interrupt = riscv_cpu_exec_interrupt,
.do_interrupt = riscv_cpu_do_interrupt,
.do_transaction_failed = riscv_cpu_do_transaction_failed,
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index f30ff672f8..9eeed38c7e 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -814,7 +814,6 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
riscv_cpu_two_stage_lookup(mmu_idx);
riscv_raise_exception(env, cs->exception_index, retaddr);
}
-#endif /* !CONFIG_USER_ONLY */
bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
@@ -822,7 +821,6 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
{
RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env;
-#ifndef CONFIG_USER_ONLY
vaddr im_address;
hwaddr pa = 0;
int prot, prot2, prot_pmp;
@@ -954,25 +952,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
}
return true;
-
-#else
- switch (access_type) {
- case MMU_INST_FETCH:
- cs->exception_index = RISCV_EXCP_INST_PAGE_FAULT;
- break;
- case MMU_DATA_LOAD:
- cs->exception_index = RISCV_EXCP_LOAD_PAGE_FAULT;
- break;
- case MMU_DATA_STORE:
- cs->exception_index = RISCV_EXCP_STORE_PAGE_FAULT;
- break;
- default:
- g_assert_not_reached();
- }
- env->badaddr = address;
- cpu_loop_exit_restore(cs, retaddr);
-#endif
}
+#endif /* !CONFIG_USER_ONLY */
/*
* Handle Traps
diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c
index 7b7b05f1d3..ccdbaf84d5 100644
--- a/target/s390x/cpu.c
+++ b/target/s390x/cpu.c
@@ -266,9 +266,12 @@ static void s390_cpu_reset_full(DeviceState *dev)
static const struct TCGCPUOps s390_tcg_ops = {
.initialize = s390x_translate_init,
- .tlb_fill = s390_cpu_tlb_fill,
-#if !defined(CONFIG_USER_ONLY)
+#ifdef CONFIG_USER_ONLY
+ .record_sigsegv = s390_cpu_record_sigsegv,
+ .record_sigbus = s390_cpu_record_sigbus,
+#else
+ .tlb_fill = s390_cpu_tlb_fill,
.cpu_exec_interrupt = s390_cpu_exec_interrupt,
.do_interrupt = s390_cpu_do_interrupt,
.debug_excp_handler = s390x_cpu_debug_excp_handler,
diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h
index 27d4a03ca1..1a178aed41 100644
--- a/target/s390x/s390x-internal.h
+++ b/target/s390x/s390x-internal.h
@@ -270,12 +270,21 @@ ObjectClass *s390_cpu_class_by_name(const char *name);
void s390x_cpu_debug_excp_handler(CPUState *cs);
void s390_cpu_do_interrupt(CPUState *cpu);
bool s390_cpu_exec_interrupt(CPUState *cpu, int int_req);
+
+#ifdef CONFIG_USER_ONLY
+void s390_cpu_record_sigsegv(CPUState *cs, vaddr address,
+ MMUAccessType access_type,
+ bool maperr, uintptr_t retaddr);
+void s390_cpu_record_sigbus(CPUState *cs, vaddr address,
+ MMUAccessType access_type, uintptr_t retaddr);
+#else
bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr);
void s390x_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
MMUAccessType access_type, int mmu_idx,
uintptr_t retaddr) QEMU_NORETURN;
+#endif
/* fpu_helper.c */
diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c
index 3d6662a53c..4e7648f301 100644
--- a/target/s390x/tcg/excp_helper.c
+++ b/target/s390x/tcg/excp_helper.c
@@ -82,6 +82,19 @@ void HELPER(data_exception)(CPUS390XState *env, uint32_t dxc)
tcg_s390_data_exception(env, dxc, GETPC());
}
+/*
+ * Unaligned accesses are only diagnosed with MO_ALIGN. At the moment,
+ * this is only for the atomic operations, for which we want to raise a
+ * specification exception.
+ */
+static void QEMU_NORETURN do_unaligned_access(CPUState *cs, uintptr_t retaddr)
+{
+ S390CPU *cpu = S390_CPU(cs);
+ CPUS390XState *env = &cpu->env;
+
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, retaddr);
+}
+
#if defined(CONFIG_USER_ONLY)
void s390_cpu_do_interrupt(CPUState *cs)
@@ -89,19 +102,29 @@ void s390_cpu_do_interrupt(CPUState *cs)
cs->exception_index = -1;
}
-bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr)
+void s390_cpu_record_sigsegv(CPUState *cs, vaddr address,
+ MMUAccessType access_type,
+ bool maperr, uintptr_t retaddr)
{
S390CPU *cpu = S390_CPU(cs);
- trigger_pgm_exception(&cpu->env, PGM_ADDRESSING);
- /* On real machines this value is dropped into LowMem. Since this
- is userland, simply put this someplace that cpu_loop can find it. */
- cpu->env.__excp_addr = address;
+ trigger_pgm_exception(&cpu->env, maperr ? PGM_ADDRESSING : PGM_PROTECTION);
+ /*
+ * On real machines this value is dropped into LowMem. Since this
+ * is userland, simply put this someplace that cpu_loop can find it.
+ * S390 only gives the page of the fault, not the exact address.
+ * C.f. the construction of TEC in mmu_translate().
+ */
+ cpu->env.__excp_addr = address & TARGET_PAGE_MASK;
cpu_loop_exit_restore(cs, retaddr);
}
+void s390_cpu_record_sigbus(CPUState *cs, vaddr address,
+ MMUAccessType access_type, uintptr_t retaddr)
+{
+ do_unaligned_access(cs, retaddr);
+}
+
#else /* !CONFIG_USER_ONLY */
static inline uint64_t cpu_mmu_idx_to_asc(int mmu_idx)
@@ -589,17 +612,11 @@ void s390x_cpu_debug_excp_handler(CPUState *cs)
}
}
-/* Unaligned accesses are only diagnosed with MO_ALIGN. At the moment,
- this is only for the atomic operations, for which we want to raise a
- specification exception. */
void s390x_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
MMUAccessType access_type,
int mmu_idx, uintptr_t retaddr)
{
- S390CPU *cpu = S390_CPU(cs);
- CPUS390XState *env = &cpu->env;
-
- tcg_s390_program_interrupt(env, PGM_SPECIFICATION, retaddr);
+ do_unaligned_access(cs, retaddr);
}
static void QEMU_NORETURN monitor_event(CPUS390XState *env,
diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c
index 17e3f83641..362a30d99e 100644
--- a/target/s390x/tcg/mem_helper.c
+++ b/target/s390x/tcg/mem_helper.c
@@ -141,20 +141,12 @@ static int s390_probe_access(CPUArchState *env, target_ulong addr, int size,
MMUAccessType access_type, int mmu_idx,
bool nonfault, void **phost, uintptr_t ra)
{
- int flags;
-
#if defined(CONFIG_USER_ONLY)
- flags = page_get_flags(addr);
- if (!(flags & (access_type == MMU_DATA_LOAD ? PAGE_READ : PAGE_WRITE_ORG))) {
- env->__excp_addr = addr;
- flags = (flags & PAGE_VALID) ? PGM_PROTECTION : PGM_ADDRESSING;
- if (nonfault) {
- return flags;
- }
- tcg_s390_program_interrupt(env, flags, ra);
- }
- *phost = g2h(env_cpu(env), addr);
+ return probe_access_flags(env, addr, access_type, mmu_idx,
+ nonfault, phost, ra);
#else
+ int flags;
+
/*
* For !CONFIG_USER_ONLY, we cannot rely on TLB_INVALID_MASK or haddr==NULL
* to detect if there was an exception during tlb_fill().
@@ -173,8 +165,8 @@ static int s390_probe_access(CPUArchState *env, target_ulong addr, int size,
(access_type == MMU_DATA_STORE
? BP_MEM_WRITE : BP_MEM_READ), ra);
}
-#endif
return 0;
+#endif
}
static int access_prepare_nf(S390Access *access, CPUS390XState *env,
diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c
index 2047742d03..06b2691dc4 100644
--- a/target/sh4/cpu.c
+++ b/target/sh4/cpu.c
@@ -236,9 +236,9 @@ static const struct SysemuCPUOps sh4_sysemu_ops = {
static const struct TCGCPUOps superh_tcg_ops = {
.initialize = sh4_translate_init,
.synchronize_from_tb = superh_cpu_synchronize_from_tb,
- .tlb_fill = superh_cpu_tlb_fill,
#ifndef CONFIG_USER_ONLY
+ .tlb_fill = superh_cpu_tlb_fill,
.cpu_exec_interrupt = superh_cpu_exec_interrupt,
.do_interrupt = superh_cpu_do_interrupt,
.do_unaligned_access = superh_cpu_do_unaligned_access,
diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h
index dc81406646..4cfb109f56 100644
--- a/target/sh4/cpu.h
+++ b/target/sh4/cpu.h
@@ -213,12 +213,12 @@ void superh_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
uintptr_t retaddr) QEMU_NORETURN;
void sh4_translate_init(void);
+void sh4_cpu_list(void);
+
+#if !defined(CONFIG_USER_ONLY)
bool superh_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr);
-
-void sh4_cpu_list(void);
-#if !defined(CONFIG_USER_ONLY)
void superh_cpu_do_interrupt(CPUState *cpu);
bool superh_cpu_exec_interrupt(CPUState *cpu, int int_req);
void cpu_sh4_invalidate_tlb(CPUSH4State *s);
diff --git a/target/sh4/helper.c b/target/sh4/helper.c
index 53cb9c3b63..6a620e36fc 100644
--- a/target/sh4/helper.c
+++ b/target/sh4/helper.c
@@ -796,8 +796,6 @@ bool superh_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
return false;
}
-#endif /* !CONFIG_USER_ONLY */
-
bool superh_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr)
@@ -806,11 +804,6 @@ bool superh_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
CPUSH4State *env = &cpu->env;
int ret;
-#ifdef CONFIG_USER_ONLY
- ret = (access_type == MMU_DATA_STORE ? MMU_DTLB_VIOLATION_WRITE :
- access_type == MMU_INST_FETCH ? MMU_ITLB_VIOLATION :
- MMU_DTLB_VIOLATION_READ);
-#else
target_ulong physical;
int prot;
@@ -829,7 +822,6 @@ bool superh_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
if (ret != MMU_DTLB_MULTIPLE && ret != MMU_ITLB_MULTIPLE) {
env->pteh = (env->pteh & PTEH_ASID_MASK) | (address & PTEH_VPN_MASK);
}
-#endif
env->tea = address;
switch (ret) {
@@ -868,3 +860,4 @@ bool superh_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
}
cpu_loop_exit_restore(cs, retaddr);
}
+#endif /* !CONFIG_USER_ONLY */
diff --git a/target/sh4/op_helper.c b/target/sh4/op_helper.c
index c996dce7df..752669825f 100644
--- a/target/sh4/op_helper.c
+++ b/target/sh4/op_helper.c
@@ -29,6 +29,9 @@ void superh_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
MMUAccessType access_type,
int mmu_idx, uintptr_t retaddr)
{
+ CPUSH4State *env = cs->env_ptr;
+
+ env->tea = addr;
switch (access_type) {
case MMU_INST_FETCH:
case MMU_DATA_LOAD:
@@ -37,6 +40,8 @@ void superh_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
case MMU_DATA_STORE:
cs->exception_index = 0x100;
break;
+ default:
+ g_assert_not_reached();
}
cpu_loop_exit_restore(cs, retaddr);
}
diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c
index 21dd27796d..55268ed2a1 100644
--- a/target/sparc/cpu.c
+++ b/target/sparc/cpu.c
@@ -865,9 +865,9 @@ static const struct SysemuCPUOps sparc_sysemu_ops = {
static const struct TCGCPUOps sparc_tcg_ops = {
.initialize = sparc_tcg_init,
.synchronize_from_tb = sparc_cpu_synchronize_from_tb,
- .tlb_fill = sparc_cpu_tlb_fill,
#ifndef CONFIG_USER_ONLY
+ .tlb_fill = sparc_cpu_tlb_fill,
.cpu_exec_interrupt = sparc_cpu_exec_interrupt,
.do_interrupt = sparc_cpu_do_interrupt,
.do_transaction_failed = sparc_cpu_do_transaction_failed,
diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c
index bbf3601cb1..a3e1cf9b6e 100644
--- a/target/sparc/ldst_helper.c
+++ b/target/sparc/ldst_helper.c
@@ -27,7 +27,6 @@
//#define DEBUG_MMU
//#define DEBUG_MXCC
-//#define DEBUG_UNALIGNED
//#define DEBUG_UNASSIGNED
//#define DEBUG_ASI
//#define DEBUG_CACHE_CONTROL
@@ -364,10 +363,6 @@ static void do_check_align(CPUSPARCState *env, target_ulong addr,
uint32_t align, uintptr_t ra)
{
if (addr & align) {
-#ifdef DEBUG_UNALIGNED
- printf("Unaligned access to 0x" TARGET_FMT_lx " from 0x" TARGET_FMT_lx
- "\n", addr, env->pc);
-#endif
cpu_raise_exception_ra(env, TT_UNALIGNED, ra);
}
}
@@ -1958,20 +1953,3 @@ void sparc_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
is_asi, size, retaddr);
}
#endif
-
-#if !defined(CONFIG_USER_ONLY)
-void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
- MMUAccessType access_type,
- int mmu_idx,
- uintptr_t retaddr)
-{
- SPARCCPU *cpu = SPARC_CPU(cs);
- CPUSPARCState *env = &cpu->env;
-
-#ifdef DEBUG_UNALIGNED
- printf("Unaligned access to 0x" TARGET_FMT_lx " from 0x" TARGET_FMT_lx
- "\n", addr, env->pc);
-#endif
- cpu_raise_exception_ra(env, TT_UNALIGNED, retaddr);
-}
-#endif
diff --git a/target/sparc/meson.build b/target/sparc/meson.build
index a3638b9503..a801802ee2 100644
--- a/target/sparc/meson.build
+++ b/target/sparc/meson.build
@@ -6,7 +6,6 @@ sparc_ss.add(files(
'gdbstub.c',
'helper.c',
'ldst_helper.c',
- 'mmu_helper.c',
'translate.c',
'win_helper.c',
))
@@ -16,6 +15,7 @@ sparc_ss.add(when: 'TARGET_SPARC64', if_true: files('int64_helper.c', 'vis_helpe
sparc_softmmu_ss = ss.source_set()
sparc_softmmu_ss.add(files(
'machine.c',
+ 'mmu_helper.c',
'monitor.c',
))
diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c
index a44473a1c7..f2668389b0 100644
--- a/target/sparc/mmu_helper.c
+++ b/target/sparc/mmu_helper.c
@@ -25,30 +25,6 @@
/* Sparc MMU emulation */
-#if defined(CONFIG_USER_ONLY)
-
-bool sparc_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr)
-{
- SPARCCPU *cpu = SPARC_CPU(cs);
- CPUSPARCState *env = &cpu->env;
-
- if (access_type == MMU_INST_FETCH) {
- cs->exception_index = TT_TFAULT;
- } else {
- cs->exception_index = TT_DFAULT;
-#ifdef TARGET_SPARC64
- env->dmmu.mmuregs[4] = address;
-#else
- env->mmuregs[4] = address;
-#endif
- }
- cpu_loop_exit_restore(cs, retaddr);
-}
-
-#else
-
#ifndef TARGET_SPARC64
/*
* Sparc V8 Reference MMU (SRMMU)
@@ -526,16 +502,60 @@ static inline int ultrasparc_tag_match(SparcTLBEntry *tlb,
return 0;
}
+static uint64_t build_sfsr(CPUSPARCState *env, int mmu_idx, int rw)
+{
+ uint64_t sfsr = SFSR_VALID_BIT;
+
+ switch (mmu_idx) {
+ case MMU_PHYS_IDX:
+ sfsr |= SFSR_CT_NOTRANS;
+ break;
+ case MMU_USER_IDX:
+ case MMU_KERNEL_IDX:
+ sfsr |= SFSR_CT_PRIMARY;
+ break;
+ case MMU_USER_SECONDARY_IDX:
+ case MMU_KERNEL_SECONDARY_IDX:
+ sfsr |= SFSR_CT_SECONDARY;
+ break;
+ case MMU_NUCLEUS_IDX:
+ sfsr |= SFSR_CT_NUCLEUS;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (rw == 1) {
+ sfsr |= SFSR_WRITE_BIT;
+ } else if (rw == 4) {
+ sfsr |= SFSR_NF_BIT;
+ }
+
+ if (env->pstate & PS_PRIV) {
+ sfsr |= SFSR_PR_BIT;
+ }
+
+ if (env->dmmu.sfsr & SFSR_VALID_BIT) { /* Fault status register */
+ sfsr |= SFSR_OW_BIT; /* overflow (not read before another fault) */
+ }
+
+ /* FIXME: ASI field in SFSR must be set */
+
+ return sfsr;
+}
+
static int get_physical_address_data(CPUSPARCState *env, hwaddr *physical,
int *prot, MemTxAttrs *attrs,
target_ulong address, int rw, int mmu_idx)
{
CPUState *cs = env_cpu(env);
unsigned int i;
+ uint64_t sfsr;
uint64_t context;
- uint64_t sfsr = 0;
bool is_user = false;
+ sfsr = build_sfsr(env, mmu_idx, rw);
+
switch (mmu_idx) {
case MMU_PHYS_IDX:
g_assert_not_reached();
@@ -544,29 +564,18 @@ static int get_physical_address_data(CPUSPARCState *env, hwaddr *physical,
/* fallthru */
case MMU_KERNEL_IDX:
context = env->dmmu.mmu_primary_context & 0x1fff;
- sfsr |= SFSR_CT_PRIMARY;
break;
case MMU_USER_SECONDARY_IDX:
is_user = true;
/* fallthru */
case MMU_KERNEL_SECONDARY_IDX:
context = env->dmmu.mmu_secondary_context & 0x1fff;
- sfsr |= SFSR_CT_SECONDARY;
break;
- case MMU_NUCLEUS_IDX:
- sfsr |= SFSR_CT_NUCLEUS;
- /* FALLTHRU */
default:
context = 0;
break;
}
- if (rw == 1) {
- sfsr |= SFSR_WRITE_BIT;
- } else if (rw == 4) {
- sfsr |= SFSR_NF_BIT;
- }
-
for (i = 0; i < 64; i++) {
/* ctx match, vaddr match, valid? */
if (ultrasparc_tag_match(&env->dtlb[i], address, context, physical)) {
@@ -616,22 +625,9 @@ static int get_physical_address_data(CPUSPARCState *env, hwaddr *physical,
return 0;
}
- if (env->dmmu.sfsr & SFSR_VALID_BIT) { /* Fault status register */
- sfsr |= SFSR_OW_BIT; /* overflow (not read before
- another fault) */
- }
-
- if (env->pstate & PS_PRIV) {
- sfsr |= SFSR_PR_BIT;
- }
-
- /* FIXME: ASI field in SFSR must be set */
- env->dmmu.sfsr = sfsr | SFSR_VALID_BIT;
-
+ env->dmmu.sfsr = sfsr;
env->dmmu.sfar = address; /* Fault address register */
-
env->dmmu.tag_access = (address & ~0x1fffULL) | context;
-
return 1;
}
}
@@ -926,4 +922,23 @@ hwaddr sparc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
}
return phys_addr;
}
+
+#ifndef CONFIG_USER_ONLY
+void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
+ MMUAccessType access_type,
+ int mmu_idx,
+ uintptr_t retaddr)
+{
+ SPARCCPU *cpu = SPARC_CPU(cs);
+ CPUSPARCState *env = &cpu->env;
+
+#ifdef TARGET_SPARC64
+ env->dmmu.sfsr = build_sfsr(env, mmu_idx, access_type);
+ env->dmmu.sfar = addr;
+#else
+ env->mmuregs[4] = addr;
#endif
+
+ cpu_raise_exception_ra(env, TT_UNALIGNED, retaddr);
+}
+#endif /* !CONFIG_USER_ONLY */
diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c
index c1cbd03595..224f723236 100644
--- a/target/xtensa/cpu.c
+++ b/target/xtensa/cpu.c
@@ -192,10 +192,10 @@ static const struct SysemuCPUOps xtensa_sysemu_ops = {
static const struct TCGCPUOps xtensa_tcg_ops = {
.initialize = xtensa_translate_init,
- .tlb_fill = xtensa_cpu_tlb_fill,
.debug_excp_handler = xtensa_breakpoint_handler,
#ifndef CONFIG_USER_ONLY
+ .tlb_fill = xtensa_cpu_tlb_fill,
.cpu_exec_interrupt = xtensa_cpu_exec_interrupt,
.do_interrupt = xtensa_cpu_do_interrupt,
.do_transaction_failed = xtensa_cpu_do_transaction_failed,
diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h
index f9a510ca46..02143f2f77 100644
--- a/target/xtensa/cpu.h
+++ b/target/xtensa/cpu.h
@@ -563,10 +563,10 @@ struct XtensaCPU {
};
+#ifndef CONFIG_USER_ONLY
bool xtensa_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr);
-#ifndef CONFIG_USER_ONLY
void xtensa_cpu_do_interrupt(CPUState *cpu);
bool xtensa_cpu_exec_interrupt(CPUState *cpu, int interrupt_request);
void xtensa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
diff --git a/target/xtensa/helper.c b/target/xtensa/helper.c
index f18ab383fd..29d216ec1b 100644
--- a/target/xtensa/helper.c
+++ b/target/xtensa/helper.c
@@ -242,27 +242,7 @@ void xtensa_cpu_list(void)
}
}
-#ifdef CONFIG_USER_ONLY
-
-bool xtensa_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr)
-{
- XtensaCPU *cpu = XTENSA_CPU(cs);
- CPUXtensaState *env = &cpu->env;
-
- qemu_log_mask(CPU_LOG_INT,
- "%s: rw = %d, address = 0x%08" VADDR_PRIx ", size = %d\n",
- __func__, access_type, address, size);
- env->sregs[EXCVADDR] = address;
- env->sregs[EXCCAUSE] = (access_type == MMU_DATA_STORE ?
- STORE_PROHIBITED_CAUSE : LOAD_PROHIBITED_CAUSE);
- cs->exception_index = EXC_USER;
- cpu_loop_exit_restore(cs, retaddr);
-}
-
-#else /* !CONFIG_USER_ONLY */
-
+#ifndef CONFIG_USER_ONLY
void xtensa_cpu_do_unaligned_access(CPUState *cs,
vaddr addr, MMUAccessType access_type,
int mmu_idx, uintptr_t retaddr)