aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--accel/kvm/kvm-all.c56
-rw-r--r--accel/tcg/atomic_template.h20
-rw-r--r--accel/tcg/cpu-exec.c2
-rw-r--r--accel/tcg/cputlb.c235
-rw-r--r--accel/tcg/softmmu_template.h64
-rw-r--r--accel/tcg/tcg-all.c2
-rw-r--r--accel/tcg/translate-all.c2
-rw-r--r--accel/tcg/user-exec.c5
-rwxr-xr-xconfigure192
-rw-r--r--cpus.c7
-rw-r--r--crypto/Makefile.objs8
-rw-r--r--crypto/init.c23
-rw-r--r--crypto/tlscredsx509.c21
-rw-r--r--crypto/tlssession.c8
-rw-r--r--default-configs/hyperv.mak2
-rw-r--r--default-configs/i386-softmmu.mak2
-rw-r--r--docs/COLO-FT.txt34
-rw-r--r--exec.c1
-rw-r--r--hw/Makefile.objs1
-rw-r--r--hw/audio/es1370.c10
-rw-r--r--hw/core/hotplug.c10
-rw-r--r--hw/core/qdev.c16
-rw-r--r--hw/hyperv/Makefile.objs2
-rw-r--r--hw/hyperv/hyperv.c654
-rw-r--r--hw/hyperv/hyperv_testdev.c327
-rw-r--r--hw/misc/Makefile.objs1
-rw-r--r--hw/misc/edu.c4
-rw-r--r--hw/misc/hyperv_testdev.c174
-rw-r--r--hw/net/e1000.c16
-rw-r--r--hw/net/ne2000.c4
-rw-r--r--hw/net/pcnet.c4
-rw-r--r--hw/net/rtl8139.c8
-rw-r--r--hw/net/trace-events3
-rw-r--r--hw/pci-host/piix.c4
-rw-r--r--hw/pci-host/q35.c4
-rw-r--r--hw/scsi/scsi-disk.c29
-rw-r--r--hw/scsi/virtio-scsi.c11
-rw-r--r--hw/timer/mc146818rtc.c9
-rw-r--r--hw/virtio/virtio.c2
-rw-r--r--include/block/aio.h59
-rw-r--r--include/exec/cpu-defs.h3
-rw-r--r--include/exec/cpu_ldst.h30
-rw-r--r--include/exec/cpu_ldst_template.h25
-rw-r--r--include/exec/exec-all.h8
-rw-r--r--include/exec/memory.h4
-rw-r--r--include/exec/ram_addr.h1
-rw-r--r--include/hw/hotplug.h11
-rw-r--r--include/hw/hyperv/hyperv-proto.h130
-rw-r--r--include/hw/hyperv/hyperv.h83
-rw-r--r--include/hw/i386/pc.h8
-rw-r--r--include/migration/colo.h11
-rw-r--r--include/net/filter.h5
-rw-r--r--include/qemu/atomic128.h153
-rw-r--r--include/qemu/compiler.h11
-rw-r--r--include/qemu/osdep.h12
-rw-r--r--include/qemu/timer.h127
-rw-r--r--include/sysemu/hvf.h4
-rw-r--r--include/sysemu/replay.h10
-rw-r--r--linux-headers/asm-powerpc/kvm.h1
-rw-r--r--linux-headers/asm-x86/kvm.h8
-rw-r--r--linux-headers/linux/kvm.h16
-rw-r--r--linux-user/ioctls.h46
-rw-r--r--linux-user/syscall.c180
-rw-r--r--linux-user/syscall_defs.h28
-rw-r--r--linux-user/syscall_types.h68
-rw-r--r--memory.c4
-rw-r--r--migration/Makefile.objs2
-rw-r--r--migration/colo-comm.c76
-rw-r--r--migration/colo-failover.c2
-rw-r--r--migration/colo.c212
-rw-r--r--migration/migration.c46
-rw-r--r--migration/ram.c166
-rw-r--r--migration/ram.h4
-rw-r--r--migration/savevm.c53
-rw-r--r--migration/savevm.h5
-rw-r--r--migration/trace-events3
-rw-r--r--monitor.c13
-rw-r--r--net/colo-compare.c115
-rw-r--r--net/colo-compare.h24
-rw-r--r--net/colo.c10
-rw-r--r--net/colo.h11
-rw-r--r--net/filter-rewriter.c166
-rw-r--r--net/filter.c17
-rw-r--r--net/net.c26
-rw-r--r--net/slirp.c21
-rw-r--r--qapi/migration.json80
-rw-r--r--qapi/misc.json3
-rw-r--r--qapi/net.json5
-rw-r--r--qemu-options.hx23
-rw-r--r--qom/cpu.c2
-rw-r--r--replay/replay-events.c1
-rw-r--r--replay/replay-internal.c25
-rw-r--r--replay/replay-internal.h2
-rw-r--r--replay/replay-time.c8
-rw-r--r--replay/replay.c9
-rw-r--r--slirp/bootp.c45
-rw-r--r--slirp/bootp.h1
-rw-r--r--slirp/ip6_icmp.c9
-rw-r--r--slirp/libslirp.h1
-rw-r--r--slirp/slirp.c2
-rw-r--r--slirp/slirp.h1
-rw-r--r--stubs/cpu-get-icount.c5
-rw-r--r--stubs/replay.c2
-rw-r--r--target/alpha/cpu.c1
-rw-r--r--target/arm/helper-a64.c253
-rw-r--r--target/arm/translate-a64.c38
-rw-r--r--target/i386/Makefile.objs17
-rw-r--r--target/i386/cpu.c3
-rw-r--r--target/i386/cpu.h2
-rw-r--r--target/i386/hvf/hvf.c9
-rw-r--r--target/i386/hvf/x86_decode.c67
-rw-r--r--target/i386/hvf/x86_decode.h6
-rw-r--r--target/i386/hyperv-proto.h116
-rw-r--r--target/i386/hyperv-stub.c48
-rw-r--r--target/i386/hyperv.c130
-rw-r--r--target/i386/hyperv.h28
-rw-r--r--target/i386/kvm.c68
-rw-r--r--target/i386/machine.c9
-rw-r--r--target/i386/mem_helper.c9
-rw-r--r--target/ppc/helper.h2
-rw-r--r--target/ppc/mem_helper.c33
-rw-r--r--target/ppc/translate.c115
-rw-r--r--target/s390x/mem_helper.c196
-rw-r--r--target/s390x/translate.c45
-rw-r--r--target/unicore32/cpu.c2
-rw-r--r--tcg/tcg-op.c9
-rw-r--r--tcg/tcg.c25
-rw-r--r--tcg/tcg.h20
-rw-r--r--tests/Makefile.include2
-rw-r--r--tests/crypto-tls-x509-helpers.h3
-rw-r--r--tests/ptimer-test-stubs.c13
-rw-r--r--tests/test-crypto-block.c2
-rw-r--r--tests/test-crypto-tlscredsx509.c8
-rw-r--r--ui/input.c9
-rw-r--r--util/qemu-timer.c65
-rw-r--r--vl.c114
136 files changed, 4136 insertions, 1550 deletions
diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index de12f78eb8..4880a05399 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -79,6 +79,7 @@ struct KVMState
int fd;
int vmfd;
int coalesced_mmio;
+ int coalesced_pio;
struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
bool coalesced_flush_in_progress;
int vcpu_events;
@@ -560,6 +561,45 @@ static void kvm_uncoalesce_mmio_region(MemoryListener *listener,
}
}
+static void kvm_coalesce_pio_add(MemoryListener *listener,
+ MemoryRegionSection *section,
+ hwaddr start, hwaddr size)
+{
+ KVMState *s = kvm_state;
+
+ if (s->coalesced_pio) {
+ struct kvm_coalesced_mmio_zone zone;
+
+ zone.addr = start;
+ zone.size = size;
+ zone.pio = 1;
+
+ (void)kvm_vm_ioctl(s, KVM_REGISTER_COALESCED_MMIO, &zone);
+ }
+}
+
+static void kvm_coalesce_pio_del(MemoryListener *listener,
+ MemoryRegionSection *section,
+ hwaddr start, hwaddr size)
+{
+ KVMState *s = kvm_state;
+
+ if (s->coalesced_pio) {
+ struct kvm_coalesced_mmio_zone zone;
+
+ zone.addr = start;
+ zone.size = size;
+ zone.pio = 1;
+
+ (void)kvm_vm_ioctl(s, KVM_UNREGISTER_COALESCED_MMIO, &zone);
+ }
+}
+
+static MemoryListener kvm_coalesced_pio_listener = {
+ .coalesced_io_add = kvm_coalesce_pio_add,
+ .coalesced_io_del = kvm_coalesce_pio_del,
+};
+
int kvm_check_extension(KVMState *s, unsigned int extension)
{
int ret;
@@ -1616,6 +1656,8 @@ static int kvm_init(MachineState *ms)
}
s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO);
+ s->coalesced_pio = s->coalesced_mmio &&
+ kvm_check_extension(s, KVM_CAP_COALESCED_PIO);
#ifdef KVM_CAP_VCPU_EVENTS
s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS);
@@ -1686,13 +1728,15 @@ static int kvm_init(MachineState *ms)
s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add;
s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del;
}
- s->memory_listener.listener.coalesced_mmio_add = kvm_coalesce_mmio_region;
- s->memory_listener.listener.coalesced_mmio_del = kvm_uncoalesce_mmio_region;
+ s->memory_listener.listener.coalesced_io_add = kvm_coalesce_mmio_region;
+ s->memory_listener.listener.coalesced_io_del = kvm_uncoalesce_mmio_region;
kvm_memory_listener_register(s, &s->memory_listener,
&address_space_memory, 0);
memory_listener_register(&kvm_io_listener,
&address_space_io);
+ memory_listener_register(&kvm_coalesced_pio_listener,
+ &address_space_io);
s->many_ioeventfds = kvm_check_many_ioeventfds();
@@ -1778,7 +1822,13 @@ void kvm_flush_coalesced_mmio_buffer(void)
ent = &ring->coalesced_mmio[ring->first];
- cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len);
+ if (ent->pio == 1) {
+ address_space_rw(&address_space_io, ent->phys_addr,
+ MEMTXATTRS_UNSPECIFIED, ent->data,
+ ent->len, true);
+ } else {
+ cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len);
+ }
smp_wmb();
ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX;
}
diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h
index d751bcba48..efde12fdb2 100644
--- a/accel/tcg/atomic_template.h
+++ b/accel/tcg/atomic_template.h
@@ -100,19 +100,24 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
DATA_TYPE ret;
ATOMIC_TRACE_RMW;
+#if DATA_SIZE == 16
+ ret = atomic16_cmpxchg(haddr, cmpv, newv);
+#else
ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
+#endif
ATOMIC_MMU_CLEANUP;
return ret;
}
#if DATA_SIZE >= 16
+#if HAVE_ATOMIC128
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
{
ATOMIC_MMU_DECLS;
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
ATOMIC_TRACE_LD;
- __atomic_load(haddr, &val, __ATOMIC_RELAXED);
+ val = atomic16_read(haddr);
ATOMIC_MMU_CLEANUP;
return val;
}
@@ -124,9 +129,10 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
ATOMIC_TRACE_ST;
- __atomic_store(haddr, &val, __ATOMIC_RELAXED);
+ atomic16_set(haddr, val);
ATOMIC_MMU_CLEANUP;
}
+#endif
#else
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
ABI_TYPE val EXTRA_ARGS)
@@ -228,19 +234,24 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
DATA_TYPE ret;
ATOMIC_TRACE_RMW;
+#if DATA_SIZE == 16
+ ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv));
+#else
ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
+#endif
ATOMIC_MMU_CLEANUP;
return BSWAP(ret);
}
#if DATA_SIZE >= 16
+#if HAVE_ATOMIC128
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
{
ATOMIC_MMU_DECLS;
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
ATOMIC_TRACE_LD;
- __atomic_load(haddr, &val, __ATOMIC_RELAXED);
+ val = atomic16_read(haddr);
ATOMIC_MMU_CLEANUP;
return BSWAP(val);
}
@@ -253,9 +264,10 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
ATOMIC_TRACE_ST;
val = BSWAP(val);
- __atomic_store(haddr, &val, __ATOMIC_RELAXED);
+ atomic16_set(haddr, val);
ATOMIC_MMU_CLEANUP;
}
+#endif
#else
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
ABI_TYPE val EXTRA_ARGS)
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index 6bcb6d99bd..870027d435 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -416,7 +416,7 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
}
#endif
/* See if we can patch the calling TB. */
- if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
+ if (last_tb) {
tb_add_jump(last_tb, tb_exit, tb);
}
return tb;
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
index f4702ce91f..af57aca5e4 100644
--- a/accel/tcg/cputlb.c
+++ b/accel/tcg/cputlb.c
@@ -32,6 +32,7 @@
#include "exec/log.h"
#include "exec/helper-proto.h"
#include "qemu/atomic.h"
+#include "qemu/atomic128.h"
/* DEBUG defines, enable DEBUG_TLB_LOG to log to the CPU_LOG_MMU target */
/* #define DEBUG_TLB */
@@ -58,9 +59,9 @@
} \
} while (0)
-#define assert_cpu_is_self(this_cpu) do { \
+#define assert_cpu_is_self(cpu) do { \
if (DEBUG_TLB_GATE) { \
- g_assert(!cpu->created || qemu_cpu_is_self(cpu)); \
+ g_assert(!(cpu)->created || qemu_cpu_is_self(cpu)); \
} \
} while (0)
@@ -73,6 +74,13 @@ QEMU_BUILD_BUG_ON(sizeof(target_ulong) > sizeof(run_on_cpu_data));
QEMU_BUILD_BUG_ON(NB_MMU_MODES > 16);
#define ALL_MMUIDX_BITS ((1 << NB_MMU_MODES) - 1)
+void tlb_init(CPUState *cpu)
+{
+ CPUArchState *env = cpu->env_ptr;
+
+ qemu_spin_init(&env->tlb_lock);
+}
+
/* flush_all_helper: run fn across all cpus
*
* If the wait flag is set then the src cpu's helper will be queued as
@@ -125,8 +133,17 @@ static void tlb_flush_nocheck(CPUState *cpu)
atomic_set(&env->tlb_flush_count, env->tlb_flush_count + 1);
tlb_debug("(count: %zu)\n", tlb_flush_count());
+ /*
+ * tlb_table/tlb_v_table updates from any thread must hold tlb_lock.
+ * However, updates from the owner thread (as is the case here; see the
+ * above assert_cpu_is_self) do not need atomic_set because all reads
+ * that do not hold the lock are performed by the same owner thread.
+ */
+ qemu_spin_lock(&env->tlb_lock);
memset(env->tlb_table, -1, sizeof(env->tlb_table));
memset(env->tlb_v_table, -1, sizeof(env->tlb_v_table));
+ qemu_spin_unlock(&env->tlb_lock);
+
cpu_tb_jmp_cache_clear(cpu);
env->vtlb_index = 0;
@@ -178,6 +195,7 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data)
tlb_debug("start: mmu_idx:0x%04lx\n", mmu_idx_bitmask);
+ qemu_spin_lock(&env->tlb_lock);
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
if (test_bit(mmu_idx, &mmu_idx_bitmask)) {
@@ -187,6 +205,7 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data)
memset(env->tlb_v_table[mmu_idx], -1, sizeof(env->tlb_v_table[0]));
}
}
+ qemu_spin_unlock(&env->tlb_lock);
cpu_tb_jmp_cache_clear(cpu);
@@ -239,23 +258,28 @@ static inline bool tlb_hit_page_anyprot(CPUTLBEntry *tlb_entry,
target_ulong page)
{
return tlb_hit_page(tlb_entry->addr_read, page) ||
- tlb_hit_page(tlb_entry->addr_write, page) ||
+ tlb_hit_page(tlb_addr_write(tlb_entry), page) ||
tlb_hit_page(tlb_entry->addr_code, page);
}
-static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong page)
+/* Called with tlb_lock held */
+static inline void tlb_flush_entry_locked(CPUTLBEntry *tlb_entry,
+ target_ulong page)
{
if (tlb_hit_page_anyprot(tlb_entry, page)) {
memset(tlb_entry, -1, sizeof(*tlb_entry));
}
}
-static inline void tlb_flush_vtlb_page(CPUArchState *env, int mmu_idx,
- target_ulong page)
+/* Called with tlb_lock held */
+static inline void tlb_flush_vtlb_page_locked(CPUArchState *env, int mmu_idx,
+ target_ulong page)
{
int k;
+
+ assert_cpu_is_self(ENV_GET_CPU(env));
for (k = 0; k < CPU_VTLB_SIZE; k++) {
- tlb_flush_entry(&env->tlb_v_table[mmu_idx][k], page);
+ tlb_flush_entry_locked(&env->tlb_v_table[mmu_idx][k], page);
}
}
@@ -263,7 +287,6 @@ static void tlb_flush_page_async_work(CPUState *cpu, run_on_cpu_data data)
{
CPUArchState *env = cpu->env_ptr;
target_ulong addr = (target_ulong) data.target_ptr;
- int i;
int mmu_idx;
assert_cpu_is_self(cpu);
@@ -281,11 +304,12 @@ static void tlb_flush_page_async_work(CPUState *cpu, run_on_cpu_data data)
}
addr &= TARGET_PAGE_MASK;
- i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ qemu_spin_lock(&env->tlb_lock);
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
- tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr);
- tlb_flush_vtlb_page(env, mmu_idx, addr);
+ tlb_flush_entry_locked(tlb_entry(env, mmu_idx, addr), addr);
+ tlb_flush_vtlb_page_locked(env, mmu_idx, addr);
}
+ qemu_spin_unlock(&env->tlb_lock);
tb_flush_jmp_cache(cpu, addr);
}
@@ -314,20 +338,21 @@ static void tlb_flush_page_by_mmuidx_async_work(CPUState *cpu,
target_ulong addr_and_mmuidx = (target_ulong) data.target_ptr;
target_ulong addr = addr_and_mmuidx & TARGET_PAGE_MASK;
unsigned long mmu_idx_bitmap = addr_and_mmuidx & ALL_MMUIDX_BITS;
- int page = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
int mmu_idx;
assert_cpu_is_self(cpu);
- tlb_debug("page:%d addr:"TARGET_FMT_lx" mmu_idx:0x%lx\n",
- page, addr, mmu_idx_bitmap);
+ tlb_debug("flush page addr:"TARGET_FMT_lx" mmu_idx:0x%lx\n",
+ addr, mmu_idx_bitmap);
+ qemu_spin_lock(&env->tlb_lock);
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
if (test_bit(mmu_idx, &mmu_idx_bitmap)) {
- tlb_flush_entry(&env->tlb_table[mmu_idx][page], addr);
- tlb_flush_vtlb_page(env, mmu_idx, addr);
+ tlb_flush_entry_locked(tlb_entry(env, mmu_idx, addr), addr);
+ tlb_flush_vtlb_page_locked(env, mmu_idx, addr);
}
}
+ qemu_spin_unlock(&env->tlb_lock);
tb_flush_jmp_cache(cpu, addr);
}
@@ -450,72 +475,44 @@ void tlb_unprotect_code(ram_addr_t ram_addr)
* most usual is detecting writes to code regions which may invalidate
* generated code.
*
- * Because we want other vCPUs to respond to changes straight away we
- * update the te->addr_write field atomically. If the TLB entry has
- * been changed by the vCPU in the mean time we skip the update.
+ * Other vCPUs might be reading their TLBs during guest execution, so we update
+ * te->addr_write with atomic_set. We don't need to worry about this for
+ * oversized guests as MTTCG is disabled for them.
*
- * As this function uses atomic accesses we also need to ensure
- * updates to tlb_entries follow the same access rules. We don't need
- * to worry about this for oversized guests as MTTCG is disabled for
- * them.
+ * Called with tlb_lock held.
*/
-
-static void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, uintptr_t start,
- uintptr_t length)
+static void tlb_reset_dirty_range_locked(CPUTLBEntry *tlb_entry,
+ uintptr_t start, uintptr_t length)
{
-#if TCG_OVERSIZED_GUEST
uintptr_t addr = tlb_entry->addr_write;
if ((addr & (TLB_INVALID_MASK | TLB_MMIO | TLB_NOTDIRTY)) == 0) {
addr &= TARGET_PAGE_MASK;
addr += tlb_entry->addend;
if ((addr - start) < length) {
+#if TCG_OVERSIZED_GUEST
tlb_entry->addr_write |= TLB_NOTDIRTY;
- }
- }
#else
- /* paired with atomic_mb_set in tlb_set_page_with_attrs */
- uintptr_t orig_addr = atomic_mb_read(&tlb_entry->addr_write);
- uintptr_t addr = orig_addr;
-
- if ((addr & (TLB_INVALID_MASK | TLB_MMIO | TLB_NOTDIRTY)) == 0) {
- addr &= TARGET_PAGE_MASK;
- addr += atomic_read(&tlb_entry->addend);
- if ((addr - start) < length) {
- uintptr_t notdirty_addr = orig_addr | TLB_NOTDIRTY;
- atomic_cmpxchg(&tlb_entry->addr_write, orig_addr, notdirty_addr);
+ atomic_set(&tlb_entry->addr_write,
+ tlb_entry->addr_write | TLB_NOTDIRTY);
+#endif
}
}
-#endif
}
-/* For atomic correctness when running MTTCG we need to use the right
- * primitives when copying entries */
-static inline void copy_tlb_helper(CPUTLBEntry *d, CPUTLBEntry *s,
- bool atomic_set)
+/*
+ * Called with tlb_lock held.
+ * Called only from the vCPU context, i.e. the TLB's owner thread.
+ */
+static inline void copy_tlb_helper_locked(CPUTLBEntry *d, const CPUTLBEntry *s)
{
-#if TCG_OVERSIZED_GUEST
*d = *s;
-#else
- if (atomic_set) {
- d->addr_read = s->addr_read;
- d->addr_code = s->addr_code;
- atomic_set(&d->addend, atomic_read(&s->addend));
- /* Pairs with flag setting in tlb_reset_dirty_range */
- atomic_mb_set(&d->addr_write, atomic_read(&s->addr_write));
- } else {
- d->addr_read = s->addr_read;
- d->addr_write = atomic_read(&s->addr_write);
- d->addr_code = s->addr_code;
- d->addend = atomic_read(&s->addend);
- }
-#endif
}
/* This is a cross vCPU call (i.e. another vCPU resetting the flags of
- * the target vCPU). As such care needs to be taken that we don't
- * dangerously race with another vCPU update. The only thing actually
- * updated is the target TLB entry ->addr_write flags.
+ * the target vCPU).
+ * We must take tlb_lock to avoid racing with another vCPU update. The only
+ * thing actually updated is the target TLB entry ->addr_write flags.
*/
void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length)
{
@@ -524,22 +521,26 @@ void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length)
int mmu_idx;
env = cpu->env_ptr;
+ qemu_spin_lock(&env->tlb_lock);
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
unsigned int i;
for (i = 0; i < CPU_TLB_SIZE; i++) {
- tlb_reset_dirty_range(&env->tlb_table[mmu_idx][i],
- start1, length);
+ tlb_reset_dirty_range_locked(&env->tlb_table[mmu_idx][i], start1,
+ length);
}
for (i = 0; i < CPU_VTLB_SIZE; i++) {
- tlb_reset_dirty_range(&env->tlb_v_table[mmu_idx][i],
- start1, length);
+ tlb_reset_dirty_range_locked(&env->tlb_v_table[mmu_idx][i], start1,
+ length);
}
}
+ qemu_spin_unlock(&env->tlb_lock);
}
-static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, target_ulong vaddr)
+/* Called with tlb_lock held */
+static inline void tlb_set_dirty1_locked(CPUTLBEntry *tlb_entry,
+ target_ulong vaddr)
{
if (tlb_entry->addr_write == (vaddr | TLB_NOTDIRTY)) {
tlb_entry->addr_write = vaddr;
@@ -551,23 +552,23 @@ static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, target_ulong vaddr)
void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
{
CPUArchState *env = cpu->env_ptr;
- int i;
int mmu_idx;
assert_cpu_is_self(cpu);
vaddr &= TARGET_PAGE_MASK;
- i = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ qemu_spin_lock(&env->tlb_lock);
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
- tlb_set_dirty1(&env->tlb_table[mmu_idx][i], vaddr);
+ tlb_set_dirty1_locked(tlb_entry(env, mmu_idx, vaddr), vaddr);
}
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
int k;
for (k = 0; k < CPU_VTLB_SIZE; k++) {
- tlb_set_dirty1(&env->tlb_v_table[mmu_idx][k], vaddr);
+ tlb_set_dirty1_locked(&env->tlb_v_table[mmu_idx][k], vaddr);
}
}
+ qemu_spin_unlock(&env->tlb_lock);
}
/* Our TLB does not support large pages, so remember the area covered by
@@ -654,15 +655,24 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
addend = (uintptr_t)memory_region_get_ram_ptr(section->mr) + xlat;
}
- /* Make sure there's no cached translation for the new page. */
- tlb_flush_vtlb_page(env, mmu_idx, vaddr_page);
-
code_address = address;
iotlb = memory_region_section_get_iotlb(cpu, section, vaddr_page,
paddr_page, xlat, prot, &address);
- index = (vaddr_page >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
- te = &env->tlb_table[mmu_idx][index];
+ index = tlb_index(env, mmu_idx, vaddr_page);
+ te = tlb_entry(env, mmu_idx, vaddr_page);
+
+ /*
+ * Hold the TLB lock for the rest of the function. We could acquire/release
+ * the lock several times in the function, but it is faster to amortize the
+ * acquisition cost by acquiring it just once. Note that this leads to
+ * a longer critical section, but this is not a concern since the TLB lock
+ * is unlikely to be contended.
+ */
+ qemu_spin_lock(&env->tlb_lock);
+
+ /* Make sure there's no cached translation for the new page. */
+ tlb_flush_vtlb_page_locked(env, mmu_idx, vaddr_page);
/*
* Only evict the old entry to the victim tlb if it's for a
@@ -673,7 +683,7 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
CPUTLBEntry *tv = &env->tlb_v_table[mmu_idx][vidx];
/* Evict the old entry into the victim tlb. */
- copy_tlb_helper(tv, te, true);
+ copy_tlb_helper_locked(tv, te);
env->iotlb_v[mmu_idx][vidx] = env->iotlb[mmu_idx][index];
}
@@ -725,9 +735,8 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
}
}
- /* Pairs with flag setting in tlb_reset_dirty_range */
- copy_tlb_helper(te, &tn, true);
- /* atomic_mb_set(&te->addr_write, write_address); */
+ copy_tlb_helper_locked(te, &tn);
+ qemu_spin_unlock(&env->tlb_lock);
}
/* Add a new TLB entry, but without specifying the memory
@@ -773,16 +782,16 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
* repeat the MMU check here. This tlb_fill() call might
* longjump out if this access should cause a guest exception.
*/
- int index;
+ CPUTLBEntry *entry;
target_ulong tlb_addr;
tlb_fill(cpu, addr, size, MMU_DATA_LOAD, mmu_idx, retaddr);
- index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
- tlb_addr = env->tlb_table[mmu_idx][index].addr_read;
+ entry = tlb_entry(env, mmu_idx, addr);
+ tlb_addr = entry->addr_read;
if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) {
/* RAM access */
- uintptr_t haddr = addr + env->tlb_table[mmu_idx][index].addend;
+ uintptr_t haddr = addr + entry->addend;
return ldn_p((void *)haddr, size);
}
@@ -840,16 +849,16 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
* repeat the MMU check here. This tlb_fill() call might
* longjump out if this access should cause a guest exception.
*/
- int index;
+ CPUTLBEntry *entry;
target_ulong tlb_addr;
tlb_fill(cpu, addr, size, MMU_DATA_STORE, mmu_idx, retaddr);
- index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
- tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
+ entry = tlb_entry(env, mmu_idx, addr);
+ tlb_addr = tlb_addr_write(entry);
if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) {
/* RAM access */
- uintptr_t haddr = addr + env->tlb_table[mmu_idx][index].addend;
+ uintptr_t haddr = addr + entry->addend;
stn_p((void *)haddr, size, val);
return;
@@ -891,17 +900,28 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index,
size_t elt_ofs, target_ulong page)
{
size_t vidx;
+
+ assert_cpu_is_self(ENV_GET_CPU(env));
for (vidx = 0; vidx < CPU_VTLB_SIZE; ++vidx) {
CPUTLBEntry *vtlb = &env->tlb_v_table[mmu_idx][vidx];
- target_ulong cmp = *(target_ulong *)((uintptr_t)vtlb + elt_ofs);
+ target_ulong cmp;
+
+ /* elt_ofs might correspond to .addr_write, so use atomic_read */
+#if TCG_OVERSIZED_GUEST
+ cmp = *(target_ulong *)((uintptr_t)vtlb + elt_ofs);
+#else
+ cmp = atomic_read((target_ulong *)((uintptr_t)vtlb + elt_ofs));
+#endif
if (cmp == page) {
/* Found entry in victim tlb, swap tlb and iotlb. */
CPUTLBEntry tmptlb, *tlb = &env->tlb_table[mmu_idx][index];
- copy_tlb_helper(&tmptlb, tlb, false);
- copy_tlb_helper(tlb, vtlb, true);
- copy_tlb_helper(vtlb, &tmptlb, true);
+ qemu_spin_lock(&env->tlb_lock);
+ copy_tlb_helper_locked(&tmptlb, tlb);
+ copy_tlb_helper_locked(tlb, vtlb);
+ copy_tlb_helper_locked(vtlb, &tmptlb);
+ qemu_spin_unlock(&env->tlb_lock);
CPUIOTLBEntry tmpio, *io = &env->iotlb[mmu_idx][index];
CPUIOTLBEntry *vio = &env->iotlb_v[mmu_idx][vidx];
@@ -924,20 +944,19 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index,
*/
tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr)
{
- int mmu_idx, index;
+ uintptr_t mmu_idx = cpu_mmu_index(env, true);
+ uintptr_t index = tlb_index(env, mmu_idx, addr);
+ CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
void *p;
- index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
- mmu_idx = cpu_mmu_index(env, true);
- if (unlikely(!tlb_hit(env->tlb_table[mmu_idx][index].addr_code, addr))) {
+ if (unlikely(!tlb_hit(entry->addr_code, addr))) {
if (!VICTIM_TLB_HIT(addr_code, addr)) {
tlb_fill(ENV_GET_CPU(env), addr, 0, MMU_INST_FETCH, mmu_idx, 0);
}
- assert(tlb_hit(env->tlb_table[mmu_idx][index].addr_code, addr));
+ assert(tlb_hit(entry->addr_code, addr));
}
- if (unlikely(env->tlb_table[mmu_idx][index].addr_code &
- (TLB_RECHECK | TLB_MMIO))) {
+ if (unlikely(entry->addr_code & (TLB_RECHECK | TLB_MMIO))) {
/*
* Return -1 if we can't translate and execute from an entire
* page of RAM here, which will cause us to execute by loading
@@ -949,7 +968,7 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr)
return -1;
}
- p = (void *)((uintptr_t)addr + env->tlb_table[mmu_idx][index].addend);
+ p = (void *)((uintptr_t)addr + entry->addend);
return qemu_ram_addr_from_host_nofail(p);
}
@@ -962,10 +981,10 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr)
void probe_write(CPUArchState *env, target_ulong addr, int size, int mmu_idx,
uintptr_t retaddr)
{
- int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
- target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
+ uintptr_t index = tlb_index(env, mmu_idx, addr);
+ CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
- if (!tlb_hit(tlb_addr, addr)) {
+ if (!tlb_hit(tlb_addr_write(entry), addr)) {
/* TLB entry is for a different page */
if (!VICTIM_TLB_HIT(addr_write, addr)) {
tlb_fill(ENV_GET_CPU(env), addr, size, MMU_DATA_STORE,
@@ -981,9 +1000,9 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
NotDirtyInfo *ndi)
{
size_t mmu_idx = get_mmuidx(oi);
- size_t index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
- CPUTLBEntry *tlbe = &env->tlb_table[mmu_idx][index];
- target_ulong tlb_addr = tlbe->addr_write;
+ uintptr_t index = tlb_index(env, mmu_idx, addr);
+ CPUTLBEntry *tlbe = tlb_entry(env, mmu_idx, addr);
+ target_ulong tlb_addr = tlb_addr_write(tlbe);
TCGMemOp mop = get_memop(oi);
int a_bits = get_alignment_bits(mop);
int s_bits = mop & MO_SIZE;
@@ -1014,7 +1033,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
tlb_fill(ENV_GET_CPU(env), addr, 1 << s_bits, MMU_DATA_STORE,
mmu_idx, retaddr);
}
- tlb_addr = tlbe->addr_write & ~TLB_INVALID_MASK;
+ tlb_addr = tlb_addr_write(tlbe) & ~TLB_INVALID_MASK;
}
/* Notice an IO access or a needs-MMU-lookup access */
@@ -1101,7 +1120,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
#include "atomic_template.h"
#endif
-#ifdef CONFIG_ATOMIC128
+#if HAVE_CMPXCHG128 || HAVE_ATOMIC128
#define DATA_SIZE 16
#include "atomic_template.h"
#endif
diff --git a/accel/tcg/softmmu_template.h b/accel/tcg/softmmu_template.h
index f060a693d4..b0adea045e 100644
--- a/accel/tcg/softmmu_template.h
+++ b/accel/tcg/softmmu_template.h
@@ -111,9 +111,10 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
TCGMemOpIdx oi, uintptr_t retaddr)
{
- unsigned mmu_idx = get_mmuidx(oi);
- int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
- target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
+ uintptr_t mmu_idx = get_mmuidx(oi);
+ uintptr_t index = tlb_index(env, mmu_idx, addr);
+ CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
+ target_ulong tlb_addr = entry->ADDR_READ;
unsigned a_bits = get_alignment_bits(get_memop(oi));
uintptr_t haddr;
DATA_TYPE res;
@@ -129,7 +130,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
mmu_idx, retaddr);
}
- tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
+ tlb_addr = entry->ADDR_READ;
}
/* Handle an IO access. */
@@ -166,7 +167,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
return res;
}
- haddr = addr + env->tlb_table[mmu_idx][index].addend;
+ haddr = addr + entry->addend;
#if DATA_SIZE == 1
res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr);
#else
@@ -179,9 +180,10 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
TCGMemOpIdx oi, uintptr_t retaddr)
{
- unsigned mmu_idx = get_mmuidx(oi);
- int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
- target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
+ uintptr_t mmu_idx = get_mmuidx(oi);
+ uintptr_t index = tlb_index(env, mmu_idx, addr);
+ CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
+ target_ulong tlb_addr = entry->ADDR_READ;
unsigned a_bits = get_alignment_bits(get_memop(oi));
uintptr_t haddr;
DATA_TYPE res;
@@ -197,7 +199,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
mmu_idx, retaddr);
}
- tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
+ tlb_addr = entry->ADDR_READ;
}
/* Handle an IO access. */
@@ -234,7 +236,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
return res;
}
- haddr = addr + env->tlb_table[mmu_idx][index].addend;
+ haddr = addr + entry->addend;
res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr);
return res;
}
@@ -275,9 +277,10 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env,
void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
TCGMemOpIdx oi, uintptr_t retaddr)
{
- unsigned mmu_idx = get_mmuidx(oi);
- int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
- target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
+ uintptr_t mmu_idx = get_mmuidx(oi);
+ uintptr_t index = tlb_index(env, mmu_idx, addr);
+ CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
+ target_ulong tlb_addr = tlb_addr_write(entry);
unsigned a_bits = get_alignment_bits(get_memop(oi));
uintptr_t haddr;
@@ -292,7 +295,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
mmu_idx, retaddr);
}
- tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK;
+ tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK;
}
/* Handle an IO access. */
@@ -313,16 +316,16 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
if (DATA_SIZE > 1
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
>= TARGET_PAGE_SIZE)) {
- int i, index2;
- target_ulong page2, tlb_addr2;
+ int i;
+ target_ulong page2;
+ CPUTLBEntry *entry2;
do_unaligned_access:
/* Ensure the second page is in the TLB. Note that the first page
is already guaranteed to be filled, and that the second page
cannot evict the first. */
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
- index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
- tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write;
- if (!tlb_hit_page(tlb_addr2, page2)
+ entry2 = tlb_entry(env, mmu_idx, page2);
+ if (!tlb_hit_page(tlb_addr_write(entry2), page2)
&& !VICTIM_TLB_HIT(addr_write, page2)) {
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
mmu_idx, retaddr);
@@ -340,7 +343,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
return;
}
- haddr = addr + env->tlb_table[mmu_idx][index].addend;
+ haddr = addr + entry->addend;
#if DATA_SIZE == 1
glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val);
#else
@@ -352,9 +355,10 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
TCGMemOpIdx oi, uintptr_t retaddr)
{
- unsigned mmu_idx = get_mmuidx(oi);
- int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
- target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
+ uintptr_t mmu_idx = get_mmuidx(oi);
+ uintptr_t index = tlb_index(env, mmu_idx, addr);
+ CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
+ target_ulong tlb_addr = tlb_addr_write(entry);
unsigned a_bits = get_alignment_bits(get_memop(oi));
uintptr_t haddr;
@@ -369,7 +373,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
mmu_idx, retaddr);
}
- tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK;
+ tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK;
}
/* Handle an IO access. */
@@ -390,16 +394,16 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
if (DATA_SIZE > 1
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
>= TARGET_PAGE_SIZE)) {
- int i, index2;
- target_ulong page2, tlb_addr2;
+ int i;
+ target_ulong page2;
+ CPUTLBEntry *entry2;
do_unaligned_access:
/* Ensure the second page is in the TLB. Note that the first page
is already guaranteed to be filled, and that the second page
cannot evict the first. */
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
- index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
- tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write;
- if (!tlb_hit_page(tlb_addr2, page2)
+ entry2 = tlb_entry(env, mmu_idx, page2);
+ if (!tlb_hit_page(tlb_addr_write(entry2), page2)
&& !VICTIM_TLB_HIT(addr_write, page2)) {
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
mmu_idx, retaddr);
@@ -417,7 +421,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
return;
}
- haddr = addr + env->tlb_table[mmu_idx][index].addend;
+ haddr = addr + entry->addend;
glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val);
}
#endif /* DATA_SIZE > 1 */
diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c
index 56dbb56a16..3d25bdcc17 100644
--- a/accel/tcg/tcg-all.c
+++ b/accel/tcg/tcg-all.c
@@ -51,7 +51,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
if (!qemu_cpu_is_self(cpu)) {
qemu_cpu_kick(cpu);
} else {
- cpu->icount_decr.u16.high = -1;
+ atomic_set(&cpu->icount_decr.u16.high, -1);
if (use_icount &&
!cpu->can_do_io
&& (mask & ~old_mask) != 0) {
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
index ad5c758246..356dcd0948 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -2341,7 +2341,7 @@ void cpu_interrupt(CPUState *cpu, int mask)
{
g_assert(qemu_mutex_iothread_locked());
cpu->interrupt_request |= mask;
- cpu->icount_decr.u16.high = -1;
+ atomic_set(&cpu->icount_decr.u16.high, -1);
}
/*
diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c
index 26a3ffbba1..cd75829cf2 100644
--- a/accel/tcg/user-exec.c
+++ b/accel/tcg/user-exec.c
@@ -25,6 +25,7 @@
#include "exec/cpu_ldst.h"
#include "translate-all.h"
#include "exec/helper-proto.h"
+#include "qemu/atomic128.h"
#undef EAX
#undef ECX
@@ -615,7 +616,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
/* 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
@@ -628,4 +629,4 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
#define DATA_SIZE 16
#include "atomic_template.h"
-#endif /* CONFIG_ATOMIC128 */
+#endif
diff --git a/configure b/configure
index 9138af37f8..e39f63d01d 100755
--- a/configure
+++ b/configure
@@ -457,12 +457,9 @@ gtk=""
gtk_gl="no"
tls_priority="NORMAL"
gnutls=""
-gnutls_rnd=""
nettle=""
-nettle_kdf="no"
gcrypt=""
gcrypt_hmac="no"
-gcrypt_kdf="no"
vte=""
virglrenderer=""
tpm="yes"
@@ -2666,79 +2663,28 @@ fi
##########################################
# GNUTLS probe
-gnutls_works() {
- # Unfortunately some distros have bad pkg-config information for gnutls
- # such that it claims to exist but you get a compiler error if you try
- # to use the options returned by --libs. Specifically, Ubuntu for --static
- # builds doesn't work:
- # https://bugs.launchpad.net/ubuntu/+source/gnutls26/+bug/1478035
- #
- # So sanity check the cflags/libs before assuming gnutls can be used.
- if ! $pkg_config --exists "gnutls"; then
- return 1
- fi
-
- write_c_skeleton
- compile_prog "$($pkg_config --cflags gnutls)" "$($pkg_config --libs gnutls)"
-}
-
-gnutls_gcrypt=no
-gnutls_nettle=no
if test "$gnutls" != "no"; then
- if gnutls_works; then
+ if $pkg_config --exists "gnutls >= 3.1.18"; then
gnutls_cflags=$($pkg_config --cflags gnutls)
gnutls_libs=$($pkg_config --libs gnutls)
libs_softmmu="$gnutls_libs $libs_softmmu"
libs_tools="$gnutls_libs $libs_tools"
QEMU_CFLAGS="$QEMU_CFLAGS $gnutls_cflags"
gnutls="yes"
-
- # gnutls_rnd requires >= 2.11.0
- if $pkg_config --exists "gnutls >= 2.11.0"; then
- gnutls_rnd="yes"
- else
- gnutls_rnd="no"
- fi
-
- if $pkg_config --exists 'gnutls >= 3.0'; then
- gnutls_gcrypt=no
- gnutls_nettle=yes
- elif $pkg_config --exists 'gnutls >= 2.12'; then
- case $($pkg_config --libs --static gnutls) in
- *gcrypt*)
- gnutls_gcrypt=yes
- gnutls_nettle=no
- ;;
- *nettle*)
- gnutls_gcrypt=no
- gnutls_nettle=yes
- ;;
- *)
- gnutls_gcrypt=yes
- gnutls_nettle=no
- ;;
- esac
- else
- gnutls_gcrypt=yes
- gnutls_nettle=no
- fi
elif test "$gnutls" = "yes"; then
- feature_not_found "gnutls" "Install gnutls devel"
+ feature_not_found "gnutls" "Install gnutls devel >= 3.1.18"
else
gnutls="no"
- gnutls_rnd="no"
fi
-else
- gnutls_rnd="no"
fi
# If user didn't give a --disable/enable-gcrypt flag,
# then mark as disabled if user requested nettle
-# explicitly, or if gnutls links to nettle
+# explicitly
if test -z "$gcrypt"
then
- if test "$nettle" = "yes" || test "$gnutls_nettle" = "yes"
+ if test "$nettle" = "yes"
then
gcrypt="no"
fi
@@ -2746,16 +2692,16 @@ fi
# If user didn't give a --disable/enable-nettle flag,
# then mark as disabled if user requested gcrypt
-# explicitly, or if gnutls links to gcrypt
+# explicitly
if test -z "$nettle"
then
- if test "$gcrypt" = "yes" || test "$gnutls_gcrypt" = "yes"
+ if test "$gcrypt" = "yes"
then
nettle="no"
fi
fi
-has_libgcrypt_config() {
+has_libgcrypt() {
if ! has "libgcrypt-config"
then
return 1
@@ -2770,11 +2716,42 @@ has_libgcrypt_config() {
fi
fi
+ maj=`libgcrypt-config --version | awk -F . '{print $1}'`
+ min=`libgcrypt-config --version | awk -F . '{print $2}'`
+
+ if test $maj != 1 || test $min -lt 5
+ then
+ return 1
+ fi
+
return 0
}
+
+if test "$nettle" != "no"; then
+ if $pkg_config --exists "nettle >= 2.7.1"; then
+ nettle_cflags=$($pkg_config --cflags nettle)
+ nettle_libs=$($pkg_config --libs nettle)
+ nettle_version=$($pkg_config --modversion nettle)
+ libs_softmmu="$nettle_libs $libs_softmmu"
+ libs_tools="$nettle_libs $libs_tools"
+ QEMU_CFLAGS="$QEMU_CFLAGS $nettle_cflags"
+ nettle="yes"
+
+ if test -z "$gcrypt"; then
+ gcrypt="no"
+ fi
+ else
+ if test "$nettle" = "yes"; then
+ feature_not_found "nettle" "Install nettle devel >= 2.7.1"
+ else
+ nettle="no"
+ fi
+ fi
+fi
+
if test "$gcrypt" != "no"; then
- if has_libgcrypt_config; then
+ if has_libgcrypt; then
gcrypt_cflags=$(libgcrypt-config --cflags)
gcrypt_libs=$(libgcrypt-config --libs)
# Debian has remove -lgpg-error from libgcrypt-config
@@ -2788,22 +2765,6 @@ if test "$gcrypt" != "no"; then
libs_tools="$gcrypt_libs $libs_tools"
QEMU_CFLAGS="$QEMU_CFLAGS $gcrypt_cflags"
gcrypt="yes"
- if test -z "$nettle"; then
- nettle="no"
- fi
-
- cat > $TMPC << EOF
-#include <gcrypt.h>
-int main(void) {
- gcry_kdf_derive(NULL, 0, GCRY_KDF_PBKDF2,
- GCRY_MD_SHA256,
- NULL, 0, 0, 0, NULL);
- return 0;
-}
-EOF
- if compile_prog "$gcrypt_cflags" "$gcrypt_libs" ; then
- gcrypt_kdf=yes
- fi
cat > $TMPC << EOF
#include <gcrypt.h>
@@ -2819,7 +2780,7 @@ EOF
fi
else
if test "$gcrypt" = "yes"; then
- feature_not_found "gcrypt" "Install gcrypt devel"
+ feature_not_found "gcrypt" "Install gcrypt devel >= 1.5.0"
else
gcrypt="no"
fi
@@ -2827,36 +2788,6 @@ EOF
fi
-if test "$nettle" != "no"; then
- if $pkg_config --exists "nettle"; then
- nettle_cflags=$($pkg_config --cflags nettle)
- nettle_libs=$($pkg_config --libs nettle)
- nettle_version=$($pkg_config --modversion nettle)
- libs_softmmu="$nettle_libs $libs_softmmu"
- libs_tools="$nettle_libs $libs_tools"
- QEMU_CFLAGS="$QEMU_CFLAGS $nettle_cflags"
- nettle="yes"
-
- cat > $TMPC << EOF
-#include <stddef.h>
-#include <nettle/pbkdf2.h>
-int main(void) {
- pbkdf2_hmac_sha256(8, NULL, 1000, 8, NULL, 8, NULL);
- return 0;
-}
-EOF
- if compile_prog "$nettle_cflags" "$nettle_libs" ; then
- nettle_kdf=yes
- fi
- else
- if test "$nettle" = "yes"; then
- feature_not_found "nettle" "Install nettle devel"
- else
- nettle="no"
- fi
- fi
-fi
-
if test "$gcrypt" = "yes" && test "$nettle" = "yes"
then
error_exit "Only one of gcrypt & nettle can be enabled"
@@ -4202,7 +4133,14 @@ if compile_prog "" "" ; then
memfd=yes
fi
-
+# check for usbfs
+have_usbfs=no
+if test "$linux_user" = "yes"; then
+ if check_include linux/usbdevice_fs.h; then
+ have_usbfs=yes
+ fi
+ have_usbfs=yes
+fi
# check for fallocate
fallocate=no
@@ -5154,6 +5092,21 @@ EOF
fi
fi
+cmpxchg128=no
+if test "$int128" = yes -a "$atomic128" = no; then
+ cat > $TMPC << EOF
+int main(void)
+{
+ unsigned __int128 x = 0, y = 0;
+ __sync_val_compare_and_swap_16(&x, y, x);
+ return 0;
+}
+EOF
+ if compile_prog "" "" ; then
+ cmpxchg128=yes
+ fi
+fi
+
#########################################
# See if 64-bit atomic operations are supported.
# Note that without __atomic builtins, we can only
@@ -5961,11 +5914,8 @@ echo "GTK GL support $gtk_gl"
echo "VTE support $vte $(echo_version $vte $vteversion)"
echo "TLS priority $tls_priority"
echo "GNUTLS support $gnutls"
-echo "GNUTLS rnd $gnutls_rnd"
echo "libgcrypt $gcrypt"
-echo "libgcrypt kdf $gcrypt_kdf"
echo "nettle $nettle $(echo_version $nettle $nettle_version)"
-echo "nettle kdf $nettle_kdf"
echo "libtasn1 $tasn1"
echo "curses support $curses"
echo "virgl support $virglrenderer $(echo_version $virglrenderer $virgl_version)"
@@ -6310,6 +6260,9 @@ fi
if test "$memfd" = "yes" ; then
echo "CONFIG_MEMFD=y" >> $config_host_mak
fi
+if test "$have_usbfs" = "yes" ; then
+ echo "CONFIG_USBFS=y" >> $config_host_mak
+fi
if test "$fallocate" = "yes" ; then
echo "CONFIG_FALLOCATE=y" >> $config_host_mak
fi
@@ -6401,24 +6354,15 @@ echo "CONFIG_TLS_PRIORITY=\"$tls_priority\"" >> $config_host_mak
if test "$gnutls" = "yes" ; then
echo "CONFIG_GNUTLS=y" >> $config_host_mak
fi
-if test "$gnutls_rnd" = "yes" ; then
- echo "CONFIG_GNUTLS_RND=y" >> $config_host_mak
-fi
if test "$gcrypt" = "yes" ; then
echo "CONFIG_GCRYPT=y" >> $config_host_mak
if test "$gcrypt_hmac" = "yes" ; then
echo "CONFIG_GCRYPT_HMAC=y" >> $config_host_mak
fi
- if test "$gcrypt_kdf" = "yes" ; then
- echo "CONFIG_GCRYPT_KDF=y" >> $config_host_mak
- fi
fi
if test "$nettle" = "yes" ; then
echo "CONFIG_NETTLE=y" >> $config_host_mak
echo "CONFIG_NETTLE_VERSION_MAJOR=${nettle_version%%.*}" >> $config_host_mak
- if test "$nettle_kdf" = "yes" ; then
- echo "CONFIG_NETTLE_KDF=y" >> $config_host_mak
- fi
fi
if test "$tasn1" = "yes" ; then
echo "CONFIG_TASN1=y" >> $config_host_mak
@@ -6663,6 +6607,10 @@ if test "$atomic128" = "yes" ; then
echo "CONFIG_ATOMIC128=y" >> $config_host_mak
fi
+if test "$cmpxchg128" = "yes" ; then
+ echo "CONFIG_CMPXCHG128=y" >> $config_host_mak
+fi
+
if test "$atomic64" = "yes" ; then
echo "CONFIG_ATOMIC64=y" >> $config_host_mak
fi
diff --git a/cpus.c b/cpus.c
index 7804071872..3978f63d8f 100644
--- a/cpus.c
+++ b/cpus.c
@@ -509,8 +509,8 @@ static void icount_warp_rt(void)
seqlock_write_lock(&timers_state.vm_clock_seqlock,
&timers_state.vm_clock_lock);
if (runstate_is_running()) {
- int64_t clock = REPLAY_CLOCK(REPLAY_CLOCK_VIRTUAL_RT,
- cpu_get_clock_locked());
+ int64_t clock = REPLAY_CLOCK_LOCKED(REPLAY_CLOCK_VIRTUAL_RT,
+ cpu_get_clock_locked());
int64_t warp_delta;
warp_delta = clock - timers_state.vm_clock_warp_start;
@@ -1425,7 +1425,8 @@ static int tcg_cpu_exec(CPUState *cpu)
ret = cpu_exec(cpu);
cpu_exec_end(cpu);
#ifdef CONFIG_PROFILER
- tcg_time += profile_getclock() - ti;
+ atomic_set(&tcg_ctx->prof.cpu_exec_time,
+ tcg_ctx->prof.cpu_exec_time + profile_getclock() - ti);
#endif
return ret;
}
diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs
index 756bab111b..256c9aca1f 100644
--- a/crypto/Makefile.objs
+++ b/crypto/Makefile.objs
@@ -20,11 +20,11 @@ crypto-obj-y += tlscredsx509.o
crypto-obj-y += tlssession.o
crypto-obj-y += secret.o
crypto-obj-$(CONFIG_GCRYPT) += random-gcrypt.o
-crypto-obj-$(if $(CONFIG_GCRYPT),n,$(CONFIG_GNUTLS_RND)) += random-gnutls.o
-crypto-obj-$(if $(CONFIG_GCRYPT),n,$(if $(CONFIG_GNUTLS_RND),n,y)) += random-platform.o
+crypto-obj-$(if $(CONFIG_GCRYPT),n,$(CONFIG_GNUTLS)) += random-gnutls.o
+crypto-obj-$(if $(CONFIG_GCRYPT),n,$(if $(CONFIG_GNUTLS),n,y)) += random-platform.o
crypto-obj-y += pbkdf.o
-crypto-obj-$(CONFIG_NETTLE_KDF) += pbkdf-nettle.o
-crypto-obj-$(if $(CONFIG_NETTLE_KDF),n,$(CONFIG_GCRYPT_KDF)) += pbkdf-gcrypt.o
+crypto-obj-$(CONFIG_NETTLE) += pbkdf-nettle.o
+crypto-obj-$(if $(CONFIG_NETTLE),n,$(CONFIG_GCRYPT)) += pbkdf-gcrypt.o
crypto-obj-y += ivgen.o
crypto-obj-y += ivgen-essiv.o
crypto-obj-y += ivgen-plain.o
diff --git a/crypto/init.c b/crypto/init.c
index f131c42306..c30156405a 100644
--- a/crypto/init.c
+++ b/crypto/init.c
@@ -37,33 +37,14 @@
/* #define DEBUG_GNUTLS */
/*
- * If GNUTLS is built against GCrypt then
- *
- * - When GNUTLS >= 2.12, we must not initialize gcrypt threading
- * because GNUTLS will do that itself
- * - When GNUTLS < 2.12 we must always initialize gcrypt threading
- * - When GNUTLS is disabled we must always initialize gcrypt threading
- *
- * But....
- *
- * When gcrypt >= 1.6.0 we must not initialize gcrypt threading
- * because gcrypt will do that itself.
- *
- * So we need to init gcrypt threading if
+ * We need to init gcrypt threading if
*
* - gcrypt < 1.6.0
- * AND
- * - gnutls < 2.12
- * OR
- * - gnutls is disabled
*
*/
#if (defined(CONFIG_GCRYPT) && \
- (!defined(CONFIG_GNUTLS) || \
- (LIBGNUTLS_VERSION_NUMBER < 0x020c00)) && \
- (!defined(GCRYPT_VERSION_NUMBER) || \
- (GCRYPT_VERSION_NUMBER < 0x010600)))
+ (GCRYPT_VERSION_NUMBER < 0x010600))
#define QCRYPTO_INIT_GCRYPT_THREADS
#else
#undef QCRYPTO_INIT_GCRYPT_THREADS
diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
index 98ee0424e5..d6ab4a9862 100644
--- a/crypto/tlscredsx509.c
+++ b/crypto/tlscredsx509.c
@@ -72,14 +72,6 @@ qcrypto_tls_creds_check_cert_times(gnutls_x509_crt_t cert,
}
-#if LIBGNUTLS_VERSION_NUMBER >= 2
-/*
- * The gnutls_x509_crt_get_basic_constraints function isn't
- * available in GNUTLS 1.0.x branches. This isn't critical
- * though, since gnutls_certificate_verify_peers2 will do
- * pretty much the same check at runtime, so we can just
- * disable this code
- */
static int
qcrypto_tls_creds_check_cert_basic_constraints(QCryptoTLSCredsX509 *creds,
gnutls_x509_crt_t cert,
@@ -130,7 +122,6 @@ qcrypto_tls_creds_check_cert_basic_constraints(QCryptoTLSCredsX509 *creds,
return 0;
}
-#endif
static int
@@ -299,14 +290,12 @@ qcrypto_tls_creds_check_cert(QCryptoTLSCredsX509 *creds,
return -1;
}
-#if LIBGNUTLS_VERSION_NUMBER >= 2
if (qcrypto_tls_creds_check_cert_basic_constraints(creds,
cert, certFile,
isServer, isCA,
errp) < 0) {
return -1;
}
-#endif
if (qcrypto_tls_creds_check_cert_key_usage(creds,
cert, certFile,
@@ -615,7 +604,6 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
}
if (cert != NULL && key != NULL) {
-#if LIBGNUTLS_VERSION_NUMBER >= 0x030111
char *password = NULL;
if (creds->passwordid) {
password = qcrypto_secret_lookup_as_utf8(creds->passwordid,
@@ -630,15 +618,6 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
password,
0);
g_free(password);
-#else /* LIBGNUTLS_VERSION_NUMBER < 0x030111 */
- if (creds->passwordid) {
- error_setg(errp, "PKCS8 decryption requires GNUTLS >= 3.1.11");
- goto cleanup;
- }
- ret = gnutls_certificate_set_x509_key_file(creds->data,
- cert, key,
- GNUTLS_X509_FMT_PEM);
-#endif
if (ret < 0) {
error_setg(errp, "Cannot load certificate '%s' & key '%s': %s",
cert, key, gnutls_strerror(ret));
diff --git a/crypto/tlssession.c b/crypto/tlssession.c
index 66a6fbe19c..2f28fa7f71 100644
--- a/crypto/tlssession.c
+++ b/crypto/tlssession.c
@@ -90,13 +90,7 @@ qcrypto_tls_session_pull(void *opaque, void *buf, size_t len)
}
#define TLS_PRIORITY_ADDITIONAL_ANON "+ANON-DH"
-
-#if GNUTLS_VERSION_MAJOR >= 3
-#define TLS_ECDHE_PSK "+ECDHE-PSK:"
-#else
-#define TLS_ECDHE_PSK ""
-#endif
-#define TLS_PRIORITY_ADDITIONAL_PSK TLS_ECDHE_PSK "+DHE-PSK:+PSK"
+#define TLS_PRIORITY_ADDITIONAL_PSK "+ECDHE-PSK:+DHE-PSK:+PSK"
QCryptoTLSSession *
qcrypto_tls_session_new(QCryptoTLSCreds *creds,
diff --git a/default-configs/hyperv.mak b/default-configs/hyperv.mak
new file mode 100644
index 0000000000..5d0d9fd830
--- /dev/null
+++ b/default-configs/hyperv.mak
@@ -0,0 +1,2 @@
+CONFIG_HYPERV=$(CONFIG_KVM)
+CONFIG_HYPERV_TESTDEV=y
diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 8c7d4a0fa0..210cff2781 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -3,6 +3,7 @@
include pci.mak
include sound.mak
include usb.mak
+include hyperv.mak
CONFIG_QXL=$(CONFIG_SPICE)
CONFIG_VGA_ISA=y
CONFIG_VGA_CIRRUS=y
@@ -58,7 +59,6 @@ CONFIG_XIO3130=y
CONFIG_IOH3420=y
CONFIG_I82801B11=y
CONFIG_SMBIOS=y
-CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
CONFIG_PXB=y
CONFIG_ACPI_VMGENID=y
CONFIG_FW_CFG_DMA=y
diff --git a/docs/COLO-FT.txt b/docs/COLO-FT.txt
index 70cfb9ce7d..6302469d0e 100644
--- a/docs/COLO-FT.txt
+++ b/docs/COLO-FT.txt
@@ -110,6 +110,40 @@ Note:
HeartBeat has not been implemented yet, so you need to trigger failover process
by using 'x-colo-lost-heartbeat' command.
+== COLO operation status ==
+
++-----------------+
+| |
+| Start COLO |
+| |
++--------+--------+
+ |
+ | Main qmp command:
+ | migrate-set-capabilities with x-colo
+ | migrate
+ |
+ v
++--------+--------+
+| |
+| COLO running |
+| |
++--------+--------+
+ |
+ | Main qmp command:
+ | x-colo-lost-heartbeat
+ | or
+ | some error happened
+ v
++--------+--------+
+| | send qmp event:
+| COLO failover | COLO_EXIT
+| |
++-----------------+
+
+COLO use the qmp command to switch and report operation status.
+The diagram just shows the main qmp command, you can get the detail
+in test procedure.
+
== Test procedure ==
1. Startup qemu
Primary:
diff --git a/exec.c b/exec.c
index 5d99ef5c93..bb6170dbff 100644
--- a/exec.c
+++ b/exec.c
@@ -965,6 +965,7 @@ void cpu_exec_realizefn(CPUState *cpu, Error **errp)
tcg_target_initialized = true;
cc->tcg_initialize();
}
+ tlb_init(cpu);
#ifndef CONFIG_USER_ONLY
if (qdev_get_vmsd(DEVICE(cpu)) == NULL) {
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index a19c1417ed..30722ccf98 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -9,6 +9,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += cpu/
devices-dirs-$(CONFIG_SOFTMMU) += display/
devices-dirs-$(CONFIG_SOFTMMU) += dma/
devices-dirs-$(CONFIG_SOFTMMU) += gpio/
+devices-dirs-$(CONFIG_HYPERV) += hyperv/
devices-dirs-$(CONFIG_SOFTMMU) += i2c/
devices-dirs-$(CONFIG_SOFTMMU) += ide/
devices-dirs-$(CONFIG_SOFTMMU) += input/
diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c
index 4f980a598b..97789a0771 100644
--- a/hw/audio/es1370.c
+++ b/hw/audio/es1370.c
@@ -585,10 +585,13 @@ static uint64_t es1370_read(void *opaque, hwaddr addr, unsigned size)
#endif
break;
+ case ES1370_REG_ADC_FRAMECNT:
+ d += 2;
+ goto framecnt;
case ES1370_REG_DAC1_FRAMECNT:
case ES1370_REG_DAC2_FRAMECNT:
- case ES1370_REG_ADC_FRAMECNT:
d += (addr - ES1370_REG_DAC1_FRAMECNT) >> 3;
+ framecnt:
val = d->frame_cnt;
#ifdef DEBUG_ES1370
{
@@ -602,10 +605,13 @@ static uint64_t es1370_read(void *opaque, hwaddr addr, unsigned size)
#endif
break;
+ case ES1370_REG_ADC_FRAMEADR:
+ d += 2;
+ goto frameadr;
case ES1370_REG_DAC1_FRAMEADR:
case ES1370_REG_DAC2_FRAMEADR:
- case ES1370_REG_ADC_FRAMEADR:
d += (addr - ES1370_REG_DAC1_FRAMEADR) >> 3;
+ frameadr:
val = d->frame_addr;
break;
diff --git a/hw/core/hotplug.c b/hw/core/hotplug.c
index 2253072d0e..17ac986685 100644
--- a/hw/core/hotplug.c
+++ b/hw/core/hotplug.c
@@ -35,16 +35,6 @@ void hotplug_handler_plug(HotplugHandler *plug_handler,
}
}
-void hotplug_handler_post_plug(HotplugHandler *plug_handler,
- DeviceState *plugged_dev)
-{
- HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler);
-
- if (hdc->post_plug) {
- hdc->post_plug(plug_handler, plugged_dev);
- }
-}
-
void hotplug_handler_unplug_request(HotplugHandler *plug_handler,
DeviceState *plugged_dev,
Error **errp)
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index 046d8f1f76..6b3cc55b27 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -832,14 +832,6 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
DEVICE_LISTENER_CALL(realize, Forward, dev);
- if (hotplug_ctrl) {
- hotplug_handler_plug(hotplug_ctrl, dev, &local_err);
- }
-
- if (local_err != NULL) {
- goto post_realize_fail;
- }
-
/*
* always free/re-initialize here since the value cannot be cleaned up
* in device_unrealize due to its usage later on in the unplug path
@@ -869,8 +861,12 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
dev->pending_deleted_event = false;
if (hotplug_ctrl) {
- hotplug_handler_post_plug(hotplug_ctrl, dev);
- }
+ hotplug_handler_plug(hotplug_ctrl, dev, &local_err);
+ if (local_err != NULL) {
+ goto child_realize_fail;
+ }
+ }
+
} else if (!value && dev->realized) {
Error **local_errp = NULL;
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
diff --git a/hw/hyperv/Makefile.objs b/hw/hyperv/Makefile.objs
new file mode 100644
index 0000000000..edaca2f763
--- /dev/null
+++ b/hw/hyperv/Makefile.objs
@@ -0,0 +1,2 @@
+obj-y += hyperv.o
+obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c
new file mode 100644
index 0000000000..a28e7249d8
--- /dev/null
+++ b/hw/hyperv/hyperv.c
@@ -0,0 +1,654 @@
+/*
+ * Hyper-V guest/hypervisor interaction
+ *
+ * Copyright (c) 2015-2018 Virtuozzo International GmbH.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/main-loop.h"
+#include "qapi/error.h"
+#include "exec/address-spaces.h"
+#include "sysemu/kvm.h"
+#include "qemu/bitops.h"
+#include "qemu/error-report.h"
+#include "qemu/queue.h"
+#include "qemu/rcu.h"
+#include "qemu/rcu_queue.h"
+#include "hw/hyperv/hyperv.h"
+
+typedef struct SynICState {
+ DeviceState parent_obj;
+
+ CPUState *cs;
+
+ bool enabled;
+ hwaddr msg_page_addr;
+ hwaddr event_page_addr;
+ MemoryRegion msg_page_mr;
+ MemoryRegion event_page_mr;
+ struct hyperv_message_page *msg_page;
+ struct hyperv_event_flags_page *event_page;
+} SynICState;
+
+#define TYPE_SYNIC "hyperv-synic"
+#define SYNIC(obj) OBJECT_CHECK(SynICState, (obj), TYPE_SYNIC)
+
+static SynICState *get_synic(CPUState *cs)
+{
+ return SYNIC(object_resolve_path_component(OBJECT(cs), "synic"));
+}
+
+static void synic_update(SynICState *synic, bool enable,
+ hwaddr msg_page_addr, hwaddr event_page_addr)
+{
+
+ synic->enabled = enable;
+ if (synic->msg_page_addr != msg_page_addr) {
+ if (synic->msg_page_addr) {
+ memory_region_del_subregion(get_system_memory(),
+ &synic->msg_page_mr);
+ }
+ if (msg_page_addr) {
+ memory_region_add_subregion(get_system_memory(), msg_page_addr,
+ &synic->msg_page_mr);
+ }
+ synic->msg_page_addr = msg_page_addr;
+ }
+ if (synic->event_page_addr != event_page_addr) {
+ if (synic->event_page_addr) {
+ memory_region_del_subregion(get_system_memory(),
+ &synic->event_page_mr);
+ }
+ if (event_page_addr) {
+ memory_region_add_subregion(get_system_memory(), event_page_addr,
+ &synic->event_page_mr);
+ }
+ synic->event_page_addr = event_page_addr;
+ }
+}
+
+void hyperv_synic_update(CPUState *cs, bool enable,
+ hwaddr msg_page_addr, hwaddr event_page_addr)
+{
+ SynICState *synic = get_synic(cs);
+
+ if (!synic) {
+ return;
+ }
+
+ synic_update(synic, enable, msg_page_addr, event_page_addr);
+}
+
+static void synic_realize(DeviceState *dev, Error **errp)
+{
+ Object *obj = OBJECT(dev);
+ SynICState *synic = SYNIC(dev);
+ char *msgp_name, *eventp_name;
+ uint32_t vp_index;
+
+ /* memory region names have to be globally unique */
+ vp_index = hyperv_vp_index(synic->cs);
+ msgp_name = g_strdup_printf("synic-%u-msg-page", vp_index);
+ eventp_name = g_strdup_printf("synic-%u-event-page", vp_index);
+
+ memory_region_init_ram(&synic->msg_page_mr, obj, msgp_name,
+ sizeof(*synic->msg_page), &error_abort);
+ memory_region_init_ram(&synic->event_page_mr, obj, eventp_name,
+ sizeof(*synic->event_page), &error_abort);
+ synic->msg_page = memory_region_get_ram_ptr(&synic->msg_page_mr);
+ synic->event_page = memory_region_get_ram_ptr(&synic->event_page_mr);
+
+ g_free(msgp_name);
+ g_free(eventp_name);
+}
+static void synic_reset(DeviceState *dev)
+{
+ SynICState *synic = SYNIC(dev);
+ memset(synic->msg_page, 0, sizeof(*synic->msg_page));
+ memset(synic->event_page, 0, sizeof(*synic->event_page));
+ synic_update(synic, false, 0, 0);
+}
+
+static void synic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = synic_realize;
+ dc->reset = synic_reset;
+ dc->user_creatable = false;
+}
+
+void hyperv_synic_add(CPUState *cs)
+{
+ Object *obj;
+ SynICState *synic;
+
+ obj = object_new(TYPE_SYNIC);
+ synic = SYNIC(obj);
+ synic->cs = cs;
+ object_property_add_child(OBJECT(cs), "synic", obj, &error_abort);
+ object_unref(obj);
+ object_property_set_bool(obj, true, "realized", &error_abort);
+}
+
+void hyperv_synic_reset(CPUState *cs)
+{
+ device_reset(DEVICE(get_synic(cs)));
+}
+
+static const TypeInfo synic_type_info = {
+ .name = TYPE_SYNIC,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(SynICState),
+ .class_init = synic_class_init,
+};
+
+static void synic_register_types(void)
+{
+ type_register_static(&synic_type_info);
+}
+
+type_init(synic_register_types)
+
+/*
+ * KVM has its own message producers (SynIC timers). To guarantee
+ * serialization with both KVM vcpu and the guest cpu, the messages are first
+ * staged in an intermediate area and then posted to the SynIC message page in
+ * the vcpu thread.
+ */
+typedef struct HvSintStagedMessage {
+ /* message content staged by hyperv_post_msg */
+ struct hyperv_message msg;
+ /* callback + data (r/o) to complete the processing in a BH */
+ HvSintMsgCb cb;
+ void *cb_data;
+ /* message posting status filled by cpu_post_msg */
+ int status;
+ /* passing the buck: */
+ enum {
+ /* initial state */
+ HV_STAGED_MSG_FREE,
+ /*
+ * hyperv_post_msg (e.g. in main loop) grabs the staged area (FREE ->
+ * BUSY), copies msg, and schedules cpu_post_msg on the assigned cpu
+ */
+ HV_STAGED_MSG_BUSY,
+ /*
+ * cpu_post_msg (vcpu thread) tries to copy staged msg to msg slot,
+ * notify the guest, records the status, marks the posting done (BUSY
+ * -> POSTED), and schedules sint_msg_bh BH
+ */
+ HV_STAGED_MSG_POSTED,
+ /*
+ * sint_msg_bh (BH) verifies that the posting is done, runs the
+ * callback, and starts over (POSTED -> FREE)
+ */
+ } state;
+} HvSintStagedMessage;
+
+struct HvSintRoute {
+ uint32_t sint;
+ SynICState *synic;
+ int gsi;
+ EventNotifier sint_set_notifier;
+ EventNotifier sint_ack_notifier;
+
+ HvSintStagedMessage *staged_msg;
+
+ unsigned refcount;
+};
+
+static CPUState *hyperv_find_vcpu(uint32_t vp_index)
+{
+ CPUState *cs = qemu_get_cpu(vp_index);
+ assert(hyperv_vp_index(cs) == vp_index);
+ return cs;
+}
+
+/*
+ * BH to complete the processing of a staged message.
+ */
+static void sint_msg_bh(void *opaque)
+{
+ HvSintRoute *sint_route = opaque;
+ HvSintStagedMessage *staged_msg = sint_route->staged_msg;
+
+ if (atomic_read(&staged_msg->state) != HV_STAGED_MSG_POSTED) {
+ /* status nor ready yet (spurious ack from guest?), ignore */
+ return;
+ }
+
+ staged_msg->cb(staged_msg->cb_data, staged_msg->status);
+ staged_msg->status = 0;
+
+ /* staged message processing finished, ready to start over */
+ atomic_set(&staged_msg->state, HV_STAGED_MSG_FREE);
+ /* drop the reference taken in hyperv_post_msg */
+ hyperv_sint_route_unref(sint_route);
+}
+
+/*
+ * Worker to transfer the message from the staging area into the SynIC message
+ * page in vcpu context.
+ */
+static void cpu_post_msg(CPUState *cs, run_on_cpu_data data)
+{
+ HvSintRoute *sint_route = data.host_ptr;
+ HvSintStagedMessage *staged_msg = sint_route->staged_msg;
+ SynICState *synic = sint_route->synic;
+ struct hyperv_message *dst_msg;
+ bool wait_for_sint_ack = false;
+
+ assert(staged_msg->state == HV_STAGED_MSG_BUSY);
+
+ if (!synic->enabled || !synic->msg_page_addr) {
+ staged_msg->status = -ENXIO;
+ goto posted;
+ }
+
+ dst_msg = &synic->msg_page->slot[sint_route->sint];
+
+ if (dst_msg->header.message_type != HV_MESSAGE_NONE) {
+ dst_msg->header.message_flags |= HV_MESSAGE_FLAG_PENDING;
+ staged_msg->status = -EAGAIN;
+ wait_for_sint_ack = true;
+ } else {
+ memcpy(dst_msg, &staged_msg->msg, sizeof(*dst_msg));
+ staged_msg->status = hyperv_sint_route_set_sint(sint_route);
+ }
+
+ memory_region_set_dirty(&synic->msg_page_mr, 0, sizeof(*synic->msg_page));
+
+posted:
+ atomic_set(&staged_msg->state, HV_STAGED_MSG_POSTED);
+ /*
+ * Notify the msg originator of the progress made; if the slot was busy we
+ * set msg_pending flag in it so it will be the guest who will do EOM and
+ * trigger the notification from KVM via sint_ack_notifier
+ */
+ if (!wait_for_sint_ack) {
+ aio_bh_schedule_oneshot(qemu_get_aio_context(), sint_msg_bh,
+ sint_route);
+ }
+}
+
+/*
+ * Post a Hyper-V message to the staging area, for delivery to guest in the
+ * vcpu thread.
+ */
+int hyperv_post_msg(HvSintRoute *sint_route, struct hyperv_message *src_msg)
+{
+ HvSintStagedMessage *staged_msg = sint_route->staged_msg;
+
+ assert(staged_msg);
+
+ /* grab the staging area */
+ if (atomic_cmpxchg(&staged_msg->state, HV_STAGED_MSG_FREE,
+ HV_STAGED_MSG_BUSY) != HV_STAGED_MSG_FREE) {
+ return -EAGAIN;
+ }
+
+ memcpy(&staged_msg->msg, src_msg, sizeof(*src_msg));
+
+ /* hold a reference on sint_route until the callback is finished */
+ hyperv_sint_route_ref(sint_route);
+
+ /* schedule message posting attempt in vcpu thread */
+ async_run_on_cpu(sint_route->synic->cs, cpu_post_msg,
+ RUN_ON_CPU_HOST_PTR(sint_route));
+ return 0;
+}
+
+static void sint_ack_handler(EventNotifier *notifier)
+{
+ HvSintRoute *sint_route = container_of(notifier, HvSintRoute,
+ sint_ack_notifier);
+ event_notifier_test_and_clear(notifier);
+
+ /*
+ * the guest consumed the previous message so complete the current one with
+ * -EAGAIN and let the msg originator retry
+ */
+ aio_bh_schedule_oneshot(qemu_get_aio_context(), sint_msg_bh, sint_route);
+}
+
+/*
+ * Set given event flag for a given sint on a given vcpu, and signal the sint.
+ */
+int hyperv_set_event_flag(HvSintRoute *sint_route, unsigned eventno)
+{
+ int ret;
+ SynICState *synic = sint_route->synic;
+ unsigned long *flags, set_mask;
+ unsigned set_idx;
+
+ if (eventno > HV_EVENT_FLAGS_COUNT) {
+ return -EINVAL;
+ }
+ if (!synic->enabled || !synic->event_page_addr) {
+ return -ENXIO;
+ }
+
+ set_idx = BIT_WORD(eventno);
+ set_mask = BIT_MASK(eventno);
+ flags = synic->event_page->slot[sint_route->sint].flags;
+
+ if ((atomic_fetch_or(&flags[set_idx], set_mask) & set_mask) != set_mask) {
+ memory_region_set_dirty(&synic->event_page_mr, 0,
+ sizeof(*synic->event_page));
+ ret = hyperv_sint_route_set_sint(sint_route);
+ } else {
+ ret = 0;
+ }
+ return ret;
+}
+
+HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint,
+ HvSintMsgCb cb, void *cb_data)
+{
+ HvSintRoute *sint_route;
+ EventNotifier *ack_notifier;
+ int r, gsi;
+ CPUState *cs;
+ SynICState *synic;
+
+ cs = hyperv_find_vcpu(vp_index);
+ if (!cs) {
+ return NULL;
+ }
+
+ synic = get_synic(cs);
+ if (!synic) {
+ return NULL;
+ }
+
+ sint_route = g_new0(HvSintRoute, 1);
+ r = event_notifier_init(&sint_route->sint_set_notifier, false);
+ if (r) {
+ goto err;
+ }
+
+
+ ack_notifier = cb ? &sint_route->sint_ack_notifier : NULL;
+ if (ack_notifier) {
+ sint_route->staged_msg = g_new0(HvSintStagedMessage, 1);
+ sint_route->staged_msg->cb = cb;
+ sint_route->staged_msg->cb_data = cb_data;
+
+ r = event_notifier_init(ack_notifier, false);
+ if (r) {
+ goto err_sint_set_notifier;
+ }
+
+ event_notifier_set_handler(ack_notifier, sint_ack_handler);
+ }
+
+ gsi = kvm_irqchip_add_hv_sint_route(kvm_state, vp_index, sint);
+ if (gsi < 0) {
+ goto err_gsi;
+ }
+
+ r = kvm_irqchip_add_irqfd_notifier_gsi(kvm_state,
+ &sint_route->sint_set_notifier,
+ ack_notifier, gsi);
+ if (r) {
+ goto err_irqfd;
+ }
+ sint_route->gsi = gsi;
+ sint_route->synic = synic;
+ sint_route->sint = sint;
+ sint_route->refcount = 1;
+
+ return sint_route;
+
+err_irqfd:
+ kvm_irqchip_release_virq(kvm_state, gsi);
+err_gsi:
+ if (ack_notifier) {
+ event_notifier_set_handler(ack_notifier, NULL);
+ event_notifier_cleanup(ack_notifier);
+ g_free(sint_route->staged_msg);
+ }
+err_sint_set_notifier:
+ event_notifier_cleanup(&sint_route->sint_set_notifier);
+err:
+ g_free(sint_route);
+
+ return NULL;
+}
+
+void hyperv_sint_route_ref(HvSintRoute *sint_route)
+{
+ sint_route->refcount++;
+}
+
+void hyperv_sint_route_unref(HvSintRoute *sint_route)
+{
+ if (!sint_route) {
+ return;
+ }
+
+ assert(sint_route->refcount > 0);
+
+ if (--sint_route->refcount) {
+ return;
+ }
+
+ kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state,
+ &sint_route->sint_set_notifier,
+ sint_route->gsi);
+ kvm_irqchip_release_virq(kvm_state, sint_route->gsi);
+ if (sint_route->staged_msg) {
+ event_notifier_set_handler(&sint_route->sint_ack_notifier, NULL);
+ event_notifier_cleanup(&sint_route->sint_ack_notifier);
+ g_free(sint_route->staged_msg);
+ }
+ event_notifier_cleanup(&sint_route->sint_set_notifier);
+ g_free(sint_route);
+}
+
+int hyperv_sint_route_set_sint(HvSintRoute *sint_route)
+{
+ return event_notifier_set(&sint_route->sint_set_notifier);
+}
+
+typedef struct MsgHandler {
+ struct rcu_head rcu;
+ QLIST_ENTRY(MsgHandler) link;
+ uint32_t conn_id;
+ HvMsgHandler handler;
+ void *data;
+} MsgHandler;
+
+typedef struct EventFlagHandler {
+ struct rcu_head rcu;
+ QLIST_ENTRY(EventFlagHandler) link;
+ uint32_t conn_id;
+ EventNotifier *notifier;
+} EventFlagHandler;
+
+static QLIST_HEAD(, MsgHandler) msg_handlers;
+static QLIST_HEAD(, EventFlagHandler) event_flag_handlers;
+static QemuMutex handlers_mutex;
+
+static void __attribute__((constructor)) hv_init(void)
+{
+ QLIST_INIT(&msg_handlers);
+ QLIST_INIT(&event_flag_handlers);
+ qemu_mutex_init(&handlers_mutex);
+}
+
+int hyperv_set_msg_handler(uint32_t conn_id, HvMsgHandler handler, void *data)
+{
+ int ret;
+ MsgHandler *mh;
+
+ qemu_mutex_lock(&handlers_mutex);
+ QLIST_FOREACH(mh, &msg_handlers, link) {
+ if (mh->conn_id == conn_id) {
+ if (handler) {
+ ret = -EEXIST;
+ } else {
+ QLIST_REMOVE_RCU(mh, link);
+ g_free_rcu(mh, rcu);
+ ret = 0;
+ }
+ goto unlock;
+ }
+ }
+
+ if (handler) {
+ mh = g_new(MsgHandler, 1);
+ mh->conn_id = conn_id;
+ mh->handler = handler;
+ mh->data = data;
+ QLIST_INSERT_HEAD_RCU(&msg_handlers, mh, link);
+ ret = 0;
+ } else {
+ ret = -ENOENT;
+ }
+unlock:
+ qemu_mutex_unlock(&handlers_mutex);
+ return ret;
+}
+
+uint16_t hyperv_hcall_post_message(uint64_t param, bool fast)
+{
+ uint16_t ret;
+ hwaddr len;
+ struct hyperv_post_message_input *msg;
+ MsgHandler *mh;
+
+ if (fast) {
+ return HV_STATUS_INVALID_HYPERCALL_CODE;
+ }
+ if (param & (__alignof__(*msg) - 1)) {
+ return HV_STATUS_INVALID_ALIGNMENT;
+ }
+
+ len = sizeof(*msg);
+ msg = cpu_physical_memory_map(param, &len, 0);
+ if (len < sizeof(*msg)) {
+ ret = HV_STATUS_INSUFFICIENT_MEMORY;
+ goto unmap;
+ }
+ if (msg->payload_size > sizeof(msg->payload)) {
+ ret = HV_STATUS_INVALID_HYPERCALL_INPUT;
+ goto unmap;
+ }
+
+ ret = HV_STATUS_INVALID_CONNECTION_ID;
+ rcu_read_lock();
+ QLIST_FOREACH_RCU(mh, &msg_handlers, link) {
+ if (mh->conn_id == (msg->connection_id & HV_CONNECTION_ID_MASK)) {
+ ret = mh->handler(msg, mh->data);
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+unmap:
+ cpu_physical_memory_unmap(msg, len, 0, 0);
+ return ret;
+}
+
+static int set_event_flag_handler(uint32_t conn_id, EventNotifier *notifier)
+{
+ int ret;
+ EventFlagHandler *handler;
+
+ qemu_mutex_lock(&handlers_mutex);
+ QLIST_FOREACH(handler, &event_flag_handlers, link) {
+ if (handler->conn_id == conn_id) {
+ if (notifier) {
+ ret = -EEXIST;
+ } else {
+ QLIST_REMOVE_RCU(handler, link);
+ g_free_rcu(handler, rcu);
+ ret = 0;
+ }
+ goto unlock;
+ }
+ }
+
+ if (notifier) {
+ handler = g_new(EventFlagHandler, 1);
+ handler->conn_id = conn_id;
+ handler->notifier = notifier;
+ QLIST_INSERT_HEAD_RCU(&event_flag_handlers, handler, link);
+ ret = 0;
+ } else {
+ ret = -ENOENT;
+ }
+unlock:
+ qemu_mutex_unlock(&handlers_mutex);
+ return ret;
+}
+
+static bool process_event_flags_userspace;
+
+int hyperv_set_event_flag_handler(uint32_t conn_id, EventNotifier *notifier)
+{
+ if (!process_event_flags_userspace &&
+ !kvm_check_extension(kvm_state, KVM_CAP_HYPERV_EVENTFD)) {
+ process_event_flags_userspace = true;
+
+ warn_report("Hyper-V event signaling is not supported by this kernel; "
+ "using slower userspace hypercall processing");
+ }
+
+ if (!process_event_flags_userspace) {
+ struct kvm_hyperv_eventfd hvevfd = {
+ .conn_id = conn_id,
+ .fd = notifier ? event_notifier_get_fd(notifier) : -1,
+ .flags = notifier ? 0 : KVM_HYPERV_EVENTFD_DEASSIGN,
+ };
+
+ return kvm_vm_ioctl(kvm_state, KVM_HYPERV_EVENTFD, &hvevfd);
+ }
+ return set_event_flag_handler(conn_id, notifier);
+}
+
+uint16_t hyperv_hcall_signal_event(uint64_t param, bool fast)
+{
+ uint16_t ret;
+ EventFlagHandler *handler;
+
+ if (unlikely(!fast)) {
+ hwaddr addr = param;
+
+ if (addr & (__alignof__(addr) - 1)) {
+ return HV_STATUS_INVALID_ALIGNMENT;
+ }
+
+ param = ldq_phys(&address_space_memory, addr);
+ }
+
+ /*
+ * Per spec, bits 32-47 contain the extra "flag number". However, we
+ * have no use for it, and in all known usecases it is zero, so just
+ * report lookup failure if it isn't.
+ */
+ if (param & 0xffff00000000ULL) {
+ return HV_STATUS_INVALID_PORT_ID;
+ }
+ /* remaining bits are reserved-zero */
+ if (param & ~HV_CONNECTION_ID_MASK) {
+ return HV_STATUS_INVALID_HYPERCALL_INPUT;
+ }
+
+ ret = HV_STATUS_INVALID_CONNECTION_ID;
+ rcu_read_lock();
+ QLIST_FOREACH_RCU(handler, &event_flag_handlers, link) {
+ if (handler->conn_id == param) {
+ event_notifier_set(handler->notifier);
+ ret = 0;
+ break;
+ }
+ }
+ rcu_read_unlock();
+ return ret;
+}
diff --git a/hw/hyperv/hyperv_testdev.c b/hw/hyperv/hyperv_testdev.c
new file mode 100644
index 0000000000..4880333cf5
--- /dev/null
+++ b/hw/hyperv/hyperv_testdev.c
@@ -0,0 +1,327 @@
+/*
+ * QEMU KVM Hyper-V test device to support Hyper-V kvm-unit-tests
+ *
+ * Copyright (C) 2015 Andrey Smetanin <asmetanin@virtuozzo.com>
+ *
+ * Authors:
+ * Andrey Smetanin <asmetanin@virtuozzo.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/main-loop.h"
+#include "qemu/queue.h"
+#include "hw/qdev.h"
+#include "hw/isa/isa.h"
+#include "hw/hyperv/hyperv.h"
+
+typedef struct TestSintRoute {
+ QLIST_ENTRY(TestSintRoute) le;
+ uint8_t vp_index;
+ uint8_t sint;
+ HvSintRoute *sint_route;
+} TestSintRoute;
+
+typedef struct TestMsgConn {
+ QLIST_ENTRY(TestMsgConn) le;
+ uint8_t conn_id;
+ HvSintRoute *sint_route;
+ struct hyperv_message msg;
+} TestMsgConn;
+
+typedef struct TestEvtConn {
+ QLIST_ENTRY(TestEvtConn) le;
+ uint8_t conn_id;
+ HvSintRoute *sint_route;
+ EventNotifier notifier;
+} TestEvtConn;
+
+struct HypervTestDev {
+ ISADevice parent_obj;
+ MemoryRegion sint_control;
+ QLIST_HEAD(, TestSintRoute) sint_routes;
+ QLIST_HEAD(, TestMsgConn) msg_conns;
+ QLIST_HEAD(, TestEvtConn) evt_conns;
+};
+typedef struct HypervTestDev HypervTestDev;
+
+#define TYPE_HYPERV_TEST_DEV "hyperv-testdev"
+#define HYPERV_TEST_DEV(obj) \
+ OBJECT_CHECK(HypervTestDev, (obj), TYPE_HYPERV_TEST_DEV)
+
+enum {
+ HV_TEST_DEV_SINT_ROUTE_CREATE = 1,
+ HV_TEST_DEV_SINT_ROUTE_DESTROY,
+ HV_TEST_DEV_SINT_ROUTE_SET_SINT,
+ HV_TEST_DEV_MSG_CONN_CREATE,
+ HV_TEST_DEV_MSG_CONN_DESTROY,
+ HV_TEST_DEV_EVT_CONN_CREATE,
+ HV_TEST_DEV_EVT_CONN_DESTROY,
+};
+
+static void sint_route_create(HypervTestDev *dev,
+ uint8_t vp_index, uint8_t sint)
+{
+ TestSintRoute *sint_route;
+
+ sint_route = g_new0(TestSintRoute, 1);
+ assert(sint_route);
+
+ sint_route->vp_index = vp_index;
+ sint_route->sint = sint;
+
+ sint_route->sint_route = hyperv_sint_route_new(vp_index, sint, NULL, NULL);
+ assert(sint_route->sint_route);
+
+ QLIST_INSERT_HEAD(&dev->sint_routes, sint_route, le);
+}
+
+static TestSintRoute *sint_route_find(HypervTestDev *dev,
+ uint8_t vp_index, uint8_t sint)
+{
+ TestSintRoute *sint_route;
+
+ QLIST_FOREACH(sint_route, &dev->sint_routes, le) {
+ if (sint_route->vp_index == vp_index && sint_route->sint == sint) {
+ return sint_route;
+ }
+ }
+ assert(false);
+ return NULL;
+}
+
+static void sint_route_destroy(HypervTestDev *dev,
+ uint8_t vp_index, uint8_t sint)
+{
+ TestSintRoute *sint_route;
+
+ sint_route = sint_route_find(dev, vp_index, sint);
+ QLIST_REMOVE(sint_route, le);
+ hyperv_sint_route_unref(sint_route->sint_route);
+ g_free(sint_route);
+}
+
+static void sint_route_set_sint(HypervTestDev *dev,
+ uint8_t vp_index, uint8_t sint)
+{
+ TestSintRoute *sint_route;
+
+ sint_route = sint_route_find(dev, vp_index, sint);
+
+ hyperv_sint_route_set_sint(sint_route->sint_route);
+}
+
+static void msg_retry(void *opaque)
+{
+ TestMsgConn *conn = opaque;
+ assert(!hyperv_post_msg(conn->sint_route, &conn->msg));
+}
+
+static void msg_cb(void *data, int status)
+{
+ TestMsgConn *conn = data;
+
+ if (!status) {
+ return;
+ }
+
+ assert(status == -EAGAIN);
+
+ aio_bh_schedule_oneshot(qemu_get_aio_context(), msg_retry, conn);
+}
+
+static uint16_t msg_handler(const struct hyperv_post_message_input *msg,
+ void *data)
+{
+ int ret;
+ TestMsgConn *conn = data;
+
+ /* post the same message we've got */
+ conn->msg.header.message_type = msg->message_type;
+ assert(msg->payload_size < sizeof(conn->msg.payload));
+ conn->msg.header.payload_size = msg->payload_size;
+ memcpy(&conn->msg.payload, msg->payload, msg->payload_size);
+
+ ret = hyperv_post_msg(conn->sint_route, &conn->msg);
+
+ switch (ret) {
+ case 0:
+ return HV_STATUS_SUCCESS;
+ case -EAGAIN:
+ return HV_STATUS_INSUFFICIENT_BUFFERS;
+ default:
+ return HV_STATUS_INVALID_HYPERCALL_INPUT;
+ }
+}
+
+static void msg_conn_create(HypervTestDev *dev, uint8_t vp_index,
+ uint8_t sint, uint8_t conn_id)
+{
+ TestMsgConn *conn;
+
+ conn = g_new0(TestMsgConn, 1);
+ assert(conn);
+
+ conn->conn_id = conn_id;
+
+ conn->sint_route = hyperv_sint_route_new(vp_index, sint, msg_cb, conn);
+ assert(conn->sint_route);
+
+ assert(!hyperv_set_msg_handler(conn->conn_id, msg_handler, conn));
+
+ QLIST_INSERT_HEAD(&dev->msg_conns, conn, le);
+}
+
+static void msg_conn_destroy(HypervTestDev *dev, uint8_t conn_id)
+{
+ TestMsgConn *conn;
+
+ QLIST_FOREACH(conn, &dev->msg_conns, le) {
+ if (conn->conn_id == conn_id) {
+ QLIST_REMOVE(conn, le);
+ hyperv_set_msg_handler(conn->conn_id, NULL, NULL);
+ hyperv_sint_route_unref(conn->sint_route);
+ g_free(conn);
+ return;
+ }
+ }
+ assert(false);
+}
+
+static void evt_conn_handler(EventNotifier *notifier)
+{
+ TestEvtConn *conn = container_of(notifier, TestEvtConn, notifier);
+
+ event_notifier_test_and_clear(notifier);
+
+ /* signal the same event flag we've got */
+ assert(!hyperv_set_event_flag(conn->sint_route, conn->conn_id));
+}
+
+static void evt_conn_create(HypervTestDev *dev, uint8_t vp_index,
+ uint8_t sint, uint8_t conn_id)
+{
+ TestEvtConn *conn;
+
+ conn = g_new0(TestEvtConn, 1);
+ assert(conn);
+
+ conn->conn_id = conn_id;
+
+ conn->sint_route = hyperv_sint_route_new(vp_index, sint, NULL, NULL);
+ assert(conn->sint_route);
+
+ assert(!event_notifier_init(&conn->notifier, false));
+
+ event_notifier_set_handler(&conn->notifier, evt_conn_handler);
+
+ assert(!hyperv_set_event_flag_handler(conn_id, &conn->notifier));
+
+ QLIST_INSERT_HEAD(&dev->evt_conns, conn, le);
+}
+
+static void evt_conn_destroy(HypervTestDev *dev, uint8_t conn_id)
+{
+ TestEvtConn *conn;
+
+ QLIST_FOREACH(conn, &dev->evt_conns, le) {
+ if (conn->conn_id == conn_id) {
+ QLIST_REMOVE(conn, le);
+ hyperv_set_event_flag_handler(conn->conn_id, NULL);
+ event_notifier_set_handler(&conn->notifier, NULL);
+ event_notifier_cleanup(&conn->notifier);
+ hyperv_sint_route_unref(conn->sint_route);
+ g_free(conn);
+ return;
+ }
+ }
+ assert(false);
+}
+
+static uint64_t hv_test_dev_read(void *opaque, hwaddr addr, unsigned size)
+{
+ return 0;
+}
+
+static void hv_test_dev_write(void *opaque, hwaddr addr, uint64_t data,
+ uint32_t len)
+{
+ HypervTestDev *dev = HYPERV_TEST_DEV(opaque);
+ uint8_t sint = data & 0xFF;
+ uint8_t vp_index = (data >> 8ULL) & 0xFF;
+ uint8_t ctl = (data >> 16ULL) & 0xFF;
+ uint8_t conn_id = (data >> 24ULL) & 0xFF;
+
+ switch (ctl) {
+ case HV_TEST_DEV_SINT_ROUTE_CREATE:
+ sint_route_create(dev, vp_index, sint);
+ break;
+ case HV_TEST_DEV_SINT_ROUTE_DESTROY:
+ sint_route_destroy(dev, vp_index, sint);
+ break;
+ case HV_TEST_DEV_SINT_ROUTE_SET_SINT:
+ sint_route_set_sint(dev, vp_index, sint);
+ break;
+ case HV_TEST_DEV_MSG_CONN_CREATE:
+ msg_conn_create(dev, vp_index, sint, conn_id);
+ break;
+ case HV_TEST_DEV_MSG_CONN_DESTROY:
+ msg_conn_destroy(dev, conn_id);
+ break;
+ case HV_TEST_DEV_EVT_CONN_CREATE:
+ evt_conn_create(dev, vp_index, sint, conn_id);
+ break;
+ case HV_TEST_DEV_EVT_CONN_DESTROY:
+ evt_conn_destroy(dev, conn_id);
+ break;
+ default:
+ break;
+ }
+}
+
+static const MemoryRegionOps synic_test_sint_ops = {
+ .read = hv_test_dev_read,
+ .write = hv_test_dev_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void hv_test_dev_realizefn(DeviceState *d, Error **errp)
+{
+ ISADevice *isa = ISA_DEVICE(d);
+ HypervTestDev *dev = HYPERV_TEST_DEV(d);
+ MemoryRegion *io = isa_address_space_io(isa);
+
+ QLIST_INIT(&dev->sint_routes);
+ QLIST_INIT(&dev->msg_conns);
+ QLIST_INIT(&dev->evt_conns);
+ memory_region_init_io(&dev->sint_control, OBJECT(dev),
+ &synic_test_sint_ops, dev,
+ "hyperv-testdev-ctl", 4);
+ memory_region_add_subregion(io, 0x3000, &dev->sint_control);
+}
+
+static void hv_test_dev_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->realize = hv_test_dev_realizefn;
+}
+
+static const TypeInfo hv_test_dev_info = {
+ .name = TYPE_HYPERV_TEST_DEV,
+ .parent = TYPE_ISA_DEVICE,
+ .instance_size = sizeof(HypervTestDev),
+ .class_init = hv_test_dev_class_init,
+};
+
+static void hv_test_dev_register_types(void)
+{
+ type_register_static(&hv_test_dev_info);
+}
+type_init(hv_test_dev_register_types);
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 6d50b03cfd..680350b3c3 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -71,7 +71,6 @@ obj-$(CONFIG_IOTKIT_SYSCTL) += iotkit-sysctl.o
obj-$(CONFIG_IOTKIT_SYSINFO) += iotkit-sysinfo.o
obj-$(CONFIG_PVPANIC) += pvpanic.o
-obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
obj-$(CONFIG_AUX) += auxbus.o
obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o
obj-$(CONFIG_MSF2) += msf2-sysreg.o
diff --git a/hw/misc/edu.c b/hw/misc/edu.c
index 0687ffd343..cdcf550dd7 100644
--- a/hw/misc/edu.c
+++ b/hw/misc/edu.c
@@ -342,7 +342,7 @@ static void *edu_fact_thread(void *opaque)
static void pci_edu_realize(PCIDevice *pdev, Error **errp)
{
- EduState *edu = DO_UPCAST(EduState, pdev, pdev);
+ EduState *edu = EDU(pdev);
uint8_t *pci_conf = pdev->config;
pci_config_set_interrupt_pin(pci_conf, 1);
@@ -365,7 +365,7 @@ static void pci_edu_realize(PCIDevice *pdev, Error **errp)
static void pci_edu_uninit(PCIDevice *pdev)
{
- EduState *edu = DO_UPCAST(EduState, pdev, pdev);
+ EduState *edu = EDU(pdev);
qemu_mutex_lock(&edu->thr_mutex);
edu->stopping = true;
diff --git a/hw/misc/hyperv_testdev.c b/hw/misc/hyperv_testdev.c
deleted file mode 100644
index 7549f470b1..0000000000
--- a/hw/misc/hyperv_testdev.c
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * QEMU KVM Hyper-V test device to support Hyper-V kvm-unit-tests
- *
- * Copyright (C) 2015 Andrey Smetanin <asmetanin@virtuozzo.com>
- *
- * Authors:
- * Andrey Smetanin <asmetanin@virtuozzo.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- *
- */
-
-#include "qemu/osdep.h"
-#include <linux/kvm.h>
-#include "hw/hw.h"
-#include "hw/qdev.h"
-#include "hw/isa/isa.h"
-#include "sysemu/kvm.h"
-#include "target/i386/hyperv.h"
-#include "kvm_i386.h"
-
-#define HV_TEST_DEV_MAX_SINT_ROUTES 64
-
-struct HypervTestDev {
- ISADevice parent_obj;
- MemoryRegion sint_control;
- HvSintRoute *sint_route[HV_TEST_DEV_MAX_SINT_ROUTES];
-};
-typedef struct HypervTestDev HypervTestDev;
-
-#define TYPE_HYPERV_TEST_DEV "hyperv-testdev"
-#define HYPERV_TEST_DEV(obj) \
- OBJECT_CHECK(HypervTestDev, (obj), TYPE_HYPERV_TEST_DEV)
-
-enum {
- HV_TEST_DEV_SINT_ROUTE_CREATE = 1,
- HV_TEST_DEV_SINT_ROUTE_DESTROY,
- HV_TEST_DEV_SINT_ROUTE_SET_SINT
-};
-
-static int alloc_sint_route_index(HypervTestDev *dev)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(dev->sint_route); i++) {
- if (dev->sint_route[i] == NULL) {
- return i;
- }
- }
- return -1;
-}
-
-static void free_sint_route_index(HypervTestDev *dev, int i)
-{
- assert(i >= 0 && i < ARRAY_SIZE(dev->sint_route));
- dev->sint_route[i] = NULL;
-}
-
-static int find_sint_route_index(HypervTestDev *dev, uint32_t vp_index,
- uint32_t sint)
-{
- HvSintRoute *sint_route;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(dev->sint_route); i++) {
- sint_route = dev->sint_route[i];
- if (sint_route && sint_route->vp_index == vp_index &&
- sint_route->sint == sint) {
- return i;
- }
- }
- return -1;
-}
-
-static void hv_synic_test_dev_control(HypervTestDev *dev, uint32_t ctl,
- uint32_t vp_index, uint32_t sint)
-{
- int i;
- HvSintRoute *sint_route;
-
- switch (ctl) {
- case HV_TEST_DEV_SINT_ROUTE_CREATE:
- i = alloc_sint_route_index(dev);
- assert(i >= 0);
- sint_route = kvm_hv_sint_route_create(vp_index, sint, NULL);
- assert(sint_route);
- dev->sint_route[i] = sint_route;
- break;
- case HV_TEST_DEV_SINT_ROUTE_DESTROY:
- i = find_sint_route_index(dev, vp_index, sint);
- assert(i >= 0);
- sint_route = dev->sint_route[i];
- kvm_hv_sint_route_destroy(sint_route);
- free_sint_route_index(dev, i);
- break;
- case HV_TEST_DEV_SINT_ROUTE_SET_SINT:
- i = find_sint_route_index(dev, vp_index, sint);
- assert(i >= 0);
- sint_route = dev->sint_route[i];
- kvm_hv_sint_route_set_sint(sint_route);
- break;
- default:
- break;
- }
-}
-
-static uint64_t hv_test_dev_read(void *opaque, hwaddr addr, unsigned size)
-{
- return 0;
-}
-
-static void hv_test_dev_write(void *opaque, hwaddr addr, uint64_t data,
- uint32_t len)
-{
- HypervTestDev *dev = HYPERV_TEST_DEV(opaque);
- uint8_t ctl;
-
- ctl = (data >> 16ULL) & 0xFF;
- switch (ctl) {
- case HV_TEST_DEV_SINT_ROUTE_CREATE:
- case HV_TEST_DEV_SINT_ROUTE_DESTROY:
- case HV_TEST_DEV_SINT_ROUTE_SET_SINT: {
- uint8_t sint = data & 0xFF;
- uint8_t vp_index = (data >> 8ULL) & 0xFF;
- hv_synic_test_dev_control(dev, ctl, vp_index, sint);
- break;
- }
- default:
- break;
- }
-}
-
-static const MemoryRegionOps synic_test_sint_ops = {
- .read = hv_test_dev_read,
- .write = hv_test_dev_write,
- .valid.min_access_size = 4,
- .valid.max_access_size = 4,
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void hv_test_dev_realizefn(DeviceState *d, Error **errp)
-{
- ISADevice *isa = ISA_DEVICE(d);
- HypervTestDev *dev = HYPERV_TEST_DEV(d);
- MemoryRegion *io = isa_address_space_io(isa);
-
- memset(dev->sint_route, 0, sizeof(dev->sint_route));
- memory_region_init_io(&dev->sint_control, OBJECT(dev),
- &synic_test_sint_ops, dev,
- "hyperv-testdev-ctl", 4);
- memory_region_add_subregion(io, 0x3000, &dev->sint_control);
-}
-
-static void hv_test_dev_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
-
- set_bit(DEVICE_CATEGORY_MISC, dc->categories);
- dc->realize = hv_test_dev_realizefn;
-}
-
-static const TypeInfo hv_test_dev_info = {
- .name = TYPE_HYPERV_TEST_DEV,
- .parent = TYPE_ISA_DEVICE,
- .instance_size = sizeof(HypervTestDev),
- .class_init = hv_test_dev_class_init,
-};
-
-static void hv_test_dev_register_types(void)
-{
- type_register_static(&hv_test_dev_info);
-}
-type_init(hv_test_dev_register_types);
diff --git a/hw/net/e1000.c b/hw/net/e1000.c
index 13a9494a8d..5e144cb4e4 100644
--- a/hw/net/e1000.c
+++ b/hw/net/e1000.c
@@ -36,6 +36,7 @@
#include "qemu/range.h"
#include "e1000x_common.h"
+#include "trace.h"
static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
@@ -847,6 +848,15 @@ static uint64_t rx_desc_base(E1000State *s)
return (bah << 32) + bal;
}
+static void
+e1000_receiver_overrun(E1000State *s, size_t size)
+{
+ trace_e1000_receiver_overrun(size, s->mac_reg[RDH], s->mac_reg[RDT]);
+ e1000x_inc_reg_if_not_full(s->mac_reg, RNBC);
+ e1000x_inc_reg_if_not_full(s->mac_reg, MPC);
+ set_ics(s, 0, E1000_ICS_RXO);
+}
+
static ssize_t
e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt)
{
@@ -916,8 +926,8 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt)
desc_offset = 0;
total_size = size + e1000x_fcs_len(s->mac_reg);
if (!e1000_has_rxbufs(s, total_size)) {
- set_ics(s, 0, E1000_ICS_RXO);
- return -1;
+ e1000_receiver_overrun(s, total_size);
+ return -1;
}
do {
desc_size = total_size - desc_offset;
@@ -969,7 +979,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt)
rdh_start >= s->mac_reg[RDLEN] / sizeof(desc)) {
DBGOUT(RXERR, "RDH wraparound @%x, RDT %x, RDLEN %x\n",
rdh_start, s->mac_reg[RDT], s->mac_reg[RDLEN]);
- set_ics(s, 0, E1000_ICS_RXO);
+ e1000_receiver_overrun(s, total_size);
return -1;
}
} while (desc_offset < total_size);
diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c
index 07d79e317f..869518ee06 100644
--- a/hw/net/ne2000.c
+++ b/hw/net/ne2000.c
@@ -174,7 +174,7 @@ static int ne2000_buffer_full(NE2000State *s)
ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
{
NE2000State *s = qemu_get_nic_opaque(nc);
- int size = size_;
+ size_t size = size_;
uint8_t *p;
unsigned int total_len, next, avail, len, index, mcast_idx;
uint8_t buf1[60];
@@ -182,7 +182,7 @@ ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
#if defined(DEBUG_NE2000)
- printf("NE2000: received len=%d\n", size);
+ printf("NE2000: received len=%zu\n", size);
#endif
if (s->cmd & E8390_STOP || ne2000_buffer_full(s))
diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c
index 0c44554168..d9ba04bdfc 100644
--- a/hw/net/pcnet.c
+++ b/hw/net/pcnet.c
@@ -988,14 +988,14 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
uint8_t buf1[60];
int remaining;
int crc_err = 0;
- int size = size_;
+ size_t size = size_;
if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size ||
(CSR_LOOP(s) && !s->looptest)) {
return -1;
}
#ifdef PCNET_DEBUG
- printf("pcnet_receive size=%d\n", size);
+ printf("pcnet_receive size=%zu\n", size);
#endif
/* if too small buffer, then expand it */
diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c
index 46daa16202..2342a095e3 100644
--- a/hw/net/rtl8139.c
+++ b/hw/net/rtl8139.c
@@ -817,7 +817,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t
RTL8139State *s = qemu_get_nic_opaque(nc);
PCIDevice *d = PCI_DEVICE(s);
/* size is the length of the buffer passed to the driver */
- int size = size_;
+ size_t size = size_;
const uint8_t *dot1q_buf = NULL;
uint32_t packet_header = 0;
@@ -826,7 +826,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t
static const uint8_t broadcast_macaddr[6] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
- DPRINTF(">>> received len=%d\n", size);
+ DPRINTF(">>> received len=%zu\n", size);
/* test if board clock is stopped */
if (!s->clock_enabled)
@@ -1035,7 +1035,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t
if (size+4 > rx_space)
{
- DPRINTF("C+ Rx mode : descriptor %d size %d received %d + 4\n",
+ DPRINTF("C+ Rx mode : descriptor %d size %d received %zu + 4\n",
descriptor, rx_space, size);
s->IntrStatus |= RxOverflow;
@@ -1148,7 +1148,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t
if (avail != 0 && RX_ALIGN(size + 8) >= avail)
{
DPRINTF("rx overflow: rx buffer length %d head 0x%04x "
- "read 0x%04x === available 0x%04x need 0x%04x\n",
+ "read 0x%04x === available 0x%04x need 0x%04zx\n",
s->RxBufferSize, s->RxBufAddr, s->RxBufPtr, avail, size + 8);
s->IntrStatus |= RxOverflow;
diff --git a/hw/net/trace-events b/hw/net/trace-events
index c1dea4b156..9d49f62fa1 100644
--- a/hw/net/trace-events
+++ b/hw/net/trace-events
@@ -98,6 +98,9 @@ net_rx_pkt_rss_ip6_ex(void) "Calculating IPv6/EX RSS hash"
net_rx_pkt_rss_hash(size_t rss_length, uint32_t rss_hash) "RSS hash for %zu bytes: 0x%X"
net_rx_pkt_rss_add_chunk(void* ptr, size_t size, size_t input_offset) "Add RSS chunk %p, %zu bytes, RSS input offset %zu bytes"
+# hw/net/e1000.c
+e1000_receiver_overrun(size_t s, uint32_t rdh, uint32_t rdt) "Receiver overrun: dropped packet of %zu bytes, RDH=%u, RDT=%u"
+
# hw/net/e1000x_common.c
e1000x_rx_can_recv_disabled(bool link_up, bool rx_enabled, bool pci_master) "link_up: %d, rx_enabled %d, pci_master %d"
e1000x_vlan_is_vlan_pkt(bool is_vlan_pkt, uint16_t eth_proto, uint16_t vet) "Is VLAN packet: %d, ETH proto: 0x%X, VET: 0x%X"
diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c
index 0e608347c1..da73743fa2 100644
--- a/hw/pci-host/piix.c
+++ b/hw/pci-host/piix.c
@@ -327,6 +327,10 @@ static void i440fx_pcihost_realize(DeviceState *dev, Error **errp)
sysbus_add_io(sbd, 0xcfc, &s->data_mem);
sysbus_init_ioports(sbd, 0xcfc, 4);
+
+ /* register i440fx 0xcf8 port as coalesced pio */
+ memory_region_set_flush_coalesced(&s->data_mem);
+ memory_region_add_coalescing(&s->conf_mem, 0, 4);
}
static void i440fx_realize(PCIDevice *dev, Error **errp)
diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
index 02f9576588..8ce1e09932 100644
--- a/hw/pci-host/q35.c
+++ b/hw/pci-host/q35.c
@@ -51,6 +51,10 @@ static void q35_host_realize(DeviceState *dev, Error **errp)
sysbus_add_io(sbd, MCH_HOST_BRIDGE_CONFIG_DATA, &pci->data_mem);
sysbus_init_ioports(sbd, MCH_HOST_BRIDGE_CONFIG_DATA, 4);
+ /* register q35 0xcf8 port as coalesced pio */
+ memory_region_set_flush_coalesced(&pci->data_mem);
+ memory_region_add_coalescing(&pci->conf_mem, 0, 4);
+
pci->bus = pci_root_bus_new(DEVICE(s), "pcie.0",
s->mch.pci_address_space,
s->mch.address_space_io,
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index c43163cef4..e2c5408aa2 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -441,9 +441,18 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed)
}
switch (error) {
case 0:
- /* The command has run, no need to fake sense. */
+ /* A passthrough command has run and has produced sense data; check
+ * whether the error has to be handled by the guest or should rather
+ * pause the host.
+ */
assert(r->status && *r->status);
- scsi_req_complete(&r->req, *r->status);
+ error = scsi_sense_buf_to_errno(r->req.sense, sizeof(r->req.sense));
+ if (error == ECANCELED || error == EAGAIN || error == ENOTCONN ||
+ error == 0) {
+ /* These errors are handled by guest. */
+ scsi_req_complete(&r->req, *r->status);
+ return true;
+ }
break;
case ENOMEDIUM:
scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
@@ -462,23 +471,17 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed)
break;
}
}
- if (!error) {
- assert(r->status && *r->status);
- error = scsi_sense_buf_to_errno(r->req.sense, sizeof(r->req.sense));
- if (error == ECANCELED || error == EAGAIN || error == ENOTCONN ||
- error == 0) {
- /* These errors are handled by guest. */
- scsi_req_complete(&r->req, *r->status);
- return true;
- }
+ blk_error_action(s->qdev.conf.blk, action, is_read, error);
+ if (action == BLOCK_ERROR_ACTION_IGNORE) {
+ scsi_req_complete(&r->req, 0);
+ return true;
}
- blk_error_action(s->qdev.conf.blk, action, is_read, error);
if (action == BLOCK_ERROR_ACTION_STOP) {
scsi_req_retry(&r->req);
}
- return action != BLOCK_ERROR_ACTION_IGNORE;
+ return false;
}
static void scsi_write_complete_noio(SCSIDiskReq *r, int ret)
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index 5a3057d1f8..3aa99717e2 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -797,16 +797,8 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev,
virtio_scsi_acquire(s);
blk_set_aio_context(sd->conf.blk, s->ctx);
virtio_scsi_release(s);
- }
-}
-/* Announce the new device after it has been plugged */
-static void virtio_scsi_post_hotplug(HotplugHandler *hotplug_dev,
- DeviceState *dev)
-{
- VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev);
- VirtIOSCSI *s = VIRTIO_SCSI(vdev);
- SCSIDevice *sd = SCSI_DEVICE(dev);
+ }
if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) {
virtio_scsi_acquire(s);
@@ -976,7 +968,6 @@ static void virtio_scsi_class_init(ObjectClass *klass, void *data)
vdc->start_ioeventfd = virtio_scsi_dataplane_start;
vdc->stop_ioeventfd = virtio_scsi_dataplane_stop;
hc->plug = virtio_scsi_hotplug;
- hc->post_plug = virtio_scsi_post_hotplug;
hc->unplug = virtio_scsi_hotunplug;
}
diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c
index acee47da0e..e4e4de8b8a 100644
--- a/hw/timer/mc146818rtc.c
+++ b/hw/timer/mc146818rtc.c
@@ -34,6 +34,7 @@
#include "qapi/qapi-commands-misc.h"
#include "qapi/qapi-events-misc.h"
#include "qapi/visitor.h"
+#include "exec/address-spaces.h"
#ifdef TARGET_I386
#include "hw/i386/apic.h"
@@ -70,6 +71,7 @@ typedef struct RTCState {
ISADevice parent_obj;
MemoryRegion io;
+ MemoryRegion coalesced_io;
uint8_t cmos_data[128];
uint8_t cmos_index;
int32_t base_year;
@@ -990,6 +992,13 @@ static void rtc_realizefn(DeviceState *dev, Error **errp)
memory_region_init_io(&s->io, OBJECT(s), &cmos_ops, s, "rtc", 2);
isa_register_ioport(isadev, &s->io, base);
+ /* register rtc 0x70 port for coalesced_pio */
+ memory_region_set_flush_coalesced(&s->io);
+ memory_region_init_io(&s->coalesced_io, OBJECT(s), &cmos_ops,
+ s, "rtc-index", 1);
+ memory_region_add_subregion(&s->io, 0, &s->coalesced_io);
+ memory_region_add_coalescing(&s->coalesced_io, 0, 1);
+
qdev_set_legacy_instance_id(dev, base, 3);
qemu_register_reset(rtc_reset, s);
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 4e61944f14..4136d239dd 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -1611,6 +1611,8 @@ void virtio_del_queue(VirtIODevice *vdev, int n)
vdev->vq[n].vring.num = 0;
vdev->vq[n].vring.num_default = 0;
+ vdev->vq[n].handle_output = NULL;
+ vdev->vq[n].handle_aio_output = NULL;
}
static void virtio_set_isr(VirtIODevice *vdev, int value)
diff --git a/include/block/aio.h b/include/block/aio.h
index f08630c6e5..0ca25dfec6 100644
--- a/include/block/aio.h
+++ b/include/block/aio.h
@@ -388,18 +388,41 @@ struct LinuxAioState *aio_setup_linux_aio(AioContext *ctx, Error **errp);
struct LinuxAioState *aio_get_linux_aio(AioContext *ctx);
/**
- * aio_timer_new:
+ * aio_timer_new_with_attrs:
* @ctx: the aio context
* @type: the clock type
* @scale: the scale
+ * @attributes: 0, or one to multiple OR'ed QEMU_TIMER_ATTR_<id> values
+ * to assign
* @cb: the callback to call on timer expiry
* @opaque: the opaque pointer to pass to the callback
*
- * Allocate a new timer attached to the context @ctx.
+ * Allocate a new timer (with attributes) attached to the context @ctx.
* The function is responsible for memory allocation.
*
- * The preferred interface is aio_timer_init. Use that
- * unless you really need dynamic memory allocation.
+ * The preferred interface is aio_timer_init or aio_timer_init_with_attrs.
+ * Use that unless you really need dynamic memory allocation.
+ *
+ * Returns: a pointer to the new timer
+ */
+static inline QEMUTimer *aio_timer_new_with_attrs(AioContext *ctx,
+ QEMUClockType type,
+ int scale, int attributes,
+ QEMUTimerCB *cb, void *opaque)
+{
+ return timer_new_full(&ctx->tlg, type, scale, attributes, cb, opaque);
+}
+
+/**
+ * aio_timer_new:
+ * @ctx: the aio context
+ * @type: the clock type
+ * @scale: the scale
+ * @cb: the callback to call on timer expiry
+ * @opaque: the opaque pointer to pass to the callback
+ *
+ * Allocate a new timer attached to the context @ctx.
+ * See aio_timer_new_with_attrs for details.
*
* Returns: a pointer to the new timer
*/
@@ -407,7 +430,29 @@ static inline QEMUTimer *aio_timer_new(AioContext *ctx, QEMUClockType type,
int scale,
QEMUTimerCB *cb, void *opaque)
{
- return timer_new_tl(ctx->tlg.tl[type], scale, cb, opaque);
+ return timer_new_full(&ctx->tlg, type, scale, 0, cb, opaque);
+}
+
+/**
+ * aio_timer_init_with_attrs:
+ * @ctx: the aio context
+ * @ts: the timer
+ * @type: the clock type
+ * @scale: the scale
+ * @attributes: 0, or one to multiple OR'ed QEMU_TIMER_ATTR_<id> values
+ * to assign
+ * @cb: the callback to call on timer expiry
+ * @opaque: the opaque pointer to pass to the callback
+ *
+ * Initialise a new timer (with attributes) attached to the context @ctx.
+ * The caller is responsible for memory allocation.
+ */
+static inline void aio_timer_init_with_attrs(AioContext *ctx,
+ QEMUTimer *ts, QEMUClockType type,
+ int scale, int attributes,
+ QEMUTimerCB *cb, void *opaque)
+{
+ timer_init_full(ts, &ctx->tlg, type, scale, attributes, cb, opaque);
}
/**
@@ -420,14 +465,14 @@ static inline QEMUTimer *aio_timer_new(AioContext *ctx, QEMUClockType type,
* @opaque: the opaque pointer to pass to the callback
*
* Initialise a new timer attached to the context @ctx.
- * The caller is responsible for memory allocation.
+ * See aio_timer_init_with_attrs for details.
*/
static inline void aio_timer_init(AioContext *ctx,
QEMUTimer *ts, QEMUClockType type,
int scale,
QEMUTimerCB *cb, void *opaque)
{
- timer_init_tl(ts, ctx->tlg.tl[type], scale, cb, opaque);
+ timer_init_full(ts, &ctx->tlg, type, scale, 0, cb, opaque);
}
/**
diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h
index a171ffc1a4..4ff62f32bf 100644
--- a/include/exec/cpu-defs.h
+++ b/include/exec/cpu-defs.h
@@ -24,6 +24,7 @@
#endif
#include "qemu/host-utils.h"
+#include "qemu/thread.h"
#include "qemu/queue.h"
#ifdef CONFIG_TCG
#include "tcg-target.h"
@@ -142,6 +143,8 @@ typedef struct CPUIOTLBEntry {
#define CPU_COMMON_TLB \
/* The meaning of the MMU modes is defined in the target code. */ \
+ /* tlb_lock serializes updates to tlb_table and tlb_v_table */ \
+ QemuSpin tlb_lock; \
CPUTLBEntry tlb_table[NB_MMU_MODES][CPU_TLB_SIZE]; \
CPUTLBEntry tlb_v_table[NB_MMU_MODES][CPU_VTLB_SIZE]; \
CPUIOTLBEntry iotlb[NB_MMU_MODES][CPU_TLB_SIZE]; \
diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h
index 41ed0526e2..959068495a 100644
--- a/include/exec/cpu_ldst.h
+++ b/include/exec/cpu_ldst.h
@@ -126,6 +126,29 @@ extern __thread uintptr_t helper_retaddr;
/* The memory helpers for tcg-generated code need tcg_target_long etc. */
#include "tcg.h"
+static inline target_ulong tlb_addr_write(const CPUTLBEntry *entry)
+{
+#if TCG_OVERSIZED_GUEST
+ return entry->addr_write;
+#else
+ return atomic_read(&entry->addr_write);
+#endif
+}
+
+/* Find the TLB index corresponding to the mmu_idx + address pair. */
+static inline uintptr_t tlb_index(CPUArchState *env, uintptr_t mmu_idx,
+ target_ulong addr)
+{
+ return (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+}
+
+/* Find the TLB entry corresponding to the mmu_idx + address pair. */
+static inline CPUTLBEntry *tlb_entry(CPUArchState *env, uintptr_t mmu_idx,
+ target_ulong addr)
+{
+ return &env->tlb_table[mmu_idx][tlb_index(env, mmu_idx, addr)];
+}
+
#ifdef MMU_MODE0_SUFFIX
#define CPU_MMU_INDEX 0
#define MEMSUFFIX MMU_MODE0_SUFFIX
@@ -416,8 +439,7 @@ static inline void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr,
#if defined(CONFIG_USER_ONLY)
return g2h(addr);
#else
- int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
- CPUTLBEntry *tlbentry = &env->tlb_table[mmu_idx][index];
+ CPUTLBEntry *tlbentry = tlb_entry(env, mmu_idx, addr);
abi_ptr tlb_addr;
uintptr_t haddr;
@@ -426,7 +448,7 @@ static inline void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr,
tlb_addr = tlbentry->addr_read;
break;
case 1:
- tlb_addr = tlbentry->addr_write;
+ tlb_addr = tlb_addr_write(tlbentry);
break;
case 2:
tlb_addr = tlbentry->addr_code;
@@ -445,7 +467,7 @@ static inline void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr,
return NULL;
}
- haddr = addr + env->tlb_table[mmu_idx][index].addend;
+ haddr = addr + tlbentry->addend;
return (void *)haddr;
#endif /* defined(CONFIG_USER_ONLY) */
}
diff --git a/include/exec/cpu_ldst_template.h b/include/exec/cpu_ldst_template.h
index 4db2302962..0f061d47ef 100644
--- a/include/exec/cpu_ldst_template.h
+++ b/include/exec/cpu_ldst_template.h
@@ -81,7 +81,7 @@ glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
target_ulong ptr,
uintptr_t retaddr)
{
- int page_index;
+ CPUTLBEntry *entry;
RES_TYPE res;
target_ulong addr;
int mmu_idx;
@@ -94,15 +94,15 @@ glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
#endif
addr = ptr;
- page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
mmu_idx = CPU_MMU_INDEX;
- if (unlikely(env->tlb_table[mmu_idx][page_index].ADDR_READ !=
+ entry = tlb_entry(env, mmu_idx, addr);
+ if (unlikely(entry->ADDR_READ !=
(addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) {
oi = make_memop_idx(SHIFT, mmu_idx);
res = glue(glue(helper_ret_ld, URETSUFFIX), MMUSUFFIX)(env, addr,
oi, retaddr);
} else {
- uintptr_t hostaddr = addr + env->tlb_table[mmu_idx][page_index].addend;
+ uintptr_t hostaddr = addr + entry->addend;
res = glue(glue(ld, USUFFIX), _p)((uint8_t *)hostaddr);
}
return res;
@@ -120,7 +120,8 @@ glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
target_ulong ptr,
uintptr_t retaddr)
{
- int res, page_index;
+ CPUTLBEntry *entry;
+ int res;
target_ulong addr;
int mmu_idx;
TCGMemOpIdx oi;
@@ -132,15 +133,15 @@ glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
#endif
addr = ptr;
- page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
mmu_idx = CPU_MMU_INDEX;
- if (unlikely(env->tlb_table[mmu_idx][page_index].ADDR_READ !=
+ entry = tlb_entry(env, mmu_idx, addr);
+ if (unlikely(entry->ADDR_READ !=
(addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) {
oi = make_memop_idx(SHIFT, mmu_idx);
res = (DATA_STYPE)glue(glue(helper_ret_ld, SRETSUFFIX),
MMUSUFFIX)(env, addr, oi, retaddr);
} else {
- uintptr_t hostaddr = addr + env->tlb_table[mmu_idx][page_index].addend;
+ uintptr_t hostaddr = addr + entry->addend;
res = glue(glue(lds, SUFFIX), _p)((uint8_t *)hostaddr);
}
return res;
@@ -162,7 +163,7 @@ glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
target_ulong ptr,
RES_TYPE v, uintptr_t retaddr)
{
- int page_index;
+ CPUTLBEntry *entry;
target_ulong addr;
int mmu_idx;
TCGMemOpIdx oi;
@@ -174,15 +175,15 @@ glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
#endif
addr = ptr;
- page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
mmu_idx = CPU_MMU_INDEX;
- if (unlikely(env->tlb_table[mmu_idx][page_index].addr_write !=
+ entry = tlb_entry(env, mmu_idx, addr);
+ if (unlikely(tlb_addr_write(entry) !=
(addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) {
oi = make_memop_idx(SHIFT, mmu_idx);
glue(glue(helper_ret_st, SUFFIX), MMUSUFFIX)(env, addr, v, oi,
retaddr);
} else {
- uintptr_t hostaddr = addr + env->tlb_table[mmu_idx][page_index].addend;
+ uintptr_t hostaddr = addr + entry->addend;
glue(glue(st, SUFFIX), _p)((uint8_t *)hostaddr, v);
}
}
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index 5f78125582..815e5b1e83 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -100,6 +100,11 @@ void cpu_address_space_init(CPUState *cpu, int asidx,
#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG)
/* cputlb.c */
/**
+ * tlb_init - initialize a CPU's TLB
+ * @cpu: CPU whose TLB should be initialized
+ */
+void tlb_init(CPUState *cpu);
+/**
* tlb_flush_page:
* @cpu: CPU whose TLB should be flushed
* @addr: virtual address of page to be flushed
@@ -258,6 +263,9 @@ void tlb_set_page(CPUState *cpu, target_ulong vaddr,
void probe_write(CPUArchState *env, target_ulong addr, int size, int mmu_idx,
uintptr_t retaddr);
#else
+static inline void tlb_init(CPUState *cpu)
+{
+}
static inline void tlb_flush_page(CPUState *cpu, target_ulong addr)
{
}
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 3a427aacf1..667466b8f3 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -419,9 +419,9 @@ struct MemoryListener {
bool match_data, uint64_t data, EventNotifier *e);
void (*eventfd_del)(MemoryListener *listener, MemoryRegionSection *section,
bool match_data, uint64_t data, EventNotifier *e);
- void (*coalesced_mmio_add)(MemoryListener *listener, MemoryRegionSection *section,
+ void (*coalesced_io_add)(MemoryListener *listener, MemoryRegionSection *section,
hwaddr addr, hwaddr len);
- void (*coalesced_mmio_del)(MemoryListener *listener, MemoryRegionSection *section,
+ void (*coalesced_io_del)(MemoryListener *listener, MemoryRegionSection *section,
hwaddr addr, hwaddr len);
/* Lower = earlier (during add), later (during del) */
unsigned priority;
diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h
index 3abb639056..9ecd911c3e 100644
--- a/include/exec/ram_addr.h
+++ b/include/exec/ram_addr.h
@@ -27,6 +27,7 @@ struct RAMBlock {
struct rcu_head rcu;
struct MemoryRegion *mr;
uint8_t *host;
+ uint8_t *colo_cache; /* For colo, VM's ram cache */
ram_addr_t offset;
ram_addr_t used_length;
ram_addr_t max_length;
diff --git a/include/hw/hotplug.h b/include/hw/hotplug.h
index 51541d63e1..1a0516a479 100644
--- a/include/hw/hotplug.h
+++ b/include/hw/hotplug.h
@@ -47,8 +47,6 @@ typedef void (*hotplug_fn)(HotplugHandler *plug_handler,
* @parent: Opaque parent interface.
* @pre_plug: pre plug callback called at start of device.realize(true)
* @plug: plug callback called at end of device.realize(true).
- * @post_plug: post plug callback called after device.realize(true) and device
- * reset
* @unplug_request: unplug request callback.
* Used as a means to initiate device unplug for devices that
* require asynchronous unplug handling.
@@ -63,7 +61,6 @@ typedef struct HotplugHandlerClass {
/* <public> */
hotplug_fn pre_plug;
hotplug_fn plug;
- void (*post_plug)(HotplugHandler *plug_handler, DeviceState *plugged_dev);
hotplug_fn unplug_request;
hotplug_fn unplug;
} HotplugHandlerClass;
@@ -87,14 +84,6 @@ void hotplug_handler_pre_plug(HotplugHandler *plug_handler,
Error **errp);
/**
- * hotplug_handler_post_plug:
- *
- * Call #HotplugHandlerClass.post_plug callback of @plug_handler.
- */
-void hotplug_handler_post_plug(HotplugHandler *plug_handler,
- DeviceState *plugged_dev);
-
-/**
* hotplug_handler_unplug_request:
*
* Calls #HotplugHandlerClass.unplug_request callback of @plug_handler.
diff --git a/include/hw/hyperv/hyperv-proto.h b/include/hw/hyperv/hyperv-proto.h
new file mode 100644
index 0000000000..21dc28aee9
--- /dev/null
+++ b/include/hw/hyperv/hyperv-proto.h
@@ -0,0 +1,130 @@
+/*
+ * Definitions for Hyper-V guest/hypervisor interaction
+ *
+ * Copyright (c) 2017-2018 Virtuozzo International GmbH.
+ *
+ * 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 HW_HYPERV_HYPERV_PROTO_H
+#define HW_HYPERV_HYPERV_PROTO_H
+
+#include "qemu/bitmap.h"
+
+/*
+ * Hypercall status code
+ */
+#define HV_STATUS_SUCCESS 0
+#define HV_STATUS_INVALID_HYPERCALL_CODE 2
+#define HV_STATUS_INVALID_HYPERCALL_INPUT 3
+#define HV_STATUS_INVALID_ALIGNMENT 4
+#define HV_STATUS_INVALID_PARAMETER 5
+#define HV_STATUS_INSUFFICIENT_MEMORY 11
+#define HV_STATUS_INVALID_PORT_ID 17
+#define HV_STATUS_INVALID_CONNECTION_ID 18
+#define HV_STATUS_INSUFFICIENT_BUFFERS 19
+
+/*
+ * Hypercall numbers
+ */
+#define HV_POST_MESSAGE 0x005c
+#define HV_SIGNAL_EVENT 0x005d
+#define HV_HYPERCALL_FAST (1u << 16)
+
+/*
+ * Message size
+ */
+#define HV_MESSAGE_PAYLOAD_SIZE 240
+
+/*
+ * Message types
+ */
+#define HV_MESSAGE_NONE 0x00000000
+#define HV_MESSAGE_VMBUS 0x00000001
+#define HV_MESSAGE_UNMAPPED_GPA 0x80000000
+#define HV_MESSAGE_GPA_INTERCEPT 0x80000001
+#define HV_MESSAGE_TIMER_EXPIRED 0x80000010
+#define HV_MESSAGE_INVALID_VP_REGISTER_VALUE 0x80000020
+#define HV_MESSAGE_UNRECOVERABLE_EXCEPTION 0x80000021
+#define HV_MESSAGE_UNSUPPORTED_FEATURE 0x80000022
+#define HV_MESSAGE_EVENTLOG_BUFFERCOMPLETE 0x80000040
+#define HV_MESSAGE_X64_IOPORT_INTERCEPT 0x80010000
+#define HV_MESSAGE_X64_MSR_INTERCEPT 0x80010001
+#define HV_MESSAGE_X64_CPUID_INTERCEPT 0x80010002
+#define HV_MESSAGE_X64_EXCEPTION_INTERCEPT 0x80010003
+#define HV_MESSAGE_X64_APIC_EOI 0x80010004
+#define HV_MESSAGE_X64_LEGACY_FP_ERROR 0x80010005
+
+/*
+ * Message flags
+ */
+#define HV_MESSAGE_FLAG_PENDING 0x1
+
+/*
+ * Number of synthetic interrupts
+ */
+#define HV_SINT_COUNT 16
+
+/*
+ * Event flags number per SINT
+ */
+#define HV_EVENT_FLAGS_COUNT (256 * 8)
+
+/*
+ * Connection id valid bits
+ */
+#define HV_CONNECTION_ID_MASK 0x00ffffff
+
+/*
+ * Input structure for POST_MESSAGE hypercall
+ */
+struct hyperv_post_message_input {
+ uint32_t connection_id;
+ uint32_t _reserved;
+ uint32_t message_type;
+ uint32_t payload_size;
+ uint8_t payload[HV_MESSAGE_PAYLOAD_SIZE];
+};
+
+/*
+ * Input structure for SIGNAL_EVENT hypercall
+ */
+struct hyperv_signal_event_input {
+ uint32_t connection_id;
+ uint16_t flag_number;
+ uint16_t _reserved_zero;
+};
+
+/*
+ * SynIC message structures
+ */
+struct hyperv_message_header {
+ uint32_t message_type;
+ uint8_t payload_size;
+ uint8_t message_flags; /* HV_MESSAGE_FLAG_XX */
+ uint8_t _reserved[2];
+ uint64_t sender;
+};
+
+struct hyperv_message {
+ struct hyperv_message_header header;
+ uint8_t payload[HV_MESSAGE_PAYLOAD_SIZE];
+};
+
+struct hyperv_message_page {
+ struct hyperv_message slot[HV_SINT_COUNT];
+};
+
+/*
+ * SynIC event flags structures
+ */
+struct hyperv_event_flags {
+ DECLARE_BITMAP(flags, HV_EVENT_FLAGS_COUNT);
+};
+
+struct hyperv_event_flags_page {
+ struct hyperv_event_flags slot[HV_SINT_COUNT];
+};
+
+#endif
diff --git a/include/hw/hyperv/hyperv.h b/include/hw/hyperv/hyperv.h
new file mode 100644
index 0000000000..597381cb01
--- /dev/null
+++ b/include/hw/hyperv/hyperv.h
@@ -0,0 +1,83 @@
+/*
+ * Hyper-V guest/hypervisor interaction
+ *
+ * Copyright (c) 2015-2018 Virtuozzo International GmbH.
+ *
+ * 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 HW_HYPERV_HYPERV_H
+#define HW_HYPERV_HYPERV_H
+
+#include "cpu-qom.h"
+#include "hw/hyperv/hyperv-proto.h"
+
+typedef struct HvSintRoute HvSintRoute;
+
+/*
+ * Callback executed in a bottom-half when the status of posting the message
+ * becomes known, before unblocking the connection for further messages
+ */
+typedef void (*HvSintMsgCb)(void *data, int status);
+
+HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint,
+ HvSintMsgCb cb, void *cb_data);
+void hyperv_sint_route_ref(HvSintRoute *sint_route);
+void hyperv_sint_route_unref(HvSintRoute *sint_route);
+
+int hyperv_sint_route_set_sint(HvSintRoute *sint_route);
+
+/*
+ * Submit a message to be posted in vcpu context. If the submission succeeds,
+ * the status of posting the message is reported via the callback associated
+ * with the @sint_route; until then no more messages are accepted.
+ */
+int hyperv_post_msg(HvSintRoute *sint_route, struct hyperv_message *msg);
+/*
+ * Set event flag @eventno, and signal the SINT if the flag has changed.
+ */
+int hyperv_set_event_flag(HvSintRoute *sint_route, unsigned eventno);
+
+/*
+ * Handler for messages arriving from the guest via HV_POST_MESSAGE hypercall.
+ * Executed in vcpu context.
+ */
+typedef uint16_t (*HvMsgHandler)(const struct hyperv_post_message_input *msg,
+ void *data);
+/*
+ * Associate @handler with the message connection @conn_id, such that @handler
+ * is called with @data when the guest executes HV_POST_MESSAGE hypercall on
+ * @conn_id. If @handler is NULL clear the association.
+ */
+int hyperv_set_msg_handler(uint32_t conn_id, HvMsgHandler handler, void *data);
+/*
+ * Associate @notifier with the event connection @conn_id, such that @notifier
+ * is signaled when the guest executes HV_SIGNAL_EVENT hypercall on @conn_id.
+ * If @notifier is NULL clear the association.
+ */
+int hyperv_set_event_flag_handler(uint32_t conn_id, EventNotifier *notifier);
+
+/*
+ * Process HV_POST_MESSAGE hypercall: parse the data in the guest memory as
+ * specified in @param, and call the HvMsgHandler associated with the
+ * connection on the message contained therein.
+ */
+uint16_t hyperv_hcall_post_message(uint64_t param, bool fast);
+/*
+ * Process HV_SIGNAL_EVENT hypercall: signal the EventNotifier associated with
+ * the connection as specified in @param.
+ */
+uint16_t hyperv_hcall_signal_event(uint64_t param, bool fast);
+
+static inline uint32_t hyperv_vp_index(CPUState *cs)
+{
+ return cs->cpu_index;
+}
+
+void hyperv_synic_add(CPUState *cs);
+void hyperv_synic_reset(CPUState *cs);
+void hyperv_synic_update(CPUState *cs, bool enable,
+ hwaddr msg_page_addr, hwaddr event_page_addr);
+
+#endif
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 6894f37df1..dfe6746692 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -294,6 +294,14 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t);
int e820_get_num_entries(void);
bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
+#define PC_COMPAT_3_0 \
+ HW_COMPAT_3_0 \
+ {\
+ .driver = TYPE_X86_CPU,\
+ .property = "x-hv-synic-kvm-only",\
+ .value = "on",\
+ }
+
#define PC_COMPAT_2_12 \
HW_COMPAT_2_12 \
{\
diff --git a/include/migration/colo.h b/include/migration/colo.h
index 2fe48ad353..99ce17aca7 100644
--- a/include/migration/colo.h
+++ b/include/migration/colo.h
@@ -16,14 +16,21 @@
#include "qemu-common.h"
#include "qapi/qapi-types-migration.h"
+enum colo_event {
+ COLO_EVENT_NONE,
+ COLO_EVENT_CHECKPOINT,
+ COLO_EVENT_FAILOVER,
+};
+
void colo_info_init(void);
void migrate_start_colo_process(MigrationState *s);
bool migration_in_colo_state(void);
/* loadvm */
-bool migration_incoming_enable_colo(void);
-void migration_incoming_exit_colo(void);
+void migration_incoming_enable_colo(void);
+void migration_incoming_disable_colo(void);
+bool migration_incoming_colo_enabled(void);
void *colo_process_incoming_thread(void *opaque);
bool migration_incoming_in_colo_state(void);
diff --git a/include/net/filter.h b/include/net/filter.h
index 435acd6f82..49da666ac0 100644
--- a/include/net/filter.h
+++ b/include/net/filter.h
@@ -38,6 +38,8 @@ typedef ssize_t (FilterReceiveIOV)(NetFilterState *nc,
typedef void (FilterStatusChanged) (NetFilterState *nf, Error **errp);
+typedef void (FilterHandleEvent) (NetFilterState *nf, int event, Error **errp);
+
typedef struct NetFilterClass {
ObjectClass parent_class;
@@ -45,6 +47,7 @@ typedef struct NetFilterClass {
FilterSetup *setup;
FilterCleanup *cleanup;
FilterStatusChanged *status_changed;
+ FilterHandleEvent *handle_event;
/* mandatory */
FilterReceiveIOV *receive_iov;
} NetFilterClass;
@@ -77,4 +80,6 @@ ssize_t qemu_netfilter_pass_to_next(NetClientState *sender,
int iovcnt,
void *opaque);
+void colo_notify_filters_event(int event, Error **errp);
+
#endif /* QEMU_NET_FILTER_H */
diff --git a/include/qemu/atomic128.h b/include/qemu/atomic128.h
new file mode 100644
index 0000000000..a6af22ff10
--- /dev/null
+++ b/include/qemu/atomic128.h
@@ -0,0 +1,153 @@
+/*
+ * Simple interface for 128-bit atomic operations.
+ *
+ * Copyright (C) 2018 Linaro, Ltd.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * See docs/devel/atomics.txt for discussion about the guarantees each
+ * atomic primitive is meant to provide.
+ */
+
+#ifndef QEMU_ATOMIC128_H
+#define QEMU_ATOMIC128_H
+
+/*
+ * GCC is a house divided about supporting large atomic operations.
+ *
+ * For hosts that only have large compare-and-swap, a legalistic reading
+ * of the C++ standard means that one cannot implement __atomic_read on
+ * read-only memory, and thus all atomic operations must synchronize
+ * through libatomic.
+ *
+ * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80878
+ *
+ * This interpretation is not especially helpful for QEMU.
+ * For softmmu, all RAM is always read/write from the hypervisor.
+ * For user-only, if the guest doesn't implement such an __atomic_read
+ * then the host need not worry about it either.
+ *
+ * Moreover, using libatomic is not an option, because its interface is
+ * built for std::atomic<T>, and requires that *all* accesses to such an
+ * object go through the library. In our case we do not have an object
+ * in the C/C++ sense, but a view of memory as seen by the guest.
+ * The guest may issue a large atomic operation and then access those
+ * pieces using word-sized accesses. From the hypervisor, we have no
+ * way to connect those two actions.
+ *
+ * Therefore, special case each platform.
+ */
+
+#if defined(CONFIG_ATOMIC128)
+static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new)
+{
+ return atomic_cmpxchg__nocheck(ptr, cmp, new);
+}
+# define HAVE_CMPXCHG128 1
+#elif defined(CONFIG_CMPXCHG128)
+static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new)
+{
+ return __sync_val_compare_and_swap_16(ptr, cmp, new);
+}
+# define HAVE_CMPXCHG128 1
+#elif defined(__aarch64__)
+/* Through gcc 8, aarch64 has no support for 128-bit at all. */
+static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new)
+{
+ uint64_t cmpl = int128_getlo(cmp), cmph = int128_gethi(cmp);
+ uint64_t newl = int128_getlo(new), newh = int128_gethi(new);
+ uint64_t oldl, oldh;
+ uint32_t tmp;
+
+ asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t"
+ "cmp %[oldl], %[cmpl]\n\t"
+ "ccmp %[oldh], %[cmph], #0, eq\n\t"
+ "b.ne 1f\n\t"
+ "stlxp %w[tmp], %[newl], %[newh], %[mem]\n\t"
+ "cbnz %w[tmp], 0b\n"
+ "1:"
+ : [mem] "+m"(*ptr), [tmp] "=&r"(tmp),
+ [oldl] "=&r"(oldl), [oldh] "=r"(oldh)
+ : [cmpl] "r"(cmpl), [cmph] "r"(cmph),
+ [newl] "r"(newl), [newh] "r"(newh)
+ : "memory", "cc");
+
+ return int128_make128(oldl, oldh);
+}
+# define HAVE_CMPXCHG128 1
+#else
+/* Fallback definition that must be optimized away, or error. */
+Int128 QEMU_ERROR("unsupported atomic")
+ atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new);
+# define HAVE_CMPXCHG128 0
+#endif /* Some definition for HAVE_CMPXCHG128 */
+
+
+#if defined(CONFIG_ATOMIC128)
+static inline Int128 atomic16_read(Int128 *ptr)
+{
+ return atomic_read__nocheck(ptr);
+}
+
+static inline void atomic16_set(Int128 *ptr, Int128 val)
+{
+ atomic_set__nocheck(ptr, val);
+}
+
+# define HAVE_ATOMIC128 1
+#elif !defined(CONFIG_USER_ONLY) && defined(__aarch64__)
+/* We can do better than cmpxchg for AArch64. */
+static inline Int128 atomic16_read(Int128 *ptr)
+{
+ uint64_t l, h;
+ uint32_t tmp;
+
+ /* The load must be paired with the store to guarantee not tearing. */
+ asm("0: ldxp %[l], %[h], %[mem]\n\t"
+ "stxp %w[tmp], %[l], %[h], %[mem]\n\t"
+ "cbnz %w[tmp], 0b"
+ : [mem] "+m"(*ptr), [tmp] "=r"(tmp), [l] "=r"(l), [h] "=r"(h));
+
+ return int128_make128(l, h);
+}
+
+static inline void atomic16_set(Int128 *ptr, Int128 val)
+{
+ uint64_t l = int128_getlo(val), h = int128_gethi(val);
+ uint64_t t1, t2;
+
+ /* Load into temporaries to acquire the exclusive access lock. */
+ asm("0: ldxp %[t1], %[t2], %[mem]\n\t"
+ "stxp %w[t1], %[l], %[h], %[mem]\n\t"
+ "cbnz %w[t1], 0b"
+ : [mem] "+m"(*ptr), [t1] "=&r"(t1), [t2] "=&r"(t2)
+ : [l] "r"(l), [h] "r"(h));
+}
+
+# define HAVE_ATOMIC128 1
+#elif !defined(CONFIG_USER_ONLY) && HAVE_CMPXCHG128
+static inline Int128 atomic16_read(Int128 *ptr)
+{
+ /* Maybe replace 0 with 0, returning the old value. */
+ return atomic16_cmpxchg(ptr, 0, 0);
+}
+
+static inline void atomic16_set(Int128 *ptr, Int128 val)
+{
+ Int128 old = *ptr, cmp;
+ do {
+ cmp = old;
+ old = atomic16_cmpxchg(ptr, cmp, val);
+ } while (old != cmp);
+}
+
+# define HAVE_ATOMIC128 1
+#else
+/* Fallback definitions that must be optimized away, or error. */
+Int128 QEMU_ERROR("unsupported atomic") atomic16_read(Int128 *ptr);
+void QEMU_ERROR("unsupported atomic") atomic16_set(Int128 *ptr, Int128 val);
+# define HAVE_ATOMIC128 0
+#endif /* Some definition for HAVE_ATOMIC128 */
+
+#endif /* QEMU_ATOMIC128_H */
diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h
index 66f8c241dc..6b92710487 100644
--- a/include/qemu/compiler.h
+++ b/include/qemu/compiler.h
@@ -146,6 +146,17 @@
# define QEMU_FLATTEN
#endif
+/*
+ * If __attribute__((error)) is present, use it to produce an error at
+ * compile time. Otherwise, one must wait for the linker to diagnose
+ * the missing symbol.
+ */
+#if __has_attribute(error)
+# define QEMU_ERROR(X) __attribute__((error(X)))
+#else
+# define QEMU_ERROR(X)
+#endif
+
/* Implement C11 _Generic via GCC builtins. Example:
*
* QEMU_GENERIC(x, (float, sinf), (long double, sinl), sin) (x)
diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h
index 4f8559e550..3bf48bcdec 100644
--- a/include/qemu/osdep.h
+++ b/include/qemu/osdep.h
@@ -123,6 +123,18 @@ extern int daemon(int, int);
#include "qemu/typedefs.h"
/*
+ * For mingw, as of v6.0.0, the function implementing the assert macro is
+ * not marked as noreturn, so the compiler cannot delete code following an
+ * assert(false) as unused. We rely on this within the code base to delete
+ * code that is unreachable when features are disabled.
+ * All supported versions of Glib's g_assert() satisfy this requirement.
+ */
+#ifdef __MINGW32__
+#undef assert
+#define assert(x) g_assert(x)
+#endif
+
+/*
* According to waitpid man page:
* WCOREDUMP
* This macro is not specified in POSIX.1-2001 and is not
diff --git a/include/qemu/timer.h b/include/qemu/timer.h
index a005ed2692..a86330c987 100644
--- a/include/qemu/timer.h
+++ b/include/qemu/timer.h
@@ -2,6 +2,7 @@
#define QEMU_TIMER_H
#include "qemu-common.h"
+#include "qemu/bitops.h"
#include "qemu/notify.h"
#include "qemu/host-utils.h"
@@ -42,14 +43,6 @@
* In icount mode, this clock counts nanoseconds while the virtual
* machine is running. It is used to increase @QEMU_CLOCK_VIRTUAL
* while the CPUs are sleeping and thus not executing instructions.
- *
- * @QEMU_CLOCK_VIRTUAL_EXT: virtual clock for external subsystems
- *
- * The virtual clock only runs during the emulation. It stops
- * when the virtual machine is stopped. The timers for this clock
- * do not recorded in rr mode, therefore this clock could be used
- * for the subsystems that operate outside the guest core.
- *
*/
typedef enum {
@@ -57,10 +50,27 @@ typedef enum {
QEMU_CLOCK_VIRTUAL = 1,
QEMU_CLOCK_HOST = 2,
QEMU_CLOCK_VIRTUAL_RT = 3,
- QEMU_CLOCK_VIRTUAL_EXT = 4,
QEMU_CLOCK_MAX
} QEMUClockType;
+/**
+ * QEMU Timer attributes:
+ *
+ * An individual timer may be given one or multiple attributes when initialized.
+ * Each attribute corresponds to one bit. Attributes modify the processing
+ * of timers when they fire.
+ *
+ * The following attributes are available:
+ *
+ * QEMU_TIMER_ATTR_EXTERNAL: drives external subsystem
+ *
+ * Timers with this attribute do not recorded in rr mode, therefore it could be
+ * used for the subsystems that operate outside the guest core. Applicable only
+ * with virtual clock type.
+ */
+
+#define QEMU_TIMER_ATTR_EXTERNAL BIT(0)
+
typedef struct QEMUTimerList QEMUTimerList;
struct QEMUTimerListGroup {
@@ -76,6 +86,7 @@ struct QEMUTimer {
QEMUTimerCB *cb;
void *opaque;
QEMUTimer *next;
+ int attributes;
int scale;
};
@@ -427,22 +438,27 @@ int64_t timerlistgroup_deadline_ns(QEMUTimerListGroup *tlg);
*/
/**
- * timer_init_tl:
+ * timer_init_full:
* @ts: the timer to be initialised
- * @timer_list: the timer list to attach the timer to
+ * @timer_list_group: (optional) the timer list group to attach the timer to
+ * @type: the clock type to use
* @scale: the scale value for the timer
+ * @attributes: 0, or one or more OR'ed QEMU_TIMER_ATTR_<id> values
* @cb: the callback to be called when the timer expires
* @opaque: the opaque pointer to be passed to the callback
*
- * Initialise a new timer and associate it with @timer_list.
+ * Initialise a timer with the given scale and attributes,
+ * and associate it with timer list for given clock @type in @timer_list_group
+ * (or default timer list group, if NULL).
* The caller is responsible for allocating the memory.
*
* You need not call an explicit deinit call. Simply make
* sure it is not on a list with timer_del.
*/
-void timer_init_tl(QEMUTimer *ts,
- QEMUTimerList *timer_list, int scale,
- QEMUTimerCB *cb, void *opaque);
+void timer_init_full(QEMUTimer *ts,
+ QEMUTimerListGroup *timer_list_group, QEMUClockType type,
+ int scale, int attributes,
+ QEMUTimerCB *cb, void *opaque);
/**
* timer_init:
@@ -454,14 +470,12 @@ void timer_init_tl(QEMUTimer *ts,
*
* Initialize a timer with the given scale on the default timer list
* associated with the clock.
- *
- * You need not call an explicit deinit call. Simply make
- * sure it is not on a list with timer_del.
+ * See timer_init_full for details.
*/
static inline void timer_init(QEMUTimer *ts, QEMUClockType type, int scale,
QEMUTimerCB *cb, void *opaque)
{
- timer_init_tl(ts, main_loop_tlg.tl[type], scale, cb, opaque);
+ timer_init_full(ts, NULL, type, scale, 0, cb, opaque);
}
/**
@@ -473,9 +487,7 @@ static inline void timer_init(QEMUTimer *ts, QEMUClockType type, int scale,
*
* Initialize a timer with nanosecond scale on the default timer list
* associated with the clock.
- *
- * You need not call an explicit deinit call. Simply make
- * sure it is not on a list with timer_del.
+ * See timer_init_full for details.
*/
static inline void timer_init_ns(QEMUTimer *ts, QEMUClockType type,
QEMUTimerCB *cb, void *opaque)
@@ -492,9 +504,7 @@ static inline void timer_init_ns(QEMUTimer *ts, QEMUClockType type,
*
* Initialize a timer with microsecond scale on the default timer list
* associated with the clock.
- *
- * You need not call an explicit deinit call. Simply make
- * sure it is not on a list with timer_del.
+ * See timer_init_full for details.
*/
static inline void timer_init_us(QEMUTimer *ts, QEMUClockType type,
QEMUTimerCB *cb, void *opaque)
@@ -511,9 +521,7 @@ static inline void timer_init_us(QEMUTimer *ts, QEMUClockType type,
*
* Initialize a timer with millisecond scale on the default timer list
* associated with the clock.
- *
- * You need not call an explicit deinit call. Simply make
- * sure it is not on a list with timer_del.
+ * See timer_init_full for details.
*/
static inline void timer_init_ms(QEMUTimer *ts, QEMUClockType type,
QEMUTimerCB *cb, void *opaque)
@@ -522,27 +530,37 @@ static inline void timer_init_ms(QEMUTimer *ts, QEMUClockType type,
}
/**
- * timer_new_tl:
- * @timer_list: the timer list to attach the timer to
+ * timer_new_full:
+ * @timer_list_group: (optional) the timer list group to attach the timer to
+ * @type: the clock type to use
* @scale: the scale value for the timer
+ * @attributes: 0, or one or more OR'ed QEMU_TIMER_ATTR_<id> values
* @cb: the callback to be called when the timer expires
* @opaque: the opaque pointer to be passed to the callback
*
- * Create a new timer and associate it with @timer_list.
+ * Create a new timer with the given scale and attributes,
+ * and associate it with timer list for given clock @type in @timer_list_group
+ * (or default timer list group, if NULL).
* The memory is allocated by the function.
*
* This is not the preferred interface unless you know you
- * are going to call timer_free. Use timer_init instead.
+ * are going to call timer_free. Use timer_init or timer_init_full instead.
+ *
+ * The default timer list has one special feature: in icount mode,
+ * %QEMU_CLOCK_VIRTUAL timers are run in the vCPU thread. This is
+ * not true of other timer lists, which are typically associated
+ * with an AioContext---each of them runs its timer callbacks in its own
+ * AioContext thread.
*
* Returns: a pointer to the timer
*/
-static inline QEMUTimer *timer_new_tl(QEMUTimerList *timer_list,
- int scale,
- QEMUTimerCB *cb,
- void *opaque)
+static inline QEMUTimer *timer_new_full(QEMUTimerListGroup *timer_list_group,
+ QEMUClockType type,
+ int scale, int attributes,
+ QEMUTimerCB *cb, void *opaque)
{
QEMUTimer *ts = g_malloc0(sizeof(QEMUTimer));
- timer_init_tl(ts, timer_list, scale, cb, opaque);
+ timer_init_full(ts, timer_list_group, type, scale, attributes, cb, opaque);
return ts;
}
@@ -553,21 +571,16 @@ static inline QEMUTimer *timer_new_tl(QEMUTimerList *timer_list,
* @cb: the callback to be called when the timer expires
* @opaque: the opaque pointer to be passed to the callback
*
- * Create a new timer and associate it with the default
- * timer list for the clock type @type.
- *
- * The default timer list has one special feature: in icount mode,
- * %QEMU_CLOCK_VIRTUAL timers are run in the vCPU thread. This is
- * not true of other timer lists, which are typically associated
- * with an AioContext---each of them runs its timer callbacks in its own
- * AioContext thread.
+ * Create a new timer with the given scale,
+ * and associate it with the default timer list for the clock type @type.
+ * See timer_new_full for details.
*
* Returns: a pointer to the timer
*/
static inline QEMUTimer *timer_new(QEMUClockType type, int scale,
QEMUTimerCB *cb, void *opaque)
{
- return timer_new_tl(main_loop_tlg.tl[type], scale, cb, opaque);
+ return timer_new_full(NULL, type, scale, 0, cb, opaque);
}
/**
@@ -578,12 +591,7 @@ static inline QEMUTimer *timer_new(QEMUClockType type, int scale,
*
* Create a new timer with nanosecond scale on the default timer list
* associated with the clock.
- *
- * The default timer list has one special feature: in icount mode,
- * %QEMU_CLOCK_VIRTUAL timers are run in the vCPU thread. This is
- * not true of other timer lists, which are typically associated
- * with an AioContext---each of them runs its timer callbacks in its own
- * AioContext thread.
+ * See timer_new_full for details.
*
* Returns: a pointer to the newly created timer
*/
@@ -599,14 +607,9 @@ static inline QEMUTimer *timer_new_ns(QEMUClockType type, QEMUTimerCB *cb,
* @cb: the callback to call when the timer expires
* @opaque: the opaque pointer to pass to the callback
*
- * The default timer list has one special feature: in icount mode,
- * %QEMU_CLOCK_VIRTUAL timers are run in the vCPU thread. This is
- * not true of other timer lists, which are typically associated
- * with an AioContext---each of them runs its timer callbacks in its own
- * AioContext thread.
- *
* Create a new timer with microsecond scale on the default timer list
* associated with the clock.
+ * See timer_new_full for details.
*
* Returns: a pointer to the newly created timer
*/
@@ -622,14 +625,9 @@ static inline QEMUTimer *timer_new_us(QEMUClockType type, QEMUTimerCB *cb,
* @cb: the callback to call when the timer expires
* @opaque: the opaque pointer to pass to the callback
*
- * The default timer list has one special feature: in icount mode,
- * %QEMU_CLOCK_VIRTUAL timers are run in the vCPU thread. This is
- * not true of other timer lists, which are typically associated
- * with an AioContext---each of them runs its timer callbacks in its own
- * AioContext thread.
- *
* Create a new timer with millisecond scale on the default timer list
* associated with the clock.
+ * See timer_new_full for details.
*
* Returns: a pointer to the newly created timer
*/
@@ -1046,7 +1044,6 @@ static inline int64_t profile_getclock(void)
return get_clock();
}
-extern int64_t tcg_time;
extern int64_t dev_time;
#endif
diff --git a/include/sysemu/hvf.h b/include/sysemu/hvf.h
index 241118845c..aaa51d2c51 100644
--- a/include/sysemu/hvf.h
+++ b/include/sysemu/hvf.h
@@ -17,7 +17,7 @@
#include "exec/memory.h"
#include "sysemu/accel.h"
-extern int hvf_disabled;
+extern bool hvf_allowed;
#ifdef CONFIG_HVF
#include <Hypervisor/hv.h>
#include <Hypervisor/hv_vmx.h>
@@ -26,7 +26,7 @@ extern int hvf_disabled;
#include "hw/hw.h"
uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx,
int reg);
-#define hvf_enabled() !hvf_disabled
+#define hvf_enabled() (hvf_allowed)
#else
#define hvf_enabled() 0
#define hvf_get_supported_cpuid(func, idx, reg) 0
diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h
index 7f7a594eca..3a7c58e423 100644
--- a/include/sysemu/replay.h
+++ b/include/sysemu/replay.h
@@ -100,14 +100,20 @@ bool replay_has_interrupt(void);
/* Processing clocks and other time sources */
/*! Save the specified clock */
-int64_t replay_save_clock(ReplayClockKind kind, int64_t clock);
+int64_t replay_save_clock(ReplayClockKind kind, int64_t clock,
+ int64_t raw_icount);
/*! Read the specified clock from the log or return cached data */
int64_t replay_read_clock(ReplayClockKind kind);
/*! Saves or reads the clock depending on the current replay mode. */
#define REPLAY_CLOCK(clock, value) \
(replay_mode == REPLAY_MODE_PLAY ? replay_read_clock((clock)) \
: replay_mode == REPLAY_MODE_RECORD \
- ? replay_save_clock((clock), (value)) \
+ ? replay_save_clock((clock), (value), cpu_get_icount_raw()) \
+ : (value))
+#define REPLAY_CLOCK_LOCKED(clock, value) \
+ (replay_mode == REPLAY_MODE_PLAY ? replay_read_clock((clock)) \
+ : replay_mode == REPLAY_MODE_RECORD \
+ ? replay_save_clock((clock), (value), cpu_get_icount_raw_locked()) \
: (value))
/* Events */
diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h
index 1b32b56a03..8c876c166e 100644
--- a/linux-headers/asm-powerpc/kvm.h
+++ b/linux-headers/asm-powerpc/kvm.h
@@ -634,6 +634,7 @@ struct kvm_ppc_cpu_char {
#define KVM_REG_PPC_DEC_EXPIRY (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbe)
#define KVM_REG_PPC_ONLINE (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xbf)
+#define KVM_REG_PPC_PTCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc0)
/* Transactional Memory checkpointed state:
* This is all GPRs, all VSX regs and a subset of SPRs
diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h
index fd23d5778e..dabfcf7c39 100644
--- a/linux-headers/asm-x86/kvm.h
+++ b/linux-headers/asm-x86/kvm.h
@@ -288,6 +288,7 @@ struct kvm_reinject_control {
#define KVM_VCPUEVENT_VALID_SIPI_VECTOR 0x00000002
#define KVM_VCPUEVENT_VALID_SHADOW 0x00000004
#define KVM_VCPUEVENT_VALID_SMM 0x00000008
+#define KVM_VCPUEVENT_VALID_PAYLOAD 0x00000010
/* Interrupt shadow states */
#define KVM_X86_SHADOW_INT_MOV_SS 0x01
@@ -299,7 +300,7 @@ struct kvm_vcpu_events {
__u8 injected;
__u8 nr;
__u8 has_error_code;
- __u8 pad;
+ __u8 pending;
__u32 error_code;
} exception;
struct {
@@ -322,7 +323,9 @@ struct kvm_vcpu_events {
__u8 smm_inside_nmi;
__u8 latched_init;
} smi;
- __u32 reserved[9];
+ __u8 reserved[27];
+ __u8 exception_has_payload;
+ __u64 exception_payload;
};
/* for KVM_GET/SET_DEBUGREGS */
@@ -381,6 +384,7 @@ struct kvm_sync_regs {
#define KVM_STATE_NESTED_GUEST_MODE 0x00000001
#define KVM_STATE_NESTED_RUN_PENDING 0x00000002
+#define KVM_STATE_NESTED_EVMCS 0x00000004
#define KVM_STATE_NESTED_SMM_GUEST_MODE 0x00000001
#define KVM_STATE_NESTED_SMM_VMXON 0x00000002
diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index 83ba4eb571..f11a7eb49c 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -420,13 +420,19 @@ struct kvm_run {
struct kvm_coalesced_mmio_zone {
__u64 addr;
__u32 size;
- __u32 pad;
+ union {
+ __u32 pad;
+ __u32 pio;
+ };
};
struct kvm_coalesced_mmio {
__u64 phys_addr;
__u32 len;
- __u32 pad;
+ union {
+ __u32 pad;
+ __u32 pio;
+ };
__u8 data[8];
};
@@ -719,6 +725,7 @@ struct kvm_ppc_one_seg_page_size {
#define KVM_PPC_PAGE_SIZES_REAL 0x00000001
#define KVM_PPC_1T_SEGMENTS 0x00000002
+#define KVM_PPC_NO_HASH 0x00000004
struct kvm_ppc_smmu_info {
__u64 flags;
@@ -953,6 +960,11 @@ struct kvm_ppc_resize_hpt {
#define KVM_CAP_NESTED_STATE 157
#define KVM_CAP_ARM_INJECT_SERROR_ESR 158
#define KVM_CAP_MSR_PLATFORM_INFO 159
+#define KVM_CAP_PPC_NESTED_HV 160
+#define KVM_CAP_HYPERV_SEND_IPI 161
+#define KVM_CAP_COALESCED_PIO 162
+#define KVM_CAP_HYPERV_ENLIGHTENED_VMCS 163
+#define KVM_CAP_EXCEPTION_PAYLOAD 164
#ifdef KVM_CAP_IRQ_ROUTING
diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h
index 586c794639..ae8951625f 100644
--- a/linux-user/ioctls.h
+++ b/linux-user/ioctls.h
@@ -131,6 +131,52 @@
IOCTL(FS_IOC_GETFLAGS, IOC_R, MK_PTR(TYPE_INT))
IOCTL(FS_IOC_SETFLAGS, IOC_W, MK_PTR(TYPE_INT))
+#ifdef CONFIG_USBFS
+ /* USB ioctls */
+ IOCTL(USBDEVFS_CONTROL, IOC_RW,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_ctrltransfer)))
+ IOCTL(USBDEVFS_BULK, IOC_RW,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_bulktransfer)))
+ IOCTL(USBDEVFS_RESETEP, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(USBDEVFS_SETINTERFACE, IOC_W,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_setinterface)))
+ IOCTL(USBDEVFS_SETCONFIGURATION, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(USBDEVFS_GETDRIVER, IOC_R,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_getdriver)))
+ IOCTL_SPECIAL(USBDEVFS_SUBMITURB, IOC_W, do_ioctl_usbdevfs_submiturb,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_urb)))
+ IOCTL_SPECIAL(USBDEVFS_DISCARDURB, IOC_RW, do_ioctl_usbdevfs_discardurb,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_urb)))
+ IOCTL_SPECIAL(USBDEVFS_REAPURB, IOC_R, do_ioctl_usbdevfs_reapurb,
+ MK_PTR(TYPE_PTRVOID))
+ IOCTL_SPECIAL(USBDEVFS_REAPURBNDELAY, IOC_R, do_ioctl_usbdevfs_reapurb,
+ MK_PTR(TYPE_PTRVOID))
+ IOCTL(USBDEVFS_DISCSIGNAL, IOC_W,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_disconnectsignal)))
+ IOCTL(USBDEVFS_CLAIMINTERFACE, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(USBDEVFS_RELEASEINTERFACE, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(USBDEVFS_CONNECTINFO, IOC_R,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_connectinfo)))
+ IOCTL(USBDEVFS_IOCTL, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_usbdevfs_ioctl)))
+ IOCTL(USBDEVFS_HUB_PORTINFO, IOC_R,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_hub_portinfo)))
+ IOCTL(USBDEVFS_RESET, 0, TYPE_NULL)
+ IOCTL(USBDEVFS_CLEAR_HALT, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(USBDEVFS_DISCONNECT, 0, TYPE_NULL)
+ IOCTL(USBDEVFS_CONNECT, 0, TYPE_NULL)
+ IOCTL(USBDEVFS_CLAIM_PORT, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(USBDEVFS_RELEASE_PORT, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(USBDEVFS_GET_CAPABILITIES, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(USBDEVFS_DISCONNECT_CLAIM, IOC_W,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_disconnect_claim)))
+#ifdef USBDEVFS_DROP_PRIVILEGES
+ IOCTL(USBDEVFS_DROP_PRIVILEGES, IOC_W, MK_PTR(TYPE_INT))
+#endif
+#ifdef USBDEVFS_GET_SPEED
+ IOCTL(USBDEVFS_GET_SPEED, 0, TYPE_NULL)
+#endif
+#endif /* CONFIG_USBFS */
+
IOCTL(SIOCATMARK, IOC_R, MK_PTR(TYPE_INT))
IOCTL(SIOCGIFNAME, IOC_RW, MK_PTR(TYPE_INT))
IOCTL(SIOCGIFFLAGS, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index d2cc971143..cf4511b0e4 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -94,6 +94,10 @@
#include <linux/fiemap.h>
#endif
#include <linux/fb.h>
+#if defined(CONFIG_USBFS)
+#include <linux/usbdevice_fs.h>
+#include <linux/usb/ch9.h>
+#endif
#include <linux/vt.h>
#include <linux/dm-ioctl.h>
#include <linux/reboot.h>
@@ -4196,6 +4200,182 @@ static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp,
return ret;
}
+#if defined(CONFIG_USBFS)
+#if HOST_LONG_BITS > 64
+#error USBDEVFS thunks do not support >64 bit hosts yet.
+#endif
+struct live_urb {
+ uint64_t target_urb_adr;
+ uint64_t target_buf_adr;
+ char *target_buf_ptr;
+ struct usbdevfs_urb host_urb;
+};
+
+static GHashTable *usbdevfs_urb_hashtable(void)
+{
+ static GHashTable *urb_hashtable;
+
+ if (!urb_hashtable) {
+ urb_hashtable = g_hash_table_new(g_int64_hash, g_int64_equal);
+ }
+ return urb_hashtable;
+}
+
+static void urb_hashtable_insert(struct live_urb *urb)
+{
+ GHashTable *urb_hashtable = usbdevfs_urb_hashtable();
+ g_hash_table_insert(urb_hashtable, urb, urb);
+}
+
+static struct live_urb *urb_hashtable_lookup(uint64_t target_urb_adr)
+{
+ GHashTable *urb_hashtable = usbdevfs_urb_hashtable();
+ return g_hash_table_lookup(urb_hashtable, &target_urb_adr);
+}
+
+static void urb_hashtable_remove(struct live_urb *urb)
+{
+ GHashTable *urb_hashtable = usbdevfs_urb_hashtable();
+ g_hash_table_remove(urb_hashtable, urb);
+}
+
+static abi_long
+do_ioctl_usbdevfs_reapurb(const IOCTLEntry *ie, uint8_t *buf_temp,
+ int fd, int cmd, abi_long arg)
+{
+ const argtype usbfsurb_arg_type[] = { MK_STRUCT(STRUCT_usbdevfs_urb) };
+ const argtype ptrvoid_arg_type[] = { TYPE_PTRVOID, 0, 0 };
+ struct live_urb *lurb;
+ void *argptr;
+ uint64_t hurb;
+ int target_size;
+ uintptr_t target_urb_adr;
+ abi_long ret;
+
+ target_size = thunk_type_size(usbfsurb_arg_type, THUNK_TARGET);
+
+ memset(buf_temp, 0, sizeof(uint64_t));
+ ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));
+ if (is_error(ret)) {
+ return ret;
+ }
+
+ memcpy(&hurb, buf_temp, sizeof(uint64_t));
+ lurb = (void *)((uintptr_t)hurb - offsetof(struct live_urb, host_urb));
+ if (!lurb->target_urb_adr) {
+ return -TARGET_EFAULT;
+ }
+ urb_hashtable_remove(lurb);
+ unlock_user(lurb->target_buf_ptr, lurb->target_buf_adr,
+ lurb->host_urb.buffer_length);
+ lurb->target_buf_ptr = NULL;
+
+ /* restore the guest buffer pointer */
+ lurb->host_urb.buffer = (void *)(uintptr_t)lurb->target_buf_adr;
+
+ /* update the guest urb struct */
+ argptr = lock_user(VERIFY_WRITE, lurb->target_urb_adr, target_size, 0);
+ if (!argptr) {
+ g_free(lurb);
+ return -TARGET_EFAULT;
+ }
+ thunk_convert(argptr, &lurb->host_urb, usbfsurb_arg_type, THUNK_TARGET);
+ unlock_user(argptr, lurb->target_urb_adr, target_size);
+
+ target_size = thunk_type_size(ptrvoid_arg_type, THUNK_TARGET);
+ /* write back the urb handle */
+ argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);
+ if (!argptr) {
+ g_free(lurb);
+ return -TARGET_EFAULT;
+ }
+
+ /* GHashTable uses 64-bit keys but thunk_convert expects uintptr_t */
+ target_urb_adr = lurb->target_urb_adr;
+ thunk_convert(argptr, &target_urb_adr, ptrvoid_arg_type, THUNK_TARGET);
+ unlock_user(argptr, arg, target_size);
+
+ g_free(lurb);
+ return ret;
+}
+
+static abi_long
+do_ioctl_usbdevfs_discardurb(const IOCTLEntry *ie,
+ uint8_t *buf_temp __attribute__((unused)),
+ int fd, int cmd, abi_long arg)
+{
+ struct live_urb *lurb;
+
+ /* map target address back to host URB with metadata. */
+ lurb = urb_hashtable_lookup(arg);
+ if (!lurb) {
+ return -TARGET_EFAULT;
+ }
+ return get_errno(safe_ioctl(fd, ie->host_cmd, &lurb->host_urb));
+}
+
+static abi_long
+do_ioctl_usbdevfs_submiturb(const IOCTLEntry *ie, uint8_t *buf_temp,
+ int fd, int cmd, abi_long arg)
+{
+ const argtype *arg_type = ie->arg_type;
+ int target_size;
+ abi_long ret;
+ void *argptr;
+ int rw_dir;
+ struct live_urb *lurb;
+
+ /*
+ * each submitted URB needs to map to a unique ID for the
+ * kernel, and that unique ID needs to be a pointer to
+ * host memory. hence, we need to malloc for each URB.
+ * isochronous transfers have a variable length struct.
+ */
+ arg_type++;
+ target_size = thunk_type_size(arg_type, THUNK_TARGET);
+
+ /* construct host copy of urb and metadata */
+ lurb = g_try_malloc0(sizeof(struct live_urb));
+ if (!lurb) {
+ return -TARGET_ENOMEM;
+ }
+
+ argptr = lock_user(VERIFY_READ, arg, target_size, 1);
+ if (!argptr) {
+ g_free(lurb);
+ return -TARGET_EFAULT;
+ }
+ thunk_convert(&lurb->host_urb, argptr, arg_type, THUNK_HOST);
+ unlock_user(argptr, arg, 0);
+
+ lurb->target_urb_adr = arg;
+ lurb->target_buf_adr = (uintptr_t)lurb->host_urb.buffer;
+
+ /* buffer space used depends on endpoint type so lock the entire buffer */
+ /* control type urbs should check the buffer contents for true direction */
+ rw_dir = lurb->host_urb.endpoint & USB_DIR_IN ? VERIFY_WRITE : VERIFY_READ;
+ lurb->target_buf_ptr = lock_user(rw_dir, lurb->target_buf_adr,
+ lurb->host_urb.buffer_length, 1);
+ if (lurb->target_buf_ptr == NULL) {
+ g_free(lurb);
+ return -TARGET_EFAULT;
+ }
+
+ /* update buffer pointer in host copy */
+ lurb->host_urb.buffer = lurb->target_buf_ptr;
+
+ ret = get_errno(safe_ioctl(fd, ie->host_cmd, &lurb->host_urb));
+ if (is_error(ret)) {
+ unlock_user(lurb->target_buf_ptr, lurb->target_buf_adr, 0);
+ g_free(lurb);
+ } else {
+ urb_hashtable_insert(lurb);
+ }
+
+ return ret;
+}
+#endif /* CONFIG_USBFS */
+
static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd,
int cmd, abi_long arg)
{
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index 18d434d6dc..99bbce083c 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -863,6 +863,34 @@ struct target_pollfd {
#define TARGET_FS_IOC_FIEMAP TARGET_IOWR('f',11,struct fiemap)
+/* usb ioctls */
+#define TARGET_USBDEVFS_CONTROL TARGET_IOWRU('U', 0)
+#define TARGET_USBDEVFS_BULK TARGET_IOWRU('U', 2)
+#define TARGET_USBDEVFS_RESETEP TARGET_IORU('U', 3)
+#define TARGET_USBDEVFS_SETINTERFACE TARGET_IORU('U', 4)
+#define TARGET_USBDEVFS_SETCONFIGURATION TARGET_IORU('U', 5)
+#define TARGET_USBDEVFS_GETDRIVER TARGET_IOWU('U', 8)
+#define TARGET_USBDEVFS_SUBMITURB TARGET_IORU('U', 10)
+#define TARGET_USBDEVFS_DISCARDURB TARGET_IO('U', 11)
+#define TARGET_USBDEVFS_REAPURB TARGET_IOWU('U', 12)
+#define TARGET_USBDEVFS_REAPURBNDELAY TARGET_IOWU('U', 13)
+#define TARGET_USBDEVFS_DISCSIGNAL TARGET_IORU('U', 14)
+#define TARGET_USBDEVFS_CLAIMINTERFACE TARGET_IORU('U', 15)
+#define TARGET_USBDEVFS_RELEASEINTERFACE TARGET_IORU('U', 16)
+#define TARGET_USBDEVFS_CONNECTINFO TARGET_IOWU('U', 17)
+#define TARGET_USBDEVFS_IOCTL TARGET_IOWRU('U', 18)
+#define TARGET_USBDEVFS_HUB_PORTINFO TARGET_IORU('U', 19)
+#define TARGET_USBDEVFS_RESET TARGET_IO('U', 20)
+#define TARGET_USBDEVFS_CLEAR_HALT TARGET_IORU('U', 21)
+#define TARGET_USBDEVFS_DISCONNECT TARGET_IO('U', 22)
+#define TARGET_USBDEVFS_CONNECT TARGET_IO('U', 23)
+#define TARGET_USBDEVFS_CLAIM_PORT TARGET_IORU('U', 24)
+#define TARGET_USBDEVFS_RELEASE_PORT TARGET_IORU('U', 25)
+#define TARGET_USBDEVFS_GET_CAPABILITIES TARGET_IORU('U', 26)
+#define TARGET_USBDEVFS_DISCONNECT_CLAIM TARGET_IORU('U', 27)
+#define TARGET_USBDEVFS_DROP_PRIVILEGES TARGET_IOWU('U', 30)
+#define TARGET_USBDEVFS_GET_SPEED TARGET_IO('U', 31)
+
/* cdrom commands */
#define TARGET_CDROMPAUSE 0x5301 /* Pause Audio Operation */
#define TARGET_CDROMRESUME 0x5302 /* Resume paused Audio Operation */
diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h
index 24631b09be..b98a23b0f1 100644
--- a/linux-user/syscall_types.h
+++ b/linux-user/syscall_types.h
@@ -266,3 +266,71 @@ STRUCT(blkpg_ioctl_arg,
TYPE_INT, /* flags */
TYPE_INT, /* datalen */
TYPE_PTRVOID) /* data */
+
+#if defined(CONFIG_USBFS)
+/* usb device ioctls */
+STRUCT(usbdevfs_ctrltransfer,
+ TYPE_CHAR, /* bRequestType */
+ TYPE_CHAR, /* bRequest */
+ TYPE_SHORT, /* wValue */
+ TYPE_SHORT, /* wIndex */
+ TYPE_SHORT, /* wLength */
+ TYPE_INT, /* timeout */
+ TYPE_PTRVOID) /* data */
+
+STRUCT(usbdevfs_bulktransfer,
+ TYPE_INT, /* ep */
+ TYPE_INT, /* len */
+ TYPE_INT, /* timeout */
+ TYPE_PTRVOID) /* data */
+
+STRUCT(usbdevfs_setinterface,
+ TYPE_INT, /* interface */
+ TYPE_INT) /* altsetting */
+
+STRUCT(usbdevfs_disconnectsignal,
+ TYPE_INT, /* signr */
+ TYPE_PTRVOID) /* context */
+
+STRUCT(usbdevfs_getdriver,
+ TYPE_INT, /* interface */
+ MK_ARRAY(TYPE_CHAR, USBDEVFS_MAXDRIVERNAME + 1)) /* driver */
+
+STRUCT(usbdevfs_connectinfo,
+ TYPE_INT, /* devnum */
+ TYPE_CHAR) /* slow */
+
+STRUCT(usbdevfs_iso_packet_desc,
+ TYPE_INT, /* length */
+ TYPE_INT, /* actual_length */
+ TYPE_INT) /* status */
+
+STRUCT(usbdevfs_urb,
+ TYPE_CHAR, /* type */
+ TYPE_CHAR, /* endpoint */
+ TYPE_INT, /* status */
+ TYPE_INT, /* flags */
+ TYPE_PTRVOID, /* buffer */
+ TYPE_INT, /* buffer_length */
+ TYPE_INT, /* actual_length */
+ TYPE_INT, /* start_frame */
+ TYPE_INT, /* union number_of_packets stream_id */
+ TYPE_INT, /* error_count */
+ TYPE_INT, /* signr */
+ TYPE_PTRVOID, /* usercontext */
+ MK_ARRAY(MK_STRUCT(STRUCT_usbdevfs_iso_packet_desc), 0)) /* desc */
+
+STRUCT(usbdevfs_ioctl,
+ TYPE_INT, /* ifno */
+ TYPE_INT, /* ioctl_code */
+ TYPE_PTRVOID) /* data */
+
+STRUCT(usbdevfs_hub_portinfo,
+ TYPE_CHAR, /* nports */
+ MK_ARRAY(TYPE_CHAR, 127)) /* port */
+
+STRUCT(usbdevfs_disconnect_claim,
+ TYPE_INT, /* interface */
+ TYPE_INT, /* flags */
+ MK_ARRAY(TYPE_CHAR, USBDEVFS_MAXDRIVERNAME + 1)) /* driver */
+#endif /* CONFIG_USBFS */
diff --git a/memory.c b/memory.c
index d852f1143d..51204aa079 100644
--- a/memory.c
+++ b/memory.c
@@ -2129,7 +2129,7 @@ static void memory_region_update_coalesced_range_as(MemoryRegion *mr, AddressSpa
.size = fr->addr.size,
};
- MEMORY_LISTENER_CALL(as, coalesced_mmio_del, Reverse, &section,
+ MEMORY_LISTENER_CALL(as, coalesced_io_del, Reverse, &section,
int128_get64(fr->addr.start),
int128_get64(fr->addr.size));
QTAILQ_FOREACH(cmr, &mr->coalesced, link) {
@@ -2140,7 +2140,7 @@ static void memory_region_update_coalesced_range_as(MemoryRegion *mr, AddressSpa
continue;
}
tmp = addrrange_intersection(tmp, fr->addr);
- MEMORY_LISTENER_CALL(as, coalesced_mmio_add, Forward, &section,
+ MEMORY_LISTENER_CALL(as, coalesced_io_add, Forward, &section,
int128_get64(tmp.start),
int128_get64(tmp.size));
}
diff --git a/migration/Makefile.objs b/migration/Makefile.objs
index c83ec47ba8..a4f3bafd86 100644
--- a/migration/Makefile.objs
+++ b/migration/Makefile.objs
@@ -1,6 +1,6 @@
common-obj-y += migration.o socket.o fd.o exec.o
common-obj-y += tls.o channel.o savevm.o
-common-obj-y += colo-comm.o colo.o colo-failover.o
+common-obj-y += colo.o colo-failover.o
common-obj-y += vmstate.o vmstate-types.o page_cache.o
common-obj-y += qemu-file.o global_state.o
common-obj-y += qemu-file-channel.o
diff --git a/migration/colo-comm.c b/migration/colo-comm.c
deleted file mode 100644
index df26e4dfe7..0000000000
--- a/migration/colo-comm.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * COarse-grain LOck-stepping Virtual Machines for Non-stop Service (COLO)
- * (a.k.a. Fault Tolerance or Continuous Replication)
- *
- * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
- * Copyright (c) 2016 FUJITSU LIMITED
- * Copyright (c) 2016 Intel Corporation
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or
- * later. See the COPYING file in the top-level directory.
- *
- */
-
-#include "qemu/osdep.h"
-#include "migration.h"
-#include "migration/colo.h"
-#include "migration/vmstate.h"
-#include "trace.h"
-
-typedef struct {
- bool colo_requested;
-} COLOInfo;
-
-static COLOInfo colo_info;
-
-COLOMode get_colo_mode(void)
-{
- if (migration_in_colo_state()) {
- return COLO_MODE_PRIMARY;
- } else if (migration_incoming_in_colo_state()) {
- return COLO_MODE_SECONDARY;
- } else {
- return COLO_MODE_UNKNOWN;
- }
-}
-
-static int colo_info_pre_save(void *opaque)
-{
- COLOInfo *s = opaque;
-
- s->colo_requested = migrate_colo_enabled();
-
- return 0;
-}
-
-static bool colo_info_need(void *opaque)
-{
- return migrate_colo_enabled();
-}
-
-static const VMStateDescription colo_state = {
- .name = "COLOState",
- .version_id = 1,
- .minimum_version_id = 1,
- .pre_save = colo_info_pre_save,
- .needed = colo_info_need,
- .fields = (VMStateField[]) {
- VMSTATE_BOOL(colo_requested, COLOInfo),
- VMSTATE_END_OF_LIST()
- },
-};
-
-void colo_info_init(void)
-{
- vmstate_register(NULL, 0, &colo_state, &colo_info);
-}
-
-bool migration_incoming_enable_colo(void)
-{
- return colo_info.colo_requested;
-}
-
-void migration_incoming_exit_colo(void)
-{
- colo_info.colo_requested = false;
-}
diff --git a/migration/colo-failover.c b/migration/colo-failover.c
index 0ae0c41221..4854a96c92 100644
--- a/migration/colo-failover.c
+++ b/migration/colo-failover.c
@@ -77,7 +77,7 @@ FailoverStatus failover_get_state(void)
void qmp_x_colo_lost_heartbeat(Error **errp)
{
- if (get_colo_mode() == COLO_MODE_UNKNOWN) {
+ if (get_colo_mode() == COLO_MODE_NONE) {
error_setg(errp, QERR_FEATURE_DISABLED, "colo");
return;
}
diff --git a/migration/colo.c b/migration/colo.c
index 88936f5962..956ac236b7 100644
--- a/migration/colo.c
+++ b/migration/colo.c
@@ -25,8 +25,16 @@
#include "qemu/error-report.h"
#include "migration/failover.h"
#include "replication.h"
+#include "net/colo-compare.h"
+#include "net/colo.h"
+#include "block/block.h"
+#include "qapi/qapi-events-migration.h"
+#include "qapi/qmp/qerror.h"
+#include "sysemu/cpus.h"
+#include "net/filter.h"
static bool vmstate_loading;
+static Notifier packets_compare_notifier;
#define COLO_BUFFER_BASE_SIZE (4 * 1024 * 1024)
@@ -53,6 +61,7 @@ static void secondary_vm_do_failover(void)
{
int old_state;
MigrationIncomingState *mis = migration_incoming_get_current();
+ Error *local_err = NULL;
/* Can not do failover during the process of VM's loading VMstate, Or
* it will break the secondary VM.
@@ -70,6 +79,17 @@ static void secondary_vm_do_failover(void)
migrate_set_state(&mis->state, MIGRATION_STATUS_COLO,
MIGRATION_STATUS_COMPLETED);
+ replication_stop_all(true, &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ }
+
+ /* Notify all filters of all NIC to do checkpoint */
+ colo_notify_filters_event(COLO_EVENT_FAILOVER, &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ }
+
if (!autostart) {
error_report("\"-S\" qemu option will be ignored in secondary side");
/* recover runstate to normal migration finish state */
@@ -107,9 +127,15 @@ static void primary_vm_do_failover(void)
{
MigrationState *s = migrate_get_current();
int old_state;
+ Error *local_err = NULL;
migrate_set_state(&s->state, MIGRATION_STATUS_COLO,
MIGRATION_STATUS_COMPLETED);
+ /*
+ * kick COLO thread which might wait at
+ * qemu_sem_wait(&s->colo_checkpoint_sem).
+ */
+ colo_checkpoint_notify(migrate_get_current());
/*
* Wake up COLO thread which may blocked in recv() or send(),
@@ -130,10 +156,28 @@ static void primary_vm_do_failover(void)
FailoverStatus_str(old_state));
return;
}
+
+ replication_stop_all(true, &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ local_err = NULL;
+ }
+
/* Notify COLO thread that failover work is finished */
qemu_sem_post(&s->colo_exit_sem);
}
+COLOMode get_colo_mode(void)
+{
+ if (migration_in_colo_state()) {
+ return COLO_MODE_PRIMARY;
+ } else if (migration_incoming_in_colo_state()) {
+ return COLO_MODE_SECONDARY;
+ } else {
+ return COLO_MODE_NONE;
+ }
+}
+
void colo_do_failover(MigrationState *s)
{
/* Make sure VM stopped while failover happened. */
@@ -207,6 +251,26 @@ void qmp_xen_colo_do_checkpoint(Error **errp)
#endif
}
+COLOStatus *qmp_query_colo_status(Error **errp)
+{
+ COLOStatus *s = g_new0(COLOStatus, 1);
+
+ s->mode = get_colo_mode();
+
+ switch (failover_get_state()) {
+ case FAILOVER_STATUS_NONE:
+ s->reason = COLO_EXIT_REASON_NONE;
+ break;
+ case FAILOVER_STATUS_REQUIRE:
+ s->reason = COLO_EXIT_REASON_REQUEST;
+ break;
+ default:
+ s->reason = COLO_EXIT_REASON_ERROR;
+ }
+
+ return s;
+}
+
static void colo_send_message(QEMUFile *f, COLOMessage msg,
Error **errp)
{
@@ -343,21 +407,42 @@ static int colo_do_checkpoint_transaction(MigrationState *s,
goto out;
}
+ colo_notify_compares_event(NULL, COLO_EVENT_CHECKPOINT, &local_err);
+ if (local_err) {
+ goto out;
+ }
+
/* Disable block migration */
migrate_set_block_enabled(false, &local_err);
- qemu_savevm_state_header(fb);
- qemu_savevm_state_setup(fb);
qemu_mutex_lock_iothread();
- qemu_savevm_state_complete_precopy(fb, false, false);
- qemu_mutex_unlock_iothread();
-
- qemu_fflush(fb);
+ replication_do_checkpoint_all(&local_err);
+ if (local_err) {
+ qemu_mutex_unlock_iothread();
+ goto out;
+ }
colo_send_message(s->to_dst_file, COLO_MESSAGE_VMSTATE_SEND, &local_err);
if (local_err) {
+ qemu_mutex_unlock_iothread();
+ goto out;
+ }
+ /* Note: device state is saved into buffer */
+ ret = qemu_save_device_state(fb);
+
+ qemu_mutex_unlock_iothread();
+ if (ret < 0) {
goto out;
}
/*
+ * Only save VM's live state, which not including device state.
+ * TODO: We may need a timeout mechanism to prevent COLO process
+ * to be blocked here.
+ */
+ qemu_savevm_live_state(s->to_dst_file);
+
+ qemu_fflush(fb);
+
+ /*
* We need the size of the VMstate data in Secondary side,
* With which we can decide how much data should be read.
*/
@@ -400,6 +485,11 @@ out:
return ret;
}
+static void colo_compare_notify_checkpoint(Notifier *notifier, void *data)
+{
+ colo_checkpoint_notify(data);
+}
+
static void colo_process_checkpoint(MigrationState *s)
{
QIOChannelBuffer *bioc;
@@ -416,6 +506,9 @@ static void colo_process_checkpoint(MigrationState *s)
goto out;
}
+ packets_compare_notifier.notify = colo_compare_notify_checkpoint;
+ colo_compare_register_notifier(&packets_compare_notifier);
+
/*
* Wait for Secondary finish loading VM states and enter COLO
* restore.
@@ -430,6 +523,12 @@ static void colo_process_checkpoint(MigrationState *s)
object_unref(OBJECT(bioc));
qemu_mutex_lock_iothread();
+ replication_start_all(REPLICATION_MODE_PRIMARY, &local_err);
+ if (local_err) {
+ qemu_mutex_unlock_iothread();
+ goto out;
+ }
+
vm_start();
qemu_mutex_unlock_iothread();
trace_colo_vm_state_change("stop", "run");
@@ -445,6 +544,9 @@ static void colo_process_checkpoint(MigrationState *s)
qemu_sem_wait(&s->colo_checkpoint_sem);
+ if (s->state != MIGRATION_STATUS_COLO) {
+ goto out;
+ }
ret = colo_do_checkpoint_transaction(s, bioc, fb);
if (ret < 0) {
goto out;
@@ -461,11 +563,38 @@ out:
qemu_fclose(fb);
}
- timer_del(s->colo_delay_timer);
+ /*
+ * There are only two reasons we can get here, some error happened
+ * or the user triggered failover.
+ */
+ switch (failover_get_state()) {
+ case FAILOVER_STATUS_NONE:
+ qapi_event_send_colo_exit(COLO_MODE_PRIMARY,
+ COLO_EXIT_REASON_ERROR);
+ break;
+ case FAILOVER_STATUS_REQUIRE:
+ qapi_event_send_colo_exit(COLO_MODE_PRIMARY,
+ COLO_EXIT_REASON_REQUEST);
+ break;
+ default:
+ abort();
+ }
/* Hope this not to be too long to wait here */
qemu_sem_wait(&s->colo_exit_sem);
qemu_sem_destroy(&s->colo_exit_sem);
+
+ /*
+ * It is safe to unregister notifier after failover finished.
+ * Besides, colo_delay_timer and colo_checkpoint_sem can't be
+ * released befor unregister notifier, or there will be use-after-free
+ * error.
+ */
+ colo_compare_unregister_notifier(&packets_compare_notifier);
+ timer_del(s->colo_delay_timer);
+ timer_free(s->colo_delay_timer);
+ qemu_sem_destroy(&s->colo_checkpoint_sem);
+
/*
* Must be called after failover BH is completed,
* Or the failover BH may shutdown the wrong fd that
@@ -533,6 +662,7 @@ void *colo_process_incoming_thread(void *opaque)
uint64_t total_size;
uint64_t value;
Error *local_err = NULL;
+ int ret;
rcu_register_thread();
qemu_sem_init(&mis->colo_incoming_sem, 0);
@@ -559,6 +689,16 @@ void *colo_process_incoming_thread(void *opaque)
fb = qemu_fopen_channel_input(QIO_CHANNEL(bioc));
object_unref(OBJECT(bioc));
+ qemu_mutex_lock_iothread();
+ replication_start_all(REPLICATION_MODE_SECONDARY, &local_err);
+ if (local_err) {
+ qemu_mutex_unlock_iothread();
+ goto out;
+ }
+ vm_start();
+ trace_colo_vm_state_change("stop", "run");
+ qemu_mutex_unlock_iothread();
+
colo_send_message(mis->to_src_file, COLO_MESSAGE_CHECKPOINT_READY,
&local_err);
if (local_err) {
@@ -578,6 +718,11 @@ void *colo_process_incoming_thread(void *opaque)
goto out;
}
+ qemu_mutex_lock_iothread();
+ vm_stop_force_state(RUN_STATE_COLO);
+ trace_colo_vm_state_change("run", "stop");
+ qemu_mutex_unlock_iothread();
+
/* FIXME: This is unnecessary for periodic checkpoint mode */
colo_send_message(mis->to_src_file, COLO_MESSAGE_CHECKPOINT_REPLY,
&local_err);
@@ -591,6 +736,16 @@ void *colo_process_incoming_thread(void *opaque)
goto out;
}
+ qemu_mutex_lock_iothread();
+ cpu_synchronize_all_pre_loadvm();
+ ret = qemu_loadvm_state_main(mis->from_src_file, mis);
+ qemu_mutex_unlock_iothread();
+
+ if (ret < 0) {
+ error_report("Load VM's live state (ram) error");
+ goto out;
+ }
+
value = colo_receive_message_value(mis->from_src_file,
COLO_MESSAGE_VMSTATE_SIZE, &local_err);
if (local_err) {
@@ -622,15 +777,37 @@ void *colo_process_incoming_thread(void *opaque)
}
qemu_mutex_lock_iothread();
- qemu_system_reset(SHUTDOWN_CAUSE_NONE);
vmstate_loading = true;
- if (qemu_loadvm_state(fb) < 0) {
- error_report("COLO: loadvm failed");
+ ret = qemu_load_device_state(fb);
+ if (ret < 0) {
+ error_report("COLO: load device state failed");
+ qemu_mutex_unlock_iothread();
+ goto out;
+ }
+
+ replication_get_error_all(&local_err);
+ if (local_err) {
+ qemu_mutex_unlock_iothread();
+ goto out;
+ }
+ /* discard colo disk buffer */
+ replication_do_checkpoint_all(&local_err);
+ if (local_err) {
+ qemu_mutex_unlock_iothread();
+ goto out;
+ }
+
+ /* Notify all filters of all NIC to do checkpoint */
+ colo_notify_filters_event(COLO_EVENT_CHECKPOINT, &local_err);
+
+ if (local_err) {
qemu_mutex_unlock_iothread();
goto out;
}
vmstate_loading = false;
+ vm_start();
+ trace_colo_vm_state_change("stop", "run");
qemu_mutex_unlock_iothread();
if (failover_get_state() == FAILOVER_STATUS_RELAUNCH) {
@@ -654,6 +831,19 @@ out:
error_report_err(local_err);
}
+ switch (failover_get_state()) {
+ case FAILOVER_STATUS_NONE:
+ qapi_event_send_colo_exit(COLO_MODE_SECONDARY,
+ COLO_EXIT_REASON_ERROR);
+ break;
+ case FAILOVER_STATUS_REQUIRE:
+ qapi_event_send_colo_exit(COLO_MODE_SECONDARY,
+ COLO_EXIT_REASON_REQUEST);
+ break;
+ default:
+ abort();
+ }
+
if (fb) {
qemu_fclose(fb);
}
@@ -665,7 +855,7 @@ out:
if (mis->to_src_file) {
qemu_fclose(mis->to_src_file);
}
- migration_incoming_exit_colo();
+ migration_incoming_disable_colo();
rcu_unregister_thread();
return NULL;
diff --git a/migration/migration.c b/migration/migration.c
index f0f299a773..8b36e7f184 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -76,10 +76,8 @@
/* Migration XBZRLE default cache size */
#define DEFAULT_MIGRATE_XBZRLE_CACHE_SIZE (64 * 1024 * 1024)
-/* The delay time (in ms) between two COLO checkpoints
- * Note: Please change this default value to 10000 when we support hybrid mode.
- */
-#define DEFAULT_MIGRATE_X_CHECKPOINT_DELAY 200
+/* The delay time (in ms) between two COLO checkpoints */
+#define DEFAULT_MIGRATE_X_CHECKPOINT_DELAY (200 * 100)
#define DEFAULT_MIGRATE_MULTIFD_CHANNELS 2
#define DEFAULT_MIGRATE_MULTIFD_PAGE_COUNT 16
@@ -298,6 +296,22 @@ int migrate_send_rp_req_pages(MigrationIncomingState *mis, const char *rbname,
return migrate_send_rp_message(mis, msg_type, msglen, bufc);
}
+static bool migration_colo_enabled;
+bool migration_incoming_colo_enabled(void)
+{
+ return migration_colo_enabled;
+}
+
+void migration_incoming_disable_colo(void)
+{
+ migration_colo_enabled = false;
+}
+
+void migration_incoming_enable_colo(void)
+{
+ migration_colo_enabled = true;
+}
+
void qemu_start_incoming_migration(const char *uri, Error **errp)
{
const char *p;
@@ -388,6 +402,7 @@ static void process_incoming_migration_co(void *opaque)
MigrationIncomingState *mis = migration_incoming_get_current();
PostcopyState ps;
int ret;
+ Error *local_err = NULL;
assert(mis->from_src_file);
mis->migration_incoming_co = qemu_coroutine_self();
@@ -419,7 +434,21 @@ static void process_incoming_migration_co(void *opaque)
}
/* we get COLO info, and know if we are in COLO mode */
- if (!ret && migration_incoming_enable_colo()) {
+ if (!ret && migration_incoming_colo_enabled()) {
+ /* Make sure all file formats flush their mutable metadata */
+ bdrv_invalidate_cache_all(&local_err);
+ if (local_err) {
+ migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
+ MIGRATION_STATUS_FAILED);
+ error_report_err(local_err);
+ exit(EXIT_FAILURE);
+ }
+
+ if (colo_init_ram_cache() < 0) {
+ error_report("Init ram cache failed");
+ exit(EXIT_FAILURE);
+ }
+
qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming",
colo_process_incoming_thread, mis, QEMU_THREAD_JOINABLE);
mis->have_colo_incoming_thread = true;
@@ -427,6 +456,8 @@ static void process_incoming_migration_co(void *opaque)
/* Wait checkpoint incoming thread exit before free resource */
qemu_thread_join(&mis->colo_incoming_thread);
+ /* We hold the global iothread lock, so it is safe here */
+ colo_release_ram_cache();
}
if (ret < 0) {
@@ -3017,6 +3048,11 @@ static void *migration_thread(void *opaque)
qemu_savevm_send_postcopy_advise(s->to_dst_file);
}
+ if (migrate_colo_enabled()) {
+ /* Notify migration destination that we enable COLO */
+ qemu_savevm_send_colo_enable(s->to_dst_file);
+ }
+
qemu_savevm_state_setup(s->to_dst_file);
s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start;
diff --git a/migration/ram.c b/migration/ram.c
index bc38d98cc3..7e7deec4d8 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -3447,6 +3447,29 @@ static inline void *host_from_ram_block_offset(RAMBlock *block,
return block->host + offset;
}
+static inline void *colo_cache_from_block_offset(RAMBlock *block,
+ ram_addr_t offset)
+{
+ if (!offset_in_ramblock(block, offset)) {
+ return NULL;
+ }
+ if (!block->colo_cache) {
+ error_report("%s: colo_cache is NULL in block :%s",
+ __func__, block->idstr);
+ return NULL;
+ }
+
+ /*
+ * During colo checkpoint, we need bitmap of these migrated pages.
+ * It help us to decide which pages in ram cache should be flushed
+ * into VM's RAM later.
+ */
+ if (!test_and_set_bit(offset >> TARGET_PAGE_BITS, block->bmap)) {
+ ram_state->migration_dirty_pages++;
+ }
+ return block->colo_cache + offset;
+}
+
/**
* ram_handle_compressed: handle the zero page case
*
@@ -3651,6 +3674,88 @@ static void decompress_data_with_multi_threads(QEMUFile *f,
qemu_mutex_unlock(&decomp_done_lock);
}
+/*
+ * colo cache: this is for secondary VM, we cache the whole
+ * memory of the secondary VM, it is need to hold the global lock
+ * to call this helper.
+ */
+int colo_init_ram_cache(void)
+{
+ RAMBlock *block;
+
+ rcu_read_lock();
+ RAMBLOCK_FOREACH_MIGRATABLE(block) {
+ block->colo_cache = qemu_anon_ram_alloc(block->used_length,
+ NULL,
+ false);
+ if (!block->colo_cache) {
+ error_report("%s: Can't alloc memory for COLO cache of block %s,"
+ "size 0x" RAM_ADDR_FMT, __func__, block->idstr,
+ block->used_length);
+ goto out_locked;
+ }
+ memcpy(block->colo_cache, block->host, block->used_length);
+ }
+ rcu_read_unlock();
+ /*
+ * Record the dirty pages that sent by PVM, we use this dirty bitmap together
+ * with to decide which page in cache should be flushed into SVM's RAM. Here
+ * we use the same name 'ram_bitmap' as for migration.
+ */
+ if (ram_bytes_total()) {
+ RAMBlock *block;
+
+ RAMBLOCK_FOREACH_MIGRATABLE(block) {
+ unsigned long pages = block->max_length >> TARGET_PAGE_BITS;
+
+ block->bmap = bitmap_new(pages);
+ bitmap_set(block->bmap, 0, pages);
+ }
+ }
+ ram_state = g_new0(RAMState, 1);
+ ram_state->migration_dirty_pages = 0;
+ memory_global_dirty_log_start();
+
+ return 0;
+
+out_locked:
+
+ RAMBLOCK_FOREACH_MIGRATABLE(block) {
+ if (block->colo_cache) {
+ qemu_anon_ram_free(block->colo_cache, block->used_length);
+ block->colo_cache = NULL;
+ }
+ }
+
+ rcu_read_unlock();
+ return -errno;
+}
+
+/* It is need to hold the global lock to call this helper */
+void colo_release_ram_cache(void)
+{
+ RAMBlock *block;
+
+ memory_global_dirty_log_stop();
+ RAMBLOCK_FOREACH_MIGRATABLE(block) {
+ g_free(block->bmap);
+ block->bmap = NULL;
+ }
+
+ rcu_read_lock();
+
+ RAMBLOCK_FOREACH_MIGRATABLE(block) {
+ if (block->colo_cache) {
+ qemu_anon_ram_free(block->colo_cache, block->used_length);
+ block->colo_cache = NULL;
+ }
+ }
+
+ rcu_read_unlock();
+ g_free(ram_state);
+ ram_state = NULL;
+}
+
/**
* ram_load_setup: Setup RAM for migration incoming side
*
@@ -3667,6 +3772,7 @@ static int ram_load_setup(QEMUFile *f, void *opaque)
xbzrle_load_setup();
ramblock_recv_map_init();
+
return 0;
}
@@ -3687,6 +3793,7 @@ static int ram_load_cleanup(void *opaque)
g_free(rb->receivedmap);
rb->receivedmap = NULL;
}
+
return 0;
}
@@ -3869,6 +3976,46 @@ static bool postcopy_is_running(void)
return ps >= POSTCOPY_INCOMING_LISTENING && ps < POSTCOPY_INCOMING_END;
}
+/*
+ * Flush content of RAM cache into SVM's memory.
+ * Only flush the pages that be dirtied by PVM or SVM or both.
+ */
+static void colo_flush_ram_cache(void)
+{
+ RAMBlock *block = NULL;
+ void *dst_host;
+ void *src_host;
+ unsigned long offset = 0;
+
+ memory_global_dirty_log_sync();
+ rcu_read_lock();
+ RAMBLOCK_FOREACH_MIGRATABLE(block) {
+ migration_bitmap_sync_range(ram_state, block, 0, block->used_length);
+ }
+ rcu_read_unlock();
+
+ trace_colo_flush_ram_cache_begin(ram_state->migration_dirty_pages);
+ rcu_read_lock();
+ block = QLIST_FIRST_RCU(&ram_list.blocks);
+
+ while (block) {
+ offset = migration_bitmap_find_dirty(ram_state, block, offset);
+
+ if (offset << TARGET_PAGE_BITS >= block->used_length) {
+ offset = 0;
+ block = QLIST_NEXT_RCU(block, next);
+ } else {
+ migration_bitmap_clear_dirty(ram_state, block, offset);
+ dst_host = block->host + (offset << TARGET_PAGE_BITS);
+ src_host = block->colo_cache + (offset << TARGET_PAGE_BITS);
+ memcpy(dst_host, src_host, TARGET_PAGE_SIZE);
+ }
+ }
+
+ rcu_read_unlock();
+ trace_colo_flush_ram_cache_end();
+}
+
static int ram_load(QEMUFile *f, void *opaque, int version_id)
{
int flags = 0, ret = 0, invalid_flags = 0;
@@ -3924,13 +4071,24 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
RAM_SAVE_FLAG_COMPRESS_PAGE | RAM_SAVE_FLAG_XBZRLE)) {
RAMBlock *block = ram_block_from_stream(f, flags);
- host = host_from_ram_block_offset(block, addr);
+ /*
+ * After going into COLO, we should load the Page into colo_cache.
+ */
+ if (migration_incoming_in_colo_state()) {
+ host = colo_cache_from_block_offset(block, addr);
+ } else {
+ host = host_from_ram_block_offset(block, addr);
+ }
if (!host) {
error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
ret = -EINVAL;
break;
}
- ramblock_recv_bitmap_set(block, host);
+
+ if (!migration_incoming_in_colo_state()) {
+ ramblock_recv_bitmap_set(block, host);
+ }
+
trace_ram_load_loop(block->idstr, (uint64_t)addr, flags, host);
}
@@ -4034,6 +4192,10 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
ret |= wait_for_decompress_done();
rcu_read_unlock();
trace_ram_load_complete(ret, seq_iter);
+
+ if (!ret && migration_incoming_in_colo_state()) {
+ colo_flush_ram_cache();
+ }
return ret;
}
diff --git a/migration/ram.h b/migration/ram.h
index a139066846..83ff1bc11a 100644
--- a/migration/ram.h
+++ b/migration/ram.h
@@ -71,4 +71,8 @@ int64_t ramblock_recv_bitmap_send(QEMUFile *file,
const char *block_name);
int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb);
+/* ram cache */
+int colo_init_ram_cache(void);
+void colo_release_ram_cache(void);
+
#endif
diff --git a/migration/savevm.c b/migration/savevm.c
index 5f8eb38676..9992af4db4 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -56,6 +56,7 @@
#include "io/channel-file.h"
#include "sysemu/replay.h"
#include "qjson.h"
+#include "migration/colo.h"
#ifndef ETH_P_RARP
#define ETH_P_RARP 0x8035
@@ -82,6 +83,7 @@ enum qemu_vm_cmd {
were previously sent during
precopy but are dirty. */
MIG_CMD_PACKAGED, /* Send a wrapped stream within this stream */
+ MIG_CMD_ENABLE_COLO, /* Enable COLO */
MIG_CMD_POSTCOPY_RESUME, /* resume postcopy on dest */
MIG_CMD_RECV_BITMAP, /* Request for recved bitmap on dst */
MIG_CMD_MAX
@@ -841,6 +843,12 @@ static void qemu_savevm_command_send(QEMUFile *f,
qemu_fflush(f);
}
+void qemu_savevm_send_colo_enable(QEMUFile *f)
+{
+ trace_savevm_send_colo_enable();
+ qemu_savevm_command_send(f, MIG_CMD_ENABLE_COLO, 0, NULL);
+}
+
void qemu_savevm_send_ping(QEMUFile *f, uint32_t value)
{
uint32_t buf;
@@ -1370,13 +1378,21 @@ done:
return ret;
}
-static int qemu_save_device_state(QEMUFile *f)
+void qemu_savevm_live_state(QEMUFile *f)
{
- SaveStateEntry *se;
+ /* save QEMU_VM_SECTION_END section */
+ qemu_savevm_state_complete_precopy(f, true, false);
+ qemu_put_byte(f, QEMU_VM_EOF);
+}
- qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
- qemu_put_be32(f, QEMU_VM_FILE_VERSION);
+int qemu_save_device_state(QEMUFile *f)
+{
+ SaveStateEntry *se;
+ if (!migration_in_colo_state()) {
+ qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
+ qemu_put_be32(f, QEMU_VM_FILE_VERSION);
+ }
cpu_synchronize_all_states();
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
@@ -1432,8 +1448,6 @@ enum LoadVMExitCodes {
LOADVM_QUIT = 1,
};
-static int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis);
-
/* ------ incoming postcopy messages ------ */
/* 'advise' arrives before any transfers just to tell us that a postcopy
* *might* happen - it might be skipped if precopy transferred everything
@@ -1922,6 +1936,12 @@ static int loadvm_handle_recv_bitmap(MigrationIncomingState *mis,
return 0;
}
+static int loadvm_process_enable_colo(MigrationIncomingState *mis)
+{
+ migration_incoming_enable_colo();
+ return colo_init_ram_cache();
+}
+
/*
* Process an incoming 'QEMU_VM_COMMAND'
* 0 just a normal return
@@ -2001,6 +2021,9 @@ static int loadvm_process_command(QEMUFile *f)
case MIG_CMD_RECV_BITMAP:
return loadvm_handle_recv_bitmap(mis, len);
+
+ case MIG_CMD_ENABLE_COLO:
+ return loadvm_process_enable_colo(mis);
}
return 0;
@@ -2230,7 +2253,7 @@ static bool postcopy_pause_incoming(MigrationIncomingState *mis)
return true;
}
-static int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis)
+int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis)
{
uint8_t section_type;
int ret = 0;
@@ -2401,6 +2424,22 @@ int qemu_loadvm_state(QEMUFile *f)
return ret;
}
+int qemu_load_device_state(QEMUFile *f)
+{
+ MigrationIncomingState *mis = migration_incoming_get_current();
+ int ret;
+
+ /* Load QEMU_VM_SECTION_FULL section */
+ ret = qemu_loadvm_state_main(f, mis);
+ if (ret < 0) {
+ error_report("Failed to load device state: %d", ret);
+ return ret;
+ }
+
+ cpu_synchronize_all_post_init();
+ return 0;
+}
+
int save_snapshot(const char *name, Error **errp)
{
BlockDriverState *bs, *bs1;
diff --git a/migration/savevm.h b/migration/savevm.h
index a5e65b8ae3..51a4b9caa8 100644
--- a/migration/savevm.h
+++ b/migration/savevm.h
@@ -55,8 +55,13 @@ void qemu_savevm_send_postcopy_ram_discard(QEMUFile *f, const char *name,
uint16_t len,
uint64_t *start_list,
uint64_t *length_list);
+void qemu_savevm_send_colo_enable(QEMUFile *f);
+void qemu_savevm_live_state(QEMUFile *f);
+int qemu_save_device_state(QEMUFile *f);
int qemu_loadvm_state(QEMUFile *f);
void qemu_loadvm_state_cleanup(void);
+int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis);
+int qemu_load_device_state(QEMUFile *f);
#endif
diff --git a/migration/trace-events b/migration/trace-events
index 9430f3cbe0..bd2d0cd25a 100644
--- a/migration/trace-events
+++ b/migration/trace-events
@@ -37,6 +37,7 @@ savevm_send_ping(uint32_t val) "0x%x"
savevm_send_postcopy_listen(void) ""
savevm_send_postcopy_run(void) ""
savevm_send_postcopy_resume(void) ""
+savevm_send_colo_enable(void) ""
savevm_send_recv_bitmap(char *name) "%s"
savevm_state_setup(void) ""
savevm_state_resume_prepare(void) ""
@@ -101,6 +102,8 @@ ram_dirty_bitmap_sync_start(void) ""
ram_dirty_bitmap_sync_wait(void) ""
ram_dirty_bitmap_sync_complete(void) ""
ram_state_resume_prepare(uint64_t v) "%" PRId64
+colo_flush_ram_cache_begin(uint64_t dirty_pages) "dirty_pages %" PRIu64
+colo_flush_ram_cache_end(void) ""
# migration/migration.c
await_return_path_close_on_source_close(void) ""
diff --git a/monitor.c b/monitor.c
index b9258a7438..823b5a1099 100644
--- a/monitor.c
+++ b/monitor.c
@@ -83,6 +83,7 @@
#include "sysemu/cpus.h"
#include "sysemu/iothread.h"
#include "qemu/cutils.h"
+#include "tcg/tcg.h"
#if defined(TARGET_S390X)
#include "hw/s390x/storage-keys.h"
@@ -1966,16 +1967,22 @@ static void hmp_info_numa(Monitor *mon, const QDict *qdict)
#ifdef CONFIG_PROFILER
-int64_t tcg_time;
int64_t dev_time;
static void hmp_info_profile(Monitor *mon, const QDict *qdict)
{
+ static int64_t last_cpu_exec_time;
+ int64_t cpu_exec_time;
+ int64_t delta;
+
+ cpu_exec_time = tcg_cpu_exec_time();
+ delta = cpu_exec_time - last_cpu_exec_time;
+
monitor_printf(mon, "async time %" PRId64 " (%0.3f)\n",
dev_time, dev_time / (double)NANOSECONDS_PER_SECOND);
monitor_printf(mon, "qemu time %" PRId64 " (%0.3f)\n",
- tcg_time, tcg_time / (double)NANOSECONDS_PER_SECOND);
- tcg_time = 0;
+ delta, delta / (double)NANOSECONDS_PER_SECOND);
+ last_cpu_exec_time = cpu_exec_time;
dev_time = 0;
}
#else
diff --git a/net/colo-compare.c b/net/colo-compare.c
index dd745a491b..a39191d522 100644
--- a/net/colo-compare.c
+++ b/net/colo-compare.c
@@ -27,11 +27,20 @@
#include "qemu/sockets.h"
#include "colo.h"
#include "sysemu/iothread.h"
+#include "net/colo-compare.h"
+#include "migration/colo.h"
+#include "migration/migration.h"
#define TYPE_COLO_COMPARE "colo-compare"
#define COLO_COMPARE(obj) \
OBJECT_CHECK(CompareState, (obj), TYPE_COLO_COMPARE)
+static QTAILQ_HEAD(, CompareState) net_compares =
+ QTAILQ_HEAD_INITIALIZER(net_compares);
+
+static NotifierList colo_compare_notifiers =
+ NOTIFIER_LIST_INITIALIZER(colo_compare_notifiers);
+
#define COMPARE_READ_LEN_MAX NET_BUFSIZE
#define MAX_QUEUE_SIZE 1024
@@ -41,6 +50,10 @@
/* TODO: Should be configurable */
#define REGULAR_PACKET_CHECK_MS 3000
+static QemuMutex event_mtx;
+static QemuCond event_complete_cond;
+static int event_unhandled_count;
+
/*
* + CompareState ++
* | |
@@ -87,6 +100,11 @@ typedef struct CompareState {
IOThread *iothread;
GMainContext *worker_context;
QEMUTimer *packet_check_timer;
+
+ QEMUBH *event_bh;
+ enum colo_event event;
+
+ QTAILQ_ENTRY(CompareState) next;
} CompareState;
typedef struct CompareClass {
@@ -98,6 +116,12 @@ enum {
SECONDARY_IN,
};
+static void colo_compare_inconsistency_notify(void)
+{
+ notifier_list_notify(&colo_compare_notifiers,
+ migrate_get_current());
+}
+
static int compare_chr_send(CompareState *s,
const uint8_t *buf,
uint32_t size,
@@ -413,10 +437,7 @@ sec:
qemu_hexdump((char *)spkt->data, stderr,
"colo-compare spkt", spkt->size);
- /*
- * colo_compare_inconsistent_notify();
- * TODO: notice to checkpoint();
- */
+ colo_compare_inconsistency_notify();
}
}
@@ -547,8 +568,18 @@ static int colo_old_packet_check_one(Packet *pkt, int64_t *check_time)
}
}
+void colo_compare_register_notifier(Notifier *notify)
+{
+ notifier_list_add(&colo_compare_notifiers, notify);
+}
+
+void colo_compare_unregister_notifier(Notifier *notify)
+{
+ notifier_remove(notify);
+}
+
static int colo_old_packet_check_one_conn(Connection *conn,
- void *user_data)
+ void *user_data)
{
GList *result = NULL;
int64_t check_time = REGULAR_PACKET_CHECK_MS;
@@ -559,10 +590,7 @@ static int colo_old_packet_check_one_conn(Connection *conn,
if (result) {
/* Do checkpoint will flush old packet */
- /*
- * TODO: Notify colo frame to do checkpoint.
- * colo_compare_inconsistent_notify();
- */
+ colo_compare_inconsistency_notify();
return 0;
}
@@ -606,11 +634,12 @@ static void colo_compare_packet(CompareState *s, Connection *conn,
/*
* If one packet arrive late, the secondary_list or
* primary_list will be empty, so we can't compare it
- * until next comparison.
+ * until next comparison. If the packets in the list are
+ * timeout, it will trigger a checkpoint request.
*/
trace_colo_compare_main("packet different");
g_queue_push_head(&conn->primary_list, pkt);
- /* TODO: colo_notify_checkpoint();*/
+ colo_compare_inconsistency_notify();
break;
}
}
@@ -736,6 +765,25 @@ static void check_old_packet_regular(void *opaque)
REGULAR_PACKET_CHECK_MS);
}
+/* Public API, Used for COLO frame to notify compare event */
+void colo_notify_compares_event(void *opaque, int event, Error **errp)
+{
+ CompareState *s;
+
+ qemu_mutex_lock(&event_mtx);
+ QTAILQ_FOREACH(s, &net_compares, next) {
+ s->event = event;
+ qemu_bh_schedule(s->event_bh);
+ event_unhandled_count++;
+ }
+ /* Wait all compare threads to finish handling this event */
+ while (event_unhandled_count > 0) {
+ qemu_cond_wait(&event_complete_cond, &event_mtx);
+ }
+
+ qemu_mutex_unlock(&event_mtx);
+}
+
static void colo_compare_timer_init(CompareState *s)
{
AioContext *ctx = iothread_get_aio_context(s->iothread);
@@ -756,6 +804,30 @@ static void colo_compare_timer_del(CompareState *s)
}
}
+static void colo_flush_packets(void *opaque, void *user_data);
+
+static void colo_compare_handle_event(void *opaque)
+{
+ CompareState *s = opaque;
+
+ switch (s->event) {
+ case COLO_EVENT_CHECKPOINT:
+ g_queue_foreach(&s->conn_list, colo_flush_packets, s);
+ break;
+ case COLO_EVENT_FAILOVER:
+ break;
+ default:
+ break;
+ }
+
+ assert(event_unhandled_count > 0);
+
+ qemu_mutex_lock(&event_mtx);
+ event_unhandled_count--;
+ qemu_cond_broadcast(&event_complete_cond);
+ qemu_mutex_unlock(&event_mtx);
+}
+
static void colo_compare_iothread(CompareState *s)
{
object_ref(OBJECT(s->iothread));
@@ -769,6 +841,7 @@ static void colo_compare_iothread(CompareState *s)
s, s->worker_context, true);
colo_compare_timer_init(s);
+ s->event_bh = qemu_bh_new(colo_compare_handle_event, s);
}
static char *compare_get_pri_indev(Object *obj, Error **errp)
@@ -926,8 +999,13 @@ static void colo_compare_complete(UserCreatable *uc, Error **errp)
net_socket_rs_init(&s->pri_rs, compare_pri_rs_finalize, s->vnet_hdr);
net_socket_rs_init(&s->sec_rs, compare_sec_rs_finalize, s->vnet_hdr);
+ QTAILQ_INSERT_TAIL(&net_compares, s, next);
+
g_queue_init(&s->conn_list);
+ qemu_mutex_init(&event_mtx);
+ qemu_cond_init(&event_complete_cond);
+
s->connection_track_table = g_hash_table_new_full(connection_key_hash,
connection_key_equal,
g_free,
@@ -990,6 +1068,7 @@ static void colo_compare_init(Object *obj)
static void colo_compare_finalize(Object *obj)
{
CompareState *s = COLO_COMPARE(obj);
+ CompareState *tmp = NULL;
qemu_chr_fe_deinit(&s->chr_pri_in, false);
qemu_chr_fe_deinit(&s->chr_sec_in, false);
@@ -997,6 +1076,16 @@ static void colo_compare_finalize(Object *obj)
if (s->iothread) {
colo_compare_timer_del(s);
}
+
+ qemu_bh_delete(s->event_bh);
+
+ QTAILQ_FOREACH(tmp, &net_compares, next) {
+ if (tmp == s) {
+ QTAILQ_REMOVE(&net_compares, s, next);
+ break;
+ }
+ }
+
/* Release all unhandled packets after compare thead exited */
g_queue_foreach(&s->conn_list, colo_flush_packets, s);
@@ -1009,6 +1098,10 @@ static void colo_compare_finalize(Object *obj)
if (s->iothread) {
object_unref(OBJECT(s->iothread));
}
+
+ qemu_mutex_destroy(&event_mtx);
+ qemu_cond_destroy(&event_complete_cond);
+
g_free(s->pri_indev);
g_free(s->sec_indev);
g_free(s->outdev);
diff --git a/net/colo-compare.h b/net/colo-compare.h
new file mode 100644
index 0000000000..22ddd512e2
--- /dev/null
+++ b/net/colo-compare.h
@@ -0,0 +1,24 @@
+/*
+ * COarse-grain LOck-stepping Virtual Machines for Non-stop Service (COLO)
+ * (a.k.a. Fault Tolerance or Continuous Replication)
+ *
+ * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD.
+ * Copyright (c) 2017 FUJITSU LIMITED
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * Authors:
+ * zhanghailiang <zhang.zhanghailiang@huawei.com>
+ * Zhang Chen <zhangckid@gmail.com>
+ *
+ * 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 QEMU_COLO_COMPARE_H
+#define QEMU_COLO_COMPARE_H
+
+void colo_notify_compares_event(void *opaque, int event, Error **errp);
+void colo_compare_register_notifier(Notifier *notify);
+void colo_compare_unregister_notifier(Notifier *notify);
+
+#endif /* QEMU_COLO_COMPARE_H */
diff --git a/net/colo.c b/net/colo.c
index 6dda4ed66e..49176bf07b 100644
--- a/net/colo.c
+++ b/net/colo.c
@@ -137,7 +137,7 @@ Connection *connection_new(ConnectionKey *key)
conn->ip_proto = key->ip_proto;
conn->processing = false;
conn->offset = 0;
- conn->syn_flag = 0;
+ conn->tcp_state = TCPS_CLOSED;
conn->pack = 0;
conn->sack = 0;
g_queue_init(&conn->primary_list);
@@ -221,3 +221,11 @@ Connection *connection_get(GHashTable *connection_track_table,
return conn;
}
+
+bool connection_has_tracked(GHashTable *connection_track_table,
+ ConnectionKey *key)
+{
+ Connection *conn = g_hash_table_lookup(connection_track_table, key);
+
+ return conn ? true : false;
+}
diff --git a/net/colo.h b/net/colo.h
index da6c36dcf7..11c5226488 100644
--- a/net/colo.h
+++ b/net/colo.h
@@ -18,6 +18,7 @@
#include "slirp/slirp.h"
#include "qemu/jhash.h"
#include "qemu/timer.h"
+#include "slirp/tcp.h"
#define HASHTABLE_MAX_SIZE 16384
@@ -81,11 +82,9 @@ typedef struct Connection {
uint32_t sack;
/* offset = secondary_seq - primary_seq */
tcp_seq offset;
- /*
- * we use this flag update offset func
- * run once in independent tcp connection
- */
- int syn_flag;
+
+ int tcp_state; /* TCP FSM state */
+ tcp_seq fin_ack_seq; /* the seq of 'fin=1,ack=1' */
} Connection;
uint32_t connection_key_hash(const void *opaque);
@@ -99,6 +98,8 @@ void connection_destroy(void *opaque);
Connection *connection_get(GHashTable *connection_track_table,
ConnectionKey *key,
GQueue *conn_list);
+bool connection_has_tracked(GHashTable *connection_track_table,
+ ConnectionKey *key);
void connection_hashtable_reset(GHashTable *connection_track_table);
Packet *packet_new(const void *data, int size, int vnet_hdr_len);
void packet_destroy(void *opaque, void *user_data);
diff --git a/net/filter-rewriter.c b/net/filter-rewriter.c
index f584e4eba4..bb8f4d93b1 100644
--- a/net/filter-rewriter.c
+++ b/net/filter-rewriter.c
@@ -20,11 +20,15 @@
#include "qemu/main-loop.h"
#include "qemu/iov.h"
#include "net/checksum.h"
+#include "net/colo.h"
+#include "migration/colo.h"
#define FILTER_COLO_REWRITER(obj) \
OBJECT_CHECK(RewriterState, (obj), TYPE_FILTER_REWRITER)
#define TYPE_FILTER_REWRITER "filter-rewriter"
+#define FAILOVER_MODE_ON true
+#define FAILOVER_MODE_OFF false
typedef struct RewriterState {
NetFilterState parent_obj;
@@ -32,8 +36,14 @@ typedef struct RewriterState {
/* hashtable to save connection */
GHashTable *connection_track_table;
bool vnet_hdr;
+ bool failover_mode;
} RewriterState;
+static void filter_rewriter_failover_mode(RewriterState *s)
+{
+ s->failover_mode = FAILOVER_MODE_ON;
+}
+
static void filter_rewriter_flush(NetFilterState *nf)
{
RewriterState *s = FILTER_COLO_REWRITER(nf);
@@ -59,9 +69,9 @@ static int is_tcp_packet(Packet *pkt)
}
/* handle tcp packet from primary guest */
-static int handle_primary_tcp_pkt(NetFilterState *nf,
+static int handle_primary_tcp_pkt(RewriterState *rf,
Connection *conn,
- Packet *pkt)
+ Packet *pkt, ConnectionKey *key)
{
struct tcphdr *tcp_pkt;
@@ -74,23 +84,28 @@ static int handle_primary_tcp_pkt(NetFilterState *nf,
trace_colo_filter_rewriter_conn_offset(conn->offset);
}
+ if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == (TH_ACK | TH_SYN)) &&
+ conn->tcp_state == TCPS_SYN_SENT) {
+ conn->tcp_state = TCPS_ESTABLISHED;
+ }
+
if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_SYN)) {
/*
* we use this flag update offset func
* run once in independent tcp connection
*/
- conn->syn_flag = 1;
+ conn->tcp_state = TCPS_SYN_RECEIVED;
}
if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_ACK)) {
- if (conn->syn_flag) {
+ if (conn->tcp_state == TCPS_SYN_RECEIVED) {
/*
* offset = secondary_seq - primary seq
* ack packet sent by guest from primary node,
* so we use th_ack - 1 get primary_seq
*/
conn->offset -= (ntohl(tcp_pkt->th_ack) - 1);
- conn->syn_flag = 0;
+ conn->tcp_state = TCPS_ESTABLISHED;
}
if (conn->offset) {
/* handle packets to the secondary from the primary */
@@ -99,15 +114,66 @@ static int handle_primary_tcp_pkt(NetFilterState *nf,
net_checksum_calculate((uint8_t *)pkt->data + pkt->vnet_hdr_len,
pkt->size - pkt->vnet_hdr_len);
}
+
+ /*
+ * Passive close step 3
+ */
+ if ((conn->tcp_state == TCPS_LAST_ACK) &&
+ (ntohl(tcp_pkt->th_ack) == (conn->fin_ack_seq + 1))) {
+ conn->tcp_state = TCPS_CLOSED;
+ g_hash_table_remove(rf->connection_track_table, key);
+ }
+ }
+
+ if ((tcp_pkt->th_flags & TH_FIN) == TH_FIN) {
+ /*
+ * Passive close.
+ * Step 1:
+ * The *server* side of this connect is VM, *client* tries to close
+ * the connection. We will into CLOSE_WAIT status.
+ *
+ * Step 2:
+ * In this step we will into LAST_ACK status.
+ *
+ * We got 'fin=1, ack=1' packet from server side, we need to
+ * record the seq of 'fin=1, ack=1' packet.
+ *
+ * Step 3:
+ * We got 'ack=1' packets from client side, it acks 'fin=1, ack=1'
+ * packet from server side. From this point, we can ensure that there
+ * will be no packets in the connection, except that, some errors
+ * happen between the path of 'filter object' and vNIC, if this rare
+ * case really happen, we can still create a new connection,
+ * So it is safe to remove the connection from connection_track_table.
+ *
+ */
+ if (conn->tcp_state == TCPS_ESTABLISHED) {
+ conn->tcp_state = TCPS_CLOSE_WAIT;
+ }
+
+ /*
+ * Active close step 2.
+ */
+ if (conn->tcp_state == TCPS_FIN_WAIT_1) {
+ conn->tcp_state = TCPS_TIME_WAIT;
+ /*
+ * For simplify implementation, we needn't wait 2MSL time
+ * in filter rewriter. Because guest kernel will track the
+ * TCP status and wait 2MSL time, if client resend the FIN
+ * packet, guest will apply the last ACK too.
+ */
+ conn->tcp_state = TCPS_CLOSED;
+ g_hash_table_remove(rf->connection_track_table, key);
+ }
}
return 0;
}
/* handle tcp packet from secondary guest */
-static int handle_secondary_tcp_pkt(NetFilterState *nf,
+static int handle_secondary_tcp_pkt(RewriterState *rf,
Connection *conn,
- Packet *pkt)
+ Packet *pkt, ConnectionKey *key)
{
struct tcphdr *tcp_pkt;
@@ -121,7 +187,8 @@ static int handle_secondary_tcp_pkt(NetFilterState *nf,
trace_colo_filter_rewriter_conn_offset(conn->offset);
}
- if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == (TH_ACK | TH_SYN))) {
+ if (conn->tcp_state == TCPS_SYN_RECEIVED &&
+ ((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == (TH_ACK | TH_SYN))) {
/*
* save offset = secondary_seq and then
* in handle_primary_tcp_pkt make offset
@@ -130,6 +197,12 @@ static int handle_secondary_tcp_pkt(NetFilterState *nf,
conn->offset = ntohl(tcp_pkt->th_seq);
}
+ /* VM active connect */
+ if (conn->tcp_state == TCPS_CLOSED &&
+ ((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_SYN)) {
+ conn->tcp_state = TCPS_SYN_SENT;
+ }
+
if ((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_ACK) {
/* Only need to adjust seq while offset is Non-zero */
if (conn->offset) {
@@ -141,6 +214,32 @@ static int handle_secondary_tcp_pkt(NetFilterState *nf,
}
}
+ /*
+ * Passive close step 2:
+ */
+ if (conn->tcp_state == TCPS_CLOSE_WAIT &&
+ (tcp_pkt->th_flags & (TH_ACK | TH_FIN)) == (TH_ACK | TH_FIN)) {
+ conn->fin_ack_seq = ntohl(tcp_pkt->th_seq);
+ conn->tcp_state = TCPS_LAST_ACK;
+ }
+
+ /*
+ * Active close
+ *
+ * Step 1:
+ * The *server* side of this connect is VM, *server* tries to close
+ * the connection.
+ *
+ * Step 2:
+ * We will into CLOSE_WAIT status.
+ * We simplify the TCPS_FIN_WAIT_2, TCPS_TIME_WAIT and
+ * CLOSING status.
+ */
+ if (conn->tcp_state == TCPS_ESTABLISHED &&
+ (tcp_pkt->th_flags & (TH_ACK | TH_FIN)) == TH_FIN) {
+ conn->tcp_state = TCPS_FIN_WAIT_1;
+ }
+
return 0;
}
@@ -184,13 +283,20 @@ static ssize_t colo_rewriter_receive_iov(NetFilterState *nf,
*/
reverse_connection_key(&key);
}
+
+ /* After failover we needn't change new TCP packet */
+ if (s->failover_mode &&
+ !connection_has_tracked(s->connection_track_table, &key)) {
+ goto out;
+ }
+
conn = connection_get(s->connection_track_table,
&key,
NULL);
if (sender == nf->netdev) {
/* NET_FILTER_DIRECTION_TX */
- if (!handle_primary_tcp_pkt(nf, conn, pkt)) {
+ if (!handle_primary_tcp_pkt(s, conn, pkt, &key)) {
qemu_net_queue_send(s->incoming_queue, sender, 0,
(const uint8_t *)pkt->data, pkt->size, NULL);
packet_destroy(pkt, NULL);
@@ -203,7 +309,7 @@ static ssize_t colo_rewriter_receive_iov(NetFilterState *nf,
}
} else {
/* NET_FILTER_DIRECTION_RX */
- if (!handle_secondary_tcp_pkt(nf, conn, pkt)) {
+ if (!handle_secondary_tcp_pkt(s, conn, pkt, &key)) {
qemu_net_queue_send(s->incoming_queue, sender, 0,
(const uint8_t *)pkt->data, pkt->size, NULL);
packet_destroy(pkt, NULL);
@@ -217,11 +323,49 @@ static ssize_t colo_rewriter_receive_iov(NetFilterState *nf,
}
}
+out:
packet_destroy(pkt, NULL);
pkt = NULL;
return 0;
}
+static void reset_seq_offset(gpointer key, gpointer value, gpointer user_data)
+{
+ Connection *conn = (Connection *)value;
+
+ conn->offset = 0;
+}
+
+static gboolean offset_is_nonzero(gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ Connection *conn = (Connection *)value;
+
+ return conn->offset ? true : false;
+}
+
+static void colo_rewriter_handle_event(NetFilterState *nf, int event,
+ Error **errp)
+{
+ RewriterState *rs = FILTER_COLO_REWRITER(nf);
+
+ switch (event) {
+ case COLO_EVENT_CHECKPOINT:
+ g_hash_table_foreach(rs->connection_track_table,
+ reset_seq_offset, NULL);
+ break;
+ case COLO_EVENT_FAILOVER:
+ if (!g_hash_table_find(rs->connection_track_table,
+ offset_is_nonzero, NULL)) {
+ filter_rewriter_failover_mode(rs);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
static void colo_rewriter_cleanup(NetFilterState *nf)
{
RewriterState *s = FILTER_COLO_REWRITER(nf);
@@ -265,6 +409,7 @@ static void filter_rewriter_init(Object *obj)
RewriterState *s = FILTER_COLO_REWRITER(obj);
s->vnet_hdr = false;
+ s->failover_mode = FAILOVER_MODE_OFF;
object_property_add_bool(obj, "vnet_hdr_support",
filter_rewriter_get_vnet_hdr,
filter_rewriter_set_vnet_hdr, NULL);
@@ -277,6 +422,7 @@ static void colo_rewriter_class_init(ObjectClass *oc, void *data)
nfc->setup = colo_rewriter_setup;
nfc->cleanup = colo_rewriter_cleanup;
nfc->receive_iov = colo_rewriter_receive_iov;
+ nfc->handle_event = colo_rewriter_handle_event;
}
static const TypeInfo colo_rewriter_info = {
diff --git a/net/filter.c b/net/filter.c
index 2fd7d7d663..c9f9e5fa08 100644
--- a/net/filter.c
+++ b/net/filter.c
@@ -17,6 +17,8 @@
#include "net/vhost_net.h"
#include "qom/object_interfaces.h"
#include "qemu/iov.h"
+#include "net/colo.h"
+#include "migration/colo.h"
static inline bool qemu_can_skip_netfilter(NetFilterState *nf)
{
@@ -245,11 +247,26 @@ static void netfilter_finalize(Object *obj)
g_free(nf->netdev_id);
}
+static void default_handle_event(NetFilterState *nf, int event, Error **errp)
+{
+ switch (event) {
+ case COLO_EVENT_CHECKPOINT:
+ break;
+ case COLO_EVENT_FAILOVER:
+ object_property_set_str(OBJECT(nf), "off", "status", errp);
+ break;
+ default:
+ break;
+ }
+}
+
static void netfilter_class_init(ObjectClass *oc, void *data)
{
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+ NetFilterClass *nfc = NETFILTER_CLASS(oc);
ucc->complete = netfilter_complete;
+ nfc->handle_event = default_handle_event;
}
static const TypeInfo netfilter_info = {
diff --git a/net/net.c b/net/net.c
index cdcd5cf634..07c194a8f6 100644
--- a/net/net.c
+++ b/net/net.c
@@ -712,10 +712,15 @@ ssize_t qemu_deliver_packet_iov(NetClientState *sender,
void *opaque)
{
NetClientState *nc = opaque;
+ size_t size = iov_size(iov, iovcnt);
int ret;
+ if (size > INT_MAX) {
+ return size;
+ }
+
if (nc->link_down) {
- return iov_size(iov, iovcnt);
+ return size;
}
if (nc->receive_disabled) {
@@ -1335,6 +1340,25 @@ void hmp_info_network(Monitor *mon, const QDict *qdict)
}
}
+void colo_notify_filters_event(int event, Error **errp)
+{
+ NetClientState *nc;
+ NetFilterState *nf;
+ NetFilterClass *nfc = NULL;
+ Error *local_err = NULL;
+
+ QTAILQ_FOREACH(nc, &net_clients, next) {
+ QTAILQ_FOREACH(nf, &nc->filters, next) {
+ nfc = NETFILTER_GET_CLASS(OBJECT(nf));
+ nfc->handle_event(nf, event, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+ }
+}
+
void qmp_set_link(const char *name, bool up, Error **errp)
{
NetClientState *ncs[MAX_QUEUE_NUM];
diff --git a/net/slirp.c b/net/slirp.c
index 99884de204..f6dc03963a 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -150,6 +150,7 @@ static int net_slirp_init(NetClientState *peer, const char *model,
const char *vnameserver, const char *vnameserver6,
const char *smb_export, const char *vsmbserver,
const char **dnssearch, const char *vdomainname,
+ const char *tftp_server_name,
Error **errp)
{
/* default settings according to historic slirp */
@@ -350,6 +351,20 @@ static int net_slirp_init(NetClientState *peer, const char *model,
return -1;
}
+ if (vdomainname && strlen(vdomainname) > 255) {
+ error_setg(errp, "'domainname' parameter cannot exceed 255 bytes");
+ return -1;
+ }
+
+ if (vhostname && strlen(vhostname) > 255) {
+ error_setg(errp, "'vhostname' parameter cannot exceed 255 bytes");
+ return -1;
+ }
+
+ if (tftp_server_name && strlen(tftp_server_name) > 255) {
+ error_setg(errp, "'tftp-server-name' parameter cannot exceed 255 bytes");
+ return -1;
+ }
nc = qemu_new_net_client(&net_slirp_info, peer, model, name);
@@ -361,7 +376,8 @@ static int net_slirp_init(NetClientState *peer, const char *model,
s->slirp = slirp_init(restricted, ipv4, net, mask, host,
ipv6, ip6_prefix, vprefix6_len, ip6_host,
- vhostname, tftp_export, bootfile, dhcp,
+ vhostname, tftp_server_name,
+ tftp_export, bootfile, dhcp,
dns, ip6_dns, dnssearch, vdomainname, s);
QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry);
@@ -898,7 +914,8 @@ int net_init_slirp(const Netdev *netdev, const char *name,
user->ipv6_host, user->hostname, user->tftp,
user->bootfile, user->dhcpstart,
user->dns, user->ipv6_dns, user->smb,
- user->smbserver, dnssearch, user->domainname, errp);
+ user->smbserver, dnssearch, user->domainname,
+ user->tftp_server_name, errp);
while (slirp_configs) {
config = slirp_configs;
diff --git a/qapi/migration.json b/qapi/migration.json
index 6e8c21258a..0928f4b727 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -923,18 +923,18 @@
##
# @COLOMode:
#
-# The colo mode
+# The COLO current mode.
#
-# @unknown: unknown mode
+# @none: COLO is disabled.
#
-# @primary: master side
+# @primary: COLO node in primary side.
#
-# @secondary: slave side
+# @secondary: COLO node in slave side.
#
# Since: 2.8
##
{ 'enum': 'COLOMode',
- 'data': [ 'unknown', 'primary', 'secondary'] }
+ 'data': [ 'none', 'primary', 'secondary'] }
##
# @FailoverStatus:
@@ -957,6 +957,44 @@
'data': [ 'none', 'require', 'active', 'completed', 'relaunch' ] }
##
+# @COLO_EXIT:
+#
+# Emitted when VM finishes COLO mode due to some errors happening or
+# at the request of users.
+#
+# @mode: report COLO mode when COLO exited.
+#
+# @reason: describes the reason for the COLO exit.
+#
+# Since: 3.1
+#
+# Example:
+#
+# <- { "timestamp": {"seconds": 2032141960, "microseconds": 417172},
+# "event": "COLO_EXIT", "data": {"mode": "primary", "reason": "request" } }
+#
+##
+{ 'event': 'COLO_EXIT',
+ 'data': {'mode': 'COLOMode', 'reason': 'COLOExitReason' } }
+
+##
+# @COLOExitReason:
+#
+# The reason for a COLO exit
+#
+# @none: no failover has ever happened. This can't occur in the
+# COLO_EXIT event, only in the result of query-colo-status.
+#
+# @request: COLO exit is due to an external request
+#
+# @error: COLO exit is due to an internal error
+#
+# Since: 3.1
+##
+{ 'enum': 'COLOExitReason',
+ 'data': [ 'none', 'request', 'error' ] }
+
+##
# @x-colo-lost-heartbeat:
#
# Tell qemu that heartbeat is lost, request it to do takeover procedures.
@@ -1270,6 +1308,38 @@
{ 'command': 'xen-colo-do-checkpoint' }
##
+# @COLOStatus:
+#
+# The result format for 'query-colo-status'.
+#
+# @mode: COLO running mode. If COLO is running, this field will return
+# 'primary' or 'secondary'.
+#
+# @reason: describes the reason for the COLO exit.
+#
+# Since: 3.0
+##
+{ 'struct': 'COLOStatus',
+ 'data': { 'mode': 'COLOMode', 'reason': 'COLOExitReason' } }
+
+##
+# @query-colo-status:
+#
+# Query COLO status while the vm is running.
+#
+# Returns: A @COLOStatus object showing the status.
+#
+# Example:
+#
+# -> { "execute": "query-colo-status" }
+# <- { "return": { "mode": "primary", "active": true, "reason": "request" } }
+#
+# Since: 3.0
+##
+{ 'command': 'query-colo-status',
+ 'returns': 'COLOStatus' }
+
+##
# @migrate-recover:
#
# Provide a recovery migration stream URI.
diff --git a/qapi/misc.json b/qapi/misc.json
index 3a68af9ca3..c85c6c8ca3 100644
--- a/qapi/misc.json
+++ b/qapi/misc.json
@@ -3070,7 +3070,8 @@
# Emitted when the guest changes the RTC time.
#
# @offset: offset between base RTC clock (as specified by -rtc base), and
-# new RTC clock value
+# new RTC clock value. Note that value will be different depending
+# on clock chosen to drive RTC (specified by -rtc clock).
#
# Note: This event is rate-limited.
#
diff --git a/qapi/net.json b/qapi/net.json
index c86f351161..8f99fd911d 100644
--- a/qapi/net.json
+++ b/qapi/net.json
@@ -174,6 +174,8 @@
#
# @guestfwd: forward guest TCP connections
#
+# @tftp-server-name: RFC2132 "TFTP server name" string (Since 3.1)
+#
# Since: 1.2
##
{ 'struct': 'NetdevUserOptions',
@@ -198,7 +200,8 @@
'*smb': 'str',
'*smbserver': 'str',
'*hostfwd': ['String'],
- '*guestfwd': ['String'] } }
+ '*guestfwd': ['String'],
+ '*tftp-server-name': 'str' } }
##
# @NetdevTapOptions:
diff --git a/qemu-options.hx b/qemu-options.hx
index f139459e80..08f8516a9a 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1823,7 +1823,7 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev,
" [,ipv6[=on|off]][,ipv6-net=addr[/int]][,ipv6-host=addr]\n"
" [,restrict=on|off][,hostname=host][,dhcpstart=addr]\n"
" [,dns=addr][,ipv6-dns=addr][,dnssearch=domain][,domainname=domain]\n"
- " [,tftp=dir][,bootfile=f][,hostfwd=rule][,guestfwd=rule]"
+ " [,tftp=dir][,tftp-server-name=name][,bootfile=f][,hostfwd=rule][,guestfwd=rule]"
#ifndef _WIN32
"[,smb=dir[,smbserver=addr]]\n"
#endif
@@ -2060,6 +2060,11 @@ server. The files in @var{dir} will be exposed as the root of a TFTP server.
The TFTP client on the guest must be configured in binary mode (use the command
@code{bin} of the Unix TFTP client).
+@item tftp-server-name=@var{name}
+In BOOTP reply, broadcast @var{name} as the "TFTP server name" (RFC2132 option
+66). This can be used to advise the guest to load boot files or configurations
+from a different server than the host address.
+
@item bootfile=@var{file}
When using the user mode network stack, broadcast @var{file} as the BOOTP
filename. In conjunction with @option{tftp}, this can be used to network boot
@@ -2256,7 +2261,7 @@ qemu-system-i386 linux.img \
-netdev socket,id=n2,mcast=230.0.0.1:1234
# launch yet another QEMU instance on same "bus"
qemu-system-i386 linux.img \
- -device e1000,netdev=n3,macaddr=52:54:00:12:34:58 \
+ -device e1000,netdev=n3,mac=52:54:00:12:34:58 \
-netdev socket,id=n3,mcast=230.0.0.1:1234
@end example
@@ -3458,25 +3463,29 @@ HXCOMM Silently ignored for compatibility
DEF("clock", HAS_ARG, QEMU_OPTION_clock, "", QEMU_ARCH_ALL)
DEF("rtc", HAS_ARG, QEMU_OPTION_rtc, \
- "-rtc [base=utc|localtime|date][,clock=host|rt|vm][,driftfix=none|slew]\n" \
+ "-rtc [base=utc|localtime|<datetime>][,clock=host|rt|vm][,driftfix=none|slew]\n" \
" set the RTC base and clock, enable drift fix for clock ticks (x86 only)\n",
QEMU_ARCH_ALL)
STEXI
-@item -rtc [base=utc|localtime|@var{date}][,clock=host|vm][,driftfix=none|slew]
+@item -rtc [base=utc|localtime|@var{datetime}][,clock=host|rt|vm][,driftfix=none|slew]
@findex -rtc
Specify @option{base} as @code{utc} or @code{localtime} to let the RTC start at the current
UTC or local time, respectively. @code{localtime} is required for correct date in
-MS-DOS or Windows. To start at a specific point in time, provide @var{date} in the
+MS-DOS or Windows. To start at a specific point in time, provide @var{datetime} in the
format @code{2006-06-17T16:01:21} or @code{2006-06-17}. The default base is UTC.
By default the RTC is driven by the host system time. This allows using of the
RTC as accurate reference clock inside the guest, specifically if the host
time is smoothly following an accurate external reference clock, e.g. via NTP.
If you want to isolate the guest time from the host, you can set @option{clock}
-to @code{rt} instead. To even prevent it from progressing during suspension,
-you can set it to @code{vm}.
+to @code{rt} instead, which provides a host monotonic clock if host support it.
+To even prevent the RTC from progressing during suspension, you can set @option{clock}
+to @code{vm} (virtual clock). @samp{clock=vm} is recommended especially in
+icount mode in order to preserve determinism; however, note that in icount mode
+the speed of the virtual clock is variable and can in general differ from the
+host clock.
Enable @option{driftfix} (i386 targets only) if you experience time drift problems,
specifically with Windows' ACPI HAL. This option will try to figure out how
diff --git a/qom/cpu.c b/qom/cpu.c
index f7746546d0..9ad1372d57 100644
--- a/qom/cpu.c
+++ b/qom/cpu.c
@@ -265,7 +265,7 @@ static void cpu_common_reset(CPUState *cpu)
cpu->mem_io_pc = 0;
cpu->mem_io_vaddr = 0;
cpu->icount_extra = 0;
- cpu->icount_decr.u32 = 0;
+ atomic_set(&cpu->icount_decr.u32, 0);
cpu->can_do_io = 1;
cpu->exception_index = -1;
cpu->crash_occurred = false;
diff --git a/replay/replay-events.c b/replay/replay-events.c
index 0964a82838..d9a2d495b9 100644
--- a/replay/replay-events.c
+++ b/replay/replay-events.c
@@ -190,6 +190,7 @@ void replay_save_events(int checkpoint)
{
g_assert(replay_mutex_locked());
g_assert(checkpoint != CHECKPOINT_CLOCK_WARP_START);
+ g_assert(checkpoint != CHECKPOINT_CLOCK_VIRTUAL);
while (!QTAILQ_EMPTY(&events_list)) {
Event *event = QTAILQ_FIRST(&events_list);
replay_save_event(event, checkpoint);
diff --git a/replay/replay-internal.c b/replay/replay-internal.c
index b077cb5fd5..1cea1d4dc9 100644
--- a/replay/replay-internal.c
+++ b/replay/replay-internal.c
@@ -217,20 +217,25 @@ void replay_mutex_unlock(void)
}
}
+void replay_advance_current_step(uint64_t current_step)
+{
+ int diff = (int)(replay_get_current_step() - replay_state.current_step);
+
+ /* Time can only go forward */
+ assert(diff >= 0);
+
+ if (diff > 0) {
+ replay_put_event(EVENT_INSTRUCTION);
+ replay_put_dword(diff);
+ replay_state.current_step += diff;
+ }
+}
+
/*! Saves cached instructions. */
void replay_save_instructions(void)
{
if (replay_file && replay_mode == REPLAY_MODE_RECORD) {
g_assert(replay_mutex_locked());
- int diff = (int)(replay_get_current_step() - replay_state.current_step);
-
- /* Time can only go forward */
- assert(diff >= 0);
-
- if (diff > 0) {
- replay_put_event(EVENT_INSTRUCTION);
- replay_put_dword(diff);
- replay_state.current_step += diff;
- }
+ replay_advance_current_step(replay_get_current_step());
}
}
diff --git a/replay/replay-internal.h b/replay/replay-internal.h
index 9b0fd916a3..af6f4d55d4 100644
--- a/replay/replay-internal.h
+++ b/replay/replay-internal.h
@@ -122,6 +122,8 @@ void replay_finish_event(void);
data_kind variable. */
void replay_fetch_data_kind(void);
+/*! Advance replay_state.current_step to the specified value. */
+void replay_advance_current_step(uint64_t current_step);
/*! Saves queued events (like instructions and sound). */
void replay_save_instructions(void);
diff --git a/replay/replay-time.c b/replay/replay-time.c
index 6a7565ec8d..0df1693337 100644
--- a/replay/replay-time.c
+++ b/replay/replay-time.c
@@ -15,13 +15,15 @@
#include "replay-internal.h"
#include "qemu/error-report.h"
-int64_t replay_save_clock(ReplayClockKind kind, int64_t clock)
+int64_t replay_save_clock(ReplayClockKind kind, int64_t clock, int64_t raw_icount)
{
-
if (replay_file) {
g_assert(replay_mutex_locked());
- replay_save_instructions();
+ /* Due to the caller's locking requirements we get the icount from it
+ * instead of using replay_save_instructions().
+ */
+ replay_advance_current_step(raw_icount);
replay_put_event(EVENT_CLOCK + kind);
replay_put_qword(clock);
}
diff --git a/replay/replay.c b/replay/replay.c
index 379b51ab46..8b172b2d1b 100644
--- a/replay/replay.c
+++ b/replay/replay.c
@@ -214,7 +214,14 @@ bool replay_checkpoint(ReplayCheckpoint checkpoint)
/* This checkpoint belongs to several threads.
Processing events from different threads is
non-deterministic */
- if (checkpoint != CHECKPOINT_CLOCK_WARP_START) {
+ if (checkpoint != CHECKPOINT_CLOCK_WARP_START
+ /* FIXME: this is temporary fix, other checkpoints
+ may also be invoked from the different threads someday.
+ Asynchronous event processing should be refactored
+ to create additional replay event kind which is
+ nailed to the one of the threads and which processes
+ the event queue. */
+ && checkpoint != CHECKPOINT_CLOCK_VIRTUAL) {
replay_save_events(checkpoint);
}
res = true;
diff --git a/slirp/bootp.c b/slirp/bootp.c
index 9e7b53ba94..7b1af73c95 100644
--- a/slirp/bootp.c
+++ b/slirp/bootp.c
@@ -159,6 +159,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
struct in_addr preq_addr;
int dhcp_msg_type, val;
uint8_t *q;
+ uint8_t *end;
uint8_t client_ethaddr[ETH_ALEN];
/* extract exact DHCP msg type */
@@ -240,6 +241,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */
q = rbp->bp_vend;
+ end = (uint8_t *)&rbp[1];
memcpy(q, rfc1533_cookie, 4);
q += 4;
@@ -292,24 +294,46 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
if (*slirp->client_hostname) {
val = strlen(slirp->client_hostname);
- *q++ = RFC1533_HOSTNAME;
- *q++ = val;
- memcpy(q, slirp->client_hostname, val);
- q += val;
+ if (q + val + 2 >= end) {
+ g_warning("DHCP packet size exceeded, "
+ "omitting host name option.");
+ } else {
+ *q++ = RFC1533_HOSTNAME;
+ *q++ = val;
+ memcpy(q, slirp->client_hostname, val);
+ q += val;
+ }
}
if (slirp->vdomainname) {
val = strlen(slirp->vdomainname);
- *q++ = RFC1533_DOMAINNAME;
- *q++ = val;
- memcpy(q, slirp->vdomainname, val);
- q += val;
+ if (q + val + 2 >= end) {
+ g_warning("DHCP packet size exceeded, "
+ "omitting domain name option.");
+ } else {
+ *q++ = RFC1533_DOMAINNAME;
+ *q++ = val;
+ memcpy(q, slirp->vdomainname, val);
+ q += val;
+ }
+ }
+
+ if (slirp->tftp_server_name) {
+ val = strlen(slirp->tftp_server_name);
+ if (q + val + 2 >= end) {
+ g_warning("DHCP packet size exceeded, "
+ "omitting tftp-server-name option.");
+ } else {
+ *q++ = RFC2132_TFTP_SERVER_NAME;
+ *q++ = val;
+ memcpy(q, slirp->tftp_server_name, val);
+ q += val;
+ }
}
if (slirp->vdnssearch) {
- size_t spaceleft = sizeof(rbp->bp_vend) - (q - rbp->bp_vend);
val = slirp->vdnssearch_len;
- if (val + 1 > spaceleft) {
+ if (q + val >= end) {
g_warning("DHCP packet size exceeded, "
"omitting domain-search option.");
} else {
@@ -331,6 +355,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
memcpy(q, nak_msg, sizeof(nak_msg) - 1);
q += sizeof(nak_msg) - 1;
}
+ assert(q < end);
*q = RFC1533_END;
daddr.sin_addr.s_addr = 0xffffffffu;
diff --git a/slirp/bootp.h b/slirp/bootp.h
index 394525733e..4043489835 100644
--- a/slirp/bootp.h
+++ b/slirp/bootp.h
@@ -70,6 +70,7 @@
#define RFC2132_MAX_SIZE 57
#define RFC2132_RENEWAL_TIME 58
#define RFC2132_REBIND_TIME 59
+#define RFC2132_TFTP_SERVER_NAME 66
#define DHCPDISCOVER 1
#define DHCPOFFER 2
diff --git a/slirp/ip6_icmp.c b/slirp/ip6_icmp.c
index 3f41187cfe..cd1e0b9fe1 100644
--- a/slirp/ip6_icmp.c
+++ b/slirp/ip6_icmp.c
@@ -17,7 +17,7 @@ static void ra_timer_handler(void *opaque)
{
Slirp *slirp = opaque;
timer_mod(slirp->ra_timer,
- qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_EXT) + NDP_Interval);
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
ndp_send_ra(slirp);
}
@@ -27,10 +27,11 @@ void icmp6_init(Slirp *slirp)
return;
}
- slirp->ra_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL_EXT,
- ra_timer_handler, slirp);
+ slirp->ra_timer = timer_new_full(NULL, QEMU_CLOCK_VIRTUAL,
+ SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL,
+ ra_timer_handler, slirp);
timer_mod(slirp->ra_timer,
- qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_EXT) + NDP_Interval);
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
}
void icmp6_cleanup(Slirp *slirp)
diff --git a/slirp/libslirp.h b/slirp/libslirp.h
index 740408a96e..42e42e9a2a 100644
--- a/slirp/libslirp.h
+++ b/slirp/libslirp.h
@@ -13,6 +13,7 @@ Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork,
bool in6_enabled,
struct in6_addr vprefix_addr6, uint8_t vprefix_len,
struct in6_addr vhost6, const char *vhostname,
+ const char *tftp_server_name,
const char *tftp_path, const char *bootfile,
struct in_addr vdhcp_start, struct in_addr vnameserver,
struct in6_addr vnameserver6, const char **vdnssearch,
diff --git a/slirp/slirp.c b/slirp/slirp.c
index 5c3bd6163f..51de41fc02 100644
--- a/slirp/slirp.c
+++ b/slirp/slirp.c
@@ -283,6 +283,7 @@ Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork,
bool in6_enabled,
struct in6_addr vprefix_addr6, uint8_t vprefix_len,
struct in6_addr vhost6, const char *vhostname,
+ const char *tftp_server_name,
const char *tftp_path, const char *bootfile,
struct in_addr vdhcp_start, struct in_addr vnameserver,
struct in6_addr vnameserver6, const char **vdnssearch,
@@ -321,6 +322,7 @@ Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork,
slirp->vdhcp_startaddr = vdhcp_start;
slirp->vnameserver_addr = vnameserver;
slirp->vnameserver_addr6 = vnameserver6;
+ slirp->tftp_server_name = g_strdup(tftp_server_name);
if (vdnssearch) {
translate_dnssearch(slirp, vdnssearch);
diff --git a/slirp/slirp.h b/slirp/slirp.h
index 10b410898a..b80725a0d6 100644
--- a/slirp/slirp.h
+++ b/slirp/slirp.h
@@ -212,6 +212,7 @@ struct Slirp {
/* tftp states */
char *tftp_prefix;
struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
+ char *tftp_server_name;
ArpTable arp_table;
NdpTable ndp_table;
diff --git a/stubs/cpu-get-icount.c b/stubs/cpu-get-icount.c
index 0b7239d721..35f0c1e24c 100644
--- a/stubs/cpu-get-icount.c
+++ b/stubs/cpu-get-icount.c
@@ -11,6 +11,11 @@ int64_t cpu_get_icount(void)
abort();
}
+int64_t cpu_get_icount_raw(void)
+{
+ abort();
+}
+
void qemu_timer_notify_cb(void *opaque, QEMUClockType type)
{
qemu_notify_event();
diff --git a/stubs/replay.c b/stubs/replay.c
index 04279abb2c..4ac607895d 100644
--- a/stubs/replay.c
+++ b/stubs/replay.c
@@ -4,7 +4,7 @@
ReplayMode replay_mode;
-int64_t replay_save_clock(unsigned int kind, int64_t clock)
+int64_t replay_save_clock(unsigned int kind, int64_t clock, int64_t raw_icount)
{
abort();
return 0;
diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c
index b08078e7fc..a953897fcc 100644
--- a/target/alpha/cpu.c
+++ b/target/alpha/cpu.c
@@ -201,7 +201,6 @@ static void alpha_cpu_initfn(Object *obj)
CPUAlphaState *env = &cpu->env;
cs->env_ptr = env;
- tlb_flush(cs);
env->lock_addr = -1;
#if defined(CONFIG_USER_ONLY)
diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c
index 7f6ad3000b..61799d20e1 100644
--- a/target/arm/helper-a64.c
+++ b/target/arm/helper-a64.c
@@ -30,6 +30,7 @@
#include "exec/exec-all.h"
#include "exec/cpu_ldst.h"
#include "qemu/int128.h"
+#include "qemu/atomic128.h"
#include "tcg.h"
#include "fpu/softfloat.h"
#include <zlib.h> /* For crc32 */
@@ -509,189 +510,187 @@ uint64_t HELPER(crc32c_64)(uint64_t acc, uint64_t val, uint32_t bytes)
return crc32c(acc, buf, bytes) ^ 0xffffffff;
}
-/* Returns 0 on success; 1 otherwise. */
-static uint64_t do_paired_cmpxchg64_le(CPUARMState *env, uint64_t addr,
- uint64_t new_lo, uint64_t new_hi,
- bool parallel, uintptr_t ra)
+uint64_t HELPER(paired_cmpxchg64_le)(CPUARMState *env, uint64_t addr,
+ uint64_t new_lo, uint64_t new_hi)
{
- Int128 oldv, cmpv, newv;
+ Int128 cmpv = int128_make128(env->exclusive_val, env->exclusive_high);
+ Int128 newv = int128_make128(new_lo, new_hi);
+ Int128 oldv;
+ uintptr_t ra = GETPC();
+ uint64_t o0, o1;
bool success;
- cmpv = int128_make128(env->exclusive_val, env->exclusive_high);
- newv = int128_make128(new_lo, new_hi);
-
- if (parallel) {
-#ifndef CONFIG_ATOMIC128
- cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
-#else
- int mem_idx = cpu_mmu_index(env, false);
- TCGMemOpIdx oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx);
- oldv = helper_atomic_cmpxchgo_le_mmu(env, addr, cmpv, newv, oi, ra);
- success = int128_eq(oldv, cmpv);
-#endif
- } else {
- uint64_t o0, o1;
-
#ifdef CONFIG_USER_ONLY
- /* ??? Enforce alignment. */
- uint64_t *haddr = g2h(addr);
-
- helper_retaddr = ra;
- o0 = ldq_le_p(haddr + 0);
- o1 = ldq_le_p(haddr + 1);
- oldv = int128_make128(o0, o1);
-
- success = int128_eq(oldv, cmpv);
- if (success) {
- stq_le_p(haddr + 0, int128_getlo(newv));
- stq_le_p(haddr + 1, int128_gethi(newv));
- }
- helper_retaddr = 0;
+ /* ??? Enforce alignment. */
+ uint64_t *haddr = g2h(addr);
+
+ helper_retaddr = ra;
+ o0 = ldq_le_p(haddr + 0);
+ o1 = ldq_le_p(haddr + 1);
+ oldv = int128_make128(o0, o1);
+
+ success = int128_eq(oldv, cmpv);
+ if (success) {
+ stq_le_p(haddr + 0, int128_getlo(newv));
+ stq_le_p(haddr + 1, int128_gethi(newv));
+ }
+ helper_retaddr = 0;
#else
- int mem_idx = cpu_mmu_index(env, false);
- TCGMemOpIdx oi0 = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx);
- TCGMemOpIdx oi1 = make_memop_idx(MO_LEQ, mem_idx);
-
- o0 = helper_le_ldq_mmu(env, addr + 0, oi0, ra);
- o1 = helper_le_ldq_mmu(env, addr + 8, oi1, ra);
- oldv = int128_make128(o0, o1);
-
- success = int128_eq(oldv, cmpv);
- if (success) {
- helper_le_stq_mmu(env, addr + 0, int128_getlo(newv), oi1, ra);
- helper_le_stq_mmu(env, addr + 8, int128_gethi(newv), oi1, ra);
- }
-#endif
+ int mem_idx = cpu_mmu_index(env, false);
+ TCGMemOpIdx oi0 = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx);
+ TCGMemOpIdx oi1 = make_memop_idx(MO_LEQ, mem_idx);
+
+ o0 = helper_le_ldq_mmu(env, addr + 0, oi0, ra);
+ o1 = helper_le_ldq_mmu(env, addr + 8, oi1, ra);
+ oldv = int128_make128(o0, o1);
+
+ success = int128_eq(oldv, cmpv);
+ if (success) {
+ helper_le_stq_mmu(env, addr + 0, int128_getlo(newv), oi1, ra);
+ helper_le_stq_mmu(env, addr + 8, int128_gethi(newv), oi1, ra);
}
+#endif
return !success;
}
-uint64_t HELPER(paired_cmpxchg64_le)(CPUARMState *env, uint64_t addr,
- uint64_t new_lo, uint64_t new_hi)
-{
- return do_paired_cmpxchg64_le(env, addr, new_lo, new_hi, false, GETPC());
-}
-
uint64_t HELPER(paired_cmpxchg64_le_parallel)(CPUARMState *env, uint64_t addr,
uint64_t new_lo, uint64_t new_hi)
{
- return do_paired_cmpxchg64_le(env, addr, new_lo, new_hi, true, GETPC());
-}
-
-static uint64_t do_paired_cmpxchg64_be(CPUARMState *env, uint64_t addr,
- uint64_t new_lo, uint64_t new_hi,
- bool parallel, uintptr_t ra)
-{
Int128 oldv, cmpv, newv;
+ uintptr_t ra = GETPC();
bool success;
+ int mem_idx;
+ TCGMemOpIdx oi;
- /* high and low need to be switched here because this is not actually a
- * 128bit store but two doublewords stored consecutively
- */
- cmpv = int128_make128(env->exclusive_high, env->exclusive_val);
- newv = int128_make128(new_hi, new_lo);
+ assert(HAVE_CMPXCHG128);
- if (parallel) {
-#ifndef CONFIG_ATOMIC128
- cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
-#else
- int mem_idx = cpu_mmu_index(env, false);
- TCGMemOpIdx oi = make_memop_idx(MO_BEQ | MO_ALIGN_16, mem_idx);
- oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra);
- success = int128_eq(oldv, cmpv);
-#endif
- } else {
- uint64_t o0, o1;
+ mem_idx = cpu_mmu_index(env, false);
+ oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx);
-#ifdef CONFIG_USER_ONLY
- /* ??? Enforce alignment. */
- uint64_t *haddr = g2h(addr);
-
- helper_retaddr = ra;
- o1 = ldq_be_p(haddr + 0);
- o0 = ldq_be_p(haddr + 1);
- oldv = int128_make128(o0, o1);
-
- success = int128_eq(oldv, cmpv);
- if (success) {
- stq_be_p(haddr + 0, int128_gethi(newv));
- stq_be_p(haddr + 1, int128_getlo(newv));
- }
- helper_retaddr = 0;
-#else
- int mem_idx = cpu_mmu_index(env, false);
- TCGMemOpIdx oi0 = make_memop_idx(MO_BEQ | MO_ALIGN_16, mem_idx);
- TCGMemOpIdx oi1 = make_memop_idx(MO_BEQ, mem_idx);
-
- o1 = helper_be_ldq_mmu(env, addr + 0, oi0, ra);
- o0 = helper_be_ldq_mmu(env, addr + 8, oi1, ra);
- oldv = int128_make128(o0, o1);
-
- success = int128_eq(oldv, cmpv);
- if (success) {
- helper_be_stq_mmu(env, addr + 0, int128_gethi(newv), oi1, ra);
- helper_be_stq_mmu(env, addr + 8, int128_getlo(newv), oi1, ra);
- }
-#endif
- }
+ cmpv = int128_make128(env->exclusive_val, env->exclusive_high);
+ newv = int128_make128(new_lo, new_hi);
+ oldv = helper_atomic_cmpxchgo_le_mmu(env, addr, cmpv, newv, oi, ra);
+ success = int128_eq(oldv, cmpv);
return !success;
}
uint64_t HELPER(paired_cmpxchg64_be)(CPUARMState *env, uint64_t addr,
uint64_t new_lo, uint64_t new_hi)
{
- return do_paired_cmpxchg64_be(env, addr, new_lo, new_hi, false, GETPC());
+ /*
+ * High and low need to be switched here because this is not actually a
+ * 128bit store but two doublewords stored consecutively
+ */
+ Int128 cmpv = int128_make128(env->exclusive_val, env->exclusive_high);
+ Int128 newv = int128_make128(new_lo, new_hi);
+ Int128 oldv;
+ uintptr_t ra = GETPC();
+ uint64_t o0, o1;
+ bool success;
+
+#ifdef CONFIG_USER_ONLY
+ /* ??? Enforce alignment. */
+ uint64_t *haddr = g2h(addr);
+
+ helper_retaddr = ra;
+ o1 = ldq_be_p(haddr + 0);
+ o0 = ldq_be_p(haddr + 1);
+ oldv = int128_make128(o0, o1);
+
+ success = int128_eq(oldv, cmpv);
+ if (success) {
+ stq_be_p(haddr + 0, int128_gethi(newv));
+ stq_be_p(haddr + 1, int128_getlo(newv));
+ }
+ helper_retaddr = 0;
+#else
+ int mem_idx = cpu_mmu_index(env, false);
+ TCGMemOpIdx oi0 = make_memop_idx(MO_BEQ | MO_ALIGN_16, mem_idx);
+ TCGMemOpIdx oi1 = make_memop_idx(MO_BEQ, mem_idx);
+
+ o1 = helper_be_ldq_mmu(env, addr + 0, oi0, ra);
+ o0 = helper_be_ldq_mmu(env, addr + 8, oi1, ra);
+ oldv = int128_make128(o0, o1);
+
+ success = int128_eq(oldv, cmpv);
+ if (success) {
+ helper_be_stq_mmu(env, addr + 0, int128_gethi(newv), oi1, ra);
+ helper_be_stq_mmu(env, addr + 8, int128_getlo(newv), oi1, ra);
+ }
+#endif
+
+ return !success;
}
uint64_t HELPER(paired_cmpxchg64_be_parallel)(CPUARMState *env, uint64_t addr,
- uint64_t new_lo, uint64_t new_hi)
+ uint64_t new_lo, uint64_t new_hi)
{
- return do_paired_cmpxchg64_be(env, addr, new_lo, new_hi, true, GETPC());
+ Int128 oldv, cmpv, newv;
+ uintptr_t ra = GETPC();
+ bool success;
+ int mem_idx;
+ TCGMemOpIdx oi;
+
+ assert(HAVE_CMPXCHG128);
+
+ mem_idx = cpu_mmu_index(env, false);
+ oi = make_memop_idx(MO_BEQ | MO_ALIGN_16, mem_idx);
+
+ /*
+ * High and low need to be switched here because this is not actually a
+ * 128bit store but two doublewords stored consecutively
+ */
+ cmpv = int128_make128(env->exclusive_high, env->exclusive_val);
+ newv = int128_make128(new_hi, new_lo);
+ oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra);
+
+ success = int128_eq(oldv, cmpv);
+ return !success;
}
/* Writes back the old data into Rs. */
void HELPER(casp_le_parallel)(CPUARMState *env, uint32_t rs, uint64_t addr,
uint64_t new_lo, uint64_t new_hi)
{
- uintptr_t ra = GETPC();
-#ifndef CONFIG_ATOMIC128
- cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
-#else
Int128 oldv, cmpv, newv;
+ uintptr_t ra = GETPC();
+ int mem_idx;
+ TCGMemOpIdx oi;
+
+ assert(HAVE_CMPXCHG128);
+
+ mem_idx = cpu_mmu_index(env, false);
+ oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx);
cmpv = int128_make128(env->xregs[rs], env->xregs[rs + 1]);
newv = int128_make128(new_lo, new_hi);
-
- int mem_idx = cpu_mmu_index(env, false);
- TCGMemOpIdx oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx);
oldv = helper_atomic_cmpxchgo_le_mmu(env, addr, cmpv, newv, oi, ra);
env->xregs[rs] = int128_getlo(oldv);
env->xregs[rs + 1] = int128_gethi(oldv);
-#endif
}
void HELPER(casp_be_parallel)(CPUARMState *env, uint32_t rs, uint64_t addr,
uint64_t new_hi, uint64_t new_lo)
{
- uintptr_t ra = GETPC();
-#ifndef CONFIG_ATOMIC128
- cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
-#else
Int128 oldv, cmpv, newv;
+ uintptr_t ra = GETPC();
+ int mem_idx;
+ TCGMemOpIdx oi;
+
+ assert(HAVE_CMPXCHG128);
+
+ mem_idx = cpu_mmu_index(env, false);
+ oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx);
cmpv = int128_make128(env->xregs[rs + 1], env->xregs[rs]);
newv = int128_make128(new_lo, new_hi);
-
- int mem_idx = cpu_mmu_index(env, false);
- TCGMemOpIdx oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx);
oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra);
env->xregs[rs + 1] = int128_getlo(oldv);
env->xregs[rs] = int128_gethi(oldv);
-#endif
}
/*
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index 8a24278d79..bb9c4d8ac7 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -37,6 +37,7 @@
#include "trace-tcg.h"
#include "translate-a64.h"
+#include "qemu/atomic128.h"
static TCGv_i64 cpu_X[32];
static TCGv_i64 cpu_pc;
@@ -2086,26 +2087,27 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2,
get_mem_index(s),
MO_64 | MO_ALIGN | s->be_data);
tcg_gen_setcond_i64(TCG_COND_NE, tmp, tmp, cpu_exclusive_val);
- } else if (s->be_data == MO_LE) {
- if (tb_cflags(s->base.tb) & CF_PARALLEL) {
+ } else if (tb_cflags(s->base.tb) & CF_PARALLEL) {
+ if (!HAVE_CMPXCHG128) {
+ gen_helper_exit_atomic(cpu_env);
+ s->base.is_jmp = DISAS_NORETURN;
+ } else if (s->be_data == MO_LE) {
gen_helper_paired_cmpxchg64_le_parallel(tmp, cpu_env,
cpu_exclusive_addr,
cpu_reg(s, rt),
cpu_reg(s, rt2));
} else {
- gen_helper_paired_cmpxchg64_le(tmp, cpu_env, cpu_exclusive_addr,
- cpu_reg(s, rt), cpu_reg(s, rt2));
- }
- } else {
- if (tb_cflags(s->base.tb) & CF_PARALLEL) {
gen_helper_paired_cmpxchg64_be_parallel(tmp, cpu_env,
cpu_exclusive_addr,
cpu_reg(s, rt),
cpu_reg(s, rt2));
- } else {
- gen_helper_paired_cmpxchg64_be(tmp, cpu_env, cpu_exclusive_addr,
- cpu_reg(s, rt), cpu_reg(s, rt2));
}
+ } else if (s->be_data == MO_LE) {
+ gen_helper_paired_cmpxchg64_le(tmp, cpu_env, cpu_exclusive_addr,
+ cpu_reg(s, rt), cpu_reg(s, rt2));
+ } else {
+ gen_helper_paired_cmpxchg64_be(tmp, cpu_env, cpu_exclusive_addr,
+ cpu_reg(s, rt), cpu_reg(s, rt2));
}
} else {
tcg_gen_atomic_cmpxchg_i64(tmp, cpu_exclusive_addr, cpu_exclusive_val,
@@ -2175,14 +2177,18 @@ static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt,
}
tcg_temp_free_i64(cmp);
} else if (tb_cflags(s->base.tb) & CF_PARALLEL) {
- TCGv_i32 tcg_rs = tcg_const_i32(rs);
-
- if (s->be_data == MO_LE) {
- gen_helper_casp_le_parallel(cpu_env, tcg_rs, addr, t1, t2);
+ if (HAVE_CMPXCHG128) {
+ TCGv_i32 tcg_rs = tcg_const_i32(rs);
+ if (s->be_data == MO_LE) {
+ gen_helper_casp_le_parallel(cpu_env, tcg_rs, addr, t1, t2);
+ } else {
+ gen_helper_casp_be_parallel(cpu_env, tcg_rs, addr, t1, t2);
+ }
+ tcg_temp_free_i32(tcg_rs);
} else {
- gen_helper_casp_be_parallel(cpu_env, tcg_rs, addr, t1, t2);
+ gen_helper_exit_atomic(cpu_env);
+ s->base.is_jmp = DISAS_NORETURN;
}
- tcg_temp_free_i32(tcg_rs);
} else {
TCGv_i64 d1 = tcg_temp_new_i64();
TCGv_i64 d2 = tcg_temp_new_i64();
diff --git a/target/i386/Makefile.objs b/target/i386/Makefile.objs
index 04678f5503..32bf966300 100644
--- a/target/i386/Makefile.objs
+++ b/target/i386/Makefile.objs
@@ -3,17 +3,20 @@ obj-$(CONFIG_TCG) += translate.o
obj-$(CONFIG_TCG) += bpt_helper.o cc_helper.o excp_helper.o fpu_helper.o
obj-$(CONFIG_TCG) += int_helper.o mem_helper.o misc_helper.o mpx_helper.o
obj-$(CONFIG_TCG) += seg_helper.o smm_helper.o svm_helper.o
-obj-$(CONFIG_SOFTMMU) += machine.o arch_memory_mapping.o arch_dump.o monitor.o
-obj-$(CONFIG_KVM) += kvm.o hyperv.o
-obj-$(CONFIG_SEV) += sev.o
+ifeq ($(CONFIG_SOFTMMU),y)
+obj-y += machine.o arch_memory_mapping.o arch_dump.o monitor.o
+obj-$(CONFIG_KVM) += kvm.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
-obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o
-# HAX support
-ifdef CONFIG_WIN32
+obj-$(CONFIG_HYPERV) += hyperv.o
+obj-$(call lnot,$(CONFIG_HYPERV)) += hyperv-stub.o
+ifeq ($(CONFIG_WIN32),y)
obj-$(CONFIG_HAX) += hax-all.o hax-mem.o hax-windows.o
endif
-ifdef CONFIG_DARWIN
+ifeq ($(CONFIG_DARWIN),y)
obj-$(CONFIG_HAX) += hax-all.o hax-mem.o hax-darwin.o
obj-$(CONFIG_HVF) += hvf/
endif
obj-$(CONFIG_WHPX) += whpx-all.o
+endif
+obj-$(CONFIG_SEV) += sev.o
+obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index 9d4217afba..1469a1be01 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -5565,6 +5565,7 @@ static Property x86_cpu_properties[] = {
DEFINE_PROP_BOOL("hv-frequencies", X86CPU, hyperv_frequencies, false),
DEFINE_PROP_BOOL("hv-reenlightenment", X86CPU, hyperv_reenlightenment, false),
DEFINE_PROP_BOOL("hv-tlbflush", X86CPU, hyperv_tlbflush, false),
+ DEFINE_PROP_BOOL("hv-ipi", X86CPU, hyperv_ipi, false),
DEFINE_PROP_BOOL("check", X86CPU, check_cpuid, true),
DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false),
DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true),
@@ -5607,6 +5608,8 @@ static Property x86_cpu_properties[] = {
* to the specific Windows version being used."
*/
DEFINE_PROP_INT32("x-hv-max-vps", X86CPU, hv_max_vps, -1),
+ DEFINE_PROP_BOOL("x-hv-synic-kvm-only", X86CPU, hyperv_synic_kvm_only,
+ false),
DEFINE_PROP_END_OF_LIST()
};
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 730c06f80a..663f3a5e67 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -1378,10 +1378,12 @@ struct X86CPU {
bool hyperv_vpindex;
bool hyperv_runtime;
bool hyperv_synic;
+ bool hyperv_synic_kvm_only;
bool hyperv_stimer;
bool hyperv_frequencies;
bool hyperv_reenlightenment;
bool hyperv_tlbflush;
+ bool hyperv_ipi;
bool check_cpuid;
bool enforce_cpuid;
bool expose_kvm;
diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c
index 9f52bc413a..e193022c03 100644
--- a/target/i386/hvf/hvf.c
+++ b/target/i386/hvf/hvf.c
@@ -73,7 +73,6 @@
#include "target/i386/cpu.h"
HVFState *hvf_state;
-int hvf_disabled = 1;
static void assert_hvf_ok(hv_return_t ret)
{
@@ -604,11 +603,6 @@ int hvf_init_vcpu(CPUState *cpu)
return 0;
}
-void hvf_disable(int shouldDisable)
-{
- hvf_disabled = shouldDisable;
-}
-
static void hvf_store_events(CPUState *cpu, uint32_t ins_len, uint64_t idtvec_info)
{
X86CPU *x86_cpu = X86_CPU(cpu);
@@ -934,7 +928,7 @@ int hvf_vcpu_exec(CPUState *cpu)
return ret;
}
-static bool hvf_allowed;
+bool hvf_allowed;
static int hvf_accel_init(MachineState *ms)
{
@@ -942,7 +936,6 @@ static int hvf_accel_init(MachineState *ms)
hv_return_t ret;
HVFState *s;
- hvf_disable(0);
ret = hv_vm_create(HV_VM_DEFAULT);
assert_hvf_ok(ret);
diff --git a/target/i386/hvf/x86_decode.c b/target/i386/hvf/x86_decode.c
index 2d7540fe7c..2e33b69541 100644
--- a/target/i386/hvf/x86_decode.c
+++ b/target/i386/hvf/x86_decode.c
@@ -113,7 +113,8 @@ static void decode_modrm_reg(CPUX86State *env, struct x86_decode *decode,
{
op->type = X86_VAR_REG;
op->reg = decode->modrm.reg;
- op->ptr = get_reg_ref(env, op->reg, decode->rex.r, decode->operand_size);
+ op->ptr = get_reg_ref(env, op->reg, decode->rex.rex, decode->rex.r,
+ decode->operand_size);
}
static void decode_rax(CPUX86State *env, struct x86_decode *decode,
@@ -121,7 +122,8 @@ static void decode_rax(CPUX86State *env, struct x86_decode *decode,
{
op->type = X86_VAR_REG;
op->reg = R_EAX;
- op->ptr = get_reg_ref(env, op->reg, 0, decode->operand_size);
+ op->ptr = get_reg_ref(env, op->reg, decode->rex.rex, 0,
+ decode->operand_size);
}
static inline void decode_immediate(CPUX86State *env, struct x86_decode *decode,
@@ -263,16 +265,16 @@ static void decode_incgroup(CPUX86State *env, struct x86_decode *decode)
{
decode->op[0].type = X86_VAR_REG;
decode->op[0].reg = decode->opcode[0] - 0x40;
- decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
- decode->operand_size);
+ decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex,
+ decode->rex.b, decode->operand_size);
}
static void decode_decgroup(CPUX86State *env, struct x86_decode *decode)
{
decode->op[0].type = X86_VAR_REG;
decode->op[0].reg = decode->opcode[0] - 0x48;
- decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
- decode->operand_size);
+ decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex,
+ decode->rex.b, decode->operand_size);
}
static void decode_incgroup2(CPUX86State *env, struct x86_decode *decode)
@@ -288,16 +290,16 @@ static void decode_pushgroup(CPUX86State *env, struct x86_decode *decode)
{
decode->op[0].type = X86_VAR_REG;
decode->op[0].reg = decode->opcode[0] - 0x50;
- decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
- decode->operand_size);
+ decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex,
+ decode->rex.b, decode->operand_size);
}
static void decode_popgroup(CPUX86State *env, struct x86_decode *decode)
{
decode->op[0].type = X86_VAR_REG;
decode->op[0].reg = decode->opcode[0] - 0x58;
- decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
- decode->operand_size);
+ decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex,
+ decode->rex.b, decode->operand_size);
}
static void decode_jxx(CPUX86State *env, struct x86_decode *decode)
@@ -378,16 +380,16 @@ static void decode_xchgroup(CPUX86State *env, struct x86_decode *decode)
{
decode->op[0].type = X86_VAR_REG;
decode->op[0].reg = decode->opcode[0] - 0x90;
- decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
- decode->operand_size);
+ decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex,
+ decode->rex.b, decode->operand_size);
}
static void decode_movgroup(CPUX86State *env, struct x86_decode *decode)
{
decode->op[0].type = X86_VAR_REG;
decode->op[0].reg = decode->opcode[0] - 0xb8;
- decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
- decode->operand_size);
+ decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex,
+ decode->rex.b, decode->operand_size);
decode_immediate(env, decode, &decode->op[1], decode->operand_size);
}
@@ -402,8 +404,8 @@ static void decode_movgroup8(CPUX86State *env, struct x86_decode *decode)
{
decode->op[0].type = X86_VAR_REG;
decode->op[0].reg = decode->opcode[0] - 0xb0;
- decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
- decode->operand_size);
+ decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex,
+ decode->rex.b, decode->operand_size);
decode_immediate(env, decode, &decode->op[1], decode->operand_size);
}
@@ -412,7 +414,8 @@ static void decode_rcx(CPUX86State *env, struct x86_decode *decode,
{
op->type = X86_VAR_REG;
op->reg = R_ECX;
- op->ptr = get_reg_ref(env, op->reg, decode->rex.b, decode->operand_size);
+ op->ptr = get_reg_ref(env, op->reg, decode->rex.rex, decode->rex.b,
+ decode->operand_size);
}
struct decode_tbl {
@@ -639,8 +642,8 @@ static void decode_bswap(CPUX86State *env, struct x86_decode *decode)
{
decode->op[0].type = X86_VAR_REG;
decode->op[0].reg = decode->opcode[1] - 0xc8;
- decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
- decode->operand_size);
+ decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex,
+ decode->rex.b, decode->operand_size);
}
static void decode_d9_4(CPUX86State *env, struct x86_decode *decode)
@@ -1686,7 +1689,8 @@ calc_addr:
}
}
-target_ulong get_reg_ref(CPUX86State *env, int reg, int is_extended, int size)
+target_ulong get_reg_ref(CPUX86State *env, int reg, int rex, int is_extended,
+ int size)
{
target_ulong ptr = 0;
int which = 0;
@@ -1698,7 +1702,7 @@ target_ulong get_reg_ref(CPUX86State *env, int reg, int is_extended, int size)
switch (size) {
case 1:
- if (is_extended || reg < 4) {
+ if (is_extended || reg < 4 || rex) {
which = 1;
ptr = (target_ulong)&RL(env, reg);
} else {
@@ -1714,10 +1718,11 @@ target_ulong get_reg_ref(CPUX86State *env, int reg, int is_extended, int size)
return ptr;
}
-target_ulong get_reg_val(CPUX86State *env, int reg, int is_extended, int size)
+target_ulong get_reg_val(CPUX86State *env, int reg, int rex, int is_extended,
+ int size)
{
target_ulong val = 0;
- memcpy(&val, (void *)get_reg_ref(env, reg, is_extended, size), size);
+ memcpy(&val, (void *)get_reg_ref(env, reg, rex, is_extended, size), size);
return val;
}
@@ -1739,7 +1744,8 @@ static target_ulong get_sib_val(CPUX86State *env, struct x86_decode *decode,
if (base_reg == R_ESP || base_reg == R_EBP) {
*sel = R_SS;
}
- base = get_reg_val(env, decode->sib.base, decode->rex.b, addr_size);
+ base = get_reg_val(env, decode->sib.base, decode->rex.rex,
+ decode->rex.b, addr_size);
}
if (decode->rex.x) {
@@ -1747,7 +1753,8 @@ static target_ulong get_sib_val(CPUX86State *env, struct x86_decode *decode,
}
if (index_reg != R_ESP) {
- scaled_index = get_reg_val(env, index_reg, decode->rex.x, addr_size) <<
+ scaled_index = get_reg_val(env, index_reg, decode->rex.rex,
+ decode->rex.x, addr_size) <<
decode->sib.scale;
}
return base + scaled_index;
@@ -1776,7 +1783,8 @@ void calc_modrm_operand32(CPUX86State *env, struct x86_decode *decode,
if (decode->modrm.rm == R_EBP || decode->modrm.rm == R_ESP) {
seg = R_SS;
}
- ptr += get_reg_val(env, decode->modrm.rm, decode->rex.b, addr_size);
+ ptr += get_reg_val(env, decode->modrm.rm, decode->rex.rex,
+ decode->rex.b, addr_size);
}
if (X86_DECODE_CMD_LEA == decode->cmd) {
@@ -1805,7 +1813,8 @@ void calc_modrm_operand64(CPUX86State *env, struct x86_decode *decode,
} else if (0 == mod && 5 == rm) {
ptr = RIP(env) + decode->len + (int32_t) offset;
} else {
- ptr = get_reg_val(env, src, decode->rex.b, 8) + (int64_t) offset;
+ ptr = get_reg_val(env, src, decode->rex.rex, decode->rex.b, 8) +
+ (int64_t) offset;
}
if (X86_DECODE_CMD_LEA == decode->cmd) {
@@ -1822,8 +1831,8 @@ void calc_modrm_operand(CPUX86State *env, struct x86_decode *decode,
if (3 == decode->modrm.mod) {
op->reg = decode->modrm.reg;
op->type = X86_VAR_REG;
- op->ptr = get_reg_ref(env, decode->modrm.rm, decode->rex.b,
- decode->operand_size);
+ op->ptr = get_reg_ref(env, decode->modrm.rm, decode->rex.rex,
+ decode->rex.b, decode->operand_size);
return;
}
diff --git a/target/i386/hvf/x86_decode.h b/target/i386/hvf/x86_decode.h
index 5ab6f31fa5..ef4bcab310 100644
--- a/target/i386/hvf/x86_decode.h
+++ b/target/i386/hvf/x86_decode.h
@@ -303,8 +303,10 @@ uint64_t sign(uint64_t val, int size);
uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode);
-target_ulong get_reg_ref(CPUX86State *env, int reg, int is_extended, int size);
-target_ulong get_reg_val(CPUX86State *env, int reg, int is_extended, int size);
+target_ulong get_reg_ref(CPUX86State *env, int reg, int rex, int is_extended,
+ int size);
+target_ulong get_reg_val(CPUX86State *env, int reg, int rex, int is_extended,
+ int size);
void calc_modrm_operand(CPUX86State *env, struct x86_decode *decode,
struct x86_decode_op *op);
target_ulong decode_linear_addr(CPUX86State *env, struct x86_decode *decode,
diff --git a/target/i386/hyperv-proto.h b/target/i386/hyperv-proto.h
index d6d5a79293..8c572cd7c2 100644
--- a/target/i386/hyperv-proto.h
+++ b/target/i386/hyperv-proto.h
@@ -1,7 +1,7 @@
/*
- * Definitions for Hyper-V guest/hypervisor interaction
+ * Definitions for Hyper-V guest/hypervisor interaction - x86-specific part
*
- * Copyright (C) 2017 Parallels International GmbH
+ * Copyright (c) 2017-2018 Virtuozzo International GmbH.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
@@ -10,7 +10,7 @@
#ifndef TARGET_I386_HYPERV_PROTO_H
#define TARGET_I386_HYPERV_PROTO_H
-#include "qemu/bitmap.h"
+#include "hw/hyperv/hyperv-proto.h"
#define HV_CPUID_VENDOR_AND_MAX_FUNCTIONS 0x40000000
#define HV_CPUID_INTERFACE 0x40000001
@@ -58,6 +58,7 @@
#define HV_APIC_ACCESS_RECOMMENDED (1u << 3)
#define HV_SYSTEM_RESET_RECOMMENDED (1u << 4)
#define HV_RELAXED_TIMING_RECOMMENDED (1u << 5)
+#define HV_CLUSTER_IPI_RECOMMENDED (1u << 10)
#define HV_EX_PROCESSOR_MASKS_RECOMMENDED (1u << 11)
/*
@@ -138,25 +139,6 @@
#define HV_X64_MSR_TSC_EMULATION_STATUS 0x40000108
/*
- * Hypercall status code
- */
-#define HV_STATUS_SUCCESS 0
-#define HV_STATUS_INVALID_HYPERCALL_CODE 2
-#define HV_STATUS_INVALID_HYPERCALL_INPUT 3
-#define HV_STATUS_INVALID_ALIGNMENT 4
-#define HV_STATUS_INVALID_PARAMETER 5
-#define HV_STATUS_INSUFFICIENT_MEMORY 11
-#define HV_STATUS_INVALID_CONNECTION_ID 18
-#define HV_STATUS_INSUFFICIENT_BUFFERS 19
-
-/*
- * Hypercall numbers
- */
-#define HV_POST_MESSAGE 0x005c
-#define HV_SIGNAL_EVENT 0x005d
-#define HV_HYPERCALL_FAST (1u << 16)
-
-/*
* Hypercall MSR bits
*/
#define HV_HYPERCALL_ENABLE (1u << 0)
@@ -165,7 +147,6 @@
* Synthetic interrupt controller definitions
*/
#define HV_SYNIC_VERSION 1
-#define HV_SINT_COUNT 16
#define HV_SYNIC_ENABLE (1u << 0)
#define HV_SIMP_ENABLE (1u << 0)
#define HV_SIEFP_ENABLE (1u << 0)
@@ -175,94 +156,5 @@
#define HV_STIMER_COUNT 4
-/*
- * Message size
- */
-#define HV_MESSAGE_PAYLOAD_SIZE 240
-
-/*
- * Message types
- */
-#define HV_MESSAGE_NONE 0x00000000
-#define HV_MESSAGE_VMBUS 0x00000001
-#define HV_MESSAGE_UNMAPPED_GPA 0x80000000
-#define HV_MESSAGE_GPA_INTERCEPT 0x80000001
-#define HV_MESSAGE_TIMER_EXPIRED 0x80000010
-#define HV_MESSAGE_INVALID_VP_REGISTER_VALUE 0x80000020
-#define HV_MESSAGE_UNRECOVERABLE_EXCEPTION 0x80000021
-#define HV_MESSAGE_UNSUPPORTED_FEATURE 0x80000022
-#define HV_MESSAGE_EVENTLOG_BUFFERCOMPLETE 0x80000040
-#define HV_MESSAGE_X64_IOPORT_INTERCEPT 0x80010000
-#define HV_MESSAGE_X64_MSR_INTERCEPT 0x80010001
-#define HV_MESSAGE_X64_CPUID_INTERCEPT 0x80010002
-#define HV_MESSAGE_X64_EXCEPTION_INTERCEPT 0x80010003
-#define HV_MESSAGE_X64_APIC_EOI 0x80010004
-#define HV_MESSAGE_X64_LEGACY_FP_ERROR 0x80010005
-
-/*
- * Message flags
- */
-#define HV_MESSAGE_FLAG_PENDING 0x1
-
-/*
- * Event flags number per SINT
- */
-#define HV_EVENT_FLAGS_COUNT (256 * 8)
-
-/*
- * Connection id valid bits
- */
-#define HV_CONNECTION_ID_MASK 0x00ffffff
-
-/*
- * Input structure for POST_MESSAGE hypercall
- */
-struct hyperv_post_message_input {
- uint32_t connection_id;
- uint32_t _reserved;
- uint32_t message_type;
- uint32_t payload_size;
- uint8_t payload[HV_MESSAGE_PAYLOAD_SIZE];
-};
-
-/*
- * Input structure for SIGNAL_EVENT hypercall
- */
-struct hyperv_signal_event_input {
- uint32_t connection_id;
- uint16_t flag_number;
- uint16_t _reserved_zero;
-};
-
-/*
- * SynIC message structures
- */
-struct hyperv_message_header {
- uint32_t message_type;
- uint8_t payload_size;
- uint8_t message_flags; /* HV_MESSAGE_FLAG_XX */
- uint8_t _reserved[2];
- uint64_t sender;
-};
-
-struct hyperv_message {
- struct hyperv_message_header header;
- uint8_t payload[HV_MESSAGE_PAYLOAD_SIZE];
-};
-
-struct hyperv_message_page {
- struct hyperv_message slot[HV_SINT_COUNT];
-};
-
-/*
- * SynIC event flags structures
- */
-struct hyperv_event_flags {
- DECLARE_BITMAP(flags, HV_EVENT_FLAGS_COUNT);
-};
-
-struct hyperv_event_flags_page {
- struct hyperv_event_flags slot[HV_SINT_COUNT];
-};
#endif
diff --git a/target/i386/hyperv-stub.c b/target/i386/hyperv-stub.c
new file mode 100644
index 0000000000..fe548cbae2
--- /dev/null
+++ b/target/i386/hyperv-stub.c
@@ -0,0 +1,48 @@
+/*
+ * Stubs for CONFIG_HYPERV=n
+ *
+ * Copyright (c) 2015-2018 Virtuozzo International GmbH.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hyperv.h"
+
+#ifdef CONFIG_KVM
+int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit)
+{
+ switch (exit->type) {
+ case KVM_EXIT_HYPERV_SYNIC:
+ if (!cpu->hyperv_synic) {
+ return -1;
+ }
+
+ /*
+ * Tracking the changes in the MSRs is unnecessary as there are no
+ * users for them beside save/load, which is handled nicely by the
+ * generic MSR save/load code
+ */
+ return 0;
+ case KVM_EXIT_HYPERV_HCALL:
+ exit->u.hcall.result = HV_STATUS_INVALID_HYPERCALL_CODE;
+ return 0;
+ default:
+ return -1;
+ }
+}
+#endif
+
+int hyperv_x86_synic_add(X86CPU *cpu)
+{
+ return -ENOSYS;
+}
+
+void hyperv_x86_synic_reset(X86CPU *cpu)
+{
+}
+
+void hyperv_x86_synic_update(X86CPU *cpu)
+{
+}
diff --git a/target/i386/hyperv.c b/target/i386/hyperv.c
index 3065d765ed..b264a28620 100644
--- a/target/i386/hyperv.c
+++ b/target/i386/hyperv.c
@@ -14,16 +14,36 @@
#include "qemu/osdep.h"
#include "qemu/main-loop.h"
#include "hyperv.h"
+#include "hw/hyperv/hyperv.h"
#include "hyperv-proto.h"
-uint32_t hyperv_vp_index(X86CPU *cpu)
+int hyperv_x86_synic_add(X86CPU *cpu)
{
- return CPU(cpu)->cpu_index;
+ hyperv_synic_add(CPU(cpu));
+ return 0;
}
-X86CPU *hyperv_find_vcpu(uint32_t vp_index)
+void hyperv_x86_synic_reset(X86CPU *cpu)
{
- return X86_CPU(qemu_get_cpu(vp_index));
+ hyperv_synic_reset(CPU(cpu));
+}
+
+void hyperv_x86_synic_update(X86CPU *cpu)
+{
+ CPUX86State *env = &cpu->env;
+ bool enable = env->msr_hv_synic_control & HV_SYNIC_ENABLE;
+ hwaddr msg_page_addr = (env->msr_hv_synic_msg_page & HV_SIMP_ENABLE) ?
+ (env->msr_hv_synic_msg_page & TARGET_PAGE_MASK) : 0;
+ hwaddr event_page_addr = (env->msr_hv_synic_evt_page & HV_SIEFP_ENABLE) ?
+ (env->msr_hv_synic_evt_page & TARGET_PAGE_MASK) : 0;
+ hyperv_synic_update(CPU(cpu), enable, msg_page_addr, event_page_addr);
+}
+
+static void async_synic_update(CPUState *cs, run_on_cpu_data data)
+{
+ qemu_mutex_lock_iothread();
+ hyperv_x86_synic_update(X86_CPU(cs));
+ qemu_mutex_unlock_iothread();
}
int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit)
@@ -36,11 +56,6 @@ int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit)
return -1;
}
- /*
- * For now just track changes in SynIC control and msg/evt pages msr's.
- * When SynIC messaging/events processing will be added in future
- * here we will do messages queues flushing and pages remapping.
- */
switch (exit->u.synic.msr) {
case HV_X64_MSR_SCONTROL:
env->msr_hv_synic_control = exit->u.synic.control;
@@ -54,98 +69,33 @@ int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit)
default:
return -1;
}
+
+ /*
+ * this will run in this cpu thread before it returns to KVM, but in a
+ * safe environment (i.e. when all cpus are quiescent) -- this is
+ * necessary because memory hierarchy is being changed
+ */
+ async_safe_run_on_cpu(CPU(cpu), async_synic_update, RUN_ON_CPU_NULL);
+
return 0;
case KVM_EXIT_HYPERV_HCALL: {
- uint16_t code;
+ uint16_t code = exit->u.hcall.input & 0xffff;
+ bool fast = exit->u.hcall.input & HV_HYPERCALL_FAST;
+ uint64_t param = exit->u.hcall.params[0];
- code = exit->u.hcall.input & 0xffff;
switch (code) {
case HV_POST_MESSAGE:
+ exit->u.hcall.result = hyperv_hcall_post_message(param, fast);
+ break;
case HV_SIGNAL_EVENT:
+ exit->u.hcall.result = hyperv_hcall_signal_event(param, fast);
+ break;
default:
exit->u.hcall.result = HV_STATUS_INVALID_HYPERCALL_CODE;
- return 0;
}
+ return 0;
}
default:
return -1;
}
}
-
-static void kvm_hv_sint_ack_handler(EventNotifier *notifier)
-{
- HvSintRoute *sint_route = container_of(notifier, HvSintRoute,
- sint_ack_notifier);
- event_notifier_test_and_clear(notifier);
- if (sint_route->sint_ack_clb) {
- sint_route->sint_ack_clb(sint_route);
- }
-}
-
-HvSintRoute *kvm_hv_sint_route_create(uint32_t vp_index, uint32_t sint,
- HvSintAckClb sint_ack_clb)
-{
- HvSintRoute *sint_route;
- int r, gsi;
-
- sint_route = g_malloc0(sizeof(*sint_route));
- r = event_notifier_init(&sint_route->sint_set_notifier, false);
- if (r) {
- goto err;
- }
-
- r = event_notifier_init(&sint_route->sint_ack_notifier, false);
- if (r) {
- goto err_sint_set_notifier;
- }
-
- event_notifier_set_handler(&sint_route->sint_ack_notifier,
- kvm_hv_sint_ack_handler);
-
- gsi = kvm_irqchip_add_hv_sint_route(kvm_state, vp_index, sint);
- if (gsi < 0) {
- goto err_gsi;
- }
-
- r = kvm_irqchip_add_irqfd_notifier_gsi(kvm_state,
- &sint_route->sint_set_notifier,
- &sint_route->sint_ack_notifier, gsi);
- if (r) {
- goto err_irqfd;
- }
- sint_route->gsi = gsi;
- sint_route->sint_ack_clb = sint_ack_clb;
- sint_route->vp_index = vp_index;
- sint_route->sint = sint;
-
- return sint_route;
-
-err_irqfd:
- kvm_irqchip_release_virq(kvm_state, gsi);
-err_gsi:
- event_notifier_set_handler(&sint_route->sint_ack_notifier, NULL);
- event_notifier_cleanup(&sint_route->sint_ack_notifier);
-err_sint_set_notifier:
- event_notifier_cleanup(&sint_route->sint_set_notifier);
-err:
- g_free(sint_route);
-
- return NULL;
-}
-
-void kvm_hv_sint_route_destroy(HvSintRoute *sint_route)
-{
- kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state,
- &sint_route->sint_set_notifier,
- sint_route->gsi);
- kvm_irqchip_release_virq(kvm_state, sint_route->gsi);
- event_notifier_set_handler(&sint_route->sint_ack_notifier, NULL);
- event_notifier_cleanup(&sint_route->sint_ack_notifier);
- event_notifier_cleanup(&sint_route->sint_set_notifier);
- g_free(sint_route);
-}
-
-int kvm_hv_sint_route_set_sint(HvSintRoute *sint_route)
-{
- return event_notifier_set(&sint_route->sint_set_notifier);
-}
diff --git a/target/i386/hyperv.h b/target/i386/hyperv.h
index 00c9b454bb..67543296c3 100644
--- a/target/i386/hyperv.h
+++ b/target/i386/hyperv.h
@@ -16,30 +16,14 @@
#include "cpu.h"
#include "sysemu/kvm.h"
-#include "qemu/event_notifier.h"
-
-typedef struct HvSintRoute HvSintRoute;
-typedef void (*HvSintAckClb)(HvSintRoute *sint_route);
-
-struct HvSintRoute {
- uint32_t sint;
- uint32_t vp_index;
- int gsi;
- EventNotifier sint_set_notifier;
- EventNotifier sint_ack_notifier;
- HvSintAckClb sint_ack_clb;
-};
+#include "hw/hyperv/hyperv.h"
+#ifdef CONFIG_KVM
int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit);
+#endif
-HvSintRoute *kvm_hv_sint_route_create(uint32_t vp_index, uint32_t sint,
- HvSintAckClb sint_ack_clb);
-
-void kvm_hv_sint_route_destroy(HvSintRoute *sint_route);
-
-int kvm_hv_sint_route_set_sint(HvSintRoute *sint_route);
-
-uint32_t hyperv_vp_index(X86CPU *cpu);
-X86CPU *hyperv_find_vcpu(uint32_t vp_index);
+int hyperv_x86_synic_add(X86CPU *cpu);
+void hyperv_x86_synic_reset(X86CPU *cpu);
+void hyperv_x86_synic_update(X86CPU *cpu);
#endif
diff --git a/target/i386/kvm.c b/target/i386/kvm.c
index dc4047b02f..115d8b4c14 100644
--- a/target/i386/kvm.c
+++ b/target/i386/kvm.c
@@ -608,7 +608,8 @@ static bool hyperv_enabled(X86CPU *cpu)
cpu->hyperv_synic ||
cpu->hyperv_stimer ||
cpu->hyperv_reenlightenment ||
- cpu->hyperv_tlbflush);
+ cpu->hyperv_tlbflush ||
+ cpu->hyperv_ipi);
}
static int kvm_arch_set_tsc_khz(CPUState *cs)
@@ -733,9 +734,20 @@ static int hyperv_handle_properties(CPUState *cs)
env->features[FEAT_HYPERV_EAX] |= HV_VP_RUNTIME_AVAILABLE;
}
if (cpu->hyperv_synic) {
- if (!has_msr_hv_synic ||
- kvm_vcpu_enable_cap(cs, KVM_CAP_HYPERV_SYNIC, 0)) {
- fprintf(stderr, "Hyper-V SynIC is not supported by kernel\n");
+ unsigned int cap = KVM_CAP_HYPERV_SYNIC;
+ if (!cpu->hyperv_synic_kvm_only) {
+ if (!cpu->hyperv_vpindex) {
+ fprintf(stderr, "Hyper-V SynIC "
+ "(requested by 'hv-synic' cpu flag) "
+ "requires Hyper-V VP_INDEX ('hv-vpindex')\n");
+ return -ENOSYS;
+ }
+ cap = KVM_CAP_HYPERV_SYNIC2;
+ }
+
+ if (!has_msr_hv_synic || !kvm_check_extension(cs->kvm_state, cap)) {
+ fprintf(stderr, "Hyper-V SynIC (requested by 'hv-synic' cpu flag) "
+ "is not supported by kernel\n");
return -ENOSYS;
}
@@ -753,12 +765,14 @@ static int hyperv_handle_properties(CPUState *cs)
static int hyperv_init_vcpu(X86CPU *cpu)
{
+ CPUState *cs = CPU(cpu);
+ int ret;
+
if (cpu->hyperv_vpindex && !hv_vpindex_settable) {
/*
* the kernel doesn't support setting vp_index; assert that its value
* is in sync
*/
- int ret;
struct {
struct kvm_msrs info;
struct kvm_msr_entry entries[1];
@@ -767,18 +781,38 @@ static int hyperv_init_vcpu(X86CPU *cpu)
.entries[0].index = HV_X64_MSR_VP_INDEX,
};
- ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_MSRS, &msr_data);
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_MSRS, &msr_data);
if (ret < 0) {
return ret;
}
assert(ret == 1);
- if (msr_data.entries[0].data != hyperv_vp_index(cpu)) {
+ if (msr_data.entries[0].data != hyperv_vp_index(CPU(cpu))) {
error_report("kernel's vp_index != QEMU's vp_index");
return -ENXIO;
}
}
+ if (cpu->hyperv_synic) {
+ uint32_t synic_cap = cpu->hyperv_synic_kvm_only ?
+ KVM_CAP_HYPERV_SYNIC : KVM_CAP_HYPERV_SYNIC2;
+ ret = kvm_vcpu_enable_cap(cs, synic_cap, 0);
+ if (ret < 0) {
+ error_report("failed to turn on HyperV SynIC in KVM: %s",
+ strerror(-ret));
+ return ret;
+ }
+
+ if (!cpu->hyperv_synic_kvm_only) {
+ ret = hyperv_x86_synic_add(cpu);
+ if (ret < 0) {
+ error_report("failed to create HyperV SynIC: %s",
+ strerror(-ret));
+ return ret;
+ }
+ }
+ }
+
return 0;
}
@@ -888,6 +922,17 @@ int kvm_arch_init_vcpu(CPUState *cs)
c->eax |= HV_REMOTE_TLB_FLUSH_RECOMMENDED;
c->eax |= HV_EX_PROCESSOR_MASKS_RECOMMENDED;
}
+ if (cpu->hyperv_ipi) {
+ if (kvm_check_extension(cs->kvm_state,
+ KVM_CAP_HYPERV_SEND_IPI) <= 0) {
+ fprintf(stderr, "Hyper-V IPI send support "
+ "(requested by 'hv-ipi' cpu flag) "
+ " is not supported by kernel\n");
+ return -ENOSYS;
+ }
+ c->eax |= HV_CLUSTER_IPI_RECOMMENDED;
+ c->eax |= HV_EX_PROCESSOR_MASKS_RECOMMENDED;
+ }
c->ebx = cpu->hyperv_spinlock_attempts;
@@ -1153,7 +1198,7 @@ int kvm_arch_init_vcpu(CPUState *cs)
if (local_err) {
error_report_err(local_err);
error_free(invtsc_mig_blocker);
- goto fail;
+ return r;
}
/* for savevm */
vmstate_x86_cpu.unmigratable = 1;
@@ -1226,6 +1271,8 @@ void kvm_arch_reset_vcpu(X86CPU *cpu)
for (i = 0; i < ARRAY_SIZE(env->msr_hv_synic_sint); i++) {
env->msr_hv_synic_sint[i] = HV_SINT_MASKED;
}
+
+ hyperv_x86_synic_reset(cpu);
}
}
@@ -1937,7 +1984,8 @@ static int kvm_put_msrs(X86CPU *cpu, int level)
kvm_msr_entry_add(cpu, HV_X64_MSR_VP_RUNTIME, env->msr_hv_runtime);
}
if (cpu->hyperv_vpindex && hv_vpindex_settable) {
- kvm_msr_entry_add(cpu, HV_X64_MSR_VP_INDEX, hyperv_vp_index(cpu));
+ kvm_msr_entry_add(cpu, HV_X64_MSR_VP_INDEX,
+ hyperv_vp_index(CPU(cpu)));
}
if (cpu->hyperv_synic) {
int j;
@@ -2686,7 +2734,6 @@ static int kvm_put_vcpu_events(X86CPU *cpu, int level)
events.exception.nr = env->exception_injected;
events.exception.has_error_code = env->has_error_code;
events.exception.error_code = env->error_code;
- events.exception.pad = 0;
events.interrupt.injected = (env->interrupt_injected >= 0);
events.interrupt.nr = env->interrupt_injected;
@@ -2695,7 +2742,6 @@ static int kvm_put_vcpu_events(X86CPU *cpu, int level)
events.nmi.injected = env->nmi_injected;
events.nmi.pending = env->nmi_pending;
events.nmi.masked = !!(env->hflags2 & HF2_NMI_MASK);
- events.nmi.pad = 0;
events.sipi_vector = env->sipi_vector;
events.flags = 0;
diff --git a/target/i386/machine.c b/target/i386/machine.c
index 084c2c73a8..225b5d433b 100644
--- a/target/i386/machine.c
+++ b/target/i386/machine.c
@@ -7,6 +7,7 @@
#include "hw/i386/pc.h"
#include "hw/isa/isa.h"
#include "migration/cpu.h"
+#include "hyperv.h"
#include "sysemu/kvm.h"
@@ -672,11 +673,19 @@ static bool hyperv_synic_enable_needed(void *opaque)
return false;
}
+static int hyperv_synic_post_load(void *opaque, int version_id)
+{
+ X86CPU *cpu = opaque;
+ hyperv_x86_synic_update(cpu);
+ return 0;
+}
+
static const VMStateDescription vmstate_msr_hyperv_synic = {
.name = "cpu/msr_hyperv_synic",
.version_id = 1,
.minimum_version_id = 1,
.needed = hyperv_synic_enable_needed,
+ .post_load = hyperv_synic_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT64(env.msr_hv_synic_control, X86CPU),
VMSTATE_UINT64(env.msr_hv_synic_evt_page, X86CPU),
diff --git a/target/i386/mem_helper.c b/target/i386/mem_helper.c
index 30c26b9d9c..6cc53bcb40 100644
--- a/target/i386/mem_helper.c
+++ b/target/i386/mem_helper.c
@@ -23,6 +23,7 @@
#include "exec/exec-all.h"
#include "exec/cpu_ldst.h"
#include "qemu/int128.h"
+#include "qemu/atomic128.h"
#include "tcg.h"
void helper_cmpxchg8b_unlocked(CPUX86State *env, target_ulong a0)
@@ -137,10 +138,7 @@ void helper_cmpxchg16b(CPUX86State *env, target_ulong a0)
if ((a0 & 0xf) != 0) {
raise_exception_ra(env, EXCP0D_GPF, ra);
- } else {
-#ifndef CONFIG_ATOMIC128
- cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
-#else
+ } else if (HAVE_CMPXCHG128) {
int eflags = cpu_cc_compute_all(env, CC_OP);
Int128 cmpv = int128_make128(env->regs[R_EAX], env->regs[R_EDX]);
@@ -159,7 +157,8 @@ void helper_cmpxchg16b(CPUX86State *env, target_ulong a0)
eflags &= ~CC_Z;
}
CC_SRC = eflags;
-#endif
+ } else {
+ cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
}
}
#endif
diff --git a/target/ppc/helper.h b/target/ppc/helper.h
index ef64248bc4..7a1481fd0b 100644
--- a/target/ppc/helper.h
+++ b/target/ppc/helper.h
@@ -800,7 +800,7 @@ DEF_HELPER_4(dscliq, void, env, fprp, fprp, i32)
DEF_HELPER_1(tbegin, void, env)
DEF_HELPER_FLAGS_1(fixup_thrm, TCG_CALL_NO_RWG, void, env)
-#if defined(TARGET_PPC64) && defined(CONFIG_ATOMIC128)
+#ifdef TARGET_PPC64
DEF_HELPER_FLAGS_3(lq_le_parallel, TCG_CALL_NO_WG, i64, env, tl, i32)
DEF_HELPER_FLAGS_3(lq_be_parallel, TCG_CALL_NO_WG, i64, env, tl, i32)
DEF_HELPER_FLAGS_5(stq_le_parallel, TCG_CALL_NO_WG,
diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c
index 8f0d86d104..a1485fad9b 100644
--- a/target/ppc/mem_helper.c
+++ b/target/ppc/mem_helper.c
@@ -25,6 +25,7 @@
#include "exec/cpu_ldst.h"
#include "tcg.h"
#include "internal.h"
+#include "qemu/atomic128.h"
//#define DEBUG_OP
@@ -215,11 +216,15 @@ target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg,
return i;
}
-#if defined(TARGET_PPC64) && defined(CONFIG_ATOMIC128)
+#ifdef TARGET_PPC64
uint64_t helper_lq_le_parallel(CPUPPCState *env, target_ulong addr,
uint32_t opidx)
{
- Int128 ret = helper_atomic_ldo_le_mmu(env, addr, opidx, GETPC());
+ Int128 ret;
+
+ /* We will have raised EXCP_ATOMIC from the translator. */
+ assert(HAVE_ATOMIC128);
+ ret = helper_atomic_ldo_le_mmu(env, addr, opidx, GETPC());
env->retxh = int128_gethi(ret);
return int128_getlo(ret);
}
@@ -227,7 +232,11 @@ uint64_t helper_lq_le_parallel(CPUPPCState *env, target_ulong addr,
uint64_t helper_lq_be_parallel(CPUPPCState *env, target_ulong addr,
uint32_t opidx)
{
- Int128 ret = helper_atomic_ldo_be_mmu(env, addr, opidx, GETPC());
+ Int128 ret;
+
+ /* We will have raised EXCP_ATOMIC from the translator. */
+ assert(HAVE_ATOMIC128);
+ ret = helper_atomic_ldo_be_mmu(env, addr, opidx, GETPC());
env->retxh = int128_gethi(ret);
return int128_getlo(ret);
}
@@ -235,14 +244,22 @@ uint64_t helper_lq_be_parallel(CPUPPCState *env, target_ulong addr,
void helper_stq_le_parallel(CPUPPCState *env, target_ulong addr,
uint64_t lo, uint64_t hi, uint32_t opidx)
{
- Int128 val = int128_make128(lo, hi);
+ Int128 val;
+
+ /* We will have raised EXCP_ATOMIC from the translator. */
+ assert(HAVE_ATOMIC128);
+ val = int128_make128(lo, hi);
helper_atomic_sto_le_mmu(env, addr, val, opidx, GETPC());
}
void helper_stq_be_parallel(CPUPPCState *env, target_ulong addr,
uint64_t lo, uint64_t hi, uint32_t opidx)
{
- Int128 val = int128_make128(lo, hi);
+ Int128 val;
+
+ /* We will have raised EXCP_ATOMIC from the translator. */
+ assert(HAVE_ATOMIC128);
+ val = int128_make128(lo, hi);
helper_atomic_sto_be_mmu(env, addr, val, opidx, GETPC());
}
@@ -252,6 +269,9 @@ uint32_t helper_stqcx_le_parallel(CPUPPCState *env, target_ulong addr,
{
bool success = false;
+ /* We will have raised EXCP_ATOMIC from the translator. */
+ assert(HAVE_CMPXCHG128);
+
if (likely(addr == env->reserve_addr)) {
Int128 oldv, cmpv, newv;
@@ -271,6 +291,9 @@ uint32_t helper_stqcx_be_parallel(CPUPPCState *env, target_ulong addr,
{
bool success = false;
+ /* We will have raised EXCP_ATOMIC from the translator. */
+ assert(HAVE_CMPXCHG128);
+
if (likely(addr == env->reserve_addr)) {
Int128 oldv, cmpv, newv;
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index 881743571b..4e59dd5f42 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -33,6 +33,7 @@
#include "trace-tcg.h"
#include "exec/translator.h"
#include "exec/log.h"
+#include "qemu/atomic128.h"
#define CPU_SINGLE_STEP 0x1
@@ -2654,22 +2655,22 @@ static void gen_lq(DisasContext *ctx)
hi = cpu_gpr[rd];
if (tb_cflags(ctx->base.tb) & CF_PARALLEL) {
-#ifdef CONFIG_ATOMIC128
- TCGv_i32 oi = tcg_temp_new_i32();
- if (ctx->le_mode) {
- tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ, ctx->mem_idx));
- gen_helper_lq_le_parallel(lo, cpu_env, EA, oi);
+ if (HAVE_ATOMIC128) {
+ TCGv_i32 oi = tcg_temp_new_i32();
+ if (ctx->le_mode) {
+ tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ, ctx->mem_idx));
+ gen_helper_lq_le_parallel(lo, cpu_env, EA, oi);
+ } else {
+ tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ, ctx->mem_idx));
+ gen_helper_lq_be_parallel(lo, cpu_env, EA, oi);
+ }
+ tcg_temp_free_i32(oi);
+ tcg_gen_ld_i64(hi, cpu_env, offsetof(CPUPPCState, retxh));
} else {
- tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ, ctx->mem_idx));
- gen_helper_lq_be_parallel(lo, cpu_env, EA, oi);
+ /* Restart with exclusive lock. */
+ gen_helper_exit_atomic(cpu_env);
+ ctx->base.is_jmp = DISAS_NORETURN;
}
- tcg_temp_free_i32(oi);
- tcg_gen_ld_i64(hi, cpu_env, offsetof(CPUPPCState, retxh));
-#else
- /* Restart with exclusive lock. */
- gen_helper_exit_atomic(cpu_env);
- ctx->base.is_jmp = DISAS_NORETURN;
-#endif
} else if (ctx->le_mode) {
tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_LEQ);
gen_addr_add(ctx, EA, EA, 8);
@@ -2805,21 +2806,21 @@ static void gen_std(DisasContext *ctx)
hi = cpu_gpr[rs];
if (tb_cflags(ctx->base.tb) & CF_PARALLEL) {
-#ifdef CONFIG_ATOMIC128
- TCGv_i32 oi = tcg_temp_new_i32();
- if (ctx->le_mode) {
- tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ, ctx->mem_idx));
- gen_helper_stq_le_parallel(cpu_env, EA, lo, hi, oi);
+ if (HAVE_ATOMIC128) {
+ TCGv_i32 oi = tcg_temp_new_i32();
+ if (ctx->le_mode) {
+ tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ, ctx->mem_idx));
+ gen_helper_stq_le_parallel(cpu_env, EA, lo, hi, oi);
+ } else {
+ tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ, ctx->mem_idx));
+ gen_helper_stq_be_parallel(cpu_env, EA, lo, hi, oi);
+ }
+ tcg_temp_free_i32(oi);
} else {
- tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ, ctx->mem_idx));
- gen_helper_stq_be_parallel(cpu_env, EA, lo, hi, oi);
+ /* Restart with exclusive lock. */
+ gen_helper_exit_atomic(cpu_env);
+ ctx->base.is_jmp = DISAS_NORETURN;
}
- tcg_temp_free_i32(oi);
-#else
- /* Restart with exclusive lock. */
- gen_helper_exit_atomic(cpu_env);
- ctx->base.is_jmp = DISAS_NORETURN;
-#endif
} else if (ctx->le_mode) {
tcg_gen_qemu_st_i64(lo, EA, ctx->mem_idx, MO_LEQ);
gen_addr_add(ctx, EA, EA, 8);
@@ -3404,26 +3405,26 @@ static void gen_lqarx(DisasContext *ctx)
hi = cpu_gpr[rd];
if (tb_cflags(ctx->base.tb) & CF_PARALLEL) {
-#ifdef CONFIG_ATOMIC128
- TCGv_i32 oi = tcg_temp_new_i32();
- if (ctx->le_mode) {
- tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ | MO_ALIGN_16,
- ctx->mem_idx));
- gen_helper_lq_le_parallel(lo, cpu_env, EA, oi);
+ if (HAVE_ATOMIC128) {
+ TCGv_i32 oi = tcg_temp_new_i32();
+ if (ctx->le_mode) {
+ tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ | MO_ALIGN_16,
+ ctx->mem_idx));
+ gen_helper_lq_le_parallel(lo, cpu_env, EA, oi);
+ } else {
+ tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ | MO_ALIGN_16,
+ ctx->mem_idx));
+ gen_helper_lq_be_parallel(lo, cpu_env, EA, oi);
+ }
+ tcg_temp_free_i32(oi);
+ tcg_gen_ld_i64(hi, cpu_env, offsetof(CPUPPCState, retxh));
} else {
- tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ | MO_ALIGN_16,
- ctx->mem_idx));
- gen_helper_lq_be_parallel(lo, cpu_env, EA, oi);
+ /* Restart with exclusive lock. */
+ gen_helper_exit_atomic(cpu_env);
+ ctx->base.is_jmp = DISAS_NORETURN;
+ tcg_temp_free(EA);
+ return;
}
- tcg_temp_free_i32(oi);
- tcg_gen_ld_i64(hi, cpu_env, offsetof(CPUPPCState, retxh));
-#else
- /* Restart with exclusive lock. */
- gen_helper_exit_atomic(cpu_env);
- ctx->base.is_jmp = DISAS_NORETURN;
- tcg_temp_free(EA);
- return;
-#endif
} else if (ctx->le_mode) {
tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_LEQ | MO_ALIGN_16);
tcg_gen_mov_tl(cpu_reserve, EA);
@@ -3461,20 +3462,22 @@ static void gen_stqcx_(DisasContext *ctx)
hi = cpu_gpr[rs];
if (tb_cflags(ctx->base.tb) & CF_PARALLEL) {
- TCGv_i32 oi = tcg_const_i32(DEF_MEMOP(MO_Q) | MO_ALIGN_16);
-#ifdef CONFIG_ATOMIC128
- if (ctx->le_mode) {
- gen_helper_stqcx_le_parallel(cpu_crf[0], cpu_env, EA, lo, hi, oi);
+ if (HAVE_CMPXCHG128) {
+ TCGv_i32 oi = tcg_const_i32(DEF_MEMOP(MO_Q) | MO_ALIGN_16);
+ if (ctx->le_mode) {
+ gen_helper_stqcx_le_parallel(cpu_crf[0], cpu_env,
+ EA, lo, hi, oi);
+ } else {
+ gen_helper_stqcx_be_parallel(cpu_crf[0], cpu_env,
+ EA, lo, hi, oi);
+ }
+ tcg_temp_free_i32(oi);
} else {
- gen_helper_stqcx_le_parallel(cpu_crf[0], cpu_env, EA, lo, hi, oi);
+ /* Restart with exclusive lock. */
+ gen_helper_exit_atomic(cpu_env);
+ ctx->base.is_jmp = DISAS_NORETURN;
}
-#else
- /* Restart with exclusive lock. */
- gen_helper_exit_atomic(cpu_env);
- ctx->base.is_jmp = DISAS_NORETURN;
-#endif
tcg_temp_free(EA);
- tcg_temp_free_i32(oi);
} else {
TCGLabel *lab_fail = gen_new_label();
TCGLabel *lab_over = gen_new_label();
diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c
index bacae4f503..490c43e6e6 100644
--- a/target/s390x/mem_helper.c
+++ b/target/s390x/mem_helper.c
@@ -25,6 +25,7 @@
#include "exec/exec-all.h"
#include "exec/cpu_ldst.h"
#include "qemu/int128.h"
+#include "qemu/atomic128.h"
#if !defined(CONFIG_USER_ONLY)
#include "hw/s390x/storage-keys.h"
@@ -1379,65 +1380,62 @@ uint32_t HELPER(trXX)(CPUS390XState *env, uint32_t r1, uint32_t r2,
return cc;
}
-static void do_cdsg(CPUS390XState *env, uint64_t addr,
- uint32_t r1, uint32_t r3, bool parallel)
+void HELPER(cdsg)(CPUS390XState *env, uint64_t addr,
+ uint32_t r1, uint32_t r3)
{
uintptr_t ra = GETPC();
Int128 cmpv = int128_make128(env->regs[r1 + 1], env->regs[r1]);
Int128 newv = int128_make128(env->regs[r3 + 1], env->regs[r3]);
Int128 oldv;
+ uint64_t oldh, oldl;
bool fail;
- if (parallel) {
-#ifndef CONFIG_ATOMIC128
- cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
-#else
- int mem_idx = cpu_mmu_index(env, false);
- TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);
- oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra);
- fail = !int128_eq(oldv, cmpv);
-#endif
- } else {
- uint64_t oldh, oldl;
+ check_alignment(env, addr, 16, ra);
- check_alignment(env, addr, 16, ra);
+ oldh = cpu_ldq_data_ra(env, addr + 0, ra);
+ oldl = cpu_ldq_data_ra(env, addr + 8, ra);
- oldh = cpu_ldq_data_ra(env, addr + 0, ra);
- oldl = cpu_ldq_data_ra(env, addr + 8, ra);
-
- oldv = int128_make128(oldl, oldh);
- fail = !int128_eq(oldv, cmpv);
- if (fail) {
- newv = oldv;
- }
-
- cpu_stq_data_ra(env, addr + 0, int128_gethi(newv), ra);
- cpu_stq_data_ra(env, addr + 8, int128_getlo(newv), ra);
+ oldv = int128_make128(oldl, oldh);
+ fail = !int128_eq(oldv, cmpv);
+ if (fail) {
+ newv = oldv;
}
+ cpu_stq_data_ra(env, addr + 0, int128_gethi(newv), ra);
+ cpu_stq_data_ra(env, addr + 8, int128_getlo(newv), ra);
+
env->cc_op = fail;
env->regs[r1] = int128_gethi(oldv);
env->regs[r1 + 1] = int128_getlo(oldv);
}
-void HELPER(cdsg)(CPUS390XState *env, uint64_t addr,
- uint32_t r1, uint32_t r3)
-{
- do_cdsg(env, addr, r1, r3, false);
-}
-
void HELPER(cdsg_parallel)(CPUS390XState *env, uint64_t addr,
uint32_t r1, uint32_t r3)
{
- do_cdsg(env, addr, r1, r3, true);
+ uintptr_t ra = GETPC();
+ Int128 cmpv = int128_make128(env->regs[r1 + 1], env->regs[r1]);
+ Int128 newv = int128_make128(env->regs[r3 + 1], env->regs[r3]);
+ int mem_idx;
+ TCGMemOpIdx oi;
+ Int128 oldv;
+ bool fail;
+
+ assert(HAVE_CMPXCHG128);
+
+ mem_idx = cpu_mmu_index(env, false);
+ oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);
+ oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra);
+ fail = !int128_eq(oldv, cmpv);
+
+ env->cc_op = fail;
+ env->regs[r1] = int128_gethi(oldv);
+ env->regs[r1 + 1] = int128_getlo(oldv);
}
static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1,
uint64_t a2, bool parallel)
{
-#if !defined(CONFIG_USER_ONLY) || defined(CONFIG_ATOMIC128)
uint32_t mem_idx = cpu_mmu_index(env, false);
-#endif
uintptr_t ra = GETPC();
uint32_t fc = extract32(env->regs[0], 0, 8);
uint32_t sc = extract32(env->regs[0], 8, 8);
@@ -1465,18 +1463,20 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1,
probe_write(env, a2, 0, mem_idx, ra);
#endif
- /* Note that the compare-and-swap is atomic, and the store is atomic, but
- the complete operation is not. Therefore we do not need to assert serial
- context in order to implement this. That said, restart early if we can't
- support either operation that is supposed to be atomic. */
+ /*
+ * Note that the compare-and-swap is atomic, and the store is atomic,
+ * but the complete operation is not. Therefore we do not need to
+ * assert serial context in order to implement this. That said,
+ * restart early if we can't support either operation that is supposed
+ * to be atomic.
+ */
if (parallel) {
- int mask = 0;
-#if !defined(CONFIG_ATOMIC64)
- mask = -8;
-#elif !defined(CONFIG_ATOMIC128)
- mask = -16;
+ uint32_t max = 2;
+#ifdef CONFIG_ATOMIC64
+ max = 3;
#endif
- if (((4 << fc) | (1 << sc)) & mask) {
+ if ((HAVE_CMPXCHG128 ? 0 : fc + 2 > max) ||
+ (HAVE_ATOMIC128 ? 0 : sc > max)) {
cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
}
}
@@ -1546,16 +1546,7 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1,
Int128 cv = int128_make128(env->regs[r3 + 1], env->regs[r3]);
Int128 ov;
- if (parallel) {
-#ifdef CONFIG_ATOMIC128
- TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);
- ov = helper_atomic_cmpxchgo_be_mmu(env, a1, cv, nv, oi, ra);
- cc = !int128_eq(ov, cv);
-#else
- /* Note that we asserted !parallel above. */
- g_assert_not_reached();
-#endif
- } else {
+ if (!parallel) {
uint64_t oh = cpu_ldq_data_ra(env, a1 + 0, ra);
uint64_t ol = cpu_ldq_data_ra(env, a1 + 8, ra);
@@ -1567,6 +1558,13 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1,
cpu_stq_data_ra(env, a1 + 0, int128_gethi(nv), ra);
cpu_stq_data_ra(env, a1 + 8, int128_getlo(nv), ra);
+ } else if (HAVE_CMPXCHG128) {
+ TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);
+ ov = helper_atomic_cmpxchgo_be_mmu(env, a1, cv, nv, oi, ra);
+ cc = !int128_eq(ov, cv);
+ } else {
+ /* Note that we asserted !parallel above. */
+ g_assert_not_reached();
}
env->regs[r3 + 0] = int128_gethi(ov);
@@ -1596,18 +1594,16 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1,
cpu_stq_data_ra(env, a2, svh, ra);
break;
case 4:
- if (parallel) {
-#ifdef CONFIG_ATOMIC128
+ if (!parallel) {
+ cpu_stq_data_ra(env, a2 + 0, svh, ra);
+ cpu_stq_data_ra(env, a2 + 8, svl, ra);
+ } else if (HAVE_ATOMIC128) {
TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);
Int128 sv = int128_make128(svl, svh);
helper_atomic_sto_be_mmu(env, a2, sv, oi, ra);
-#else
+ } else {
/* Note that we asserted !parallel above. */
g_assert_not_reached();
-#endif
- } else {
- cpu_stq_data_ra(env, a2 + 0, svh, ra);
- cpu_stq_data_ra(env, a2 + 8, svl, ra);
}
break;
default:
@@ -2100,76 +2096,64 @@ uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
#endif
/* load pair from quadword */
-static uint64_t do_lpq(CPUS390XState *env, uint64_t addr, bool parallel)
+uint64_t HELPER(lpq)(CPUS390XState *env, uint64_t addr)
{
uintptr_t ra = GETPC();
uint64_t hi, lo;
- if (parallel) {
-#ifndef CONFIG_ATOMIC128
- cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
-#else
- int mem_idx = cpu_mmu_index(env, false);
- TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);
- Int128 v = helper_atomic_ldo_be_mmu(env, addr, oi, ra);
- hi = int128_gethi(v);
- lo = int128_getlo(v);
-#endif
- } else {
- check_alignment(env, addr, 16, ra);
-
- hi = cpu_ldq_data_ra(env, addr + 0, ra);
- lo = cpu_ldq_data_ra(env, addr + 8, ra);
- }
+ check_alignment(env, addr, 16, ra);
+ hi = cpu_ldq_data_ra(env, addr + 0, ra);
+ lo = cpu_ldq_data_ra(env, addr + 8, ra);
env->retxl = lo;
return hi;
}
-uint64_t HELPER(lpq)(CPUS390XState *env, uint64_t addr)
-{
- return do_lpq(env, addr, false);
-}
-
uint64_t HELPER(lpq_parallel)(CPUS390XState *env, uint64_t addr)
{
- return do_lpq(env, addr, true);
-}
-
-/* store pair to quadword */
-static void do_stpq(CPUS390XState *env, uint64_t addr,
- uint64_t low, uint64_t high, bool parallel)
-{
uintptr_t ra = GETPC();
+ uint64_t hi, lo;
+ int mem_idx;
+ TCGMemOpIdx oi;
+ Int128 v;
- if (parallel) {
-#ifndef CONFIG_ATOMIC128
- cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
-#else
- int mem_idx = cpu_mmu_index(env, false);
- TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);
+ assert(HAVE_ATOMIC128);
- Int128 v = int128_make128(low, high);
- helper_atomic_sto_be_mmu(env, addr, v, oi, ra);
-#endif
- } else {
- check_alignment(env, addr, 16, ra);
+ mem_idx = cpu_mmu_index(env, false);
+ oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);
+ v = helper_atomic_ldo_be_mmu(env, addr, oi, ra);
+ hi = int128_gethi(v);
+ lo = int128_getlo(v);
- cpu_stq_data_ra(env, addr + 0, high, ra);
- cpu_stq_data_ra(env, addr + 8, low, ra);
- }
+ env->retxl = lo;
+ return hi;
}
+/* store pair to quadword */
void HELPER(stpq)(CPUS390XState *env, uint64_t addr,
uint64_t low, uint64_t high)
{
- do_stpq(env, addr, low, high, false);
+ uintptr_t ra = GETPC();
+
+ check_alignment(env, addr, 16, ra);
+ cpu_stq_data_ra(env, addr + 0, high, ra);
+ cpu_stq_data_ra(env, addr + 8, low, ra);
}
void HELPER(stpq_parallel)(CPUS390XState *env, uint64_t addr,
uint64_t low, uint64_t high)
{
- do_stpq(env, addr, low, high, true);
+ uintptr_t ra = GETPC();
+ int mem_idx;
+ TCGMemOpIdx oi;
+ Int128 v;
+
+ assert(HAVE_ATOMIC128);
+
+ mem_idx = cpu_mmu_index(env, false);
+ oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);
+ v = int128_make128(low, high);
+ helper_atomic_sto_be_mmu(env, addr, v, oi, ra);
}
/* Execute instruction. This instruction executes an insn modified with
diff --git a/target/s390x/translate.c b/target/s390x/translate.c
index 18861cd186..b5bd56b7ee 100644
--- a/target/s390x/translate.c
+++ b/target/s390x/translate.c
@@ -44,6 +44,7 @@
#include "trace-tcg.h"
#include "exec/translator.h"
#include "exec/log.h"
+#include "qemu/atomic128.h"
/* Information that (most) every instruction needs to manipulate. */
@@ -1128,11 +1129,19 @@ struct DisasInsn {
const char *name;
+ /* Pre-process arguments before HELP_OP. */
void (*help_in1)(DisasContext *, DisasFields *, DisasOps *);
void (*help_in2)(DisasContext *, DisasFields *, DisasOps *);
void (*help_prep)(DisasContext *, DisasFields *, DisasOps *);
+
+ /*
+ * Post-process output after HELP_OP.
+ * Note that these are not called if HELP_OP returns DISAS_NORETURN.
+ */
void (*help_wout)(DisasContext *, DisasFields *, DisasOps *);
void (*help_cout)(DisasContext *, DisasOps *);
+
+ /* Implement the operation itself. */
DisasJumpType (*help_op)(DisasContext *, DisasOps *);
uint64_t data;
@@ -2032,6 +2041,7 @@ static DisasJumpType op_cdsg(DisasContext *s, DisasOps *o)
int r3 = get_field(s->fields, r3);
int d2 = get_field(s->fields, d2);
int b2 = get_field(s->fields, b2);
+ DisasJumpType ret = DISAS_NEXT;
TCGv_i64 addr;
TCGv_i32 t_r1, t_r3;
@@ -2039,17 +2049,20 @@ static DisasJumpType op_cdsg(DisasContext *s, DisasOps *o)
addr = get_address(s, 0, b2, d2);
t_r1 = tcg_const_i32(r1);
t_r3 = tcg_const_i32(r3);
- if (tb_cflags(s->base.tb) & CF_PARALLEL) {
+ if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) {
+ gen_helper_cdsg(cpu_env, addr, t_r1, t_r3);
+ } else if (HAVE_CMPXCHG128) {
gen_helper_cdsg_parallel(cpu_env, addr, t_r1, t_r3);
} else {
- gen_helper_cdsg(cpu_env, addr, t_r1, t_r3);
+ gen_helper_exit_atomic(cpu_env);
+ ret = DISAS_NORETURN;
}
tcg_temp_free_i64(addr);
tcg_temp_free_i32(t_r1);
tcg_temp_free_i32(t_r3);
set_cc_static(s);
- return DISAS_NEXT;
+ return ret;
}
static DisasJumpType op_csst(DisasContext *s, DisasOps *o)
@@ -3026,10 +3039,13 @@ static DisasJumpType op_lpd(DisasContext *s, DisasOps *o)
static DisasJumpType op_lpq(DisasContext *s, DisasOps *o)
{
- if (tb_cflags(s->base.tb) & CF_PARALLEL) {
+ if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) {
+ gen_helper_lpq(o->out, cpu_env, o->in2);
+ } else if (HAVE_ATOMIC128) {
gen_helper_lpq_parallel(o->out, cpu_env, o->in2);
} else {
- gen_helper_lpq(o->out, cpu_env, o->in2);
+ gen_helper_exit_atomic(cpu_env);
+ return DISAS_NORETURN;
}
return_low128(o->out2);
return DISAS_NEXT;
@@ -4406,10 +4422,13 @@ static DisasJumpType op_stmh(DisasContext *s, DisasOps *o)
static DisasJumpType op_stpq(DisasContext *s, DisasOps *o)
{
- if (tb_cflags(s->base.tb) & CF_PARALLEL) {
+ if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) {
+ gen_helper_stpq(cpu_env, o->in2, o->out2, o->out);
+ } else if (HAVE_ATOMIC128) {
gen_helper_stpq_parallel(cpu_env, o->in2, o->out2, o->out);
} else {
- gen_helper_stpq(cpu_env, o->in2, o->out2, o->out);
+ gen_helper_exit_atomic(cpu_env);
+ return DISAS_NORETURN;
}
return DISAS_NEXT;
}
@@ -6125,11 +6144,13 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s)
if (insn->help_op) {
ret = insn->help_op(s, &o);
}
- if (insn->help_wout) {
- insn->help_wout(s, &f, &o);
- }
- if (insn->help_cout) {
- insn->help_cout(s, &o);
+ if (ret != DISAS_NORETURN) {
+ if (insn->help_wout) {
+ insn->help_wout(s, &f, &o);
+ }
+ if (insn->help_cout) {
+ insn->help_cout(s, &o);
+ }
}
/* Free any temporaries created by the helpers. */
diff --git a/target/unicore32/cpu.c b/target/unicore32/cpu.c
index 68f978d80b..2b49d1ca40 100644
--- a/target/unicore32/cpu.c
+++ b/target/unicore32/cpu.c
@@ -116,8 +116,6 @@ static void uc32_cpu_initfn(Object *obj)
env->uncached_asr = ASR_MODE_PRIV;
env->regs[31] = 0x03000000;
#endif
-
- tlb_flush(cs);
}
static const VMStateDescription vmstate_uc32_cpu = {
diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c
index daa416a143..7a8015c5a9 100644
--- a/tcg/tcg-op.c
+++ b/tcg/tcg-op.c
@@ -2586,6 +2586,10 @@ void tcg_gen_exit_tb(TranslationBlock *tb, unsigned idx)
seen this numbered exit before, via tcg_gen_goto_tb. */
tcg_debug_assert(tcg_ctx->goto_tb_issue_mask & (1 << idx));
#endif
+ /* When not chaining, exit without indicating a link. */
+ if (qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
+ val = 0;
+ }
} else {
/* This is an exit via the exitreq label. */
tcg_debug_assert(idx == TB_EXIT_REQUESTED);
@@ -2603,7 +2607,10 @@ void tcg_gen_goto_tb(unsigned idx)
tcg_debug_assert((tcg_ctx->goto_tb_issue_mask & (1 << idx)) == 0);
tcg_ctx->goto_tb_issue_mask |= 1 << idx;
#endif
- tcg_gen_op1i(INDEX_op_goto_tb, idx);
+ /* When not chaining, we simply fall through to the "fallback" exit. */
+ if (!qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
+ tcg_gen_op1i(INDEX_op_goto_tb, idx);
+ }
}
void tcg_gen_lookup_and_goto_ptr(void)
diff --git a/tcg/tcg.c b/tcg/tcg.c
index f27b22bd3c..e85133ef05 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -30,6 +30,7 @@
/* Define to jump the ELF file used to communicate with GDB. */
#undef DEBUG_JIT
+#include "qemu/error-report.h"
#include "qemu/cutils.h"
#include "qemu/host-utils.h"
#include "qemu/timer.h"
@@ -3361,6 +3362,7 @@ void tcg_profile_snapshot(TCGProfile *prof, bool counters, bool table)
const TCGProfile *orig = &s->prof;
if (counters) {
+ PROF_ADD(prof, orig, cpu_exec_time);
PROF_ADD(prof, orig, tb_count1);
PROF_ADD(prof, orig, tb_count);
PROF_ADD(prof, orig, op_count);
@@ -3412,11 +3414,32 @@ void tcg_dump_op_count(FILE *f, fprintf_function cpu_fprintf)
prof.table_op_count[i]);
}
}
+
+int64_t tcg_cpu_exec_time(void)
+{
+ unsigned int n_ctxs = atomic_read(&n_tcg_ctxs);
+ unsigned int i;
+ int64_t ret = 0;
+
+ for (i = 0; i < n_ctxs; i++) {
+ const TCGContext *s = atomic_read(&tcg_ctxs[i]);
+ const TCGProfile *prof = &s->prof;
+
+ ret += atomic_read(&prof->cpu_exec_time);
+ }
+ return ret;
+}
#else
void tcg_dump_op_count(FILE *f, fprintf_function cpu_fprintf)
{
cpu_fprintf(f, "[TCG profiler not compiled]\n");
}
+
+int64_t tcg_cpu_exec_time(void)
+{
+ error_report("%s: TCG profiler not compiled", __func__);
+ exit(EXIT_FAILURE);
+}
#endif
@@ -3430,7 +3453,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb)
#ifdef CONFIG_PROFILER
{
- int n;
+ int n = 0;
QTAILQ_FOREACH(op, &s->ops, link) {
n++;
diff --git a/tcg/tcg.h b/tcg/tcg.h
index f9f12378e9..f4efbaa680 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -32,6 +32,7 @@
#include "qemu/queue.h"
#include "tcg-mo.h"
#include "tcg-target.h"
+#include "qemu/int128.h"
/* XXX: make safe guess about sizes */
#define MAX_OP_PER_INSTR 266
@@ -629,12 +630,13 @@ typedef struct TCGOp {
QEMU_BUILD_BUG_ON(NB_OPS > (1 << 8));
typedef struct TCGProfile {
+ int64_t cpu_exec_time;
int64_t tb_count1;
int64_t tb_count;
int64_t op_count; /* total insn count */
int op_count_max; /* max insn per TB */
- int64_t temp_count;
int temp_count_max;
+ int64_t temp_count;
int64_t del_op_count;
int64_t code_in_len;
int64_t code_out_len;
@@ -1002,6 +1004,7 @@ int tcg_check_temp_count(void);
#define tcg_check_temp_count() 0
#endif
+int64_t tcg_cpu_exec_time(void);
void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf);
void tcg_dump_op_count(FILE *f, fprintf_function cpu_fprintf);
@@ -1454,11 +1457,14 @@ GEN_ATOMIC_HELPER_ALL(xchg)
#undef GEN_ATOMIC_HELPER
#endif /* CONFIG_SOFTMMU */
-#ifdef CONFIG_ATOMIC128
-#include "qemu/int128.h"
-
-/* These aren't really a "proper" helpers because TCG cannot manage Int128.
- However, use the same format as the others, for use by the backends. */
+/*
+ * These aren't really a "proper" helpers because TCG cannot manage Int128.
+ * However, use the same format as the others, for use by the backends.
+ *
+ * The cmpxchg functions are only defined if HAVE_CMPXCHG128;
+ * the ld/st functions are only defined if HAVE_ATOMIC128,
+ * as defined by <qemu/atomic128.h>.
+ */
Int128 helper_atomic_cmpxchgo_le_mmu(CPUArchState *env, target_ulong addr,
Int128 cmpv, Int128 newv,
TCGMemOpIdx oi, uintptr_t retaddr);
@@ -1475,6 +1481,4 @@ void helper_atomic_sto_le_mmu(CPUArchState *env, target_ulong addr, Int128 val,
void helper_atomic_sto_be_mmu(CPUArchState *env, target_ulong addr, Int128 val,
TCGMemOpIdx oi, uintptr_t retaddr);
-#endif /* CONFIG_ATOMIC128 */
-
#endif /* TCG_H */
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 7fe8578972..f77a495109 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -115,7 +115,7 @@ check-unit-$(CONFIG_GNUTLS) += tests/test-io-channel-tls$(EXESUF)
check-unit-y += tests/test-io-channel-command$(EXESUF)
check-unit-y += tests/test-io-channel-buffer$(EXESUF)
check-unit-y += tests/test-base64$(EXESUF)
-check-unit-$(if $(CONFIG_NETTLE_KDF),y,$(CONFIG_GCRYPT_KDF)) += tests/test-crypto-pbkdf$(EXESUF)
+check-unit-$(if $(CONFIG_NETTLE),y,$(CONFIG_GCRYPT)) += tests/test-crypto-pbkdf$(EXESUF)
check-unit-y += tests/test-crypto-ivgen$(EXESUF)
check-unit-y += tests/test-crypto-afsplit$(EXESUF)
check-unit-y += tests/test-crypto-xts$(EXESUF)
diff --git a/tests/crypto-tls-x509-helpers.h b/tests/crypto-tls-x509-helpers.h
index 921341c649..88c30d7c94 100644
--- a/tests/crypto-tls-x509-helpers.h
+++ b/tests/crypto-tls-x509-helpers.h
@@ -22,8 +22,7 @@
#include <gnutls/x509.h>
#if !(defined WIN32) && \
- defined(CONFIG_TASN1) && \
- (LIBGNUTLS_VERSION_NUMBER >= 0x020600)
+ defined(CONFIG_TASN1)
# define QCRYPTO_HAVE_TLS_TEST_SUPPORT
#endif
diff --git a/tests/ptimer-test-stubs.c b/tests/ptimer-test-stubs.c
index ca5cc3b13b..54b3fd26f6 100644
--- a/tests/ptimer-test-stubs.c
+++ b/tests/ptimer-test-stubs.c
@@ -34,14 +34,19 @@ int64_t ptimer_test_time_ns;
int use_icount = 1;
bool qtest_allowed;
-void timer_init_tl(QEMUTimer *ts,
- QEMUTimerList *timer_list, int scale,
- QEMUTimerCB *cb, void *opaque)
+void timer_init_full(QEMUTimer *ts,
+ QEMUTimerListGroup *timer_list_group, QEMUClockType type,
+ int scale, int attributes,
+ QEMUTimerCB *cb, void *opaque)
{
- ts->timer_list = timer_list;
+ if (!timer_list_group) {
+ timer_list_group = &main_loop_tlg;
+ }
+ ts->timer_list = timer_list_group->tl[type];
ts->cb = cb;
ts->opaque = opaque;
ts->scale = scale;
+ ts->attributes = attributes;
ts->expire_time = -1;
}
diff --git a/tests/test-crypto-block.c b/tests/test-crypto-block.c
index fd29a045d2..fae4ffc453 100644
--- a/tests/test-crypto-block.c
+++ b/tests/test-crypto-block.c
@@ -29,7 +29,7 @@
#endif
#if (defined(_WIN32) || defined RUSAGE_THREAD) && \
- (defined(CONFIG_NETTLE_KDF) || defined(CONFIG_GCRYPT_KDF))
+ (defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT))
#define TEST_LUKS
#else
#undef TEST_LUKS
diff --git a/tests/test-crypto-tlscredsx509.c b/tests/test-crypto-tlscredsx509.c
index 30f9ac4bbf..940a026c6e 100644
--- a/tests/test-crypto-tlscredsx509.c
+++ b/tests/test-crypto-tlscredsx509.c
@@ -283,14 +283,8 @@ int main(int argc, char **argv)
true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
0, 0);
- /* Technically a CA cert with basic constraints
- * key purpose == key signing + non-critical should
- * be rejected. GNUTLS < 3.1 does not reject it and
- * we don't anticipate them changing this behaviour
- */
TLS_TEST_REG(badca1, true, cacert4req.filename, servercert4req.filename,
- (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR >= 1) ||
- GNUTLS_VERSION_MAJOR > 3);
+ true);
TLS_TEST_REG(badca2, true,
cacert5req.filename, servercert5req.filename, true);
TLS_TEST_REG(badca3, true,
diff --git a/ui/input.c b/ui/input.c
index dd7f6d7f21..7c9a4109c4 100644
--- a/ui/input.c
+++ b/ui/input.c
@@ -271,7 +271,7 @@ static void qemu_input_queue_process(void *opaque)
item = QTAILQ_FIRST(queue);
switch (item->type) {
case QEMU_INPUT_QUEUE_DELAY:
- timer_mod(item->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_EXT)
+ timer_mod(item->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)
+ item->delay_ms);
return;
case QEMU_INPUT_QUEUE_EVENT:
@@ -301,7 +301,7 @@ static void qemu_input_queue_delay(struct QemuInputEventQueueHead *queue,
queue_count++;
if (start_timer) {
- timer_mod(item->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_EXT)
+ timer_mod(item->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)
+ item->delay_ms);
}
}
@@ -448,8 +448,9 @@ void qemu_input_event_send_key_delay(uint32_t delay_ms)
}
if (!kbd_timer) {
- kbd_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL_EXT,
- qemu_input_queue_process, &kbd_queue);
+ kbd_timer = timer_new_full(NULL, QEMU_CLOCK_VIRTUAL,
+ SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL,
+ qemu_input_queue_process, &kbd_queue);
}
if (queue_count < queue_limit) {
qemu_input_queue_delay(&kbd_queue, kbd_timer,
diff --git a/util/qemu-timer.c b/util/qemu-timer.c
index eb60d8f73a..1cc1b2f2c3 100644
--- a/util/qemu-timer.c
+++ b/util/qemu-timer.c
@@ -339,14 +339,19 @@ int qemu_poll_ns(GPollFD *fds, guint nfds, int64_t timeout)
}
-void timer_init_tl(QEMUTimer *ts,
- QEMUTimerList *timer_list, int scale,
- QEMUTimerCB *cb, void *opaque)
+void timer_init_full(QEMUTimer *ts,
+ QEMUTimerListGroup *timer_list_group, QEMUClockType type,
+ int scale, int attributes,
+ QEMUTimerCB *cb, void *opaque)
{
- ts->timer_list = timer_list;
+ if (!timer_list_group) {
+ timer_list_group = &main_loop_tlg;
+ }
+ ts->timer_list = timer_list_group->tl[type];
ts->cb = cb;
ts->opaque = opaque;
ts->scale = scale;
+ ts->attributes = attributes;
ts->expire_time = -1;
}
@@ -484,6 +489,7 @@ bool timerlist_run_timers(QEMUTimerList *timer_list)
bool progress = false;
QEMUTimerCB *cb;
void *opaque;
+ bool need_replay_checkpoint = false;
if (!atomic_read(&timer_list->active_timers)) {
return false;
@@ -496,12 +502,18 @@ bool timerlist_run_timers(QEMUTimerList *timer_list)
switch (timer_list->clock->type) {
case QEMU_CLOCK_REALTIME:
- case QEMU_CLOCK_VIRTUAL_EXT:
break;
default:
case QEMU_CLOCK_VIRTUAL:
- if (!replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL)) {
- goto out;
+ if (replay_mode != REPLAY_MODE_NONE) {
+ /* Checkpoint for virtual clock is redundant in cases where
+ * it's being triggered with only non-EXTERNAL timers, because
+ * these timers don't change guest state directly.
+ * Since it has conditional dependence on specific timers, it is
+ * subject to race conditions and requires special handling.
+ * See below.
+ */
+ need_replay_checkpoint = true;
}
break;
case QEMU_CLOCK_HOST:
@@ -516,14 +528,39 @@ bool timerlist_run_timers(QEMUTimerList *timer_list)
break;
}
+ /*
+ * Extract expired timers from active timers list and and process them.
+ *
+ * In rr mode we need "filtered" checkpointing for virtual clock. The
+ * checkpoint must be recorded/replayed before processing any non-EXTERNAL timer,
+ * and that must only be done once since the clock value stays the same. Because
+ * non-EXTERNAL timers may appear in the timers list while it being processed,
+ * the checkpoint can be issued at a time until no timers are left and we are
+ * done".
+ */
current_time = qemu_clock_get_ns(timer_list->clock->type);
- for(;;) {
- qemu_mutex_lock(&timer_list->active_timers_lock);
- ts = timer_list->active_timers;
+ qemu_mutex_lock(&timer_list->active_timers_lock);
+ while ((ts = timer_list->active_timers)) {
if (!timer_expired_ns(ts, current_time)) {
- qemu_mutex_unlock(&timer_list->active_timers_lock);
+ /* No expired timers left. The checkpoint can be skipped
+ * if no timers fired or they were all external.
+ */
break;
}
+ if (need_replay_checkpoint
+ && !(ts->attributes & QEMU_TIMER_ATTR_EXTERNAL)) {
+ /* once we got here, checkpoint clock only once */
+ need_replay_checkpoint = false;
+ qemu_mutex_unlock(&timer_list->active_timers_lock);
+ if (!replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL)) {
+ goto out;
+ }
+ qemu_mutex_lock(&timer_list->active_timers_lock);
+ /* The lock was released; start over again in case the list was
+ * modified.
+ */
+ continue;
+ }
/* remove timer from the list before calling the callback */
timer_list->active_timers = ts->next;
@@ -531,12 +568,15 @@ bool timerlist_run_timers(QEMUTimerList *timer_list)
ts->expire_time = -1;
cb = ts->cb;
opaque = ts->opaque;
- qemu_mutex_unlock(&timer_list->active_timers_lock);
/* run the callback (the timer list can be modified) */
+ qemu_mutex_unlock(&timer_list->active_timers_lock);
cb(opaque);
+ qemu_mutex_lock(&timer_list->active_timers_lock);
+
progress = true;
}
+ qemu_mutex_unlock(&timer_list->active_timers_lock);
out:
qemu_event_set(&timer_list->timers_done_ev);
@@ -598,7 +638,6 @@ int64_t qemu_clock_get_ns(QEMUClockType type)
return get_clock();
default:
case QEMU_CLOCK_VIRTUAL:
- case QEMU_CLOCK_VIRTUAL_EXT:
if (use_icount) {
return cpu_get_icount();
} else {
diff --git a/vl.c b/vl.c
index d53e2563e9..b2a405f80f 100644
--- a/vl.c
+++ b/vl.c
@@ -147,8 +147,15 @@ bool enable_cpu_pm = false;
int nb_nics;
NICInfo nd_table[MAX_NICS];
int autostart;
-static int rtc_utc = 1;
-static int rtc_date_offset = -1; /* -1 means no change */
+static enum {
+ RTC_BASE_UTC,
+ RTC_BASE_LOCALTIME,
+ RTC_BASE_DATETIME,
+} rtc_base_type = RTC_BASE_UTC;
+static time_t rtc_ref_start_datetime;
+static int rtc_realtime_clock_offset; /* used only with QEMU_CLOCK_REALTIME */
+static int rtc_host_datetime_offset = -1; /* valid & used only with
+ RTC_BASE_DATETIME */
QEMUClockType rtc_clock;
int vga_interface_type = VGA_NONE;
static DisplayOptions dpy;
@@ -242,6 +249,7 @@ static struct {
static QemuOptsList qemu_rtc_opts = {
.name = "rtc",
.head = QTAILQ_HEAD_INITIALIZER(qemu_rtc_opts.head),
+ .merge_lists = true,
.desc = {
{
.name = "base",
@@ -780,28 +788,42 @@ void qemu_system_vmstop_request(RunState state)
}
/***********************************************************/
-/* real time host monotonic timer */
-
-static time_t qemu_time(void)
+/* RTC reference time/date access */
+static time_t qemu_ref_timedate(QEMUClockType clock)
{
- return qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000;
+ time_t value = qemu_clock_get_ms(clock) / 1000;
+ switch (clock) {
+ case QEMU_CLOCK_REALTIME:
+ value -= rtc_realtime_clock_offset;
+ /* no break */
+ case QEMU_CLOCK_VIRTUAL:
+ value += rtc_ref_start_datetime;
+ break;
+ case QEMU_CLOCK_HOST:
+ if (rtc_base_type == RTC_BASE_DATETIME) {
+ value -= rtc_host_datetime_offset;
+ }
+ break;
+ default:
+ assert(0);
+ }
+ return value;
}
-/***********************************************************/
-/* host time/date access */
void qemu_get_timedate(struct tm *tm, int offset)
{
- time_t ti = qemu_time();
+ time_t ti = qemu_ref_timedate(rtc_clock);
ti += offset;
- if (rtc_date_offset == -1) {
- if (rtc_utc)
- gmtime_r(&ti, tm);
- else
- localtime_r(&ti, tm);
- } else {
- ti -= rtc_date_offset;
+
+ switch (rtc_base_type) {
+ case RTC_BASE_DATETIME:
+ case RTC_BASE_UTC:
gmtime_r(&ti, tm);
+ break;
+ case RTC_BASE_LOCALTIME:
+ localtime_r(&ti, tm);
+ break;
}
}
@@ -809,23 +831,28 @@ int qemu_timedate_diff(struct tm *tm)
{
time_t seconds;
- if (rtc_date_offset == -1)
- if (rtc_utc)
- seconds = mktimegm(tm);
- else {
- struct tm tmp = *tm;
- tmp.tm_isdst = -1; /* use timezone to figure it out */
- seconds = mktime(&tmp);
- }
- else
- seconds = mktimegm(tm) + rtc_date_offset;
-
- return seconds - qemu_time();
+ switch (rtc_base_type) {
+ case RTC_BASE_DATETIME:
+ case RTC_BASE_UTC:
+ seconds = mktimegm(tm);
+ break;
+ case RTC_BASE_LOCALTIME:
+ {
+ struct tm tmp = *tm;
+ tmp.tm_isdst = -1; /* use timezone to figure it out */
+ seconds = mktime(&tmp);
+ break;
+ }
+ default:
+ abort();
+ }
+
+ return seconds - qemu_ref_timedate(QEMU_CLOCK_HOST);
}
-static void configure_rtc_date_offset(const char *startdate)
+static void configure_rtc_base_datetime(const char *startdate)
{
- time_t rtc_start_date;
+ time_t rtc_start_datetime;
struct tm tm;
if (sscanf(startdate, "%d-%d-%dT%d:%d:%d", &tm.tm_year, &tm.tm_mon,
@@ -841,33 +868,40 @@ static void configure_rtc_date_offset(const char *startdate)
}
tm.tm_year -= 1900;
tm.tm_mon--;
- rtc_start_date = mktimegm(&tm);
- if (rtc_start_date == -1) {
+ rtc_start_datetime = mktimegm(&tm);
+ if (rtc_start_datetime == -1) {
date_fail:
- error_report("invalid date format");
+ error_report("invalid datetime format");
error_printf("valid formats: "
"'2006-06-17T16:01:21' or '2006-06-17'\n");
exit(1);
}
- rtc_date_offset = qemu_time() - rtc_start_date;
+ rtc_host_datetime_offset = rtc_ref_start_datetime - rtc_start_datetime;
+ rtc_ref_start_datetime = rtc_start_datetime;
}
static void configure_rtc(QemuOpts *opts)
{
const char *value;
+ /* Set defaults */
+ rtc_clock = QEMU_CLOCK_HOST;
+ rtc_ref_start_datetime = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000;
+ rtc_realtime_clock_offset = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000;
+
value = qemu_opt_get(opts, "base");
if (value) {
if (!strcmp(value, "utc")) {
- rtc_utc = 1;
+ rtc_base_type = RTC_BASE_UTC;
} else if (!strcmp(value, "localtime")) {
Error *blocker = NULL;
- rtc_utc = 0;
+ rtc_base_type = RTC_BASE_LOCALTIME;
error_setg(&blocker, QERR_REPLAY_NOT_SUPPORTED,
"-rtc base=localtime");
replay_add_blocker(blocker);
} else {
- configure_rtc_date_offset(value);
+ rtc_base_type = RTC_BASE_DATETIME;
+ configure_rtc_base_datetime(value);
}
}
value = qemu_opt_get(opts, "clock");
@@ -3009,7 +3043,6 @@ int main(int argc, char **argv, char **envp)
error_reportf_err(err, "cannot initialize crypto: ");
exit(1);
}
- rtc_clock = QEMU_CLOCK_HOST;
QLIST_INIT (&vm_change_state_head);
os_setup_early_signal_handling();
@@ -3729,7 +3762,6 @@ int main(int argc, char **argv, char **envp)
if (!opts) {
exit(1);
}
- configure_rtc(opts);
break;
case QEMU_OPTION_tb_size:
#ifndef CONFIG_TCG
@@ -3947,6 +3979,8 @@ int main(int argc, char **argv, char **envp)
exit(EXIT_FAILURE);
}
+ configure_rtc(qemu_find_opts_singleton("rtc"));
+
machine_class = select_machine();
set_memory_options(&ram_slots, &maxram_size, machine_class);
@@ -4342,8 +4376,6 @@ int main(int argc, char **argv, char **envp)
#endif
}
- colo_info_init();
-
if (net_init_clients(&err) < 0) {
error_report_err(err);
exit(1);