* Reverse debugging (Pavel)
* CFLAGS cleanup (Paolo) * ASLR fix (Mark) * cpus.c refactoring (Claudio) -----BEGIN PGP SIGNATURE----- iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAl98EB0UHHBib256aW5p QHJlZGhhdC5jb20ACgkQv/vSX3jHroOCsQf9G7EUAK1zcEOx20LtDdXFrk4tjsRp S83OGdihWe8SM+XiY9BfqsBbXdByqF+SitePOV3feGK0mOP5vtJIL7/2DLrtFTeF wOeARRA9ePVb7hcL5oXAQeE3bXrX8wq8Qtw9xAoHdw5JAEVmKIEJS6AL5Eu3M2Fh pvdBoV84pOm2/ARS3eRstRyW8gCC8rdLDlNsVDtCbYdNVq+VdkzR0l5Phc8JDx1M Qjdl1KpN6ZkuN8M6tnaQNTb9IUVu5c1tu5jdR6JdLUqAWp1wYZJ6r2jSatZWfLR3 H+gzFsDoLPfCjZ3IhfZyvzF5leSZmdbFfzI0tHS1UJ/ZZYjutDvlPlbyYA== =Jys5 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/bonzini-gitlab/tags/for-upstream' into staging * Reverse debugging (Pavel) * CFLAGS cleanup (Paolo) * ASLR fix (Mark) * cpus.c refactoring (Claudio) # gpg: Signature made Tue 06 Oct 2020 07:35:09 BST # gpg: using RSA key F13338574B662389866C7682BFFBD25F78C7AE83 # gpg: issuer "pbonzini@redhat.com" # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full] # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" [full] # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * remotes/bonzini-gitlab/tags/for-upstream: (37 commits) tests/acceptance: add reverse debugging test replay: create temporary snapshot at debugger connection replay: describe reverse debugging in docs/replay.txt gdbstub: add reverse continue support in replay mode gdbstub: add reverse step support in replay mode replay: flush rr queue before loading the vmstate replay: implement replay-seek command replay: introduce breakpoint at the specified step replay: introduce info hmp/qmp command qapi: introduce replay.json for record/replay-related stuff migration: introduce icount field for snapshots qcow2: introduce icount field for snapshots replay: provide an accessor for rr filename replay: don't record interrupt poll configure: don't enable ASLR for --enable-debug Windows builds configure: consistently pass CFLAGS/CXXFLAGS/LDFLAGS to meson configure: do not clobber environment CFLAGS/CXXFLAGS/LDFLAGS dtc: Convert Makefile bits to meson bits slirp: Convert Makefile bits to meson bits accel/tcg: use current_machine as it is always set for softmmu ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
f2687fdb75
@ -451,6 +451,7 @@ WHPX CPUs
|
||||
M: Sunil Muthuswamy <sunilmut@microsoft.com>
|
||||
S: Supported
|
||||
F: target/i386/whpx-all.c
|
||||
F: target/i386/whpx-cpus.c
|
||||
F: target/i386/whp-dispatch.h
|
||||
F: accel/stubs/whpx-stub.c
|
||||
F: include/sysemu/whpx.h
|
||||
@ -2321,6 +2322,8 @@ F: softmmu/vl.c
|
||||
F: softmmu/main.c
|
||||
F: softmmu/cpus.c
|
||||
F: softmmu/cpu-throttle.c
|
||||
F: softmmu/cpu-timers.c
|
||||
F: softmmu/icount.c
|
||||
F: qapi/run-state.json
|
||||
|
||||
Read, Copy, Update (RCU)
|
||||
@ -2490,7 +2493,7 @@ M: Laurent Vivier <lvivier@redhat.com>
|
||||
R: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Maintained
|
||||
F: softmmu/qtest.c
|
||||
F: accel/qtest.c
|
||||
F: accel/qtest/
|
||||
F: tests/qtest/
|
||||
X: tests/qtest/bios-tables-test-allowed-diff.h
|
||||
|
||||
@ -2693,6 +2696,8 @@ F: include/sysemu/replay.h
|
||||
F: docs/replay.txt
|
||||
F: stubs/replay.c
|
||||
F: tests/acceptance/replay_kernel.py
|
||||
F: tests/acceptance/reverse_debugging.py
|
||||
F: qapi/replay.json
|
||||
|
||||
IOVA Tree
|
||||
M: Peter Xu <peterx@redhat.com>
|
||||
|
28
Makefile
28
Makefile
@ -142,33 +142,7 @@ SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet)
|
||||
include $(SRC_PATH)/tests/Makefile.include
|
||||
|
||||
all: recurse-all
|
||||
Makefile: $(addsuffix /all, $(SUBDIRS))
|
||||
|
||||
# LIBFDT_lib="": avoid breaking existing trees with objects requiring -fPIC
|
||||
DTC_MAKE_ARGS=-I$(SRC_PATH)/dtc VPATH=$(SRC_PATH)/dtc -C dtc V="$(V)" LIBFDT_lib=""
|
||||
DTC_CFLAGS=$(CFLAGS) $(QEMU_CFLAGS)
|
||||
DTC_CPPFLAGS=-I$(SRC_PATH)/dtc/libfdt
|
||||
|
||||
.PHONY: dtc/all
|
||||
dtc/all: .git-submodule-status dtc/libfdt
|
||||
$(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(QEMU_LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt,)
|
||||
|
||||
dtc/%: .git-submodule-status
|
||||
@mkdir -p $@
|
||||
|
||||
# Retain for a while so that incremental build across this patch
|
||||
# does not raise an error for missing target "capstone/all", which
|
||||
# comes from the saved SUBDIRS value.
|
||||
.PHONY: capstone/all
|
||||
capstone/all:
|
||||
|
||||
.PHONY: slirp/all
|
||||
slirp/all: .git-submodule-status
|
||||
$(call quiet-command,$(MAKE) -C $(SRC_PATH)/slirp \
|
||||
BUILD_DIR="$(BUILD_DIR)/slirp" \
|
||||
PKG_CONFIG="$(PKG_CONFIG)" \
|
||||
CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" \
|
||||
CFLAGS="$(QEMU_CFLAGS) $(CFLAGS)" LDFLAGS="$(QEMU_LDFLAGS)")
|
||||
Makefile:
|
||||
|
||||
ROM_DIRS = $(addprefix pc-bios/, $(ROMS))
|
||||
ROM_DIRS_RULES=$(foreach t, all clean, $(addsuffix /$(t), $(ROM_DIRS)))
|
||||
|
@ -44,6 +44,9 @@
|
||||
#include "qapi/qapi-types-common.h"
|
||||
#include "qapi/qapi-visit-common.h"
|
||||
#include "sysemu/reset.h"
|
||||
#include "qemu/guest-random.h"
|
||||
#include "sysemu/hw_accel.h"
|
||||
#include "kvm-cpus.h"
|
||||
|
||||
#include "hw/boards.h"
|
||||
|
||||
@ -378,7 +381,7 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int kvm_destroy_vcpu(CPUState *cpu)
|
||||
static int do_kvm_destroy_vcpu(CPUState *cpu)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
long mmap_size;
|
||||
@ -412,6 +415,14 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void kvm_destroy_vcpu(CPUState *cpu)
|
||||
{
|
||||
if (do_kvm_destroy_vcpu(cpu) < 0) {
|
||||
error_report("kvm_destroy_vcpu failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static int kvm_get_vcpu(KVMState *s, unsigned long vcpu_id)
|
||||
{
|
||||
struct KVMParkedVcpu *cpu;
|
||||
@ -430,17 +441,18 @@ static int kvm_get_vcpu(KVMState *s, unsigned long vcpu_id)
|
||||
return kvm_vm_ioctl(s, KVM_CREATE_VCPU, (void *)vcpu_id);
|
||||
}
|
||||
|
||||
int kvm_init_vcpu(CPUState *cpu)
|
||||
int kvm_init_vcpu(CPUState *cpu, Error **errp)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
long mmap_size;
|
||||
int ret;
|
||||
|
||||
DPRINTF("kvm_init_vcpu\n");
|
||||
trace_kvm_init_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu));
|
||||
|
||||
ret = kvm_get_vcpu(s, kvm_arch_vcpu_id(cpu));
|
||||
if (ret < 0) {
|
||||
DPRINTF("kvm_create_vcpu failed\n");
|
||||
error_setg_errno(errp, -ret, "kvm_init_vcpu: kvm_get_vcpu failed (%lu)",
|
||||
kvm_arch_vcpu_id(cpu));
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -451,7 +463,8 @@ int kvm_init_vcpu(CPUState *cpu)
|
||||
mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0);
|
||||
if (mmap_size < 0) {
|
||||
ret = mmap_size;
|
||||
DPRINTF("KVM_GET_VCPU_MMAP_SIZE failed\n");
|
||||
error_setg_errno(errp, -mmap_size,
|
||||
"kvm_init_vcpu: KVM_GET_VCPU_MMAP_SIZE failed");
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -459,7 +472,9 @@ int kvm_init_vcpu(CPUState *cpu)
|
||||
cpu->kvm_fd, 0);
|
||||
if (cpu->kvm_run == MAP_FAILED) {
|
||||
ret = -errno;
|
||||
DPRINTF("mmap'ing vcpu state failed\n");
|
||||
error_setg_errno(errp, ret,
|
||||
"kvm_init_vcpu: mmap'ing vcpu state failed (%lu)",
|
||||
kvm_arch_vcpu_id(cpu));
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -469,6 +484,11 @@ int kvm_init_vcpu(CPUState *cpu)
|
||||
}
|
||||
|
||||
ret = kvm_arch_init_vcpu(cpu);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"kvm_init_vcpu: kvm_arch_init_vcpu failed (%lu)",
|
||||
kvm_arch_vcpu_id(cpu));
|
||||
}
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
@ -2232,6 +2252,7 @@ static int kvm_init(MachineState *ms)
|
||||
assert(!ret);
|
||||
}
|
||||
|
||||
cpus_register_accel(&kvm_cpus);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
|
84
accel/kvm/kvm-cpus.c
Normal file
84
accel/kvm/kvm-cpus.c
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* QEMU KVM support
|
||||
*
|
||||
* Copyright IBM, Corp. 2008
|
||||
* Red Hat, Inc. 2008
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
* Glauber Costa <gcosta@redhat.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/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "sysemu/kvm_int.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "qemu/guest-random.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
#include "kvm-cpus.h"
|
||||
|
||||
static void *kvm_vcpu_thread_fn(void *arg)
|
||||
{
|
||||
CPUState *cpu = arg;
|
||||
int r;
|
||||
|
||||
rcu_register_thread();
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
qemu_thread_get_self(cpu->thread);
|
||||
cpu->thread_id = qemu_get_thread_id();
|
||||
cpu->can_do_io = 1;
|
||||
current_cpu = cpu;
|
||||
|
||||
r = kvm_init_vcpu(cpu, &error_fatal);
|
||||
kvm_init_cpu_signals(cpu);
|
||||
|
||||
/* signal CPU creation */
|
||||
cpu_thread_signal_created(cpu);
|
||||
qemu_guest_random_seed_thread_part2(cpu->random_seed);
|
||||
|
||||
do {
|
||||
if (cpu_can_run(cpu)) {
|
||||
r = kvm_cpu_exec(cpu);
|
||||
if (r == EXCP_DEBUG) {
|
||||
cpu_handle_guest_debug(cpu);
|
||||
}
|
||||
}
|
||||
qemu_wait_io_event(cpu);
|
||||
} while (!cpu->unplug || cpu_can_run(cpu));
|
||||
|
||||
kvm_destroy_vcpu(cpu);
|
||||
cpu_thread_signal_destroyed(cpu);
|
||||
qemu_mutex_unlock_iothread();
|
||||
rcu_unregister_thread();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void kvm_start_vcpu_thread(CPUState *cpu)
|
||||
{
|
||||
char thread_name[VCPU_THREAD_NAME_SIZE];
|
||||
|
||||
cpu->thread = g_malloc0(sizeof(QemuThread));
|
||||
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
|
||||
qemu_cond_init(cpu->halt_cond);
|
||||
snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/KVM",
|
||||
cpu->cpu_index);
|
||||
qemu_thread_create(cpu->thread, thread_name, kvm_vcpu_thread_fn,
|
||||
cpu, QEMU_THREAD_JOINABLE);
|
||||
}
|
||||
|
||||
const CpusAccel kvm_cpus = {
|
||||
.create_vcpu_thread = kvm_start_vcpu_thread,
|
||||
|
||||
.synchronize_post_reset = kvm_cpu_synchronize_post_reset,
|
||||
.synchronize_post_init = kvm_cpu_synchronize_post_init,
|
||||
.synchronize_state = kvm_cpu_synchronize_state,
|
||||
.synchronize_pre_loadvm = kvm_cpu_synchronize_pre_loadvm,
|
||||
};
|
24
accel/kvm/kvm-cpus.h
Normal file
24
accel/kvm/kvm-cpus.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Accelerator CPUS Interface
|
||||
*
|
||||
* Copyright 2020 SUSE LLC
|
||||
*
|
||||
* 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 KVM_CPUS_H
|
||||
#define KVM_CPUS_H
|
||||
|
||||
#include "sysemu/cpus.h"
|
||||
|
||||
extern const CpusAccel kvm_cpus;
|
||||
|
||||
int kvm_init_vcpu(CPUState *cpu, Error **errp);
|
||||
int kvm_cpu_exec(CPUState *cpu);
|
||||
void kvm_destroy_vcpu(CPUState *cpu);
|
||||
void kvm_cpu_synchronize_post_reset(CPUState *cpu);
|
||||
void kvm_cpu_synchronize_post_init(CPUState *cpu);
|
||||
void kvm_cpu_synchronize_pre_loadvm(CPUState *cpu);
|
||||
|
||||
#endif /* KVM_CPUS_H */
|
@ -1,5 +1,8 @@
|
||||
kvm_ss = ss.source_set()
|
||||
kvm_ss.add(files('kvm-all.c'))
|
||||
kvm_ss.add(files(
|
||||
'kvm-all.c',
|
||||
'kvm-cpus.c',
|
||||
))
|
||||
kvm_ss.add(when: 'CONFIG_SEV', if_false: files('sev-stub.c'))
|
||||
|
||||
specific_ss.add_all(when: 'CONFIG_KVM', if_true: kvm_ss)
|
||||
|
@ -8,6 +8,7 @@ kvm_run_exit(int cpu_index, uint32_t reason) "cpu_index %d, reason %d"
|
||||
kvm_device_ioctl(int fd, int type, void *arg) "dev fd %d, type 0x%x, arg %p"
|
||||
kvm_failed_reg_get(uint64_t id, const char *msg) "Warning: Unable to retrieve ONEREG %" PRIu64 " from KVM: %s"
|
||||
kvm_failed_reg_set(uint64_t id, const char *msg) "Warning: Unable to set ONEREG %" PRIu64 " to KVM: %s"
|
||||
kvm_init_vcpu(int cpu_index, unsigned long arch_cpu_id) "index: %d id: %lu"
|
||||
kvm_irqchip_commit_routes(void) ""
|
||||
kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d"
|
||||
kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d"
|
||||
|
@ -1,6 +1,6 @@
|
||||
softmmu_ss.add(files('accel.c'))
|
||||
specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_POSIX'], if_true: files('qtest.c'))
|
||||
|
||||
subdir('qtest')
|
||||
subdir('kvm')
|
||||
subdir('tcg')
|
||||
subdir('xen')
|
||||
|
7
accel/qtest/meson.build
Normal file
7
accel/qtest/meson.build
Normal file
@ -0,0 +1,7 @@
|
||||
qtest_ss = ss.source_set()
|
||||
qtest_ss.add(files(
|
||||
'qtest.c',
|
||||
'qtest-cpus.c',
|
||||
))
|
||||
|
||||
specific_ss.add_all(when: ['CONFIG_SOFTMMU', 'CONFIG_POSIX'], if_true: qtest_ss)
|
91
accel/qtest/qtest-cpus.c
Normal file
91
accel/qtest/qtest-cpus.c
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* QTest accelerator code
|
||||
*
|
||||
* Copyright IBM, Corp. 2011
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.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/rcu.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "sysemu/accel.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "qemu/guest-random.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "hw/core/cpu.h"
|
||||
|
||||
#include "qtest-cpus.h"
|
||||
|
||||
static void *qtest_cpu_thread_fn(void *arg)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
error_report("qtest is not supported under Windows");
|
||||
exit(1);
|
||||
#else
|
||||
CPUState *cpu = arg;
|
||||
sigset_t waitset;
|
||||
int r;
|
||||
|
||||
rcu_register_thread();
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
qemu_thread_get_self(cpu->thread);
|
||||
cpu->thread_id = qemu_get_thread_id();
|
||||
cpu->can_do_io = 1;
|
||||
current_cpu = cpu;
|
||||
|
||||
sigemptyset(&waitset);
|
||||
sigaddset(&waitset, SIG_IPI);
|
||||
|
||||
/* signal CPU creation */
|
||||
cpu_thread_signal_created(cpu);
|
||||
qemu_guest_random_seed_thread_part2(cpu->random_seed);
|
||||
|
||||
do {
|
||||
qemu_mutex_unlock_iothread();
|
||||
do {
|
||||
int sig;
|
||||
r = sigwait(&waitset, &sig);
|
||||
} while (r == -1 && (errno == EAGAIN || errno == EINTR));
|
||||
if (r == -1) {
|
||||
perror("sigwait");
|
||||
exit(1);
|
||||
}
|
||||
qemu_mutex_lock_iothread();
|
||||
qemu_wait_io_event(cpu);
|
||||
} while (!cpu->unplug);
|
||||
|
||||
qemu_mutex_unlock_iothread();
|
||||
rcu_unregister_thread();
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void qtest_start_vcpu_thread(CPUState *cpu)
|
||||
{
|
||||
char thread_name[VCPU_THREAD_NAME_SIZE];
|
||||
|
||||
cpu->thread = g_malloc0(sizeof(QemuThread));
|
||||
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
|
||||
qemu_cond_init(cpu->halt_cond);
|
||||
snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/DUMMY",
|
||||
cpu->cpu_index);
|
||||
qemu_thread_create(cpu->thread, thread_name, qtest_cpu_thread_fn, cpu,
|
||||
QEMU_THREAD_JOINABLE);
|
||||
}
|
||||
|
||||
const CpusAccel qtest_cpus = {
|
||||
.create_vcpu_thread = qtest_start_vcpu_thread,
|
||||
.get_virtual_clock = qtest_get_virtual_clock,
|
||||
};
|
17
accel/qtest/qtest-cpus.h
Normal file
17
accel/qtest/qtest-cpus.h
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Accelerator CPUS Interface
|
||||
*
|
||||
* Copyright 2020 SUSE LLC
|
||||
*
|
||||
* 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 QTEST_CPUS_H
|
||||
#define QTEST_CPUS_H
|
||||
|
||||
#include "sysemu/cpus.h"
|
||||
|
||||
extern const CpusAccel qtest_cpus;
|
||||
|
||||
#endif /* QTEST_CPUS_H */
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/rcu.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
@ -19,14 +20,16 @@
|
||||
#include "sysemu/accel.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "qemu/guest-random.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "hw/core/cpu.h"
|
||||
|
||||
#include "qtest-cpus.h"
|
||||
|
||||
static int qtest_init_accel(MachineState *ms)
|
||||
{
|
||||
QemuOpts *opts = qemu_opts_create(qemu_find_opts("icount"), NULL, 0,
|
||||
&error_abort);
|
||||
qemu_opt_set(opts, "shift", "0", &error_abort);
|
||||
configure_icount(opts, &error_abort);
|
||||
qemu_opts_del(opts);
|
||||
cpus_register_accel(&qtest_cpus);
|
||||
return 0;
|
||||
}
|
||||
|
@ -21,13 +21,3 @@ int hax_sync_vcpus(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hax_init_vcpu(CPUState *cpu)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int hax_smp_cpu_exec(CPUState *cpu)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
@ -1,30 +0,0 @@
|
||||
/*
|
||||
* QEMU HVF support
|
||||
*
|
||||
* Copyright 2017 Red Hat, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2 or later, as published by the Free Software Foundation,
|
||||
* and may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "sysemu/hvf.h"
|
||||
|
||||
int hvf_init_vcpu(CPUState *cpu)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int hvf_vcpu_exec(CPUState *cpu)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
void hvf_vcpu_destroy(CPUState *cpu)
|
||||
{
|
||||
}
|
@ -32,16 +32,6 @@ bool kvm_readonly_mem_allowed;
|
||||
bool kvm_ioeventfd_any_length_allowed;
|
||||
bool kvm_msi_use_devid;
|
||||
|
||||
int kvm_destroy_vcpu(CPUState *cpu)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int kvm_init_vcpu(CPUState *cpu)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
void kvm_flush_coalesced_mmio_buffer(void)
|
||||
{
|
||||
}
|
||||
@ -50,19 +40,6 @@ void kvm_cpu_synchronize_state(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
|
||||
void kvm_cpu_synchronize_post_reset(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
|
||||
void kvm_cpu_synchronize_post_init(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
|
||||
int kvm_cpu_exec(CPUState *cpu)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
bool kvm_has_sync_mmu(void)
|
||||
{
|
||||
return false;
|
||||
|
@ -1,6 +1,4 @@
|
||||
specific_ss.add(when: 'CONFIG_HAX', if_false: files('hax-stub.c'))
|
||||
specific_ss.add(when: 'CONFIG_XEN', if_false: files('xen-stub.c'))
|
||||
specific_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c'))
|
||||
specific_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c'))
|
||||
specific_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c'))
|
||||
specific_ss.add(when: 'CONFIG_WHPX', if_false: files('whpx-stub.c'))
|
||||
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* QEMU Windows Hypervisor Platform accelerator (WHPX) stub
|
||||
*
|
||||
* Copyright Microsoft Corp. 2017
|
||||
*
|
||||
* 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 "cpu.h"
|
||||
#include "sysemu/whpx.h"
|
||||
|
||||
int whpx_init_vcpu(CPUState *cpu)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int whpx_vcpu_exec(CPUState *cpu)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
void whpx_destroy_vcpu(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
|
||||
void whpx_vcpu_kick(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
|
||||
void whpx_cpu_synchronize_state(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
|
||||
void whpx_cpu_synchronize_post_reset(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
|
||||
void whpx_cpu_synchronize_post_init(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
|
||||
void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu)
|
||||
{
|
||||
}
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/qemu-print.h"
|
||||
#include "cpu.h"
|
||||
#include "trace.h"
|
||||
#include "disas/disas.h"
|
||||
@ -36,6 +37,8 @@
|
||||
#include "hw/i386/apic.h"
|
||||
#endif
|
||||
#include "sysemu/cpus.h"
|
||||
#include "exec/cpu-all.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "sysemu/replay.h"
|
||||
|
||||
/* -icount align implementation. */
|
||||
@ -56,6 +59,9 @@ typedef struct SyncClocks {
|
||||
#define MAX_DELAY_PRINT_RATE 2000000000LL
|
||||
#define MAX_NB_PRINTS 100
|
||||
|
||||
static int64_t max_delay;
|
||||
static int64_t max_advance;
|
||||
|
||||
static void align_clocks(SyncClocks *sc, CPUState *cpu)
|
||||
{
|
||||
int64_t cpu_icount;
|
||||
@ -65,7 +71,7 @@ static void align_clocks(SyncClocks *sc, CPUState *cpu)
|
||||
}
|
||||
|
||||
cpu_icount = cpu->icount_extra + cpu_neg(cpu)->icount_decr.u16.low;
|
||||
sc->diff_clk += cpu_icount_to_ns(sc->last_cpu_icount - cpu_icount);
|
||||
sc->diff_clk += icount_to_ns(sc->last_cpu_icount - cpu_icount);
|
||||
sc->last_cpu_icount = cpu_icount;
|
||||
|
||||
if (sc->diff_clk > VM_CLOCK_ADVANCE) {
|
||||
@ -98,9 +104,9 @@ static void print_delay(const SyncClocks *sc)
|
||||
(-sc->diff_clk / (float)1000000000LL <
|
||||
(threshold_delay - THRESHOLD_REDUCE))) {
|
||||
threshold_delay = (-sc->diff_clk / 1000000000LL) + 1;
|
||||
printf("Warning: The guest is now late by %.1f to %.1f seconds\n",
|
||||
threshold_delay - 1,
|
||||
threshold_delay);
|
||||
qemu_printf("Warning: The guest is now late by %.1f to %.1f seconds\n",
|
||||
threshold_delay - 1,
|
||||
threshold_delay);
|
||||
nb_prints++;
|
||||
last_realtime_clock = sc->realtime_clock;
|
||||
}
|
||||
@ -430,8 +436,7 @@ static inline bool cpu_handle_halt(CPUState *cpu)
|
||||
{
|
||||
if (cpu->halted) {
|
||||
#if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY)
|
||||
if ((cpu->interrupt_request & CPU_INTERRUPT_POLL)
|
||||
&& replay_interrupt()) {
|
||||
if (cpu->interrupt_request & CPU_INTERRUPT_POLL) {
|
||||
X86CPU *x86_cpu = X86_CPU(cpu);
|
||||
qemu_mutex_lock_iothread();
|
||||
apic_poll_irq(x86_cpu->apic_state);
|
||||
@ -527,6 +532,20 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* CPU_INTERRUPT_POLL is a virtual event which gets converted into a
|
||||
* "real" interrupt event later. It does not need to be recorded for
|
||||
* replay purposes.
|
||||
*/
|
||||
static inline bool need_replay_interrupt(int interrupt_request)
|
||||
{
|
||||
#if defined(TARGET_I386)
|
||||
return !(interrupt_request & CPU_INTERRUPT_POLL);
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
TranslationBlock **last_tb)
|
||||
{
|
||||
@ -588,7 +607,9 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
and via longjmp via cpu_loop_exit. */
|
||||
else {
|
||||
if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
|
||||
replay_interrupt();
|
||||
if (need_replay_interrupt(interrupt_request)) {
|
||||
replay_interrupt();
|
||||
}
|
||||
/*
|
||||
* After processing the interrupt, ensure an EXCP_DEBUG is
|
||||
* raised when single-stepping so that GDB doesn't miss the
|
||||
@ -615,7 +636,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
|
||||
/* Finally, check if we need to exit to the main loop. */
|
||||
if (unlikely(qatomic_read(&cpu->exit_request))
|
||||
|| (use_icount
|
||||
|| (icount_enabled()
|
||||
&& cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra == 0)) {
|
||||
qatomic_set(&cpu->exit_request, 0);
|
||||
if (cpu->exception_index == -1) {
|
||||
@ -656,10 +677,10 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
|
||||
}
|
||||
|
||||
/* Instruction counter expired. */
|
||||
assert(use_icount);
|
||||
assert(icount_enabled());
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
/* Ensure global icount has gone forward */
|
||||
cpu_update_icount(cpu);
|
||||
icount_update(cpu);
|
||||
/* Refill decrementer and continue execution. */
|
||||
insns_left = MIN(0xffff, cpu->icount_budget);
|
||||
cpu_neg(cpu)->icount_decr.u16.low = insns_left;
|
||||
@ -759,3 +780,26 @@ int cpu_exec(CPUState *cpu)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
|
||||
void dump_drift_info(void)
|
||||
{
|
||||
if (!icount_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_printf("Host - Guest clock %"PRIi64" ms\n",
|
||||
(cpu_get_clock() - icount_get()) / SCALE_MS);
|
||||
if (icount_align_option) {
|
||||
qemu_printf("Max guest delay %"PRIi64" ms\n",
|
||||
-max_delay / SCALE_MS);
|
||||
qemu_printf("Max guest advance %"PRIi64" ms\n",
|
||||
max_advance / SCALE_MS);
|
||||
} else {
|
||||
qemu_printf("Max guest delay NA\n");
|
||||
qemu_printf("Max guest advance NA\n");
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
|
@ -12,4 +12,4 @@ tcg_ss.add(when: 'CONFIG_SOFTMMU', if_false: files('user-exec-stub.c'))
|
||||
tcg_ss.add(when: 'CONFIG_PLUGIN', if_true: [files('plugin-gen.c'), libdl])
|
||||
specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_ss)
|
||||
|
||||
specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files('tcg-all.c', 'cputlb.c'))
|
||||
specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files('tcg-all.c', 'cputlb.c', 'tcg-cpus.c'))
|
||||
|
@ -24,17 +24,15 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/accel.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/tcg.h"
|
||||
#include "qom/object.h"
|
||||
#include "cpu.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/boards.h"
|
||||
#include "qapi/qapi-builtin-visit.h"
|
||||
#include "tcg-cpus.h"
|
||||
|
||||
struct TCGState {
|
||||
AccelState parent_obj;
|
||||
@ -49,31 +47,6 @@ typedef struct TCGState TCGState;
|
||||
DECLARE_INSTANCE_CHECKER(TCGState, TCG_STATE,
|
||||
TYPE_TCG_ACCEL)
|
||||
|
||||
/* mask must never be zero, except for A20 change call */
|
||||
static void tcg_handle_interrupt(CPUState *cpu, int mask)
|
||||
{
|
||||
int old_mask;
|
||||
g_assert(qemu_mutex_iothread_locked());
|
||||
|
||||
old_mask = cpu->interrupt_request;
|
||||
cpu->interrupt_request |= mask;
|
||||
|
||||
/*
|
||||
* If called from iothread context, wake the target cpu in
|
||||
* case its halted.
|
||||
*/
|
||||
if (!qemu_cpu_is_self(cpu)) {
|
||||
qemu_cpu_kick(cpu);
|
||||
} else {
|
||||
qatomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1);
|
||||
if (use_icount &&
|
||||
!cpu->can_do_io
|
||||
&& (mask & ~old_mask) != 0) {
|
||||
cpu_abort(cpu, "Raised interrupt while not in I/O function");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We default to false if we know other options have been enabled
|
||||
* which are currently incompatible with MTTCG. Otherwise when each
|
||||
@ -105,7 +78,7 @@ static bool check_tcg_memory_orders_compatible(void)
|
||||
|
||||
static bool default_mttcg_enabled(void)
|
||||
{
|
||||
if (use_icount || TCG_OVERSIZED_GUEST) {
|
||||
if (icount_enabled() || TCG_OVERSIZED_GUEST) {
|
||||
return false;
|
||||
} else {
|
||||
#ifdef TARGET_SUPPORTS_MTTCG
|
||||
@ -123,13 +96,16 @@ static void tcg_accel_instance_init(Object *obj)
|
||||
s->mttcg_enabled = default_mttcg_enabled();
|
||||
}
|
||||
|
||||
bool mttcg_enabled;
|
||||
|
||||
static int tcg_init(MachineState *ms)
|
||||
{
|
||||
TCGState *s = TCG_STATE(current_accel());
|
||||
|
||||
tcg_exec_init(s->tb_size * 1024 * 1024);
|
||||
cpu_interrupt_handler = tcg_handle_interrupt;
|
||||
mttcg_enabled = s->mttcg_enabled;
|
||||
cpus_register_accel(&tcg_cpus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -147,7 +123,7 @@ static void tcg_set_thread(Object *obj, const char *value, Error **errp)
|
||||
if (strcmp(value, "multi") == 0) {
|
||||
if (TCG_OVERSIZED_GUEST) {
|
||||
error_setg(errp, "No MTTCG when guest word size > hosts");
|
||||
} else if (use_icount) {
|
||||
} else if (icount_enabled()) {
|
||||
error_setg(errp, "No MTTCG when icount is enabled");
|
||||
} else {
|
||||
#ifndef TARGET_SUPPORTS_MTTCG
|
||||
|
570
accel/tcg/tcg-cpus.c
Normal file
570
accel/tcg/tcg-cpus.c
Normal file
@ -0,0 +1,570 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
* Copyright (c) 2014 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/tcg.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/guest-random.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "hw/boards.h"
|
||||
|
||||
#include "tcg-cpus.h"
|
||||
|
||||
/* Kick all RR vCPUs */
|
||||
static void qemu_cpu_kick_rr_cpus(void)
|
||||
{
|
||||
CPUState *cpu;
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
cpu_exit(cpu);
|
||||
};
|
||||
}
|
||||
|
||||
static void tcg_kick_vcpu_thread(CPUState *cpu)
|
||||
{
|
||||
if (qemu_tcg_mttcg_enabled()) {
|
||||
cpu_exit(cpu);
|
||||
} else {
|
||||
qemu_cpu_kick_rr_cpus();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TCG vCPU kick timer
|
||||
*
|
||||
* The kick timer is responsible for moving single threaded vCPU
|
||||
* emulation on to the next vCPU. If more than one vCPU is running a
|
||||
* timer event with force a cpu->exit so the next vCPU can get
|
||||
* scheduled.
|
||||
*
|
||||
* The timer is removed if all vCPUs are idle and restarted again once
|
||||
* idleness is complete.
|
||||
*/
|
||||
|
||||
static QEMUTimer *tcg_kick_vcpu_timer;
|
||||
static CPUState *tcg_current_rr_cpu;
|
||||
|
||||
#define TCG_KICK_PERIOD (NANOSECONDS_PER_SECOND / 10)
|
||||
|
||||
static inline int64_t qemu_tcg_next_kick(void)
|
||||
{
|
||||
return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + TCG_KICK_PERIOD;
|
||||
}
|
||||
|
||||
/* Kick the currently round-robin scheduled vCPU to next */
|
||||
static void qemu_cpu_kick_rr_next_cpu(void)
|
||||
{
|
||||
CPUState *cpu;
|
||||
do {
|
||||
cpu = qatomic_mb_read(&tcg_current_rr_cpu);
|
||||
if (cpu) {
|
||||
cpu_exit(cpu);
|
||||
}
|
||||
} while (cpu != qatomic_mb_read(&tcg_current_rr_cpu));
|
||||
}
|
||||
|
||||
static void kick_tcg_thread(void *opaque)
|
||||
{
|
||||
timer_mod(tcg_kick_vcpu_timer, qemu_tcg_next_kick());
|
||||
qemu_cpu_kick_rr_next_cpu();
|
||||
}
|
||||
|
||||
static void start_tcg_kick_timer(void)
|
||||
{
|
||||
assert(!mttcg_enabled);
|
||||
if (!tcg_kick_vcpu_timer && CPU_NEXT(first_cpu)) {
|
||||
tcg_kick_vcpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||
kick_tcg_thread, NULL);
|
||||
}
|
||||
if (tcg_kick_vcpu_timer && !timer_pending(tcg_kick_vcpu_timer)) {
|
||||
timer_mod(tcg_kick_vcpu_timer, qemu_tcg_next_kick());
|
||||
}
|
||||
}
|
||||
|
||||
static void stop_tcg_kick_timer(void)
|
||||
{
|
||||
assert(!mttcg_enabled);
|
||||
if (tcg_kick_vcpu_timer && timer_pending(tcg_kick_vcpu_timer)) {
|
||||
timer_del(tcg_kick_vcpu_timer);
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_tcg_destroy_vcpu(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
|
||||
static void qemu_tcg_rr_wait_io_event(void)
|
||||
{
|
||||
CPUState *cpu;
|
||||
|
||||
while (all_cpu_threads_idle()) {
|
||||
stop_tcg_kick_timer();
|
||||
qemu_cond_wait_iothread(first_cpu->halt_cond);
|
||||
}
|
||||
|
||||
start_tcg_kick_timer();
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
qemu_wait_io_event_common(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
static int64_t tcg_get_icount_limit(void)
|
||||
{
|
||||
int64_t deadline;
|
||||
|
||||
if (replay_mode != REPLAY_MODE_PLAY) {
|
||||
/*
|
||||
* Include all the timers, because they may need an attention.
|
||||
* Too long CPU execution may create unnecessary delay in UI.
|
||||
*/
|
||||
deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
|
||||
QEMU_TIMER_ATTR_ALL);
|
||||
/* Check realtime timers, because they help with input processing */
|
||||
deadline = qemu_soonest_timeout(deadline,
|
||||
qemu_clock_deadline_ns_all(QEMU_CLOCK_REALTIME,
|
||||
QEMU_TIMER_ATTR_ALL));
|
||||
|
||||
/*
|
||||
* Maintain prior (possibly buggy) behaviour where if no deadline
|
||||
* was set (as there is no QEMU_CLOCK_VIRTUAL timer) or it is more than
|
||||
* INT32_MAX nanoseconds ahead, we still use INT32_MAX
|
||||
* nanoseconds.
|
||||
*/
|
||||
if ((deadline < 0) || (deadline > INT32_MAX)) {
|
||||
deadline = INT32_MAX;
|
||||
}
|
||||
|
||||
return icount_round(deadline);
|
||||
} else {
|
||||
return replay_get_instructions();
|
||||
}
|
||||
}
|
||||
|
||||
static void notify_aio_contexts(void)
|
||||
{
|
||||
/* Wake up other AioContexts. */
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
|
||||
static void handle_icount_deadline(void)
|
||||
{
|
||||
assert(qemu_in_vcpu_thread());
|
||||
if (icount_enabled()) {
|
||||
int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
|
||||
QEMU_TIMER_ATTR_ALL);
|
||||
|
||||
if (deadline == 0) {
|
||||
notify_aio_contexts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void prepare_icount_for_run(CPUState *cpu)
|
||||
{
|
||||
if (icount_enabled()) {
|
||||
int insns_left;
|
||||
|
||||
/*
|
||||
* These should always be cleared by process_icount_data after
|
||||
* each vCPU execution. However u16.high can be raised
|
||||
* asynchronously by cpu_exit/cpu_interrupt/tcg_handle_interrupt
|
||||
*/
|
||||
g_assert(cpu_neg(cpu)->icount_decr.u16.low == 0);
|
||||
g_assert(cpu->icount_extra == 0);
|
||||
|
||||
cpu->icount_budget = tcg_get_icount_limit();
|
||||
insns_left = MIN(0xffff, cpu->icount_budget);
|
||||
cpu_neg(cpu)->icount_decr.u16.low = insns_left;
|
||||
cpu->icount_extra = cpu->icount_budget - insns_left;
|
||||
|
||||
replay_mutex_lock();
|
||||
|
||||
if (cpu->icount_budget == 0 && replay_has_checkpoint()) {
|
||||
notify_aio_contexts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void process_icount_data(CPUState *cpu)
|
||||
{
|
||||
if (icount_enabled()) {
|
||||
/* Account for executed instructions */
|
||||
icount_update(cpu);
|
||||
|
||||
/* Reset the counters */
|
||||
cpu_neg(cpu)->icount_decr.u16.low = 0;
|
||||
cpu->icount_extra = 0;
|
||||
cpu->icount_budget = 0;
|
||||
|
||||
replay_account_executed_instructions();
|
||||
|
||||
replay_mutex_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
static int tcg_cpu_exec(CPUState *cpu)
|
||||
{
|
||||
int ret;
|
||||
#ifdef CONFIG_PROFILER
|
||||
int64_t ti;
|
||||
#endif
|
||||
|
||||
assert(tcg_enabled());
|
||||
#ifdef CONFIG_PROFILER
|
||||
ti = profile_getclock();
|
||||
#endif
|
||||
cpu_exec_start(cpu);
|
||||
ret = cpu_exec(cpu);
|
||||
cpu_exec_end(cpu);
|
||||
#ifdef CONFIG_PROFILER
|
||||
qatomic_set(&tcg_ctx->prof.cpu_exec_time,
|
||||
tcg_ctx->prof.cpu_exec_time + profile_getclock() - ti);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy any remaining vCPUs which have been unplugged and have
|
||||
* finished running
|
||||
*/
|
||||
static void deal_with_unplugged_cpus(void)
|
||||
{
|
||||
CPUState *cpu;
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
if (cpu->unplug && !cpu_can_run(cpu)) {
|
||||
qemu_tcg_destroy_vcpu(cpu);
|
||||
cpu_thread_signal_destroyed(cpu);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Single-threaded TCG
|
||||
*
|
||||
* In the single-threaded case each vCPU is simulated in turn. If
|
||||
* there is more than a single vCPU we create a simple timer to kick
|
||||
* the vCPU and ensure we don't get stuck in a tight loop in one vCPU.
|
||||
* This is done explicitly rather than relying on side-effects
|
||||
* elsewhere.
|
||||
*/
|
||||
|
||||
static void *tcg_rr_cpu_thread_fn(void *arg)
|
||||
{
|
||||
CPUState *cpu = arg;
|
||||
|
||||
assert(tcg_enabled());
|
||||
rcu_register_thread();
|
||||
tcg_register_thread();
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
qemu_thread_get_self(cpu->thread);
|
||||
|
||||
cpu->thread_id = qemu_get_thread_id();
|
||||
cpu->can_do_io = 1;
|
||||
cpu_thread_signal_created(cpu);
|
||||
qemu_guest_random_seed_thread_part2(cpu->random_seed);
|
||||
|
||||
/* wait for initial kick-off after machine start */
|
||||
while (first_cpu->stopped) {
|
||||
qemu_cond_wait_iothread(first_cpu->halt_cond);
|
||||
|
||||
/* process any pending work */
|
||||
CPU_FOREACH(cpu) {
|
||||
current_cpu = cpu;
|
||||
qemu_wait_io_event_common(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
start_tcg_kick_timer();
|
||||
|
||||
cpu = first_cpu;
|
||||
|
||||
/* process any pending work */
|
||||
cpu->exit_request = 1;
|
||||
|
||||
while (1) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
replay_mutex_lock();
|
||||
qemu_mutex_lock_iothread();
|
||||
/* Account partial waits to QEMU_CLOCK_VIRTUAL. */
|
||||
icount_account_warp_timer();
|
||||
|
||||
/*
|
||||
* Run the timers here. This is much more efficient than
|
||||
* waking up the I/O thread and waiting for completion.
|
||||
*/
|
||||
handle_icount_deadline();
|
||||
|
||||
replay_mutex_unlock();
|
||||
|
||||
if (!cpu) {
|
||||
cpu = first_cpu;
|
||||
}
|
||||
|
||||
while (cpu && cpu_work_list_empty(cpu) && !cpu->exit_request) {
|
||||
|
||||
qatomic_mb_set(&tcg_current_rr_cpu, cpu);
|
||||
current_cpu = cpu;
|
||||
|
||||
qemu_clock_enable(QEMU_CLOCK_VIRTUAL,
|
||||
(cpu->singlestep_enabled & SSTEP_NOTIMER) == 0);
|
||||
|
||||
if (cpu_can_run(cpu)) {
|
||||
int r;
|
||||
|
||||
qemu_mutex_unlock_iothread();
|
||||
prepare_icount_for_run(cpu);
|
||||
|
||||
r = tcg_cpu_exec(cpu);
|
||||
|
||||
process_icount_data(cpu);
|
||||
qemu_mutex_lock_iothread();
|
||||
|
||||
if (r == EXCP_DEBUG) {
|
||||
cpu_handle_guest_debug(cpu);
|
||||
break;
|
||||
} else if (r == EXCP_ATOMIC) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
cpu_exec_step_atomic(cpu);
|
||||
qemu_mutex_lock_iothread();
|
||||
break;
|
||||
}
|
||||
} else if (cpu->stop) {
|
||||
if (cpu->unplug) {
|
||||
cpu = CPU_NEXT(cpu);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
cpu = CPU_NEXT(cpu);
|
||||
} /* while (cpu && !cpu->exit_request).. */
|
||||
|
||||
/* Does not need qatomic_mb_set because a spurious wakeup is okay. */
|
||||
qatomic_set(&tcg_current_rr_cpu, NULL);
|
||||
|
||||
if (cpu && cpu->exit_request) {
|
||||
qatomic_mb_set(&cpu->exit_request, 0);
|
||||
}
|
||||
|
||||
if (icount_enabled() && all_cpu_threads_idle()) {
|
||||
/*
|
||||
* When all cpus are sleeping (e.g in WFI), to avoid a deadlock
|
||||
* in the main_loop, wake it up in order to start the warp timer.
|
||||
*/
|
||||
qemu_notify_event();
|
||||
}
|
||||
|
||||
qemu_tcg_rr_wait_io_event();
|
||||
deal_with_unplugged_cpus();
|
||||
}
|
||||
|
||||
rcu_unregister_thread();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Multi-threaded TCG
|
||||
*
|
||||
* In the multi-threaded case each vCPU has its own thread. The TLS
|
||||
* variable current_cpu can be used deep in the code to find the
|
||||
* current CPUState for a given thread.
|
||||
*/
|
||||
|
||||
static void *tcg_cpu_thread_fn(void *arg)
|
||||
{
|
||||
CPUState *cpu = arg;
|
||||
|
||||
assert(tcg_enabled());
|
||||
g_assert(!icount_enabled());
|
||||
|
||||
rcu_register_thread();
|
||||
tcg_register_thread();
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
qemu_thread_get_self(cpu->thread);
|
||||
|
||||
cpu->thread_id = qemu_get_thread_id();
|
||||
cpu->can_do_io = 1;
|
||||
current_cpu = cpu;
|
||||
cpu_thread_signal_created(cpu);
|
||||
qemu_guest_random_seed_thread_part2(cpu->random_seed);
|
||||
|
||||
/* process any pending work */
|
||||
cpu->exit_request = 1;
|
||||
|
||||
do {
|
||||
if (cpu_can_run(cpu)) {
|
||||
int r;
|
||||
qemu_mutex_unlock_iothread();
|
||||
r = tcg_cpu_exec(cpu);
|
||||
qemu_mutex_lock_iothread();
|
||||
switch (r) {
|
||||
case EXCP_DEBUG:
|
||||
cpu_handle_guest_debug(cpu);
|
||||
break;
|
||||
case EXCP_HALTED:
|
||||
/*
|
||||
* during start-up the vCPU is reset and the thread is
|
||||
* kicked several times. If we don't ensure we go back
|
||||
* to sleep in the halted state we won't cleanly
|
||||
* start-up when the vCPU is enabled.
|
||||
*
|
||||
* cpu->halted should ensure we sleep in wait_io_event
|
||||
*/
|
||||
g_assert(cpu->halted);
|
||||
break;
|
||||
case EXCP_ATOMIC:
|
||||
qemu_mutex_unlock_iothread();
|
||||
cpu_exec_step_atomic(cpu);
|
||||
qemu_mutex_lock_iothread();
|
||||
default:
|
||||
/* Ignore everything else? */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
qatomic_mb_set(&cpu->exit_request, 0);
|
||||
qemu_wait_io_event(cpu);
|
||||
} while (!cpu->unplug || cpu_can_run(cpu));
|
||||
|
||||
qemu_tcg_destroy_vcpu(cpu);
|
||||
cpu_thread_signal_destroyed(cpu);
|
||||
qemu_mutex_unlock_iothread();
|
||||
rcu_unregister_thread();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void tcg_start_vcpu_thread(CPUState *cpu)
|
||||
{
|
||||
char thread_name[VCPU_THREAD_NAME_SIZE];
|
||||
static QemuCond *single_tcg_halt_cond;
|
||||
static QemuThread *single_tcg_cpu_thread;
|
||||
static int tcg_region_inited;
|
||||
|
||||
assert(tcg_enabled());
|
||||
/*
|
||||
* Initialize TCG regions--once. Now is a good time, because:
|
||||
* (1) TCG's init context, prologue and target globals have been set up.
|
||||
* (2) qemu_tcg_mttcg_enabled() works now (TCG init code runs before the
|
||||
* -accel flag is processed, so the check doesn't work then).
|
||||
*/
|
||||
if (!tcg_region_inited) {
|
||||
tcg_region_inited = 1;
|
||||
tcg_region_init();
|
||||
parallel_cpus = qemu_tcg_mttcg_enabled() && current_machine->smp.max_cpus > 1;
|
||||
}
|
||||
|
||||
if (qemu_tcg_mttcg_enabled() || !single_tcg_cpu_thread) {
|
||||
cpu->thread = g_malloc0(sizeof(QemuThread));
|
||||
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
|
||||
qemu_cond_init(cpu->halt_cond);
|
||||
|
||||
if (qemu_tcg_mttcg_enabled()) {
|
||||
/* create a thread per vCPU with TCG (MTTCG) */
|
||||
snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG",
|
||||
cpu->cpu_index);
|
||||
|
||||
qemu_thread_create(cpu->thread, thread_name, tcg_cpu_thread_fn,
|
||||
cpu, QEMU_THREAD_JOINABLE);
|
||||
|
||||
} else {
|
||||
/* share a single thread for all cpus with TCG */
|
||||
snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "ALL CPUs/TCG");
|
||||
qemu_thread_create(cpu->thread, thread_name,
|
||||
tcg_rr_cpu_thread_fn,
|
||||
cpu, QEMU_THREAD_JOINABLE);
|
||||
|
||||
single_tcg_halt_cond = cpu->halt_cond;
|
||||
single_tcg_cpu_thread = cpu->thread;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
cpu->hThread = qemu_thread_get_handle(cpu->thread);
|
||||
#endif
|
||||
} else {
|
||||
/* For non-MTTCG cases we share the thread */
|
||||
cpu->thread = single_tcg_cpu_thread;
|
||||
cpu->halt_cond = single_tcg_halt_cond;
|
||||
cpu->thread_id = first_cpu->thread_id;
|
||||
cpu->can_do_io = 1;
|
||||
cpu->created = true;
|
||||
}
|
||||
}
|
||||
|
||||
static int64_t tcg_get_virtual_clock(void)
|
||||
{
|
||||
if (icount_enabled()) {
|
||||
return icount_get();
|
||||
}
|
||||
return cpu_get_clock();
|
||||
}
|
||||
|
||||
static int64_t tcg_get_elapsed_ticks(void)
|
||||
{
|
||||
if (icount_enabled()) {
|
||||
return icount_get();
|
||||
}
|
||||
return cpu_get_ticks();
|
||||
}
|
||||
|
||||
/* mask must never be zero, except for A20 change call */
|
||||
static void tcg_handle_interrupt(CPUState *cpu, int mask)
|
||||
{
|
||||
int old_mask;
|
||||
g_assert(qemu_mutex_iothread_locked());
|
||||
|
||||
old_mask = cpu->interrupt_request;
|
||||
cpu->interrupt_request |= mask;
|
||||
|
||||
/*
|
||||
* If called from iothread context, wake the target cpu in
|
||||
* case its halted.
|
||||
*/
|
||||
if (!qemu_cpu_is_self(cpu)) {
|
||||
qemu_cpu_kick(cpu);
|
||||
} else {
|
||||
qatomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1);
|
||||
if (icount_enabled() &&
|
||||
!cpu->can_do_io
|
||||
&& (mask & ~old_mask) != 0) {
|
||||
cpu_abort(cpu, "Raised interrupt while not in I/O function");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const CpusAccel tcg_cpus = {
|
||||
.create_vcpu_thread = tcg_start_vcpu_thread,
|
||||
.kick_vcpu_thread = tcg_kick_vcpu_thread,
|
||||
|
||||
.handle_interrupt = tcg_handle_interrupt,
|
||||
|
||||
.get_virtual_clock = tcg_get_virtual_clock,
|
||||
.get_elapsed_ticks = tcg_get_elapsed_ticks,
|
||||
};
|
17
accel/tcg/tcg-cpus.h
Normal file
17
accel/tcg/tcg-cpus.h
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Accelerator CPUS Interface
|
||||
*
|
||||
* Copyright 2020 SUSE LLC
|
||||
*
|
||||
* 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 TCG_CPUS_H
|
||||
#define TCG_CPUS_H
|
||||
|
||||
#include "sysemu/cpus.h"
|
||||
|
||||
extern const CpusAccel tcg_cpus;
|
||||
|
||||
#endif /* TCG_CPUS_H */
|
@ -57,6 +57,7 @@
|
||||
#include "qemu/main-loop.h"
|
||||
#include "exec/log.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "sysemu/tcg.h"
|
||||
|
||||
/* #define DEBUG_TB_INVALIDATE */
|
||||
@ -369,7 +370,7 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb,
|
||||
|
||||
found:
|
||||
if (reset_icount && (tb_cflags(tb) & CF_USE_ICOUNT)) {
|
||||
assert(use_icount);
|
||||
assert(icount_enabled());
|
||||
/* Reset the cycle counter to the start of the block
|
||||
and shift if to the number of actually executed instructions */
|
||||
cpu_neg(cpu)->icount_decr.u16.low += num_insns - i;
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "exec/log.h"
|
||||
#include "exec/translator.h"
|
||||
#include "exec/plugin-gen.h"
|
||||
#include "sysemu/replay.h"
|
||||
|
||||
/* Pairs with tcg_clear_temp_count.
|
||||
To be called by #TranslatorOps.{translate_insn,tb_stop} if
|
||||
|
18
block/qapi.c
18
block/qapi.c
@ -230,6 +230,8 @@ int bdrv_query_snapshot_info_list(BlockDriverState *bs,
|
||||
info->date_nsec = sn_tab[i].date_nsec;
|
||||
info->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000;
|
||||
info->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000;
|
||||
info->icount = sn_tab[i].icount;
|
||||
info->has_icount = sn_tab[i].icount != -1ULL;
|
||||
|
||||
info_list = g_new0(SnapshotInfoList, 1);
|
||||
info_list->value = info;
|
||||
@ -694,14 +696,15 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes,
|
||||
void bdrv_snapshot_dump(QEMUSnapshotInfo *sn)
|
||||
{
|
||||
char date_buf[128], clock_buf[128];
|
||||
char icount_buf[128] = {0};
|
||||
struct tm tm;
|
||||
time_t ti;
|
||||
int64_t secs;
|
||||
char *sizing = NULL;
|
||||
|
||||
if (!sn) {
|
||||
qemu_printf("%-10s%-20s%11s%20s%15s",
|
||||
"ID", "TAG", "VM SIZE", "DATE", "VM CLOCK");
|
||||
qemu_printf("%-10s%-18s%7s%20s%13s%11s",
|
||||
"ID", "TAG", "VM SIZE", "DATE", "VM CLOCK", "ICOUNT");
|
||||
} else {
|
||||
ti = sn->date_sec;
|
||||
localtime_r(&ti, &tm);
|
||||
@ -715,11 +718,16 @@ void bdrv_snapshot_dump(QEMUSnapshotInfo *sn)
|
||||
(int)(secs % 60),
|
||||
(int)((sn->vm_clock_nsec / 1000000) % 1000));
|
||||
sizing = size_to_str(sn->vm_state_size);
|
||||
qemu_printf("%-10s%-20s%11s%20s%15s",
|
||||
if (sn->icount != -1ULL) {
|
||||
snprintf(icount_buf, sizeof(icount_buf),
|
||||
"%"PRId64, sn->icount);
|
||||
}
|
||||
qemu_printf("%-9s %-17s %7s%20s%13s%11s",
|
||||
sn->id_str, sn->name,
|
||||
sizing,
|
||||
date_buf,
|
||||
clock_buf);
|
||||
clock_buf,
|
||||
icount_buf);
|
||||
}
|
||||
g_free(sizing);
|
||||
}
|
||||
@ -881,6 +889,8 @@ void bdrv_image_info_dump(ImageInfo *info)
|
||||
.date_nsec = elem->value->date_nsec,
|
||||
.vm_clock_nsec = elem->value->vm_clock_sec * 1000000000ULL +
|
||||
elem->value->vm_clock_nsec,
|
||||
.icount = elem->value->has_icount ?
|
||||
elem->value->icount : -1ULL,
|
||||
};
|
||||
|
||||
pstrcpy(sn.id_str, sizeof(sn.id_str), elem->value->id);
|
||||
|
@ -164,6 +164,12 @@ static int qcow2_do_read_snapshots(BlockDriverState *bs, bool repair,
|
||||
sn->disk_size = bs->total_sectors * BDRV_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
if (sn->extra_data_size >= endof(QCowSnapshotExtraData, icount)) {
|
||||
sn->icount = be64_to_cpu(extra.icount);
|
||||
} else {
|
||||
sn->icount = -1ULL;
|
||||
}
|
||||
|
||||
if (sn->extra_data_size > sizeof(extra)) {
|
||||
uint64_t extra_data_end;
|
||||
size_t unknown_extra_data_size;
|
||||
@ -333,6 +339,7 @@ int qcow2_write_snapshots(BlockDriverState *bs)
|
||||
memset(&extra, 0, sizeof(extra));
|
||||
extra.vm_state_size_large = cpu_to_be64(sn->vm_state_size);
|
||||
extra.disk_size = cpu_to_be64(sn->disk_size);
|
||||
extra.icount = cpu_to_be64(sn->icount);
|
||||
|
||||
id_str_size = strlen(sn->id_str);
|
||||
name_size = strlen(sn->name);
|
||||
@ -656,6 +663,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
sn->date_sec = sn_info->date_sec;
|
||||
sn->date_nsec = sn_info->date_nsec;
|
||||
sn->vm_clock_nsec = sn_info->vm_clock_nsec;
|
||||
sn->icount = sn_info->icount;
|
||||
sn->extra_data_size = sizeof(QCowSnapshotExtraData);
|
||||
|
||||
/* Allocate the L1 table of the snapshot and copy the current one there. */
|
||||
@ -1000,6 +1008,7 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
||||
sn_info->date_sec = sn->date_sec;
|
||||
sn_info->date_nsec = sn->date_nsec;
|
||||
sn_info->vm_clock_nsec = sn->vm_clock_nsec;
|
||||
sn_info->icount = sn->icount;
|
||||
}
|
||||
*psn_tab = sn_tab;
|
||||
return s->nb_snapshots;
|
||||
|
@ -206,6 +206,7 @@ typedef struct QEMU_PACKED QCowSnapshotHeader {
|
||||
typedef struct QEMU_PACKED QCowSnapshotExtraData {
|
||||
uint64_t vm_state_size_large;
|
||||
uint64_t disk_size;
|
||||
uint64_t icount;
|
||||
} QCowSnapshotExtraData;
|
||||
|
||||
|
||||
@ -219,6 +220,8 @@ typedef struct QCowSnapshot {
|
||||
uint32_t date_sec;
|
||||
uint32_t date_nsec;
|
||||
uint64_t vm_clock_nsec;
|
||||
/* icount value for the moment when snapshot was taken */
|
||||
uint64_t icount;
|
||||
/* Size of all extra data, including QCowSnapshotExtraData if available */
|
||||
uint32_t extra_data_size;
|
||||
/* Data beyond QCowSnapshotExtraData, if any */
|
||||
|
10
blockdev.c
10
blockdev.c
@ -59,6 +59,7 @@
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/help_option.h"
|
||||
#include "qemu/main-loop.h"
|
||||
@ -1190,6 +1191,10 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
|
||||
info->vm_state_size = sn.vm_state_size;
|
||||
info->vm_clock_nsec = sn.vm_clock_nsec % 1000000000;
|
||||
info->vm_clock_sec = sn.vm_clock_nsec / 1000000000;
|
||||
if (sn.icount != -1ULL) {
|
||||
info->icount = sn.icount;
|
||||
info->has_icount = true;
|
||||
}
|
||||
|
||||
return info;
|
||||
|
||||
@ -1350,6 +1355,11 @@ static void internal_snapshot_prepare(BlkActionState *common,
|
||||
sn->date_sec = tv.tv_sec;
|
||||
sn->date_nsec = tv.tv_usec * 1000;
|
||||
sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
if (replay_mode != REPLAY_MODE_NONE) {
|
||||
sn->icount = replay_get_current_icount();
|
||||
} else {
|
||||
sn->icount = -1ULL;
|
||||
}
|
||||
|
||||
ret1 = bdrv_snapshot_create(bs, sn);
|
||||
if (ret1 < 0) {
|
||||
|
231
configure
vendored
231
configure
vendored
@ -155,7 +155,7 @@ update_cxxflags() {
|
||||
# options which some versions of GCC's C++ compiler complain about
|
||||
# because they only make sense for C programs.
|
||||
QEMU_CXXFLAGS="$QEMU_CXXFLAGS -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS"
|
||||
CXXFLAGS=$(echo "$CFLAGS" | sed s/-std=gnu99/-std=gnu++11/)
|
||||
CONFIGURE_CXXFLAGS=$(echo "$CONFIGURE_CFLAGS" | sed s/-std=gnu99/-std=gnu++11/)
|
||||
for arg in $QEMU_CFLAGS; do
|
||||
case $arg in
|
||||
-Wstrict-prototypes|-Wmissing-prototypes|-Wnested-externs|\
|
||||
@ -170,13 +170,14 @@ update_cxxflags() {
|
||||
|
||||
compile_object() {
|
||||
local_cflags="$1"
|
||||
do_cc $CFLAGS $QEMU_CFLAGS $local_cflags -c -o $TMPO $TMPC
|
||||
do_cc $CFLAGS $CONFIGURE_CFLAGS $QEMU_CFLAGS $local_cflags -c -o $TMPO $TMPC
|
||||
}
|
||||
|
||||
compile_prog() {
|
||||
local_cflags="$1"
|
||||
local_ldflags="$2"
|
||||
do_cc $CFLAGS $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $QEMU_LDFLAGS $local_ldflags
|
||||
do_cc $CFLAGS $CONFIGURE_CFLAGS $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC \
|
||||
$LDFLAGS $CONFIGURE_LDFLAGS $QEMU_LDFLAGS $local_ldflags
|
||||
}
|
||||
|
||||
# symbolically link $1 to $2. Portable version of "ln -sf".
|
||||
@ -296,7 +297,7 @@ brlapi=""
|
||||
curl=""
|
||||
curses=""
|
||||
docs=""
|
||||
fdt=""
|
||||
fdt="auto"
|
||||
netmap="no"
|
||||
sdl="auto"
|
||||
sdl_image="auto"
|
||||
@ -349,7 +350,7 @@ modules="no"
|
||||
module_upgrades="no"
|
||||
prefix="/usr/local"
|
||||
qemu_suffix="qemu"
|
||||
slirp=""
|
||||
slirp="auto"
|
||||
oss_lib=""
|
||||
bsd="no"
|
||||
linux="no"
|
||||
@ -537,7 +538,10 @@ QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS"
|
||||
QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS"
|
||||
QEMU_INCLUDES="-iquote . -iquote ${source_path} -iquote ${source_path}/accel/tcg -iquote ${source_path}/include"
|
||||
QEMU_INCLUDES="$QEMU_INCLUDES -iquote ${source_path}/disas/libvixl"
|
||||
CFLAGS="-std=gnu99 -Wall"
|
||||
|
||||
# Flags that are needed during configure but later taken care of by Meson
|
||||
CONFIGURE_CFLAGS="-std=gnu99 -Wall"
|
||||
CONFIGURE_LDFLAGS=
|
||||
|
||||
|
||||
check_define() {
|
||||
@ -851,7 +855,7 @@ if test "$mingw32" = "yes" ; then
|
||||
EXESUF=".exe"
|
||||
HOST_DSOSUF=".dll"
|
||||
# MinGW needs -mthreads for TLS and macro _MT.
|
||||
CFLAGS="-mthreads $CFLAGS"
|
||||
CONFIGURE_CFLAGS="-mthreads $CONFIGURE_CFLAGS"
|
||||
write_c_skeleton;
|
||||
prefix="/qemu"
|
||||
qemu_suffix=""
|
||||
@ -1058,9 +1062,9 @@ for opt do
|
||||
;;
|
||||
--enable-vnc-png) vnc_png="enabled"
|
||||
;;
|
||||
--disable-slirp) slirp="no"
|
||||
--disable-slirp) slirp="disabled"
|
||||
;;
|
||||
--enable-slirp=git) slirp="git"
|
||||
--enable-slirp=git) slirp="internal"
|
||||
;;
|
||||
--enable-slirp=system) slirp="system"
|
||||
;;
|
||||
@ -1181,9 +1185,13 @@ for opt do
|
||||
;;
|
||||
--enable-curl) curl="yes"
|
||||
;;
|
||||
--disable-fdt) fdt="no"
|
||||
--disable-fdt) fdt="disabled"
|
||||
;;
|
||||
--enable-fdt) fdt="yes"
|
||||
--enable-fdt) fdt="enabled"
|
||||
;;
|
||||
--enable-fdt=git) fdt="internal"
|
||||
;;
|
||||
--enable-fdt=system) fdt="system"
|
||||
;;
|
||||
--disable-linux-aio) linux_aio="no"
|
||||
;;
|
||||
@ -2105,7 +2113,7 @@ fi
|
||||
|
||||
if test "$static" = "yes"; then
|
||||
if test "$pie" != "no" && compile_prog "-Werror -fPIE -DPIE" "-static-pie"; then
|
||||
CFLAGS="-fPIE -DPIE $CFLAGS"
|
||||
CONFIGURE_CFLAGS="-fPIE -DPIE $CONFIGURE_CFLAGS"
|
||||
QEMU_LDFLAGS="-static-pie $QEMU_LDFLAGS"
|
||||
pie="yes"
|
||||
elif test "$pie" = "yes"; then
|
||||
@ -2115,11 +2123,11 @@ if test "$static" = "yes"; then
|
||||
pie="no"
|
||||
fi
|
||||
elif test "$pie" = "no"; then
|
||||
CFLAGS="$CFLAGS_NOPIE $CFLAGS"
|
||||
LDFLAGS="$LDFLAGS_NOPIE $LDFLAGS"
|
||||
CONFIGURE_CFLAGS="$CFLAGS_NOPIE $CONFIGURE_CFLAGS"
|
||||
CONFIGURE_LDFLAGS="$LDFLAGS_NOPIE $CONFIGURE_LDFLAGS"
|
||||
elif compile_prog "-Werror -fPIE -DPIE" "-pie"; then
|
||||
CFLAGS="-fPIE -DPIE $CFLAGS"
|
||||
LDFLAGS="-pie $LDFLAGS"
|
||||
CONFIGURE_CFLAGS="-fPIE -DPIE $CONFIGURE_CFLAGS"
|
||||
CONFIGURE_LDFLAGS="-pie $CONFIGURE_LDFLAGS"
|
||||
pie="yes"
|
||||
elif test "$pie" = "yes"; then
|
||||
error_exit "PIE not available due to missing toolchain support"
|
||||
@ -3663,7 +3671,7 @@ EOF
|
||||
if ! compile_prog "$glib_cflags -Werror" "$glib_libs" ; then
|
||||
if cc_has_warning_flag "-Wno-unknown-attributes"; then
|
||||
glib_cflags="-Wno-unknown-attributes $glib_cflags"
|
||||
CFLAGS="-Wno-unknown-attributes $CFLAGS"
|
||||
CONFIGURE_CFLAGS="-Wno-unknown-attributes $CONFIGURE_CFLAGS"
|
||||
fi
|
||||
fi
|
||||
|
||||
@ -3683,7 +3691,7 @@ EOF
|
||||
if ! compile_prog "$glib_cflags -Werror" "$glib_libs" ; then
|
||||
if cc_has_warning_flag "-Wno-unused-function"; then
|
||||
glib_cflags="$glib_cflags -Wno-unused-function"
|
||||
CFLAGS="$CFLAGS -Wno-unused-function"
|
||||
CONFIGURE_CFLAGS="$CONFIGURE_CFLAGS -Wno-unused-function"
|
||||
fi
|
||||
fi
|
||||
|
||||
@ -3941,67 +3949,15 @@ fi
|
||||
|
||||
##########################################
|
||||
# fdt probe
|
||||
# fdt support is mandatory for at least some target architectures,
|
||||
# so insist on it if we're building those system emulators.
|
||||
fdt_required=no
|
||||
for target in $target_list; do
|
||||
case $target in
|
||||
aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu|riscv*-softmmu|rx-softmmu)
|
||||
fdt_required=yes
|
||||
|
||||
case "$fdt" in
|
||||
auto | enabled | internal)
|
||||
# Simpler to always update submodule, even if not needed.
|
||||
if test -e "${source_path}/.git" && test $git_update = 'yes' ; then
|
||||
git_submodules="${git_submodules} dtc"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if test "$fdt_required" = "yes"; then
|
||||
if test "$fdt" = "no"; then
|
||||
error_exit "fdt disabled but some requested targets require it." \
|
||||
"You can turn off fdt only if you also disable all the system emulation" \
|
||||
"targets which need it (by specifying a cut down --target-list)."
|
||||
fi
|
||||
fdt=yes
|
||||
elif test "$fdt" != "yes" ; then
|
||||
fdt=no
|
||||
fi
|
||||
|
||||
# fdt is only required when building softmmu targets
|
||||
if test -z "$fdt" -a "$softmmu" != "yes" ; then
|
||||
fdt="no"
|
||||
fi
|
||||
|
||||
if test "$fdt" != "no" ; then
|
||||
fdt_libs="-lfdt"
|
||||
# explicitly check for libfdt_env.h as it is missing in some stable installs
|
||||
# and test for required functions to make sure we are on a version >= 1.4.2
|
||||
cat > $TMPC << EOF
|
||||
#include <libfdt.h>
|
||||
#include <libfdt_env.h>
|
||||
int main(void) { fdt_check_full(NULL, 0); return 0; }
|
||||
EOF
|
||||
if compile_prog "" "$fdt_libs" ; then
|
||||
# system DTC is good - use it
|
||||
fdt=system
|
||||
else
|
||||
# have GIT checkout, so activate dtc submodule
|
||||
if test -e "${source_path}/.git" ; then
|
||||
git_submodules="${git_submodules} dtc"
|
||||
fi
|
||||
if test -d "${source_path}/dtc/libfdt" || test -e "${source_path}/.git" ; then
|
||||
fdt=git
|
||||
mkdir -p dtc
|
||||
fdt_cflags="-I${source_path}/dtc/libfdt"
|
||||
fdt_ldflags="-Ldtc/libfdt"
|
||||
fdt_libs="$fdt_libs"
|
||||
elif test "$fdt" = "yes" ; then
|
||||
# Not a git build & no libfdt found, prompt for system install
|
||||
error_exit "DTC (libfdt) version >= 1.4.2 not present." \
|
||||
"Please install the DTC (libfdt) devel package"
|
||||
else
|
||||
# don't have and don't want
|
||||
fdt_libs=
|
||||
fdt=no
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
esac
|
||||
|
||||
##########################################
|
||||
# opengl probe (for sdl2, gtk, milkymist-tmu2)
|
||||
@ -5806,56 +5762,12 @@ fi
|
||||
##########################################
|
||||
# check for slirp
|
||||
|
||||
# slirp is only required when building softmmu targets
|
||||
if test -z "$slirp" -a "$softmmu" != "yes" ; then
|
||||
slirp="no"
|
||||
fi
|
||||
|
||||
case "$slirp" in
|
||||
"" | yes)
|
||||
if $pkg_config slirp; then
|
||||
slirp=system
|
||||
elif test -e "${source_path}/.git" && test $git_update = 'yes' ; then
|
||||
slirp=git
|
||||
elif test -e "${source_path}/slirp/Makefile" ; then
|
||||
slirp=internal
|
||||
elif test -z "$slirp" ; then
|
||||
slirp=no
|
||||
else
|
||||
feature_not_found "slirp" "Install slirp devel or git submodule"
|
||||
fi
|
||||
;;
|
||||
|
||||
system)
|
||||
if ! $pkg_config slirp; then
|
||||
feature_not_found "slirp" "Install slirp devel"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$slirp" in
|
||||
git | internal)
|
||||
if test "$slirp" = git; then
|
||||
auto | enabled | internal)
|
||||
# Simpler to always update submodule, even if not needed.
|
||||
if test -e "${source_path}/.git" && test $git_update = 'yes' ; then
|
||||
git_submodules="${git_submodules} slirp"
|
||||
fi
|
||||
mkdir -p slirp
|
||||
slirp_cflags="-I${source_path}/slirp/src -Islirp/src"
|
||||
slirp_libs="-Lslirp -lslirp"
|
||||
if test "$mingw32" = "yes" ; then
|
||||
slirp_libs="$slirp_libs -lws2_32 -liphlpapi"
|
||||
fi
|
||||
;;
|
||||
|
||||
system)
|
||||
slirp_version=$($pkg_config --modversion slirp 2>/dev/null)
|
||||
slirp_cflags=$($pkg_config --cflags slirp 2>/dev/null)
|
||||
slirp_libs=$($pkg_config --libs slirp 2>/dev/null)
|
||||
;;
|
||||
|
||||
no)
|
||||
;;
|
||||
*)
|
||||
error_exit "Unknown state for slirp: $slirp"
|
||||
;;
|
||||
esac
|
||||
|
||||
@ -5906,13 +5818,6 @@ elif test "$fortify_source" = "yes" ; then
|
||||
QEMU_CFLAGS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 $QEMU_CFLAGS"
|
||||
debug=no
|
||||
fi
|
||||
if test "$debug_info" = "yes"; then
|
||||
CFLAGS="-g $CFLAGS"
|
||||
LDFLAGS="-g $LDFLAGS"
|
||||
fi
|
||||
if test "$debug" = "no"; then
|
||||
CFLAGS="-O2 $CFLAGS"
|
||||
fi
|
||||
|
||||
case "$ARCH" in
|
||||
alpha)
|
||||
@ -5977,7 +5882,14 @@ fi
|
||||
|
||||
# Use ASLR, no-SEH and DEP if available
|
||||
if test "$mingw32" = "yes" ; then
|
||||
for flag in --dynamicbase --no-seh --nxcompat; do
|
||||
flags="--no-seh --nxcompat"
|
||||
|
||||
# Disable ASLR for debug builds to allow debugging with gdb
|
||||
if test "$debug" = "no" ; then
|
||||
flags="--dynamicbase $flags"
|
||||
fi
|
||||
|
||||
for flag in $flags; do
|
||||
if ld_has $flag ; then
|
||||
QEMU_LDFLAGS="-Wl,$flag $QEMU_LDFLAGS"
|
||||
fi
|
||||
@ -6135,7 +6047,7 @@ EOF
|
||||
|
||||
update_cxxflags
|
||||
|
||||
if do_cxx $CXXFLAGS $QEMU_CXXFLAGS -o $TMPE $TMPCXX $TMPO $QEMU_LDFLAGS; then
|
||||
if do_cxx $CXXFLAGS $CONFIGURE_CXXFLAGS $QEMU_CXXFLAGS -o $TMPE $TMPCXX $TMPO $QEMU_LDFLAGS; then
|
||||
# C++ compiler $cxx works ok with C compiler $cc
|
||||
:
|
||||
else
|
||||
@ -6151,9 +6063,6 @@ fi
|
||||
if test $git_update = 'yes' ; then
|
||||
(cd "${source_path}" && GIT="$git" "./scripts/git-submodule.sh" update "$git_submodules")
|
||||
fi
|
||||
if test "$fdt" = "git" ; then
|
||||
symlink "$source_path/dtc/Makefile" "dtc/Makefile"
|
||||
fi
|
||||
|
||||
config_host_mak="config-host.mak"
|
||||
|
||||
@ -6256,16 +6165,7 @@ fi
|
||||
if test "$guest_agent" = "yes" ; then
|
||||
echo "CONFIG_GUEST_AGENT=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$slirp" != "no"; then
|
||||
echo "CONFIG_SLIRP=y" >> $config_host_mak
|
||||
echo "CONFIG_SMBD_COMMAND=\"$smbd\"" >> $config_host_mak
|
||||
echo "SLIRP_CFLAGS=$slirp_cflags" >> $config_host_mak
|
||||
echo "SLIRP_LIBS=$slirp_libs" >> $config_host_mak
|
||||
fi
|
||||
subdirs=
|
||||
if [ "$slirp" = "git" -o "$slirp" = "internal" ]; then
|
||||
subdirs="$subdirs slirp"
|
||||
fi
|
||||
echo "CONFIG_SMBD_COMMAND=\"$smbd\"" >> $config_host_mak
|
||||
if test "$vde" = "yes" ; then
|
||||
echo "CONFIG_VDE=y" >> $config_host_mak
|
||||
echo "VDE_LIBS=$vde_libs" >> $config_host_mak
|
||||
@ -6591,11 +6491,6 @@ fi
|
||||
if test "$preadv" = "yes" ; then
|
||||
echo "CONFIG_PREADV=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$fdt" != "no" ; then
|
||||
echo "CONFIG_FDT=y" >> $config_host_mak
|
||||
echo "FDT_CFLAGS=$fdt_cflags" >> $config_host_mak
|
||||
echo "FDT_LIBS=$fdt_ldflags $fdt_libs" >> $config_host_mak
|
||||
fi
|
||||
if test "$membarrier" = "yes" ; then
|
||||
echo "CONFIG_MEMBARRIER=y" >> $config_host_mak
|
||||
fi
|
||||
@ -7062,7 +6957,6 @@ echo "RANLIB=$ranlib" >> $config_host_mak
|
||||
echo "NM=$nm" >> $config_host_mak
|
||||
echo "PKG_CONFIG=$pkg_config_exe" >> $config_host_mak
|
||||
echo "WINDRES=$windres" >> $config_host_mak
|
||||
echo "CFLAGS=$CFLAGS" >> $config_host_mak
|
||||
echo "CFLAGS_NOPIE=$CFLAGS_NOPIE" >> $config_host_mak
|
||||
echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak
|
||||
echo "QEMU_CXXFLAGS=$QEMU_CXXFLAGS" >> $config_host_mak
|
||||
@ -7135,14 +7029,10 @@ for target in $target_list; do
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$fdt" = "git" ]; then
|
||||
subdirs="$subdirs dtc"
|
||||
fi
|
||||
echo "CONFIG_QEMU_INTERP_PREFIX=$interp_prefix" | sed 's/%M/@0@/' >> $config_host_mak
|
||||
if test "$default_targets" = "yes"; then
|
||||
echo "CONFIG_DEFAULT_TARGETS=y" >> $config_host_mak
|
||||
fi
|
||||
echo "SUBDIRS=$subdirs" >> $config_host_mak
|
||||
|
||||
if test "$numa" = "yes"; then
|
||||
echo "CONFIG_NUMA=y" >> $config_host_mak
|
||||
@ -7247,24 +7137,29 @@ echo "export PYTHON='$python'" >> "$iotests_common_env"
|
||||
if test "$skip_meson" = no; then
|
||||
cross="config-meson.cross.new"
|
||||
meson_quote() {
|
||||
echo "['$(echo $* | sed "s/ /','/g")']"
|
||||
echo "'$(echo $* | sed "s/ /','/g")'"
|
||||
}
|
||||
|
||||
echo "# Automatically generated by configure - do not modify" > $cross
|
||||
echo "[properties]" >> $cross
|
||||
test -z "$cxx" && echo "link_language = 'c'" >> $cross
|
||||
echo "[built-in options]" >> $cross
|
||||
echo "c_args = [${CFLAGS:+$(meson_quote $CFLAGS)}]" >> $cross
|
||||
echo "cpp_args = [${CXXFLAGS:+$(meson_quote $CXXFLAGS)}]" >> $cross
|
||||
echo "c_link_args = [${LDFLAGS:+$(meson_quote $LDFLAGS)}]" >> $cross
|
||||
echo "cpp_link_args = [${LDFLAGS:+$(meson_quote $LDFLAGS)}]" >> $cross
|
||||
echo "[binaries]" >> $cross
|
||||
echo "c = $(meson_quote $cc)" >> $cross
|
||||
test -n "$cxx" && echo "cpp = $(meson_quote $cxx)" >> $cross
|
||||
echo "ar = $(meson_quote $ar)" >> $cross
|
||||
echo "nm = $(meson_quote $nm)" >> $cross
|
||||
echo "pkgconfig = $(meson_quote $pkg_config_exe)" >> $cross
|
||||
echo "ranlib = $(meson_quote $ranlib)" >> $cross
|
||||
echo "c = [$(meson_quote $cc)]" >> $cross
|
||||
test -n "$cxx" && echo "cpp = [$(meson_quote $cxx)]" >> $cross
|
||||
echo "ar = [$(meson_quote $ar)]" >> $cross
|
||||
echo "nm = [$(meson_quote $nm)]" >> $cross
|
||||
echo "pkgconfig = [$(meson_quote $pkg_config_exe)]" >> $cross
|
||||
echo "ranlib = [$(meson_quote $ranlib)]" >> $cross
|
||||
if has $sdl2_config; then
|
||||
echo "sdl2-config = $(meson_quote $sdl2_config)" >> $cross
|
||||
echo "sdl2-config = [$(meson_quote $sdl2_config)]" >> $cross
|
||||
fi
|
||||
echo "strip = $(meson_quote $strip)" >> $cross
|
||||
echo "windres = $(meson_quote $windres)" >> $cross
|
||||
echo "strip = [$(meson_quote $strip)]" >> $cross
|
||||
echo "windres = [$(meson_quote $windres)]" >> $cross
|
||||
if test -n "$cross_prefix"; then
|
||||
cross_arg="--cross-file config-meson.cross"
|
||||
echo "[host_machine]" >> $cross
|
||||
@ -7321,7 +7216,7 @@ NINJA=${ninja:-$PWD/ninjatool} $meson setup \
|
||||
-Dcocoa=$cocoa -Dmpath=$mpath -Dsdl=$sdl -Dsdl_image=$sdl_image \
|
||||
-Dvnc=$vnc -Dvnc_sasl=$vnc_sasl -Dvnc_jpeg=$vnc_jpeg -Dvnc_png=$vnc_png \
|
||||
-Dgettext=$gettext -Dxkbcommon=$xkbcommon -Du2f=$u2f \
|
||||
-Dcapstone=$capstone \
|
||||
-Dcapstone=$capstone -Dslirp=$slirp -Dfdt=$fdt \
|
||||
$cross_arg \
|
||||
"$PWD" "$source_path"
|
||||
|
||||
|
@ -2,3 +2,4 @@ TARGET_ARCH=aarch64
|
||||
TARGET_BASE_ARCH=arm
|
||||
TARGET_SUPPORTS_MTTCG=y
|
||||
TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml
|
||||
TARGET_NEED_FDT=y
|
||||
|
@ -1,3 +1,4 @@
|
||||
TARGET_ARCH=arm
|
||||
TARGET_SUPPORTS_MTTCG=y
|
||||
TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml
|
||||
TARGET_NEED_FDT=y
|
||||
|
@ -1,3 +1,4 @@
|
||||
TARGET_ARCH=microblaze
|
||||
TARGET_WORDS_BIGENDIAN=y
|
||||
TARGET_SUPPORTS_MTTCG=y
|
||||
TARGET_NEED_FDT=y
|
||||
|
@ -1,2 +1,3 @@
|
||||
TARGET_ARCH=microblaze
|
||||
TARGET_SUPPORTS_MTTCG=y
|
||||
TARGET_NEED_FDT=y
|
||||
|
@ -1,3 +1,4 @@
|
||||
TARGET_ARCH=mips64
|
||||
TARGET_BASE_ARCH=mips
|
||||
TARGET_ALIGNED_ONLY=y
|
||||
TARGET_NEED_FDT=y
|
||||
|
@ -1,3 +1,4 @@
|
||||
TARGET_ARCH=ppc
|
||||
TARGET_WORDS_BIGENDIAN=y
|
||||
TARGET_XML_FILES= gdb-xml/power-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml
|
||||
TARGET_NEED_FDT=y
|
||||
|
@ -3,3 +3,4 @@ TARGET_BASE_ARCH=ppc
|
||||
TARGET_WORDS_BIGENDIAN=y
|
||||
TARGET_SUPPORTS_MTTCG=y
|
||||
TARGET_XML_FILES= gdb-xml/power64-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml gdb-xml/power-vsx.xml
|
||||
TARGET_NEED_FDT=y
|
||||
|
@ -2,3 +2,4 @@ TARGET_ARCH=riscv32
|
||||
TARGET_BASE_ARCH=riscv
|
||||
TARGET_SUPPORTS_MTTCG=y
|
||||
TARGET_XML_FILES= gdb-xml/riscv-32bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-32bit-csr.xml gdb-xml/riscv-32bit-virtual.xml
|
||||
TARGET_NEED_FDT=y
|
||||
|
@ -2,3 +2,4 @@ TARGET_ARCH=riscv64
|
||||
TARGET_BASE_ARCH=riscv
|
||||
TARGET_SUPPORTS_MTTCG=y
|
||||
TARGET_XML_FILES= gdb-xml/riscv-64bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-64bit-csr.xml gdb-xml/riscv-64bit-virtual.xml
|
||||
TARGET_NEED_FDT=y
|
||||
|
@ -1,2 +1,3 @@
|
||||
TARGET_ARCH=rx
|
||||
TARGET_XML_FILES= gdb-xml/rx-core.xml
|
||||
TARGET_NEED_FDT=y
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "trace/trace-root.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "qemu/range.h"
|
||||
|
||||
/* #define DEBUG_IOMMU */
|
||||
@ -151,7 +151,7 @@ static void dma_blk_cb(void *opaque, int ret)
|
||||
* from several sectors. This code splits all SGs into several
|
||||
* groups. SGs in every group do not overlap.
|
||||
*/
|
||||
if (mem && use_icount && dbs->dir == DMA_DIRECTION_FROM_DEVICE) {
|
||||
if (mem && icount_enabled() && dbs->dir == DMA_DIRECTION_FROM_DEVICE) {
|
||||
int i;
|
||||
for (i = 0 ; i < dbs->iov.niov ; ++i) {
|
||||
if (ranges_overlap((intptr_t)dbs->iov.iov[i].iov_base,
|
||||
|
@ -707,6 +707,11 @@ Snapshot table entry:
|
||||
|
||||
Byte 48 - 55: Virtual disk size of the snapshot in bytes
|
||||
|
||||
Byte 56 - 63: icount value which corresponds to
|
||||
the record/replay instruction count
|
||||
when the snapshot was taken. Set to -1
|
||||
if icount was disabled
|
||||
|
||||
Version 3 images must include extra data at least up to
|
||||
byte 55.
|
||||
|
||||
|
@ -184,11 +184,11 @@ is then incremented (which is called "warping" the virtual clock) as
|
||||
soon as the timer fires or the CPUs need to go out of the idle state.
|
||||
Two functions are used for this purpose; because these actions change
|
||||
virtual machine state and must be deterministic, each of them creates a
|
||||
checkpoint. qemu_start_warp_timer checks if the CPUs are idle and if so
|
||||
starts accounting real time to virtual clock. qemu_account_warp_timer
|
||||
checkpoint. icount_start_warp_timer checks if the CPUs are idle and if so
|
||||
starts accounting real time to virtual clock. icount_account_warp_timer
|
||||
is called when the CPUs get an interrupt or when the warp timer fires,
|
||||
and it warps the virtual clock by the amount of real time that has passed
|
||||
since qemu_start_warp_timer.
|
||||
since icount_start_warp_timer.
|
||||
|
||||
Bottom halves
|
||||
-------------
|
||||
@ -265,6 +265,16 @@ of the original disk image, use overlay files linked to the original images.
|
||||
Therefore all new snapshots (including the starting one) will be saved in
|
||||
overlays and the original image remains unchanged.
|
||||
|
||||
When you need to use snapshots with diskless virtual machine,
|
||||
it must be started with 'orphan' qcow2 image. This image will be used
|
||||
for storing VM snapshots. Here is the example of the command line for this:
|
||||
|
||||
qemu-system-i386 -icount shift=3,rr=replay,rrfile=record.bin,rrsnapshot=init \
|
||||
-net none -drive file=empty.qcow2,if=none,id=rr
|
||||
|
||||
empty.qcow2 drive does not connected to any virtual block device and used
|
||||
for VM snapshots only.
|
||||
|
||||
Network devices
|
||||
---------------
|
||||
|
||||
@ -294,6 +304,42 @@ for recording and replaying must contain identical number of ports in record
|
||||
and replay modes, but their backends may differ.
|
||||
E.g., '-serial stdio' in record mode, and '-serial null' in replay mode.
|
||||
|
||||
Reverse debugging
|
||||
-----------------
|
||||
|
||||
Reverse debugging allows "executing" the program in reverse direction.
|
||||
GDB remote protocol supports "reverse step" and "reverse continue"
|
||||
commands. The first one steps single instruction backwards in time,
|
||||
and the second one finds the last breakpoint in the past.
|
||||
|
||||
Recorded executions may be used to enable reverse debugging. QEMU can't
|
||||
execute the code in backwards direction, but can load a snapshot and
|
||||
replay forward to find the desired position or breakpoint.
|
||||
|
||||
The following GDB commands are supported:
|
||||
- reverse-stepi (or rsi) - step one instruction backwards
|
||||
- reverse-continue (or rc) - find last breakpoint in the past
|
||||
|
||||
Reverse step loads the nearest snapshot and replays the execution until
|
||||
the required instruction is met.
|
||||
|
||||
Reverse continue may include several passes of examining the execution
|
||||
between the snapshots. Each of the passes include the following steps:
|
||||
1. loading the snapshot
|
||||
2. replaying to examine the breakpoints
|
||||
3. if breakpoint or watchpoint was met
|
||||
- loading the snaphot again
|
||||
- replaying to the required breakpoint
|
||||
4. else
|
||||
- proceeding to the p.1 with the earlier snapshot
|
||||
|
||||
Therefore usage of the reverse debugging requires at least one snapshot
|
||||
created in advance. This can be done by omitting 'snapshot' option
|
||||
for the block drives and adding 'rrsnapshot' for both record and replay
|
||||
command lines.
|
||||
See the "Snapshotting" section to learn more about running record/replay
|
||||
and creating the snapshot in these modes.
|
||||
|
||||
Replay log format
|
||||
-----------------
|
||||
|
||||
|
12
exec.c
12
exec.c
@ -102,10 +102,6 @@ uintptr_t qemu_host_page_size;
|
||||
intptr_t qemu_host_page_mask;
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
/* 0 = Do not count executed instructions.
|
||||
1 = Precise instruction counting.
|
||||
2 = Adaptive rate instruction counting. */
|
||||
int use_icount;
|
||||
|
||||
typedef struct PhysPageEntry PhysPageEntry;
|
||||
|
||||
@ -2752,6 +2748,14 @@ void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len,
|
||||
QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) {
|
||||
if (watchpoint_address_matches(wp, addr, len)
|
||||
&& (wp->flags & flags)) {
|
||||
if (replay_running_debug()) {
|
||||
/*
|
||||
* Don't process the watchpoints when we are
|
||||
* in a reverse debugging operation.
|
||||
*/
|
||||
replay_breakpoint();
|
||||
return;
|
||||
}
|
||||
if (flags == BP_MEM_READ) {
|
||||
wp->flags |= BP_WATCHPOINT_HIT_READ;
|
||||
} else {
|
||||
|
64
gdbstub.c
64
gdbstub.c
@ -51,6 +51,7 @@
|
||||
#include "sysemu/runstate.h"
|
||||
#include "hw/semihosting/semihost.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "sysemu/replay.h"
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
#define GDB_ATTACHED "0"
|
||||
@ -375,6 +376,20 @@ typedef struct GDBState {
|
||||
*/
|
||||
static int sstep_flags = SSTEP_ENABLE|SSTEP_NOIRQ|SSTEP_NOTIMER;
|
||||
|
||||
/* Retrieves flags for single step mode. */
|
||||
static int get_sstep_flags(void)
|
||||
{
|
||||
/*
|
||||
* In replay mode all events written into the log should be replayed.
|
||||
* That is why NOIRQ flag is removed in this mode.
|
||||
*/
|
||||
if (replay_mode != REPLAY_MODE_NONE) {
|
||||
return SSTEP_ENABLE;
|
||||
} else {
|
||||
return sstep_flags;
|
||||
}
|
||||
}
|
||||
|
||||
static GDBState gdbserver_state;
|
||||
|
||||
static void init_gdbserver_state(void)
|
||||
@ -501,7 +516,7 @@ static int gdb_continue_partial(char *newstates)
|
||||
break; /* nothing to do here */
|
||||
case 's':
|
||||
trace_gdbstub_op_stepping(cpu->cpu_index);
|
||||
cpu_single_step(cpu, sstep_flags);
|
||||
cpu_single_step(cpu, get_sstep_flags());
|
||||
cpu_resume(cpu);
|
||||
flag = 1;
|
||||
break;
|
||||
@ -1874,10 +1889,38 @@ static void handle_step(GdbCmdContext *gdb_ctx, void *user_ctx)
|
||||
gdb_set_cpu_pc((target_ulong)gdb_ctx->params[0].val_ull);
|
||||
}
|
||||
|
||||
cpu_single_step(gdbserver_state.c_cpu, sstep_flags);
|
||||
cpu_single_step(gdbserver_state.c_cpu, get_sstep_flags());
|
||||
gdb_continue();
|
||||
}
|
||||
|
||||
static void handle_backward(GdbCmdContext *gdb_ctx, void *user_ctx)
|
||||
{
|
||||
if (replay_mode != REPLAY_MODE_PLAY) {
|
||||
put_packet("E22");
|
||||
}
|
||||
if (gdb_ctx->num_params == 1) {
|
||||
switch (gdb_ctx->params[0].opcode) {
|
||||
case 's':
|
||||
if (replay_reverse_step()) {
|
||||
gdb_continue();
|
||||
} else {
|
||||
put_packet("E14");
|
||||
}
|
||||
return;
|
||||
case 'c':
|
||||
if (replay_reverse_continue()) {
|
||||
gdb_continue();
|
||||
} else {
|
||||
put_packet("E14");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Default invalid command */
|
||||
put_packet("");
|
||||
}
|
||||
|
||||
static void handle_v_cont_query(GdbCmdContext *gdb_ctx, void *user_ctx)
|
||||
{
|
||||
put_packet("vCont;c;C;s;S");
|
||||
@ -2124,6 +2167,11 @@ static void handle_query_supported(GdbCmdContext *gdb_ctx, void *user_ctx)
|
||||
g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+");
|
||||
}
|
||||
|
||||
if (replay_mode == REPLAY_MODE_PLAY) {
|
||||
g_string_append(gdbserver_state.str_buf,
|
||||
";ReverseStep+;ReverseContinue+");
|
||||
}
|
||||
|
||||
if (gdb_ctx->num_params &&
|
||||
strstr(gdb_ctx->params[0].data, "multiprocess+")) {
|
||||
gdbserver_state.multiprocess = true;
|
||||
@ -2460,6 +2508,17 @@ static int gdb_handle_packet(const char *line_buf)
|
||||
cmd_parser = &step_cmd_desc;
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
{
|
||||
static const GdbCmdParseEntry backward_cmd_desc = {
|
||||
.handler = handle_backward,
|
||||
.cmd = "b",
|
||||
.cmd_startswith = 1,
|
||||
.schema = "o0"
|
||||
};
|
||||
cmd_parser = &backward_cmd_desc;
|
||||
}
|
||||
break;
|
||||
case 'F':
|
||||
{
|
||||
static const GdbCmdParseEntry file_io_cmd_desc = {
|
||||
@ -3262,6 +3321,7 @@ static void gdb_chr_event(void *opaque, QEMUChrEvent event)
|
||||
s->g_cpu = s->c_cpu;
|
||||
|
||||
vm_stop(RUN_STATE_PAUSED);
|
||||
replay_gdb_attached();
|
||||
gdb_has_xml = false;
|
||||
break;
|
||||
default:
|
||||
|
@ -881,4 +881,15 @@ SRST
|
||||
Show SEV information.
|
||||
ERST
|
||||
|
||||
{
|
||||
.name = "replay",
|
||||
.args_type = "",
|
||||
.params = "",
|
||||
.help = "show record/replay information",
|
||||
.cmd = hmp_info_replay,
|
||||
},
|
||||
|
||||
SRST
|
||||
``info replay``
|
||||
Display the record/replay information: mode and the current icount.
|
||||
ERST
|
||||
|
@ -1804,6 +1804,56 @@ SRST
|
||||
Set QOM property *property* of object at location *path* to value *value*
|
||||
ERST
|
||||
|
||||
{
|
||||
.name = "replay_break",
|
||||
.args_type = "icount:i",
|
||||
.params = "icount",
|
||||
.help = "set breakpoint at the specified instruction count",
|
||||
.cmd = hmp_replay_break,
|
||||
},
|
||||
|
||||
SRST
|
||||
``replay_break`` *icount*
|
||||
Set replay breakpoint at instruction count *icount*.
|
||||
Execution stops when the specified instruction is reached.
|
||||
There can be at most one breakpoint. When breakpoint is set, any prior
|
||||
one is removed. The breakpoint may be set only in replay mode and only
|
||||
"in the future", i.e. at instruction counts greater than the current one.
|
||||
The current instruction count can be observed with ``info replay``.
|
||||
ERST
|
||||
|
||||
{
|
||||
.name = "replay_delete_break",
|
||||
.args_type = "",
|
||||
.params = "",
|
||||
.help = "remove replay breakpoint",
|
||||
.cmd = hmp_replay_delete_break,
|
||||
},
|
||||
|
||||
SRST
|
||||
``replay_delete_break``
|
||||
Remove replay breakpoint which was previously set with ``replay_break``.
|
||||
The command is ignored when there are no replay breakpoints.
|
||||
ERST
|
||||
|
||||
{
|
||||
.name = "replay_seek",
|
||||
.args_type = "icount:i",
|
||||
.params = "icount",
|
||||
.help = "replay execution to the specified instruction count",
|
||||
.cmd = hmp_replay_seek,
|
||||
},
|
||||
|
||||
SRST
|
||||
``replay_seek`` *icount*
|
||||
Automatically proceed to the instruction count *icount*, when
|
||||
replaying the execution. The command automatically loads nearest
|
||||
snapshot and replays the execution to find the desired instruction.
|
||||
When there is no preceding snapshot or the execution is not replayed,
|
||||
then the command fails.
|
||||
*icount* for the reference may be observed with ``info replay`` command.
|
||||
ERST
|
||||
|
||||
{
|
||||
.name = "info",
|
||||
.args_type = "item:s?",
|
||||
|
@ -33,8 +33,7 @@
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "trace/trace-root.h"
|
||||
#include "qemu/plugin.h"
|
||||
|
||||
CPUInterruptHandler cpu_interrupt_handler;
|
||||
#include "sysemu/hw_accel.h"
|
||||
|
||||
CPUState *cpu_by_arch_id(int64_t id)
|
||||
{
|
||||
@ -393,17 +392,6 @@ static vaddr cpu_adjust_watchpoint_address(CPUState *cpu, vaddr addr, int len)
|
||||
return addr;
|
||||
}
|
||||
|
||||
static void generic_handle_interrupt(CPUState *cpu, int mask)
|
||||
{
|
||||
cpu->interrupt_request |= mask;
|
||||
|
||||
if (!qemu_cpu_is_self(cpu)) {
|
||||
qemu_cpu_kick(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
CPUInterruptHandler cpu_interrupt_handler = generic_handle_interrupt;
|
||||
|
||||
static void cpu_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
@ -7,11 +7,11 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/ptimer.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "block/aio.h"
|
||||
#include "sysemu/cpus.h"
|
||||
@ -134,7 +134,8 @@ static void ptimer_reload(ptimer_state *s, int delta_adjust)
|
||||
* on the current generation of host machines.
|
||||
*/
|
||||
|
||||
if (s->enabled == 1 && (delta * period < 10000) && !use_icount) {
|
||||
if (s->enabled == 1 && (delta * period < 10000) &&
|
||||
!icount_enabled() && !qtest_enabled()) {
|
||||
period = 10000 / delta;
|
||||
period_frac = 0;
|
||||
}
|
||||
@ -217,7 +218,8 @@ uint64_t ptimer_get_count(ptimer_state *s)
|
||||
uint32_t period_frac = s->period_frac;
|
||||
uint64_t period = s->period;
|
||||
|
||||
if (!oneshot && (s->delta * period < 10000) && !use_icount) {
|
||||
if (!oneshot && (s->delta * period < 10000) &&
|
||||
!icount_enabled() && !qtest_enabled()) {
|
||||
period = 10000 / s->delta;
|
||||
period_frac = 0;
|
||||
}
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "sysemu/numa.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "trace.h"
|
||||
|
||||
#include "hw/i386/x86.h"
|
||||
@ -521,7 +522,7 @@ static long get_file_size(FILE *f)
|
||||
/* TSC handling */
|
||||
uint64_t cpu_get_tsc(CPUX86State *env)
|
||||
{
|
||||
return cpu_get_ticks();
|
||||
return cpus_get_elapsed_ticks();
|
||||
}
|
||||
|
||||
/* IRQ handling */
|
||||
|
@ -42,6 +42,7 @@ typedef struct QEMUSnapshotInfo {
|
||||
uint32_t date_sec; /* UTC date of the snapshot */
|
||||
uint32_t date_nsec;
|
||||
uint64_t vm_clock_nsec; /* VM clock relative to boot */
|
||||
uint64_t icount; /* record/replay step */
|
||||
} QEMUSnapshotInfo;
|
||||
|
||||
int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
|
||||
|
@ -407,8 +407,12 @@ static inline bool tlb_hit(target_ulong tlb_addr, target_ulong addr)
|
||||
return tlb_hit_page(tlb_addr, addr & TARGET_PAGE_MASK);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TCG
|
||||
void dump_drift_info(void);
|
||||
void dump_exec_info(void);
|
||||
void dump_opcount_info(void);
|
||||
#endif /* CONFIG_TCG */
|
||||
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
|
||||
/* Returns: 0 on success, -1 on error */
|
||||
|
@ -25,7 +25,7 @@
|
||||
#ifdef CONFIG_TCG
|
||||
#include "exec/cpu_ldst.h"
|
||||
#endif
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
|
||||
/* allow to see translation results - the slowdown should be negligible, so we leave it */
|
||||
#define DEBUG_DISAS
|
||||
@ -497,7 +497,7 @@ static inline uint32_t tb_cflags(const TranslationBlock *tb)
|
||||
static inline uint32_t curr_cflags(void)
|
||||
{
|
||||
return (parallel_cpus ? CF_PARALLEL : 0)
|
||||
| (use_icount ? CF_USE_ICOUNT : 0);
|
||||
| (icount_enabled() ? CF_USE_ICOUNT : 0);
|
||||
}
|
||||
|
||||
/* TranslationBlock invalidate API */
|
||||
|
@ -844,12 +844,6 @@ bool cpu_exists(int64_t id);
|
||||
*/
|
||||
CPUState *cpu_by_arch_id(int64_t id);
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
|
||||
typedef void (*CPUInterruptHandler)(CPUState *, int);
|
||||
|
||||
extern CPUInterruptHandler cpu_interrupt_handler;
|
||||
|
||||
/**
|
||||
* cpu_interrupt:
|
||||
* @cpu: The CPU to set an interrupt on.
|
||||
@ -857,17 +851,9 @@ extern CPUInterruptHandler cpu_interrupt_handler;
|
||||
*
|
||||
* Invokes the interrupt handler.
|
||||
*/
|
||||
static inline void cpu_interrupt(CPUState *cpu, int mask)
|
||||
{
|
||||
cpu_interrupt_handler(cpu, mask);
|
||||
}
|
||||
|
||||
#else /* USER_ONLY */
|
||||
|
||||
void cpu_interrupt(CPUState *cpu, int mask);
|
||||
|
||||
#endif /* USER_ONLY */
|
||||
|
||||
#ifdef NEED_CPU_H
|
||||
|
||||
#ifdef CONFIG_SOFTMMU
|
||||
|
@ -129,5 +129,9 @@ void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_sev(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_replay(Monitor *mon, const QDict *qdict);
|
||||
void hmp_replay_break(Monitor *mon, const QDict *qdict);
|
||||
void hmp_replay_delete_break(Monitor *mon, const QDict *qdict);
|
||||
void hmp_replay_seek(Monitor *mon, const QDict *qdict);
|
||||
|
||||
#endif
|
||||
|
@ -166,8 +166,8 @@ bool qemu_clock_expired(QEMUClockType type);
|
||||
*
|
||||
* Determine whether a clock should be used for deadline
|
||||
* calculations. Some clocks, for instance vm_clock with
|
||||
* use_icount set, do not count in nanoseconds. Such clocks
|
||||
* are not used for deadline calculations, and are presumed
|
||||
* icount_enabled() set, do not count in nanoseconds.
|
||||
* Such clocks are not used for deadline calculations, and are presumed
|
||||
* to interrupt any poll using qemu_notify/aio_notify
|
||||
* etc.
|
||||
*
|
||||
@ -224,13 +224,6 @@ void qemu_clock_notify(QEMUClockType type);
|
||||
*/
|
||||
void qemu_clock_enable(QEMUClockType type, bool enabled);
|
||||
|
||||
/**
|
||||
* qemu_start_warp_timer:
|
||||
*
|
||||
* Starts a timer for virtual clock update
|
||||
*/
|
||||
void qemu_start_warp_timer(void);
|
||||
|
||||
/**
|
||||
* qemu_clock_run_timers:
|
||||
* @type: clock on which to operate
|
||||
@ -791,12 +784,6 @@ static inline int64_t qemu_soonest_timeout(int64_t timeout1, int64_t timeout2)
|
||||
*/
|
||||
void init_clocks(QEMUTimerListNotifyCB *notify_cb);
|
||||
|
||||
int64_t cpu_get_ticks(void);
|
||||
/* Caller must hold BQL */
|
||||
void cpu_enable_ticks(void);
|
||||
/* Caller must hold BQL */
|
||||
void cpu_disable_ticks(void);
|
||||
|
||||
static inline int64_t get_max_clock_jump(void)
|
||||
{
|
||||
/* This should be small enough to prevent excessive interrupts from being
|
||||
@ -850,13 +837,6 @@ static inline int64_t get_clock(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* icount */
|
||||
int64_t cpu_get_icount_raw(void);
|
||||
int64_t cpu_get_icount(void);
|
||||
int64_t cpu_get_clock(void);
|
||||
int64_t cpu_icount_to_ns(int64_t icount);
|
||||
void cpu_update_icount(CPUState *cpu);
|
||||
|
||||
/*******************************************/
|
||||
/* host CPU ticks (if available) */
|
||||
|
||||
|
90
include/sysemu/cpu-timers.h
Normal file
90
include/sysemu/cpu-timers.h
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* CPU timers state API
|
||||
*
|
||||
* Copyright 2020 SUSE LLC
|
||||
*
|
||||
* 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 SYSEMU_CPU_TIMERS_H
|
||||
#define SYSEMU_CPU_TIMERS_H
|
||||
|
||||
#include "qemu/timer.h"
|
||||
|
||||
/* init the whole cpu timers API, including icount, ticks, and cpu_throttle */
|
||||
void cpu_timers_init(void);
|
||||
|
||||
/* icount - Instruction Counter API */
|
||||
|
||||
/*
|
||||
* icount enablement state:
|
||||
*
|
||||
* 0 = Disabled - Do not count executed instructions.
|
||||
* 1 = Enabled - Fixed conversion of insn to ns via "shift" option
|
||||
* 2 = Enabled - Runtime adaptive algorithm to compute shift
|
||||
*/
|
||||
#ifdef CONFIG_TCG
|
||||
extern int use_icount;
|
||||
#define icount_enabled() (use_icount)
|
||||
#else
|
||||
#define icount_enabled() 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Update the icount with the executed instructions. Called by
|
||||
* cpus-tcg vCPU thread so the main-loop can see time has moved forward.
|
||||
*/
|
||||
void icount_update(CPUState *cpu);
|
||||
|
||||
/* get raw icount value */
|
||||
int64_t icount_get_raw(void);
|
||||
|
||||
/* return the virtual CPU time in ns, based on the instruction counter. */
|
||||
int64_t icount_get(void);
|
||||
/*
|
||||
* convert an instruction counter value to ns, based on the icount shift.
|
||||
* This shift is set as a fixed value with the icount "shift" option
|
||||
* (precise mode), or it is constantly approximated and corrected at
|
||||
* runtime in adaptive mode.
|
||||
*/
|
||||
int64_t icount_to_ns(int64_t icount);
|
||||
|
||||
/* configure the icount options, including "shift" */
|
||||
void icount_configure(QemuOpts *opts, Error **errp);
|
||||
|
||||
/* used by tcg vcpu thread to calc icount budget */
|
||||
int64_t icount_round(int64_t count);
|
||||
|
||||
/* if the CPUs are idle, start accounting real time to virtual clock. */
|
||||
void icount_start_warp_timer(void);
|
||||
void icount_account_warp_timer(void);
|
||||
|
||||
/*
|
||||
* CPU Ticks and Clock
|
||||
*/
|
||||
|
||||
/* Caller must hold BQL */
|
||||
void cpu_enable_ticks(void);
|
||||
/* Caller must hold BQL */
|
||||
void cpu_disable_ticks(void);
|
||||
|
||||
/*
|
||||
* return the time elapsed in VM between vm_start and vm_stop.
|
||||
* cpu_get_ticks() uses units of the host CPU cycle counter.
|
||||
*/
|
||||
int64_t cpu_get_ticks(void);
|
||||
|
||||
/*
|
||||
* Returns the monotonic time elapsed in VM, i.e.,
|
||||
* the time between vm_start and vm_stop
|
||||
*/
|
||||
int64_t cpu_get_clock(void);
|
||||
|
||||
void qemu_timer_notify_cb(void *opaque, QEMUClockType type);
|
||||
|
||||
/* get the VIRTUAL clock and VM elapsed ticks via the cpus accel interface */
|
||||
int64_t cpus_get_virtual_clock(void);
|
||||
int64_t cpus_get_elapsed_ticks(void);
|
||||
|
||||
#endif /* SYSEMU_CPU_TIMERS_H */
|
@ -4,33 +4,61 @@
|
||||
#include "qemu/timer.h"
|
||||
|
||||
/* cpus.c */
|
||||
|
||||
/* CPU execution threads */
|
||||
|
||||
typedef struct CpusAccel {
|
||||
void (*create_vcpu_thread)(CPUState *cpu); /* MANDATORY */
|
||||
void (*kick_vcpu_thread)(CPUState *cpu);
|
||||
|
||||
void (*synchronize_post_reset)(CPUState *cpu);
|
||||
void (*synchronize_post_init)(CPUState *cpu);
|
||||
void (*synchronize_state)(CPUState *cpu);
|
||||
void (*synchronize_pre_loadvm)(CPUState *cpu);
|
||||
|
||||
void (*handle_interrupt)(CPUState *cpu, int mask);
|
||||
|
||||
int64_t (*get_virtual_clock)(void);
|
||||
int64_t (*get_elapsed_ticks)(void);
|
||||
} CpusAccel;
|
||||
|
||||
/* register accel-specific cpus interface implementation */
|
||||
void cpus_register_accel(const CpusAccel *i);
|
||||
|
||||
/* interface available for cpus accelerator threads */
|
||||
|
||||
/* For temporary buffers for forming a name */
|
||||
#define VCPU_THREAD_NAME_SIZE 16
|
||||
|
||||
void cpus_kick_thread(CPUState *cpu);
|
||||
bool cpu_work_list_empty(CPUState *cpu);
|
||||
bool cpu_thread_is_idle(CPUState *cpu);
|
||||
bool all_cpu_threads_idle(void);
|
||||
bool cpu_can_run(CPUState *cpu);
|
||||
void qemu_wait_io_event_common(CPUState *cpu);
|
||||
void qemu_wait_io_event(CPUState *cpu);
|
||||
void cpu_thread_signal_created(CPUState *cpu);
|
||||
void cpu_thread_signal_destroyed(CPUState *cpu);
|
||||
void cpu_handle_guest_debug(CPUState *cpu);
|
||||
|
||||
/* end interface for cpus accelerator threads */
|
||||
|
||||
bool qemu_in_vcpu_thread(void);
|
||||
void qemu_init_cpu_loop(void);
|
||||
void resume_all_vcpus(void);
|
||||
void pause_all_vcpus(void);
|
||||
void cpu_stop_current(void);
|
||||
void cpu_ticks_init(void);
|
||||
|
||||
void configure_icount(QemuOpts *opts, Error **errp);
|
||||
extern int use_icount;
|
||||
extern int icount_align_option;
|
||||
|
||||
/* drift information for info jit command */
|
||||
extern int64_t max_delay;
|
||||
extern int64_t max_advance;
|
||||
void dump_drift_info(void);
|
||||
|
||||
/* Unblock cpu */
|
||||
void qemu_cpu_kick_self(void);
|
||||
void qemu_timer_notify_cb(void *opaque, QEMUClockType type);
|
||||
|
||||
void cpu_synchronize_all_states(void);
|
||||
void cpu_synchronize_all_post_reset(void);
|
||||
void cpu_synchronize_all_post_init(void);
|
||||
void cpu_synchronize_all_pre_loadvm(void);
|
||||
|
||||
void qtest_clock_warp(int64_t dest);
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
/* vl.c */
|
||||
/* *-user doesn't have configurable SMP topology */
|
||||
|
@ -22,29 +22,12 @@
|
||||
#ifndef QEMU_HAX_H
|
||||
#define QEMU_HAX_H
|
||||
|
||||
|
||||
int hax_sync_vcpus(void);
|
||||
int hax_init_vcpu(CPUState *cpu);
|
||||
int hax_smp_cpu_exec(CPUState *cpu);
|
||||
int hax_populate_ram(uint64_t va, uint64_t size);
|
||||
|
||||
void hax_cpu_synchronize_state(CPUState *cpu);
|
||||
void hax_cpu_synchronize_post_reset(CPUState *cpu);
|
||||
void hax_cpu_synchronize_post_init(CPUState *cpu);
|
||||
void hax_cpu_synchronize_pre_loadvm(CPUState *cpu);
|
||||
|
||||
#ifdef CONFIG_HAX
|
||||
|
||||
int hax_enabled(void);
|
||||
|
||||
#include "qemu/bitops.h"
|
||||
#include "exec/memory.h"
|
||||
int hax_vcpu_destroy(CPUState *cpu);
|
||||
void hax_raise_event(CPUState *cpu);
|
||||
void hax_reset_vcpu_state(void *opaque);
|
||||
#include "target/i386/hax-interface.h"
|
||||
#include "target/i386/hax-i386.h"
|
||||
|
||||
#else /* CONFIG_HAX */
|
||||
|
||||
#define hax_enabled() (0)
|
||||
|
@ -26,14 +26,6 @@ extern bool hvf_allowed;
|
||||
#define hvf_get_supported_cpuid(func, idx, reg) 0
|
||||
#endif /* !CONFIG_HVF */
|
||||
|
||||
int hvf_init_vcpu(CPUState *);
|
||||
int hvf_vcpu_exec(CPUState *);
|
||||
void hvf_cpu_synchronize_state(CPUState *);
|
||||
void hvf_cpu_synchronize_post_reset(CPUState *);
|
||||
void hvf_cpu_synchronize_post_init(CPUState *);
|
||||
void hvf_cpu_synchronize_pre_loadvm(CPUState *);
|
||||
void hvf_vcpu_destroy(CPUState *);
|
||||
|
||||
#define TYPE_HVF_ACCEL ACCEL_CLASS_NAME("hvf")
|
||||
|
||||
typedef struct HVFState HVFState;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* QEMU Hardware accelertors support
|
||||
* QEMU Hardware accelerators support
|
||||
*
|
||||
* Copyright 2016 Google, Inc.
|
||||
*
|
||||
@ -17,68 +17,9 @@
|
||||
#include "sysemu/hvf.h"
|
||||
#include "sysemu/whpx.h"
|
||||
|
||||
static inline void cpu_synchronize_state(CPUState *cpu)
|
||||
{
|
||||
if (kvm_enabled()) {
|
||||
kvm_cpu_synchronize_state(cpu);
|
||||
}
|
||||
if (hax_enabled()) {
|
||||
hax_cpu_synchronize_state(cpu);
|
||||
}
|
||||
if (hvf_enabled()) {
|
||||
hvf_cpu_synchronize_state(cpu);
|
||||
}
|
||||
if (whpx_enabled()) {
|
||||
whpx_cpu_synchronize_state(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void cpu_synchronize_post_reset(CPUState *cpu)
|
||||
{
|
||||
if (kvm_enabled()) {
|
||||
kvm_cpu_synchronize_post_reset(cpu);
|
||||
}
|
||||
if (hax_enabled()) {
|
||||
hax_cpu_synchronize_post_reset(cpu);
|
||||
}
|
||||
if (hvf_enabled()) {
|
||||
hvf_cpu_synchronize_post_reset(cpu);
|
||||
}
|
||||
if (whpx_enabled()) {
|
||||
whpx_cpu_synchronize_post_reset(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void cpu_synchronize_post_init(CPUState *cpu)
|
||||
{
|
||||
if (kvm_enabled()) {
|
||||
kvm_cpu_synchronize_post_init(cpu);
|
||||
}
|
||||
if (hax_enabled()) {
|
||||
hax_cpu_synchronize_post_init(cpu);
|
||||
}
|
||||
if (hvf_enabled()) {
|
||||
hvf_cpu_synchronize_post_init(cpu);
|
||||
}
|
||||
if (whpx_enabled()) {
|
||||
whpx_cpu_synchronize_post_init(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void cpu_synchronize_pre_loadvm(CPUState *cpu)
|
||||
{
|
||||
if (kvm_enabled()) {
|
||||
kvm_cpu_synchronize_pre_loadvm(cpu);
|
||||
}
|
||||
if (hax_enabled()) {
|
||||
hax_cpu_synchronize_pre_loadvm(cpu);
|
||||
}
|
||||
if (hvf_enabled()) {
|
||||
hvf_cpu_synchronize_pre_loadvm(cpu);
|
||||
}
|
||||
if (whpx_enabled()) {
|
||||
whpx_cpu_synchronize_pre_loadvm(cpu);
|
||||
}
|
||||
}
|
||||
void cpu_synchronize_state(CPUState *cpu);
|
||||
void cpu_synchronize_post_reset(CPUState *cpu);
|
||||
void cpu_synchronize_post_init(CPUState *cpu);
|
||||
void cpu_synchronize_pre_loadvm(CPUState *cpu);
|
||||
|
||||
#endif /* QEMU_HW_ACCEL_H */
|
||||
|
@ -223,10 +223,6 @@ int kvm_has_many_ioeventfds(void);
|
||||
int kvm_has_gsi_routing(void);
|
||||
int kvm_has_intx_set_mask(void);
|
||||
|
||||
int kvm_init_vcpu(CPUState *cpu);
|
||||
int kvm_cpu_exec(CPUState *cpu);
|
||||
int kvm_destroy_vcpu(CPUState *cpu);
|
||||
|
||||
/**
|
||||
* kvm_arm_supports_user_irq
|
||||
*
|
||||
@ -486,9 +482,6 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram_addr,
|
||||
#endif /* NEED_CPU_H */
|
||||
|
||||
void kvm_cpu_synchronize_state(CPUState *cpu);
|
||||
void kvm_cpu_synchronize_post_reset(CPUState *cpu);
|
||||
void kvm_cpu_synchronize_post_init(CPUState *cpu);
|
||||
void kvm_cpu_synchronize_pre_loadvm(CPUState *cpu);
|
||||
|
||||
void kvm_init_cpu_signals(CPUState *cpu);
|
||||
|
||||
|
@ -30,4 +30,6 @@ void qtest_server_set_send_handler(void (*send)(void *, const char *),
|
||||
void *opaque);
|
||||
void qtest_server_inproc_recv(void *opaque, const char *buf);
|
||||
|
||||
int64_t qtest_get_virtual_clock(void);
|
||||
|
||||
#endif
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "qapi/qapi-types-misc.h"
|
||||
#include "qapi/qapi-types-run-state.h"
|
||||
#include "qapi/qapi-types-replay.h"
|
||||
#include "qapi/qapi-types-ui.h"
|
||||
#include "block/aio.h"
|
||||
|
||||
@ -72,6 +73,29 @@ void replay_start(void);
|
||||
void replay_finish(void);
|
||||
/*! Adds replay blocker with the specified error description */
|
||||
void replay_add_blocker(Error *reason);
|
||||
/* Returns name of the replay log file */
|
||||
const char *replay_get_filename(void);
|
||||
/*
|
||||
* Start making one step in backward direction.
|
||||
* Used by gdbstub for backwards debugging.
|
||||
* Returns true on success.
|
||||
*/
|
||||
bool replay_reverse_step(void);
|
||||
/*
|
||||
* Start searching the last breakpoint/watchpoint.
|
||||
* Used by gdbstub for backwards debugging.
|
||||
* Returns true if the process successfully started.
|
||||
*/
|
||||
bool replay_reverse_continue(void);
|
||||
/*
|
||||
* Returns true if replay module is processing
|
||||
* reverse_continue or reverse_step request
|
||||
*/
|
||||
bool replay_running_debug(void);
|
||||
/* Called in reverse debugging mode to collect breakpoint information */
|
||||
void replay_breakpoint(void);
|
||||
/* Called when gdb is attached to gdbstub */
|
||||
void replay_gdb_attached(void);
|
||||
|
||||
/* Processing the instructions */
|
||||
|
||||
@ -109,12 +133,12 @@ int64_t replay_read_clock(ReplayClockKind kind);
|
||||
#define REPLAY_CLOCK(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()) \
|
||||
? replay_save_clock((clock), (value), icount_get_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()) \
|
||||
? replay_save_clock((clock), (value), icount_get_raw_locked()) \
|
||||
: (value))
|
||||
|
||||
/* Processing data from random generators */
|
||||
@ -146,6 +170,8 @@ void replay_disable_events(void);
|
||||
void replay_enable_events(void);
|
||||
/*! Returns true when saving events is enabled */
|
||||
bool replay_events_enabled(void);
|
||||
/* Flushes events queue */
|
||||
void replay_flush_events(void);
|
||||
/*! Adds bottom half event to the queue */
|
||||
void replay_bh_schedule_event(QEMUBH *bh);
|
||||
/* Adds oneshot bottom half event to the queue */
|
||||
|
@ -13,18 +13,6 @@
|
||||
#ifndef QEMU_WHPX_H
|
||||
#define QEMU_WHPX_H
|
||||
|
||||
|
||||
int whpx_init_vcpu(CPUState *cpu);
|
||||
int whpx_vcpu_exec(CPUState *cpu);
|
||||
void whpx_destroy_vcpu(CPUState *cpu);
|
||||
void whpx_vcpu_kick(CPUState *cpu);
|
||||
|
||||
|
||||
void whpx_cpu_synchronize_state(CPUState *cpu);
|
||||
void whpx_cpu_synchronize_post_reset(CPUState *cpu);
|
||||
void whpx_cpu_synchronize_post_init(CPUState *cpu);
|
||||
void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu);
|
||||
|
||||
#ifdef CONFIG_WHPX
|
||||
|
||||
int whpx_enabled(void);
|
||||
@ -35,11 +23,4 @@ int whpx_enabled(void);
|
||||
|
||||
#endif /* CONFIG_WHPX */
|
||||
|
||||
/* state subset only touched by the VCPU itself during runtime */
|
||||
#define WHPX_SET_RUNTIME_STATE 1
|
||||
/* state subset modified during VCPU reset */
|
||||
#define WHPX_SET_RESET_STATE 2
|
||||
/* full state set, modified during initialization or on vmload */
|
||||
#define WHPX_SET_FULL_STATE 3
|
||||
|
||||
#endif /* QEMU_WHPX_H */
|
||||
|
164
meson.build
164
meson.build
@ -300,11 +300,6 @@ else
|
||||
xkbcommon = dependency('xkbcommon', required: get_option('xkbcommon'),
|
||||
method: 'pkg-config', static: enable_static)
|
||||
endif
|
||||
slirp = not_found
|
||||
if config_host.has_key('CONFIG_SLIRP')
|
||||
slirp = declare_dependency(compile_args: config_host['SLIRP_CFLAGS'].split(),
|
||||
link_args: config_host['SLIRP_LIBS'].split())
|
||||
endif
|
||||
vde = not_found
|
||||
if config_host.has_key('CONFIG_VDE')
|
||||
vde = declare_dependency(link_args: config_host['VDE_LIBS'].split())
|
||||
@ -536,11 +531,6 @@ if get_option('vnc').enabled()
|
||||
compile_args: '-DSTRUCT_IOVEC_DEFINED')
|
||||
endif
|
||||
endif
|
||||
fdt = not_found
|
||||
if 'CONFIG_FDT' in config_host
|
||||
fdt = declare_dependency(compile_args: config_host['FDT_CFLAGS'].split(),
|
||||
link_args: config_host['FDT_LIBS'].split())
|
||||
endif
|
||||
snappy = not_found
|
||||
if 'CONFIG_SNAPPY' in config_host
|
||||
snappy = declare_dependency(link_args: config_host['SNAPPY_LIBS'].split())
|
||||
@ -728,6 +718,7 @@ ignored = [ 'TARGET_XML_FILES', 'TARGET_ABI_DIR', 'TARGET_ARCH' ]
|
||||
|
||||
default_targets = 'CONFIG_DEFAULT_TARGETS' in config_host
|
||||
actual_target_dirs = []
|
||||
fdt_required = []
|
||||
foreach target : target_dirs
|
||||
config_target = { 'TARGET_NAME': target.split('-')[0] }
|
||||
if target.endswith('linux-user')
|
||||
@ -779,6 +770,10 @@ foreach target : target_dirs
|
||||
config_target += keyval.load('default-configs/targets' / target + '.mak')
|
||||
config_target += { 'TARGET_' + config_target['TARGET_ARCH'].to_upper(): 'y' }
|
||||
|
||||
if 'TARGET_NEED_FDT' in config_target
|
||||
fdt_required += target
|
||||
endif
|
||||
|
||||
# Add default keys
|
||||
if 'TARGET_BASE_ARCH' not in config_target
|
||||
config_target += {'TARGET_BASE_ARCH': config_target['TARGET_ARCH']}
|
||||
@ -978,7 +973,135 @@ if capstone_opt == 'internal'
|
||||
capstone = declare_dependency(link_with: libcapstone,
|
||||
include_directories: 'capstone/include/capstone')
|
||||
endif
|
||||
|
||||
slirp = not_found
|
||||
slirp_opt = 'disabled'
|
||||
if have_system
|
||||
slirp_opt = get_option('slirp')
|
||||
if slirp_opt in ['enabled', 'auto', 'system']
|
||||
have_internal = fs.exists(meson.current_source_dir() / 'slirp/meson.build')
|
||||
slirp = dependency('slirp', static: enable_static,
|
||||
method: 'pkg-config',
|
||||
required: slirp_opt == 'system' or
|
||||
slirp_opt == 'enabled' and not have_internal)
|
||||
if slirp.found()
|
||||
slirp_opt = 'system'
|
||||
elif have_internal
|
||||
slirp_opt = 'internal'
|
||||
else
|
||||
slirp_opt = 'disabled'
|
||||
endif
|
||||
endif
|
||||
if slirp_opt == 'internal'
|
||||
slirp_deps = []
|
||||
if targetos == 'windows'
|
||||
slirp_deps = cc.find_library('iphlpapi')
|
||||
endif
|
||||
slirp_conf = configuration_data()
|
||||
slirp_conf.set('SLIRP_MAJOR_VERSION', meson.project_version().split('.')[0])
|
||||
slirp_conf.set('SLIRP_MINOR_VERSION', meson.project_version().split('.')[1])
|
||||
slirp_conf.set('SLIRP_MICRO_VERSION', meson.project_version().split('.')[2])
|
||||
slirp_conf.set_quoted('SLIRP_VERSION_STRING', meson.project_version())
|
||||
slirp_cargs = ['-DG_LOG_DOMAIN="Slirp"']
|
||||
slirp_files = [
|
||||
'slirp/src/arp_table.c',
|
||||
'slirp/src/bootp.c',
|
||||
'slirp/src/cksum.c',
|
||||
'slirp/src/dhcpv6.c',
|
||||
'slirp/src/dnssearch.c',
|
||||
'slirp/src/if.c',
|
||||
'slirp/src/ip6_icmp.c',
|
||||
'slirp/src/ip6_input.c',
|
||||
'slirp/src/ip6_output.c',
|
||||
'slirp/src/ip_icmp.c',
|
||||
'slirp/src/ip_input.c',
|
||||
'slirp/src/ip_output.c',
|
||||
'slirp/src/mbuf.c',
|
||||
'slirp/src/misc.c',
|
||||
'slirp/src/ncsi.c',
|
||||
'slirp/src/ndp_table.c',
|
||||
'slirp/src/sbuf.c',
|
||||
'slirp/src/slirp.c',
|
||||
'slirp/src/socket.c',
|
||||
'slirp/src/state.c',
|
||||
'slirp/src/stream.c',
|
||||
'slirp/src/tcp_input.c',
|
||||
'slirp/src/tcp_output.c',
|
||||
'slirp/src/tcp_subr.c',
|
||||
'slirp/src/tcp_timer.c',
|
||||
'slirp/src/tftp.c',
|
||||
'slirp/src/udp.c',
|
||||
'slirp/src/udp6.c',
|
||||
'slirp/src/util.c',
|
||||
'slirp/src/version.c',
|
||||
'slirp/src/vmstate.c',
|
||||
]
|
||||
|
||||
configure_file(
|
||||
input : 'slirp/src/libslirp-version.h.in',
|
||||
output : 'libslirp-version.h',
|
||||
configuration: slirp_conf)
|
||||
|
||||
slirp_inc = include_directories('slirp', 'slirp/src')
|
||||
libslirp = static_library('slirp',
|
||||
sources: slirp_files,
|
||||
c_args: slirp_cargs,
|
||||
include_directories: slirp_inc)
|
||||
slirp = declare_dependency(link_with: libslirp,
|
||||
dependencies: slirp_deps,
|
||||
include_directories: slirp_inc)
|
||||
endif
|
||||
endif
|
||||
|
||||
fdt = not_found
|
||||
fdt_opt = get_option('fdt')
|
||||
if have_system
|
||||
if fdt_opt in ['enabled', 'auto', 'system']
|
||||
have_internal = fs.exists(meson.current_source_dir() / 'dtc/libfdt/Makefile.libfdt')
|
||||
fdt = cc.find_library('fdt', static: enable_static,
|
||||
required: fdt_opt == 'system' or
|
||||
fdt_opt == 'enabled' and not have_internal)
|
||||
if fdt.found() and cc.links('''
|
||||
#include <libfdt.h>
|
||||
#include <libfdt_env.h>
|
||||
int main(void) { fdt_check_full(NULL, 0); return 0; }''',
|
||||
dependencies: fdt)
|
||||
fdt_opt = 'system'
|
||||
elif have_internal
|
||||
fdt_opt = 'internal'
|
||||
else
|
||||
fdt_opt = 'disabled'
|
||||
endif
|
||||
endif
|
||||
if fdt_opt == 'internal'
|
||||
fdt_files = files(
|
||||
'dtc/libfdt/fdt.c',
|
||||
'dtc/libfdt/fdt_ro.c',
|
||||
'dtc/libfdt/fdt_wip.c',
|
||||
'dtc/libfdt/fdt_sw.c',
|
||||
'dtc/libfdt/fdt_rw.c',
|
||||
'dtc/libfdt/fdt_strerror.c',
|
||||
'dtc/libfdt/fdt_empty_tree.c',
|
||||
'dtc/libfdt/fdt_addresses.c',
|
||||
'dtc/libfdt/fdt_overlay.c',
|
||||
'dtc/libfdt/fdt_check.c',
|
||||
)
|
||||
|
||||
fdt_inc = include_directories('dtc/libfdt')
|
||||
libfdt = static_library('fdt',
|
||||
sources: fdt_files,
|
||||
include_directories: fdt_inc)
|
||||
fdt = declare_dependency(link_with: libfdt,
|
||||
include_directories: fdt_inc)
|
||||
endif
|
||||
endif
|
||||
if not fdt.found() and fdt_required.length() > 0
|
||||
error('fdt not available but required by targets ' + ', '.join(fdt_required))
|
||||
endif
|
||||
|
||||
config_host_data.set('CONFIG_CAPSTONE', capstone.found())
|
||||
config_host_data.set('CONFIG_FDT', fdt.found())
|
||||
config_host_data.set('CONFIG_SLIRP', slirp.found())
|
||||
|
||||
genh += configure_file(output: 'config-host.h', configuration: config_host_data)
|
||||
|
||||
@ -1247,7 +1370,7 @@ softmmu_ss.add(files(
|
||||
|
||||
softmmu_ss.add(when: 'CONFIG_TPM', if_true: files('tpm.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_SECCOMP', if_true: [files('qemu-seccomp.c'), seccomp])
|
||||
softmmu_ss.add(when: ['CONFIG_FDT', fdt], if_true: [files('device_tree.c')])
|
||||
softmmu_ss.add(when: fdt, if_true: files('device_tree.c'))
|
||||
|
||||
common_ss.add(files('cpus-common.c'))
|
||||
|
||||
@ -1648,7 +1771,18 @@ if targetos == 'darwin'
|
||||
summary_info += {'Objective-C compiler': meson.get_compiler('objc').cmd_array()[0]}
|
||||
endif
|
||||
summary_info += {'ARFLAGS': config_host['ARFLAGS']}
|
||||
summary_info += {'CFLAGS': config_host['CFLAGS']}
|
||||
summary_info += {'CFLAGS': ' '.join(get_option('c_args')
|
||||
+ ['-O' + get_option('optimization')]
|
||||
+ (get_option('debug') ? ['-g'] : []))}
|
||||
if link_language == 'cpp'
|
||||
summary_info += {'CXXFLAGS': ' '.join(get_option('cpp_args')
|
||||
+ ['-O' + get_option('optimization')]
|
||||
+ (get_option('debug') ? ['-g'] : []))}
|
||||
endif
|
||||
link_args = get_option(link_language + '_link_args')
|
||||
if link_args.length() > 0
|
||||
summary_info += {'LDFLAGS': ' '.join(link_args)}
|
||||
endif
|
||||
summary_info += {'QEMU_CFLAGS': config_host['QEMU_CFLAGS']}
|
||||
summary_info += {'QEMU_LDFLAGS': config_host['QEMU_LDFLAGS']}
|
||||
summary_info += {'make': config_host['MAKE']}
|
||||
@ -1656,8 +1790,8 @@ summary_info += {'python': '@0@ (version: @1@)'.format(python.full_pa
|
||||
summary_info += {'sphinx-build': config_host['SPHINX_BUILD']}
|
||||
summary_info += {'genisoimage': config_host['GENISOIMAGE']}
|
||||
# TODO: add back version
|
||||
summary_info += {'slirp support': config_host.has_key('CONFIG_SLIRP')}
|
||||
if config_host.has_key('CONFIG_SLIRP')
|
||||
summary_info += {'slirp support': slirp_opt == 'disabled' ? false : slirp_opt}
|
||||
if slirp_opt != 'disabled'
|
||||
summary_info += {'smbd': config_host['CONFIG_SMBD_COMMAND']}
|
||||
endif
|
||||
summary_info += {'module support': config_host.has_key('CONFIG_MODULES')}
|
||||
@ -1741,7 +1875,7 @@ endif
|
||||
summary_info += {'malloc trim support': has_malloc_trim}
|
||||
summary_info += {'RDMA support': config_host.has_key('CONFIG_RDMA')}
|
||||
summary_info += {'PVRDMA support': config_host.has_key('CONFIG_PVRDMA')}
|
||||
summary_info += {'fdt support': config_host.has_key('CONFIG_FDT')}
|
||||
summary_info += {'fdt support': fdt_opt == 'disabled' ? false : fdt_opt}
|
||||
summary_info += {'membarrier': config_host.has_key('CONFIG_MEMBARRIER')}
|
||||
summary_info += {'preadv support': config_host.has_key('CONFIG_PREADV')}
|
||||
summary_info += {'fdatasync': config_host.has_key('CONFIG_FDATASYNC')}
|
||||
|
@ -52,3 +52,9 @@ option('xkbcommon', type : 'feature', value : 'auto',
|
||||
option('capstone', type: 'combo', value: 'auto',
|
||||
choices: ['disabled', 'enabled', 'auto', 'system', 'internal'],
|
||||
description: 'Whether and how to find the capstone library')
|
||||
option('slirp', type: 'combo', value: 'auto',
|
||||
choices: ['disabled', 'enabled', 'auto', 'system', 'internal'],
|
||||
description: 'Whether and how to find the slirp library')
|
||||
option('fdt', type: 'combo', value: 'auto',
|
||||
choices: ['disabled', 'enabled', 'auto', 'system', 'internal'],
|
||||
description: 'Whether and how to find the libfdt library')
|
||||
|
@ -2723,6 +2723,11 @@ int save_snapshot(const char *name, Error **errp)
|
||||
sn->date_sec = tv.tv_sec;
|
||||
sn->date_nsec = tv.tv_usec * 1000;
|
||||
sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
if (replay_mode != REPLAY_MODE_NONE) {
|
||||
sn->icount = replay_get_current_icount();
|
||||
} else {
|
||||
sn->icount = -1ULL;
|
||||
}
|
||||
|
||||
if (name) {
|
||||
ret = bdrv_snapshot_find(bs, old_sn, name);
|
||||
@ -2876,12 +2881,6 @@ int load_snapshot(const char *name, Error **errp)
|
||||
AioContext *aio_context;
|
||||
MigrationIncomingState *mis = migration_incoming_get_current();
|
||||
|
||||
if (!replay_can_snapshot()) {
|
||||
error_setg(errp, "Record/replay does not allow loading snapshot "
|
||||
"right now. Try once more later.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!bdrv_all_can_snapshot(&bs)) {
|
||||
error_setg(errp,
|
||||
"Device '%s' is writable but does not support snapshots",
|
||||
@ -2915,6 +2914,12 @@ int load_snapshot(const char *name, Error **errp)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush the record/replay queue. Now the VM state is going
|
||||
* to change. Therefore we don't need to preserve its consistency
|
||||
*/
|
||||
replay_flush_events();
|
||||
|
||||
/* Flush all IO requests so they don't interfere with the new state. */
|
||||
bdrv_drain_all_begin();
|
||||
|
||||
|
@ -18,7 +18,7 @@ softmmu_ss.add(files(
|
||||
))
|
||||
|
||||
softmmu_ss.add(when: 'CONFIG_L2TPV3', if_true: files('l2tpv3.c'))
|
||||
softmmu_ss.add(when: ['CONFIG_SLIRP', slirp], if_true: files('slirp.c'))
|
||||
softmmu_ss.add(when: slirp, if_true: files('slirp.c'))
|
||||
softmmu_ss.add(when: ['CONFIG_VDE', vde], if_true: files('vde.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_NETMAP', if_true: files('netmap.c'))
|
||||
vhost_user_ss = ss.source_set()
|
||||
|
@ -27,13 +27,19 @@
|
||||
#
|
||||
# @vm-clock-nsec: fractional part in nano seconds to be used with vm-clock-sec
|
||||
#
|
||||
# @icount: Current instruction count. Appears when execution record/replay
|
||||
# is enabled. Used for "time-traveling" to match the moment
|
||||
# in the recorded execution with the snapshots. This counter may
|
||||
# be obtained through @query-replay command (since 5.2)
|
||||
#
|
||||
# Since: 1.3
|
||||
#
|
||||
##
|
||||
{ 'struct': 'SnapshotInfo',
|
||||
'data': { 'id': 'str', 'name': 'str', 'vm-state-size': 'int',
|
||||
'date-sec': 'int', 'date-nsec': 'int',
|
||||
'vm-clock-sec': 'int', 'vm-clock-nsec': 'int' } }
|
||||
'vm-clock-sec': 'int', 'vm-clock-nsec': 'int',
|
||||
'*icount': 'int' } }
|
||||
|
||||
##
|
||||
# @ImageInfoSpecificQCow2EncryptionBase:
|
||||
@ -5363,7 +5369,8 @@
|
||||
# "date-sec": 1000012,
|
||||
# "date-nsec": 10,
|
||||
# "vm-clock-sec": 100,
|
||||
# "vm-clock-nsec": 20
|
||||
# "vm-clock-nsec": 20,
|
||||
# "icount": 220414
|
||||
# }
|
||||
# }
|
||||
#
|
||||
|
@ -39,6 +39,7 @@ qapi_all_modules = [
|
||||
'pci',
|
||||
'qom',
|
||||
'rdma',
|
||||
'replay',
|
||||
'rocker',
|
||||
'run-state',
|
||||
'sockets',
|
||||
|
@ -757,24 +757,6 @@
|
||||
'returns': ['CommandLineOptionInfo'],
|
||||
'allow-preconfig': true }
|
||||
|
||||
##
|
||||
# @ReplayMode:
|
||||
#
|
||||
# Mode of the replay subsystem.
|
||||
#
|
||||
# @none: normal execution mode. Replay or record are not enabled.
|
||||
#
|
||||
# @record: record mode. All non-deterministic data is written into the
|
||||
# replay log.
|
||||
#
|
||||
# @play: replay mode. Non-deterministic data required for system execution
|
||||
# is read from the log.
|
||||
#
|
||||
# Since: 2.5
|
||||
##
|
||||
{ 'enum': 'ReplayMode',
|
||||
'data': [ 'none', 'record', 'play' ] }
|
||||
|
||||
##
|
||||
# @xen-load-devices-state:
|
||||
#
|
||||
|
@ -85,6 +85,7 @@
|
||||
{ 'include': 'qdev.json' }
|
||||
{ 'include': 'machine.json' }
|
||||
{ 'include': 'machine-target.json' }
|
||||
{ 'include': 'replay.json' }
|
||||
{ 'include': 'misc.json' }
|
||||
{ 'include': 'misc-target.json' }
|
||||
{ 'include': 'audio.json' }
|
||||
|
121
qapi/replay.json
Normal file
121
qapi/replay.json
Normal file
@ -0,0 +1,121 @@
|
||||
# -*- Mode: Python -*-
|
||||
#
|
||||
|
||||
##
|
||||
# = Record/replay
|
||||
##
|
||||
|
||||
{ 'include': 'common.json' }
|
||||
|
||||
##
|
||||
# @ReplayMode:
|
||||
#
|
||||
# Mode of the replay subsystem.
|
||||
#
|
||||
# @none: normal execution mode. Replay or record are not enabled.
|
||||
#
|
||||
# @record: record mode. All non-deterministic data is written into the
|
||||
# replay log.
|
||||
#
|
||||
# @play: replay mode. Non-deterministic data required for system execution
|
||||
# is read from the log.
|
||||
#
|
||||
# Since: 2.5
|
||||
##
|
||||
{ 'enum': 'ReplayMode',
|
||||
'data': [ 'none', 'record', 'play' ] }
|
||||
|
||||
##
|
||||
# @ReplayInfo:
|
||||
#
|
||||
# Record/replay information.
|
||||
#
|
||||
# @mode: current mode.
|
||||
#
|
||||
# @filename: name of the record/replay log file.
|
||||
# It is present only in record or replay modes, when the log
|
||||
# is recorded or replayed.
|
||||
#
|
||||
# @icount: current number of executed instructions.
|
||||
#
|
||||
# Since: 5.2
|
||||
#
|
||||
##
|
||||
{ 'struct': 'ReplayInfo',
|
||||
'data': { 'mode': 'ReplayMode', '*filename': 'str', 'icount': 'int' } }
|
||||
|
||||
##
|
||||
# @query-replay:
|
||||
#
|
||||
# Retrieve the record/replay information.
|
||||
# It includes current instruction count which may be used for
|
||||
# @replay-break and @replay-seek commands.
|
||||
#
|
||||
# Returns: record/replay information.
|
||||
#
|
||||
# Since: 5.2
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# -> { "execute": "query-replay" }
|
||||
# <- { "return": { "mode": "play", "filename": "log.rr", "icount": 220414 } }
|
||||
#
|
||||
##
|
||||
{ 'command': 'query-replay',
|
||||
'returns': 'ReplayInfo' }
|
||||
|
||||
##
|
||||
# @replay-break:
|
||||
#
|
||||
# Set replay breakpoint at instruction count @icount.
|
||||
# Execution stops when the specified instruction is reached.
|
||||
# There can be at most one breakpoint. When breakpoint is set, any prior
|
||||
# one is removed. The breakpoint may be set only in replay mode and only
|
||||
# "in the future", i.e. at instruction counts greater than the current one.
|
||||
# The current instruction count can be observed with @query-replay.
|
||||
#
|
||||
# @icount: instruction count to stop at
|
||||
#
|
||||
# Since: 5.2
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# -> { "execute": "replay-break", "data": { "icount": 220414 } }
|
||||
#
|
||||
##
|
||||
{ 'command': 'replay-break', 'data': { 'icount': 'int' } }
|
||||
|
||||
##
|
||||
# @replay-delete-break:
|
||||
#
|
||||
# Remove replay breakpoint which was set with @replay-break.
|
||||
# The command is ignored when there are no replay breakpoints.
|
||||
#
|
||||
# Since: 5.2
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# -> { "execute": "replay-delete-break" }
|
||||
#
|
||||
##
|
||||
{ 'command': 'replay-delete-break' }
|
||||
|
||||
##
|
||||
# @replay-seek:
|
||||
#
|
||||
# Automatically proceed to the instruction count @icount, when
|
||||
# replaying the execution. The command automatically loads nearest
|
||||
# snapshot and replays the execution to find the desired instruction.
|
||||
# When there is no preceding snapshot or the execution is not replayed,
|
||||
# then the command fails.
|
||||
# icount for the reference may be obtained with @query-replay command.
|
||||
#
|
||||
# @icount: target instruction count
|
||||
#
|
||||
# Since: 5.2
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# -> { "execute": "replay-seek", "data": { "icount": 220414 } }
|
||||
##
|
||||
{ 'command': 'replay-seek', 'data': { 'icount': 'int' } }
|
@ -9,4 +9,5 @@ softmmu_ss.add(files(
|
||||
'replay-net.c',
|
||||
'replay-audio.c',
|
||||
'replay-random.c',
|
||||
'replay-debugging.c',
|
||||
))
|
||||
|
334
replay/replay-debugging.c
Normal file
334
replay/replay-debugging.c
Normal file
@ -0,0 +1,334 @@
|
||||
/*
|
||||
* replay-debugging.c
|
||||
*
|
||||
* Copyright (c) 2010-2020 Institute for System Programming
|
||||
* of the Russian Academy of Sciences.
|
||||
*
|
||||
* 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 "qapi/error.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "replay-internal.h"
|
||||
#include "monitor/hmp.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "qapi/qapi-commands-replay.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "block/snapshot.h"
|
||||
#include "migration/snapshot.h"
|
||||
|
||||
static bool replay_is_debugging;
|
||||
static int64_t replay_last_breakpoint;
|
||||
static int64_t replay_last_snapshot;
|
||||
|
||||
bool replay_running_debug(void)
|
||||
{
|
||||
return replay_is_debugging;
|
||||
}
|
||||
|
||||
void hmp_info_replay(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
if (replay_mode == REPLAY_MODE_NONE) {
|
||||
monitor_printf(mon, "Record/replay is not active\n");
|
||||
} else {
|
||||
monitor_printf(mon,
|
||||
"%s execution '%s': instruction count = %"PRId64"\n",
|
||||
replay_mode == REPLAY_MODE_RECORD ? "Recording" : "Replaying",
|
||||
replay_get_filename(), replay_get_current_icount());
|
||||
}
|
||||
}
|
||||
|
||||
ReplayInfo *qmp_query_replay(Error **errp)
|
||||
{
|
||||
ReplayInfo *retval = g_new0(ReplayInfo, 1);
|
||||
|
||||
retval->mode = replay_mode;
|
||||
if (replay_get_filename()) {
|
||||
retval->filename = g_strdup(replay_get_filename());
|
||||
retval->has_filename = true;
|
||||
}
|
||||
retval->icount = replay_get_current_icount();
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void replay_break(uint64_t icount, QEMUTimerCB callback, void *opaque)
|
||||
{
|
||||
assert(replay_mode == REPLAY_MODE_PLAY);
|
||||
assert(replay_mutex_locked());
|
||||
assert(replay_break_icount >= replay_get_current_icount());
|
||||
assert(callback);
|
||||
|
||||
replay_break_icount = icount;
|
||||
|
||||
if (replay_break_timer) {
|
||||
timer_del(replay_break_timer);
|
||||
}
|
||||
replay_break_timer = timer_new_ns(QEMU_CLOCK_REALTIME,
|
||||
callback, opaque);
|
||||
}
|
||||
|
||||
static void replay_delete_break(void)
|
||||
{
|
||||
assert(replay_mode == REPLAY_MODE_PLAY);
|
||||
assert(replay_mutex_locked());
|
||||
|
||||
if (replay_break_timer) {
|
||||
timer_del(replay_break_timer);
|
||||
timer_free(replay_break_timer);
|
||||
replay_break_timer = NULL;
|
||||
}
|
||||
replay_break_icount = -1ULL;
|
||||
}
|
||||
|
||||
static void replay_stop_vm(void *opaque)
|
||||
{
|
||||
vm_stop(RUN_STATE_PAUSED);
|
||||
replay_delete_break();
|
||||
}
|
||||
|
||||
void qmp_replay_break(int64_t icount, Error **errp)
|
||||
{
|
||||
if (replay_mode == REPLAY_MODE_PLAY) {
|
||||
if (icount >= replay_get_current_icount()) {
|
||||
replay_break(icount, replay_stop_vm, NULL);
|
||||
} else {
|
||||
error_setg(errp,
|
||||
"cannot set breakpoint at the instruction in the past");
|
||||
}
|
||||
} else {
|
||||
error_setg(errp, "setting the breakpoint is allowed only in play mode");
|
||||
}
|
||||
}
|
||||
|
||||
void hmp_replay_break(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
int64_t icount = qdict_get_try_int(qdict, "icount", -1LL);
|
||||
Error *err = NULL;
|
||||
|
||||
qmp_replay_break(icount, &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void qmp_replay_delete_break(Error **errp)
|
||||
{
|
||||
if (replay_mode == REPLAY_MODE_PLAY) {
|
||||
replay_delete_break();
|
||||
} else {
|
||||
error_setg(errp, "replay breakpoints are allowed only in play mode");
|
||||
}
|
||||
}
|
||||
|
||||
void hmp_replay_delete_break(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
Error *err = NULL;
|
||||
|
||||
qmp_replay_delete_break(&err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static char *replay_find_nearest_snapshot(int64_t icount,
|
||||
int64_t *snapshot_icount)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
QEMUSnapshotInfo *sn_tab;
|
||||
QEMUSnapshotInfo *nearest = NULL;
|
||||
char *ret = NULL;
|
||||
int nb_sns, i;
|
||||
AioContext *aio_context;
|
||||
|
||||
*snapshot_icount = -1;
|
||||
|
||||
bs = bdrv_all_find_vmstate_bs();
|
||||
if (!bs) {
|
||||
goto fail;
|
||||
}
|
||||
aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(aio_context);
|
||||
nb_sns = bdrv_snapshot_list(bs, &sn_tab);
|
||||
aio_context_release(aio_context);
|
||||
|
||||
for (i = 0; i < nb_sns; i++) {
|
||||
if (bdrv_all_find_snapshot(sn_tab[i].name, &bs) == 0) {
|
||||
if (sn_tab[i].icount != -1ULL
|
||||
&& sn_tab[i].icount <= icount
|
||||
&& (!nearest || nearest->icount < sn_tab[i].icount)) {
|
||||
nearest = &sn_tab[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nearest) {
|
||||
ret = g_strdup(nearest->name);
|
||||
*snapshot_icount = nearest->icount;
|
||||
}
|
||||
g_free(sn_tab);
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void replay_seek(int64_t icount, QEMUTimerCB callback, Error **errp)
|
||||
{
|
||||
char *snapshot = NULL;
|
||||
int64_t snapshot_icount;
|
||||
|
||||
if (replay_mode != REPLAY_MODE_PLAY) {
|
||||
error_setg(errp, "replay must be enabled to seek");
|
||||
return;
|
||||
}
|
||||
|
||||
snapshot = replay_find_nearest_snapshot(icount, &snapshot_icount);
|
||||
if (snapshot) {
|
||||
if (icount < replay_get_current_icount()
|
||||
|| replay_get_current_icount() < snapshot_icount) {
|
||||
vm_stop(RUN_STATE_RESTORE_VM);
|
||||
load_snapshot(snapshot, errp);
|
||||
}
|
||||
g_free(snapshot);
|
||||
}
|
||||
if (replay_get_current_icount() <= icount) {
|
||||
replay_break(icount, callback, NULL);
|
||||
vm_start();
|
||||
} else {
|
||||
error_setg(errp, "cannot seek to the specified instruction count");
|
||||
}
|
||||
}
|
||||
|
||||
void qmp_replay_seek(int64_t icount, Error **errp)
|
||||
{
|
||||
replay_seek(icount, replay_stop_vm, errp);
|
||||
}
|
||||
|
||||
void hmp_replay_seek(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
int64_t icount = qdict_get_try_int(qdict, "icount", -1LL);
|
||||
Error *err = NULL;
|
||||
|
||||
qmp_replay_seek(icount, &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void replay_stop_vm_debug(void *opaque)
|
||||
{
|
||||
replay_is_debugging = false;
|
||||
vm_stop(RUN_STATE_DEBUG);
|
||||
replay_delete_break();
|
||||
}
|
||||
|
||||
bool replay_reverse_step(void)
|
||||
{
|
||||
Error *err = NULL;
|
||||
|
||||
assert(replay_mode == REPLAY_MODE_PLAY);
|
||||
|
||||
if (replay_get_current_icount() != 0) {
|
||||
replay_seek(replay_get_current_icount() - 1,
|
||||
replay_stop_vm_debug, &err);
|
||||
if (err) {
|
||||
error_free(err);
|
||||
return false;
|
||||
}
|
||||
replay_is_debugging = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void replay_continue_end(void)
|
||||
{
|
||||
replay_is_debugging = false;
|
||||
vm_stop(RUN_STATE_DEBUG);
|
||||
replay_delete_break();
|
||||
}
|
||||
|
||||
static void replay_continue_stop(void *opaque)
|
||||
{
|
||||
Error *err = NULL;
|
||||
if (replay_last_breakpoint != -1LL) {
|
||||
replay_seek(replay_last_breakpoint, replay_stop_vm_debug, &err);
|
||||
if (err) {
|
||||
error_free(err);
|
||||
replay_continue_end();
|
||||
}
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* No breakpoints since the last snapshot.
|
||||
* Find previous snapshot and try again.
|
||||
*/
|
||||
if (replay_last_snapshot != 0) {
|
||||
replay_seek(replay_last_snapshot - 1, replay_continue_stop, &err);
|
||||
if (err) {
|
||||
error_free(err);
|
||||
replay_continue_end();
|
||||
}
|
||||
replay_last_snapshot = replay_get_current_icount();
|
||||
return;
|
||||
} else {
|
||||
/* Seek to the very first step */
|
||||
replay_seek(0, replay_stop_vm_debug, &err);
|
||||
if (err) {
|
||||
error_free(err);
|
||||
replay_continue_end();
|
||||
}
|
||||
return;
|
||||
}
|
||||
replay_continue_end();
|
||||
}
|
||||
|
||||
bool replay_reverse_continue(void)
|
||||
{
|
||||
Error *err = NULL;
|
||||
|
||||
assert(replay_mode == REPLAY_MODE_PLAY);
|
||||
|
||||
if (replay_get_current_icount() != 0) {
|
||||
replay_seek(replay_get_current_icount() - 1,
|
||||
replay_continue_stop, &err);
|
||||
if (err) {
|
||||
error_free(err);
|
||||
return false;
|
||||
}
|
||||
replay_last_breakpoint = -1LL;
|
||||
replay_is_debugging = true;
|
||||
replay_last_snapshot = replay_get_current_icount();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void replay_breakpoint(void)
|
||||
{
|
||||
assert(replay_mode == REPLAY_MODE_PLAY);
|
||||
replay_last_breakpoint = replay_get_current_icount();
|
||||
}
|
||||
|
||||
void replay_gdb_attached(void)
|
||||
{
|
||||
/*
|
||||
* Create VM snapshot on temporary overlay to allow reverse
|
||||
* debugging even if snapshots were not enabled.
|
||||
*/
|
||||
if (replay_mode == REPLAY_MODE_PLAY
|
||||
&& !replay_snapshot) {
|
||||
if (save_snapshot("start_debugging", NULL) != 0) {
|
||||
/* Can't create the snapshot. Continue conventional debugging. */
|
||||
}
|
||||
}
|
||||
}
|
@ -77,6 +77,10 @@ bool replay_has_events(void)
|
||||
|
||||
void replay_flush_events(void)
|
||||
{
|
||||
if (replay_mode == REPLAY_MODE_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_assert(replay_mutex_locked());
|
||||
|
||||
while (!QTAILQ_EMPTY(&events_list)) {
|
||||
|
@ -94,6 +94,10 @@ extern ReplayState replay_state;
|
||||
|
||||
/* File for replay writing */
|
||||
extern FILE *replay_file;
|
||||
/* Instruction count of the replay breakpoint */
|
||||
extern uint64_t replay_break_icount;
|
||||
/* Timer for the replay breakpoint callback */
|
||||
extern QEMUTimer *replay_break_timer;
|
||||
|
||||
void replay_put_byte(uint8_t byte);
|
||||
void replay_put_event(uint8_t event);
|
||||
@ -145,8 +149,6 @@ void replay_read_next_clock(unsigned int kind);
|
||||
void replay_init_events(void);
|
||||
/*! Clears internal data structures for events handling */
|
||||
void replay_finish_events(void);
|
||||
/*! Flushes events queue */
|
||||
void replay_flush_events(void);
|
||||
/*! Returns true if there are any unsaved events in the queue */
|
||||
bool replay_has_events(void);
|
||||
/*! Saves events from queue into the file */
|
||||
|
@ -11,10 +11,10 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "replay-internal.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/option.h"
|
||||
#include "sysemu/cpus.h"
|
||||
@ -34,6 +34,10 @@ static char *replay_filename;
|
||||
ReplayState replay_state;
|
||||
static GSList *replay_blockers;
|
||||
|
||||
/* Replay breakpoints */
|
||||
uint64_t replay_break_icount = -1ULL;
|
||||
QEMUTimer *replay_break_timer;
|
||||
|
||||
bool replay_next_event_is(int event)
|
||||
{
|
||||
bool res = false;
|
||||
@ -64,7 +68,7 @@ bool replay_next_event_is(int event)
|
||||
|
||||
uint64_t replay_get_current_icount(void)
|
||||
{
|
||||
return cpu_get_icount_raw();
|
||||
return icount_get_raw();
|
||||
}
|
||||
|
||||
int replay_get_instructions(void)
|
||||
@ -73,6 +77,13 @@ int replay_get_instructions(void)
|
||||
replay_mutex_lock();
|
||||
if (replay_next_event_is(EVENT_INSTRUCTION)) {
|
||||
res = replay_state.instruction_count;
|
||||
if (replay_break_icount != -1LL) {
|
||||
uint64_t current = replay_get_current_icount();
|
||||
assert(replay_break_icount >= current);
|
||||
if (current + res > replay_break_icount) {
|
||||
res = replay_break_icount - current;
|
||||
}
|
||||
}
|
||||
}
|
||||
replay_mutex_unlock();
|
||||
return res;
|
||||
@ -99,6 +110,12 @@ void replay_account_executed_instructions(void)
|
||||
will be read from the log. */
|
||||
qemu_notify_event();
|
||||
}
|
||||
/* Execution reached the break step */
|
||||
if (replay_break_icount == replay_state.current_icount) {
|
||||
/* Cannot make callback directly from the vCPU thread */
|
||||
timer_mod_ns(replay_break_timer,
|
||||
qemu_clock_get_ns(QEMU_CLOCK_REALTIME));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -345,7 +362,7 @@ void replay_start(void)
|
||||
error_reportf_err(replay_blockers->data, "Record/replay: ");
|
||||
exit(1);
|
||||
}
|
||||
if (!use_icount) {
|
||||
if (!icount_enabled()) {
|
||||
error_report("Please enable icount to use record/replay");
|
||||
exit(1);
|
||||
}
|
||||
@ -399,3 +416,8 @@ void replay_add_blocker(Error *reason)
|
||||
{
|
||||
replay_blockers = g_slist_prepend(replay_blockers, reason);
|
||||
}
|
||||
|
||||
const char *replay_get_filename(void)
|
||||
{
|
||||
return replay_filename;
|
||||
}
|
||||
|
279
softmmu/cpu-timers.c
Normal file
279
softmmu/cpu-timers.c
Normal file
@ -0,0 +1,279 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/seqlock.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "sysemu/cpu-throttle.h"
|
||||
#include "timers-state.h"
|
||||
|
||||
/* clock and ticks */
|
||||
|
||||
static int64_t cpu_get_ticks_locked(void)
|
||||
{
|
||||
int64_t ticks = timers_state.cpu_ticks_offset;
|
||||
if (timers_state.cpu_ticks_enabled) {
|
||||
ticks += cpu_get_host_ticks();
|
||||
}
|
||||
|
||||
if (timers_state.cpu_ticks_prev > ticks) {
|
||||
/* Non increasing ticks may happen if the host uses software suspend. */
|
||||
timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks;
|
||||
ticks = timers_state.cpu_ticks_prev;
|
||||
}
|
||||
|
||||
timers_state.cpu_ticks_prev = ticks;
|
||||
return ticks;
|
||||
}
|
||||
|
||||
/*
|
||||
* return the time elapsed in VM between vm_start and vm_stop.
|
||||
* cpu_get_ticks() uses units of the host CPU cycle counter.
|
||||
*/
|
||||
int64_t cpu_get_ticks(void)
|
||||
{
|
||||
int64_t ticks;
|
||||
|
||||
qemu_spin_lock(&timers_state.vm_clock_lock);
|
||||
ticks = cpu_get_ticks_locked();
|
||||
qemu_spin_unlock(&timers_state.vm_clock_lock);
|
||||
return ticks;
|
||||
}
|
||||
|
||||
int64_t cpu_get_clock_locked(void)
|
||||
{
|
||||
int64_t time;
|
||||
|
||||
time = timers_state.cpu_clock_offset;
|
||||
if (timers_state.cpu_ticks_enabled) {
|
||||
time += get_clock();
|
||||
}
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the monotonic time elapsed in VM, i.e.,
|
||||
* the time between vm_start and vm_stop
|
||||
*/
|
||||
int64_t cpu_get_clock(void)
|
||||
{
|
||||
int64_t ti;
|
||||
unsigned start;
|
||||
|
||||
do {
|
||||
start = seqlock_read_begin(&timers_state.vm_clock_seqlock);
|
||||
ti = cpu_get_clock_locked();
|
||||
} while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));
|
||||
|
||||
return ti;
|
||||
}
|
||||
|
||||
/*
|
||||
* enable cpu_get_ticks()
|
||||
* Caller must hold BQL which serves as mutex for vm_clock_seqlock.
|
||||
*/
|
||||
void cpu_enable_ticks(void)
|
||||
{
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
if (!timers_state.cpu_ticks_enabled) {
|
||||
timers_state.cpu_ticks_offset -= cpu_get_host_ticks();
|
||||
timers_state.cpu_clock_offset -= get_clock();
|
||||
timers_state.cpu_ticks_enabled = 1;
|
||||
}
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* disable cpu_get_ticks() : the clock is stopped. You must not call
|
||||
* cpu_get_ticks() after that.
|
||||
* Caller must hold BQL which serves as mutex for vm_clock_seqlock.
|
||||
*/
|
||||
void cpu_disable_ticks(void)
|
||||
{
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
if (timers_state.cpu_ticks_enabled) {
|
||||
timers_state.cpu_ticks_offset += cpu_get_host_ticks();
|
||||
timers_state.cpu_clock_offset = cpu_get_clock_locked();
|
||||
timers_state.cpu_ticks_enabled = 0;
|
||||
}
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
}
|
||||
|
||||
static bool icount_state_needed(void *opaque)
|
||||
{
|
||||
return icount_enabled();
|
||||
}
|
||||
|
||||
static bool warp_timer_state_needed(void *opaque)
|
||||
{
|
||||
TimersState *s = opaque;
|
||||
return s->icount_warp_timer != NULL;
|
||||
}
|
||||
|
||||
static bool adjust_timers_state_needed(void *opaque)
|
||||
{
|
||||
TimersState *s = opaque;
|
||||
return s->icount_rt_timer != NULL;
|
||||
}
|
||||
|
||||
static bool icount_shift_state_needed(void *opaque)
|
||||
{
|
||||
return icount_enabled() == 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Subsection for warp timer migration is optional, because may not be created
|
||||
*/
|
||||
static const VMStateDescription icount_vmstate_warp_timer = {
|
||||
.name = "timer/icount/warp_timer",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = warp_timer_state_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT64(vm_clock_warp_start, TimersState),
|
||||
VMSTATE_TIMER_PTR(icount_warp_timer, TimersState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription icount_vmstate_adjust_timers = {
|
||||
.name = "timer/icount/timers",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = adjust_timers_state_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_TIMER_PTR(icount_rt_timer, TimersState),
|
||||
VMSTATE_TIMER_PTR(icount_vm_timer, TimersState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription icount_vmstate_shift = {
|
||||
.name = "timer/icount/shift",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = icount_shift_state_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT16(icount_time_shift, TimersState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* This is a subsection for icount migration.
|
||||
*/
|
||||
static const VMStateDescription icount_vmstate_timers = {
|
||||
.name = "timer/icount",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = icount_state_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT64(qemu_icount_bias, TimersState),
|
||||
VMSTATE_INT64(qemu_icount, TimersState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (const VMStateDescription * []) {
|
||||
&icount_vmstate_warp_timer,
|
||||
&icount_vmstate_adjust_timers,
|
||||
&icount_vmstate_shift,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_timers = {
|
||||
.name = "timer",
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT64(cpu_ticks_offset, TimersState),
|
||||
VMSTATE_UNUSED(8),
|
||||
VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (const VMStateDescription * []) {
|
||||
&icount_vmstate_timers,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
static void do_nothing(CPUState *cpu, run_on_cpu_data unused)
|
||||
{
|
||||
}
|
||||
|
||||
void qemu_timer_notify_cb(void *opaque, QEMUClockType type)
|
||||
{
|
||||
if (!icount_enabled() || type != QEMU_CLOCK_VIRTUAL) {
|
||||
qemu_notify_event();
|
||||
return;
|
||||
}
|
||||
|
||||
if (qemu_in_vcpu_thread()) {
|
||||
/*
|
||||
* A CPU is currently running; kick it back out to the
|
||||
* tcg_cpu_exec() loop so it will recalculate its
|
||||
* icount deadline immediately.
|
||||
*/
|
||||
qemu_cpu_kick(current_cpu);
|
||||
} else if (first_cpu) {
|
||||
/*
|
||||
* qemu_cpu_kick is not enough to kick a halted CPU out of
|
||||
* qemu_tcg_wait_io_event. async_run_on_cpu, instead,
|
||||
* causes cpu_thread_is_idle to return false. This way,
|
||||
* handle_icount_deadline can run.
|
||||
* If we have no CPUs at all for some reason, we don't
|
||||
* need to do anything.
|
||||
*/
|
||||
async_run_on_cpu(first_cpu, do_nothing, RUN_ON_CPU_NULL);
|
||||
}
|
||||
}
|
||||
|
||||
TimersState timers_state;
|
||||
|
||||
/* initialize timers state and the cpu throttle for convenience */
|
||||
void cpu_timers_init(void)
|
||||
{
|
||||
seqlock_init(&timers_state.vm_clock_seqlock);
|
||||
qemu_spin_init(&timers_state.vm_clock_lock);
|
||||
vmstate_register(NULL, 0, &vmstate_timers, &timers_state);
|
||||
|
||||
cpu_throttle_init();
|
||||
}
|
1725
softmmu/cpus.c
1725
softmmu/cpus.c
File diff suppressed because it is too large
Load Diff
492
softmmu/icount.c
Normal file
492
softmmu/icount.c
Normal file
@ -0,0 +1,492 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/seqlock.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "sysemu/cpu-throttle.h"
|
||||
#include "timers-state.h"
|
||||
|
||||
/*
|
||||
* ICOUNT: Instruction Counter
|
||||
*
|
||||
* this module is split off from cpu-timers because the icount part
|
||||
* is TCG-specific, and does not need to be built for other accels.
|
||||
*/
|
||||
static bool icount_sleep = true;
|
||||
/* Arbitrarily pick 1MIPS as the minimum allowable speed. */
|
||||
#define MAX_ICOUNT_SHIFT 10
|
||||
|
||||
/*
|
||||
* 0 = Do not count executed instructions.
|
||||
* 1 = Fixed conversion of insn to ns via "shift" option
|
||||
* 2 = Runtime adaptive algorithm to compute shift
|
||||
*/
|
||||
int use_icount;
|
||||
|
||||
static void icount_enable_precise(void)
|
||||
{
|
||||
use_icount = 1;
|
||||
}
|
||||
|
||||
static void icount_enable_adaptive(void)
|
||||
{
|
||||
use_icount = 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* The current number of executed instructions is based on what we
|
||||
* originally budgeted minus the current state of the decrementing
|
||||
* icount counters in extra/u16.low.
|
||||
*/
|
||||
static int64_t icount_get_executed(CPUState *cpu)
|
||||
{
|
||||
return (cpu->icount_budget -
|
||||
(cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra));
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the global shared timer_state.qemu_icount to take into
|
||||
* account executed instructions. This is done by the TCG vCPU
|
||||
* thread so the main-loop can see time has moved forward.
|
||||
*/
|
||||
static void icount_update_locked(CPUState *cpu)
|
||||
{
|
||||
int64_t executed = icount_get_executed(cpu);
|
||||
cpu->icount_budget -= executed;
|
||||
|
||||
qatomic_set_i64(&timers_state.qemu_icount,
|
||||
timers_state.qemu_icount + executed);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the global shared timer_state.qemu_icount to take into
|
||||
* account executed instructions. This is done by the TCG vCPU
|
||||
* thread so the main-loop can see time has moved forward.
|
||||
*/
|
||||
void icount_update(CPUState *cpu)
|
||||
{
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
icount_update_locked(cpu);
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
}
|
||||
|
||||
static int64_t icount_get_raw_locked(void)
|
||||
{
|
||||
CPUState *cpu = current_cpu;
|
||||
|
||||
if (cpu && cpu->running) {
|
||||
if (!cpu->can_do_io) {
|
||||
error_report("Bad icount read");
|
||||
exit(1);
|
||||
}
|
||||
/* Take into account what has run */
|
||||
icount_update_locked(cpu);
|
||||
}
|
||||
/* The read is protected by the seqlock, but needs atomic64 to avoid UB */
|
||||
return qatomic_read_i64(&timers_state.qemu_icount);
|
||||
}
|
||||
|
||||
static int64_t icount_get_locked(void)
|
||||
{
|
||||
int64_t icount = icount_get_raw_locked();
|
||||
return qatomic_read_i64(&timers_state.qemu_icount_bias) +
|
||||
icount_to_ns(icount);
|
||||
}
|
||||
|
||||
int64_t icount_get_raw(void)
|
||||
{
|
||||
int64_t icount;
|
||||
unsigned start;
|
||||
|
||||
do {
|
||||
start = seqlock_read_begin(&timers_state.vm_clock_seqlock);
|
||||
icount = icount_get_raw_locked();
|
||||
} while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));
|
||||
|
||||
return icount;
|
||||
}
|
||||
|
||||
/* Return the virtual CPU time, based on the instruction counter. */
|
||||
int64_t icount_get(void)
|
||||
{
|
||||
int64_t icount;
|
||||
unsigned start;
|
||||
|
||||
do {
|
||||
start = seqlock_read_begin(&timers_state.vm_clock_seqlock);
|
||||
icount = icount_get_locked();
|
||||
} while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));
|
||||
|
||||
return icount;
|
||||
}
|
||||
|
||||
int64_t icount_to_ns(int64_t icount)
|
||||
{
|
||||
return icount << qatomic_read(&timers_state.icount_time_shift);
|
||||
}
|
||||
|
||||
/*
|
||||
* Correlation between real and virtual time is always going to be
|
||||
* fairly approximate, so ignore small variation.
|
||||
* When the guest is idle real and virtual time will be aligned in
|
||||
* the IO wait loop.
|
||||
*/
|
||||
#define ICOUNT_WOBBLE (NANOSECONDS_PER_SECOND / 10)
|
||||
|
||||
static void icount_adjust(void)
|
||||
{
|
||||
int64_t cur_time;
|
||||
int64_t cur_icount;
|
||||
int64_t delta;
|
||||
|
||||
/* Protected by TimersState mutex. */
|
||||
static int64_t last_delta;
|
||||
|
||||
/* If the VM is not running, then do nothing. */
|
||||
if (!runstate_is_running()) {
|
||||
return;
|
||||
}
|
||||
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
cur_time = REPLAY_CLOCK_LOCKED(REPLAY_CLOCK_VIRTUAL_RT,
|
||||
cpu_get_clock_locked());
|
||||
cur_icount = icount_get_locked();
|
||||
|
||||
delta = cur_icount - cur_time;
|
||||
/* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */
|
||||
if (delta > 0
|
||||
&& last_delta + ICOUNT_WOBBLE < delta * 2
|
||||
&& timers_state.icount_time_shift > 0) {
|
||||
/* The guest is getting too far ahead. Slow time down. */
|
||||
qatomic_set(&timers_state.icount_time_shift,
|
||||
timers_state.icount_time_shift - 1);
|
||||
}
|
||||
if (delta < 0
|
||||
&& last_delta - ICOUNT_WOBBLE > delta * 2
|
||||
&& timers_state.icount_time_shift < MAX_ICOUNT_SHIFT) {
|
||||
/* The guest is getting too far behind. Speed time up. */
|
||||
qatomic_set(&timers_state.icount_time_shift,
|
||||
timers_state.icount_time_shift + 1);
|
||||
}
|
||||
last_delta = delta;
|
||||
qatomic_set_i64(&timers_state.qemu_icount_bias,
|
||||
cur_icount - (timers_state.qemu_icount
|
||||
<< timers_state.icount_time_shift));
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
}
|
||||
|
||||
static void icount_adjust_rt(void *opaque)
|
||||
{
|
||||
timer_mod(timers_state.icount_rt_timer,
|
||||
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) + 1000);
|
||||
icount_adjust();
|
||||
}
|
||||
|
||||
static void icount_adjust_vm(void *opaque)
|
||||
{
|
||||
timer_mod(timers_state.icount_vm_timer,
|
||||
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
|
||||
NANOSECONDS_PER_SECOND / 10);
|
||||
icount_adjust();
|
||||
}
|
||||
|
||||
int64_t icount_round(int64_t count)
|
||||
{
|
||||
int shift = qatomic_read(&timers_state.icount_time_shift);
|
||||
return (count + (1 << shift) - 1) >> shift;
|
||||
}
|
||||
|
||||
static void icount_warp_rt(void)
|
||||
{
|
||||
unsigned seq;
|
||||
int64_t warp_start;
|
||||
|
||||
/*
|
||||
* The icount_warp_timer is rescheduled soon after vm_clock_warp_start
|
||||
* changes from -1 to another value, so the race here is okay.
|
||||
*/
|
||||
do {
|
||||
seq = seqlock_read_begin(&timers_state.vm_clock_seqlock);
|
||||
warp_start = timers_state.vm_clock_warp_start;
|
||||
} while (seqlock_read_retry(&timers_state.vm_clock_seqlock, seq));
|
||||
|
||||
if (warp_start == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
if (runstate_is_running()) {
|
||||
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;
|
||||
if (icount_enabled() == 2) {
|
||||
/*
|
||||
* In adaptive mode, do not let QEMU_CLOCK_VIRTUAL run too
|
||||
* far ahead of real time.
|
||||
*/
|
||||
int64_t cur_icount = icount_get_locked();
|
||||
int64_t delta = clock - cur_icount;
|
||||
warp_delta = MIN(warp_delta, delta);
|
||||
}
|
||||
qatomic_set_i64(&timers_state.qemu_icount_bias,
|
||||
timers_state.qemu_icount_bias + warp_delta);
|
||||
}
|
||||
timers_state.vm_clock_warp_start = -1;
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
|
||||
if (qemu_clock_expired(QEMU_CLOCK_VIRTUAL)) {
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
}
|
||||
|
||||
static void icount_timer_cb(void *opaque)
|
||||
{
|
||||
/*
|
||||
* No need for a checkpoint because the timer already synchronizes
|
||||
* with CHECKPOINT_CLOCK_VIRTUAL_RT.
|
||||
*/
|
||||
icount_warp_rt();
|
||||
}
|
||||
|
||||
void icount_start_warp_timer(void)
|
||||
{
|
||||
int64_t clock;
|
||||
int64_t deadline;
|
||||
|
||||
assert(icount_enabled());
|
||||
|
||||
/*
|
||||
* Nothing to do if the VM is stopped: QEMU_CLOCK_VIRTUAL timers
|
||||
* do not fire, so computing the deadline does not make sense.
|
||||
*/
|
||||
if (!runstate_is_running()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (replay_mode != REPLAY_MODE_PLAY) {
|
||||
if (!all_cpu_threads_idle()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (qtest_enabled()) {
|
||||
/* When testing, qtest commands advance icount. */
|
||||
return;
|
||||
}
|
||||
|
||||
replay_checkpoint(CHECKPOINT_CLOCK_WARP_START);
|
||||
} else {
|
||||
/* warp clock deterministically in record/replay mode */
|
||||
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_START)) {
|
||||
/*
|
||||
* vCPU is sleeping and warp can't be started.
|
||||
* It is probably a race condition: notification sent
|
||||
* to vCPU was processed in advance and vCPU went to sleep.
|
||||
* Therefore we have to wake it up for doing someting.
|
||||
*/
|
||||
if (replay_has_checkpoint()) {
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* We want to use the earliest deadline from ALL vm_clocks */
|
||||
clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT);
|
||||
deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
|
||||
~QEMU_TIMER_ATTR_EXTERNAL);
|
||||
if (deadline < 0) {
|
||||
static bool notified;
|
||||
if (!icount_sleep && !notified) {
|
||||
warn_report("icount sleep disabled and no active timers");
|
||||
notified = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (deadline > 0) {
|
||||
/*
|
||||
* Ensure QEMU_CLOCK_VIRTUAL proceeds even when the virtual CPU goes to
|
||||
* sleep. Otherwise, the CPU might be waiting for a future timer
|
||||
* interrupt to wake it up, but the interrupt never comes because
|
||||
* the vCPU isn't running any insns and thus doesn't advance the
|
||||
* QEMU_CLOCK_VIRTUAL.
|
||||
*/
|
||||
if (!icount_sleep) {
|
||||
/*
|
||||
* We never let VCPUs sleep in no sleep icount mode.
|
||||
* If there is a pending QEMU_CLOCK_VIRTUAL timer we just advance
|
||||
* to the next QEMU_CLOCK_VIRTUAL event and notify it.
|
||||
* It is useful when we want a deterministic execution time,
|
||||
* isolated from host latencies.
|
||||
*/
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
qatomic_set_i64(&timers_state.qemu_icount_bias,
|
||||
timers_state.qemu_icount_bias + deadline);
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
} else {
|
||||
/*
|
||||
* We do stop VCPUs and only advance QEMU_CLOCK_VIRTUAL after some
|
||||
* "real" time, (related to the time left until the next event) has
|
||||
* passed. The QEMU_CLOCK_VIRTUAL_RT clock will do this.
|
||||
* This avoids that the warps are visible externally; for example,
|
||||
* you will not be sending network packets continuously instead of
|
||||
* every 100ms.
|
||||
*/
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
if (timers_state.vm_clock_warp_start == -1
|
||||
|| timers_state.vm_clock_warp_start > clock) {
|
||||
timers_state.vm_clock_warp_start = clock;
|
||||
}
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
timer_mod_anticipate(timers_state.icount_warp_timer,
|
||||
clock + deadline);
|
||||
}
|
||||
} else if (deadline == 0) {
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
}
|
||||
|
||||
void icount_account_warp_timer(void)
|
||||
{
|
||||
if (!icount_enabled() || !icount_sleep) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Nothing to do if the VM is stopped: QEMU_CLOCK_VIRTUAL timers
|
||||
* do not fire, so computing the deadline does not make sense.
|
||||
*/
|
||||
if (!runstate_is_running()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* warp clock deterministically in record/replay mode */
|
||||
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_ACCOUNT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
timer_del(timers_state.icount_warp_timer);
|
||||
icount_warp_rt();
|
||||
}
|
||||
|
||||
void icount_configure(QemuOpts *opts, Error **errp)
|
||||
{
|
||||
const char *option = qemu_opt_get(opts, "shift");
|
||||
bool sleep = qemu_opt_get_bool(opts, "sleep", true);
|
||||
bool align = qemu_opt_get_bool(opts, "align", false);
|
||||
long time_shift = -1;
|
||||
|
||||
if (!option) {
|
||||
if (qemu_opt_get(opts, "align") != NULL) {
|
||||
error_setg(errp, "Please specify shift option when using align");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (align && !sleep) {
|
||||
error_setg(errp, "align=on and sleep=off are incompatible");
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(option, "auto") != 0) {
|
||||
if (qemu_strtol(option, NULL, 0, &time_shift) < 0
|
||||
|| time_shift < 0 || time_shift > MAX_ICOUNT_SHIFT) {
|
||||
error_setg(errp, "icount: Invalid shift value");
|
||||
return;
|
||||
}
|
||||
} else if (icount_align_option) {
|
||||
error_setg(errp, "shift=auto and align=on are incompatible");
|
||||
return;
|
||||
} else if (!icount_sleep) {
|
||||
error_setg(errp, "shift=auto and sleep=off are incompatible");
|
||||
return;
|
||||
}
|
||||
|
||||
icount_sleep = sleep;
|
||||
if (icount_sleep) {
|
||||
timers_state.icount_warp_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
|
||||
icount_timer_cb, NULL);
|
||||
}
|
||||
|
||||
icount_align_option = align;
|
||||
|
||||
if (time_shift >= 0) {
|
||||
timers_state.icount_time_shift = time_shift;
|
||||
icount_enable_precise();
|
||||
return;
|
||||
}
|
||||
|
||||
icount_enable_adaptive();
|
||||
|
||||
/*
|
||||
* 125MIPS seems a reasonable initial guess at the guest speed.
|
||||
* It will be corrected fairly quickly anyway.
|
||||
*/
|
||||
timers_state.icount_time_shift = 3;
|
||||
|
||||
/*
|
||||
* Have both realtime and virtual time triggers for speed adjustment.
|
||||
* The realtime trigger catches emulated time passing too slowly,
|
||||
* the virtual time trigger catches emulated time passing too fast.
|
||||
* Realtime triggers occur even when idle, so use them less frequently
|
||||
* than VM triggers.
|
||||
*/
|
||||
timers_state.vm_clock_warp_start = -1;
|
||||
timers_state.icount_rt_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL_RT,
|
||||
icount_adjust_rt, NULL);
|
||||
timer_mod(timers_state.icount_rt_timer,
|
||||
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) + 1000);
|
||||
timers_state.icount_vm_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||
icount_adjust_vm, NULL);
|
||||
timer_mod(timers_state.icount_vm_timer,
|
||||
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
|
||||
NANOSECONDS_PER_SECOND / 10);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: files(
|
||||
specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: [files(
|
||||
'arch_init.c',
|
||||
'balloon.c',
|
||||
'cpus.c',
|
||||
@ -7,4 +7,10 @@ specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: files(
|
||||
'memory.c',
|
||||
'memory_mapping.c',
|
||||
'qtest.c',
|
||||
'vl.c'))
|
||||
'vl.c',
|
||||
'cpu-timers.c',
|
||||
)])
|
||||
|
||||
specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: [files(
|
||||
'icount.c'
|
||||
)])
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include "exec/memory.h"
|
||||
#include "hw/irq.h"
|
||||
#include "sysemu/accel.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/error-report.h"
|
||||
@ -273,6 +273,38 @@ static void qtest_irq_handler(void *opaque, int n, int level)
|
||||
}
|
||||
}
|
||||
|
||||
static int64_t qtest_clock_counter;
|
||||
|
||||
int64_t qtest_get_virtual_clock(void)
|
||||
{
|
||||
return qatomic_read_i64(&qtest_clock_counter);
|
||||
}
|
||||
|
||||
static void qtest_set_virtual_clock(int64_t count)
|
||||
{
|
||||
qatomic_set_i64(&qtest_clock_counter, count);
|
||||
}
|
||||
|
||||
static void qtest_clock_warp(int64_t dest)
|
||||
{
|
||||
int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
AioContext *aio_context;
|
||||
assert(qtest_enabled());
|
||||
aio_context = qemu_get_aio_context();
|
||||
while (clock < dest) {
|
||||
int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
|
||||
QEMU_TIMER_ATTR_ALL);
|
||||
int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
|
||||
|
||||
qtest_set_virtual_clock(qtest_get_virtual_clock() + warp);
|
||||
|
||||
qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
|
||||
timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]);
|
||||
clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
|
||||
static void qtest_process_command(CharBackend *chr, gchar **words)
|
||||
{
|
||||
const gchar *command;
|
||||
|
69
softmmu/timers-state.h
Normal file
69
softmmu/timers-state.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef TIMERS_STATE_H
|
||||
#define TIMERS_STATE_H
|
||||
|
||||
/* timers state, for sharing between icount and cpu-timers */
|
||||
|
||||
typedef struct TimersState {
|
||||
/* Protected by BQL. */
|
||||
int64_t cpu_ticks_prev;
|
||||
int64_t cpu_ticks_offset;
|
||||
|
||||
/*
|
||||
* Protect fields that can be respectively read outside the
|
||||
* BQL, and written from multiple threads.
|
||||
*/
|
||||
QemuSeqLock vm_clock_seqlock;
|
||||
QemuSpin vm_clock_lock;
|
||||
|
||||
int16_t cpu_ticks_enabled;
|
||||
|
||||
/* Conversion factor from emulated instructions to virtual clock ticks. */
|
||||
int16_t icount_time_shift;
|
||||
|
||||
/* Compensate for varying guest execution speed. */
|
||||
int64_t qemu_icount_bias;
|
||||
|
||||
int64_t vm_clock_warp_start;
|
||||
int64_t cpu_clock_offset;
|
||||
|
||||
/* Only written by TCG thread */
|
||||
int64_t qemu_icount;
|
||||
|
||||
/* for adjusting icount */
|
||||
QEMUTimer *icount_rt_timer;
|
||||
QEMUTimer *icount_vm_timer;
|
||||
QEMUTimer *icount_warp_timer;
|
||||
} TimersState;
|
||||
|
||||
extern TimersState timers_state;
|
||||
|
||||
/*
|
||||
* icount needs this internal from cpu-timers when adjusting the icount shift.
|
||||
*/
|
||||
int64_t cpu_get_clock_locked(void);
|
||||
|
||||
#endif /* TIMERS_STATE_H */
|
@ -74,6 +74,7 @@
|
||||
#include "hw/audio/soundhw.h"
|
||||
#include "audio/audio.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "migration/colo.h"
|
||||
#include "migration/postcopy-ram.h"
|
||||
#include "sysemu/kvm.h"
|
||||
@ -2694,7 +2695,7 @@ static void user_register_global_props(void)
|
||||
|
||||
static int do_configure_icount(void *opaque, QemuOpts *opts, Error **errp)
|
||||
{
|
||||
configure_icount(opts, errp);
|
||||
icount_configure(opts, errp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2804,7 +2805,7 @@ static void configure_accelerators(const char *progname)
|
||||
error_report("falling back to %s", ac->name);
|
||||
}
|
||||
|
||||
if (use_icount && !(tcg_enabled() || qtest_enabled())) {
|
||||
if (icount_enabled() && !tcg_enabled()) {
|
||||
error_report("-icount is not allowed with hardware virtualization");
|
||||
exit(1);
|
||||
}
|
||||
@ -4254,7 +4255,8 @@ void qemu_init(int argc, char **argv, char **envp)
|
||||
semihosting_arg_fallback(kernel_filename, kernel_cmdline);
|
||||
}
|
||||
|
||||
cpu_ticks_init();
|
||||
/* initialize cpu timers and VCPU throttle modules */
|
||||
cpu_timers_init();
|
||||
|
||||
if (default_net) {
|
||||
QemuOptsList *net = qemu_find_opts("net");
|
||||
|
@ -1,7 +0,0 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/timer.h"
|
||||
|
||||
void qemu_start_warp_timer(void)
|
||||
{
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "qemu/main-loop.h"
|
||||
|
||||
int64_t cpu_get_clock(void)
|
||||
{
|
||||
|
@ -1,16 +0,0 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "qemu/main-loop.h"
|
||||
|
||||
int use_icount;
|
||||
|
||||
int64_t cpu_get_icount(void)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
int64_t cpu_get_icount_raw(void)
|
||||
{
|
||||
abort();
|
||||
}
|
9
stubs/cpu-synchronize-state.c
Normal file
9
stubs/cpu-synchronize-state.c
Normal file
@ -0,0 +1,9 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/hw_accel.h"
|
||||
|
||||
void cpu_synchronize_state(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
void cpu_synchronize_post_init(CPUState *cpu)
|
||||
{
|
||||
}
|
8
stubs/cpus-get-virtual-clock.c
Normal file
8
stubs/cpus-get-virtual-clock.c
Normal file
@ -0,0 +1,8 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "qemu/main-loop.h"
|
||||
|
||||
int64_t cpus_get_virtual_clock(void)
|
||||
{
|
||||
return cpu_get_clock();
|
||||
}
|
45
stubs/icount.c
Normal file
45
stubs/icount.c
Normal file
@ -0,0 +1,45 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
|
||||
/* icount - Instruction Counter API */
|
||||
|
||||
int use_icount;
|
||||
|
||||
void icount_update(CPUState *cpu)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
void icount_configure(QemuOpts *opts, Error **errp)
|
||||
{
|
||||
/* signal error */
|
||||
error_setg(errp, "cannot configure icount, TCG support not available");
|
||||
}
|
||||
int64_t icount_get_raw(void)
|
||||
{
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
int64_t icount_get(void)
|
||||
{
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
int64_t icount_to_ns(int64_t icount)
|
||||
{
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
int64_t icount_round(int64_t count)
|
||||
{
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
void icount_start_warp_timer(void)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
void icount_account_warp_timer(void)
|
||||
{
|
||||
abort();
|
||||
}
|
@ -3,10 +3,11 @@ stub_ss.add(files('bdrv-next-monitor-owned.c'))
|
||||
stub_ss.add(files('blk-commit-all.c'))
|
||||
stub_ss.add(files('blockdev-close-all-bdrv-states.c'))
|
||||
stub_ss.add(files('change-state-handler.c'))
|
||||
stub_ss.add(files('clock-warp.c'))
|
||||
stub_ss.add(files('cmos.c'))
|
||||
stub_ss.add(files('cpu-get-clock.c'))
|
||||
stub_ss.add(files('cpu-get-icount.c'))
|
||||
stub_ss.add(files('cpus-get-virtual-clock.c'))
|
||||
stub_ss.add(files('qemu-timer-notify-cb.c'))
|
||||
stub_ss.add(files('icount.c'))
|
||||
stub_ss.add(files('dump.c'))
|
||||
stub_ss.add(files('error-printf.c'))
|
||||
stub_ss.add(files('fdset.c'))
|
||||
@ -44,6 +45,7 @@ stub_ss.add(files('vmgenid.c'))
|
||||
stub_ss.add(files('vmstate.c'))
|
||||
stub_ss.add(files('vm-stop.c'))
|
||||
stub_ss.add(files('win32-kbd-hook.c'))
|
||||
stub_ss.add(files('cpu-synchronize-state.c'))
|
||||
if have_system
|
||||
stub_ss.add(files('semihost.c'))
|
||||
stub_ss.add(files('xen-hw-stub.c'))
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "qemu/main-loop.h"
|
||||
|
||||
void qemu_timer_notify_cb(void *opaque, QEMUClockType type)
|
||||
|
@ -18,3 +18,8 @@ bool qtest_driver(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t qtest_get_virtual_clock(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -88,3 +88,18 @@ int replay_read_random(void *buf, size_t len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t replay_get_current_icount(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool replay_reverse_step(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool replay_reverse_continue(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "disas/disas.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "exec/exec-all.h"
|
||||
@ -1329,7 +1330,7 @@ static DisasJumpType gen_mfpr(DisasContext *ctx, TCGv va, int regno)
|
||||
case 249: /* VMTIME */
|
||||
helper = gen_helper_get_vmtime;
|
||||
do_helper:
|
||||
if (use_icount) {
|
||||
if (icount_enabled()) {
|
||||
gen_io_start();
|
||||
helper(va);
|
||||
return DISAS_PC_STALE;
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "hw/irq.h"
|
||||
#include "hw/semihosting/semihost.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/tcg.h"
|
||||
#include "qemu/range.h"
|
||||
@ -1206,17 +1207,17 @@ static int64_t cycles_ns_per(uint64_t cycles)
|
||||
|
||||
static bool instructions_supported(CPUARMState *env)
|
||||
{
|
||||
return use_icount == 1 /* Precise instruction counting */;
|
||||
return icount_enabled() == 1; /* Precise instruction counting */
|
||||
}
|
||||
|
||||
static uint64_t instructions_get_count(CPUARMState *env)
|
||||
{
|
||||
return (uint64_t)cpu_get_icount_raw();
|
||||
return (uint64_t)icount_get_raw();
|
||||
}
|
||||
|
||||
static int64_t instructions_ns_per(uint64_t icount)
|
||||
{
|
||||
return cpu_icount_to_ns((int64_t)icount);
|
||||
return icount_to_ns((int64_t)icount);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -28,13 +28,13 @@
|
||||
#include "exec/address-spaces.h"
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "hax-i386.h"
|
||||
#include "sysemu/accel.h"
|
||||
#include "sysemu/reset.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "hw/boards.h"
|
||||
|
||||
#include "hax-cpus.h"
|
||||
|
||||
#define DEBUG_HAX 0
|
||||
|
||||
#define DPRINTF(fmt, ...) \
|
||||
@ -296,15 +296,6 @@ int hax_vm_destroy(struct hax_vm *vm)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hax_handle_interrupt(CPUState *cpu, int mask)
|
||||
{
|
||||
cpu->interrupt_request |= mask;
|
||||
|
||||
if (!qemu_cpu_is_self(cpu)) {
|
||||
qemu_cpu_kick(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
static int hax_init(ram_addr_t ram_size, int max_cpus)
|
||||
{
|
||||
struct hax_state *hax = NULL;
|
||||
@ -349,7 +340,6 @@ static int hax_init(ram_addr_t ram_size, int max_cpus)
|
||||
qversion.cur_version = hax_cur_version;
|
||||
qversion.min_version = hax_min_version;
|
||||
hax_notify_qemu_version(hax->vm->fd, &qversion);
|
||||
cpu_interrupt_handler = hax_handle_interrupt;
|
||||
|
||||
return ret;
|
||||
error:
|
||||
@ -374,6 +364,9 @@ static int hax_accel_init(MachineState *ms)
|
||||
!ret ? "working" : "not working",
|
||||
!ret ? "fast virt" : "emulation");
|
||||
}
|
||||
if (ret == 0) {
|
||||
cpus_register_accel(&hax_cpus);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
84
target/i386/hax-cpus.c
Normal file
84
target/i386/hax-cpus.c
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* QEMU HAX support
|
||||
*
|
||||
* Copyright IBM, Corp. 2008
|
||||
* Red Hat, Inc. 2008
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
* Glauber Costa <gcosta@redhat.com>
|
||||
*
|
||||
* Copyright (c) 2011 Intel Corporation
|
||||
* Written by:
|
||||
* Jiang Yunhong<yunhong.jiang@intel.com>
|
||||
* Xin Xiaohui<xiaohui.xin@intel.com>
|
||||
* Zhang Xiantao<xiantao.zhang@intel.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/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "qemu/guest-random.h"
|
||||
|
||||
#include "hax-cpus.h"
|
||||
|
||||
static void *hax_cpu_thread_fn(void *arg)
|
||||
{
|
||||
CPUState *cpu = arg;
|
||||
int r;
|
||||
|
||||
rcu_register_thread();
|
||||
qemu_mutex_lock_iothread();
|
||||
qemu_thread_get_self(cpu->thread);
|
||||
|
||||
cpu->thread_id = qemu_get_thread_id();
|
||||
hax_init_vcpu(cpu);
|
||||
cpu_thread_signal_created(cpu);
|
||||
qemu_guest_random_seed_thread_part2(cpu->random_seed);
|
||||
|
||||
do {
|
||||
if (cpu_can_run(cpu)) {
|
||||
r = hax_smp_cpu_exec(cpu);
|
||||
if (r == EXCP_DEBUG) {
|
||||
cpu_handle_guest_debug(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
qemu_wait_io_event(cpu);
|
||||
} while (!cpu->unplug || cpu_can_run(cpu));
|
||||
rcu_unregister_thread();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void hax_start_vcpu_thread(CPUState *cpu)
|
||||
{
|
||||
char thread_name[VCPU_THREAD_NAME_SIZE];
|
||||
|
||||
cpu->thread = g_malloc0(sizeof(QemuThread));
|
||||
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
|
||||
qemu_cond_init(cpu->halt_cond);
|
||||
|
||||
snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/HAX",
|
||||
cpu->cpu_index);
|
||||
qemu_thread_create(cpu->thread, thread_name, hax_cpu_thread_fn,
|
||||
cpu, QEMU_THREAD_JOINABLE);
|
||||
#ifdef _WIN32
|
||||
cpu->hThread = qemu_thread_get_handle(cpu->thread);
|
||||
#endif
|
||||
}
|
||||
|
||||
const CpusAccel hax_cpus = {
|
||||
.create_vcpu_thread = hax_start_vcpu_thread,
|
||||
.kick_vcpu_thread = hax_kick_vcpu_thread,
|
||||
|
||||
.synchronize_post_reset = hax_cpu_synchronize_post_reset,
|
||||
.synchronize_post_init = hax_cpu_synchronize_post_init,
|
||||
.synchronize_state = hax_cpu_synchronize_state,
|
||||
.synchronize_pre_loadvm = hax_cpu_synchronize_pre_loadvm,
|
||||
};
|
33
target/i386/hax-cpus.h
Normal file
33
target/i386/hax-cpus.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Accelerator CPUS Interface
|
||||
*
|
||||
* Copyright 2020 SUSE LLC
|
||||
*
|
||||
* 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 HAX_CPUS_H
|
||||
#define HAX_CPUS_H
|
||||
|
||||
#include "sysemu/cpus.h"
|
||||
|
||||
extern const CpusAccel hax_cpus;
|
||||
|
||||
#include "hax-interface.h"
|
||||
#include "hax-i386.h"
|
||||
|
||||
int hax_init_vcpu(CPUState *cpu);
|
||||
int hax_smp_cpu_exec(CPUState *cpu);
|
||||
int hax_populate_ram(uint64_t va, uint64_t size);
|
||||
|
||||
void hax_cpu_synchronize_state(CPUState *cpu);
|
||||
void hax_cpu_synchronize_post_reset(CPUState *cpu);
|
||||
void hax_cpu_synchronize_post_init(CPUState *cpu);
|
||||
void hax_cpu_synchronize_pre_loadvm(CPUState *cpu);
|
||||
|
||||
int hax_vcpu_destroy(CPUState *cpu);
|
||||
void hax_raise_event(CPUState *cpu);
|
||||
void hax_reset_vcpu_state(void *opaque);
|
||||
|
||||
#endif /* HAX_CPUS_H */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user