Merge tag 'pull-target-arm-20231019' of https://git.linaro.org/people/pmaydell/qemu-arm into staging
target-arm queue:
* hw/arm: Move raspberrypi-fw-defs.h to the include/hw/arm/ folder
* hw/arm/exynos4210: Get arm_boot_info declaration from 'hw/arm/boot'
* xlnx devices: remove deprecated device reset
* xlnx-bbram: hw/nvram: Use dot in device type name
* elf2dmp: fix coverity issues
* elf2dmp: convert to g_malloc, g_new and g_free
* target/arm: Fix CNTPCT_EL0 trapping from EL0 when HCR_EL2.E2H is 0
* hw/arm: refactor virt PPI logic
* arm/kvm: convert to kvm_set_one_reg, kvm_get_one_reg
* target/arm: Permit T32 LDM with single register
* smmuv3: Advertise SMMUv3.1-XNX
* target/arm: Implement FEAT_HPMN0
* Remove some unnecessary include lines
* target/arm/arm-powerctl: Correctly init CPUs when powered on to lower EL
* hw/timer/npcm7xx_timer: Prevent timer from counting down past zero
# -----BEGIN PGP SIGNATURE-----
#
# iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmUxMF4ZHHBldGVyLm1h
# eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3oJND/4p64q0Wxq8x8yXCDUZAHME
# lZe2liBPBkqZusGfK0O4CpClwGbM5+8tMeLaRgSOUgJ/WGFiLCGAKEKB0S7EiCa5
# 1bNvVn+a7cdDj7FdYf+Dvp5fNZZIus4w+CUlUaiRyDhIfYquz53J1RD1wN5+SQ/I
# g6JQRp2gONeqGM5hT+0v2J/wGMmhuI5XO+PtQ1QNGoUnAA4QNof1thYjqdTJxzfz
# V2CUSOKnAT/PDcUWoy8BVPDDE+wYTnjTO1j/ZsQvnNQm7r18OiMUn85teLq1JtB+
# T3vyVZ2f2gc8lAgkKy5n3NH5fmLVgbO0WXgpWLHNkcp+shZMM6J5J/u/P6B/wk95
# DMzQy4slu/UfWMvsaxq+OjejhAtbdiIOeNfF6dAMy2NAyZplEAjlP8dsFrqAdACL
# 9m/DA4ODAV6OJ3E0zQ0dI4o6kr+/wbPVseLklqn3Ss0dndjU1K9XR0qpC8OruUJq
# 4h6kl5q6V3BHAoELvBtAqb0yHYdqhLqznpO8HsrUEmU5eTjDaOyyI4HW+AY5GG1R
# dtvrCLSiPe0EMartMMtezaB2GxQb9O7e+OI3XL2zVxb1F+QQ+vRZE3zVIdXm+Ev4
# oBztF1peZC3c8zurjr7/MxnDSnzynpkSR1zOY8+WJnAqpQ+C1YvdF6/Llwn7IMHw
# ZHh6sGzQsaAu7u/DW9yY5w==
# =WreO
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 19 Oct 2023 06:34:22 PDT
# gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE
# gpg: issuer "peter.maydell@linaro.org"
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [full]
# gpg: aka "Peter Maydell <pmaydell@gmail.com>" [full]
# gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [full]
# gpg: aka "Peter Maydell <peter@archaic.org.uk>" [unknown]
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE
* tag 'pull-target-arm-20231019' of https://git.linaro.org/people/pmaydell/qemu-arm: (24 commits)
contrib/elf2dmp: Use g_malloc(), g_new() and g_free()
hw/timer/npcm7xx_timer: Prevent timer from counting down past zero
target/arm/arm-powerctl: Correctly init CPUs when powered on to lower EL
target/arm/common-semi-target.h: Remove unnecessary boot.h include
target/arm/kvm64.c: Remove unused include
target/arm: Implement FEAT_HPMN0
hw/arm/smmuv3: Advertise SMMUv3.1-XNX feature
hw/arm/smmuv3: Sort ID register setting into field order
hw/arm/smmuv3: Update ID register bit field definitions
target/arm: Permit T32 LDM with single register
arm/kvm: convert to kvm_get_one_reg
arm/kvm: convert to kvm_set_one_reg
hw/arm/sbsa-ref: use bsa.h for PPI definitions
include/hw/arm: move BSA definitions to bsa.h
{include/}hw/arm: refactor virt PPI logic
target/arm: Fix CNTPCT_EL0 trapping from EL0 when HCR_EL2.E2H is 0
elf2dmp: check array bounds in pdb_get_file_size
elf2dmp: limit print length for sign_rsds
xlnx-bbram: hw/nvram: Use dot in device type name
xlnx-versal-efuse: hw/nvram: Remove deprecated device reset
...
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index f21e2e0..2e3809f 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1237,6 +1237,14 @@
hwcaps |= HWCAP_LOONGARCH_LAM;
}
+ if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) {
+ hwcaps |= HWCAP_LOONGARCH_LSX;
+ }
+
+ if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) {
+ hwcaps |= HWCAP_LOONGARCH_LASX;
+ }
+
return hwcaps;
}
@@ -2362,31 +2370,58 @@
* Map and zero the bss. We need to explicitly zero any fractional pages
* after the data section (i.e. bss). Return false on mapping failure.
*/
-static bool zero_bss(abi_ulong start_bss, abi_ulong end_bss, int prot)
+static bool zero_bss(abi_ulong start_bss, abi_ulong end_bss,
+ int prot, Error **errp)
{
abi_ulong align_bss;
+ /* We only expect writable bss; the code segment shouldn't need this. */
+ if (!(prot & PROT_WRITE)) {
+ error_setg(errp, "PT_LOAD with non-writable bss");
+ return false;
+ }
+
align_bss = TARGET_PAGE_ALIGN(start_bss);
end_bss = TARGET_PAGE_ALIGN(end_bss);
if (start_bss < align_bss) {
int flags = page_get_flags(start_bss);
- if (!(flags & PAGE_VALID)) {
- /* Map the start of the bss. */
+ if (!(flags & PAGE_BITS)) {
+ /*
+ * The whole address space of the executable was reserved
+ * at the start, therefore all pages will be VALID.
+ * But assuming there are no PROT_NONE PT_LOAD segments,
+ * a PROT_NONE page means no data all bss, and we can
+ * simply extend the new anon mapping back to the start
+ * of the page of bss.
+ */
align_bss -= TARGET_PAGE_SIZE;
- } else if (flags & PAGE_WRITE) {
- /* The page is already mapped writable. */
- memset(g2h_untagged(start_bss), 0, align_bss - start_bss);
} else {
- /* Read-only zeros? */
- g_assert_not_reached();
+ /*
+ * The start of the bss shares a page with something.
+ * The only thing that we expect is the data section,
+ * which would already be marked writable.
+ * Overlapping the RX code segment seems malformed.
+ */
+ if (!(flags & PAGE_WRITE)) {
+ error_setg(errp, "PT_LOAD with bss overlapping "
+ "non-writable page");
+ return false;
+ }
+
+ /* The page is already mapped and writable. */
+ memset(g2h_untagged(start_bss), 0, align_bss - start_bss);
}
}
- return align_bss >= end_bss ||
- target_mmap(align_bss, end_bss - align_bss, prot,
- MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0) != -1;
+ if (align_bss < end_bss &&
+ target_mmap(align_bss, end_bss - align_bss, prot,
+ MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0) == -1) {
+ error_setg_errno(errp, errno, "Error mapping bss");
+ return false;
+ }
+ return true;
}
#if defined(TARGET_ARM)
@@ -3410,8 +3445,8 @@
/* If the load segment requests extra zeros (e.g. bss), map it. */
if (vaddr_ef < vaddr_em &&
- !zero_bss(vaddr_ef, vaddr_em, elf_prot)) {
- goto exit_mmap;
+ !zero_bss(vaddr_ef, vaddr_em, elf_prot, &err)) {
+ goto exit_errmsg;
}
/* Find the full program boundaries. */
diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c
index 8735e58..990b03e 100644
--- a/linux-user/mips/cpu_loop.c
+++ b/linux-user/mips/cpu_loop.c
@@ -180,7 +180,9 @@
}
force_sig_fault(TARGET_SIGFPE, si_code, env->active_tc.PC);
break;
-
+ case EXCP_OVERFLOW:
+ force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTOVF, env->active_tc.PC);
+ break;
/* The code below was inspired by the MIPS Linux kernel trap
* handling code in arch/mips/kernel/traps.c.
*/
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index 8ccaab7..7b44b9f 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -778,7 +778,7 @@
return -1;
}
-static void mmap_reserve_or_unmap(abi_ulong start, abi_ulong len)
+static int mmap_reserve_or_unmap(abi_ulong start, abi_ulong len)
{
abi_ulong real_start;
abi_ulong real_last;
@@ -807,7 +807,7 @@
prot |= page_get_flags(a + 1);
}
if (prot != 0) {
- return;
+ return 0;
}
} else {
for (prot = 0, a = real_start; a < start; a += TARGET_PAGE_SIZE) {
@@ -825,7 +825,7 @@
}
if (real_last < real_start) {
- return;
+ return 0;
}
}
@@ -836,32 +836,36 @@
void *ptr = mmap(host_start, real_len, PROT_NONE,
MAP_FIXED | MAP_ANONYMOUS
| MAP_PRIVATE | MAP_NORESERVE, -1, 0);
- assert(ptr == host_start);
- } else {
- int ret = munmap(host_start, real_len);
- assert(ret == 0);
+ return ptr == host_start ? 0 : -1;
}
+ return munmap(host_start, real_len);
}
int target_munmap(abi_ulong start, abi_ulong len)
{
+ int ret;
+
trace_target_munmap(start, len);
if (start & ~TARGET_PAGE_MASK) {
- return -TARGET_EINVAL;
+ errno = EINVAL;
+ return -1;
}
len = TARGET_PAGE_ALIGN(len);
if (len == 0 || !guest_range_valid_untagged(start, len)) {
- return -TARGET_EINVAL;
+ errno = EINVAL;
+ return -1;
}
mmap_lock();
- mmap_reserve_or_unmap(start, len);
- page_set_flags(start, start + len - 1, 0);
- shm_region_rm_complete(start, start + len - 1);
+ ret = mmap_reserve_or_unmap(start, len);
+ if (likely(ret == 0)) {
+ page_set_flags(start, start + len - 1, 0);
+ shm_region_rm_complete(start, start + len - 1);
+ }
mmap_unlock();
- return 0;
+ return ret;
}
abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c
index c4ba962..c16c2c2 100644
--- a/linux-user/sh4/signal.c
+++ b/linux-user/sh4/signal.c
@@ -104,6 +104,14 @@
/* Reset the SP to the saved version in R1. */
regs->gregs[15] = regs->gregs[1];
+ } else if (regs->gregs[15] >= -128u && regs->pc == regs->gregs[0]) {
+ /* If we are on the last instruction of a gUSA region, we must reset
+ the SP, otherwise we would be pushing the signal context to
+ invalid memory. */
+ regs->gregs[15] = regs->gregs[1];
+ } else if (regs->flags & TB_FLAG_DELAY_SLOT) {
+ /* If we are in a delay slot, push the previous instruction. */
+ regs->pc -= 2;
}
}
diff --git a/linux-user/signal.c b/linux-user/signal.c
index a67ab47..3b8efec 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -32,6 +32,7 @@
#include "signal-common.h"
#include "host-signal.h"
#include "user/safe-syscall.h"
+#include "tcg/tcg.h"
static struct target_sigaction sigact_table[TARGET_NSIG];
@@ -43,9 +44,8 @@
abi_ulong default_rt_sigreturn;
/*
- * System includes define _NSIG as SIGRTMAX + 1,
- * but qemu (like the kernel) defines TARGET_NSIG as TARGET_SIGRTMAX
- * and the first signal is SIGHUP defined as 1
+ * System includes define _NSIG as SIGRTMAX + 1, but qemu (like the kernel)
+ * defines TARGET_NSIG as TARGET_SIGRTMAX and the first signal is 1.
* Signal number 0 is reserved for use as kill(pid, 0), to test whether
* a process exists without sending it a signal.
*/
@@ -56,7 +56,6 @@
#define MAKE_SIG_ENTRY(sig) [sig] = TARGET_##sig,
MAKE_SIGNAL_LIST
#undef MAKE_SIG_ENTRY
- /* next signals stay the same */
};
static uint8_t target_to_host_signal_table[TARGET_NSIG + 1];
@@ -64,18 +63,24 @@
/* valid sig is between 1 and _NSIG - 1 */
int host_to_target_signal(int sig)
{
- if (sig < 1 || sig >= _NSIG) {
+ if (sig < 1) {
return sig;
}
+ if (sig >= _NSIG) {
+ return TARGET_NSIG + 1;
+ }
return host_to_target_signal_table[sig];
}
/* valid sig is between 1 and TARGET_NSIG */
int target_to_host_signal(int sig)
{
- if (sig < 1 || sig > TARGET_NSIG) {
+ if (sig < 1) {
return sig;
}
+ if (sig > TARGET_NSIG) {
+ return _NSIG;
+ }
return target_to_host_signal_table[sig];
}
@@ -487,26 +492,6 @@
info->si_value.sival_ptr = (void *)(long)sival_ptr;
}
-static int fatal_signal (int sig)
-{
- switch (sig) {
- case TARGET_SIGCHLD:
- case TARGET_SIGURG:
- case TARGET_SIGWINCH:
- /* Ignored by default. */
- return 0;
- case TARGET_SIGCONT:
- case TARGET_SIGSTOP:
- case TARGET_SIGTSTP:
- case TARGET_SIGTTIN:
- case TARGET_SIGTTOU:
- /* Job control signals. */
- return 0;
- default:
- return 1;
- }
-}
-
/* returns 1 if given signal should dump core if not handled */
static int core_dump_signal(int sig)
{
@@ -526,57 +511,69 @@
static void signal_table_init(void)
{
- int host_sig, target_sig, count;
+ int hsig, tsig, count;
/*
* Signals are supported starting from TARGET_SIGRTMIN and going up
- * until we run out of host realtime signals.
- * glibc at least uses only the lower 2 rt signals and probably
- * nobody's using the upper ones.
- * it's why SIGRTMIN (34) is generally greater than __SIGRTMIN (32)
- * To fix this properly we need to do manual signal delivery multiplexed
- * over a single host signal.
+ * until we run out of host realtime signals. Glibc uses the lower 2
+ * RT signals and (hopefully) nobody uses the upper ones.
+ * This is why SIGRTMIN (34) is generally greater than __SIGRTMIN (32).
+ * To fix this properly we would need to do manual signal delivery
+ * multiplexed over a single host signal.
* Attempts for configure "missing" signals via sigaction will be
* silently ignored.
+ *
+ * Remap the target SIGABRT, so that we can distinguish host abort
+ * from guest abort. When the guest registers a signal handler or
+ * calls raise(SIGABRT), the host will raise SIG_RTn. If the guest
+ * arrives at dump_core_and_abort(), we will map back to host SIGABRT
+ * so that the parent (native or emulated) sees the correct signal.
+ * Finally, also map host to guest SIGABRT so that the emulated
+ * parent sees the correct mapping from wait status.
*/
- for (host_sig = SIGRTMIN; host_sig <= SIGRTMAX; host_sig++) {
- target_sig = host_sig - SIGRTMIN + TARGET_SIGRTMIN;
- if (target_sig <= TARGET_NSIG) {
- host_to_target_signal_table[host_sig] = target_sig;
+
+ hsig = SIGRTMIN;
+ host_to_target_signal_table[SIGABRT] = 0;
+ host_to_target_signal_table[hsig++] = TARGET_SIGABRT;
+
+ for (; hsig <= SIGRTMAX; hsig++) {
+ tsig = hsig - SIGRTMIN + TARGET_SIGRTMIN;
+ if (tsig <= TARGET_NSIG) {
+ host_to_target_signal_table[hsig] = tsig;
}
}
- /* generate signal conversion tables */
- for (target_sig = 1; target_sig <= TARGET_NSIG; target_sig++) {
- target_to_host_signal_table[target_sig] = _NSIG; /* poison */
- }
- for (host_sig = 1; host_sig < _NSIG; host_sig++) {
- if (host_to_target_signal_table[host_sig] == 0) {
- host_to_target_signal_table[host_sig] = host_sig;
- }
- target_sig = host_to_target_signal_table[host_sig];
- if (target_sig <= TARGET_NSIG) {
- target_to_host_signal_table[target_sig] = host_sig;
+ /* Invert the mapping that has already been assigned. */
+ for (hsig = 1; hsig < _NSIG; hsig++) {
+ tsig = host_to_target_signal_table[hsig];
+ if (tsig) {
+ assert(target_to_host_signal_table[tsig] == 0);
+ target_to_host_signal_table[tsig] = hsig;
}
}
- if (trace_event_get_state_backends(TRACE_SIGNAL_TABLE_INIT)) {
- for (target_sig = 1, count = 0; target_sig <= TARGET_NSIG; target_sig++) {
- if (target_to_host_signal_table[target_sig] == _NSIG) {
- count++;
- }
+ host_to_target_signal_table[SIGABRT] = TARGET_SIGABRT;
+
+ /* Map everything else out-of-bounds. */
+ for (hsig = 1; hsig < _NSIG; hsig++) {
+ if (host_to_target_signal_table[hsig] == 0) {
+ host_to_target_signal_table[hsig] = TARGET_NSIG + 1;
}
- trace_signal_table_init(count);
}
+ for (count = 0, tsig = 1; tsig <= TARGET_NSIG; tsig++) {
+ if (target_to_host_signal_table[tsig] == 0) {
+ target_to_host_signal_table[tsig] = _NSIG;
+ count++;
+ }
+ }
+
+ trace_signal_table_init(count);
}
void signal_init(void)
{
TaskState *ts = (TaskState *)thread_cpu->opaque;
- struct sigaction act;
- struct sigaction oact;
- int i;
- int host_sig;
+ struct sigaction act, oact;
/* initialize signal conversion tables */
signal_table_init();
@@ -587,22 +584,36 @@
sigfillset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = host_signal_handler;
- for(i = 1; i <= TARGET_NSIG; i++) {
- host_sig = target_to_host_signal(i);
- sigaction(host_sig, NULL, &oact);
- if (oact.sa_sigaction == (void *)SIG_IGN) {
- sigact_table[i - 1]._sa_handler = TARGET_SIG_IGN;
- } else if (oact.sa_sigaction == (void *)SIG_DFL) {
- sigact_table[i - 1]._sa_handler = TARGET_SIG_DFL;
+
+ /*
+ * A parent process may configure ignored signals, but all other
+ * signals are default. For any target signals that have no host
+ * mapping, set to ignore. For all core_dump_signal, install our
+ * host signal handler so that we may invoke dump_core_and_abort.
+ * This includes SIGSEGV and SIGBUS, which are also need our signal
+ * handler for paging and exceptions.
+ */
+ for (int tsig = 1; tsig <= TARGET_NSIG; tsig++) {
+ int hsig = target_to_host_signal(tsig);
+ abi_ptr thand = TARGET_SIG_IGN;
+
+ if (hsig >= _NSIG) {
+ continue;
}
- /* If there's already a handler installed then something has
- gone horribly wrong, so don't even try to handle that case. */
- /* Install some handlers for our own use. We need at least
- SIGSEGV and SIGBUS, to detect exceptions. We can not just
- trap all signals because it affects syscall interrupt
- behavior. But do trap all default-fatal signals. */
- if (fatal_signal (i))
- sigaction(host_sig, &act, NULL);
+
+ /* As we force remap SIGABRT, cannot probe and install in one step. */
+ if (tsig == TARGET_SIGABRT) {
+ sigaction(SIGABRT, NULL, &oact);
+ sigaction(hsig, &act, NULL);
+ } else {
+ struct sigaction *iact = core_dump_signal(tsig) ? &act : NULL;
+ sigaction(hsig, iact, &oact);
+ }
+
+ if (oact.sa_sigaction != (void *)SIG_IGN) {
+ thand = TARGET_SIG_DFL;
+ }
+ sigact_table[tsig - 1]._sa_handler = thand;
}
}
@@ -690,14 +701,45 @@
/* abort execution with signal */
static G_NORETURN
+void die_with_signal(int host_sig)
+{
+ struct sigaction act = {
+ .sa_handler = SIG_DFL,
+ };
+
+ /*
+ * The proper exit code for dying from an uncaught signal is -<signal>.
+ * The kernel doesn't allow exit() or _exit() to pass a negative value.
+ * To get the proper exit code we need to actually die from an uncaught
+ * signal. Here the default signal handler is installed, we send
+ * the signal and we wait for it to arrive.
+ */
+ sigfillset(&act.sa_mask);
+ sigaction(host_sig, &act, NULL);
+
+ kill(getpid(), host_sig);
+
+ /* Make sure the signal isn't masked (reusing the mask inside of act). */
+ sigdelset(&act.sa_mask, host_sig);
+ sigsuspend(&act.sa_mask);
+
+ /* unreachable */
+ _exit(EXIT_FAILURE);
+}
+
+static G_NORETURN
void dump_core_and_abort(CPUArchState *env, int target_sig)
{
CPUState *cpu = env_cpu(env);
TaskState *ts = (TaskState *)cpu->opaque;
int host_sig, core_dumped = 0;
- struct sigaction act;
- host_sig = target_to_host_signal(target_sig);
+ /* On exit, undo the remapping of SIGABRT. */
+ if (target_sig == TARGET_SIGABRT) {
+ host_sig = SIGABRT;
+ } else {
+ host_sig = target_to_host_signal(target_sig);
+ }
trace_user_dump_core_and_abort(env, target_sig, host_sig);
gdb_signalled(env, target_sig);
@@ -719,29 +761,7 @@
}
preexit_cleanup(env, 128 + target_sig);
-
- /* The proper exit code for dying from an uncaught signal is
- * -<signal>. The kernel doesn't allow exit() or _exit() to pass
- * a negative value. To get the proper exit code we need to
- * actually die from an uncaught signal. Here the default signal
- * handler is installed, we send ourself a signal and we wait for
- * it to arrive. */
- sigfillset(&act.sa_mask);
- act.sa_handler = SIG_DFL;
- act.sa_flags = 0;
- sigaction(host_sig, &act, NULL);
-
- /* For some reason raise(host_sig) doesn't send the signal when
- * statically linked on x86-64. */
- kill(getpid(), host_sig);
-
- /* Make sure the signal isn't masked (just reuse the mask inside
- of act) */
- sigdelset(&act.sa_mask, host_sig);
- sigsuspend(&act.sa_mask);
-
- /* unreachable */
- abort();
+ die_with_signal(host_sig);
}
/* queue a signal so that it will be send to the virtual CPU as soon
@@ -775,6 +795,161 @@
}
}
+static G_NORETURN
+void die_from_signal(siginfo_t *info)
+{
+ char sigbuf[4], codebuf[12];
+ const char *sig, *code = NULL;
+
+ switch (info->si_signo) {
+ case SIGSEGV:
+ sig = "SEGV";
+ switch (info->si_code) {
+ case SEGV_MAPERR:
+ code = "MAPERR";
+ break;
+ case SEGV_ACCERR:
+ code = "ACCERR";
+ break;
+ }
+ break;
+ case SIGBUS:
+ sig = "BUS";
+ switch (info->si_code) {
+ case BUS_ADRALN:
+ code = "ADRALN";
+ break;
+ case BUS_ADRERR:
+ code = "ADRERR";
+ break;
+ }
+ break;
+ case SIGILL:
+ sig = "ILL";
+ switch (info->si_code) {
+ case ILL_ILLOPC:
+ code = "ILLOPC";
+ break;
+ case ILL_ILLOPN:
+ code = "ILLOPN";
+ break;
+ case ILL_ILLADR:
+ code = "ILLADR";
+ break;
+ case ILL_PRVOPC:
+ code = "PRVOPC";
+ break;
+ case ILL_PRVREG:
+ code = "PRVREG";
+ break;
+ case ILL_COPROC:
+ code = "COPROC";
+ break;
+ }
+ break;
+ case SIGFPE:
+ sig = "FPE";
+ switch (info->si_code) {
+ case FPE_INTDIV:
+ code = "INTDIV";
+ break;
+ case FPE_INTOVF:
+ code = "INTOVF";
+ break;
+ }
+ break;
+ case SIGTRAP:
+ sig = "TRAP";
+ break;
+ default:
+ snprintf(sigbuf, sizeof(sigbuf), "%d", info->si_signo);
+ sig = sigbuf;
+ break;
+ }
+ if (code == NULL) {
+ snprintf(codebuf, sizeof(sigbuf), "%d", info->si_code);
+ code = codebuf;
+ }
+
+ error_report("QEMU internal SIG%s {code=%s, addr=%p}",
+ sig, code, info->si_addr);
+ die_with_signal(info->si_signo);
+}
+
+static void host_sigsegv_handler(CPUState *cpu, siginfo_t *info,
+ host_sigcontext *uc)
+{
+ uintptr_t 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.
+ */
+ bool is_valid = h2g_valid(host_addr);
+ abi_ptr guest_addr = h2g_nocheck(host_addr);
+ uintptr_t pc = host_signal_pc(uc);
+ bool is_write = host_signal_write(info, uc);
+ MMUAccessType access_type = adjust_signal_pc(&pc, is_write);
+ bool maperr;
+
+ /* If this was a write to a TB protected page, restart. */
+ if (is_write
+ && is_valid
+ && info->si_code == SEGV_ACCERR
+ && handle_sigsegv_accerr_write(cpu, host_signal_mask(uc),
+ pc, guest_addr)) {
+ return;
+ }
+
+ /*
+ * If the access was not on behalf of the guest, within the executable
+ * mapping of the generated code buffer, then it is a host bug.
+ */
+ if (access_type != MMU_INST_FETCH
+ && !in_code_gen_buffer((void *)(pc - tcg_splitwx_diff))) {
+ die_from_signal(info);
+ }
+
+ maperr = true;
+ if (is_valid && info->si_code == SEGV_ACCERR) {
+ /*
+ * 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, host_signal_mask(uc), NULL);
+ cpu_loop_exit_sigsegv(cpu, guest_addr, access_type, maperr, pc);
+}
+
+static void host_sigbus_handler(CPUState *cpu, siginfo_t *info,
+ host_sigcontext *uc)
+{
+ uintptr_t pc = host_signal_pc(uc);
+ bool is_write = host_signal_write(info, uc);
+ MMUAccessType access_type = adjust_signal_pc(&pc, is_write);
+
+ /*
+ * If the access was not on behalf of the guest, within the executable
+ * mapping of the generated code buffer, then it is a host bug.
+ */
+ if (!in_code_gen_buffer((void *)(pc - tcg_splitwx_diff))) {
+ die_from_signal(info);
+ }
+
+ if (info->si_code == BUS_ADRALN) {
+ uintptr_t host_addr = (uintptr_t)info->si_addr;
+ abi_ptr guest_addr = h2g_nocheck(host_addr);
+
+ sigprocmask(SIG_SETMASK, host_signal_mask(uc), NULL);
+ cpu_loop_exit_sigbus(cpu, guest_addr, access_type, pc);
+ }
+}
+
static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
{
CPUState *cpu = thread_cpu;
@@ -786,61 +961,28 @@
int guest_sig;
uintptr_t pc = 0;
bool sync_sig = false;
- void *sigmask = host_signal_mask(uc);
+ void *sigmask;
/*
* Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special
- * handling wrt signal blocking and unwinding.
+ * handling wrt signal blocking and unwinding. Non-spoofed SIGILL,
+ * SIGFPE, SIGTRAP are always host bugs.
*/
- if ((host_sig == SIGSEGV || host_sig == SIGBUS) && info->si_code > 0) {
- MMUAccessType access_type;
- uintptr_t host_addr;
- abi_ptr guest_addr;
- bool is_write;
-
- host_addr = (uintptr_t)info->si_addr;
-
- /*
- * Convert forcefully to guest address space: addresses outside
- * reserved_va are still valid to report via SEGV_MAPERR.
- */
- guest_addr = h2g_nocheck(host_addr);
-
- pc = host_signal_pc(uc);
- is_write = host_signal_write(info, uc);
- access_type = adjust_signal_pc(&pc, is_write);
-
- if (host_sig == SIGSEGV) {
- bool maperr = true;
-
- if (info->si_code == SEGV_ACCERR && h2g_valid(host_addr)) {
- /* If this was a write to a TB protected page, restart. */
- if (is_write &&
- handle_sigsegv_accerr_write(cpu, 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, sigmask, NULL);
- cpu_loop_exit_sigsegv(cpu, guest_addr, access_type, maperr, pc);
- } else {
- sigprocmask(SIG_SETMASK, sigmask, NULL);
- if (info->si_code == BUS_ADRALN) {
- cpu_loop_exit_sigbus(cpu, guest_addr, access_type, pc);
- }
+ if (info->si_code > 0) {
+ switch (host_sig) {
+ case SIGSEGV:
+ /* Only returns on handle_sigsegv_accerr_write success. */
+ host_sigsegv_handler(cpu, info, uc);
+ return;
+ case SIGBUS:
+ host_sigbus_handler(cpu, info, uc);
+ sync_sig = true;
+ break;
+ case SIGILL:
+ case SIGFPE:
+ case SIGTRAP:
+ die_from_signal(info);
}
-
- sync_sig = true;
}
/* get target signal number */
@@ -881,6 +1023,7 @@
* would write 0xff bytes off the end of the structure and trash
* data on the struct.
*/
+ sigmask = host_signal_mask(uc);
memset(sigmask, 0xff, SIGSET_T_SIZE);
sigdelset(sigmask, SIGSEGV);
sigdelset(sigmask, SIGBUS);
@@ -936,7 +1079,6 @@
struct target_sigaction *oact, abi_ulong ka_restorer)
{
struct target_sigaction *k;
- struct sigaction act1;
int host_sig;
int ret = 0;
@@ -996,22 +1138,27 @@
return 0;
}
if (host_sig != SIGSEGV && host_sig != SIGBUS) {
+ struct sigaction act1;
+
sigfillset(&act1.sa_mask);
act1.sa_flags = SA_SIGINFO;
- if (k->sa_flags & TARGET_SA_RESTART)
- act1.sa_flags |= SA_RESTART;
- /* NOTE: it is important to update the host kernel signal
- ignore state to avoid getting unexpected interrupted
- syscalls */
if (k->_sa_handler == TARGET_SIG_IGN) {
+ /*
+ * It is important to update the host kernel signal ignore
+ * state to avoid getting unexpected interrupted syscalls.
+ */
act1.sa_sigaction = (void *)SIG_IGN;
} else if (k->_sa_handler == TARGET_SIG_DFL) {
- if (fatal_signal (sig))
+ if (core_dump_signal(sig)) {
act1.sa_sigaction = host_signal_handler;
- else
+ } else {
act1.sa_sigaction = (void *)SIG_DFL;
+ }
} else {
act1.sa_sigaction = host_signal_handler;
+ if (k->sa_flags & TARGET_SA_RESTART) {
+ act1.sa_flags |= SA_RESTART;
+ }
}
ret = sigaction(host_sig, &act1, NULL);
}
diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
index d206700..a82597f 100644
--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -30,6 +30,7 @@
#include "sysemu/runstate.h"
#include "ui/qemu-spice.h"
#include "sysemu/sysemu.h"
+#include "options.h"
#include "migration.h"
static void migration_global_dump(Monitor *mon)
@@ -696,7 +697,6 @@
typedef struct HMPMigrationStatus {
QEMUTimer *timer;
Monitor *mon;
- bool is_block_migration;
} HMPMigrationStatus;
static void hmp_migrate_status_cb(void *opaque)
@@ -722,7 +722,7 @@
timer_mod(status->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000);
} else {
- if (status->is_block_migration) {
+ if (migrate_block()) {
monitor_printf(status->mon, "\n");
}
if (info->error_desc) {
@@ -762,7 +762,6 @@
status = g_malloc0(sizeof(*status));
status->mon = mon;
- status->is_block_migration = blk || inc;
status->timer = timer_new_ms(QEMU_CLOCK_REALTIME, hmp_migrate_status_cb,
status);
timer_mod(status->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
diff --git a/migration/migration.c b/migration/migration.c
index 6ba5e14..05c0b80 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -447,6 +447,18 @@
socket_start_incoming_migration(p ? p : uri, errp);
#ifdef CONFIG_RDMA
} else if (strstart(uri, "rdma:", &p)) {
+ if (migrate_compress()) {
+ error_setg(errp, "RDMA and compression can't be used together");
+ return;
+ }
+ if (migrate_xbzrle()) {
+ error_setg(errp, "RDMA and XBZRLE can't be used together");
+ return;
+ }
+ if (migrate_multifd()) {
+ error_setg(errp, "RDMA and multifd can't be used together");
+ return;
+ }
rdma_start_incoming_migration(p, errp);
#endif
} else if (strstart(uri, "exec:", &p)) {
@@ -962,16 +974,7 @@
info->xbzrle_cache->overflow = xbzrle_counters.overflow;
}
- if (migrate_compress()) {
- info->compression = g_malloc0(sizeof(*info->compression));
- info->compression->pages = compression_counters.pages;
- info->compression->busy = compression_counters.busy;
- info->compression->busy_rate = compression_counters.busy_rate;
- info->compression->compressed_size =
- compression_counters.compressed_size;
- info->compression->compression_rate =
- compression_counters.compression_rate;
- }
+ populate_compress(info);
if (cpu_throttle_active()) {
info->has_cpu_throttle_percentage = true;
@@ -1454,11 +1457,9 @@
s->switchover_acked = false;
s->rdma_migration = false;
/*
- * set mig_stats compression_counters memory to zero for a
- * new migration
+ * set mig_stats memory to zero for a new migration
*/
memset(&mig_stats, 0, sizeof(mig_stats));
- memset(&compression_counters, 0, sizeof(compression_counters));
migration_reset_vfio_bytes_transferred();
return 0;
diff --git a/migration/ram-compress.c b/migration/ram-compress.c
index 06254d8..d037dfe 100644
--- a/migration/ram-compress.c
+++ b/migration/ram-compress.c
@@ -32,11 +32,14 @@
#include "ram-compress.h"
#include "qemu/error-report.h"
+#include "qemu/stats64.h"
#include "migration.h"
#include "options.h"
#include "io/channel-null.h"
#include "exec/target_page.h"
#include "exec/ramblock.h"
+#include "ram.h"
+#include "migration-stats.h"
CompressionStats compression_counters;
@@ -227,27 +230,25 @@
void flush_compressed_data(int (send_queued_data(CompressParam *)))
{
- int idx, thread_count;
-
- thread_count = migrate_compress_threads();
+ int thread_count = migrate_compress_threads();
qemu_mutex_lock(&comp_done_lock);
- for (idx = 0; idx < thread_count; idx++) {
- while (!comp_param[idx].done) {
+ for (int i = 0; i < thread_count; i++) {
+ while (!comp_param[i].done) {
qemu_cond_wait(&comp_done_cond, &comp_done_lock);
}
}
qemu_mutex_unlock(&comp_done_lock);
- for (idx = 0; idx < thread_count; idx++) {
- qemu_mutex_lock(&comp_param[idx].mutex);
- if (!comp_param[idx].quit) {
- CompressParam *param = &comp_param[idx];
+ for (int i = 0; i < thread_count; i++) {
+ qemu_mutex_lock(&comp_param[i].mutex);
+ if (!comp_param[i].quit) {
+ CompressParam *param = &comp_param[i];
send_queued_data(param);
assert(qemu_file_buffer_empty(param->file));
compress_reset_result(param);
}
- qemu_mutex_unlock(&comp_param[idx].mutex);
+ qemu_mutex_unlock(&comp_param[i].mutex);
}
}
@@ -262,15 +263,15 @@
int compress_page_with_multi_thread(RAMBlock *block, ram_addr_t offset,
int (send_queued_data(CompressParam *)))
{
- int idx, thread_count, pages = -1;
+ int thread_count, pages = -1;
bool wait = migrate_compress_wait_thread();
thread_count = migrate_compress_threads();
qemu_mutex_lock(&comp_done_lock);
retry:
- for (idx = 0; idx < thread_count; idx++) {
- if (comp_param[idx].done) {
- CompressParam *param = &comp_param[idx];
+ for (int i = 0; i < thread_count; i++) {
+ if (comp_param[i].done) {
+ CompressParam *param = &comp_param[i];
qemu_mutex_lock(¶m->mutex);
param->done = false;
send_queued_data(param);
@@ -364,16 +365,14 @@
int wait_for_decompress_done(void)
{
- int idx, thread_count;
-
if (!migrate_compress()) {
return 0;
}
- thread_count = migrate_decompress_threads();
+ int thread_count = migrate_decompress_threads();
qemu_mutex_lock(&decomp_done_lock);
- for (idx = 0; idx < thread_count; idx++) {
- while (!decomp_param[idx].done) {
+ for (int i = 0; i < thread_count; i++) {
+ while (!decomp_param[i].done) {
qemu_cond_wait(&decomp_done_cond, &decomp_done_lock);
}
}
@@ -430,6 +429,11 @@
return 0;
}
+ /*
+ * set compression_counters memory to zero for a new migration
+ */
+ memset(&compression_counters, 0, sizeof(compression_counters));
+
thread_count = migrate_decompress_threads();
decompress_threads = g_new0(QemuThread, thread_count);
decomp_param = g_new0(DecompressParam, thread_count);
@@ -459,27 +463,54 @@
void decompress_data_with_multi_threads(QEMUFile *f, void *host, int len)
{
- int idx, thread_count;
-
- thread_count = migrate_decompress_threads();
+ int thread_count = migrate_decompress_threads();
QEMU_LOCK_GUARD(&decomp_done_lock);
while (true) {
- for (idx = 0; idx < thread_count; idx++) {
- if (decomp_param[idx].done) {
- decomp_param[idx].done = false;
- qemu_mutex_lock(&decomp_param[idx].mutex);
- qemu_get_buffer(f, decomp_param[idx].compbuf, len);
- decomp_param[idx].des = host;
- decomp_param[idx].len = len;
- qemu_cond_signal(&decomp_param[idx].cond);
- qemu_mutex_unlock(&decomp_param[idx].mutex);
- break;
+ for (int i = 0; i < thread_count; i++) {
+ if (decomp_param[i].done) {
+ decomp_param[i].done = false;
+ qemu_mutex_lock(&decomp_param[i].mutex);
+ qemu_get_buffer(f, decomp_param[i].compbuf, len);
+ decomp_param[i].des = host;
+ decomp_param[i].len = len;
+ qemu_cond_signal(&decomp_param[i].cond);
+ qemu_mutex_unlock(&decomp_param[i].mutex);
+ return;
}
}
- if (idx < thread_count) {
- break;
- } else {
- qemu_cond_wait(&decomp_done_cond, &decomp_done_lock);
- }
+ qemu_cond_wait(&decomp_done_cond, &decomp_done_lock);
}
}
+
+void populate_compress(MigrationInfo *info)
+{
+ if (!migrate_compress()) {
+ return;
+ }
+ info->compression = g_malloc0(sizeof(*info->compression));
+ info->compression->pages = compression_counters.pages;
+ info->compression->busy = compression_counters.busy;
+ info->compression->busy_rate = compression_counters.busy_rate;
+ info->compression->compressed_size = compression_counters.compressed_size;
+ info->compression->compression_rate = compression_counters.compression_rate;
+}
+
+uint64_t ram_compressed_pages(void)
+{
+ return compression_counters.pages;
+}
+
+void update_compress_thread_counts(const CompressParam *param, int bytes_xmit)
+{
+ ram_transferred_add(bytes_xmit);
+
+ if (param->result == RES_ZEROPAGE) {
+ stat64_add(&mig_stats.zero_pages, 1);
+ return;
+ }
+
+ /* 8 means a header with RAM_SAVE_FLAG_CONTINUE. */
+ compression_counters.compressed_size += bytes_xmit - 8;
+ compression_counters.pages++;
+}
+
diff --git a/migration/ram-compress.h b/migration/ram-compress.h
index 6f7fe2f..e55d3b5 100644
--- a/migration/ram-compress.h
+++ b/migration/ram-compress.h
@@ -30,6 +30,7 @@
#define QEMU_MIGRATION_COMPRESS_H
#include "qemu-file.h"
+#include "qapi/qapi-types-migration.h"
enum CompressResult {
RES_NONE = 0,
@@ -67,4 +68,8 @@
int compress_threads_load_setup(QEMUFile *f);
void decompress_data_with_multi_threads(QEMUFile *f, void *host, int len);
+void populate_compress(MigrationInfo *info);
+uint64_t ram_compressed_pages(void);
+void update_compress_thread_counts(const CompressParam *param, int bytes_xmit);
+
#endif
diff --git a/migration/ram.c b/migration/ram.c
index c844151..16c30a9 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -939,7 +939,7 @@
{
return stat64_get(&mig_stats.normal_pages) +
stat64_get(&mig_stats.zero_pages) +
- compression_counters.pages + xbzrle_counters.pages;
+ ram_compressed_pages() + xbzrle_counters.pages;
}
static void migration_update_rates(RAMState *rs, int64_t end_time)
@@ -1144,13 +1144,12 @@
*
* @rs: current RAM state
* @pss: current PSS channel
- * @block: block that contains the page we want to send
* @offset: offset inside the block for the page
*/
-static int save_zero_page(RAMState *rs, PageSearchStatus *pss, RAMBlock *block,
+static int save_zero_page(RAMState *rs, PageSearchStatus *pss,
ram_addr_t offset)
{
- uint8_t *p = block->host + offset;
+ uint8_t *p = pss->block->host + offset;
QEMUFile *file = pss->pss_channel;
int len = 0;
@@ -1158,10 +1157,10 @@
return 0;
}
- len += save_page_header(pss, file, block, offset | RAM_SAVE_FLAG_ZERO);
+ len += save_page_header(pss, file, pss->block, offset | RAM_SAVE_FLAG_ZERO);
qemu_put_byte(file, 0);
len += 1;
- ram_release_page(block->idstr, offset);
+ ram_release_page(pss->block->idstr, offset);
stat64_add(&mig_stats.zero_pages, 1);
ram_transferred_add(len);
@@ -1172,7 +1171,7 @@
*/
if (rs->xbzrle_started) {
XBZRLE_cache_lock();
- xbzrle_cache_zero_page(block->offset + offset);
+ xbzrle_cache_zero_page(pss->block->offset + offset);
XBZRLE_cache_unlock();
}
@@ -1186,12 +1185,12 @@
*
* Return true if the pages has been saved, otherwise false is returned.
*/
-static bool control_save_page(PageSearchStatus *pss, RAMBlock *block,
+static bool control_save_page(PageSearchStatus *pss,
ram_addr_t offset, int *pages)
{
int ret;
- ret = rdma_control_save_page(pss->pss_channel, block->offset, offset,
+ ret = rdma_control_save_page(pss->pss_channel, pss->block->offset, offset,
TARGET_PAGE_SIZE);
if (ret == RAM_SAVE_CONTROL_NOT_SUPP) {
return false;
@@ -1292,21 +1291,6 @@
return 1;
}
-static void
-update_compress_thread_counts(const CompressParam *param, int bytes_xmit)
-{
- ram_transferred_add(bytes_xmit);
-
- if (param->result == RES_ZEROPAGE) {
- stat64_add(&mig_stats.zero_pages, 1);
- return;
- }
-
- /* 8 means a header with RAM_SAVE_FLAG_CONTINUE. */
- compression_counters.compressed_size += bytes_xmit - 8;
- compression_counters.pages++;
-}
-
static bool save_page_use_compression(RAMState *rs);
static int send_queued_data(CompressParam *param)
@@ -2082,7 +2066,7 @@
* paths to handle it
*/
static bool save_compress_page(RAMState *rs, PageSearchStatus *pss,
- RAMBlock *block, ram_addr_t offset)
+ ram_addr_t offset)
{
if (!save_page_use_compression(rs)) {
return false;
@@ -2098,12 +2082,13 @@
* We post the fist page as normal page as compression will take
* much CPU resource.
*/
- if (block != pss->last_sent_block) {
+ if (pss->block != pss->last_sent_block) {
ram_flush_compressed_data(rs);
return false;
}
- if (compress_page_with_multi_thread(block, offset, send_queued_data) > 0) {
+ if (compress_page_with_multi_thread(pss->block, offset,
+ send_queued_data) > 0) {
return true;
}
@@ -2125,15 +2110,15 @@
ram_addr_t offset = ((ram_addr_t)pss->page) << TARGET_PAGE_BITS;
int res;
- if (control_save_page(pss, block, offset, &res)) {
+ if (control_save_page(pss, offset, &res)) {
return res;
}
- if (save_compress_page(rs, pss, block, offset)) {
+ if (save_compress_page(rs, pss, offset)) {
return 1;
}
- if (save_zero_page(rs, pss, block, offset)) {
+ if (save_zero_page(rs, pss, offset)) {
return 1;
}