diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2018-03-16 11:05:03 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2018-03-16 11:05:03 +0000 |
commit | 3788c7b6e56fa34ee2a73e41706eb2a2447ba75a (patch) | |
tree | 8f016e7c9175686b4d7c2d1847c8cc877102dc6b | |
parent | a57946ff2acb9c0d95c9f127914540586b0b8c21 (diff) | |
parent | 0790f86861079b1932679d0f011e431aaf4ee9e2 (diff) | |
download | qemu-3788c7b6e56fa34ee2a73e41706eb2a2447ba75a.zip qemu-3788c7b6e56fa34ee2a73e41706eb2a2447ba75a.tar.gz qemu-3788c7b6e56fa34ee2a73e41706eb2a2447ba75a.tar.bz2 |
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
117 files changed, 1953 insertions, 729 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 354a18c..216d01e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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* @@ -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' diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 280200f..9cc6972 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -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; } diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 67795cd..5ad1b91 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -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. diff --git a/bsd-user/main.c b/bsd-user/main.c index efef5ff..05aa559 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -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" diff --git a/chardev/char-mux.c b/chardev/char-mux.c index d48e781..1b925c8 100644 --- a/chardev/char-mux.c +++ b/chardev/char-mux.c @@ -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 = { diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 0c8d6d4..d92c5ae 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -41,6 +41,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 */ QIOChannelSocket *sioc; /* Client master 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, diff --git a/chardev/char.c b/chardev/char.c index f7e0d37..76d866e 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -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); @@ -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 @@ -5139,6 +5144,37 @@ if compile_prog "" "" ; then 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 cat > $TMPC << EOF @@ -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 @@ -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) diff --git a/default-configs/alpha-softmmu.mak b/default-configs/alpha-softmmu.mak index e0d75e3..bbe361f 100644 --- a/default-configs/alpha-softmmu.mak +++ b/default-configs/alpha-softmmu.mak @@ -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 diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 8973579..8c7d4a0 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -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 diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index 5e27a7a..0390b43 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -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 diff --git a/docs/devel/atomics.txt b/docs/devel/atomics.txt index 10c5fa3..a4db3a4 100644 --- a/docs/devel/atomics.txt +++ b/docs/devel/atomics.txt @@ -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 ======= diff --git a/docs/replay.txt b/docs/replay.txt index 486c1e0..2e21e9c 100644 --- a/docs/replay.txt +++ b/docs/replay.txt @@ -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. - -Papers with description of deterministic replay implementation: + 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. + +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. diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index 766373e..80b987f 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -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]; diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 287392b..1742cf6 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -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" diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c index a8ec2cd..f68df56 100644 --- a/hw/arm/msf2-soc.c +++ b/hw/arm/msf2-soc.c @@ -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" diff --git a/hw/arm/realview.c b/hw/arm/realview.c index 87cd1e5..2139a62 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -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" diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c index a55b1a3..7a925fa 100644 --- a/hw/arm/tosa.c +++ b/hw/arm/tosa.c @@ -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" diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index 1bcd37e..1b97910 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -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 diff --git a/hw/char/parallel-isa.c b/hw/char/parallel-isa.c new file mode 100644 index 0000000..639e179 --- /dev/null +++ b/hw/char/parallel-isa.c @@ -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]); + } + } +} diff --git a/hw/char/parallel.c b/hw/char/parallel.c index f79dc76..1542d62 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -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 diff --git a/hw/dma/i82374.c b/hw/dma/i82374.c index 6c0f975..83c87d9 100644 --- a/hw/dma/i82374.c +++ b/hw/dma/i82374.c @@ -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)); } diff --git a/hw/dma/i8257.c b/hw/dma/i8257.c index bd23e89..52675e9 100644 --- a/hw/dma/i8257.c +++ b/hw/dma/i8257.c @@ -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; diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index fd279e7..fa87a14 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -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 diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 35fcb6e..d36bac8 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -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); - - 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); + i8257_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(); diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 8658bcb..729a050 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -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"); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 0c0bc48..9ae9163 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -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); } diff --git a/hw/i386/vmmouse.c b/hw/i386/vmmouse.c index 65ef553..5d2d278 100644 --- a/hw/i386/vmmouse.c +++ b/hw/i386/vmmouse.c @@ -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 */ diff --git a/hw/i386/vmport.c b/hw/i386/vmport.c index 116aa09..3bf8cfe 100644 --- a/hw/i386/vmport.c +++ b/hw/i386/vmport.c @@ -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" diff --git a/hw/ide/ahci-allwinner.c b/hw/ide/ahci-allwinner.c index c3f1604..5397483 100644 --- a/hw/ide/ahci-allwinner.c +++ b/hw/ide/ahci-allwinner.c @@ -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" diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index 65aff51..6bb92d7 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -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" diff --git a/hw/ide/ich.c b/hw/ide/ich.c index c01b24e..134478e 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -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" diff --git a/hw/ide/isa.c b/hw/ide/isa.c index 9fb24fc..028bd61 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -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" diff --git a/hw/ide/microdrive.c b/hw/ide/microdrive.c index 58e4f52..34bb98d 100644 --- a/hw/ide/microdrive.c +++ b/hw/ide/microdrive.c @@ -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" diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index 6f12f45..42fcf13 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -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" diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index c479f82..f17f18e 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -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 { diff --git a/hw/isa/Makefile.objs b/hw/isa/Makefile.objs index fb37c55..83e06f6 100644 --- a/hw/isa/Makefile.objs +++ b/hw/isa/Makefile.objs @@ -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 diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index 0f2e426..63fa77e 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -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]); - } - } -} diff --git a/hw/isa/isa-superio.c b/hw/isa/isa-superio.c new file mode 100644 index 0000000..b95608a --- /dev/null +++ b/hw/isa/isa-superio.c @@ -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) diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c index 48b29e3..5cf6450 100644 --- a/hw/isa/pc87312.c +++ b/hw/isa/pc87312.c @@ -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) diff --git a/hw/isa/smc37c669-superio.c b/hw/isa/smc37c669-superio.c new file mode 100644 index 0000000..aa233c6 --- /dev/null +++ b/hw/isa/smc37c669-superio.c @@ -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) diff --git a/hw/isa/trace-events b/hw/isa/trace-events index a4ab4e3..80ac617 100644 --- a/hw/isa/trace-events +++ b/hw/isa/trace-events @@ -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" diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index 070cc18..cff1946 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -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); } diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c index f68c625..02fb2fd 100644 --- a/hw/mips/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -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); } diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c index b09871a..7223085 100644 --- a/hw/mips/mips_jazz.c +++ b/hw/mips/mips_jazz.c @@ -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++) { diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 6f0deb9..f6513a4 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -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); - 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"); - + i8257_dma_init(isa_bus, 0); 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); + /* 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); + + /* Super I/O: SMS FDC37M817 */ + isa_create_simple(isa_bus, TYPE_FDC37M81X_SUPERIO); /* Network card */ network_init(pci_bus); diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index 830ee77..aeadc4a 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -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) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 2174c25..67a3f72 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -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; } diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 43c15d1..2238f96 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -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); } } diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index a749e25..2f5b6f6 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -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" diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 935493c..10e291c 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -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" diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 096d4d4..5c78503 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -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")); diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 45975c2..64ec285 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -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 = { diff --git a/hw/scsi/mptendian.c b/hw/scsi/mptendian.c index 3415229..8ae39a7 100644 --- a/hw/scsi/mptendian.c +++ b/hw/scsi/mptendian.c @@ -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" diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 1eaeffc..9646743 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -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; diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 49d2559..5b7a48f 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -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. */ diff --git a/hw/sd/core.c b/hw/sd/core.c index 3c6eae6..820345f 100644 --- a/hw/sd/core.c +++ b/hw/sd/core.c @@ -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" diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 0f5804b..6471aca 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -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) { diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index da28ab9..2044a52 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -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) { diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c index ac75eb2..8e61dfc 100644 --- a/hw/tricore/tricore_testboard.c +++ b/hw/tricore/tricore_testboard.c @@ -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" diff --git a/hw/unicore32/puv3.c b/hw/unicore32/puv3.c index db26959..830fe3f 100644 --- a/hw/unicore32/puv3.c +++ b/hw/unicore32/puv3.c @@ -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 */ diff --git a/include/chardev/char-mux.h b/include/chardev/char-mux.h index 8928977..1e13187 100644 --- a/include/chardev/char-mux.h +++ b/include/chardev/char-mux.h @@ -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) diff --git a/include/chardev/char.h b/include/chardev/char.h index ebf1e0b..04de457 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -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, diff --git a/include/hw/char/parallel.h b/include/hw/char/parallel.h new file mode 100644 index 0000000..d6dd62f --- /dev/null +++ b/include/hw/char/parallel.h @@ -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 diff --git a/include/hw/isa/i8257.h b/include/hw/dma/i8257.h index 88a2766..2cab50b 100644 --- a/include/hw/isa/i8257.h +++ b/include/hw/dma/i8257.h @@ -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 diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index bb49165..6598d57 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -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); diff --git a/include/hw/input/i8042.h b/include/hw/input/i8042.h new file mode 100644 index 0000000..f6ff146 --- /dev/null +++ b/include/hw/input/i8042.h @@ -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 */ diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h index 9559340..b9dbab2 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -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 diff --git a/include/hw/isa/pc87312.h b/include/hw/isa/pc87312.h index bf74470..e16263d 100644 --- a/include/hw/isa/pc87312.h +++ b/include/hw/isa/pc87312.h @@ -25,32 +25,22 @@ #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; MemoryRegion io; diff --git a/include/hw/isa/superio.h b/include/hw/isa/superio.h new file mode 100644 index 0000000..f9ba29aa --- /dev/null +++ b/include/hw/isa/superio.h @@ -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 */ diff --git a/include/hw/isa/vt82c686.h b/include/hw/isa/vt82c686.h index 471b5e9..c3c2b6e 100644 --- a/include/hw/isa/vt82c686.h +++ b/include/hw/isa/vt82c686.h @@ -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, diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h index 3b160f8..93fdace 100644 --- a/include/hw/scsi/esp.h +++ b/include/hw/scsi/esp.h @@ -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); diff --git a/include/qemu/rcu.h b/include/qemu/rcu.h index f19413d..22876d1 100644 --- a/include/qemu/rcu.h +++ b/include/qemu/rcu.h @@ -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); diff --git a/include/qemu/sys_membarrier.h b/include/qemu/sys_membarrier.h new file mode 100644 index 0000000..316e3dc --- /dev/null +++ b/include/qemu/sys_membarrier.h @@ -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 diff --git a/include/qemu/timer.h b/include/qemu/timer.h index 3b5a54b..39ea907 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -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 */ diff --git a/include/qom/object.h b/include/qom/object.h index 4f07090..96ce81b 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -914,6 +914,17 @@ 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 * diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h index c0204e6..3ced6bc 100644 --- a/include/sysemu/replay.h +++ b/include/sysemu/replay.h @@ -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 diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 356bfdc..2b42151 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -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); diff --git a/linux-user/main.c b/linux-user/main.c index b5d5e9c..73aafcf 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -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); } diff --git a/migration/savevm.c b/migration/savevm.c index 358c5b5..fbeac65 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -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", diff --git a/qdev-monitor.c b/qdev-monitor.c index b7e3291..61e0300 100644 --- a/qdev-monitor.c +++ b/qdev-monitor.c @@ -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; diff --git a/qemu-doc.texi b/qemu-doc.texi index 39e38c8..f8da9b8 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -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 @@ -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 { @@ -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': @@ -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" @@ -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" diff --git a/qom/object.c b/qom/object.c index 755ad03..6088f55 100644 --- a/qom/object.c +++ b/qom/object.c @@ -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) { diff --git a/replay/replay-audio.c b/replay/replay-audio.c index 3d83743..b113836 100644 --- a/replay/replay-audio.c +++ b/replay/replay-audio.c @@ -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(); } diff --git a/replay/replay-char.c b/replay/replay-char.c index cbf7c04..736cc8c 100755 --- a/replay/replay-char.c +++ b/replay/replay-char.c @@ -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(); } diff --git a/replay/replay-events.c b/replay/replay-events.c index 94a6dcc..707de38 100644 --- a/replay/replay-events.c +++ b/replay/replay-events.c @@ -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) diff --git a/replay/replay-internal.c b/replay/replay-internal.c index fca8514..b077cb5 100644 --- a/replay/replay-internal.c +++ b/replay/replay-internal.c @@ -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(); } } diff --git a/replay/replay-internal.h b/replay/replay-internal.h index 3ebb199..ac4b27b 100644 --- a/replay/replay-internal.h +++ b/replay/replay-internal.h @@ -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); diff --git a/replay/replay-snapshot.c b/replay/replay-snapshot.c index b2e1076..2ab85cf 100644 --- a/replay/replay-snapshot.c +++ b/replay/replay-snapshot.c @@ -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(); +} diff --git a/replay/replay-time.c b/replay/replay-time.c index f70382a..6a7565e 100644 --- a/replay/replay-time.c +++ b/replay/replay-time.c @@ -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; } diff --git a/replay/replay.c b/replay/replay.c index 7a23c62..8228261 100644 --- a/replay/replay.c +++ b/replay/replay.c @@ -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) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index d1fe79b..57daae0 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -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) { diff --git a/scripts/replay-dump.py b/scripts/replay-dump.py new file mode 100755 index 0000000..e274086 --- /dev/null +++ b/scripts/replay-dump.py @@ -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) diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index 9fe615c..3facbba 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -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" diff --git a/stubs/machine-init-done.c b/stubs/machine-init-done.c index 9a0d625..4121f17 100644 --- a/stubs/machine-init-done.c +++ b/stubs/machine-init-done.c @@ -2,6 +2,8 @@ #include "qemu-common.h" #include "sysemu/sysemu.h" +bool machine_init_done = true; + void qemu_add_machine_init_done_notifier(Notifier *notify) { } diff --git a/stubs/replay.c b/stubs/replay.c index 9c8aa48..04279ab 100644 --- a/stubs/replay.c +++ b/stubs/replay.c @@ -72,3 +72,11 @@ uint64_t blkreplay_next_id(void) { return 0; } + +void replay_mutex_lock(void) +{ +} + +void replay_mutex_unlock(void) +{ +} diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 55675ce..b08078e 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -71,18 +71,6 @@ static void alpha_cpu_realizefn(DeviceState *dev, Error **errp) acc->parent_realize(dev, errp); } -/* Sort alphabetically by type name. */ -static gint alpha_cpu_list_compare(gconstpointer a, gconstpointer b) -{ - ObjectClass *class_a = (ObjectClass *)a; - ObjectClass *class_b = (ObjectClass *)b; - const char *name_a, *name_b; - - name_a = object_class_get_name(class_a); - name_b = object_class_get_name(class_b); - return strcmp(name_a, name_b); -} - static void alpha_cpu_list_entry(gpointer data, gpointer user_data) { ObjectClass *oc = data; @@ -100,8 +88,7 @@ void alpha_cpu_list(FILE *f, fprintf_function cpu_fprintf) }; GSList *list; - list = object_class_get_list(TYPE_ALPHA_CPU, false); - list = g_slist_sort(list, alpha_cpu_list_compare); + list = object_class_get_list_sorted(TYPE_ALPHA_CPU, false); (*cpu_fprintf)(f, "Available CPUs:\n"); g_slist_foreach(list, alpha_cpu_list_entry, &s); g_slist_free(list); diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 969f628..c261b6b 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -110,18 +110,6 @@ static void hppa_cpu_realizefn(DeviceState *dev, Error **errp) #endif } -/* Sort hppabetically by type name. */ -static gint hppa_cpu_list_compare(gconstpointer a, gconstpointer b) -{ - ObjectClass *class_a = (ObjectClass *)a; - ObjectClass *class_b = (ObjectClass *)b; - const char *name_a, *name_b; - - name_a = object_class_get_name(class_a); - name_b = object_class_get_name(class_b); - return strcmp(name_a, name_b); -} - static void hppa_cpu_list_entry(gpointer data, gpointer user_data) { ObjectClass *oc = data; @@ -138,8 +126,7 @@ void hppa_cpu_list(FILE *f, fprintf_function cpu_fprintf) }; GSList *list; - list = object_class_get_list(TYPE_HPPA_CPU, false); - list = g_slist_sort(list, hppa_cpu_list_compare); + list = object_class_get_list_sorted(TYPE_HPPA_CPU, false); (*cpu_fprintf)(f, "Available CPUs:\n"); g_slist_foreach(list, hppa_cpu_list_entry, &s); g_slist_free(list); diff --git a/target/lm32/cpu.c b/target/lm32/cpu.c index 96c2499..0003152 100644 --- a/target/lm32/cpu.c +++ b/target/lm32/cpu.c @@ -32,18 +32,6 @@ static void lm32_cpu_set_pc(CPUState *cs, vaddr value) cpu->env.pc = value; } -/* Sort alphabetically by type name. */ -static gint lm32_cpu_list_compare(gconstpointer a, gconstpointer b) -{ - ObjectClass *class_a = (ObjectClass *)a; - ObjectClass *class_b = (ObjectClass *)b; - const char *name_a, *name_b; - - name_a = object_class_get_name(class_a); - name_b = object_class_get_name(class_b); - return strcmp(name_a, name_b); -} - static void lm32_cpu_list_entry(gpointer data, gpointer user_data) { ObjectClass *oc = data; @@ -65,8 +53,7 @@ void lm32_cpu_list(FILE *f, fprintf_function cpu_fprintf) }; GSList *list; - list = object_class_get_list(TYPE_LM32_CPU, false); - list = g_slist_sort(list, lm32_cpu_list_compare); + list = object_class_get_list_sorted(TYPE_LM32_CPU, false); (*cpu_fprintf)(f, "Available CPUs:\n"); g_slist_foreach(list, lm32_cpu_list_entry, &s); g_slist_free(list); diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 6302cfd..541ffc2 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -85,18 +85,6 @@ typedef struct SuperHCPUListState { FILE *file; } SuperHCPUListState; -/* Sort alphabetically by type name. */ -static gint superh_cpu_list_compare(gconstpointer a, gconstpointer b) -{ - ObjectClass *class_a = (ObjectClass *)a; - ObjectClass *class_b = (ObjectClass *)b; - const char *name_a, *name_b; - - name_a = object_class_get_name(class_a); - name_b = object_class_get_name(class_b); - return strcmp(name_a, name_b); -} - static void superh_cpu_list_entry(gpointer data, gpointer user_data) { SuperHCPUListState *s = user_data; @@ -114,8 +102,7 @@ void sh4_cpu_list(FILE *f, fprintf_function cpu_fprintf) }; GSList *list; - list = object_class_get_list(TYPE_SUPERH_CPU, false); - list = g_slist_sort(list, superh_cpu_list_compare); + list = object_class_get_list_sorted(TYPE_SUPERH_CPU, false); g_slist_foreach(list, superh_cpu_list_entry, &s); g_slist_free(list); } diff --git a/target/tricore/helper.c b/target/tricore/helper.c index 45276d3..dad7eea 100644 --- a/target/tricore/helper.c +++ b/target/tricore/helper.c @@ -101,7 +101,7 @@ void tricore_cpu_list(FILE *f, fprintf_function cpu_fprintf) }; GSList *list; - list = object_class_get_list(TYPE_TRICORE_CPU, false); + list = object_class_get_list_sorted(TYPE_TRICORE_CPU, false); (*cpu_fprintf)(f, "Available CPUs:\n"); g_slist_foreach(list, tricore_cpu_list_entry, &s); g_slist_free(list); diff --git a/tests/rcutorture.c b/tests/rcutorture.c index 4002ecf..49311c8 100644 --- a/tests/rcutorture.c +++ b/tests/rcutorture.c @@ -238,7 +238,6 @@ long long rcu_stress_count[RCU_STRESS_PIPE_LEN + 1]; static void *rcu_read_stress_test(void *arg) { int i; - int itercnt = 0; struct rcu_stress *p; int pc; long long n_reads_local = 0; @@ -269,9 +268,6 @@ static void *rcu_read_stress_test(void *arg) } rcu_stress_local[pc]++; n_reads_local++; - if ((++itercnt % 0x1000) == 0) { - synchronize_rcu(); - } } qemu_mutex_lock(&counts_mutex); n_reads += n_reads_local; diff --git a/tests/test-char.c b/tests/test-char.c index dffb354..df939cc 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -166,7 +166,6 @@ static void char_mux_test(void) FeHandler h1 = { 0, }, h2 = { 0, }; CharBackend chr_be1, chr_be2; - muxes_realized = true; /* done after machine init */ opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label", 1, &error_abort); qemu_opt_set(opts, "backend", "ringbuf", &error_abort); diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c index 7393d69..037872b 100644 --- a/tests/virtio-scsi-test.c +++ b/tests/virtio-scsi-test.c @@ -216,6 +216,9 @@ static void test_unaligned_write_same(void) const uint8_t write_same_cdb_2[VIRTIO_SCSI_CDB_SIZE] = { 0x41, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x00, 0x00 }; + const uint8_t write_same_cdb_ndob[VIRTIO_SCSI_CDB_SIZE] = { + 0x41, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x00, 0x00 + }; vs = qvirtio_scsi_pci_init(PCI_SLOT); @@ -225,6 +228,9 @@ static void test_unaligned_write_same(void) g_assert_cmphex(0, ==, virtio_scsi_do_command(vs, write_same_cdb_2, NULL, 0, buf2, 512, NULL)); + g_assert_cmphex(0, ==, + virtio_scsi_do_command(vs, write_same_cdb_ndob, NULL, 0, NULL, 0, NULL)); + qvirtio_scsi_pci_free(vs); } @@ -1330,7 +1330,7 @@ QemuCocoaView *cocoaView; /* Create the version string*/ NSString *version_string; version_string = [[NSString alloc] initWithFormat: - @"QEMU emulator version %s%s", QEMU_VERSION, QEMU_PKGVERSION]; + @"QEMU emulator version %s", QEMU_FULL_VERSION]; [version_label setStringValue: version_string]; [superView addSubview: version_label]; diff --git a/util/Makefile.objs b/util/Makefile.objs index ae90b99..728c354 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -33,6 +33,7 @@ util-obj-y += throttle.o util-obj-y += getauxval.o util-obj-y += readline.o util-obj-y += rcu.o +util-obj-$(CONFIG_MEMBARRIER) += sys_membarrier.o util-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o util-obj-y += qemu-coroutine-sleep.o util-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o diff --git a/util/main-loop.c b/util/main-loop.c index 7558eb5..992f9b0 100644 --- a/util/main-loop.c +++ b/util/main-loop.c @@ -29,6 +29,7 @@ #include "qemu/sockets.h" // struct in_addr needed for libslirp.h #include "sysemu/qtest.h" #include "sysemu/cpus.h" +#include "sysemu/replay.h" #include "slirp/libslirp.h" #include "qemu/main-loop.h" #include "block/aio.h" @@ -245,18 +246,19 @@ static int os_host_main_loop_wait(int64_t timeout) timeout = SCALE_MS; } + if (timeout) { spin_counter = 0; - qemu_mutex_unlock_iothread(); } else { spin_counter++; } + qemu_mutex_unlock_iothread(); + replay_mutex_unlock(); ret = qemu_poll_ns((GPollFD *)gpollfds->data, gpollfds->len, timeout); - if (timeout) { - qemu_mutex_lock_iothread(); - } + replay_mutex_lock(); + qemu_mutex_lock_iothread(); glib_pollfds_poll(); @@ -463,8 +465,13 @@ static int os_host_main_loop_wait(int64_t timeout) poll_timeout_ns = qemu_soonest_timeout(poll_timeout_ns, timeout); qemu_mutex_unlock_iothread(); + + replay_mutex_unlock(); + g_poll_ret = qemu_poll_ns(poll_fds, n_poll_fds + w->num, poll_timeout_ns); + replay_mutex_lock(); + qemu_mutex_lock_iothread(); if (g_poll_ret > 0) { for (i = 0; i < w->num; i++) { diff --git a/util/qemu-timer.c b/util/qemu-timer.c index 82d5650..2ed1bf2 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -622,6 +622,18 @@ int64_t qemu_clock_get_ns(QEMUClockType type) } } +uint64_t qemu_clock_get_last(QEMUClockType type) +{ + QEMUClock *clock = qemu_clock_ptr(type); + return clock->last; +} + +void qemu_clock_set_last(QEMUClockType type, uint64_t last) +{ + QEMUClock *clock = qemu_clock_ptr(type); + clock->last = last; +} + void qemu_clock_register_reset_notifier(QEMUClockType type, Notifier *notifier) { @@ -92,10 +92,11 @@ static void wait_for_readers(void) atomic_set(&index->waiting, true); } - /* Here, order the stores to index->waiting before the - * loads of index->ctr. + /* Here, order the stores to index->waiting before the loads of + * index->ctr. Pairs with smp_mb_placeholder() in rcu_read_unlock(), + * ensuring that the loads of index->ctr are sequentially consistent. */ - smp_mb(); + smp_mb_global(); QLIST_FOREACH_SAFE(index, ®istry, node, tmp) { if (!rcu_gp_ongoing(&index->ctr)) { @@ -142,8 +143,13 @@ static void wait_for_readers(void) void synchronize_rcu(void) { qemu_mutex_lock(&rcu_sync_lock); - qemu_mutex_lock(&rcu_registry_lock); + /* Write RCU-protected pointers before reading p_rcu_reader->ctr. + * Pairs with smp_mb_placeholder() in rcu_read_lock(). + */ + smp_mb_global(); + + qemu_mutex_lock(&rcu_registry_lock); if (!QLIST_EMPTY(®istry)) { /* In either case, the atomic_mb_set below blocks stores that free * old RCU-protected pointers. @@ -370,6 +376,7 @@ static void rcu_init_child(void) static void __attribute__((__constructor__)) rcu_init(void) { + smp_mb_global_init(); #ifdef CONFIG_POSIX pthread_atfork(rcu_init_lock, rcu_init_unlock, rcu_init_child); #endif diff --git a/util/sys_membarrier.c b/util/sys_membarrier.c new file mode 100644 index 0000000..8dcb53e --- /dev/null +++ b/util/sys_membarrier.c @@ -0,0 +1,50 @@ +/* + * Process-global memory barriers + * + * Copyright (c) 2018 Red Hat, Inc. + * + * Author: Paolo Bonzini <pbonzini@redhat.com> + */ + +#include <qemu/osdep.h> +#include <qemu/sys_membarrier.h> +#include <qemu/error-report.h> + +#ifdef CONFIG_LINUX +#include <linux/membarrier.h> +#include <sys/syscall.h> + +static int +membarrier(int cmd, int flags) +{ + return syscall(__NR_membarrier, cmd, flags); +} +#endif + +void smp_mb_global(void) +{ +#if defined CONFIG_WIN32 + FlushProcessWriteBuffers(); +#elif defined CONFIG_LINUX + membarrier(MEMBARRIER_CMD_SHARED, 0); +#else +#error --enable-membarrier is not supported on this operating system. +#endif +} + +void smp_mb_global_init(void) +{ +#ifdef CONFIG_LINUX + int ret = membarrier(MEMBARRIER_CMD_QUERY, 0); + if (ret < 0) { + error_report("This QEMU binary requires the membarrier system call."); + error_report("Please upgrade your system to a newer version of Linux"); + exit(1); + } + if (!(ret & MEMBARRIER_CMD_SHARED)) { + error_report("This QEMU binary requires MEMBARRIER_CMD_SHARED support."); + error_report("Please upgrade your system to a newer version of Linux"); + exit(1); + } +#endif +} @@ -1948,7 +1948,7 @@ static void main_loop(void) static void version(void) { - printf("QEMU emulator version " QEMU_VERSION QEMU_PKGVERSION "\n" + printf("QEMU emulator version " QEMU_FULL_VERSION "\n" QEMU_COPYRIGHT "\n"); } @@ -2696,7 +2696,7 @@ static void qemu_run_exit_notifiers(void) notifier_list_notify(&exit_notifiers, NULL); } -static bool machine_init_done; +bool machine_init_done; void qemu_add_machine_init_done_notifier(Notifier *notify) { @@ -2713,8 +2713,8 @@ void qemu_remove_machine_init_done_notifier(Notifier *notify) static void qemu_run_machine_init_done_notifiers(void) { - notifier_list_notify(&machine_init_done_notifiers, NULL); machine_init_done = true; + notifier_list_notify(&machine_init_done_notifiers, NULL); } static const QEMUOption *lookup_opt(int argc, char **argv, @@ -3058,6 +3058,7 @@ int main(int argc, char **argv, char **envp) qemu_init_cpu_list(); qemu_init_cpu_loop(); + qemu_mutex_lock_iothread(); atexit(qemu_run_exit_notifiers); |