* Record-replay lockstep execution, log dumper and fixes (Alex, Pavel)

* SCSI fix to pass maximum transfer size (Daniel Barboza)
 * chardev fixes and improved iothread support (Daniel Berrangé, Peter)
 * checkpatch tweak (Eric)
 * make help tweak (Marc-André)
 * make more PCI NICs available with -net or -nic (myself)
 * change default q35 NIC to e1000e (myself)
 * SCSI support for NDOB bit (myself)
 * membarrier system call support (myself)
 * SuperIO refactoring (Philippe)
 * miscellaneous cleanups and fixes (Thomas)
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQEcBAABAgAGBQJapqaMAAoJEL/70l94x66DQoUH/Rvg+a8giz/SrEA4P8D3Cb2z
 4GNbNUUoy4oU0ltD5IAMskMwpOsvl1batE0D+pKIlfO9NV4+Cj2kpgo0p9TxoYqM
 VCby3wRtx27zb5nVytC6M++iIKXmeEMqXmFw61I6umddNPSl4IR3hiHEE0DM+7dV
 UPIOvJeEiazyQaw3Iw+ZctNn8dDBKc/+6oxP9xRcYTaZ6hB4G9RZkqGNNSLcJkk7
 R0UotdjzIZhyWMOkjIwlpTF4sWv8gsYUV4bPYKMYho5B0Obda2dBM3I1kpA8yDa/
 xZ5lheOaAVBZvM5aMIcaQPa65MO9hLyXFmhMOgyfpJhLBBz6Qpa4OLLI6DeTN+0=
 =UAgA
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging

* Record-replay lockstep execution, log dumper and fixes (Alex, Pavel)
* SCSI fix to pass maximum transfer size (Daniel Barboza)
* chardev fixes and improved iothread support (Daniel Berrangé, Peter)
* checkpatch tweak (Eric)
* make help tweak (Marc-André)
* make more PCI NICs available with -net or -nic (myself)
* change default q35 NIC to e1000e (myself)
* SCSI support for NDOB bit (myself)
* membarrier system call support (myself)
* SuperIO refactoring (Philippe)
* miscellaneous cleanups and fixes (Thomas)

# gpg: Signature made Mon 12 Mar 2018 16:10:52 GMT
# gpg:                using RSA key BFFBD25F78C7AE83
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>"
# gpg:                 aka "Paolo Bonzini <pbonzini@redhat.com>"
# 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/tags/for-upstream: (69 commits)
  tcg: fix cpu_io_recompile
  replay: update documentation
  replay: save vmstate of the asynchronous events
  replay: don't process async events when warping the clock
  scripts/replay-dump.py: replay log dumper
  replay: avoid recursive call of checkpoints
  replay: check return values of fwrite
  replay: push replay_mutex_lock up the call tree
  replay: don't destroy mutex at exit
  replay: make locking visible outside replay code
  replay/replay-internal.c: track holding of replay_lock
  replay/replay.c: bump REPLAY_VERSION again
  replay: save prior value of the host clock
  replay: added replay log format description
  replay: fix save/load vm for non-empty queue
  replay: fixed replay_enable_events
  replay: fix processing async events
  cpu-exec: fix exception_index handling
  hw/i386/pc: Factor out the superio code
  hw/alpha/dp264: Use the TYPE_SMC37C669_SUPERIO
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

# Conflicts:
#	default-configs/i386-softmmu.mak
#	default-configs/x86_64-softmmu.mak
This commit is contained in:
Peter Maydell 2018-03-16 11:05:03 +00:00
commit 3788c7b6e5
117 changed files with 1952 additions and 728 deletions

View File

@ -127,7 +127,6 @@ Alpha
M: Richard Henderson <rth@twiddle.net>
S: Maintained
F: target/alpha/
F: hw/alpha/
F: tests/tcg/alpha/
F: disas/alpha.c
@ -413,6 +412,12 @@ F: include/*/*win32*
X: qga/*win32*
F: qemu.nsi
Alpha Machines
M: Richard Henderson <rth@twiddle.net>
S: Maintained
F: hw/alpha/
F: hw/isa/smc37c669-superio.c
ARM Machines
------------
Allwinner-a10
@ -700,6 +705,8 @@ Fulong 2E
M: Yongbok Kim <yongbok.kim@mips.com>
S: Odd Fixes
F: hw/mips/mips_fulong2e.c
F: hw/isa/vt82c686.c
F: include/hw/isa/vt82c686.h
Boston
M: Paul Burton <paul.burton@mips.com>
@ -776,9 +783,10 @@ F: hw/ppc/prep_systemio.c
F: hw/ppc/rs6000_mc.c
F: hw/pci-host/prep.[hc]
F: hw/isa/i82378.c
F: hw/isa/pc87312.[hc]
F: hw/isa/pc87312.c
F: hw/dma/i82374.c
F: hw/timer/m48t59-isa.c
F: include/hw/isa/pc87312.h
F: include/hw/timer/m48t59.h
F: pc-bios/ppc_rom.bin
@ -924,7 +932,7 @@ M: Michael S. Tsirkin <mst@redhat.com>
M: Paolo Bonzini <pbonzini@redhat.com>
S: Supported
F: hw/char/debugcon.c
F: hw/char/parallel.c
F: hw/char/parallel*
F: hw/char/serial*
F: hw/dma/i8257*
F: hw/i2c/pm_smbus.c
@ -932,6 +940,7 @@ F: hw/input/pckbd.c
F: hw/intc/apic*
F: hw/intc/ioapic*
F: hw/intc/i8259*
F: hw/isa/isa-superio.c
F: hw/misc/debugexit.c
F: hw/misc/pc-testdev.c
F: hw/timer/hpet*
@ -939,8 +948,11 @@ F: hw/timer/i8254*
F: hw/timer/mc146818rtc*
F: hw/watchdog/wdt_ib700.c
F: include/hw/display/vga.h
F: include/hw/char/parallel.h
F: include/hw/dma/i8257.h
F: include/hw/i2c/pm_smbus.h
F: include/hw/isa/i8257.h
F: include/hw/input/i8042.h
F: include/hw/isa/superio.h
F: include/hw/timer/hpet.h
F: include/hw/timer/i8254*
F: include/hw/timer/mc146818rtc*

View File

@ -438,21 +438,23 @@ all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules
qemu-version.h: FORCE
$(call quiet-command, \
(cd $(SRC_PATH); \
printf '#define QEMU_PKGVERSION '; \
if test -n "$(PKGVERSION)"; then \
printf '"$(PKGVERSION)"\n'; \
pkgvers="$(PKGVERSION)"; \
else \
if test -d .git; then \
printf '" ('; \
git describe --match 'v*' 2>/dev/null | tr -d '\n'; \
pkgvers=$$(git describe --match 'v*' 2>/dev/null | tr -d '\n');\
if ! git diff-index --quiet HEAD &>/dev/null; then \
printf -- '-dirty'; \
pkgvers="$${pkgvers}-dirty"; \
fi; \
printf ')"\n'; \
else \
printf '""\n'; \
fi; \
fi) > $@.tmp)
fi; \
printf "#define QEMU_PKGVERSION \"$${pkgvers}\"\n"; \
if test -n "$${pkgvers}"; then \
printf '#define QEMU_FULL_VERSION QEMU_VERSION " (" QEMU_PKGVERSION ")"\n'; \
else \
printf '#define QEMU_FULL_VERSION QEMU_VERSION\n'; \
fi; \
) > $@.tmp)
$(call quiet-command, if ! cmp -s $@ $@.tmp; then \
mv $@.tmp $@; \
else \
@ -1050,6 +1052,9 @@ include $(SRC_PATH)/tests/vm/Makefile.include
help:
@echo 'Generic targets:'
@echo ' all - Build all'
ifdef CONFIG_MODULES
@echo ' modules - Build all modules'
endif
@echo ' dir/file.o - Build specified target only'
@echo ' install - Install QEMU, documentation and tools'
@echo ' ctags/TAGS - Generate tags file for editors'

View File

@ -585,6 +585,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
else {
if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
replay_interrupt();
cpu->exception_index = -1;
*last_tb = NULL;
}
/* The target hook may have updated the 'cpu->interrupt_request';
@ -606,7 +607,9 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
if (unlikely(atomic_read(&cpu->exit_request)
|| (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) {
atomic_set(&cpu->exit_request, 0);
cpu->exception_index = EXCP_INTERRUPT;
if (cpu->exception_index == -1) {
cpu->exception_index = EXCP_INTERRUPT;
}
return true;
}

View File

@ -1728,7 +1728,8 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
CPUArchState *env = cpu->env_ptr;
#endif
TranslationBlock *tb;
uint32_t n;
uint32_t n, flags;
target_ulong pc, cs_base;
tb_lock();
tb = tb_find_pc(retaddr);
@ -1766,8 +1767,14 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
cpu_abort(cpu, "TB too big during recompile");
}
/* Adjust the execution state of the next TB. */
cpu->cflags_next_tb = curr_cflags() | CF_LAST_IO | n;
pc = tb->pc;
cs_base = tb->cs_base;
flags = tb->flags;
tb_phys_invalidate(tb, -1);
/* Execute one IO instruction without caching
instead of creating large TB. */
cpu->cflags_next_tb = curr_cflags() | CF_LAST_IO | CF_NOCACHE | 1;
if (tb->cflags & CF_NOCACHE) {
if (tb->orig_tb) {
@ -1778,6 +1785,11 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
tb_remove(tb);
}
/* Generate new TB instead of the current one. */
/* FIXME: In theory this could raise an exception. In practice
we have already translated the block once so it's probably ok. */
tb_gen_code(cpu, pc, cs_base, flags, curr_cflags() | CF_LAST_IO | n);
/* TODO: If env->pc != tb->pc (i.e. the faulting instruction was not
* the first in the TB) then we end up generating a whole new TB and
* repeating the fault, which is horribly inefficient.

View File

@ -649,7 +649,7 @@ void cpu_loop(CPUSPARCState *env)
static void usage(void)
{
printf("qemu-" TARGET_NAME " version " QEMU_VERSION QEMU_PKGVERSION
printf("qemu-" TARGET_NAME " version " QEMU_FULL_VERSION
"\n" QEMU_COPYRIGHT "\n"
"usage: qemu-" TARGET_NAME " [options] program [arguments...]\n"
"BSD CPU emulator (compiled for %s emulation)\n"

View File

@ -27,6 +27,7 @@
#include "qemu/option.h"
#include "chardev/char.h"
#include "sysemu/block-backend.h"
#include "sysemu/sysemu.h"
#include "chardev/char-mux.h"
/* MUX driver for serial I/O splitting */
@ -230,14 +231,12 @@ static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
}
}
bool muxes_realized;
void mux_chr_send_all_event(Chardev *chr, int event)
{
MuxChardev *d = MUX_CHARDEV(chr);
int i;
if (!muxes_realized) {
if (!machine_init_done) {
return;
}
@ -327,7 +326,7 @@ static void qemu_chr_open_mux(Chardev *chr,
/* only default to opened state if we've realized the initial
* set of muxes
*/
*be_opened = muxes_realized;
*be_opened = machine_init_done;
qemu_chr_fe_init(&d->chr, drv, errp);
}
@ -347,6 +346,31 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
mux->chardev = g_strdup(chardev);
}
/**
* Called after processing of default and command-line-specified
* chardevs to deliver CHR_EVENT_OPENED events to any FEs attached
* to a mux chardev. This is done here to ensure that
* output/prompts/banners are only displayed for the FE that has
* focus when initial command-line processing/machine init is
* completed.
*
* After this point, any new FE attached to any new or existing
* mux will receive CHR_EVENT_OPENED notifications for the BE
* immediately.
*/
static int open_muxes(Chardev *chr)
{
/* send OPENED to all already-attached FEs */
mux_chr_send_all_event(chr, CHR_EVENT_OPENED);
/*
* mark mux as OPENED so any new FEs will immediately receive
* OPENED event
*/
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
return 0;
}
static void char_mux_class_init(ObjectClass *oc, void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
@ -357,6 +381,7 @@ static void char_mux_class_init(ObjectClass *oc, void *data)
cc->chr_accept_input = mux_chr_accept_input;
cc->chr_add_watch = mux_chr_add_watch;
cc->chr_be_event = mux_chr_be_event;
cc->chr_machine_done = open_muxes;
}
static const TypeInfo char_mux_type_info = {

View File

@ -40,6 +40,11 @@
#define TCP_MAX_FDS 16
typedef struct {
char buf[21];
size_t buflen;
} TCPChardevTelnetInit;
typedef struct {
Chardev parent;
QIOChannel *ioc; /* Client I/O channel */
@ -60,6 +65,8 @@ typedef struct {
bool is_listen;
bool is_telnet;
bool is_tn3270;
GSource *telnet_source;
TCPChardevTelnetInit *telnet_init;
GSource *reconnect_timer;
int64_t reconnect_time;
@ -70,6 +77,7 @@ typedef struct {
OBJECT_CHECK(SocketChardev, (obj), TYPE_CHARDEV_SOCKET)
static gboolean socket_reconnect_timeout(gpointer opaque);
static void tcp_chr_telnet_init(Chardev *chr);
static void tcp_chr_reconn_timer_cancel(SocketChardev *s)
{
@ -423,8 +431,8 @@ static void tcp_chr_disconnect(Chardev *chr)
tcp_chr_free_connection(chr);
if (s->listener) {
qio_net_listener_set_client_func(s->listener, tcp_chr_accept,
chr, NULL);
qio_net_listener_set_client_func_full(s->listener, tcp_chr_accept,
chr, NULL, chr->gcontext);
}
update_disconnected_filename(s);
if (emit_close) {
@ -450,7 +458,7 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
len = s->max_size;
}
size = tcp_chr_recv(chr, (void *)buf, len);
if (size == 0 || size == -1) {
if (size == 0 || (size == -1 && errno != EAGAIN)) {
/* connection closed */
tcp_chr_disconnect(chr);
} else if (size > 0) {
@ -556,10 +564,33 @@ static void tcp_chr_connect(void *opaque)
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
}
static void tcp_chr_telnet_destroy(SocketChardev *s)
{
if (s->telnet_source) {
g_source_destroy(s->telnet_source);
g_source_unref(s->telnet_source);
s->telnet_source = NULL;
}
}
static void tcp_chr_update_read_handler(Chardev *chr)
{
SocketChardev *s = SOCKET_CHARDEV(chr);
if (s->listener) {
/*
* It's possible that chardev context is changed in
* qemu_chr_be_update_read_handlers(). Reset it for QIO net
* listener if there is.
*/
qio_net_listener_set_client_func_full(s->listener, tcp_chr_accept,
chr, NULL, chr->gcontext);
}
if (s->telnet_source) {
tcp_chr_telnet_init(CHARDEV(s));
}
if (!s->connected) {
return;
}
@ -573,32 +604,30 @@ static void tcp_chr_update_read_handler(Chardev *chr)
}
}
typedef struct {
Chardev *chr;
char buf[21];
size_t buflen;
} TCPChardevTelnetInit;
static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
GIOCondition cond G_GNUC_UNUSED,
gpointer user_data)
{
TCPChardevTelnetInit *init = user_data;
SocketChardev *s = user_data;
Chardev *chr = CHARDEV(s);
TCPChardevTelnetInit *init = s->telnet_init;
ssize_t ret;
assert(init);
ret = qio_channel_write(ioc, init->buf, init->buflen, NULL);
if (ret < 0) {
if (ret == QIO_CHANNEL_ERR_BLOCK) {
ret = 0;
} else {
tcp_chr_disconnect(init->chr);
tcp_chr_disconnect(chr);
goto end;
}
}
init->buflen -= ret;
if (init->buflen == 0) {
tcp_chr_connect(init->chr);
tcp_chr_connect(chr);
goto end;
}
@ -607,16 +636,30 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
return G_SOURCE_CONTINUE;
end:
g_free(init);
g_free(s->telnet_init);
s->telnet_init = NULL;
g_source_unref(s->telnet_source);
s->telnet_source = NULL;
return G_SOURCE_REMOVE;
}
static void tcp_chr_telnet_init(Chardev *chr)
{
SocketChardev *s = SOCKET_CHARDEV(chr);
TCPChardevTelnetInit *init = g_new0(TCPChardevTelnetInit, 1);
TCPChardevTelnetInit *init;
size_t n = 0;
/* Destroy existing task */
tcp_chr_telnet_destroy(s);
if (s->telnet_init) {
/* We are possibly during a handshake already */
goto cont;
}
s->telnet_init = g_new0(TCPChardevTelnetInit, 1);
init = s->telnet_init;
#define IACSET(x, a, b, c) \
do { \
x[n++] = a; \
@ -624,7 +667,6 @@ static void tcp_chr_telnet_init(Chardev *chr)
x[n++] = c; \
} while (0)
init->chr = chr;
if (!s->is_tn3270) {
init->buflen = 12;
/* Prep the telnet negotion to put telnet in binary,
@ -647,10 +689,11 @@ static void tcp_chr_telnet_init(Chardev *chr)
#undef IACSET
qio_channel_add_watch(
s->ioc, G_IO_OUT,
tcp_chr_telnet_init_io,
init, NULL);
cont:
s->telnet_source = qio_channel_add_watch_source(s->ioc, G_IO_OUT,
tcp_chr_telnet_init_io,
s, NULL,
chr->gcontext);
}
@ -707,7 +750,7 @@ static void tcp_chr_tls_init(Chardev *chr)
tcp_chr_tls_handshake,
chr,
NULL,
NULL);
chr->gcontext);
}
@ -743,7 +786,8 @@ static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc)
qio_channel_set_delay(s->ioc, false);
}
if (s->listener) {
qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
qio_net_listener_set_client_func_full(s->listener, NULL, NULL,
NULL, chr->gcontext);
}
if (s->tls_creds) {
@ -823,8 +867,11 @@ static void char_socket_finalize(Object *obj)
tcp_chr_free_connection(chr);
tcp_chr_reconn_timer_cancel(s);
qapi_free_SocketAddress(s->addr);
tcp_chr_telnet_destroy(s);
g_free(s->telnet_init);
if (s->listener) {
qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
qio_net_listener_set_client_func_full(s->listener, NULL, NULL,
NULL, chr->gcontext);
object_unref(OBJECT(s->listener));
}
if (s->tls_creds) {
@ -854,11 +901,22 @@ cleanup:
object_unref(OBJECT(sioc));
}
static void tcp_chr_connect_async(Chardev *chr)
{
SocketChardev *s = SOCKET_CHARDEV(chr);
QIOChannelSocket *sioc;
sioc = qio_channel_socket_new();
tcp_chr_set_client_ioc_name(chr, sioc);
qio_channel_socket_connect_async(sioc, s->addr,
qemu_chr_socket_connected,
chr, NULL, chr->gcontext);
}
static gboolean socket_reconnect_timeout(gpointer opaque)
{
Chardev *chr = CHARDEV(opaque);
SocketChardev *s = SOCKET_CHARDEV(opaque);
QIOChannelSocket *sioc;
g_source_unref(s->reconnect_timer);
s->reconnect_timer = NULL;
@ -867,11 +925,7 @@ static gboolean socket_reconnect_timeout(gpointer opaque)
return false;
}
sioc = qio_channel_socket_new();
tcp_chr_set_client_ioc_name(chr, sioc);
qio_channel_socket_connect_async(sioc, s->addr,
qemu_chr_socket_connected,
chr, NULL, NULL);
tcp_chr_connect_async(chr);
return false;
}
@ -950,13 +1004,8 @@ static void qmp_chardev_open_socket(Chardev *chr,
s->reconnect_time = reconnect;
}
if (s->reconnect_time) {
sioc = qio_channel_socket_new();
tcp_chr_set_client_ioc_name(chr, sioc);
qio_channel_socket_connect_async(sioc, s->addr,
qemu_chr_socket_connected,
chr, NULL, NULL);
} else {
/* If reconnect_time is set, will do that in chr_machine_done. */
if (!s->reconnect_time) {
if (s->is_listen) {
char *name;
s->listener = qio_net_listener_new();
@ -980,8 +1029,10 @@ static void qmp_chardev_open_socket(Chardev *chr,
return;
}
if (!s->ioc) {
qio_net_listener_set_client_func(s->listener, tcp_chr_accept,
chr, NULL);
qio_net_listener_set_client_func_full(s->listener,
tcp_chr_accept,
chr, NULL,
chr->gcontext);
}
} else if (qemu_chr_wait_connected(chr, errp) < 0) {
goto error;
@ -1103,6 +1154,17 @@ char_socket_get_connected(Object *obj, Error **errp)
return s->connected;
}
static int tcp_chr_machine_done_hook(Chardev *chr)
{
SocketChardev *s = SOCKET_CHARDEV(chr);
if (s->reconnect_time) {
tcp_chr_connect_async(chr);
}
return 0;
}
static void char_socket_class_init(ObjectClass *oc, void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
@ -1118,6 +1180,7 @@ static void char_socket_class_init(ObjectClass *oc, void *data)
cc->chr_add_client = tcp_chr_add_client;
cc->chr_add_watch = tcp_chr_add_watch;
cc->chr_update_read_handler = tcp_chr_update_read_handler;
cc->chr_machine_done = tcp_chr_machine_done_hook;
object_class_property_add(oc, "addr", "SocketAddress",
char_socket_get_addr, NULL,

View File

@ -281,40 +281,31 @@ static const TypeInfo char_type_info = {
.class_init = char_class_init,
};
/**
* Called after processing of default and command-line-specified
* chardevs to deliver CHR_EVENT_OPENED events to any FEs attached
* to a mux chardev. This is done here to ensure that
* output/prompts/banners are only displayed for the FE that has
* focus when initial command-line processing/machine init is
* completed.
*
* After this point, any new FE attached to any new or existing
* mux will receive CHR_EVENT_OPENED notifications for the BE
* immediately.
*/
static int open_muxes(Object *child, void *opaque)
static int chardev_machine_done_notify_one(Object *child, void *opaque)
{
if (CHARDEV_IS_MUX(child)) {
/* send OPENED to all already-attached FEs */
mux_chr_send_all_event(CHARDEV(child), CHR_EVENT_OPENED);
/* mark mux as OPENED so any new FEs will immediately receive
* OPENED event
*/
qemu_chr_be_event(CHARDEV(child), CHR_EVENT_OPENED);
Chardev *chr = (Chardev *)child;
ChardevClass *class = CHARDEV_GET_CLASS(chr);
if (class->chr_machine_done) {
return class->chr_machine_done(chr);
}
return 0;
}
static void muxes_realize_done(Notifier *notifier, void *unused)
static void chardev_machine_done_hook(Notifier *notifier, void *unused)
{
muxes_realized = true;
object_child_foreach(get_chardevs_root(), open_muxes, NULL);
int ret = object_child_foreach(get_chardevs_root(),
chardev_machine_done_notify_one, NULL);
if (ret) {
error_report("Failed to call chardev machine_done hooks");
exit(1);
}
}
static Notifier muxes_realize_notify = {
.notify = muxes_realize_done,
static Notifier chardev_machine_done_notify = {
.notify = chardev_machine_done_hook,
};
static bool qemu_chr_is_busy(Chardev *s)
@ -1121,7 +1112,7 @@ static void register_types(void)
* as part of realize functions like serial_isa_realizefn when -nographic
* is specified
*/
qemu_add_machine_init_done_notifier(&muxes_realize_notify);
qemu_add_machine_init_done_notifier(&chardev_machine_done_notify);
}
type_init(register_types);

44
configure vendored
View File

@ -342,7 +342,7 @@ attr=""
libattr=""
xfs=""
tcg="yes"
membarrier=""
vhost_net="no"
vhost_crypto="no"
vhost_scsi="no"
@ -1161,9 +1161,13 @@ for opt do
;;
--enable-attr) attr="yes"
;;
--disable-membarrier) membarrier="no"
;;
--enable-membarrier) membarrier="yes"
;;
--disable-blobs) blobs="no"
;;
--with-pkgversion=*) pkgversion=" ($optarg)"
--with-pkgversion=*) pkgversion="$optarg"
;;
--with-coroutine=*) coroutine="$optarg"
;;
@ -1577,6 +1581,7 @@ disabled with --disable-FEATURE, default is enabled if available:
xen-pci-passthrough
brlapi BrlAPI (Braile)
curl curl connectivity
membarrier membarrier system call (for Linux 4.14+ or Windows)
fdt fdt device tree
bluez bluez stack connectivity
kvm KVM acceleration support
@ -5138,6 +5143,37 @@ if compile_prog "" "" ; then
have_fsxattr=yes
fi
##########################################
# check for usable membarrier system call
if test "$membarrier" = "yes"; then
have_membarrier=no
if test "$mingw32" = "yes" ; then
have_membarrier=yes
elif test "$linux" = "yes" ; then
cat > $TMPC << EOF
#include <linux/membarrier.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdlib.h>
int main(void) {
syscall(__NR_membarrier, MEMBARRIER_CMD_QUERY, 0);
syscall(__NR_membarrier, MEMBARRIER_CMD_SHARED, 0);
exit(0);
}
EOF
if compile_prog "" "" ; then
have_membarrier=yes
fi
fi
if test "$have_membarrier" = "no"; then
feature_not_found "membarrier" "membarrier system call not available"
fi
else
# Do not enable it by default even for Mingw32, because it doesn't
# work on Wine.
membarrier=no
fi
##########################################
# check if rtnetlink.h exists and is useful
have_rtnetlink=no
@ -5764,6 +5800,7 @@ fi
echo "malloc trim support $malloc_trim"
echo "RDMA support $rdma"
echo "fdt support $fdt"
echo "membarrier $membarrier"
echo "preadv support $preadv"
echo "fdatasync $fdatasync"
echo "madvise $madvise"
@ -6251,6 +6288,9 @@ fi
if test "$fdt" = "yes" ; then
echo "CONFIG_FDT=y" >> $config_host_mak
fi
if test "$membarrier" = "yes" ; then
echo "CONFIG_MEMBARRIER=y" >> $config_host_mak
fi
if test "$signalfd" = "yes" ; then
echo "CONFIG_SIGNALFD=y" >> $config_host_mak
fi

24
cpus.c
View File

@ -1317,6 +1317,8 @@ static void prepare_icount_for_run(CPUState *cpu)
insns_left = MIN(0xffff, cpu->icount_budget);
cpu->icount_decr.u16.low = insns_left;
cpu->icount_extra = cpu->icount_budget - insns_left;
replay_mutex_lock();
}
}
@ -1332,6 +1334,8 @@ static void process_icount_data(CPUState *cpu)
cpu->icount_budget = 0;
replay_account_executed_instructions();
replay_mutex_unlock();
}
}
@ -1346,11 +1350,9 @@ static int tcg_cpu_exec(CPUState *cpu)
#ifdef CONFIG_PROFILER
ti = profile_getclock();
#endif
qemu_mutex_unlock_iothread();
cpu_exec_start(cpu);
ret = cpu_exec(cpu);
cpu_exec_end(cpu);
qemu_mutex_lock_iothread();
#ifdef CONFIG_PROFILER
tcg_time += profile_getclock() - ti;
#endif
@ -1417,6 +1419,9 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
cpu->exit_request = 1;
while (1) {
qemu_mutex_unlock_iothread();
replay_mutex_lock();
qemu_mutex_lock_iothread();
/* Account partial waits to QEMU_CLOCK_VIRTUAL. */
qemu_account_warp_timer();
@ -1425,6 +1430,8 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
*/
handle_icount_deadline();
replay_mutex_unlock();
if (!cpu) {
cpu = first_cpu;
}
@ -1440,11 +1447,13 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
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);
@ -1634,7 +1643,9 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
while (1) {
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);
@ -1781,12 +1792,21 @@ void pause_all_vcpus(void)
}
}
/* We need to drop the replay_lock so any vCPU threads woken up
* can finish their replay tasks
*/
replay_mutex_unlock();
while (!all_vcpus_paused()) {
qemu_cond_wait(&qemu_pause_cond, &qemu_global_mutex);
CPU_FOREACH(cpu) {
qemu_cpu_kick(cpu);
}
}
qemu_mutex_unlock_iothread();
replay_mutex_lock();
qemu_mutex_lock_iothread();
}
void cpu_resume(CPUState *cpu)

View File

@ -4,7 +4,12 @@ include pci.mak
include usb.mak
CONFIG_SERIAL=y
CONFIG_SERIAL_ISA=y
CONFIG_I82374=y
CONFIG_I8254=y
CONFIG_I8257=y
CONFIG_PARALLEL=y
CONFIG_PARALLEL_ISA=y
CONFIG_FDC=y
CONFIG_PCKBD=y
CONFIG_VGA_CIRRUS=y
CONFIG_IDE_CORE=y

View File

@ -64,3 +64,5 @@ CONFIG_ACPI_VMGENID=y
CONFIG_FW_CFG_DMA=y
CONFIG_I2C=y
CONFIG_SEV=$(CONFIG_KVM)
CONFIG_VTD=y
CONFIG_AMD_IOMMU=y

View File

@ -64,3 +64,5 @@ CONFIG_ACPI_VMGENID=y
CONFIG_FW_CFG_DMA=y
CONFIG_I2C=y
CONFIG_SEV=$(CONFIG_KVM)
CONFIG_VTD=y
CONFIG_AMD_IOMMU=y

View File

@ -122,20 +122,30 @@ In general, if the algorithm you are writing includes both writes
and reads on the same side, it is generally simpler to use sequentially
consistent primitives.
When using this model, variables are accessed with atomic_read() and
atomic_set(), and restrictions to the ordering of accesses is enforced
When using this model, variables are accessed with:
- atomic_read() and atomic_set(); these prevent the compiler from
optimizing accesses out of existence and creating unsolicited
accesses, but do not otherwise impose any ordering on loads and
stores: both the compiler and the processor are free to reorder
them.
- atomic_load_acquire(), which guarantees the LOAD to appear to
happen, with respect to the other components of the system,
before all the LOAD or STORE operations specified afterwards.
Operations coming before atomic_load_acquire() can still be
reordered after it.
- atomic_store_release(), which guarantees the STORE to appear to
happen, with respect to the other components of the system,
after all the LOAD or STORE operations specified afterwards.
Operations coming after atomic_store_release() can still be
reordered after it.
Restrictions to the ordering of accesses can also be specified
using the memory barrier macros: smp_rmb(), smp_wmb(), smp_mb(),
smp_mb_acquire(), smp_mb_release(), smp_read_barrier_depends().
atomic_read() and atomic_set() prevents the compiler from using
optimizations that might otherwise optimize accesses out of existence
on the one hand, or that might create unsolicited accesses on the other.
In general this should not have any effect, because the same compiler
barriers are already implied by memory barriers. However, it is useful
to do so, because it tells readers which variables are shared with
other threads, and which are local to the current thread or protected
by other, more mundane means.
Memory barriers control the order of references to shared memory.
They come in six kinds:
@ -232,7 +242,7 @@ make atomic_mb_set() the more expensive operation.
There are two common cases in which atomic_mb_read and atomic_mb_set
generate too many memory barriers, and thus it can be useful to manually
place barriers instead:
place barriers, or use atomic_load_acquire/atomic_store_release instead:
- when a data structure has one thread that is always a writer
and one thread that is always a reader, manual placement of
@ -243,18 +253,15 @@ place barriers instead:
thread 1 thread 1
------------------------- ------------------------
(other writes)
smp_mb_release()
atomic_mb_set(&a, x) atomic_set(&a, x)
smp_wmb()
atomic_mb_set(&b, y) atomic_set(&b, y)
atomic_mb_set(&a, x) atomic_store_release(&a, x)
atomic_mb_set(&b, y) atomic_store_release(&b, y)
=>
thread 2 thread 2
------------------------- ------------------------
y = atomic_mb_read(&b) y = atomic_read(&b)
smp_rmb()
x = atomic_mb_read(&a) x = atomic_read(&a)
smp_mb_acquire()
y = atomic_mb_read(&b) y = atomic_load_acquire(&b)
x = atomic_mb_read(&a) x = atomic_load_acquire(&a)
(other reads)
Note that the barrier between the stores in thread 1, and between
the loads in thread 2, has been optimized here to a write or a
@ -276,7 +283,6 @@ place barriers instead:
smp_mb_acquire();
Similarly, atomic_mb_set() can be transformed as follows:
smp_mb():
smp_mb_release();
for (i = 0; i < 10; i++) => for (i = 0; i < 10; i++)
@ -284,6 +290,8 @@ place barriers instead:
smp_mb();
The other thread can still use atomic_mb_read()/atomic_mb_set().
The two tricks can be combined. In this case, splitting a loop in
two lets you hoist the barriers out of the loops _and_ eliminate the
expensive smp_mb():
@ -296,8 +304,6 @@ expensive smp_mb():
atomic_set(&a[i], false);
smp_mb();
The other thread can still use atomic_mb_read()/atomic_mb_set()
Memory barrier pairing
----------------------
@ -386,10 +392,7 @@ and memory barriers, and the equivalents in QEMU:
note that smp_store_mb() is a little weaker than atomic_mb_set().
atomic_mb_read() compiles to the same instructions as Linux's
smp_load_acquire(), but this should be treated as an implementation
detail. QEMU does have atomic_load_acquire() and atomic_store_release()
macros, but for now they are only used within atomic.h. This may
change in the future.
detail.
SOURCES
=======

View File

@ -7,14 +7,10 @@ See the COPYING file in the top-level directory.
Record/replay
-------------
Record/replay functions are used for the reverse execution and deterministic
replay of qemu execution. This implementation of deterministic replay can
be used for deterministic debugging of guest code through a gdb remote
interface.
Record/replay functions are used for the deterministic replay of qemu execution.
Execution recording writes a non-deterministic events log, which can be later
used for replaying the execution anywhere and for unlimited number of times.
It also supports checkpointing for faster rewinding during reverse debugging.
It also supports checkpointing for faster rewind to the specific replay moment.
Execution replaying reads the log and replays all non-deterministic events
including external input, hardware clocks, and interrupts.
@ -28,16 +24,36 @@ Deterministic replay has the following features:
input devices.
Usage of the record/replay:
* First, record the execution, by adding the following arguments to the command line:
'-icount shift=7,rr=record,rrfile=replay.bin -net none'.
Block devices' images are not actually changed in the recording mode,
* First, record the execution with the following command line:
qemu-system-i386 \
-icount shift=7,rr=record,rrfile=replay.bin \
-drive file=disk.qcow2,if=none,id=img-direct \
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \
-device ide-hd,drive=img-blkreplay \
-netdev user,id=net1 -device rtl8139,netdev=net1 \
-object filter-replay,id=replay,netdev=net1
* After recording, you can replay it by using another command line:
qemu-system-i386 \
-icount shift=7,rr=replay,rrfile=replay.bin \
-drive file=disk.qcow2,if=none,id=img-direct \
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \
-device ide-hd,drive=img-blkreplay \
-netdev user,id=net1 -device rtl8139,netdev=net1 \
-object filter-replay,id=replay,netdev=net1
The only difference with recording is changing the rr option
from record to replay.
* Block device images are not actually changed in the recording mode,
because all of the changes are written to the temporary overlay file.
* Then you can replay it by using another command
line option: '-icount shift=7,rr=replay,rrfile=replay.bin -net none'
* '-net none' option should also be specified if network replay patches
are not applied.
This behavior is enabled by using blkreplay driver. It should be used
for every enabled block device, as described in 'Block devices' section.
* '-net none' option should be specified when network is not used,
because QEMU adds network card by default. When network is needed,
it should be configured explicitly with replay filter, as described
in 'Network devices' section.
* Interaction with audio devices and serial ports are recorded and replayed
automatically when such devices are enabled.
Papers with description of deterministic replay implementation:
Academic papers with description of deterministic replay implementation:
http://www.computer.org/csdl/proceedings/csmr/2012/4666/00/4666a553-abs.html
http://dl.acm.org/citation.cfm?id=2786805.2803179
@ -46,8 +62,33 @@ Modifications of qemu include:
* saving different asynchronous events (e.g. system shutdown) into the log
* synchronization of the bottom halves execution
* synchronization of the threads from thread pool
* recording/replaying user input (mouse and keyboard)
* recording/replaying user input (mouse, keyboard, and microphone)
* adding internal checkpoints for cpu and io synchronization
* network filter for recording and replaying the packets
* block driver for making block layer deterministic
* serial port input record and replay
Locking and thread synchronisation
----------------------------------
Previously the synchronisation of the main thread and the vCPU thread
was ensured by the holding of the BQL. However the trend has been to
reduce the time the BQL was held across the system including under TCG
system emulation. As it is important that batches of events are kept
in sequence (e.g. expiring timers and checkpoints in the main thread
while instruction checkpoints are written by the vCPU thread) we need
another lock to keep things in lock-step. This role is now handled by
the replay_mutex_lock. It used to be held only for each event being
written but now it is held for a whole execution period. This results
in a deterministic ping-pong between the two main threads.
As the BQL is now a finer grained lock than the replay_lock it is almost
certainly a bug, and a source of deadlocks, to take the
replay_mutex_lock while the BQL is held. This is enforced by an assert.
While the unlocks are usually in the reverse order, this is not
necessary; you can drop the replay_lock while holding the BQL, without
doing a more complicated unlock_iothread/replay_unlock/lock_iothread
sequence.
Non-deterministic events
------------------------
@ -55,12 +96,11 @@ Non-deterministic events
Our record/replay system is based on saving and replaying non-deterministic
events (e.g. keyboard input) and simulating deterministic ones (e.g. reading
from HDD or memory of the VM). Saving only non-deterministic events makes
log file smaller, simulation faster, and allows using reverse debugging even
for realtime applications.
log file smaller and simulation faster.
The following non-deterministic data from peripheral devices is saved into
the log: mouse and keyboard input, network packets, audio controller input,
USB packets, serial port input, and hardware clocks (they are non-deterministic
serial port input, and hardware clocks (they are non-deterministic
too, because their values are taken from the host machine). Inputs from
simulated hardware, memory of VM, software interrupts, and execution of
instructions are not saved into the log, because they are deterministic and
@ -183,7 +223,7 @@ Block devices record/replay module intercepts calls of
bdrv coroutine functions at the top of block drivers stack.
To record and replay block operations the drive must be configured
as following:
-drive file=disk.qcow,if=none,id=img-direct
-drive file=disk.qcow2,if=none,id=img-direct
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay
-device ide-hd,drive=img-blkreplay
@ -212,6 +252,12 @@ This snapshot is created at start of recording and restored at start
of replaying. It also can be loaded while replaying to roll back
the execution.
Use QEMU monitor to create additional snapshots. 'savevm <name>' command
created the snapshot and 'loadvm <name>' restores it. To prevent corruption
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.
Network devices
---------------
@ -232,3 +278,80 @@ Audio devices
Audio data is recorded and replay automatically. The command line for recording
and replaying must contain identical specifications of audio hardware, e.g.:
-soundhw ac97
Serial ports
------------
Serial ports input is recorded and replay automatically. The command lines
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.
Replay log format
-----------------
Record/replay log consits of the header and the sequence of execution
events. The header includes 4-byte replay version id and 8-byte reserved
field. Version is updated every time replay log format changes to prevent
using replay log created by another build of qemu.
The sequence of the events describes virtual machine state changes.
It includes all non-deterministic inputs of VM, synchronization marks and
instruction counts used to correctly inject inputs at replay.
Synchronization marks (checkpoints) are used for synchronizing qemu threads
that perform operations with virtual hardware. These operations may change
system's state (e.g., change some register or generate interrupt) and
therefore should execute synchronously with CPU thread.
Every event in the log includes 1-byte event id and optional arguments.
When argument is an array, it is stored as 4-byte array length
and corresponding number of bytes with data.
Here is the list of events that are written into the log:
- EVENT_INSTRUCTION. Instructions executed since last event.
Argument: 4-byte number of executed instructions.
- EVENT_INTERRUPT. Used to synchronize interrupt processing.
- EVENT_EXCEPTION. Used to synchronize exception handling.
- EVENT_ASYNC. This is a group of events. They are always processed
together with checkpoints. When such an event is generated, it is
stored in the queue and processed only when checkpoint occurs.
Every such event is followed by 1-byte checkpoint id and 1-byte
async event id from the following list:
- REPLAY_ASYNC_EVENT_BH. Bottom-half callback. This event synchronizes
callbacks that affect virtual machine state, but normally called
asyncronously.
Argument: 8-byte operation id.
- REPLAY_ASYNC_EVENT_INPUT. Input device event. Contains
parameters of keyboard and mouse input operations
(key press/release, mouse pointer movement).
Arguments: 9-16 bytes depending of input event.
- REPLAY_ASYNC_EVENT_INPUT_SYNC. Internal input synchronization event.
- REPLAY_ASYNC_EVENT_CHAR_READ. Character (e.g., serial port) device input
initiated by the sender.
Arguments: 1-byte character device id.
Array with bytes were read.
- REPLAY_ASYNC_EVENT_BLOCK. Block device operation. Used to synchronize
operations with disk and flash drives with CPU.
Argument: 8-byte operation id.
- REPLAY_ASYNC_EVENT_NET. Incoming network packet.
Arguments: 1-byte network adapter id.
4-byte packet flags.
Array with packet bytes.
- EVENT_SHUTDOWN. Occurs when user sends shutdown event to qemu,
e.g., by closing the window.
- EVENT_CHAR_WRITE. Used to synchronize character output operations.
Arguments: 4-byte output function return value.
4-byte offset in the output array.
- EVENT_CHAR_READ_ALL. Used to synchronize character input operations,
initiated by qemu.
Argument: Array with bytes that were read.
- EVENT_CHAR_READ_ALL_ERROR. Unsuccessful character input operation,
initiated by qemu.
Argument: 4-byte error code.
- EVENT_CLOCK + clock_id. Group of events for host clock read operations.
Argument: 8-byte clock value.
- EVENT_CHECKPOINT + checkpoint_id. Checkpoint for synchronization of
CPU, internal threads, and asynchronous input events. May be followed
by one or more EVENT_ASYNC events.
- EVENT_END. Last event in the log.

View File

@ -19,7 +19,8 @@
#include "hw/timer/mc146818rtc.h"
#include "hw/ide.h"
#include "hw/timer/i8254.h"
#include "hw/char/serial.h"
#include "hw/isa/superio.h"
#include "hw/dma/i8257.h"
#include "qemu/cutils.h"
#define MAX_IDE_BUS 2
@ -81,19 +82,21 @@ static void clipper_init(MachineState *machine)
mc146818_rtc_init(isa_bus, 1900, rtc_irq);
i8254_pit_init(isa_bus, 0x40, 0, NULL);
isa_create_simple(isa_bus, "i8042");
/* VGA setup. Don't bother loading the bios. */
pci_vga_init(pci_bus);
/* Serial code setup. */
serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS);
/* Network setup. e1000 is good enough, failing Tulip support. */
for (i = 0; i < nb_nics; i++) {
pci_nic_init_nofail(&nd_table[i], pci_bus, "e1000", NULL);
}
/* 2 82C37 (dma) */
isa_create_simple(isa_bus, "i82374");
/* Super I/O */
isa_create_simple(isa_bus, TYPE_SMC37C669_SUPERIO);
/* IDE disk setup. */
{
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];

View File

@ -27,7 +27,6 @@
#include "sysemu/kvm.h"
#include "sysemu/sysemu.h"
#include "hw/boards.h"
#include "sysemu/block-backend.h"
#include "exec/address-spaces.h"
#include "qemu/error-report.h"
#include "hw/char/pl011.h"

View File

@ -29,7 +29,6 @@
#include "exec/address-spaces.h"
#include "hw/char/serial.h"
#include "hw/boards.h"
#include "sysemu/block-backend.h"
#include "qemu/cutils.h"
#include "hw/arm/msf2-soc.h"
#include "hw/misc/unimp.h"

View File

@ -20,7 +20,6 @@
#include "sysemu/sysemu.h"
#include "hw/boards.h"
#include "hw/i2c/i2c.h"
#include "sysemu/block-backend.h"
#include "exec/address-spaces.h"
#include "qemu/error-report.h"
#include "hw/char/pl011.h"

View File

@ -22,7 +22,6 @@
#include "hw/boards.h"
#include "hw/i2c/i2c.h"
#include "hw/ssi/ssi.h"
#include "sysemu/block-backend.h"
#include "hw/sysbus.h"
#include "exec/address-spaces.h"
#include "sysemu/sysemu.h"

View File

@ -1,6 +1,7 @@
common-obj-$(CONFIG_IPACK) += ipoctal232.o
common-obj-$(CONFIG_ESCC) += escc.o
common-obj-$(CONFIG_PARALLEL) += parallel.o
common-obj-$(CONFIG_PARALLEL) += parallel-isa.o
common-obj-$(CONFIG_PL011) += pl011.o
common-obj-$(CONFIG_SERIAL) += serial.o
common-obj-$(CONFIG_SERIAL_ISA) += serial-isa.o

36
hw/char/parallel-isa.c Normal file
View File

@ -0,0 +1,36 @@
/*
* QEMU Parallel PORT (ISA bus helpers)
*
* Copyright (c) 2003 Fabrice Bellard
*
* SPDX-License-Identifier: MIT
*/
#include "qemu/osdep.h"
#include "sysemu/sysemu.h"
#include "hw/isa/isa.h"
#include "hw/char/parallel.h"
static void parallel_init(ISABus *bus, int index, Chardev *chr)
{
DeviceState *dev;
ISADevice *isadev;
isadev = isa_create(bus, "isa-parallel");
dev = DEVICE(isadev);
qdev_prop_set_uint32(dev, "index", index);
qdev_prop_set_chr(dev, "chardev", chr);
qdev_init_nofail(dev);
}
void parallel_hds_isa_init(ISABus *bus, int n)
{
int i;
assert(n <= MAX_PARALLEL_PORTS);
for (i = 0; i < n; i++) {
if (parallel_hds[i]) {
parallel_init(bus, i, parallel_hds[i]);
}
}
}

View File

@ -28,7 +28,7 @@
#include "chardev/char-parallel.h"
#include "chardev/char-fe.h"
#include "hw/isa/isa.h"
#include "hw/i386/pc.h"
#include "hw/char/parallel.h"
#include "sysemu/sysemu.h"
//#define DEBUG_PARALLEL

View File

@ -24,6 +24,7 @@
#include "qemu/osdep.h"
#include "hw/isa/isa.h"
#include "hw/dma/i8257.h"
#define TYPE_I82374 "i82374"
#define I82374(obj) OBJECT_CHECK(I82374State, (obj), TYPE_I82374)
@ -123,7 +124,7 @@ static void i82374_realize(DeviceState *dev, Error **errp)
portio_list_add(&s->port_list, isa_address_space_io(&s->parent_obj),
s->iobase);
DMA_init(isa_bus_from_device(ISA_DEVICE(dev)), 1);
i8257_dma_init(isa_bus_from_device(ISA_DEVICE(dev)), true);
memset(s->commands, 0, sizeof(s->commands));
}

View File

@ -24,7 +24,7 @@
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/isa/isa.h"
#include "hw/isa/i8257.h"
#include "hw/dma/i8257.h"
#include "qemu/main-loop.h"
#include "trace.h"
@ -622,7 +622,7 @@ static void i8257_register_types(void)
type_init(i8257_register_types)
void DMA_init(ISABus *bus, int high_page_enable)
void i8257_dma_init(ISABus *bus, bool high_page_enable)
{
ISADevice *isa1, *isa2;
DeviceState *d;

View File

@ -2,8 +2,8 @@ obj-$(CONFIG_KVM) += kvm/
obj-y += multiboot.o
obj-y += pc.o pc_piix.o pc_q35.o
obj-y += pc_sysfw.o
obj-y += x86-iommu.o intel_iommu.o
obj-y += amd_iommu.o
obj-$(CONFIG_VTD) += x86-iommu.o intel_iommu.o
obj-$(CONFIG_AMD_IOMMU) += x86-iommu.o amd_iommu.o
obj-$(CONFIG_XEN) += ../xenpv/ xen/
obj-$(CONFIG_VMPORT) += vmport.o
obj-$(CONFIG_VMMOUSE) += vmmouse.o

View File

@ -26,6 +26,7 @@
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/char/serial.h"
#include "hw/char/parallel.h"
#include "hw/i386/apic.h"
#include "hw/i386/topology.h"
#include "sysemu/cpus.h"
@ -40,7 +41,9 @@
#include "elf.h"
#include "multiboot.h"
#include "hw/timer/mc146818rtc.h"
#include "hw/dma/i8257.h"
#include "hw/timer/i8254.h"
#include "hw/input/i8042.h"
#include "hw/audio/pcspk.h"
#include "hw/pci/msi.h"
#include "hw/sysbus.h"
@ -50,8 +53,6 @@
#include "sysemu/qtest.h"
#include "kvm_i386.h"
#include "hw/xen/xen.h"
#include "sysemu/block-backend.h"
#include "hw/block/block.h"
#include "ui/qemu-spice.h"
#include "exec/memory.h"
#include "exec/address-spaces.h"
@ -1516,6 +1517,44 @@ static const MemoryRegionOps ioportF0_io_ops = {
},
};
static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, bool no_vmport)
{
int i;
DriveInfo *fd[MAX_FD];
qemu_irq *a20_line;
ISADevice *i8042, *port92, *vmmouse;
serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS);
parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS);
for (i = 0; i < MAX_FD; i++) {
fd[i] = drive_get(IF_FLOPPY, 0, i);
create_fdctrl |= !!fd[i];
}
if (create_fdctrl) {
fdctrl_init_isa(isa_bus, fd);
}
i8042 = isa_create_simple(isa_bus, "i8042");
if (!no_vmport) {
vmport_init(isa_bus);
vmmouse = isa_try_create(isa_bus, "vmmouse");
} else {
vmmouse = NULL;
}
if (vmmouse) {
DeviceState *dev = DEVICE(vmmouse);
qdev_prop_set_ptr(dev, "ps2_mouse", i8042);
qdev_init_nofail(dev);
}
port92 = isa_create_simple(isa_bus, "port92");
a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2);
i8042_setup_a20_line(i8042, a20_line[0]);
port92_init(port92, a20_line[1]);
g_free(a20_line);
}
void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
ISADevice **rtc_state,
bool create_fdctrl,
@ -1524,13 +1563,11 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
uint32_t hpet_irqs)
{
int i;
DriveInfo *fd[MAX_FD];
DeviceState *hpet = NULL;
int pit_isa_irq = 0;
qemu_irq pit_alt_irq = NULL;
qemu_irq rtc_irq = NULL;
qemu_irq *a20_line;
ISADevice *i8042, *port92, *vmmouse, *pit = NULL;
ISADevice *pit = NULL;
MemoryRegion *ioport80_io = g_new(MemoryRegion, 1);
MemoryRegion *ioportF0_io = g_new(MemoryRegion, 1);
@ -1587,50 +1624,25 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
pcspk_init(isa_bus, pit);
}
serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS);
parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS);
i8257_dma_init(isa_bus, 0);
a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2);
i8042 = isa_create_simple(isa_bus, "i8042");
i8042_setup_a20_line(i8042, a20_line[0]);
if (!no_vmport) {
vmport_init(isa_bus);
vmmouse = isa_try_create(isa_bus, "vmmouse");
} else {
vmmouse = NULL;
}
if (vmmouse) {
DeviceState *dev = DEVICE(vmmouse);
qdev_prop_set_ptr(dev, "ps2_mouse", i8042);
qdev_init_nofail(dev);
}
port92 = isa_create_simple(isa_bus, "port92");
port92_init(port92, a20_line[1]);
g_free(a20_line);
DMA_init(isa_bus, 0);
for(i = 0; i < MAX_FD; i++) {
fd[i] = drive_get(IF_FLOPPY, 0, i);
create_fdctrl |= !!fd[i];
}
if (create_fdctrl) {
fdctrl_init_isa(isa_bus, fd);
}
/* Super I/O */
pc_superio_init(isa_bus, create_fdctrl, no_vmport);
}
void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus)
{
int i;
rom_set_order_override(FW_CFG_ORDER_OVERRIDE_NIC);
for (i = 0; i < nb_nics; i++) {
NICInfo *nd = &nd_table[i];
const char *model = nd->model ? nd->model : pcmc->default_nic_model;
if (!pci_bus || (nd->model && strcmp(nd->model, "ne2k_isa") == 0)) {
if (g_str_equal(model, "ne2k_isa")) {
pc_init_ne2k_isa(isa_bus, nd);
} else {
pci_nic_init_nofail(nd, pci_bus, "e1000", NULL);
pci_nic_init_nofail(nd, pci_bus, model, NULL);
}
}
rom_reset_order_override();

View File

@ -40,7 +40,6 @@
#include "sysemu/sysemu.h"
#include "hw/sysbus.h"
#include "sysemu/arch_init.h"
#include "sysemu/block-backend.h"
#include "hw/i2c/smbus.h"
#include "hw/xen/xen.h"
#include "exec/memory.h"
@ -240,7 +239,7 @@ static void pc_init1(MachineState *machine,
pc_basic_device_init(isa_bus, pcms->gsi, &rtc_state, true,
(pcms->vmport != ON_OFF_AUTO_ON), pcms->pit, 0x4);
pc_nic_init(isa_bus, pci_bus);
pc_nic_init(pcmc, isa_bus, pci_bus);
ide_drive_get(hd, ARRAY_SIZE(hd));
if (pcmc->pci_enabled) {
@ -417,6 +416,9 @@ static void pc_xen_hvm_init(MachineState *machine)
static void pc_i440fx_machine_options(MachineClass *m)
{
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pcmc->default_nic_model = "e1000";
m->family = "pc_piix";
m->desc = "Standard PC (i440FX + PIIX, 1996)";
m->default_machine_opts = "firmware=bios-256k.bin";
@ -1114,6 +1116,7 @@ static void isapc_machine_options(MachineClass *m)
pcmc->gigabyte_align = false;
pcmc->smbios_legacy_mode = true;
pcmc->has_reserved_memory = false;
pcmc->default_nic_model = "ne2k_isa";
m->default_cpu_type = X86_CPU_TYPE_NAME("486");
}

View File

@ -272,7 +272,7 @@ static void pc_q35_init(MachineState *machine)
/* the rest devices to which pci devfn is automatically assigned */
pc_vga_init(isa_bus, host_bus);
pc_nic_init(isa_bus, host_bus);
pc_nic_init(pcmc, isa_bus, host_bus);
if (pcms->acpi_nvdimm_state.is_enabled) {
nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io,
@ -294,6 +294,9 @@ static void pc_q35_init(MachineState *machine)
static void pc_q35_machine_options(MachineClass *m)
{
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pcmc->default_nic_model = "e1000e";
m->family = "pc_q35";
m->desc = "Standard PC (Q35 + ICH9, 2009)";
m->units_per_default_bus = 1;
@ -316,7 +319,10 @@ DEFINE_Q35_MACHINE(v2_12, "pc-q35-2.12", NULL,
static void pc_q35_2_11_machine_options(MachineClass *m)
{
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_q35_2_12_machine_options(m);
pcmc->default_nic_model = "e1000";
m->alias = NULL;
SET_MACHINE_COMPAT(m, PC_COMPAT_2_11);
}

View File

@ -25,6 +25,7 @@
#include "hw/hw.h"
#include "ui/console.h"
#include "hw/i386/pc.h"
#include "hw/input/i8042.h"
#include "hw/qdev.h"
/* debug only vmmouse */

View File

@ -25,6 +25,7 @@
#include "hw/hw.h"
#include "hw/isa/isa.h"
#include "hw/i386/pc.h"
#include "hw/input/i8042.h"
#include "sysemu/hw_accel.h"
#include "hw/qdev.h"
#include "qemu/log.h"

View File

@ -18,7 +18,6 @@
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "qemu/error-report.h"
#include "sysemu/block-backend.h"
#include "sysemu/dma.h"
#include "hw/ide/internal.h"
#include "hw/ide/ahci_internal.h"

View File

@ -26,7 +26,6 @@
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "hw/isa/isa.h"
#include "sysemu/block-backend.h"
#include "sysemu/sysemu.h"
#include "sysemu/dma.h"

View File

@ -65,7 +65,6 @@
#include "hw/pci/msi.h"
#include "hw/pci/pci.h"
#include "hw/isa/isa.h"
#include "sysemu/block-backend.h"
#include "sysemu/dma.h"
#include "hw/ide/pci.h"
#include "hw/ide/ahci_internal.h"

View File

@ -25,7 +25,6 @@
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/isa/isa.h"
#include "sysemu/block-backend.h"
#include "sysemu/dma.h"
#include "hw/ide/internal.h"

View File

@ -25,7 +25,6 @@
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pcmcia.h"
#include "sysemu/block-backend.h"
#include "sysemu/dma.h"
#include "hw/ide/internal.h"

View File

@ -25,7 +25,6 @@
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "sysemu/block-backend.h"
#include "sysemu/dma.h"
#include "hw/ide/internal.h"

View File

@ -26,6 +26,7 @@
#include "hw/isa/isa.h"
#include "hw/i386/pc.h"
#include "hw/input/ps2.h"
#include "hw/input/i8042.h"
#include "sysemu/sysemu.h"
/* debug PC keyboard */
@ -480,7 +481,6 @@ void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
qemu_register_reset(kbd_reset, s);
}
#define TYPE_I8042 "i8042"
#define I8042(obj) OBJECT_CHECK(ISAKBDState, (obj), TYPE_I8042)
typedef struct ISAKBDState {

View File

@ -1,4 +1,5 @@
common-obj-$(CONFIG_ISA_BUS) += isa-bus.o
common-obj-$(CONFIG_ISA_BUS) += isa-superio.o smc37c669-superio.o
common-obj-$(CONFIG_APM) += apm.o
common-obj-$(CONFIG_I82378) += i82378.o
common-obj-$(CONFIG_PC87312) += pc87312.o

View File

@ -24,7 +24,6 @@
#include "hw/sysbus.h"
#include "sysemu/sysemu.h"
#include "hw/isa/isa.h"
#include "hw/i386/pc.h"
static ISABus *isabus;
@ -288,28 +287,3 @@ MemoryRegion *isa_address_space_io(ISADevice *dev)
}
type_init(isabus_register_types)
static void parallel_init(ISABus *bus, int index, Chardev *chr)
{
DeviceState *dev;
ISADevice *isadev;
isadev = isa_create(bus, "isa-parallel");
dev = DEVICE(isadev);
qdev_prop_set_uint32(dev, "index", index);
qdev_prop_set_chr(dev, "chardev", chr);
qdev_init_nofail(dev);
}
void parallel_hds_isa_init(ISABus *bus, int n)
{
int i;
assert(n <= MAX_PARALLEL_PORTS);
for (i = 0; i < n; i++) {
if (parallel_hds[i]) {
parallel_init(bus, i, parallel_hds[i]);
}
}
}

214
hw/isa/isa-superio.c Normal file
View File

@ -0,0 +1,214 @@
/*
* Generic ISA Super I/O
*
* Copyright (c) 2010-2012 Herve Poussineau
* Copyright (c) 2011-2012 Andreas Färber
* Copyright (c) 2018 Philippe Mathieu-Daudé
*
* This code is licensed under the GNU GPLv2 and later.
* See the COPYING file in the top-level directory.
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "sysemu/sysemu.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "chardev/char.h"
#include "hw/isa/superio.h"
#include "hw/input/i8042.h"
#include "hw/char/serial.h"
#include "trace.h"
static void isa_superio_realize(DeviceState *dev, Error **errp)
{
ISASuperIODevice *sio = ISA_SUPERIO(dev);
ISASuperIOClass *k = ISA_SUPERIO_GET_CLASS(sio);
ISABus *bus = isa_bus_from_device(ISA_DEVICE(dev));
ISADevice *isa;
DeviceState *d;
Chardev *chr;
DriveInfo *drive;
char *name;
int i;
/* Parallel port */
for (i = 0; i < k->parallel.count; i++) {
if (i >= ARRAY_SIZE(sio->parallel)) {
warn_report("superio: ignoring %td parallel controllers",
k->parallel.count - ARRAY_SIZE(sio->parallel));
break;
}
if (!k->parallel.is_enabled || k->parallel.is_enabled(sio, i)) {
/* FIXME use a qdev chardev prop instead of parallel_hds[] */
chr = parallel_hds[i];
if (chr == NULL || chr->be) {
name = g_strdup_printf("discarding-parallel%d", i);
chr = qemu_chr_new(name, "null");
} else {
name = g_strdup_printf("parallel%d", i);
}
isa = isa_create(bus, "isa-parallel");
d = DEVICE(isa);
qdev_prop_set_uint32(d, "index", i);
if (k->parallel.get_iobase) {
qdev_prop_set_uint32(d, "iobase",
k->parallel.get_iobase(sio, i));
}
if (k->parallel.get_irq) {
qdev_prop_set_uint32(d, "irq", k->parallel.get_irq(sio, i));
}
qdev_prop_set_chr(d, "chardev", chr);
qdev_init_nofail(d);
sio->parallel[i] = isa;
trace_superio_create_parallel(i,
k->parallel.get_iobase ?
k->parallel.get_iobase(sio, i) : -1,
k->parallel.get_irq ?
k->parallel.get_irq(sio, i) : -1);
object_property_add_child(OBJECT(dev), name,
OBJECT(sio->parallel[i]), NULL);
g_free(name);
}
}
/* Serial */
for (i = 0; i < k->serial.count; i++) {
if (i >= ARRAY_SIZE(sio->serial)) {
warn_report("superio: ignoring %td serial controllers",
k->serial.count - ARRAY_SIZE(sio->serial));
break;
}
if (!k->serial.is_enabled || k->serial.is_enabled(sio, i)) {
/* FIXME use a qdev chardev prop instead of serial_hds[] */
chr = serial_hds[i];
if (chr == NULL || chr->be) {
name = g_strdup_printf("discarding-serial%d", i);
chr = qemu_chr_new(name, "null");
} else {
name = g_strdup_printf("serial%d", i);
}
isa = isa_create(bus, TYPE_ISA_SERIAL);
d = DEVICE(isa);
qdev_prop_set_uint32(d, "index", i);
if (k->serial.get_iobase) {
qdev_prop_set_uint32(d, "iobase",
k->serial.get_iobase(sio, i));
}
if (k->serial.get_irq) {
qdev_prop_set_uint32(d, "irq", k->serial.get_irq(sio, i));
}
qdev_prop_set_chr(d, "chardev", chr);
qdev_init_nofail(d);
sio->serial[i] = isa;
trace_superio_create_serial(i,
k->serial.get_iobase ?
k->serial.get_iobase(sio, i) : -1,
k->serial.get_irq ?
k->serial.get_irq(sio, i) : -1);
object_property_add_child(OBJECT(dev), name,
OBJECT(sio->serial[0]), NULL);
g_free(name);
}
}
/* Floppy disc */
if (!k->floppy.is_enabled || k->floppy.is_enabled(sio, 0)) {
isa = isa_create(bus, "isa-fdc");
d = DEVICE(isa);
if (k->floppy.get_iobase) {
qdev_prop_set_uint32(d, "iobase", k->floppy.get_iobase(sio, 0));
}
if (k->floppy.get_irq) {
qdev_prop_set_uint32(d, "irq", k->floppy.get_irq(sio, 0));
}
/* FIXME use a qdev drive property instead of drive_get() */
drive = drive_get(IF_FLOPPY, 0, 0);
if (drive != NULL) {
qdev_prop_set_drive(d, "driveA", blk_by_legacy_dinfo(drive),
&error_fatal);
}
/* FIXME use a qdev drive property instead of drive_get() */
drive = drive_get(IF_FLOPPY, 0, 1);
if (drive != NULL) {
qdev_prop_set_drive(d, "driveB", blk_by_legacy_dinfo(drive),
&error_fatal);
}
qdev_init_nofail(d);
sio->floppy = isa;
trace_superio_create_floppy(0,
k->floppy.get_iobase ?
k->floppy.get_iobase(sio, 0) : -1,
k->floppy.get_irq ?
k->floppy.get_irq(sio, 0) : -1);
}
/* Keyboard, mouse */
sio->kbc = isa_create_simple(bus, TYPE_I8042);
/* IDE */
if (k->ide.count && (!k->ide.is_enabled || k->ide.is_enabled(sio, 0))) {
isa = isa_create(bus, "isa-ide");
d = DEVICE(isa);
if (k->ide.get_iobase) {
qdev_prop_set_uint32(d, "iobase", k->ide.get_iobase(sio, 0));
}
if (k->ide.get_iobase) {
qdev_prop_set_uint32(d, "iobase2", k->ide.get_iobase(sio, 1));
}
if (k->ide.get_irq) {
qdev_prop_set_uint32(d, "irq", k->ide.get_irq(sio, 0));
}
qdev_init_nofail(d);
sio->ide = isa;
trace_superio_create_ide(0,
k->ide.get_iobase ?
k->ide.get_iobase(sio, 0) : -1,
k->ide.get_irq ?
k->ide.get_irq(sio, 0) : -1);
}
}
static void isa_superio_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = isa_superio_realize;
/* Reason: Uses parallel_hds[0] in realize(), so it can't be used twice */
dc->user_creatable = false;
}
static const TypeInfo isa_superio_type_info = {
.name = TYPE_ISA_SUPERIO,
.parent = TYPE_ISA_DEVICE,
.abstract = true,
.class_size = sizeof(ISASuperIOClass),
.class_init = isa_superio_class_init,
};
/* SMS FDC37M817 Super I/O */
static void fdc37m81x_class_init(ObjectClass *klass, void *data)
{
ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass);
sc->serial.count = 2; /* NS16C550A */
sc->parallel.count = 1;
sc->floppy.count = 1; /* SMSC 82077AA Compatible */
sc->ide.count = 0;
}
static const TypeInfo fdc37m81x_type_info = {
.name = TYPE_FDC37M81X_SUPERIO,
.parent = TYPE_ISA_SUPERIO,
.instance_size = sizeof(ISASuperIODevice),
.class_init = fdc37m81x_class_init,
};
static void isa_superio_register_types(void)
{
type_register_static(&isa_superio_type_info);
type_register_static(&fdc37m81x_type_info);
}
type_init(isa_superio_register_types)

View File

@ -27,10 +27,6 @@
#include "hw/isa/pc87312.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "sysemu/sysemu.h"
#include "chardev/char.h"
#include "trace.h"
@ -64,22 +60,25 @@
/* Parallel port */
static inline bool is_parallel_enabled(PC87312State *s)
static bool is_parallel_enabled(ISASuperIODevice *sio, uint8_t index)
{
return s->regs[REG_FER] & FER_PARALLEL_EN;
PC87312State *s = PC87312(sio);
return index ? false : s->regs[REG_FER] & FER_PARALLEL_EN;
}
static const uint32_t parallel_base[] = { 0x378, 0x3bc, 0x278, 0x00 };
static const uint16_t parallel_base[] = { 0x378, 0x3bc, 0x278, 0x00 };
static inline uint32_t get_parallel_iobase(PC87312State *s)
static uint16_t get_parallel_iobase(ISASuperIODevice *sio, uint8_t index)
{
PC87312State *s = PC87312(sio);
return parallel_base[s->regs[REG_FAR] & FAR_PARALLEL_ADDR];
}
static const uint32_t parallel_irq[] = { 5, 7, 5, 0 };
static const unsigned int parallel_irq[] = { 5, 7, 5, 0 };
static inline uint32_t get_parallel_irq(PC87312State *s)
static unsigned int get_parallel_irq(ISASuperIODevice *sio, uint8_t index)
{
PC87312State *s = PC87312(sio);
int idx;
idx = (s->regs[REG_FAR] & FAR_PARALLEL_ADDR);
if (idx == 0) {
@ -92,13 +91,14 @@ static inline uint32_t get_parallel_irq(PC87312State *s)
/* UARTs */
static const uint32_t uart_base[2][4] = {
static const uint16_t uart_base[2][4] = {
{ 0x3e8, 0x338, 0x2e8, 0x220 },
{ 0x2e8, 0x238, 0x2e0, 0x228 }
};
static inline uint32_t get_uart_iobase(PC87312State *s, int i)
static uint16_t get_uart_iobase(ISASuperIODevice *sio, uint8_t i)
{
PC87312State *s = PC87312(sio);
int idx;
idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
if (idx == 0) {
@ -110,44 +110,68 @@ static inline uint32_t get_uart_iobase(PC87312State *s, int i)
}
}
static inline uint32_t get_uart_irq(PC87312State *s, int i)
static unsigned int get_uart_irq(ISASuperIODevice *sio, uint8_t i)
{
PC87312State *s = PC87312(sio);
int idx;
idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
return (idx & 1) ? 3 : 4;
}
static inline bool is_uart_enabled(PC87312State *s, int i)
static bool is_uart_enabled(ISASuperIODevice *sio, uint8_t i)
{
PC87312State *s = PC87312(sio);
return s->regs[REG_FER] & (FER_UART1_EN << i);
}
/* Floppy controller */
static inline bool is_fdc_enabled(PC87312State *s)
static bool is_fdc_enabled(ISASuperIODevice *sio, uint8_t index)
{
PC87312State *s = PC87312(sio);
assert(!index);
return s->regs[REG_FER] & FER_FDC_EN;
}
static inline uint32_t get_fdc_iobase(PC87312State *s)
static uint16_t get_fdc_iobase(ISASuperIODevice *sio, uint8_t index)
{
PC87312State *s = PC87312(sio);
assert(!index);
return (s->regs[REG_FER] & FER_FDC_ADDR) ? 0x370 : 0x3f0;
}
static unsigned int get_fdc_irq(ISASuperIODevice *sio, uint8_t index)
{
assert(!index);
return 6;
}
/* IDE controller */
static inline bool is_ide_enabled(PC87312State *s)
static bool is_ide_enabled(ISASuperIODevice *sio, uint8_t index)
{
PC87312State *s = PC87312(sio);
return s->regs[REG_FER] & FER_IDE_EN;
}
static inline uint32_t get_ide_iobase(PC87312State *s)
static uint16_t get_ide_iobase(ISASuperIODevice *sio, uint8_t index)
{
PC87312State *s = PC87312(sio);
if (index == 1) {
return get_ide_iobase(sio, 0) + 0x206;
}
return (s->regs[REG_FER] & FER_IDE_ADDR) ? 0x170 : 0x1f0;
}
static unsigned int get_ide_irq(ISASuperIODevice *sio, uint8_t index)
{
assert(index == 0);
return 14;
}
static void reconfigure_devices(PC87312State *s)
{
@ -265,90 +289,18 @@ static void pc87312_reset(DeviceState *d)
static void pc87312_realize(DeviceState *dev, Error **errp)
{
PC87312State *s;
DeviceState *d;
ISADevice *isa;
ISABus *bus;
Chardev *chr;
DriveInfo *drive;
char name[5];
int i;
Error *local_err = NULL;
s = PC87312(dev);
isa = ISA_DEVICE(dev);
bus = isa_bus_from_device(isa);
isa_register_ioport(isa, &s->io, s->iobase);
pc87312_hard_reset(s);
if (is_parallel_enabled(s)) {
/* FIXME use a qdev chardev prop instead of parallel_hds[] */
chr = parallel_hds[0];
if (chr == NULL) {
chr = qemu_chr_new("par0", "null");
}
isa = isa_create(bus, "isa-parallel");
d = DEVICE(isa);
qdev_prop_set_uint32(d, "index", 0);
qdev_prop_set_uint32(d, "iobase", get_parallel_iobase(s));
qdev_prop_set_uint32(d, "irq", get_parallel_irq(s));
qdev_prop_set_chr(d, "chardev", chr);
qdev_init_nofail(d);
s->parallel.dev = isa;
trace_pc87312_info_parallel(get_parallel_iobase(s),
get_parallel_irq(s));
}
for (i = 0; i < 2; i++) {
if (is_uart_enabled(s, i)) {
/* FIXME use a qdev chardev prop instead of serial_hds[] */
chr = serial_hds[i];
if (chr == NULL) {
snprintf(name, sizeof(name), "ser%d", i);
chr = qemu_chr_new(name, "null");
}
isa = isa_create(bus, "isa-serial");
d = DEVICE(isa);
qdev_prop_set_uint32(d, "index", i);
qdev_prop_set_uint32(d, "iobase", get_uart_iobase(s, i));
qdev_prop_set_uint32(d, "irq", get_uart_irq(s, i));
qdev_prop_set_chr(d, "chardev", chr);
qdev_init_nofail(d);
s->uart[i].dev = isa;
trace_pc87312_info_serial(i, get_uart_iobase(s, i),
get_uart_irq(s, i));
}
}
if (is_fdc_enabled(s)) {
isa = isa_create(bus, "isa-fdc");
d = DEVICE(isa);
qdev_prop_set_uint32(d, "iobase", get_fdc_iobase(s));
qdev_prop_set_uint32(d, "irq", 6);
/* FIXME use a qdev drive property instead of drive_get() */
drive = drive_get(IF_FLOPPY, 0, 0);
if (drive != NULL) {
qdev_prop_set_drive(d, "driveA", blk_by_legacy_dinfo(drive),
&error_fatal);
}
/* FIXME use a qdev drive property instead of drive_get() */
drive = drive_get(IF_FLOPPY, 0, 1);
if (drive != NULL) {
qdev_prop_set_drive(d, "driveB", blk_by_legacy_dinfo(drive),
&error_fatal);
}
qdev_init_nofail(d);
s->fdc.dev = isa;
trace_pc87312_info_floppy(get_fdc_iobase(s));
}
if (is_ide_enabled(s)) {
isa = isa_create(bus, "isa-ide");
d = DEVICE(isa);
qdev_prop_set_uint32(d, "iobase", get_ide_iobase(s));
qdev_prop_set_uint32(d, "iobase2", get_ide_iobase(s) + 0x206);
qdev_prop_set_uint32(d, "irq", 14);
qdev_init_nofail(d);
s->ide.dev = isa;
trace_pc87312_info_ide(get_ide_iobase(s));
ISA_SUPERIO_GET_CLASS(dev)->parent_realize(dev, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
}
@ -373,7 +325,7 @@ static const VMStateDescription vmstate_pc87312 = {
};
static Property pc87312_properties[] = {
DEFINE_PROP_UINT32("iobase", PC87312State, iobase, 0x398),
DEFINE_PROP_UINT16("iobase", PC87312State, iobase, 0x398),
DEFINE_PROP_UINT8("config", PC87312State, config, 1),
DEFINE_PROP_END_OF_LIST()
};
@ -381,21 +333,47 @@ static Property pc87312_properties[] = {
static void pc87312_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass);
sc->parent_realize = dc->realize;
dc->realize = pc87312_realize;
dc->reset = pc87312_reset;
dc->vmsd = &vmstate_pc87312;
dc->props = pc87312_properties;
/* Reason: Uses parallel_hds[0] in realize(), so it can't be used twice */
dc->user_creatable = false;
sc->parallel = (ISASuperIOFuncs){
.count = 1,
.is_enabled = is_parallel_enabled,
.get_iobase = get_parallel_iobase,
.get_irq = get_parallel_irq,
};
sc->serial = (ISASuperIOFuncs){
.count = 2,
.is_enabled = is_uart_enabled,
.get_iobase = get_uart_iobase,
.get_irq = get_uart_irq,
};
sc->floppy = (ISASuperIOFuncs){
.count = 1,
.is_enabled = is_fdc_enabled,
.get_iobase = get_fdc_iobase,
.get_irq = get_fdc_irq,
};
sc->ide = (ISASuperIOFuncs){
.count = 1,
.is_enabled = is_ide_enabled,
.get_iobase = get_ide_iobase,
.get_irq = get_ide_irq,
};
}
static const TypeInfo pc87312_type_info = {
.name = TYPE_PC87312,
.parent = TYPE_ISA_DEVICE,
.name = TYPE_PC87312_SUPERIO,
.parent = TYPE_ISA_SUPERIO,
.instance_size = sizeof(PC87312State),
.instance_init = pc87312_initfn,
.class_init = pc87312_class_init,
/* FIXME use a qdev drive property instead of drive_get() */
};
static void pc87312_register_types(void)

115
hw/isa/smc37c669-superio.c Normal file
View File

@ -0,0 +1,115 @@
/*
* SMC FDC37C669 Super I/O controller
*
* Copyright (c) 2018 Philippe Mathieu-Daudé
*
* This code is licensed under the GNU GPLv2 and later.
* See the COPYING file in the top-level directory.
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "hw/isa/superio.h"
/* UARTs (compatible with NS16450 or PC16550) */
static bool is_serial_enabled(ISASuperIODevice *sio, uint8_t index)
{
return index < 2;
}
static uint16_t get_serial_iobase(ISASuperIODevice *sio, uint8_t index)
{
return index ? 0x2f8 : 0x3f8;
}
static unsigned int get_serial_irq(ISASuperIODevice *sio, uint8_t index)
{
return index ? 3 : 4;
}
/* Parallel port */
static bool is_parallel_enabled(ISASuperIODevice *sio, uint8_t index)
{
return index < 1;
}
static uint16_t get_parallel_iobase(ISASuperIODevice *sio, uint8_t index)
{
return 0x3bc;
}
static unsigned int get_parallel_irq(ISASuperIODevice *sio, uint8_t index)
{
return 7;
}
static unsigned int get_parallel_dma(ISASuperIODevice *sio, uint8_t index)
{
return 3;
}
/* Diskette controller (Software compatible with the Intel PC8477) */
static bool is_fdc_enabled(ISASuperIODevice *sio, uint8_t index)
{
return index < 1;
}
static uint16_t get_fdc_iobase(ISASuperIODevice *sio, uint8_t index)
{
return 0x3f0;
}
static unsigned int get_fdc_irq(ISASuperIODevice *sio, uint8_t index)
{
return 6;
}
static unsigned int get_fdc_dma(ISASuperIODevice *sio, uint8_t index)
{
return 2;
}
static void smc37c669_class_init(ObjectClass *klass, void *data)
{
ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass);
sc->parallel = (ISASuperIOFuncs){
.count = 1,
.is_enabled = is_parallel_enabled,
.get_iobase = get_parallel_iobase,
.get_irq = get_parallel_irq,
.get_dma = get_parallel_dma,
};
sc->serial = (ISASuperIOFuncs){
.count = 2,
.is_enabled = is_serial_enabled,
.get_iobase = get_serial_iobase,
.get_irq = get_serial_irq,
};
sc->floppy = (ISASuperIOFuncs){
.count = 1,
.is_enabled = is_fdc_enabled,
.get_iobase = get_fdc_iobase,
.get_irq = get_fdc_irq,
.get_dma = get_fdc_dma,
};
sc->ide.count = 0;
}
static const TypeInfo smc37c669_type_info = {
.name = TYPE_SMC37C669_SUPERIO,
.parent = TYPE_ISA_SUPERIO,
.instance_size = sizeof(ISASuperIODevice),
.class_size = sizeof(ISASuperIOClass),
.class_init = smc37c669_class_init,
};
static void smc37c669_register_types(void)
{
type_register_static(&smc37c669_type_info);
}
type_init(smc37c669_register_types)

View File

@ -1,9 +1,11 @@
# See docs/devel/tracing.txt for syntax documentation.
# hw/isa/isa-superio.c
superio_create_parallel(int id, uint16_t base, unsigned int irq) "id=%d, base 0x%03x, irq %u"
superio_create_serial(int id, uint16_t base, unsigned int irq) "id=%d, base 0x%03x, irq %u"
superio_create_floppy(int id, uint16_t base, unsigned int irq) "id=%d, base 0x%03x, irq %u"
superio_create_ide(int id, uint16_t base, unsigned int irq) "id=%d, base 0x%03x, irq %u"
# hw/isa/pc87312.c
pc87312_io_read(uint32_t addr, uint32_t val) "read addr=0x%x val=0x%x"
pc87312_io_write(uint32_t addr, uint32_t val) "write addr=0x%x val=0x%x"
pc87312_info_floppy(uint32_t base) "base 0x%x"
pc87312_info_ide(uint32_t base) "base 0x%x"
pc87312_info_parallel(uint32_t base, uint32_t irq) "base 0x%x, irq %u"
pc87312_info_serial(int n, uint32_t base, uint32_t irq) "id=%d, base 0x%x, irq %u"

View File

@ -17,6 +17,7 @@
#include "hw/i2c/smbus.h"
#include "hw/pci/pci.h"
#include "hw/isa/isa.h"
#include "hw/isa/superio.h"
#include "hw/sysbus.h"
#include "hw/mips/mips.h"
#include "hw/isa/apm.h"
@ -478,7 +479,7 @@ static void vt82c686b_realize(PCIDevice *d, Error **errp)
qemu_register_reset(vt82c686b_reset, d);
}
ISABus *vt82c686b_init(PCIBus *bus, int devfn)
ISABus *vt82c686b_isa_init(PCIBus *bus, int devfn)
{
PCIDevice *d;
@ -519,11 +520,30 @@ static const TypeInfo via_info = {
},
};
static void vt82c686b_superio_class_init(ObjectClass *klass, void *data)
{
ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass);
sc->serial.count = 2;
sc->parallel.count = 1;
sc->ide.count = 0;
sc->floppy.count = 1;
}
static const TypeInfo via_superio_info = {
.name = TYPE_VT82C686B_SUPERIO,
.parent = TYPE_ISA_SUPERIO,
.instance_size = sizeof(ISASuperIODevice),
.class_size = sizeof(ISASuperIOClass),
.class_init = vt82c686b_superio_class_init,
};
static void vt82c686b_register_types(void)
{
type_register_static(&via_ac97_info);
type_register_static(&via_mc97_info);
type_register_static(&via_pm_info);
type_register_static(&via_superio_info);
type_register_static(&via_info);
}

View File

@ -22,17 +22,15 @@
#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/char/serial.h"
#include "hw/block/fdc.h"
#include "hw/dma/i8257.h"
#include "hw/isa/superio.h"
#include "net/net.h"
#include "hw/boards.h"
#include "hw/i2c/smbus.h"
#include "sysemu/block-backend.h"
#include "hw/block/flash.h"
#include "hw/mips/mips.h"
#include "hw/mips/cpudevs.h"
#include "hw/pci/pci.h"
#include "sysemu/sysemu.h"
#include "audio/audio.h"
#include "qemu/log.h"
#include "hw/loader.h"
@ -42,7 +40,6 @@
#include "hw/isa/vt82c686.h"
#include "hw/timer/mc146818rtc.h"
#include "hw/timer/i8254.h"
#include "sysemu/blockdev.h"
#include "exec/address-spaces.h"
#include "sysemu/qtest.h"
#include "qemu/error-report.h"
@ -75,8 +72,6 @@
#define FULONG2E_ATI_SLOT 6
#define FULONG2E_RTL8139_SLOT 7
static ISADevice *pit;
static struct _loaderparams {
int ram_size;
const char *kernel_filename;
@ -229,11 +224,40 @@ static const uint8_t eeprom_spd[0x80] = {
0x20,0x30,0x20
};
/* Audio support */
static void audio_init (PCIBus *pci_bus)
static void vt82c686b_southbridge_init(PCIBus *pci_bus, int slot, qemu_irq intc,
I2CBus **i2c_bus, ISABus **p_isa_bus)
{
vt82c686b_ac97_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 5));
vt82c686b_mc97_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 6));
qemu_irq *i8259;
ISABus *isa_bus;
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
isa_bus = vt82c686b_isa_init(pci_bus, PCI_DEVFN(slot, 0));
if (!isa_bus) {
fprintf(stderr, "vt82c686b_init error\n");
exit(1);
}
*p_isa_bus = isa_bus;
/* Interrupt controller */
/* The 8259 -> IP5 */
i8259 = i8259_init(isa_bus, intc);
isa_bus_irqs(isa_bus, i8259);
/* init other devices */
i8254_pit_init(isa_bus, 0x40, 0, NULL);
i8257_dma_init(isa_bus, 0);
/* Super I/O */
isa_create_simple(isa_bus, TYPE_VT82C686B_SUPERIO);
ide_drive_get(hd, ARRAY_SIZE(hd));
vt82c686b_ide_init(pci_bus, hd, PCI_DEVFN(slot, 1));
pci_create_simple(pci_bus, PCI_DEVFN(slot, 2), "vt82c686b-usb-uhci");
pci_create_simple(pci_bus, PCI_DEVFN(slot, 3), "vt82c686b-usb-uhci");
*i2c_bus = vt82c686b_pm_init(pci_bus, PCI_DEVFN(slot, 4), 0xeee1, NULL);
/* Audio support */
vt82c686b_ac97_init(pci_bus, PCI_DEVFN(slot, 5));
vt82c686b_mc97_init(pci_bus, PCI_DEVFN(slot, 6));
}
/* Network support */
@ -266,11 +290,9 @@ static void mips_fulong2e_init(MachineState *machine)
MemoryRegion *bios = g_new(MemoryRegion, 1);
long bios_size;
int64_t kernel_entry;
qemu_irq *i8259;
PCIBus *pci_bus;
ISABus *isa_bus;
I2CBus *smbus;
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
MIPSCPU *cpu;
CPUMIPSState *env;
@ -332,46 +354,16 @@ static void mips_fulong2e_init(MachineState *machine)
/* North bridge, Bonito --> IP2 */
pci_bus = bonito_init((qemu_irq *)&(env->irq[2]));
/* South bridge */
ide_drive_get(hd, ARRAY_SIZE(hd));
/* South bridge -> IP5 */
vt82c686b_southbridge_init(pci_bus, FULONG2E_VIA_SLOT, env->irq[5],
&smbus, &isa_bus);
isa_bus = vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 0));
if (!isa_bus) {
error_report("vt82c686b_init error");
exit(1);
}
/* Interrupt controller */
/* The 8259 -> IP5 */
i8259 = i8259_init(isa_bus, env->irq[5]);
isa_bus_irqs(isa_bus, i8259);
vt82c686b_ide_init(pci_bus, hd, PCI_DEVFN(FULONG2E_VIA_SLOT, 1));
pci_create_simple(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 2),
"vt82c686b-usb-uhci");
pci_create_simple(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 3),
"vt82c686b-usb-uhci");
smbus = vt82c686b_pm_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 4),
0xeee1, NULL);
/* TODO: Populate SPD eeprom data. */
smbus_eeprom_init(smbus, 1, eeprom_spd, sizeof(eeprom_spd));
/* init other devices */
pit = i8254_pit_init(isa_bus, 0x40, 0, NULL);
DMA_init(isa_bus, 0);
/* Super I/O */
isa_create_simple(isa_bus, "i8042");
mc146818_rtc_init(isa_bus, 2000, NULL);
serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS);
parallel_hds_isa_init(isa_bus, 1);
/* Sound card */
audio_init(pci_bus);
/* Network card */
/* Network card: RTL8139D */
network_init(pci_bus);
}

View File

@ -27,7 +27,9 @@
#include "hw/mips/mips.h"
#include "hw/mips/cpudevs.h"
#include "hw/i386/pc.h"
#include "hw/dma/i8257.h"
#include "hw/char/serial.h"
#include "hw/char/parallel.h"
#include "hw/isa/isa.h"
#include "hw/block/fdc.h"
#include "sysemu/sysemu.h"
@ -41,7 +43,7 @@
#include "hw/timer/i8254.h"
#include "hw/display/vga.h"
#include "hw/audio/pcspk.h"
#include "sysemu/block-backend.h"
#include "hw/input/i8042.h"
#include "hw/sysbus.h"
#include "exec/address-spaces.h"
#include "sysemu/qtest.h"
@ -147,6 +149,7 @@ static void mips_jazz_init(MachineState *machine,
MemoryRegion *ram = g_new(MemoryRegion, 1);
MemoryRegion *bios = g_new(MemoryRegion, 1);
MemoryRegion *bios2 = g_new(MemoryRegion, 1);
ESPState *esp;
/* init CPUs */
cpu = MIPS_CPU(cpu_create(machine->cpu_type));
@ -219,7 +222,7 @@ static void mips_jazz_init(MachineState *machine,
/* ISA devices */
i8259 = i8259_init(isa_bus, env->irq[4]);
isa_bus_irqs(isa_bus, i8259);
DMA_init(isa_bus, 0);
i8257_dma_init(isa_bus, 0);
pit = i8254_pit_init(isa_bus, 0x40, 0, NULL);
pcspk_init(isa_bus, pit);
@ -278,9 +281,9 @@ static void mips_jazz_init(MachineState *machine,
}
/* SCSI adapter */
esp_init(0x80002000, 0,
rc4030_dma_read, rc4030_dma_write, dmas[0],
qdev_get_gpio_in(rc4030, 5), &esp_reset, &dma_enable);
esp = esp_init(0x80002000, 0, rc4030_dma_read, rc4030_dma_write, dmas[0],
qdev_get_gpio_in(rc4030, 5), &esp_reset, &dma_enable);
scsi_bus_legacy_handle_cmdline(&esp->bus);
/* Floppy */
for (n = 0; n < MAX_FD; n++) {

View File

@ -27,12 +27,12 @@
#include "cpu.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/isa/superio.h"
#include "hw/dma/i8257.h"
#include "hw/char/serial.h"
#include "hw/block/fdc.h"
#include "net/net.h"
#include "hw/boards.h"
#include "hw/i2c/smbus.h"
#include "sysemu/block-backend.h"
#include "hw/block/flash.h"
#include "hw/mips/mips.h"
#include "hw/mips/cpudevs.h"
@ -1002,10 +1002,8 @@ void mips_malta_init(MachineState *machine)
qemu_irq cbus_irq, i8259_irq;
int piix4_devfn;
I2CBus *smbus;
int i;
DriveInfo *dinfo;
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
DriveInfo *fd[MAX_FD];
int fl_idx = 0;
int fl_sectors = bios_size >> 16;
int be;
@ -1020,15 +1018,6 @@ void mips_malta_init(MachineState *machine)
qdev_init_nofail(dev);
/* Make sure the first 3 serial ports are associated with a device. */
for(i = 0; i < 3; i++) {
if (!serial_hds[i]) {
char label[32];
snprintf(label, sizeof(label), "serial%d", i);
serial_hds[i] = qemu_chr_new(label, "null");
}
}
/* create CPU */
mips_create_cpu(s, machine->cpu_type, &cbus_irq, &i8259_irq);
@ -1059,16 +1048,19 @@ void mips_malta_init(MachineState *machine)
memory_region_add_subregion(system_memory, 512 << 20, ram_low_postio);
}
/* generate SPD EEPROM data */
generate_eeprom_spd(&smbus_eeprom_buf[0 * 256], ram_size);
generate_eeprom_serial(&smbus_eeprom_buf[6 * 256]);
#ifdef TARGET_WORDS_BIGENDIAN
be = 1;
#else
be = 0;
#endif
/* FPGA */
/* Make sure the second serial port is associated with a device. */
if (!serial_hds[2]) {
serial_hds[2] = qemu_chr_new("fpga-uart", "null");
}
/* The CBUS UART is attached to the MIPS CPU INT2 pin, ie interrupt 4 */
malta_fpga_init(system_memory, FPGA_ADDRESS, cbus_irq, serial_hds[2]);
@ -1205,22 +1197,18 @@ void mips_malta_init(MachineState *machine)
pci_create_simple(pci_bus, piix4_devfn + 2, "piix4-usb-uhci");
smbus = piix4_pm_init(pci_bus, piix4_devfn + 3, 0x1100,
isa_get_irq(NULL, 9), NULL, 0, NULL);
pit = i8254_pit_init(isa_bus, 0x40, 0, NULL);
i8257_dma_init(isa_bus, 0);
mc146818_rtc_init(isa_bus, 2000, NULL);
/* generate SPD EEPROM data */
generate_eeprom_spd(&smbus_eeprom_buf[0 * 256], ram_size);
generate_eeprom_serial(&smbus_eeprom_buf[6 * 256]);
smbus_eeprom_init(smbus, 8, smbus_eeprom_buf, smbus_eeprom_size);
g_free(smbus_eeprom_buf);
pit = i8254_pit_init(isa_bus, 0x40, 0, NULL);
DMA_init(isa_bus, 0);
/* Super I/O */
isa_create_simple(isa_bus, "i8042");
mc146818_rtc_init(isa_bus, 2000, NULL);
serial_hds_isa_init(isa_bus, 0, 2);
parallel_hds_isa_init(isa_bus, 1);
for(i = 0; i < MAX_FD; i++) {
fd[i] = drive_get(IF_FLOPPY, 0, i);
}
fdctrl_init_isa(isa_bus, fd);
/* Super I/O: SMS FDC37M817 */
isa_create_simple(isa_bus, TYPE_FDC37M81X_SUPERIO);
/* Network card */
network_init(pci_bus);

View File

@ -28,6 +28,7 @@
#include "hw/loader.h"
#include "elf.h"
#include "hw/timer/mc146818rtc.h"
#include "hw/input/i8042.h"
#include "hw/timer/i8254.h"
#include "sysemu/block-backend.h"
#include "exec/address-spaces.h"
@ -286,7 +287,7 @@ void mips_r4k_init(MachineState *machine)
hd[MAX_IDE_DEVS * i],
hd[MAX_IDE_DEVS * i + 1]);
isa_create_simple(isa_bus, "i8042");
isa_create_simple(isa_bus, TYPE_I8042);
}
static void mips_machine_init(MachineClass *mc)

View File

@ -1815,49 +1815,48 @@ PciInfoList *qmp_query_pci(Error **errp)
return head;
}
static const char * const pci_nic_models[] = {
"ne2k_pci",
"i82551",
"i82557b",
"i82559er",
"rtl8139",
"e1000",
"pcnet",
"virtio",
"sungem",
NULL
};
static const char * const pci_nic_names[] = {
"ne2k_pci",
"i82551",
"i82557b",
"i82559er",
"rtl8139",
"e1000",
"pcnet",
"virtio-net-pci",
"sungem",
NULL
};
/* Initialize a PCI NIC. */
PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus,
const char *default_model,
const char *default_devaddr)
{
const char *devaddr = nd->devaddr ? nd->devaddr : default_devaddr;
GSList *list;
GPtrArray *pci_nic_models;
PCIBus *bus;
PCIDevice *pci_dev;
DeviceState *dev;
int devfn;
int i;
if (qemu_show_nic_models(nd->model, pci_nic_models)) {
if (nd->model && !strcmp(nd->model, "virtio")) {
g_free(nd->model);
nd->model = g_strdup("virtio-net-pci");
}
list = object_class_get_list_sorted(TYPE_PCI_DEVICE, false);
pci_nic_models = g_ptr_array_new();
while (list) {
DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, list->data,
TYPE_DEVICE);
GSList *next;
if (test_bit(DEVICE_CATEGORY_NETWORK, dc->categories) &&
dc->user_creatable) {
const char *name = object_class_get_name(list->data);
g_ptr_array_add(pci_nic_models, (gpointer)name);
}
next = list->next;
g_slist_free_1(list);
list = next;
}
g_ptr_array_add(pci_nic_models, NULL);
if (qemu_show_nic_models(nd->model, (const char **)pci_nic_models->pdata)) {
exit(0);
}
i = qemu_find_nic_model(nd, pci_nic_models, default_model);
i = qemu_find_nic_model(nd, (const char **)pci_nic_models->pdata,
default_model);
if (i < 0) {
exit(1);
}
@ -1865,15 +1864,15 @@ PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus,
bus = pci_get_bus_devfn(&devfn, rootbus, devaddr);
if (!bus) {
error_report("Invalid PCI device address %s for device %s",
devaddr, pci_nic_names[i]);
devaddr, nd->model);
exit(1);
}
pci_dev = pci_create(bus, devfn, pci_nic_names[i]);
pci_dev = pci_create(bus, devfn, nd->model);
dev = &pci_dev->qdev;
qdev_set_nic_properties(dev, nd);
qdev_init_nofail(dev);
g_ptr_array_free(pci_nic_models, true);
return pci_dev;
}

View File

@ -916,7 +916,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
if (pci_bus) {
/* Register network interfaces. */
for (i = 0; i < nb_nics; i++) {
pci_nic_init_nofail(&nd_table[i], pci_bus, "virtio", NULL);
pci_nic_init_nofail(&nd_table[i], pci_bus, "virtio-net-pci", NULL);
}
}

View File

@ -69,7 +69,6 @@
#include "sysemu/kvm.h"
#include "kvm_ppc.h"
#include "hw/usb.h"
#include "sysemu/block-backend.h"
#include "exec/address-spaces.h"
#include "hw/sysbus.h"
#include "qemu/cutils.h"

View File

@ -44,7 +44,6 @@
#include "qemu/error-report.h"
#include "sysemu/kvm.h"
#include "kvm_ppc.h"
#include "sysemu/block-backend.h"
#include "exec/address-spaces.h"
#include "qemu/cutils.h"

View File

@ -41,9 +41,9 @@
#include "hw/ide.h"
#include "hw/loader.h"
#include "hw/timer/mc146818rtc.h"
#include "hw/input/i8042.h"
#include "hw/isa/pc87312.h"
#include "hw/net/ne2000-isa.h"
#include "sysemu/block-backend.h"
#include "sysemu/arch_init.h"
#include "sysemu/kvm.h"
#include "sysemu/qtest.h"
@ -612,7 +612,7 @@ static void ppc_prep_init(MachineState *machine)
isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(pci), "isa.0"));
/* Super I/O (parallel + serial ports) */
isa = isa_create(isa_bus, TYPE_PC87312);
isa = isa_create(isa_bus, TYPE_PC87312_SUPERIO);
dev = DEVICE(isa);
qdev_prop_set_uint8(dev, "config", 13); /* fdc, ser0, ser1, par0 */
qdev_init_nofail(dev);
@ -641,7 +641,6 @@ static void ppc_prep_init(MachineState *machine)
hd[2 * i],
hd[2 * i + 1]);
}
isa_create_simple(isa_bus, "i8042");
cpu = POWERPC_CPU(first_cpu);
sysctrl->reset_irq = cpu->env.irq_inputs[PPC6xx_INPUT_HRESET];
@ -771,7 +770,7 @@ static void ibm_40p_init(MachineState *machine)
/* add some more devices */
if (defaults_enabled()) {
isa_create_simple(isa_bus, "i8042");
isa_create_simple(isa_bus, TYPE_I8042);
m48t59 = NVRAM(isa_create_simple(isa_bus, "isa-m48t59"));
dev = DEVICE(isa_create(isa_bus, "cs4231a"));

View File

@ -618,11 +618,11 @@ static const MemoryRegionOps sysbus_esp_mem_ops = {
.valid.accepts = esp_mem_accepts,
};
void esp_init(hwaddr espaddr, int it_shift,
ESPDMAMemoryReadWriteFunc dma_memory_read,
ESPDMAMemoryReadWriteFunc dma_memory_write,
void *dma_opaque, qemu_irq irq, qemu_irq *reset,
qemu_irq *dma_enable)
ESPState *esp_init(hwaddr espaddr, int it_shift,
ESPDMAMemoryReadWriteFunc dma_memory_read,
ESPDMAMemoryReadWriteFunc dma_memory_write,
void *dma_opaque, qemu_irq irq, qemu_irq *reset,
qemu_irq *dma_enable)
{
DeviceState *dev;
SysBusDevice *s;
@ -644,6 +644,8 @@ void esp_init(hwaddr espaddr, int it_shift,
sysbus_mmio_map(s, 0, espaddr);
*reset = qdev_get_gpio_in(dev, 0);
*dma_enable = qdev_get_gpio_in(dev, 1);
return esp;
}
static const struct SCSIBusInfo esp_scsi_info = {

View File

@ -24,7 +24,6 @@
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "sysemu/dma.h"
#include "sysemu/block-backend.h"
#include "hw/pci/msi.h"
#include "qemu/iov.h"
#include "hw/scsi/scsi.h"

View File

@ -944,7 +944,7 @@ static int scsi_req_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
break;
case WRITE_SAME_10:
case WRITE_SAME_16:
cmd->xfer = dev->blocksize;
cmd->xfer = buf[1] & 1 ? 0 : dev->blocksize;
break;
case READ_CAPACITY_10:
cmd->xfer = 8;

View File

@ -704,6 +704,21 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
page_code);
return -1;
}
if (s->qdev.type == TYPE_DISK) {
int max_transfer_blk = blk_get_max_transfer(s->qdev.conf.blk);
int max_io_sectors_blk =
max_transfer_blk / s->qdev.blocksize;
max_io_sectors =
MIN_NON_ZERO(max_io_sectors_blk, max_io_sectors);
/* min_io_size and opt_io_size can't be greater than
* max_io_sectors */
min_io_size =
MIN_NON_ZERO(min_io_size, max_io_sectors);
opt_io_size =
MIN_NON_ZERO(opt_io_size, max_io_sectors);
}
/* required VPD size with unmap support */
buflen = 0x40;
memset(outbuf + 4, 0, buflen - 4);
@ -1792,7 +1807,7 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf)
return;
}
if (buffer_is_zero(inbuf, s->qdev.blocksize)) {
if ((req->cmd.buf[1] & 0x1) || buffer_is_zero(inbuf, s->qdev.blocksize)) {
int flags = (req->cmd.buf[1] & 0x8) ? BDRV_REQ_MAY_UNMAP : 0;
/* The request is used as the AIO opaque value, so add a ref. */

View File

@ -21,7 +21,6 @@
#include "qemu/osdep.h"
#include "hw/qdev-core.h"
#include "sysemu/block-backend.h"
#include "hw/sd/sd.h"
#include "trace.h"

View File

@ -44,7 +44,6 @@
#include "hw/empty_slot.h"
#include "hw/loader.h"
#include "elf.h"
#include "sysemu/block-backend.h"
#include "trace.h"
#include "qemu/cutils.h"
@ -99,10 +98,6 @@ struct sun4m_hwdef {
uint8_t nvram_machine_id;
};
void DMA_init(ISABus *bus, int high_page_enable)
{
}
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
Error **errp)
{

View File

@ -34,7 +34,9 @@
#include "hw/pci-host/sabre.h"
#include "hw/i386/pc.h"
#include "hw/char/serial.h"
#include "hw/char/parallel.h"
#include "hw/timer/m48t59.h"
#include "hw/input/i8042.h"
#include "hw/block/fdc.h"
#include "net/net.h"
#include "qemu/timer.h"
@ -89,10 +91,6 @@ typedef struct EbusState {
#define TYPE_EBUS "ebus"
#define EBUS(obj) OBJECT_CHECK(EbusState, (obj), TYPE_EBUS)
void DMA_init(ISABus *bus, int high_page_enable)
{
}
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
Error **errp)
{

View File

@ -28,9 +28,7 @@
#include "sysemu/sysemu.h"
#include "hw/boards.h"
#include "hw/loader.h"
#include "sysemu/block-backend.h"
#include "exec/address-spaces.h"
#include "hw/block/flash.h"
#include "elf.h"
#include "hw/tricore/tricore.h"
#include "qemu/error-report.h"

View File

@ -20,6 +20,7 @@
#undef DEBUG_PUV3
#include "hw/unicore32/puv3.h"
#include "hw/input/i8042.h"
#define KERNEL_LOAD_ADDR 0x03000000
#define KERNEL_MAX_SIZE 0x00800000 /* Just a guess */

View File

@ -27,8 +27,6 @@
#include "chardev/char.h"
#include "chardev/char-fe.h"
extern bool muxes_realized;
#define MAX_MUX 4
#define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */
#define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)

View File

@ -248,6 +248,8 @@ typedef struct ChardevClass {
void (*chr_set_echo)(Chardev *chr, bool echo);
void (*chr_set_fe_open)(Chardev *chr, int fe_open);
void (*chr_be_event)(Chardev *s, int event);
/* Return 0 if succeeded, 1 if failed */
int (*chr_machine_done)(Chardev *chr);
} ChardevClass;
Chardev *qemu_chardev_new(const char *id, const char *typename,

View File

@ -0,0 +1,14 @@
#ifndef HW_PARALLEL_H
#define HW_PARALLEL_H
#include "exec/memory.h"
#include "hw/isa/isa.h"
#include "chardev/char.h"
void parallel_hds_isa_init(ISABus *bus, int n);
bool parallel_mm_init(MemoryRegion *address_space,
hwaddr base, int it_shift, qemu_irq irq,
Chardev *chr);
#endif

View File

@ -1,6 +1,10 @@
#ifndef HW_I8257_H
#define HW_I8257_H
#include "hw/hw.h"
#include "hw/isa/isa.h"
#include "exec/ioport.h"
#define TYPE_I8257 "i8257"
typedef struct I8257Regs {
@ -40,4 +44,6 @@ typedef struct I8257State {
PortioList portio_pageh;
} I8257State;
void i8257_dma_init(ISABus *bus, bool high_page_enable);
#endif

View File

@ -114,6 +114,7 @@ struct PCMachineClass {
/* Device configuration: */
bool pci_enabled;
bool kvmclock_enabled;
const char *default_nic_model;
/* Compat options: */
@ -151,14 +152,6 @@ struct PCMachineClass {
#define PC_MACHINE_CLASS(klass) \
OBJECT_CLASS_CHECK(PCMachineClass, (klass), TYPE_PC_MACHINE)
/* parallel.c */
void parallel_hds_isa_init(ISABus *bus, int n);
bool parallel_mm_init(MemoryRegion *address_space,
hwaddr base, int it_shift, qemu_irq irq,
Chardev *chr);
/* i8259.c */
extern DeviceState *isa_pic;
@ -196,15 +189,6 @@ void vmport_register(unsigned char command, VMPortReadFunc *func, void *opaque);
void vmmouse_get_data(uint32_t *data);
void vmmouse_set_data(const uint32_t *data);
/* pckbd.c */
#define I8042_A20_LINE "a20"
void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
MemoryRegion *region, ram_addr_t size,
hwaddr mask);
void i8042_isa_mouse_fake_event(void *opaque);
void i8042_setup_a20_line(ISADevice *dev, qemu_irq a20_out);
/* pc.c */
extern int fd_bootchk;
@ -248,7 +232,7 @@ void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd);
void pc_cmos_init(PCMachineState *pcms,
BusState *ide0, BusState *ide1,
ISADevice *s);
void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus);
void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus);
void pc_pci_device_init(PCIBus *pci_bus);
typedef void (*cpu_set_smm_t)(int smm, void *arg);

24
include/hw/input/i8042.h Normal file
View File

@ -0,0 +1,24 @@
/*
* QEMU PS/2 Controller
*
* Copyright (c) 2003 Fabrice Bellard
*
* SPDX-License-Identifier: MIT
*/
#ifndef HW_INPUT_I8042_H
#define HW_INPUT_I8042_H
#include "hw/hw.h"
#include "hw/isa/isa.h"
#define TYPE_I8042 "i8042"
#define I8042_A20_LINE "a20"
void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
MemoryRegion *region, ram_addr_t size,
hwaddr mask);
void i8042_isa_mouse_fake_event(void *opaque);
void i8042_setup_a20_line(ISADevice *dev, qemu_irq a20_out);
#endif /* HW_INPUT_I8042_H */

View File

@ -151,6 +151,4 @@ static inline ISABus *isa_bus_from_device(ISADevice *d)
return ISA_BUS(qdev_get_parent_bus(DEVICE(d)));
}
/* i8257.c */
void DMA_init(ISABus *bus, int high_page_enable);
#endif

View File

@ -25,30 +25,20 @@
#ifndef QEMU_PC87312_H
#define QEMU_PC87312_H
#include "hw/isa/isa.h"
#include "hw/isa/superio.h"
#define TYPE_PC87312 "pc87312"
#define PC87312(obj) OBJECT_CHECK(PC87312State, (obj), TYPE_PC87312)
#define TYPE_PC87312_SUPERIO "pc87312"
#define PC87312(obj) OBJECT_CHECK(PC87312State, (obj), TYPE_PC87312_SUPERIO)
typedef struct PC87312State {
ISADevice dev;
/*< private >*/
ISASuperIODevice parent_dev;
/*< public >*/
uint32_t iobase;
uint16_t iobase;
uint8_t config; /* initial configuration */
struct {
ISADevice *dev;
} parallel;
struct {
ISADevice *dev;
} uart[2];
struct {
ISADevice *dev;
} fdc;
struct {
ISADevice *dev;
} ide;

60
include/hw/isa/superio.h Normal file
View File

@ -0,0 +1,60 @@
/*
* Generic ISA Super I/O
*
* Copyright (c) 2018 Philippe Mathieu-Daudé
*
* This code is licensed under the GNU GPLv2 and later.
* See the COPYING file in the top-level directory.
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef HW_ISA_SUPERIO_H
#define HW_ISA_SUPERIO_H
#include "qemu-common.h"
#include "sysemu/sysemu.h"
#include "hw/isa/isa.h"
#define TYPE_ISA_SUPERIO "isa-superio"
#define ISA_SUPERIO(obj) \
OBJECT_CHECK(ISASuperIODevice, (obj), TYPE_ISA_SUPERIO)
#define ISA_SUPERIO_GET_CLASS(obj) \
OBJECT_GET_CLASS(ISASuperIOClass, (obj), TYPE_ISA_SUPERIO)
#define ISA_SUPERIO_CLASS(klass) \
OBJECT_CLASS_CHECK(ISASuperIOClass, (klass), TYPE_ISA_SUPERIO)
typedef struct ISASuperIODevice {
/*< private >*/
ISADevice parent_obj;
/*< public >*/
ISADevice *parallel[MAX_PARALLEL_PORTS];
ISADevice *serial[MAX_SERIAL_PORTS];
ISADevice *floppy;
ISADevice *kbc;
ISADevice *ide;
} ISASuperIODevice;
typedef struct ISASuperIOFuncs {
size_t count;
bool (*is_enabled)(ISASuperIODevice *sio, uint8_t index);
uint16_t (*get_iobase)(ISASuperIODevice *sio, uint8_t index);
unsigned int (*get_irq)(ISASuperIODevice *sio, uint8_t index);
unsigned int (*get_dma)(ISASuperIODevice *sio, uint8_t index);
} ISASuperIOFuncs;
typedef struct ISASuperIOClass {
/*< private >*/
ISADeviceClass parent_class;
/*< public >*/
DeviceRealize parent_realize;
ISASuperIOFuncs parallel;
ISASuperIOFuncs serial;
ISASuperIOFuncs floppy;
ISASuperIOFuncs ide;
} ISASuperIOClass;
#define TYPE_FDC37M81X_SUPERIO "fdc37m81x-superio"
#define TYPE_SMC37C669_SUPERIO "smc37c669-superio"
#endif /* HW_ISA_SUPERIO_H */

View File

@ -1,8 +1,10 @@
#ifndef HW_VT82C686_H
#define HW_VT82C686_H
#define TYPE_VT82C686B_SUPERIO "vt82c686b-superio"
/* vt82c686.c */
ISABus *vt82c686b_init(PCIBus * bus, int devfn);
ISABus *vt82c686b_isa_init(PCIBus * bus, int devfn);
void vt82c686b_ac97_init(PCIBus *bus, int devfn);
void vt82c686b_mc97_init(PCIBus *bus, int devfn);
I2CBus *vt82c686b_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,

View File

@ -7,11 +7,6 @@
/* esp.c */
#define ESP_MAX_DEVS 7
typedef void (*ESPDMAMemoryReadWriteFunc)(void *opaque, uint8_t *buf, int len);
void esp_init(hwaddr espaddr, int it_shift,
ESPDMAMemoryReadWriteFunc dma_memory_read,
ESPDMAMemoryReadWriteFunc dma_memory_write,
void *dma_opaque, qemu_irq irq, qemu_irq *reset,
qemu_irq *dma_enable);
#define ESP_REGS 16
#define TI_BUFSZ 16
@ -136,6 +131,11 @@ typedef struct {
#define TCHI_FAS100A 0x4
#define TCHI_AM53C974 0x12
ESPState *esp_init(hwaddr espaddr, int it_shift,
ESPDMAMemoryReadWriteFunc dma_memory_read,
ESPDMAMemoryReadWriteFunc dma_memory_write,
void *dma_opaque, qemu_irq irq, qemu_irq *reset,
qemu_irq *dma_enable);
void esp_dma_enable(ESPState *s, int irq, int level);
void esp_request_cancelled(SCSIRequest *req);
void esp_command_complete(SCSIRequest *req, uint32_t status, size_t resid);

View File

@ -27,6 +27,7 @@
#include "qemu/thread.h"
#include "qemu/queue.h"
#include "qemu/atomic.h"
#include "qemu/sys_membarrier.h"
#ifdef __cplusplus
extern "C" {
@ -79,7 +80,10 @@ static inline void rcu_read_lock(void)
}
ctr = atomic_read(&rcu_gp_ctr);
atomic_xchg(&p_rcu_reader->ctr, ctr);
atomic_set(&p_rcu_reader->ctr, ctr);
/* Write p_rcu_reader->ctr before reading RCU-protected pointers. */
smp_mb_placeholder();
}
static inline void rcu_read_unlock(void)
@ -91,7 +95,15 @@ static inline void rcu_read_unlock(void)
return;
}
atomic_xchg(&p_rcu_reader->ctr, 0);
/* Ensure that the critical section is seen to precede the
* store to p_rcu_reader->ctr. Together with the following
* smp_mb_placeholder(), this ensures writes to p_rcu_reader->ctr
* are sequentially consistent.
*/
atomic_store_release(&p_rcu_reader->ctr, 0);
/* Write p_rcu_reader->ctr before reading p_rcu_reader->waiting. */
smp_mb_placeholder();
if (unlikely(atomic_read(&p_rcu_reader->waiting))) {
atomic_set(&p_rcu_reader->waiting, false);
qemu_event_set(&rcu_gp_event);

View File

@ -0,0 +1,27 @@
/*
* Process-global memory barriers
*
* Copyright (c) 2018 Red Hat, Inc.
*
* Author: Paolo Bonzini <pbonzini@redhat.com>
*/
#ifndef QEMU_SYS_MEMBARRIER_H
#define QEMU_SYS_MEMBARRIER_H 1
#ifdef CONFIG_MEMBARRIER
/* Only block reordering at the compiler level in the performance-critical
* side. The slow side forces processor-level ordering on all other cores
* through a system call.
*/
extern void smp_mb_global_init(void);
extern void smp_mb_global(void);
#define smp_mb_placeholder() barrier()
#else
/* Keep it simple, execute a real memory barrier on both sides. */
static inline void smp_mb_global_init(void) {}
#define smp_mb_global() smp_mb()
#define smp_mb_placeholder() smp_mb()
#endif
#endif

View File

@ -251,6 +251,20 @@ bool qemu_clock_run_timers(QEMUClockType type);
*/
bool qemu_clock_run_all_timers(void);
/**
* qemu_clock_get_last:
*
* Returns last clock query time.
*/
uint64_t qemu_clock_get_last(QEMUClockType type);
/**
* qemu_clock_set_last:
*
* Sets last clock query time.
*/
void qemu_clock_set_last(QEMUClockType type, uint64_t last);
/*
* QEMUTimerList
*/

View File

@ -913,6 +913,17 @@ void object_class_foreach(void (*fn)(ObjectClass *klass, void *opaque),
GSList *object_class_get_list(const char *implements_type,
bool include_abstract);
/**
* object_class_get_list_sorted:
* @implements_type: The type to filter for, including its derivatives.
* @include_abstract: Whether to include abstract classes.
*
* Returns: A singly-linked list of the classes in alphabetical
* case-insensitive order.
*/
GSList *object_class_get_list_sorted(const char *implements_type,
bool include_abstract);
/**
* object_ref:
* @obj: the object

View File

@ -48,6 +48,19 @@ extern ReplayMode replay_mode;
/* Name of the initial VM snapshot */
extern char *replay_snapshot;
/* Replay locking
*
* The locks are needed to protect the shared structures and log file
* when doing record/replay. They also are the main sync-point between
* the main-loop thread and the vCPU thread. This was a role
* previously filled by the BQL which has been busy trying to reduce
* its impact across the code. This ensures blocks of events stay
* sequential and reproducible.
*/
void replay_mutex_lock(void);
void replay_mutex_unlock(void);
/* Replay process control functions */
/*! Enables recording or saving event log with specified parameters */
@ -166,5 +179,8 @@ void replay_audio_in(int *recorded, void *samples, int *wpos, int size);
/*! Called at the start of execution.
Loads or saves initial vmstate depending on execution mode. */
void replay_vmstate_init(void);
/*! Called to ensure that replay state is consistent and VM snapshot
can be created */
bool replay_can_snapshot(void);
#endif

View File

@ -88,6 +88,8 @@ void qemu_system_guest_panicked(GuestPanicInformation *info);
void qemu_add_exit_notifier(Notifier *notify);
void qemu_remove_exit_notifier(Notifier *notify);
extern bool machine_init_done;
void qemu_add_machine_init_done_notifier(Notifier *notify);
void qemu_remove_machine_init_done_notifier(Notifier *notify);

View File

@ -4074,7 +4074,7 @@ static void handle_arg_strace(const char *arg)
static void handle_arg_version(const char *arg)
{
printf("qemu-" TARGET_NAME " version " QEMU_VERSION QEMU_PKGVERSION
printf("qemu-" TARGET_NAME " version " QEMU_FULL_VERSION
"\n" QEMU_COPYRIGHT "\n");
exit(EXIT_SUCCESS);
}

View File

@ -54,6 +54,7 @@
#include "qemu/cutils.h"
#include "io/channel-buffer.h"
#include "io/channel-file.h"
#include "sysemu/replay.h"
#ifndef ETH_P_RARP
#define ETH_P_RARP 0x8035
@ -2197,6 +2198,12 @@ int save_snapshot(const char *name, Error **errp)
struct tm tm;
AioContext *aio_context;
if (!replay_can_snapshot()) {
error_report("Record/replay does not allow making snapshot "
"right now. Try once more later.");
return ret;
}
if (!bdrv_all_can_snapshot(&bs)) {
error_setg(errp, "Device '%s' is writable but does not support "
"snapshots", bdrv_get_device_name(bs));
@ -2388,6 +2395,12 @@ int load_snapshot(const char *name, Error **errp)
AioContext *aio_context;
MigrationIncomingState *mis = migration_incoming_get_current();
if (!replay_can_snapshot()) {
error_report("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",

View File

@ -122,12 +122,6 @@ static void qdev_print_devinfo(DeviceClass *dc)
error_printf("\n");
}
static gint devinfo_cmp(gconstpointer a, gconstpointer b)
{
return strcasecmp(object_class_get_name((ObjectClass *)a),
object_class_get_name((ObjectClass *)b));
}
static void qdev_print_devinfos(bool show_no_user)
{
static const char *cat_name[DEVICE_CATEGORY_MAX + 1] = {
@ -146,8 +140,7 @@ static void qdev_print_devinfos(bool show_no_user)
int i;
bool cat_printed;
list = g_slist_sort(object_class_get_list(TYPE_DEVICE, false),
devinfo_cmp);
list = object_class_get_list_sorted(TYPE_DEVICE, false);
for (i = 0; i <= DEVICE_CATEGORY_MAX; i++) {
cat_printed = false;

View File

@ -2643,42 +2643,46 @@ combined with ``-vnc tls-creds=tls0'
@subsection -tftp (since 2.6.0)
The ``-tftp /some/dir'' argument is replaced by
``-netdev user,id=x,tftp=/some/dir'', either accompanied with
``-device ...,netdev=x'' (for pluggable NICs) or ``-net nic,netdev=x''
The ``-tftp /some/dir'' argument is replaced by either
``-netdev user,id=x,tftp=/some/dir '' (for pluggable NICs, accompanied
with ``-device ...,netdev=x''), or ``-nic user,tftp=/some/dir''
(for embedded NICs). The new syntax allows different settings to be
provided per NIC.
@subsection -bootp (since 2.6.0)
The ``-bootp /some/file'' argument is replaced by
``-netdev user,id=x,bootp=/some/file'', either accompanied with
``-device ...,netdev=x'' (for pluggable NICs) or ``-net nic,netdev=x''
The ``-bootp /some/file'' argument is replaced by either
``-netdev user,id=x,bootp=/some/file '' (for pluggable NICs, accompanied
with ``-device ...,netdev=x''), or ``-nic user,bootp=/some/file''
(for embedded NICs). The new syntax allows different settings to be
provided per NIC.
@subsection -redir (since 2.6.0)
The ``-redir [tcp|udp]:hostport:[guestaddr]:guestport'' argument is
replaced by ``-netdev
user,id=x,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport'',
either accompanied with ``-device ...,netdev=x'' (for pluggable NICs) or
``-net nic,netdev=x'' (for embedded NICs). The new syntax allows different
settings to be provided per NIC.
replaced by either
``-netdev user,id=x,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport''
(for pluggable NICs, accompanied with ``-device ...,netdev=x'') or
``-nic user,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport''
(for embedded NICs). The new syntax allows different settings to be
provided per NIC.
@subsection -smb (since 2.6.0)
The ``-smb /some/dir'' argument is replaced by
``-netdev user,id=x,smb=/some/dir'', either accompanied with
``-device ...,netdev=x'' (for pluggable NICs) or ``-net nic,netdev=x''
The ``-smb /some/dir'' argument is replaced by either
``-netdev user,id=x,smb=/some/dir '' (for pluggable NICs, accompanied
with ``-device ...,netdev=x''), or ``-nic user,smb=/some/dir''
(for embedded NICs). The new syntax allows different settings to be
provided per NIC.
@subsection -net vlan (since 2.9.0)
The ``-net vlan=NN'' argument is partially replaced with the
new ``-netdev'' argument. The remaining use cases will no
longer be directly supported in QEMU.
The ``-net vlan=NN'' argument was mostly used to attach separate
network backends to different virtual NICs. This is the default
behavior for ``-netdev'' and ``-nic''. You can connect multiple
``-netdev'' and ``-nic'' devices to the same network using the
"hubport" network backend, created with ``-netdev hubport,hubid=NN,...''
and ``-nic hubport,hubid=NN''.
@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0)
@ -2728,6 +2732,12 @@ filesystem test suite. Also it requires the CAP_DAC_READ_SEARCH capability,
which is not the recommended way to run QEMU. This backend should not be
used and it will be removed with no replacement.
@subsection -no-frame (since 2.12.0)
The @code{--no-frame} argument works with SDL 1.2 only. The other user
interfaces never implemented this in the first place. So this will be
removed together with SDL 1.2 support.
@subsection -rtc-td-hack (since 2.12.0)
The @code{-rtc-td-hack} option has been replaced by

View File

@ -46,7 +46,7 @@
#include "crypto/init.h"
#include "trace/control.h"
#define QEMU_IMG_VERSION "qemu-img version " QEMU_VERSION QEMU_PKGVERSION \
#define QEMU_IMG_VERSION "qemu-img version " QEMU_FULL_VERSION \
"\n" QEMU_COPYRIGHT "\n"
typedef struct img_cmd_t {

View File

@ -558,7 +558,7 @@ int main(int argc, char **argv)
trace_file = trace_opt_parse(optarg);
break;
case 'V':
printf("%s version " QEMU_VERSION QEMU_PKGVERSION "\n"
printf("%s version " QEMU_FULL_VERSION "\n"
QEMU_COPYRIGHT "\n", progname);
exit(0);
case 'h':

View File

@ -130,7 +130,7 @@ QEMU_HELP_BOTTOM "\n"
static void version(const char *name)
{
printf(
"%s " QEMU_VERSION QEMU_PKGVERSION "\n"
"%s " QEMU_FULL_VERSION "\n"
"Written by Anthony Liguori.\n"
"\n"
QEMU_COPYRIGHT "\n"

View File

@ -218,7 +218,7 @@ static void usage(const char *cmd)
{
printf(
"Usage: %s [-m <method> -p <path>] [<options>]\n"
"QEMU Guest Agent " QEMU_VERSION QEMU_PKGVERSION "\n"
"QEMU Guest Agent " QEMU_FULL_VERSION "\n"
QEMU_COPYRIGHT "\n"
"\n"
" -m, --method transport method: one of unix-listen, virtio-serial,\n"

View File

@ -891,6 +891,19 @@ GSList *object_class_get_list(const char *implements_type,
return list;
}
static gint object_class_cmp(gconstpointer a, gconstpointer b)
{
return strcasecmp(object_class_get_name((ObjectClass *)a),
object_class_get_name((ObjectClass *)b));
}
GSList *object_class_get_list_sorted(const char *implements_type,
bool include_abstract)
{
return g_slist_sort(object_class_get_list(implements_type, include_abstract),
object_class_cmp);
}
void object_ref(Object *obj)
{
if (!obj) {

View File

@ -19,20 +19,17 @@
void replay_audio_out(int *played)
{
if (replay_mode == REPLAY_MODE_RECORD) {
g_assert(replay_mutex_locked());
replay_save_instructions();
replay_mutex_lock();
replay_put_event(EVENT_AUDIO_OUT);
replay_put_dword(*played);
replay_mutex_unlock();
} else if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked());
replay_account_executed_instructions();
replay_mutex_lock();
if (replay_next_event_is(EVENT_AUDIO_OUT)) {
*played = replay_get_dword();
replay_finish_event();
replay_mutex_unlock();
} else {
replay_mutex_unlock();
error_report("Missing audio out event in the replay log");
abort();
}
@ -44,8 +41,8 @@ void replay_audio_in(int *recorded, void *samples, int *wpos, int size)
int pos;
uint64_t left, right;
if (replay_mode == REPLAY_MODE_RECORD) {
g_assert(replay_mutex_locked());
replay_save_instructions();
replay_mutex_lock();
replay_put_event(EVENT_AUDIO_IN);
replay_put_dword(*recorded);
replay_put_dword(*wpos);
@ -55,10 +52,9 @@ void replay_audio_in(int *recorded, void *samples, int *wpos, int size)
replay_put_qword(left);
replay_put_qword(right);
}
replay_mutex_unlock();
} else if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked());
replay_account_executed_instructions();
replay_mutex_lock();
if (replay_next_event_is(EVENT_AUDIO_IN)) {
*recorded = replay_get_dword();
*wpos = replay_get_dword();
@ -69,9 +65,7 @@ void replay_audio_in(int *recorded, void *samples, int *wpos, int size)
audio_sample_from_uint64(samples, pos, left, right);
}
replay_finish_event();
replay_mutex_unlock();
} else {
replay_mutex_unlock();
error_report("Missing audio in event in the replay log");
abort();
}

View File

@ -96,25 +96,24 @@ void *replay_event_char_read_load(void)
void replay_char_write_event_save(int res, int offset)
{
g_assert(replay_mutex_locked());
replay_save_instructions();
replay_mutex_lock();
replay_put_event(EVENT_CHAR_WRITE);
replay_put_dword(res);
replay_put_dword(offset);
replay_mutex_unlock();
}
void replay_char_write_event_load(int *res, int *offset)
{
g_assert(replay_mutex_locked());
replay_account_executed_instructions();
replay_mutex_lock();
if (replay_next_event_is(EVENT_CHAR_WRITE)) {
*res = replay_get_dword();
*offset = replay_get_dword();
replay_finish_event();
replay_mutex_unlock();
} else {
replay_mutex_unlock();
error_report("Missing character write event in the replay log");
exit(1);
}
@ -122,23 +121,21 @@ void replay_char_write_event_load(int *res, int *offset)
int replay_char_read_all_load(uint8_t *buf)
{
replay_mutex_lock();
g_assert(replay_mutex_locked());
if (replay_next_event_is(EVENT_CHAR_READ_ALL)) {
size_t size;
int res;
replay_get_array(buf, &size);
replay_finish_event();
replay_mutex_unlock();
res = (int)size;
assert(res >= 0);
return res;
} else if (replay_next_event_is(EVENT_CHAR_READ_ALL_ERROR)) {
int res = replay_get_dword();
replay_finish_event();
replay_mutex_unlock();
return res;
} else {
replay_mutex_unlock();
error_report("Missing character read all event in the replay log");
exit(1);
}
@ -146,19 +143,17 @@ int replay_char_read_all_load(uint8_t *buf)
void replay_char_read_all_save_error(int res)
{
g_assert(replay_mutex_locked());
assert(res < 0);
replay_save_instructions();
replay_mutex_lock();
replay_put_event(EVENT_CHAR_READ_ALL_ERROR);
replay_put_dword(res);
replay_mutex_unlock();
}
void replay_char_read_all_save_buf(uint8_t *buf, int offset)
{
g_assert(replay_mutex_locked());
replay_save_instructions();
replay_mutex_lock();
replay_put_event(EVENT_CHAR_READ_ALL);
replay_put_array(buf, offset);
replay_mutex_unlock();
}

View File

@ -27,10 +27,6 @@ typedef struct Event {
} Event;
static QTAILQ_HEAD(, Event) events_list = QTAILQ_HEAD_INITIALIZER(events_list);
static unsigned int read_event_kind = -1;
static uint64_t read_id = -1;
static int read_checkpoint = -1;
static bool events_enabled;
/* Functions */
@ -67,7 +63,9 @@ static void replay_run_event(Event *event)
void replay_enable_events(void)
{
events_enabled = true;
if (replay_mode != REPLAY_MODE_NONE) {
events_enabled = true;
}
}
bool replay_has_events(void)
@ -77,16 +75,14 @@ bool replay_has_events(void)
void replay_flush_events(void)
{
replay_mutex_lock();
g_assert(replay_mutex_locked());
while (!QTAILQ_EMPTY(&events_list)) {
Event *event = QTAILQ_FIRST(&events_list);
replay_mutex_unlock();
replay_run_event(event);
replay_mutex_lock();
QTAILQ_REMOVE(&events_list, event, events);
g_free(event);
}
replay_mutex_unlock();
}
void replay_disable_events(void)
@ -100,14 +96,14 @@ void replay_disable_events(void)
void replay_clear_events(void)
{
replay_mutex_lock();
g_assert(replay_mutex_locked());
while (!QTAILQ_EMPTY(&events_list)) {
Event *event = QTAILQ_FIRST(&events_list);
QTAILQ_REMOVE(&events_list, event, events);
g_free(event);
}
replay_mutex_unlock();
}
/*! Adds specified async event to the queue */
@ -134,14 +130,13 @@ void replay_add_event(ReplayAsyncEventKind event_kind,
event->opaque2 = opaque2;
event->id = id;
replay_mutex_lock();
g_assert(replay_mutex_locked());
QTAILQ_INSERT_TAIL(&events_list, event, events);
replay_mutex_unlock();
}
void replay_bh_schedule_event(QEMUBH *bh)
{
if (replay_mode != REPLAY_MODE_NONE && events_enabled) {
if (events_enabled) {
uint64_t id = replay_get_current_step();
replay_add_event(REPLAY_ASYNC_EVENT_BH, bh, NULL, id);
} else {
@ -161,7 +156,7 @@ void replay_add_input_sync_event(void)
void replay_block_event(QEMUBH *bh, uint64_t id)
{
if (replay_mode != REPLAY_MODE_NONE && events_enabled) {
if (events_enabled) {
replay_add_event(REPLAY_ASYNC_EVENT_BLOCK, bh, NULL, id);
} else {
qemu_bh_schedule(bh);
@ -205,13 +200,12 @@ static void replay_save_event(Event *event, int checkpoint)
/* Called with replay mutex locked */
void replay_save_events(int checkpoint)
{
g_assert(replay_mutex_locked());
g_assert(checkpoint != CHECKPOINT_CLOCK_WARP_START);
while (!QTAILQ_EMPTY(&events_list)) {
Event *event = QTAILQ_FIRST(&events_list);
replay_save_event(event, checkpoint);
replay_mutex_unlock();
replay_run_event(event);
replay_mutex_lock();
QTAILQ_REMOVE(&events_list, event, events);
g_free(event);
}
@ -220,58 +214,60 @@ void replay_save_events(int checkpoint)
static Event *replay_read_event(int checkpoint)
{
Event *event;
if (read_event_kind == -1) {
read_checkpoint = replay_get_byte();
read_event_kind = replay_get_byte();
read_id = -1;
if (replay_state.read_event_kind == -1) {
replay_state.read_event_checkpoint = replay_get_byte();
replay_state.read_event_kind = replay_get_byte();
replay_state.read_event_id = -1;
replay_check_error();
}
if (checkpoint != read_checkpoint) {
if (checkpoint != replay_state.read_event_checkpoint) {
return NULL;
}
/* Events that has not to be in the queue */
switch (read_event_kind) {
switch (replay_state.read_event_kind) {
case REPLAY_ASYNC_EVENT_BH:
if (read_id == -1) {
read_id = replay_get_qword();
if (replay_state.read_event_id == -1) {
replay_state.read_event_id = replay_get_qword();
}
break;
case REPLAY_ASYNC_EVENT_INPUT:
event = g_malloc0(sizeof(Event));
event->event_kind = read_event_kind;
event->event_kind = replay_state.read_event_kind;
event->opaque = replay_read_input_event();
return event;
case REPLAY_ASYNC_EVENT_INPUT_SYNC:
event = g_malloc0(sizeof(Event));
event->event_kind = read_event_kind;
event->event_kind = replay_state.read_event_kind;
event->opaque = 0;
return event;
case REPLAY_ASYNC_EVENT_CHAR_READ:
event = g_malloc0(sizeof(Event));
event->event_kind = read_event_kind;
event->event_kind = replay_state.read_event_kind;
event->opaque = replay_event_char_read_load();
return event;
case REPLAY_ASYNC_EVENT_BLOCK:
if (read_id == -1) {
read_id = replay_get_qword();
if (replay_state.read_event_id == -1) {
replay_state.read_event_id = replay_get_qword();
}
break;
case REPLAY_ASYNC_EVENT_NET:
event = g_malloc0(sizeof(Event));
event->event_kind = read_event_kind;
event->event_kind = replay_state.read_event_kind;
event->opaque = replay_event_net_load();
return event;
default:
error_report("Unknown ID %d of replay event", read_event_kind);
error_report("Unknown ID %d of replay event",
replay_state.read_event_kind);
exit(1);
break;
}
QTAILQ_FOREACH(event, &events_list, events) {
if (event->event_kind == read_event_kind
&& (read_id == -1 || read_id == event->id)) {
if (event->event_kind == replay_state.read_event_kind
&& (replay_state.read_event_id == -1
|| replay_state.read_event_id == event->id)) {
break;
}
}
@ -290,24 +286,23 @@ static Event *replay_read_event(int checkpoint)
/* Called with replay mutex locked */
void replay_read_events(int checkpoint)
{
g_assert(replay_mutex_locked());
while (replay_state.data_kind == EVENT_ASYNC) {
Event *event = replay_read_event(checkpoint);
if (!event) {
break;
}
replay_mutex_unlock();
replay_finish_event();
replay_state.read_event_kind = -1;
replay_run_event(event);
replay_mutex_lock();
g_free(event);
replay_finish_event();
read_event_kind = -1;
}
}
void replay_init_events(void)
{
read_event_kind = -1;
replay_state.read_event_kind = -1;
}
void replay_finish_events(void)

View File

@ -24,12 +24,23 @@
static QemuMutex lock;
/* File for replay writing */
static bool write_error;
FILE *replay_file;
static void replay_write_error(void)
{
if (!write_error) {
error_report("replay write error");
write_error = true;
}
}
void replay_put_byte(uint8_t byte)
{
if (replay_file) {
putc(byte, replay_file);
if (putc(byte, replay_file) == EOF) {
replay_write_error();
}
}
}
@ -62,7 +73,9 @@ void replay_put_array(const uint8_t *buf, size_t size)
{
if (replay_file) {
replay_put_dword(size);
fwrite(buf, 1, size, replay_file);
if (fwrite(buf, 1, size, replay_file) != size) {
replay_write_error();
}
}
}
@ -169,31 +182,46 @@ void replay_finish_event(void)
replay_fetch_data_kind();
}
static __thread bool replay_locked;
void replay_mutex_init(void)
{
qemu_mutex_init(&lock);
/* Hold the mutex while we start-up */
qemu_mutex_lock(&lock);
replay_locked = true;
}
void replay_mutex_destroy(void)
bool replay_mutex_locked(void)
{
qemu_mutex_destroy(&lock);
return replay_locked;
}
/* Ordering constraints, replay_lock must be taken before BQL */
void replay_mutex_lock(void)
{
qemu_mutex_lock(&lock);
if (replay_mode != REPLAY_MODE_NONE) {
g_assert(!qemu_mutex_iothread_locked());
g_assert(!replay_mutex_locked());
qemu_mutex_lock(&lock);
replay_locked = true;
}
}
void replay_mutex_unlock(void)
{
qemu_mutex_unlock(&lock);
if (replay_mode != REPLAY_MODE_NONE) {
g_assert(replay_mutex_locked());
replay_locked = false;
qemu_mutex_unlock(&lock);
}
}
/*! Saves cached instructions. */
void replay_save_instructions(void)
{
if (replay_file && replay_mode == REPLAY_MODE_RECORD) {
replay_mutex_lock();
g_assert(replay_mutex_locked());
int diff = (int)(replay_get_current_step() - replay_state.current_step);
/* Time can only go forward */
@ -204,6 +232,5 @@ void replay_save_instructions(void)
replay_put_dword(diff);
replay_state.current_step += diff;
}
replay_mutex_unlock();
}
}

View File

@ -12,7 +12,7 @@
*
*/
/* Any changes to order/number of events will need to bump REPLAY_VERSION */
enum ReplayEvents {
/* for instruction event */
EVENT_INSTRUCTION,
@ -78,6 +78,14 @@ typedef struct ReplayState {
This counter is global, because requests from different
block devices should not get overlapping ids. */
uint64_t block_request_id;
/*! Prior value of the host clock */
uint64_t host_clock_last;
/*! Asynchronous event type read from the log */
int32_t read_event_kind;
/*! Asynchronous event id read from the log */
uint64_t read_event_id;
/*! Asynchronous event checkpoint id read from the log */
int32_t read_event_checkpoint;
} ReplayState;
extern ReplayState replay_state;
@ -98,12 +106,11 @@ int64_t replay_get_qword(void);
void replay_get_array(uint8_t *buf, size_t *size);
void replay_get_array_alloc(uint8_t **buf, size_t *size);
/* Mutex functions for protecting replay log file */
/* Mutex functions for protecting replay log file and ensuring
* synchronisation between vCPU and main-loop threads. */
void replay_mutex_init(void);
void replay_mutex_destroy(void);
void replay_mutex_lock(void);
void replay_mutex_unlock(void);
bool replay_mutex_locked(void);
/*! Checks error status of the file. */
void replay_check_error(void);

View File

@ -25,6 +25,7 @@ static int replay_pre_save(void *opaque)
{
ReplayState *state = opaque;
state->file_offset = ftell(replay_file);
state->host_clock_last = qemu_clock_get_last(QEMU_CLOCK_HOST);
return 0;
}
@ -33,6 +34,7 @@ static int replay_post_load(void *opaque, int version_id)
{
ReplayState *state = opaque;
fseek(replay_file, state->file_offset, SEEK_SET);
qemu_clock_set_last(QEMU_CLOCK_HOST, state->host_clock_last);
/* If this was a vmstate, saved in recording mode,
we need to initialize replay data fields. */
replay_fetch_data_kind();
@ -54,6 +56,10 @@ static const VMStateDescription vmstate_replay = {
VMSTATE_UINT32(has_unread_data, ReplayState),
VMSTATE_UINT64(file_offset, ReplayState),
VMSTATE_UINT64(block_request_id, ReplayState),
VMSTATE_UINT64(host_clock_last, ReplayState),
VMSTATE_INT32(read_event_kind, ReplayState),
VMSTATE_UINT64(read_event_id, ReplayState),
VMSTATE_INT32(read_event_checkpoint, ReplayState),
VMSTATE_END_OF_LIST()
},
};
@ -83,3 +89,9 @@ void replay_vmstate_init(void)
}
}
}
bool replay_can_snapshot(void)
{
return replay_mode == REPLAY_MODE_NONE
|| !replay_has_events();
}

View File

@ -17,13 +17,13 @@
int64_t replay_save_clock(ReplayClockKind kind, int64_t clock)
{
replay_save_instructions();
if (replay_file) {
replay_mutex_lock();
g_assert(replay_mutex_locked());
replay_save_instructions();
replay_put_event(EVENT_CLOCK + kind);
replay_put_qword(clock);
replay_mutex_unlock();
}
return clock;
@ -46,16 +46,16 @@ void replay_read_next_clock(ReplayClockKind kind)
/*! Reads next clock event from the input. */
int64_t replay_read_clock(ReplayClockKind kind)
{
g_assert(replay_file && replay_mutex_locked());
replay_account_executed_instructions();
if (replay_file) {
int64_t ret;
replay_mutex_lock();
if (replay_next_event_is(EVENT_CLOCK + kind)) {
replay_read_next_clock(kind);
}
ret = replay_state.cached_clock[kind];
replay_mutex_unlock();
return ret;
}

View File

@ -22,7 +22,7 @@
/* Current version of the replay mechanism.
Increase it when file format changes. */
#define REPLAY_VERSION 0xe02006
#define REPLAY_VERSION 0xe02007
/* Size of replay log header */
#define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t))
@ -81,7 +81,7 @@ int replay_get_instructions(void)
void replay_account_executed_instructions(void)
{
if (replay_mode == REPLAY_MODE_PLAY) {
replay_mutex_lock();
g_assert(replay_mutex_locked());
if (replay_state.instructions_count > 0) {
int count = (int)(replay_get_current_step()
- replay_state.current_step);
@ -100,24 +100,22 @@ void replay_account_executed_instructions(void)
qemu_notify_event();
}
}
replay_mutex_unlock();
}
}
bool replay_exception(void)
{
if (replay_mode == REPLAY_MODE_RECORD) {
g_assert(replay_mutex_locked());
replay_save_instructions();
replay_mutex_lock();
replay_put_event(EVENT_EXCEPTION);
replay_mutex_unlock();
return true;
} else if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked());
bool res = replay_has_exception();
if (res) {
replay_mutex_lock();
replay_finish_event();
replay_mutex_unlock();
}
return res;
}
@ -129,10 +127,9 @@ bool replay_has_exception(void)
{
bool res = false;
if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked());
replay_account_executed_instructions();
replay_mutex_lock();
res = replay_next_event_is(EVENT_EXCEPTION);
replay_mutex_unlock();
}
return res;
@ -141,17 +138,15 @@ bool replay_has_exception(void)
bool replay_interrupt(void)
{
if (replay_mode == REPLAY_MODE_RECORD) {
g_assert(replay_mutex_locked());
replay_save_instructions();
replay_mutex_lock();
replay_put_event(EVENT_INTERRUPT);
replay_mutex_unlock();
return true;
} else if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked());
bool res = replay_has_interrupt();
if (res) {
replay_mutex_lock();
replay_finish_event();
replay_mutex_unlock();
}
return res;
}
@ -163,10 +158,9 @@ bool replay_has_interrupt(void)
{
bool res = false;
if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked());
replay_account_executed_instructions();
replay_mutex_lock();
res = replay_next_event_is(EVENT_INTERRUPT);
replay_mutex_unlock();
}
return res;
}
@ -174,25 +168,35 @@ bool replay_has_interrupt(void)
void replay_shutdown_request(ShutdownCause cause)
{
if (replay_mode == REPLAY_MODE_RECORD) {
replay_mutex_lock();
g_assert(replay_mutex_locked());
replay_put_event(EVENT_SHUTDOWN + cause);
replay_mutex_unlock();
}
}
bool replay_checkpoint(ReplayCheckpoint checkpoint)
{
bool res = false;
static bool in_checkpoint;
assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
replay_save_instructions();
if (!replay_file) {
return true;
}
replay_mutex_lock();
if (in_checkpoint) {
/* If we are already in checkpoint, then there is no need
for additional synchronization.
Recursion occurs when HW event modifies timers.
Timer modification may invoke the checkpoint and
proceed to recursion. */
return true;
}
in_checkpoint = true;
replay_save_instructions();
if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked());
if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
replay_finish_event();
} else if (replay_state.data_kind != EVENT_ASYNC) {
@ -205,12 +209,18 @@ bool replay_checkpoint(ReplayCheckpoint checkpoint)
checkpoint were processed */
res = replay_state.data_kind != EVENT_ASYNC;
} else if (replay_mode == REPLAY_MODE_RECORD) {
g_assert(replay_mutex_locked());
replay_put_event(EVENT_CHECKPOINT + checkpoint);
replay_save_events(checkpoint);
/* This checkpoint belongs to several threads.
Processing events from different threads is
non-deterministic */
if (checkpoint != CHECKPOINT_CLOCK_WARP_START) {
replay_save_events(checkpoint);
}
res = true;
}
out:
replay_mutex_unlock();
in_checkpoint = false;
return res;
}
@ -233,8 +243,6 @@ static void replay_enable(const char *fname, int mode)
atexit(replay_finish);
replay_mutex_init();
replay_file = fopen(fname, fmode);
if (replay_file == NULL) {
fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
@ -242,8 +250,9 @@ static void replay_enable(const char *fname, int mode)
}
replay_filename = g_strdup(fname);
replay_mode = mode;
replay_mutex_init();
replay_state.data_kind = -1;
replay_state.instructions_count = 0;
replay_state.current_step = 0;
@ -358,7 +367,6 @@ void replay_finish(void)
replay_snapshot = NULL;
replay_finish_events();
replay_mutex_destroy();
}
void replay_add_blocker(Error *reason)

View File

@ -1447,9 +1447,10 @@ sub process {
# check we are in a valid source file if not then ignore this hunk
next if ($realfile !~ /$SrcFile/);
#90 column limit
#90 column limit; exempt URLs, if no other words on line
if ($line =~ /^\+/ &&
!($line =~ /^\+\s*"[^"]*"\s*(?:\s*|,|\)\s*;)\s*$/) &&
!($rawline =~ /^[^[:alnum:]]*https?:\S*$/) &&
$length > 80)
{
if ($length > 90) {

308
scripts/replay-dump.py Executable file
View File

@ -0,0 +1,308 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Dump the contents of a recorded execution stream
#
# Copyright (c) 2017 Alex Bennée <alex.bennee@linaro.org>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, see <http://www.gnu.org/licenses/>.
import argparse
import struct
from collections import namedtuple
# This mirrors some of the global replay state which some of the
# stream loading refers to. Some decoders may read the next event so
# we need handle that case. Calling reuse_event will ensure the next
# event is read from the cache rather than advancing the file.
class ReplayState(object):
def __init__(self):
self.event = -1
self.event_count = 0
self.already_read = False
self.current_checkpoint = 0
self.checkpoint = 0
def set_event(self, ev):
self.event = ev
self.event_count += 1
def get_event(self):
self.already_read = False
return self.event
def reuse_event(self, ev):
self.event = ev
self.already_read = True
def set_checkpoint(self):
self.checkpoint = self.event - self.checkpoint_start
def get_checkpoint(self):
return self.checkpoint
replay_state = ReplayState()
# Simple read functions that mirror replay-internal.c
# The file-stream is big-endian and manually written out a byte at a time.
def read_byte(fin):
"Read a single byte"
return struct.unpack('>B', fin.read(1))[0]
def read_event(fin):
"Read a single byte event, but save some state"
if replay_state.already_read:
return replay_state.get_event()
else:
replay_state.set_event(read_byte(fin))
return replay_state.event
def read_word(fin):
"Read a 16 bit word"
return struct.unpack('>H', fin.read(2))[0]
def read_dword(fin):
"Read a 32 bit word"
return struct.unpack('>I', fin.read(4))[0]
def read_qword(fin):
"Read a 64 bit word"
return struct.unpack('>Q', fin.read(8))[0]
# Generic decoder structure
Decoder = namedtuple("Decoder", "eid name fn")
def call_decode(table, index, dumpfile):
"Search decode table for next step"
decoder = next((d for d in table if d.eid == index), None)
if not decoder:
print "Could not decode index: %d" % (index)
print "Entry is: %s" % (decoder)
print "Decode Table is:\n%s" % (table)
return False
else:
return decoder.fn(decoder.eid, decoder.name, dumpfile)
# Print event
def print_event(eid, name, string=None, event_count=None):
"Print event with count"
if not event_count:
event_count = replay_state.event_count
if string:
print "%d:%s(%d) %s" % (event_count, name, eid, string)
else:
print "%d:%s(%d)" % (event_count, name, eid)
# Decoders for each event type
def decode_unimp(eid, name, _unused_dumpfile):
"Unimplimented decoder, will trigger exit"
print "%s not handled - will now stop" % (name)
return False
# Checkpoint decoder
def swallow_async_qword(eid, name, dumpfile):
"Swallow a qword of data without looking at it"
step_id = read_qword(dumpfile)
print " %s(%d) @ %d" % (name, eid, step_id)
return True
async_decode_table = [ Decoder(0, "REPLAY_ASYNC_EVENT_BH", swallow_async_qword),
Decoder(1, "REPLAY_ASYNC_INPUT", decode_unimp),
Decoder(2, "REPLAY_ASYNC_INPUT_SYNC", decode_unimp),
Decoder(3, "REPLAY_ASYNC_CHAR_READ", decode_unimp),
Decoder(4, "REPLAY_ASYNC_EVENT_BLOCK", decode_unimp),
Decoder(5, "REPLAY_ASYNC_EVENT_NET", decode_unimp),
]
# See replay_read_events/replay_read_event
def decode_async(eid, name, dumpfile):
"""Decode an ASYNC event"""
print_event(eid, name)
async_event_kind = read_byte(dumpfile)
async_event_checkpoint = read_byte(dumpfile)
if async_event_checkpoint != replay_state.current_checkpoint:
print " mismatch between checkpoint %d and async data %d" % (
replay_state.current_checkpoint, async_event_checkpoint)
return True
return call_decode(async_decode_table, async_event_kind, dumpfile)
def decode_instruction(eid, name, dumpfile):
ins_diff = read_dword(dumpfile)
print_event(eid, name, "0x%x" % (ins_diff))
return True
def decode_audio_out(eid, name, dumpfile):
audio_data = read_dword(dumpfile)
print_event(eid, name, "%d" % (audio_data))
return True
def decode_checkpoint(eid, name, dumpfile):
"""Decode a checkpoint.
Checkpoints contain a series of async events with their own specific data.
"""
replay_state.set_checkpoint()
# save event count as we peek ahead
event_number = replay_state.event_count
next_event = read_event(dumpfile)
# if the next event is EVENT_ASYNC there are a bunch of
# async events to read, otherwise we are done
if next_event != 3:
print_event(eid, name, "no additional data", event_number)
else:
print_event(eid, name, "more data follows", event_number)
replay_state.reuse_event(next_event)
return True
def decode_checkpoint_init(eid, name, dumpfile):
print_event(eid, name)
return True
def decode_interrupt(eid, name, dumpfile):
print_event(eid, name)
return True
def decode_clock(eid, name, dumpfile):
clock_data = read_qword(dumpfile)
print_event(eid, name, "0x%x" % (clock_data))
return True
# pre-MTTCG merge
v5_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction),
Decoder(1, "EVENT_INTERRUPT", decode_interrupt),
Decoder(2, "EVENT_EXCEPTION", decode_unimp),
Decoder(3, "EVENT_ASYNC", decode_async),
Decoder(4, "EVENT_SHUTDOWN", decode_unimp),
Decoder(5, "EVENT_CHAR_WRITE", decode_unimp),
Decoder(6, "EVENT_CHAR_READ_ALL", decode_unimp),
Decoder(7, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp),
Decoder(8, "EVENT_CLOCK_HOST", decode_clock),
Decoder(9, "EVENT_CLOCK_VIRTUAL_RT", decode_clock),
Decoder(10, "EVENT_CP_CLOCK_WARP_START", decode_checkpoint),
Decoder(11, "EVENT_CP_CLOCK_WARP_ACCOUNT", decode_checkpoint),
Decoder(12, "EVENT_CP_RESET_REQUESTED", decode_checkpoint),
Decoder(13, "EVENT_CP_SUSPEND_REQUESTED", decode_checkpoint),
Decoder(14, "EVENT_CP_CLOCK_VIRTUAL", decode_checkpoint),
Decoder(15, "EVENT_CP_CLOCK_HOST", decode_checkpoint),
Decoder(16, "EVENT_CP_CLOCK_VIRTUAL_RT", decode_checkpoint),
Decoder(17, "EVENT_CP_INIT", decode_checkpoint_init),
Decoder(18, "EVENT_CP_RESET", decode_checkpoint),
]
# post-MTTCG merge, AUDIO support added
v6_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction),
Decoder(1, "EVENT_INTERRUPT", decode_interrupt),
Decoder(2, "EVENT_EXCEPTION", decode_unimp),
Decoder(3, "EVENT_ASYNC", decode_async),
Decoder(4, "EVENT_SHUTDOWN", decode_unimp),
Decoder(5, "EVENT_CHAR_WRITE", decode_unimp),
Decoder(6, "EVENT_CHAR_READ_ALL", decode_unimp),
Decoder(7, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp),
Decoder(8, "EVENT_AUDIO_OUT", decode_audio_out),
Decoder(9, "EVENT_AUDIO_IN", decode_unimp),
Decoder(10, "EVENT_CLOCK_HOST", decode_clock),
Decoder(11, "EVENT_CLOCK_VIRTUAL_RT", decode_clock),
Decoder(12, "EVENT_CP_CLOCK_WARP_START", decode_checkpoint),
Decoder(13, "EVENT_CP_CLOCK_WARP_ACCOUNT", decode_checkpoint),
Decoder(14, "EVENT_CP_RESET_REQUESTED", decode_checkpoint),
Decoder(15, "EVENT_CP_SUSPEND_REQUESTED", decode_checkpoint),
Decoder(16, "EVENT_CP_CLOCK_VIRTUAL", decode_checkpoint),
Decoder(17, "EVENT_CP_CLOCK_HOST", decode_checkpoint),
Decoder(18, "EVENT_CP_CLOCK_VIRTUAL_RT", decode_checkpoint),
Decoder(19, "EVENT_CP_INIT", decode_checkpoint_init),
Decoder(20, "EVENT_CP_RESET", decode_checkpoint),
]
# Shutdown cause added
v7_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction),
Decoder(1, "EVENT_INTERRUPT", decode_interrupt),
Decoder(2, "EVENT_EXCEPTION", decode_unimp),
Decoder(3, "EVENT_ASYNC", decode_async),
Decoder(4, "EVENT_SHUTDOWN", decode_unimp),
Decoder(5, "EVENT_SHUTDOWN_HOST_ERR", decode_unimp),
Decoder(6, "EVENT_SHUTDOWN_HOST_QMP", decode_unimp),
Decoder(7, "EVENT_SHUTDOWN_HOST_SIGNAL", decode_unimp),
Decoder(8, "EVENT_SHUTDOWN_HOST_UI", decode_unimp),
Decoder(9, "EVENT_SHUTDOWN_GUEST_SHUTDOWN", decode_unimp),
Decoder(10, "EVENT_SHUTDOWN_GUEST_RESET", decode_unimp),
Decoder(11, "EVENT_SHUTDOWN_GUEST_PANIC", decode_unimp),
Decoder(12, "EVENT_SHUTDOWN___MAX", decode_unimp),
Decoder(13, "EVENT_CHAR_WRITE", decode_unimp),
Decoder(14, "EVENT_CHAR_READ_ALL", decode_unimp),
Decoder(15, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp),
Decoder(16, "EVENT_AUDIO_OUT", decode_audio_out),
Decoder(17, "EVENT_AUDIO_IN", decode_unimp),
Decoder(18, "EVENT_CLOCK_HOST", decode_clock),
Decoder(19, "EVENT_CLOCK_VIRTUAL_RT", decode_clock),
Decoder(20, "EVENT_CP_CLOCK_WARP_START", decode_checkpoint),
Decoder(21, "EVENT_CP_CLOCK_WARP_ACCOUNT", decode_checkpoint),
Decoder(22, "EVENT_CP_RESET_REQUESTED", decode_checkpoint),
Decoder(23, "EVENT_CP_SUSPEND_REQUESTED", decode_checkpoint),
Decoder(24, "EVENT_CP_CLOCK_VIRTUAL", decode_checkpoint),
Decoder(25, "EVENT_CP_CLOCK_HOST", decode_checkpoint),
Decoder(26, "EVENT_CP_CLOCK_VIRTUAL_RT", decode_checkpoint),
Decoder(27, "EVENT_CP_INIT", decode_checkpoint_init),
Decoder(28, "EVENT_CP_RESET", decode_checkpoint),
]
def parse_arguments():
"Grab arguments for script"
parser = argparse.ArgumentParser()
parser.add_argument("-f", "--file", help='record/replay dump to read from',
required=True)
return parser.parse_args()
def decode_file(filename):
"Decode a record/replay dump"
dumpfile = open(filename, "rb")
# read and throwaway the header
version = read_dword(dumpfile)
junk = read_qword(dumpfile)
print "HEADER: version 0x%x" % (version)
if version == 0xe02007:
event_decode_table = v7_event_table
replay_state.checkpoint_start = 12
elif version == 0xe02006:
event_decode_table = v6_event_table
replay_state.checkpoint_start = 12
else:
event_decode_table = v5_event_table
replay_state.checkpoint_start = 10
try:
decode_ok = True
while decode_ok:
event = read_event(dumpfile)
decode_ok = call_decode(event_decode_table, event, dumpfile)
finally:
dumpfile.close()
if __name__ == "__main__":
args = parse_arguments()
decode_file(args.file)

View File

@ -102,7 +102,7 @@ QEMU_HELP_BOTTOM "\n"
static void version(const char *name)
{
printf(
"%s " QEMU_VERSION QEMU_PKGVERSION "\n"
"%s " QEMU_FULL_VERSION "\n"
"Written by Paolo Bonzini.\n"
"\n"
QEMU_COPYRIGHT "\n"

Some files were not shown because too many files have changed in this diff Show More