* 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 of the License, or (at your option) any later version.
+ * 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
#include "exec/cpu_ldst.h"
#include "translate-all.h"
#include "exec/helper-proto.h"
+#include "qemu/atomic128.h"
#undef EAX
#undef ECX
#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
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, unsigned long address,
+static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
int is_write, sigset_t *old_set)
{
CPUState *cpu = current_cpu;
CPUClass *cc;
- int ret;
+ unsigned long address = (unsigned long)info->si_addr;
+ MMUAccessType access_type;
+
+ /* We must handle PC addresses from two different sources:
+ * a call return address and a signal frame address.
+ *
+ * Within cpu_restore_state_from_tb we assume the former and adjust
+ * the address by -GETPC_ADJ so that the address is within the call
+ * insn so that addr does not accidentally match the beginning of the
+ * next guest insn.
+ *
+ * However, when the PC comes from the signal frame, it points to
+ * the actual faulting host insn and not a call insn. Subtracting
+ * GETPC_ADJ in that case may accidentally match the previous guest insn.
+ *
+ * So for the later case, adjust forward to compensate for what
+ * will be done later by cpu_restore_state_from_tb.
+ */
+ if (helper_retaddr) {
+ pc = helper_retaddr;
+ } else {
+ pc += GETPC_ADJ;
+ }
/* For synchronous signals we expect to be coming from the vCPU
* thread (so current_cpu should be valid) and either from running
pc, address, is_write, *(unsigned long *)old_set);
#endif
/* XXX: locking issue */
- if (is_write && h2g_valid(address)) {
+ /* 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
+ * cached translations, must be the guest binary's problem.
*/
break;
case 1:
/* Fault caused by protection of cached translation; TBs
- * invalidated, so resume execution
+ * 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.
+ * immediately. Clear helper_retaddr for next execution.
*/
+ clear_helper_retaddr();
cpu_exit_tb_from_sighandler(cpu, old_set);
- g_assert_not_reached();
+ /* NORETURN */
+
default:
g_assert_not_reached();
}
are still valid segv ones */
address = h2g_nocheck(address);
- cc = CPU_GET_CLASS(cpu);
- /* see if it is an MMU fault */
- g_assert(cc->handle_mmu_fault);
- ret = cc->handle_mmu_fault(cpu, address, is_write, MMU_USER_IDX);
- if (ret < 0) {
- return 0; /* not an MMU fault */
- }
- if (ret == 0) {
- return 1; /* the MMU fault was handled without causing real CPU fault */
- }
-
- /* Now we have a real cpu fault. Since this is the exact location of
- * the exception, we must undo the adjustment done by cpu_restore_state
- * for handling call return addresses. */
- cpu_restore_state(cpu, pc + GETPC_ADJ);
-
+ /*
+ * 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);
- cpu_loop_exit(cpu);
+ clear_helper_retaddr();
- /* never comes here */
- return 1;
+ cc = CPU_GET_CLASS(cpu);
+ access_type = is_write ? MMU_DATA_STORE : MMU_DATA_LOAD;
+ cc->tlb_fill(cpu, address, 0, access_type, MMU_USER_IDX, false, pc);
+ g_assert_not_reached();
}
#if defined(__i386__)
#endif
pc = EIP_sig(uc);
trapno = TRAP_sig(uc);
- return handle_cpu_signal(pc, (unsigned long)info->si_addr,
- trapno == 0xe ?
- (ERROR_sig(uc) >> 1) & 1 : 0,
+ return handle_cpu_signal(pc, info,
+ trapno == 0xe ? (ERROR_sig(uc) >> 1) & 1 : 0,
&MASK_sig(uc));
}
#endif
pc = PC_sig(uc);
- return handle_cpu_signal(pc, (unsigned long)info->si_addr,
- TRAP_sig(uc) == 0xe ?
- (ERROR_sig(uc) >> 1) & 1 : 0,
+ return handle_cpu_signal(pc, info,
+ TRAP_sig(uc) == 0xe ? (ERROR_sig(uc) >> 1) & 1 : 0,
&MASK_sig(uc));
}
is_write = 1;
}
#endif
- return handle_cpu_signal(pc, (unsigned long)info->si_addr,
- is_write, &uc->uc_sigmask);
+ return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
}
#elif defined(__alpha__)
is_write = 1;
}
- return handle_cpu_signal(pc, (unsigned long)info->si_addr,
- is_write, &uc->uc_sigmask);
+ return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
}
#elif defined(__sparc__)
break;
}
}
- return handle_cpu_signal(pc, (unsigned long)info->si_addr,
- is_write, sigmask);
+ return handle_cpu_signal(pc, info, is_write, sigmask);
}
#elif defined(__arm__)
* later processor; on v5 we will always report this as a read).
*/
is_write = extract32(uc->uc_mcontext.error_code, 11, 1);
- return handle_cpu_signal(pc, (unsigned long)info->si_addr,
- is_write,
- &uc->uc_sigmask);
+ return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
}
#elif defined(__aarch64__)
-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;
- bool is_write;
+#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
- /* XXX: need kernel patch to get write flag faster. */
- 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 */
- /* Ingore 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, (uintptr_t)info->si_addr,
- is_write, &uc->uc_sigmask);
+static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc)
+{
+ return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved;
}
-#elif defined(__ia64)
-
-#ifndef __ISR_VALID
- /* This ought to be in <bits/siginfo.h>... */
-# define __ISR_VALID 1
-#endif
+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;
- unsigned long ip;
- int is_write = 0;
+ uintptr_t pc = uc->uc_mcontext.pc;
+ bool is_write;
+ struct _aarch64_ctx *hdr;
+ struct esr_context const *esrctx = NULL;
- ip = uc->uc_mcontext.sc_ip;
- switch (host_signum) {
- case SIGILL:
- case SIGFPE:
- case SIGSEGV:
- case SIGBUS:
- case SIGTRAP:
- if (info->si_code && (info->si_segvflags & __ISR_VALID)) {
- /* ISR.W (write-access) is bit 33: */
- is_write = (info->si_isr >> 33) & 1;
+ /* 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;
}
- break;
+ }
- default:
- 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(ip, (unsigned long)info->si_addr,
- is_write,
- (sigset_t *)&uc->uc_sigmask);
+ return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
}
#elif defined(__s390__)
}
break;
}
- return handle_cpu_signal(pc, (unsigned long)info->si_addr,
- is_write, &uc->uc_sigmask);
+ return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
}
#elif defined(__mips__)
/* XXX: compute is_write */
is_write = 0;
- return handle_cpu_signal(pc, (unsigned long)info->si_addr,
- is_write, &uc->uc_sigmask);
+ 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
{
/* Enforce qemu required alignment. */
if (unlikely(addr & (size - 1))) {
- cpu_loop_exit_atomic(ENV_GET_CPU(env), retaddr);
+ cpu_loop_exit_atomic(env_cpu(env), retaddr);
}
- return g2h(addr);
+ void *ret = g2h(addr);
+ set_helper_retaddr(retaddr);
+ return ret;
}
/* Macro to call the above, with local variables from the use context. */
+#define ATOMIC_MMU_DECLS do {} while (0)
#define ATOMIC_MMU_LOOKUP atomic_mmu_lookup(env, addr, DATA_SIZE, GETPC())
+#define ATOMIC_MMU_CLEANUP do { clear_helper_retaddr(); } while (0)
#define ATOMIC_NAME(X) HELPER(glue(glue(atomic_ ## X, SUFFIX), END))
#define EXTRA_ARGS
/* The following is only callable from other helpers, and matches up
with the softmmu version. */
-#ifdef CONFIG_ATOMIC128
+#if HAVE_ATOMIC128 || HAVE_CMPXCHG128
#undef EXTRA_ARGS
#undef ATOMIC_NAME
#define DATA_SIZE 16
#include "atomic_template.h"
-#endif /* CONFIG_ATOMIC128 */
+#endif