mirror of https://github.com/xemu-project/xemu.git
KVM changes include a MIPS patch and the testdev backend used by the
ARM kvm-unit-tests. icount include the first part of reverse execution and Sebastian Tanase's patches to slow down -icount execution to the desired speed of the target. v1->v2: fix dump_drift_info to print nothing outside icount mode, and to compile on 32-bit architectures -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJT43qmAAoJEBvWZb6bTYbySBYP/Rhk22T/PCYMt2GDCAECcpxG +ccNxk9nJKsqpuMr9bM7ig7BXlEXm9Z6wC57waUqcHTbbc/6pTC5deMQOp3vz7ll aPQ1GLmkRuxbZckhjwww3KIZWFRVZBlNHSmAm+oqV+w9WXAERXTwBR7w+e427q5W KiDkO2nBAg/6g6NT+DrzspEbHWRDMBOR6EAd/LAMKInQz+o1wStlKhBNlJB/fvtx AflY8xSaY8g/+q6IG9qmc2//QD8tVR5sPJ4io4+86GuFlrnhukHHyY0UtuiDRr4S cwSlphCTUDriOsnXkci5ya5ZKNtaQdwdiVWbXK8tZ6gkvbeVTuTVe/D2/2qGjbKN GCsEsLRyGdR/lUPLpTt6m7T0JnP98H7fb8GJypEnmqAPjsNVk2inSi87Oc22Osv1 9zkzHJL4PIQvsMdBQhG3PuXhIVWqrsS29hp9MR6O1NgLnHROc+l8/luOyCICG7FL gW8CwAUwxH7UM1VKmwC5JG9BDNqxP24T33OXoDkVnJoqgP1sBdz2BVW13vctVM3Q MgQG3cOBQLAA8iqGRRvjY59b17ieJnjyhEWmG6fp9uyhQKyj8K61RH4s+di4q4Ym k7Z+FYsIRHVY5vu+MEhacrs4CRFHBTxM2uwOHXk5WVPFnMMyXhA/bdAurS2u9UzO fyUSEhvCse7SqS1TJock =7ZQL -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging KVM changes include a MIPS patch and the testdev backend used by the ARM kvm-unit-tests. icount include the first part of reverse execution and Sebastian Tanase's patches to slow down -icount execution to the desired speed of the target. v1->v2: fix dump_drift_info to print nothing outside icount mode, and to compile on 32-bit architectures # gpg: Signature made Thu 07 Aug 2014 14:09:58 BST using RSA key ID 9B4D86F2 # gpg: Good signature from "Paolo Bonzini <pbonzini@redhat.com>" # gpg: aka "Paolo Bonzini <bonzini@gnu.org>" * remotes/bonzini/tags/for-upstream: target-mips: Ignore unassigned accesses with KVM monitor: Add drift info to 'info jit' cpu-exec: Print to console if the guest is late cpu-exec: Add sleeping algorithm icount: Add align option to icount icount: Add QemuOpts for icount icount: Fix virtual clock start value on ARM timer: add cpu_icount_to_ns function. migration: migrate icount fields. icount: put icount variables into TimerState. backends: Introduce chr-testdev Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
2ee55b8351
|
@ -1,7 +1,7 @@
|
|||
common-obj-y += rng.o rng-egd.o
|
||||
common-obj-$(CONFIG_POSIX) += rng-random.o
|
||||
|
||||
common-obj-y += msmouse.o
|
||||
common-obj-y += msmouse.o testdev.o
|
||||
common-obj-$(CONFIG_BRLAPI) += baum.o
|
||||
baum.o-cflags := $(SDL_CFLAGS)
|
||||
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* QEMU Char Device for testsuite control
|
||||
*
|
||||
* Copyright (c) 2014 Red Hat, Inc.
|
||||
*
|
||||
* Author: Paolo Bonzini <pbonzini@redhat.com>
|
||||
*
|
||||
* 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-common.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
#define BUF_SIZE 32
|
||||
|
||||
typedef struct {
|
||||
CharDriverState *chr;
|
||||
uint8_t in_buf[32];
|
||||
int in_buf_used;
|
||||
} TestdevCharState;
|
||||
|
||||
/* Try to interpret a whole incoming packet */
|
||||
static int testdev_eat_packet(TestdevCharState *testdev)
|
||||
{
|
||||
const uint8_t *cur = testdev->in_buf;
|
||||
int len = testdev->in_buf_used;
|
||||
uint8_t c;
|
||||
int arg;
|
||||
|
||||
#define EAT(c) do { \
|
||||
if (!len--) { \
|
||||
return 0; \
|
||||
} \
|
||||
c = *cur++; \
|
||||
} while (0)
|
||||
|
||||
EAT(c);
|
||||
|
||||
while (isspace(c)) {
|
||||
EAT(c);
|
||||
}
|
||||
|
||||
arg = 0;
|
||||
while (isdigit(c)) {
|
||||
arg = arg * 10 + c - '0';
|
||||
EAT(c);
|
||||
}
|
||||
|
||||
while (isspace(c)) {
|
||||
EAT(c);
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 'q':
|
||||
exit((arg << 1) | 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return cur - testdev->in_buf;
|
||||
}
|
||||
|
||||
/* The other end is writing some data. Store it and try to interpret */
|
||||
static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len)
|
||||
{
|
||||
TestdevCharState *testdev = chr->opaque;
|
||||
int tocopy, eaten, orig_len = len;
|
||||
|
||||
while (len) {
|
||||
/* Complete our buffer as much as possible */
|
||||
tocopy = MIN(len, BUF_SIZE - testdev->in_buf_used);
|
||||
|
||||
memcpy(testdev->in_buf + testdev->in_buf_used, buf, tocopy);
|
||||
testdev->in_buf_used += tocopy;
|
||||
buf += tocopy;
|
||||
len -= tocopy;
|
||||
|
||||
/* Interpret it as much as possible */
|
||||
while (testdev->in_buf_used > 0 &&
|
||||
(eaten = testdev_eat_packet(testdev)) > 0) {
|
||||
memmove(testdev->in_buf, testdev->in_buf + eaten,
|
||||
testdev->in_buf_used - eaten);
|
||||
testdev->in_buf_used -= eaten;
|
||||
}
|
||||
}
|
||||
return orig_len;
|
||||
}
|
||||
|
||||
static void testdev_close(struct CharDriverState *chr)
|
||||
{
|
||||
TestdevCharState *testdev = chr->opaque;
|
||||
|
||||
g_free(testdev);
|
||||
}
|
||||
|
||||
CharDriverState *chr_testdev_init(void)
|
||||
{
|
||||
TestdevCharState *testdev;
|
||||
CharDriverState *chr;
|
||||
|
||||
testdev = g_malloc0(sizeof(TestdevCharState));
|
||||
testdev->chr = chr = g_malloc0(sizeof(CharDriverState));
|
||||
|
||||
chr->opaque = testdev;
|
||||
chr->chr_write = testdev_write;
|
||||
chr->chr_close = testdev_close;
|
||||
|
||||
return chr;
|
||||
}
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
register_char_driver_qapi("testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
116
cpu-exec.c
116
cpu-exec.c
|
@ -22,6 +22,109 @@
|
|||
#include "tcg.h"
|
||||
#include "qemu/atomic.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "qemu/timer.h"
|
||||
|
||||
/* -icount align implementation. */
|
||||
|
||||
typedef struct SyncClocks {
|
||||
int64_t diff_clk;
|
||||
int64_t last_cpu_icount;
|
||||
int64_t realtime_clock;
|
||||
} SyncClocks;
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
/* Allow the guest to have a max 3ms advance.
|
||||
* The difference between the 2 clocks could therefore
|
||||
* oscillate around 0.
|
||||
*/
|
||||
#define VM_CLOCK_ADVANCE 3000000
|
||||
#define THRESHOLD_REDUCE 1.5
|
||||
#define MAX_DELAY_PRINT_RATE 2000000000LL
|
||||
#define MAX_NB_PRINTS 100
|
||||
|
||||
static void align_clocks(SyncClocks *sc, const CPUState *cpu)
|
||||
{
|
||||
int64_t cpu_icount;
|
||||
|
||||
if (!icount_align_option) {
|
||||
return;
|
||||
}
|
||||
|
||||
cpu_icount = cpu->icount_extra + cpu->icount_decr.u16.low;
|
||||
sc->diff_clk += cpu_icount_to_ns(sc->last_cpu_icount - cpu_icount);
|
||||
sc->last_cpu_icount = cpu_icount;
|
||||
|
||||
if (sc->diff_clk > VM_CLOCK_ADVANCE) {
|
||||
#ifndef _WIN32
|
||||
struct timespec sleep_delay, rem_delay;
|
||||
sleep_delay.tv_sec = sc->diff_clk / 1000000000LL;
|
||||
sleep_delay.tv_nsec = sc->diff_clk % 1000000000LL;
|
||||
if (nanosleep(&sleep_delay, &rem_delay) < 0) {
|
||||
sc->diff_clk -= (sleep_delay.tv_sec - rem_delay.tv_sec) * 1000000000LL;
|
||||
sc->diff_clk -= sleep_delay.tv_nsec - rem_delay.tv_nsec;
|
||||
} else {
|
||||
sc->diff_clk = 0;
|
||||
}
|
||||
#else
|
||||
Sleep(sc->diff_clk / SCALE_MS);
|
||||
sc->diff_clk = 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void print_delay(const SyncClocks *sc)
|
||||
{
|
||||
static float threshold_delay;
|
||||
static int64_t last_realtime_clock;
|
||||
static int nb_prints;
|
||||
|
||||
if (icount_align_option &&
|
||||
sc->realtime_clock - last_realtime_clock >= MAX_DELAY_PRINT_RATE &&
|
||||
nb_prints < MAX_NB_PRINTS) {
|
||||
if ((-sc->diff_clk / (float)1000000000LL > threshold_delay) ||
|
||||
(-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);
|
||||
nb_prints++;
|
||||
last_realtime_clock = sc->realtime_clock;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void init_delay_params(SyncClocks *sc,
|
||||
const CPUState *cpu)
|
||||
{
|
||||
if (!icount_align_option) {
|
||||
return;
|
||||
}
|
||||
sc->realtime_clock = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
|
||||
sc->diff_clk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
|
||||
sc->realtime_clock +
|
||||
cpu_get_clock_offset();
|
||||
sc->last_cpu_icount = cpu->icount_extra + cpu->icount_decr.u16.low;
|
||||
if (sc->diff_clk < max_delay) {
|
||||
max_delay = sc->diff_clk;
|
||||
}
|
||||
if (sc->diff_clk > max_advance) {
|
||||
max_advance = sc->diff_clk;
|
||||
}
|
||||
|
||||
/* Print every 2s max if the guest is late. We limit the number
|
||||
of printed messages to NB_PRINT_MAX(currently 100) */
|
||||
print_delay(sc);
|
||||
}
|
||||
#else
|
||||
static void align_clocks(SyncClocks *sc, const CPUState *cpu)
|
||||
{
|
||||
}
|
||||
|
||||
static void init_delay_params(SyncClocks *sc, const CPUState *cpu)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG USER ONLY */
|
||||
|
||||
void cpu_loop_exit(CPUState *cpu)
|
||||
{
|
||||
|
@ -227,6 +330,8 @@ int cpu_exec(CPUArchState *env)
|
|||
TranslationBlock *tb;
|
||||
uint8_t *tc_ptr;
|
||||
uintptr_t next_tb;
|
||||
SyncClocks sc;
|
||||
|
||||
/* This must be volatile so it is not trashed by longjmp() */
|
||||
volatile bool have_tb_lock = false;
|
||||
|
||||
|
@ -283,6 +388,13 @@ int cpu_exec(CPUArchState *env)
|
|||
#endif
|
||||
cpu->exception_index = -1;
|
||||
|
||||
/* Calculate difference between guest clock and host clock.
|
||||
* This delay includes the delay of the last cycle, so
|
||||
* what we have to do is sleep until it is 0. As for the
|
||||
* advance/delay we gain here, we try to fix it next time.
|
||||
*/
|
||||
init_delay_params(&sc, cpu);
|
||||
|
||||
/* prepare setjmp context for exception handling */
|
||||
for(;;) {
|
||||
if (sigsetjmp(cpu->jmp_env, 0) == 0) {
|
||||
|
@ -672,6 +784,7 @@ int cpu_exec(CPUArchState *env)
|
|||
if (insns_left > 0) {
|
||||
/* Execute remaining instructions. */
|
||||
cpu_exec_nocache(env, insns_left, tb);
|
||||
align_clocks(&sc, cpu);
|
||||
}
|
||||
cpu->exception_index = EXCP_INTERRUPT;
|
||||
next_tb = 0;
|
||||
|
@ -684,6 +797,9 @@ int cpu_exec(CPUArchState *env)
|
|||
}
|
||||
}
|
||||
cpu->current_tb = NULL;
|
||||
/* Try to align the host and virtual clocks
|
||||
if the guest is in advance */
|
||||
align_clocks(&sc, cpu);
|
||||
/* reset soft MMU for next block (it can currently
|
||||
only be set by a memory fault) */
|
||||
} /* for(;;) */
|
||||
|
|
118
cpus.c
118
cpus.c
|
@ -64,6 +64,8 @@
|
|||
#endif /* CONFIG_LINUX */
|
||||
|
||||
static CPUState *next_cpu;
|
||||
int64_t max_delay;
|
||||
int64_t max_advance;
|
||||
|
||||
bool cpu_is_stopped(CPUState *cpu)
|
||||
{
|
||||
|
@ -102,17 +104,12 @@ static bool all_cpu_threads_idle(void)
|
|||
|
||||
/* Protected by TimersState seqlock */
|
||||
|
||||
/* Compensate for varying guest execution speed. */
|
||||
static int64_t qemu_icount_bias;
|
||||
static int64_t vm_clock_warp_start;
|
||||
static int64_t vm_clock_warp_start = -1;
|
||||
/* Conversion factor from emulated instructions to virtual clock ticks. */
|
||||
static int icount_time_shift;
|
||||
/* Arbitrarily pick 1MIPS as the minimum allowable speed. */
|
||||
#define MAX_ICOUNT_SHIFT 10
|
||||
|
||||
/* Only written by TCG thread */
|
||||
static int64_t qemu_icount;
|
||||
|
||||
static QEMUTimer *icount_rt_timer;
|
||||
static QEMUTimer *icount_vm_timer;
|
||||
static QEMUTimer *icount_warp_timer;
|
||||
|
@ -129,6 +126,11 @@ typedef struct TimersState {
|
|||
int64_t cpu_clock_offset;
|
||||
int32_t cpu_ticks_enabled;
|
||||
int64_t dummy;
|
||||
|
||||
/* Compensate for varying guest execution speed. */
|
||||
int64_t qemu_icount_bias;
|
||||
/* Only written by TCG thread */
|
||||
int64_t qemu_icount;
|
||||
} TimersState;
|
||||
|
||||
static TimersState timers_state;
|
||||
|
@ -139,14 +141,14 @@ static int64_t cpu_get_icount_locked(void)
|
|||
int64_t icount;
|
||||
CPUState *cpu = current_cpu;
|
||||
|
||||
icount = qemu_icount;
|
||||
icount = timers_state.qemu_icount;
|
||||
if (cpu) {
|
||||
if (!cpu_can_do_io(cpu)) {
|
||||
fprintf(stderr, "Bad clock read\n");
|
||||
}
|
||||
icount -= (cpu->icount_decr.u16.low + cpu->icount_extra);
|
||||
}
|
||||
return qemu_icount_bias + (icount << icount_time_shift);
|
||||
return timers_state.qemu_icount_bias + cpu_icount_to_ns(icount);
|
||||
}
|
||||
|
||||
int64_t cpu_get_icount(void)
|
||||
|
@ -162,6 +164,11 @@ int64_t cpu_get_icount(void)
|
|||
return icount;
|
||||
}
|
||||
|
||||
int64_t cpu_icount_to_ns(int64_t icount)
|
||||
{
|
||||
return icount << icount_time_shift;
|
||||
}
|
||||
|
||||
/* return the host CPU cycle counter and handle stop/restart */
|
||||
/* Caller must hold the BQL */
|
||||
int64_t cpu_get_ticks(void)
|
||||
|
@ -214,6 +221,23 @@ int64_t cpu_get_clock(void)
|
|||
return ti;
|
||||
}
|
||||
|
||||
/* return the offset between the host clock and virtual CPU clock */
|
||||
int64_t cpu_get_clock_offset(void)
|
||||
{
|
||||
int64_t ti;
|
||||
unsigned start;
|
||||
|
||||
do {
|
||||
start = seqlock_read_begin(&timers_state.vm_clock_seqlock);
|
||||
ti = timers_state.cpu_clock_offset;
|
||||
if (!timers_state.cpu_ticks_enabled) {
|
||||
ti -= get_clock();
|
||||
}
|
||||
} while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));
|
||||
|
||||
return -ti;
|
||||
}
|
||||
|
||||
/* enable cpu_get_ticks()
|
||||
* Caller must hold BQL which server as mutex for vm_clock_seqlock.
|
||||
*/
|
||||
|
@ -284,7 +308,8 @@ static void icount_adjust(void)
|
|||
icount_time_shift++;
|
||||
}
|
||||
last_delta = delta;
|
||||
qemu_icount_bias = cur_icount - (qemu_icount << icount_time_shift);
|
||||
timers_state.qemu_icount_bias = cur_icount
|
||||
- (timers_state.qemu_icount << icount_time_shift);
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock);
|
||||
}
|
||||
|
||||
|
@ -333,7 +358,7 @@ static void icount_warp_rt(void *opaque)
|
|||
int64_t delta = cur_time - cur_icount;
|
||||
warp_delta = MIN(warp_delta, delta);
|
||||
}
|
||||
qemu_icount_bias += warp_delta;
|
||||
timers_state.qemu_icount_bias += warp_delta;
|
||||
}
|
||||
vm_clock_warp_start = -1;
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock);
|
||||
|
@ -351,7 +376,7 @@ void qtest_clock_warp(int64_t dest)
|
|||
int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL);
|
||||
int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock);
|
||||
qemu_icount_bias += warp;
|
||||
timers_state.qemu_icount_bias += warp;
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock);
|
||||
|
||||
qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
|
||||
|
@ -428,6 +453,25 @@ void qemu_clock_warp(QEMUClockType type)
|
|||
}
|
||||
}
|
||||
|
||||
static bool icount_state_needed(void *opaque)
|
||||
{
|
||||
return use_icount;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a subsection for icount migration.
|
||||
*/
|
||||
static const VMStateDescription icount_vmstate_timers = {
|
||||
.name = "timer/icount",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT64(qemu_icount_bias, TimersState),
|
||||
VMSTATE_INT64(qemu_icount, TimersState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_timers = {
|
||||
.name = "timer",
|
||||
.version_id = 2,
|
||||
|
@ -437,23 +481,44 @@ static const VMStateDescription vmstate_timers = {
|
|||
VMSTATE_INT64(dummy, TimersState),
|
||||
VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection[]) {
|
||||
{
|
||||
.vmsd = &icount_vmstate_timers,
|
||||
.needed = icount_state_needed,
|
||||
}, {
|
||||
/* empty */
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void configure_icount(const char *option)
|
||||
void configure_icount(QemuOpts *opts, Error **errp)
|
||||
{
|
||||
const char *option;
|
||||
char *rem_str = NULL;
|
||||
|
||||
seqlock_init(&timers_state.vm_clock_seqlock, NULL);
|
||||
vmstate_register(NULL, 0, &vmstate_timers, &timers_state);
|
||||
option = qemu_opt_get(opts, "shift");
|
||||
if (!option) {
|
||||
if (qemu_opt_get(opts, "align") != NULL) {
|
||||
error_setg(errp, "Please specify shift option when using align");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
icount_align_option = qemu_opt_get_bool(opts, "align", false);
|
||||
icount_warp_timer = timer_new_ns(QEMU_CLOCK_REALTIME,
|
||||
icount_warp_rt, NULL);
|
||||
if (strcmp(option, "auto") != 0) {
|
||||
icount_time_shift = strtol(option, NULL, 0);
|
||||
errno = 0;
|
||||
icount_time_shift = strtol(option, &rem_str, 0);
|
||||
if (errno != 0 || *rem_str != '\0' || !strlen(option)) {
|
||||
error_setg(errp, "icount: Invalid shift value");
|
||||
}
|
||||
use_icount = 1;
|
||||
return;
|
||||
} else if (icount_align_option) {
|
||||
error_setg(errp, "shift=auto and align=on are incompatible");
|
||||
}
|
||||
|
||||
use_icount = 2;
|
||||
|
@ -1250,7 +1315,8 @@ static int tcg_cpu_exec(CPUArchState *env)
|
|||
int64_t count;
|
||||
int64_t deadline;
|
||||
int decr;
|
||||
qemu_icount -= (cpu->icount_decr.u16.low + cpu->icount_extra);
|
||||
timers_state.qemu_icount -= (cpu->icount_decr.u16.low
|
||||
+ cpu->icount_extra);
|
||||
cpu->icount_decr.u16.low = 0;
|
||||
cpu->icount_extra = 0;
|
||||
deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL);
|
||||
|
@ -1265,7 +1331,7 @@ static int tcg_cpu_exec(CPUArchState *env)
|
|||
}
|
||||
|
||||
count = qemu_icount_round(deadline);
|
||||
qemu_icount += count;
|
||||
timers_state.qemu_icount += count;
|
||||
decr = (count > 0xffff) ? 0xffff : count;
|
||||
count -= decr;
|
||||
cpu->icount_decr.u16.low = decr;
|
||||
|
@ -1278,7 +1344,8 @@ static int tcg_cpu_exec(CPUArchState *env)
|
|||
if (use_icount) {
|
||||
/* Fold pending instructions back into the
|
||||
instruction counter, and clear the interrupt flag. */
|
||||
qemu_icount -= (cpu->icount_decr.u16.low + cpu->icount_extra);
|
||||
timers_state.qemu_icount -= (cpu->icount_decr.u16.low
|
||||
+ cpu->icount_extra);
|
||||
cpu->icount_decr.u32 = 0;
|
||||
cpu->icount_extra = 0;
|
||||
}
|
||||
|
@ -1487,3 +1554,20 @@ void qmp_inject_nmi(Error **errp)
|
|||
error_set(errp, QERR_UNSUPPORTED);
|
||||
#endif
|
||||
}
|
||||
|
||||
void dump_drift_info(FILE *f, fprintf_function cpu_fprintf)
|
||||
{
|
||||
if (!use_icount) {
|
||||
return;
|
||||
}
|
||||
|
||||
cpu_fprintf(f, "Host - Guest clock %"PRIi64" ms\n",
|
||||
(cpu_get_clock() - cpu_get_icount())/SCALE_MS);
|
||||
if (icount_align_option) {
|
||||
cpu_fprintf(f, "Max guest delay %"PRIi64" ms\n", -max_delay/SCALE_MS);
|
||||
cpu_fprintf(f, "Max guest advance %"PRIi64" ms\n", max_advance/SCALE_MS);
|
||||
} else {
|
||||
cpu_fprintf(f, "Max guest delay NA\n");
|
||||
cpu_fprintf(f, "Max guest advance NA\n");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include "glib-compat.h"
|
||||
#include "qemu/option.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "sysemu/os-win32.h"
|
||||
|
@ -105,8 +106,13 @@ static inline char *realpath(const char *path, char *resolved_path)
|
|||
#endif
|
||||
|
||||
/* icount */
|
||||
void configure_icount(const char *option);
|
||||
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(FILE *f, fprintf_function cpu_fprintf);
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/bswap.h"
|
||||
|
|
|
@ -745,6 +745,8 @@ static inline int64_t get_clock(void)
|
|||
/* icount */
|
||||
int64_t cpu_get_icount(void);
|
||||
int64_t cpu_get_clock(void);
|
||||
int64_t cpu_get_clock_offset(void);
|
||||
int64_t cpu_icount_to_ns(int64_t icount);
|
||||
|
||||
/*******************************************/
|
||||
/* host CPU ticks (if available) */
|
||||
|
|
|
@ -358,6 +358,9 @@ CharDriverState *qemu_char_get_next_serial(void);
|
|||
/* msmouse */
|
||||
CharDriverState *qemu_chr_open_msmouse(void);
|
||||
|
||||
/* testdev.c */
|
||||
CharDriverState *chr_testdev_init(void);
|
||||
|
||||
/* baum.c */
|
||||
CharDriverState *chr_baum_init(void);
|
||||
|
||||
|
|
|
@ -1047,6 +1047,7 @@ static void do_info_registers(Monitor *mon, const QDict *qdict)
|
|||
static void do_info_jit(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
dump_exec_info((FILE *)mon, monitor_fprintf);
|
||||
dump_drift_info((FILE *)mon, monitor_fprintf);
|
||||
}
|
||||
|
||||
static void do_info_history(Monitor *mon, const QDict *qdict)
|
||||
|
|
|
@ -2749,7 +2749,7 @@
|
|||
#
|
||||
# Configuration info for the new chardev backend.
|
||||
#
|
||||
# Since: 1.4
|
||||
# Since: 1.4 (testdev since 2.2)
|
||||
##
|
||||
{ 'type': 'ChardevDummy', 'data': { } }
|
||||
|
||||
|
@ -2764,6 +2764,7 @@
|
|||
'mux' : 'ChardevMux',
|
||||
'msmouse': 'ChardevDummy',
|
||||
'braille': 'ChardevDummy',
|
||||
'testdev': 'ChardevDummy',
|
||||
'stdio' : 'ChardevStdio',
|
||||
'console': 'ChardevDummy',
|
||||
'spicevmc' : 'ChardevSpiceChannel',
|
||||
|
|
|
@ -3256,6 +3256,7 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
|
|||
strcmp(filename, "pty") == 0 ||
|
||||
strcmp(filename, "msmouse") == 0 ||
|
||||
strcmp(filename, "braille") == 0 ||
|
||||
strcmp(filename, "testdev") == 0 ||
|
||||
strcmp(filename, "stdio") == 0) {
|
||||
qemu_opt_set(opts, "backend", filename);
|
||||
return opts;
|
||||
|
@ -4057,6 +4058,9 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
|
|||
chr = chr_baum_init();
|
||||
break;
|
||||
#endif
|
||||
case CHARDEV_BACKEND_KIND_TESTDEV:
|
||||
chr = chr_testdev_init();
|
||||
break;
|
||||
case CHARDEV_BACKEND_KIND_STDIO:
|
||||
chr = qemu_chr_open_stdio(backend->stdio);
|
||||
break;
|
||||
|
|
|
@ -3011,11 +3011,11 @@ re-inject them.
|
|||
ETEXI
|
||||
|
||||
DEF("icount", HAS_ARG, QEMU_OPTION_icount, \
|
||||
"-icount [N|auto]\n" \
|
||||
"-icount [shift=N|auto][,align=on|off]\n" \
|
||||
" enable virtual instruction counter with 2^N clock ticks per\n" \
|
||||
" instruction\n", QEMU_ARCH_ALL)
|
||||
" instruction and enable aligning the host and virtual clocks\n", QEMU_ARCH_ALL)
|
||||
STEXI
|
||||
@item -icount [@var{N}|auto]
|
||||
@item -icount [shift=@var{N}|auto]
|
||||
@findex -icount
|
||||
Enable virtual instruction counter. The virtual cpu will execute one
|
||||
instruction every 2^@var{N} ns of virtual time. If @code{auto} is specified
|
||||
|
@ -3026,6 +3026,17 @@ Note that while this option can give deterministic behavior, it does not
|
|||
provide cycle accurate emulation. Modern CPUs contain superscalar out of
|
||||
order cores with complex cache hierarchies. The number of instructions
|
||||
executed often has little or no correlation with actual performance.
|
||||
|
||||
@option{align=on} will activate the delay algorithm which will try to
|
||||
to synchronise the host clock and the virtual clock. The goal is to
|
||||
have a guest running at the real frequency imposed by the shift option.
|
||||
Whenever the guest clock is behind the host clock and if
|
||||
@option{align=on} is specified then we print a messsage to the user
|
||||
to inform about the delay.
|
||||
Currently this option does not work when @option{shift} is @code{auto}.
|
||||
Note: The sync algorithm will work for those shift values for which
|
||||
the guest clock runs ahead of the host clock. Typically this happens
|
||||
when the shift value is high (how high depends on the host machine).
|
||||
ETEXI
|
||||
|
||||
DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \
|
||||
|
|
13
qtest.c
13
qtest.c
|
@ -19,6 +19,9 @@
|
|||
#include "hw/irq.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#define MAX_IRQ 256
|
||||
|
||||
|
@ -509,10 +512,16 @@ static void qtest_event(void *opaque, int event)
|
|||
}
|
||||
}
|
||||
|
||||
static void configure_qtest_icount(const char *options)
|
||||
{
|
||||
QemuOpts *opts = qemu_opts_parse(qemu_find_opts("icount"), options, 1);
|
||||
configure_icount(opts, &error_abort);
|
||||
qemu_opts_del(opts);
|
||||
}
|
||||
|
||||
int qtest_init_accel(MachineClass *mc)
|
||||
{
|
||||
configure_icount("0");
|
||||
|
||||
configure_qtest_icount("0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ stub-obj-y += arch-query-cpu-def.o
|
|||
stub-obj-y += bdrv-commit-all.o
|
||||
stub-obj-y += chr-baum-init.o
|
||||
stub-obj-y += chr-msmouse.o
|
||||
stub-obj-y += chr-testdev.o
|
||||
stub-obj-y += clock-warp.o
|
||||
stub-obj-y += cpu-get-clock.o
|
||||
stub-obj-y += cpu-get-icount.o
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#include "qemu-common.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
CharDriverState *chr_testdev_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -21,6 +21,7 @@
|
|||
#include "qemu/host-utils.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "sysemu/kvm.h"
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
static inline void cpu_mips_tlb_flush (CPUMIPSState *env, int flush_global);
|
||||
|
@ -2168,6 +2169,16 @@ void mips_cpu_unassigned_access(CPUState *cs, hwaddr addr,
|
|||
MIPSCPU *cpu = MIPS_CPU(cs);
|
||||
CPUMIPSState *env = &cpu->env;
|
||||
|
||||
/*
|
||||
* Raising an exception with KVM enabled will crash because it won't be from
|
||||
* the main execution loop so the longjmp won't have a matching setjmp.
|
||||
* Until we can trigger a bus error exception through KVM lets just ignore
|
||||
* the access.
|
||||
*/
|
||||
if (kvm_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_exec) {
|
||||
helper_raise_exception(env, EXCP_IBE);
|
||||
} else {
|
||||
|
|
39
vl.c
39
vl.c
|
@ -183,6 +183,7 @@ uint8_t *boot_splash_filedata;
|
|||
size_t boot_splash_filedata_size;
|
||||
uint8_t qemu_extra_params_fw[2];
|
||||
|
||||
int icount_align_option;
|
||||
typedef struct FWBootEntry FWBootEntry;
|
||||
|
||||
struct FWBootEntry {
|
||||
|
@ -537,6 +538,23 @@ static QemuOptsList qemu_mem_opts = {
|
|||
},
|
||||
};
|
||||
|
||||
static QemuOptsList qemu_icount_opts = {
|
||||
.name = "icount",
|
||||
.implied_opt_name = "shift",
|
||||
.merge_lists = true,
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qemu_icount_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = "shift",
|
||||
.type = QEMU_OPT_STRING,
|
||||
}, {
|
||||
.name = "align",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Get machine options
|
||||
*
|
||||
|
@ -2908,13 +2926,12 @@ int main(int argc, char **argv, char **envp)
|
|||
{
|
||||
int i;
|
||||
int snapshot, linux_boot;
|
||||
const char *icount_option = NULL;
|
||||
const char *initrd_filename;
|
||||
const char *kernel_filename, *kernel_cmdline;
|
||||
const char *boot_order;
|
||||
DisplayState *ds;
|
||||
int cyls, heads, secs, translation;
|
||||
QemuOpts *hda_opts = NULL, *opts, *machine_opts;
|
||||
QemuOpts *hda_opts = NULL, *opts, *machine_opts, *icount_opts = NULL;
|
||||
QemuOptsList *olist;
|
||||
int optind;
|
||||
const char *optarg;
|
||||
|
@ -2979,6 +2996,7 @@ int main(int argc, char **argv, char **envp)
|
|||
qemu_add_opts(&qemu_msg_opts);
|
||||
qemu_add_opts(&qemu_name_opts);
|
||||
qemu_add_opts(&qemu_numa_opts);
|
||||
qemu_add_opts(&qemu_icount_opts);
|
||||
|
||||
runstate_init();
|
||||
|
||||
|
@ -3830,7 +3848,11 @@ int main(int argc, char **argv, char **envp)
|
|||
}
|
||||
break;
|
||||
case QEMU_OPTION_icount:
|
||||
icount_option = optarg;
|
||||
icount_opts = qemu_opts_parse(qemu_find_opts("icount"),
|
||||
optarg, 1);
|
||||
if (!icount_opts) {
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case QEMU_OPTION_incoming:
|
||||
incoming = optarg;
|
||||
|
@ -4306,11 +4328,14 @@ int main(int argc, char **argv, char **envp)
|
|||
qemu_spice_init();
|
||||
#endif
|
||||
|
||||
if (icount_option && (kvm_enabled() || xen_enabled())) {
|
||||
fprintf(stderr, "-icount is not allowed with kvm or xen\n");
|
||||
exit(1);
|
||||
if (icount_opts) {
|
||||
if (kvm_enabled() || xen_enabled()) {
|
||||
fprintf(stderr, "-icount is not allowed with kvm or xen\n");
|
||||
exit(1);
|
||||
}
|
||||
configure_icount(icount_opts, &error_abort);
|
||||
qemu_opts_del(icount_opts);
|
||||
}
|
||||
configure_icount(icount_option);
|
||||
|
||||
/* clean up network at qemu process termination */
|
||||
atexit(&net_cleanup);
|
||||
|
|
Loading…
Reference in New Issue