aboutsummaryrefslogtreecommitdiff
path: root/accel
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2018-01-25 09:53:53 +0000
committerPeter Maydell <peter.maydell@linaro.org>2018-01-25 09:53:53 +0000
commit0f79bfe38a2cf0f43c7ea4959da7f8ebd7858f3d (patch)
tree80981403f3a897a1e20808e3c7a76fe082ddfc30 /accel
parentf78b6f9b1161fb67f3ac54e73f7f37464cbff76f (diff)
parent95d0307cc10ca3df879c1be519f1ad650efb20a8 (diff)
Merge remote-tracking branch 'remotes/vivier2/tags/linux-user-for-2.12-pull-request' into staging
# gpg: Signature made Tue 23 Jan 2018 14:47:41 GMT # gpg: using RSA key 0xF30C38BD3F2FBE3C # gpg: Good signature from "Laurent Vivier <lvivier@redhat.com>" # gpg: aka "Laurent Vivier <laurent@vivier.eu>" # gpg: aka "Laurent Vivier (Red Hat) <lvivier@redhat.com>" # Primary key fingerprint: CD2F 75DD C8E3 A4DC 2E4F 5173 F30C 38BD 3F2F BE3C * remotes/vivier2/tags/linux-user-for-2.12-pull-request: linux-user: implement renameat2 page_unprotect(): handle calls to pages that are PAGE_WRITE linux-user: Propagate siginfo_t through to handle_cpu_signal() linux-user: remove nmi.c and fw-path-provider.c linux-user: Add getcpu() support linux-user: Add AT_SECURE auxval linux-user: Fix sched_get/setaffinity conversion linux-user/mmap.c: Avoid choosing NULL as start address linux-user: Translate flags argument to dup3 syscall linux-user: Don't use CMSG_ALIGN(sizeof struct cmsghdr) linux-user: Fix length calculations in host_to_target_cmsg() linux-user: wrap fork() in a start/end exclusive section linux-user: Fix locking order in fork_start() Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'accel')
-rw-r--r--accel/tcg/translate-all.c50
-rw-r--r--accel/tcg/user-exec.c52
2 files changed, 57 insertions, 45 deletions
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
index 7736257085..67795cd78c 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -2181,29 +2181,41 @@ int page_unprotect(target_ulong address, uintptr_t pc)
/* if the page was really writable, then we change its
protection back to writable */
- if ((p->flags & PAGE_WRITE_ORG) && !(p->flags & PAGE_WRITE)) {
- host_start = address & qemu_host_page_mask;
- host_end = host_start + qemu_host_page_size;
-
- prot = 0;
+ if (p->flags & PAGE_WRITE_ORG) {
current_tb_invalidated = false;
- for (addr = host_start ; addr < host_end ; addr += TARGET_PAGE_SIZE) {
- p = page_find(addr >> TARGET_PAGE_BITS);
- p->flags |= PAGE_WRITE;
- prot |= p->flags;
-
- /* and since the content will be modified, we must invalidate
- the corresponding translated code. */
- current_tb_invalidated |= tb_invalidate_phys_page(addr, pc);
-#ifdef CONFIG_USER_ONLY
- if (DEBUG_TB_CHECK_GATE) {
- tb_invalidate_check(addr);
+ if (p->flags & PAGE_WRITE) {
+ /* If the page is actually marked WRITE then assume this is because
+ * this thread raced with another one which got here first and
+ * set the page to PAGE_WRITE and did the TB invalidate for us.
+ */
+#ifdef TARGET_HAS_PRECISE_SMC
+ TranslationBlock *current_tb = tb_find_pc(pc);
+ if (current_tb) {
+ current_tb_invalidated = tb_cflags(current_tb) & CF_INVALID;
}
#endif
+ } else {
+ host_start = address & qemu_host_page_mask;
+ host_end = host_start + qemu_host_page_size;
+
+ prot = 0;
+ for (addr = host_start; addr < host_end; addr += TARGET_PAGE_SIZE) {
+ p = page_find(addr >> TARGET_PAGE_BITS);
+ p->flags |= PAGE_WRITE;
+ prot |= p->flags;
+
+ /* and since the content will be modified, we must invalidate
+ the corresponding translated code. */
+ current_tb_invalidated |= tb_invalidate_phys_page(addr, pc);
+#ifdef CONFIG_USER_ONLY
+ if (DEBUG_TB_CHECK_GATE) {
+ tb_invalidate_check(addr);
+ }
+#endif
+ }
+ mprotect((void *)g2h(host_start), qemu_host_page_size,
+ prot & PAGE_BITS);
}
- mprotect((void *)g2h(host_start), qemu_host_page_size,
- prot & PAGE_BITS);
-
mmap_unlock();
/* If current TB was invalidated return to main loop */
return current_tb_invalidated ? 2 : 1;
diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c
index f42285ea1c..c973752562 100644
--- a/accel/tcg/user-exec.c
+++ b/accel/tcg/user-exec.c
@@ -57,12 +57,13 @@ static void cpu_exit_tb_from_sighandler(CPUState *cpu, sigset_t *old_set)
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;
/* We must handle PC addresses from two different sources:
* a call return address and a signal frame address.
@@ -103,7 +104,18 @@ static inline int handle_cpu_signal(uintptr_t pc, unsigned long address,
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
@@ -215,9 +227,8 @@ int cpu_signal_handler(int host_signum, void *pinfo,
#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));
}
@@ -261,9 +272,8 @@ int cpu_signal_handler(int host_signum, void *pinfo,
#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));
}
@@ -341,8 +351,7 @@ int cpu_signal_handler(int host_signum, void *pinfo,
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__)
@@ -372,8 +381,7 @@ int cpu_signal_handler(int host_signum, void *pinfo,
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__)
@@ -432,8 +440,7 @@ int cpu_signal_handler(int host_signum, void *pinfo,
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__)
@@ -466,9 +473,7 @@ int cpu_signal_handler(int host_signum, void *pinfo,
* 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__)
@@ -495,8 +500,7 @@ int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
/* 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);
+ return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
}
#elif defined(__ia64)
@@ -529,9 +533,7 @@ int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
default:
break;
}
- return handle_cpu_signal(ip, (unsigned long)info->si_addr,
- is_write,
- (sigset_t *)&uc->uc_sigmask);
+ return handle_cpu_signal(ip, info, is_write, (sigset_t *)&uc->uc_sigmask);
}
#elif defined(__s390__)
@@ -583,8 +585,7 @@ int cpu_signal_handler(int host_signum, void *pinfo,
}
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__)
@@ -599,8 +600,7 @@ int cpu_signal_handler(int host_signum, void *pinfo,
/* 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);
}
#else