diff --git a/Makefile.objs b/Makefile.objs index f46a4cdd6a09..170a11f68843 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -47,6 +47,7 @@ common-obj-y += readline.o common-obj-y += qdev-monitor.o device-hotplug.o common-obj-$(CONFIG_WIN32) += os-win32.o common-obj-$(CONFIG_POSIX) += os-posix.o +common-obj-$(CONFIG_SIMLIB) += simlib.o common-obj-$(CONFIG_LINUX) += fsdev/ diff --git a/README-SimLib b/README-SimLib new file mode 100644 index 000000000000..da39def5d0ed --- /dev/null +++ b/README-SimLib @@ -0,0 +1 @@ +Patch applies to Qemu 1.6.0 (git hash 1ee2daeb6448312d6d0e22175f5c1b9b01f8974c) diff --git a/compile.sh b/compile.sh new file mode 100755 index 000000000000..1e723d0344cc --- /dev/null +++ b/compile.sh @@ -0,0 +1,14 @@ +#!/bin/bash +echo "For use with gprof call as ./compile.sh --enable-gprof --extra-cflags=-fPIC --disable-pie" + +./configure --static --disable-curl --enable-simlib --disable-tools --disable-werror --target-list=i386-softmmu,x86_64-softmmu --disable-curses --audio-drv-list="" --disable-vhost-net --disable-guest-agent --disable-blobs --disable-bluez --disable-vnc-sasl --disable-vnc-jpeg --disable-vnc-png --disable-vnc-tls --disable-vnc --disable-sdl --block-drv-whitelist="" "$@" && \ +make clean && \ +nice make -j3 recurse-all + +if [ "$1" == "" ] ; then + #don't strip if other options are given, etc gprof + strip i386-softmmu/qemu-system-i386 + strip x86_64-softmmu/qemu-system-x86_64 +fi +cp i386-softmmu/qemu-system-i386 ../qemu-java/lib/qemu +cp x86_64-softmmu/qemu-system-x86_64 ../qemu-java/lib/qemu64 diff --git a/configure b/configure index 18fa60824bef..da124e48cfb9 100755 --- a/configure +++ b/configure @@ -243,6 +243,7 @@ gtk="" gtkabi="2.0" tpm="no" libssh2="" +simlib="no" # parse CC options first for opt do @@ -945,6 +946,8 @@ for opt do ;; --enable-libssh2) libssh2="yes" ;; + --enable-simlib) simlib="yes" + ;; *) echo "ERROR: unknown option $opt"; show_help="yes" ;; esac @@ -1159,6 +1162,7 @@ echo " --gcov=GCOV use specified gcov [$gcov_tool]" echo " --enable-tpm enable TPM support" echo " --disable-libssh2 disable ssh block device support" echo " --enable-libssh2 enable ssh block device support" +echo " --enable-simlib enable simlib support" echo "" echo "NOTE: The object files are built at the place where configure is launched" exit 1 @@ -3613,6 +3617,7 @@ echo "TPM support $tpm" echo "libssh2 support $libssh2" echo "TPM passthrough $tpm_passthrough" echo "QOM debugging $qom_cast_debug" +echo "simlib support $simlib" if test "$sdl_too_old" = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -4003,6 +4008,10 @@ if test "$virtio_blk_data_plane" = "yes" ; then echo 'CONFIG_VIRTIO_BLK_DATA_PLANE=$(CONFIG_VIRTIO)' >> $config_host_mak fi +if test "$simlib" = "yes" ; then + echo 'CONFIG_SIMLIB=y' >> $config_host_mak +fi + # USB host support case "$usb" in linux) diff --git a/cpu-exec.c b/cpu-exec.c index 301be28bf740..911f320881f1 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -198,9 +198,16 @@ static void cpu_handle_debug_exception(CPUArchState *env) /* main execution loop */ volatile sig_atomic_t exit_request; +#ifdef CONFIG_SIMLIB +#include "simlib.h" +volatile long long cpu_exec_iterations; +#endif int cpu_exec(CPUArchState *env) { +#ifdef CONFIG_SIMLIB + cpu_exec_iterations = 0; +#endif CPUState *cpu = ENV_GET_CPU(env); #if !(defined(CONFIG_USER_ONLY) && \ (defined(TARGET_M68K) || defined(TARGET_PPC) || defined(TARGET_S390X))) @@ -574,6 +581,14 @@ int cpu_exec(CPUArchState *env) next_tb = 0; } } +#ifdef CONFIG_SIMLIB + if(is_simlib_enabled()) { + cpu_exec_iterations++; + if(simlib_get_max_iterations_enabled() && (cpu_exec_iterations >= CPU_EXEC_MAX_ITERATIONS)) { + cpu->exit_request = 1; + } + } +#endif if (unlikely(cpu->exit_request)) { cpu->exit_request = 0; env->exception_index = EXCP_INTERRUPT; @@ -614,6 +629,11 @@ int cpu_exec(CPUArchState *env) spans two pages, we cannot safely do a direct jump. */ if (next_tb != 0 && tb->page_addr[1] == -1) { +#ifdef CONFIG_SIMLIB + /* SIMLIB: avoid linking of TBs, to be able to exit this loop + * warning: cannot be undone, so timeout cannot be re-enabled */ + if(!is_simlib_enabled() || !simlib_get_max_iterations_enabled()) +#endif tb_add_jump((TranslationBlock *)(next_tb & ~TB_EXIT_MASK), next_tb & TB_EXIT_MASK, tb); } diff --git a/cpus.c b/cpus.c index 0f65e763f26f..d758a2937da5 100644 --- a/cpus.c +++ b/cpus.c @@ -88,6 +88,13 @@ static bool all_cpu_threads_idle(void) } return true; } +#ifdef CONFIG_SIMLIB +#include "simlib.h" +/* method is required to signal idle to the simlib */ +bool my_all_cpu_threads_idle(void) { + return all_cpu_threads_idle(); +} +#endif /***********************************************************/ /* guest cycle counter */ @@ -134,6 +141,11 @@ int64_t cpu_get_icount(void) /* return the host CPU cycle counter and handle stop/restart */ int64_t cpu_get_ticks(void) { +#ifdef CONFIG_SIMLIB + /* simlib time overrides all other time sources */ + if(is_simlib_enabled()) + return simlib_get_time(); +#endif if (use_icount) { return cpu_get_icount(); } @@ -155,6 +167,11 @@ int64_t cpu_get_ticks(void) /* return the host CPU monotonic timer and handle stop/restart */ int64_t cpu_get_clock(void) { +#ifdef CONFIG_SIMLIB + /* simlib time overrides all other time sources */ + if(is_simlib_enabled()) + return simlib_get_time(); +#endif int64_t ti; if (!timers_state.cpu_ticks_enabled) { return timers_state.cpu_clock_offset; @@ -168,6 +185,13 @@ int64_t cpu_get_clock(void) void cpu_enable_ticks(void) { if (!timers_state.cpu_ticks_enabled) { +#ifdef CONFIG_SIMLIB + /* + * it doesn't make sense to read the host tsc, + * and the cpu_ticks_offset value isn't used anyway + */ + if(!is_simlib_enabled()) +#endif timers_state.cpu_ticks_offset -= cpu_get_real_ticks(); timers_state.cpu_clock_offset -= get_clock(); timers_state.cpu_ticks_enabled = 1; @@ -180,6 +204,12 @@ void cpu_disable_ticks(void) { if (timers_state.cpu_ticks_enabled) { timers_state.cpu_ticks_offset = cpu_get_ticks(); +#ifdef CONFIG_SIMLIB + /* + * don't save clock when simlib is used, as that collides with loading a snapshot + */ + if(!is_simlib_enabled()) +#endif timers_state.cpu_clock_offset = cpu_get_clock(); timers_state.cpu_ticks_enabled = 0; } @@ -732,6 +762,13 @@ static void qemu_tcg_wait_io_event(void) { CPUState *cpu; +#ifdef CONFIG_SIMLIB + /* give the iothread a chance to run even if the CPU was interrupted */ + if (is_simlib_enabled() && !all_cpu_threads_idle()) { + qemu_cond_wait(tcg_halt_cond, &qemu_global_mutex); + } +#endif + while (all_cpu_threads_idle()) { /* Start accounting real time to the virtual clock if the CPUs are idle. */ diff --git a/hw/char/serial.c b/hw/char/serial.c index 602559254e75..0bb87b893224 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -242,6 +242,9 @@ static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque) /* in loopback mode, say that we just received a char */ serial_receive1(s, &s->tsr, 1); } else if (qemu_chr_fe_write(s->chr, &s->tsr, 1) != 1) { +#ifdef CONFIG_SIMLIB + fprintf(stderr, "[SIMLIB] write to char backend failed\n"); +#endif if (s->tsr_retry >= 0 && s->tsr_retry < MAX_XMIT_RETRY && qemu_chr_fe_add_watch(s->chr, G_IO_OUT, serial_xmit, s) > 0) { s->tsr_retry++; @@ -283,6 +286,9 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, /* xmit overruns overwrite data, so make space if needed */ if (fifo8_is_full(&s->xmit_fifo)) { fifo8_pop(&s->xmit_fifo); +#ifdef CONFIG_SIMLIB + fprintf(stderr, "[SIMLIB] serial dropped char from queue\n"); +#endif } fifo8_push(&s->xmit_fifo, s->thr); s->lsr &= ~UART_LSR_TEMT; diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 648b38362de9..191f7b3730ab 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -360,7 +360,12 @@ static void hpet_set_timer(HPETTimer *t) */ if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) { wrap_diff = 0xffffffff - (uint32_t)cur_tick; +#ifdef CONFIG_SIMLIB + /* otherwise, this results in an infinite loop because the CPU is too fast */ + if (wrap_diff < (uint32_t)diff && wrap_diff > 0) { +#else if (wrap_diff < (uint32_t)diff) { +#endif diff = wrap_diff; t->wrap_flag = 1; } diff --git a/include/qemu/timer.h b/include/qemu/timer.h index 9dd206ce7f47..5af3239addc2 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -5,6 +5,10 @@ #include "qemu/main-loop.h" #include "qemu/notify.h" +#ifdef CONFIG_SIMLIB +#include "simlib.h" +#endif + /* timers */ #define SCALE_MS 1000000 @@ -113,6 +117,10 @@ extern int use_rt_clock; static inline int64_t get_clock(void) { +#ifdef CONFIG_SIMLIB + if(is_simlib_enabled()) + return simlib_get_time(); +#endif #ifdef CLOCK_MONOTONIC if (use_rt_clock) { struct timespec ts; @@ -168,6 +176,13 @@ static inline int64_t cpu_get_real_ticks(void) static inline int64_t cpu_get_real_ticks(void) { +#ifdef CONFIG_SIMLIB + if(is_simlib_enabled()) { + fprintf(stderr, "[SIMLIB] host tsc should not be read\n"); + fflush(stderr); + abort(); + } +#endif int64_t val; asm volatile ("rdtsc" : "=A" (val)); return val; @@ -177,6 +192,13 @@ static inline int64_t cpu_get_real_ticks(void) static inline int64_t cpu_get_real_ticks(void) { +#ifdef CONFIG_SIMLIB + if(is_simlib_enabled()) { + fprintf(stderr, "[SIMLIB] host tsc should not be read\n"); + fflush(stderr); + abort(); + } +#endif uint32_t low,high; int64_t val; asm volatile("rdtsc" : "=a" (low), "=d" (high)); diff --git a/include/sysemu/char.h b/include/sysemu/char.h index 8053130a9795..dbc94591cf74 100644 --- a/include/sysemu/char.h +++ b/include/sysemu/char.h @@ -298,4 +298,8 @@ CharDriverState *qemu_chr_open_msmouse(void); /* baum.c */ CharDriverState *chr_baum_init(void); +#ifdef CONFIG_SIMLIB +void simlib_chr_read(int index, uint8_t *buf, int len); +#endif + #endif diff --git a/main-loop.c b/main-loop.c index a44fff6e9aea..8d5ff212bee2 100644 --- a/main-loop.c +++ b/main-loop.c @@ -191,6 +191,12 @@ static void glib_pollfds_poll(void) #define MAX_MAIN_LOOP_SPIN (1000) +#ifdef CONFIG_SIMLIB +#include "qom/cpu.h" +#include "simlib.h" +static void dummy_fn(void * dummy) { /* noop */ } +#endif + static int os_host_main_loop_wait(uint32_t timeout) { int ret; @@ -198,6 +204,23 @@ static int os_host_main_loop_wait(uint32_t timeout) glib_pollfds_fill(&timeout); +#ifdef CONFIG_SIMLIB + if(is_simlib_enabled()) { + /* + * make sure that the CPU thread runs until either the max. iterations + * have been executed or the CPU becomes idle + */ + cpu_exec_iterations = 0; + while(!(my_all_cpu_threads_idle() || cpu_exec_iterations >= CPU_EXEC_MAX_ITERATIONS)) { + run_on_cpu(first_cpu, dummy_fn, NULL); + } + cpu_exec_iterations = 0; + /* disable the timeout, because waiting doesn't make + * sense when the other thread is already idle + */ + timeout = 0; + } else { +#endif /* If the I/O thread is very busy or we are incorrectly busy waiting in * the I/O thread, this can lead to starvation of the BQL such that the * VCPU threads never run. To make sure we can detect the later case, @@ -216,6 +239,9 @@ static int os_host_main_loop_wait(uint32_t timeout) timeout = 1; } +#ifdef CONFIG_SIMLIB + } +#endif if (timeout > 0) { spin_counter = 0; diff --git a/maketar.sh b/maketar.sh new file mode 100755 index 000000000000..e4ae23669fe6 --- /dev/null +++ b/maketar.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +version=$(git tag --points-at HEAD | cut -d '-' -f 3) +TAR="qemu-simlib-src-"$D".tar.gz" +dist="qemu-simlib" + +./compile.sh + +mkdir $dist + +cp i386-softmmu/qemu-system-i386 $dist/ +cp pc-bios/bios.bin $dist/ +cp pc-bios/efi-virtio.rom $dist/ +cp pc-bios/kvmvapic.bin $dist/ +cp pc-bios/linuxboot.bin $dist + +make clean +git archive --prefix qemu-simlib-src -o $dist/$TAR simlib +git diff simlib v1.6.0 > $dist/simlib.patch + +cp README-SimLib $dist/ + +tar -cz $dist > qemu-simlib-$version.tar.gz + +rm -r $dist diff --git a/net/Makefile.objs b/net/Makefile.objs index 4854a14fe425..f69a9d5ae2c1 100644 --- a/net/Makefile.objs +++ b/net/Makefile.objs @@ -11,3 +11,4 @@ common-obj-$(CONFIG_AIX) += tap-aix.o common-obj-$(CONFIG_HAIKU) += tap-haiku.o common-obj-$(CONFIG_SLIRP) += slirp.o common-obj-$(CONFIG_VDE) += vde.o +common-obj-$(CONFIG_SIMLIB) += simlib-net.o diff --git a/net/clients.h b/net/clients.h index 77932942bdde..d50aa4f88b88 100644 --- a/net/clients.h +++ b/net/clients.h @@ -52,4 +52,9 @@ int net_init_vde(const NetClientOptions *opts, const char *name, NetClientState *peer); #endif +#ifdef CONFIG_SIMLIB +int net_init_simlib(const NetClientOptions *opts, const char *name, + NetClientState *peer); +#endif + #endif /* QEMU_NET_CLIENTS_H */ diff --git a/net/hub.c b/net/hub.c index df32074de014..cf397cc8a6f0 100644 --- a/net/hub.c +++ b/net/hub.c @@ -332,9 +332,13 @@ void net_hub_check_clients(void) fprintf(stderr, "Warning: vlan %d with no nics\n", hub->id); } if (has_nic && !has_host_dev) { + if(!is_simlib_enabled()){ fprintf(stderr, "Warning: vlan %d is not connected to host network\n", hub->id); + } else { + fprintf(stdout, "Info: vlan %d is not connected to host network which is normal for SimLib integrated VMs\n", hub->id); + } } } } diff --git a/net/net.c b/net/net.c index c0d61bf78b1b..b99a133ac266 100644 --- a/net/net.c +++ b/net/net.c @@ -725,6 +725,9 @@ static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])( [NET_CLIENT_OPTIONS_KIND_BRIDGE] = net_init_bridge, #endif [NET_CLIENT_OPTIONS_KIND_HUBPORT] = net_init_hubport, +#ifdef CONFIG_SIMLIB + [NET_CLIENT_OPTIONS_KIND_SIMLIB] = net_init_simlib, +#endif }; diff --git a/net/simlib-net.c b/net/simlib-net.c new file mode 100644 index 000000000000..809a014117a2 --- /dev/null +++ b/net/simlib-net.c @@ -0,0 +1,74 @@ +#include "net/simlib-net.h" + +#include "config-host.h" + +#include "net/net.h" +#include "qemu-common.h" +#include +#include + +#include "simlib.h" + +typedef struct SimlibState { + NetClientState nc; + int32_t index; +} SimlibState; + +#define SIMLIB_MAX_VLAN 256 +NetClientState * simlib_vlan[SIMLIB_MAX_VLAN]; + +static ssize_t simlib_receive(NetClientState *nc, const uint8_t *buf, + size_t size) { + SimlibState *s = DO_UPCAST(SimlibState, nc, nc); + + uint8_t lbuf[12]; + *((int32_t *) lbuf) = htobe32(SIMLIB_MSG_PACKET); + *((int32_t *) (lbuf + 4)) = htobe32(size + 4); + *((int32_t *) (lbuf + 8)) = htobe32(s->index); + + simlib_write_message(lbuf, 12); + simlib_write_message(buf, size); + + return size; +} + +static void simlib_cleanup(NetClientState *nc) { + //noop +} + +static NetClientInfo net_simlib_info = { + .type = NET_CLIENT_OPTIONS_KIND_SIMLIB, + .size = sizeof(SimlibState), + .receive = simlib_receive, + .cleanup = simlib_cleanup, +}; + +int net_init_simlib(const NetClientOptions *opts, const char *name, + NetClientState *peer) { + const NetdevSimlibOptions *simlib; + NetClientState *nc; + SimlibState *s; + + assert(opts->kind == NET_CLIENT_OPTIONS_KIND_SIMLIB); + simlib = opts->simlib; + + nc = qemu_new_net_client(&net_simlib_info, peer, "simlib", name); + + s = DO_UPCAST(SimlibState, nc, nc); + s->index = simlib->index; + + if (s->index >= SIMLIB_MAX_VLAN) { + fprintf(stderr, "FATAL: vlan_id %d larger than SIMLIB_MAX_VLAN\n", + s->index); + exit(-1); + } + simlib_vlan[s->index] = nc; + + snprintf(nc->info_str, sizeof(nc->info_str), "id=%d", s->index); + + return 0; +} + +void simlib_send_packet(int32_t vlan, const uint8_t *buf, size_t size) { + qemu_send_packet(simlib_vlan[vlan], buf, size); +} diff --git a/net/simlib-net.h b/net/simlib-net.h new file mode 100644 index 000000000000..2bd0e1a5dae1 --- /dev/null +++ b/net/simlib-net.h @@ -0,0 +1,11 @@ +#ifndef QEMU_NET_SIMLIB_NET_H +#define QEMU_NET_SIMLIB_NET_H + +#include "qemu-common.h" +#include "qemu/option.h" + +int net_init_simlib(const NetClientOptions *opts, const char *name, + NetClientState *peer); +void simlib_send_packet(int32_t vlan, const uint8_t *buf, size_t size); + +#endif /* QEMU_NET_SIMLIB_NET_H */ diff --git a/qapi-schema.json b/qapi-schema.json index a51f7d2d6ebc..4e5416d5bf67 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2804,6 +2804,19 @@ 'data': { 'hubid': 'int32' } } +## +# @NetdevSimlibOptions +# +# Connect to a simulation library +# +# @id: index of the device +# +# Since 1.6+x +## +{ 'type': 'NetdevSimlibOptions', + 'data': { + 'index': 'int32' } } + ## # @NetClientOptions # @@ -2821,7 +2834,8 @@ 'vde': 'NetdevVdeOptions', 'dump': 'NetdevDumpOptions', 'bridge': 'NetdevBridgeOptions', - 'hubport': 'NetdevHubPortOptions' } } + 'hubport': 'NetdevHubPortOptions', + 'simlib': 'NetdevSimlibOptions' } } ## # @NetLegacy @@ -3372,6 +3386,17 @@ ## { 'type': 'ChardevRingbuf', 'data': { '*size' : 'int' } } +## +# @ChardevSimlib: +# +# Configuration info for simlib chardevs. +# +# @index: index of the character stream (multiple streams are multiplexed through the simlib communication pipes) +# +# Since: 1.6+x +## +{ 'type': 'ChardevSimlib', 'data': { 'index' : 'int' } } + ## # @ChardevBackend: # @@ -3398,6 +3423,7 @@ 'spiceport' : 'ChardevSpicePort', 'vc' : 'ChardevVC', 'ringbuf': 'ChardevRingbuf', + 'simlib' : 'ChardevSimlib', # next one is just for compatibility 'memory' : 'ChardevRingbuf' } } diff --git a/qemu-char.c b/qemu-char.c index 1be1cf676eef..5b9f14fa1762 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -79,6 +79,9 @@ #endif #endif #endif +#ifdef CONFIG_SIMLIB +#include "simlib.h" +#endif #include "qemu/sockets.h" #include "ui/qemu-spice.h" @@ -2708,7 +2711,7 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, } if (is_listen && is_waitconnect) { - fprintf(stderr, "QEMU waiting for connection on: %s\n", + fprintf(stdout, "QEMU waiting for connection on: %s\n", chr->filename); tcp_chr_accept(s->listen_chan, G_IO_IN, chr); qemu_set_nonblock(s->listen_fd); @@ -2954,6 +2957,217 @@ char *qmp_ringbuf_read(const char *device, int64_t size, return data; } +#ifdef CONFIG_SIMLIB +/* + * simlib chardev backend + */ + +#define MAX_SIMLIB_CONSOLES 16 + +typedef struct { + int index; +} SimLibCharDriver; + +CharDriverState *activeSimLibConsoles[MAX_SIMLIB_CONSOLES]; + +static CharDriverState* simlib_chr_get_driver_from_index(int index) +{ + CharDriverState *chr = activeSimLibConsoles[index]; + if(chr == NULL) { + fprintf(stderr, "[SIMLIB] FATAL: internal error - chardev with index %d not initialized\n", index); + fflush(stderr); + _exit(-1); + } + + // check the index + SimLibCharDriver *s = chr->opaque; + if (index != s->index) { + fprintf(stderr, "[SIMLIB] FATAL: internal error - index mismatch\n"); + fflush(stderr); + _exit(-1); + } + + return chr; +} + +/* called when the emulated device wants to send data */ +static int simlib_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ + SimLibCharDriver *s = chr->opaque; + const int index = s->index; + + uint8_t * lbuf = alloca(len + 12); + *((int32_t *)lbuf) = htobe32(SIMLIB_MSG_CONSOLE); + *((int32_t *)(lbuf+4)) = htobe32(len + 4); + *((int32_t *)(lbuf+8)) = htobe32(index); + memcpy(lbuf + 12, buf, len); + + simlib_write_message(lbuf, len + 12); + + return len; +} + +/* + * Each of these buffers is one command send by the simlib. + * These are queued here, because the standard serial devices have + * a small fifo. We don't want to signal the number of written bytes + * back to the simlib. + * + * Queueing is implemented here, although it is not required because + * the simlib waits for acknowledgement after each command. + */ +typedef struct SimLibCharBuffer SimLibCharBuffer; +struct SimLibCharBuffer { + uint8_t *buffer; + int length; + int offset; + int deviceIndex; + QEMUBH *bh; + SimLibCharBuffer *next; +}; + +// the first element of the buffer queue for each console +SimLibCharBuffer *activeTransmissions[MAX_SIMLIB_CONSOLES]; + +void simlib_chr_read_bh_callback(void *opaque); + +/* + * schedule a bh-callback to transfer the buffer into the serial devices fifo + */ +static void simlib_chr_schedule_buffer(int index) +{ + //simlib_lock(); + QEMUBH *bh = qemu_bh_new(simlib_chr_read_bh_callback, activeTransmissions[index]); + activeTransmissions[index]->bh = bh; + qemu_bh_schedule(bh); +} + +/* + * callback which is used to transmit data from the buffer to the fifo + */ +void simlib_chr_read_bh_callback(void *opaque) +{ + SimLibCharBuffer *tmp_buffer = opaque; + int index = tmp_buffer->deviceIndex; + CharDriverState *chr = simlib_chr_get_driver_from_index(index); + + SimLibCharBuffer *buffer = activeTransmissions[index]; + int len = buffer->length - buffer->offset; + + // how much free space is there in the fifo? + int capacity = chr->chr_can_read(chr->handler_opaque); + if (len > capacity) + len = capacity; + + // transmit data and move the buffers offset + if(len > 0) { + uint8_t *ptr = buffer->buffer; + ptr += buffer->offset; + qemu_chr_be_write(chr, ptr, len); + buffer->offset += len; + + //simlib_chr_schedule_interrupt(chr); + } + + int remaining = buffer->length - buffer->offset; + + if (remaining == 0) { + //simlib_unlock(); + // save pointer to next element + SimLibCharBuffer *next = buffer->next; + // clean up + qemu_bh_delete(buffer->bh); + g_free(buffer->buffer); + g_free(buffer); + activeTransmissions[index] = next; + // reschedule if there is another element + if (next != NULL) + simlib_chr_schedule_buffer(index); + } else { + // more work to do - schedule as idle because no event has to be generated + qemu_bh_schedule(buffer->bh); + } +} + +/* + * called by simlib.c when the simlib wants to send data + * + * creates a buffer and appends it to the queue, schedules a + * bh callback if there isn't already one pending + */ +void simlib_chr_read(int index, uint8_t *buf, int len) +{ + // create a new buffer struct + SimLibCharBuffer *buffer = g_malloc0(sizeof(SimLibCharBuffer)); + buffer->buffer = g_malloc(len); + memcpy(buffer->buffer, buf, len); + buffer->length = len; + buffer->deviceIndex = index; + + // append it to the list or start a new list + if (activeTransmissions[index] == NULL) { + //nothing is transmitting, schedule new transmission + activeTransmissions[index] = buffer; + simlib_chr_schedule_buffer(index); + } else { + fprintf(stdout, "[SIMLIB] chardev transmission pending, appending to queue\n"); + SimLibCharBuffer *last = activeTransmissions[index]; + while (last->next != NULL) + last = last->next; + last->next = buffer; + } +} + +/* + * don't know whether this is required at all + */ +static void simlib_chr_close(struct CharDriverState *chr) +{ + SimLibCharDriver *s = chr->opaque; + + g_free(s); + chr->opaque = NULL; + + qemu_chr_be_event(chr, CHR_EVENT_CLOSED); +} + +/* + * called when a simlib chardev is defined on the command line + */ +static CharDriverState *qemu_chr_open_simlib(ChardevSimlib *opts, + Error **errp) +{ + if(!is_simlib_enabled()) { + fprintf(stderr, "[SIMLIB] simlib support not enabled - specify -simlib on the cmd line\n"); + fflush(stderr); + exit(1); + } + + CharDriverState *chr; + SimLibCharDriver *s; + const int index = opts->index; + + fprintf(stdout, "[SIMLIB] opening chardev with index %d\n", index); + + if (activeSimLibConsoles[index] != NULL) { + fprintf(stderr, "[SIMLIB] chardev: simlib: index %d already used\n", index); + return NULL; + } + + s = g_malloc0(sizeof(SimLibCharDriver)); + s->index = index; + + chr = g_malloc0(sizeof(CharDriverState)); + chr->chr_write = simlib_chr_write; + chr->chr_close = simlib_chr_close; + chr->opaque = s; + + activeSimLibConsoles[index] = chr; + + return chr; +} +#endif + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) { char host[65], port[33], width[8], height[8]; @@ -3177,6 +3391,24 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend, backend->mux->chardev = g_strdup(chardev); } +#ifdef CONFIG_SIMLIB +static void qemu_chr_parse_simlib(QemuOpts *opts, ChardevBackend *backend, + Error **errp) +{ + const int index = qemu_opt_get_number(opts, "index", -1); + + if (index == -1) { + error_setg(errp, "chardev: simlib: no index given"); + return; + } + backend->simlib = g_new0(ChardevSimlib, 1); + backend->simlib->index = index; + + fprintf(stdout,"[SIMLIB] configured chardev with index %d\n", index); + fflush(stdout); +} +#endif + typedef struct CharDriver { const char *name; /* old, pre qapi */ @@ -3547,6 +3779,9 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "chardev", .type = QEMU_OPT_STRING, + },{ + .name = "index", + .type = QEMU_OPT_NUMBER, }, { /* end of list */ } }, @@ -3772,6 +4007,11 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, case CHARDEV_BACKEND_KIND_MEMORY: chr = qemu_chr_open_ringbuf(backend->ringbuf, errp); break; +#ifdef CONFIG_SIMLIB + case CHARDEV_BACKEND_KIND_SIMLIB: + chr = qemu_chr_open_simlib(backend->simlib, errp); + break; +#endif default: error_setg(errp, "unknown chardev backend (%d)", backend->kind); break; @@ -3840,6 +4080,10 @@ static void register_types(void) qemu_chr_parse_pipe); register_char_driver_qapi("mux", CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux); +#ifdef CONFIG_SIMLIB + register_char_driver_qapi("simlib", CHARDEV_BACKEND_KIND_SIMLIB, + qemu_chr_parse_simlib); +#endif /* Bug-compatibility: */ register_char_driver_qapi("memory", CHARDEV_BACKEND_KIND_MEMORY, qemu_chr_parse_ringbuf); diff --git a/qemu-options.hx b/qemu-options.hx index d15338e879cf..71c0fc34bb68 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2758,6 +2758,16 @@ Force the use of the given methods for timer alarm. To see what timers are available use @code{-clock help}. ETEXI +DEF("simlib", HAS_ARG, QEMU_OPTION_simlib, \ + "-simlib read=file,write=file\n" \ + " Specify the names of the two named pipes to communicate with simlib.\n", \ + QEMU_ARCH_ALL) +STEXI +@item -simlib read=@var{read},write=@var{write} +@findex -simlib +Specify the names of the two named pipes to communicate with simlib. +ETEXI + HXCOMM Options deprecated by -rtc DEF("localtime", 0, QEMU_OPTION_localtime, "", QEMU_ARCH_ALL) DEF("startdate", HAS_ARG, QEMU_OPTION_startdate, "", QEMU_ARCH_ALL) diff --git a/qemu-timer.c b/qemu-timer.c index b2d95e2fec8e..3eff65ded1dd 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -37,6 +37,9 @@ #include #endif +#ifdef CONFIG_SIMLIB +#include "simlib.h" +#endif /***********************************************************/ /* timers */ @@ -85,7 +88,12 @@ static bool qemu_timer_expired_ns(QEMUTimer *timer_head, int64_t current_time) return timer_head && (timer_head->expire_time <= current_time); } +#ifdef CONFIG_SIMLIB +/* required in simlib.c */ +int64_t qemu_next_alarm_deadline(void) +#else static int64_t qemu_next_alarm_deadline(void) +#endif { int64_t delta = INT64_MAX; int64_t rtdelta; @@ -149,6 +157,12 @@ static void dynticks_rearm_timer(struct qemu_alarm_timer *t, int64_t delta); #endif /* _WIN32 */ +#ifdef CONFIG_SIMLIB +static int simlib_start_timer(struct qemu_alarm_timer *t); +static void simlib_stop_timer(struct qemu_alarm_timer *t); +static void simlib_rearm_timer(struct qemu_alarm_timer *t, int64_t delta); +#endif /* CONFIG_SIMLIB */ + static struct qemu_alarm_timer alarm_timers[] = { #ifndef _WIN32 #ifdef __linux__ @@ -160,6 +174,9 @@ static struct qemu_alarm_timer alarm_timers[] = { {"mmtimer", mm_start_timer, mm_stop_timer, mm_rearm_timer}, {"dynticks", win32_start_timer, win32_stop_timer, win32_rearm_timer}, #endif +#ifdef CONFIG_SIMLIB + {"simlib", simlib_start_timer, simlib_stop_timer, simlib_rearm_timer}, +#endif /* CONFIG_SIMLIB */ {NULL, } }; @@ -386,6 +403,15 @@ void qemu_run_timers(QEMUClock *clock) if (!qemu_timer_expired_ns(ts, current_time)) { break; } + +#ifdef CONFIG_SIMLIB + if (is_simlib_enabled() && (ts->expire_time < current_time)) + { + fprintf(stderr, "[SIMLIB] wakeup call too late for timer - delayed by %lld ns (now=%lld,simlib_get_time() returns %lld)\n", + current_time - ts->expire_time, current_time, simlib_get_time()); + } +#endif + /* remove timer from the list before calling the callback */ clock->active_timers = ts->next; ts->next = NULL; @@ -410,6 +436,10 @@ int64_t qemu_get_clock_ns(QEMUClock *clock) return cpu_get_clock(); } case QEMU_CLOCK_HOST: +#ifdef CONFIG_SIMLIB + if(is_simlib_enabled()) + abort(); /* not supported */ +#endif now = get_clock_realtime(); last = clock->last; clock->last = now; @@ -451,6 +481,9 @@ void qemu_run_all_timers(void) /* vm time timers */ qemu_run_timers(vm_clock); qemu_run_timers(rt_clock); +#ifdef CONFIG_SIMLIB + if(!is_simlib_enabled() || (host_clock->active_timers)) /* there should not be active timers, tun_timers will fail */ +#endif qemu_run_timers(host_clock); /* rearm timer, if not periodic */ @@ -725,6 +758,30 @@ static void win32_rearm_timer(struct qemu_alarm_timer *t, #endif /* _WIN32 */ +#ifdef CONFIG_SIMLIB +static int simlib_start_timer(struct qemu_alarm_timer *t) +{ + enable_simlib(); + return 0; +} + +static void simlib_stop_timer(struct qemu_alarm_timer *t) +{ + /* noop */ +} + +static void simlib_rearm_timer(struct qemu_alarm_timer *t, + int64_t nearest_delta_ns) +{ + /* noop */ +} + +void simlib_alarm_handler(void) +{ + host_alarm_handler(0); +} +#endif /* CONFIG_SIMLIB */ + static void quit_timers(void) { struct qemu_alarm_timer *t = alarm_timer; diff --git a/simlib.c b/simlib.c new file mode 100644 index 000000000000..407d8b2c95c4 --- /dev/null +++ b/simlib.c @@ -0,0 +1,367 @@ +#include "simlib.h" + +#include + +#include "qemu-common.h" +#include "net/net.h" +#include "net/simlib-net.h" +#include "qemu/main-loop.h" +#include "sysemu/char.h" + +int simlib_fd_r = -1; +int simlib_fd_w = -1; + +bool _simlib_waits_for_ack = false; +int64_t simlib_expected_next_wakeup = -1; + +static void simlib_init(void); +static void simlib_signal_idle(bool maxIterationsTriggered); +static void simlib_read(void * dummy); + +/******************************************************************** + * simlib enabled? + *******************************************************************/ + +bool enabled = 0; +bool enable_read = 0; + +void enable_simlib(void) { + if (enable_read) { + fprintf(stderr, "enable value has already been read\n"); + abort(); + } + enabled = 1; + simlib_init(); + fprintf(stdout, "[SIMLIB] enabling simlib support\n"); +} + +bool is_simlib_enabled(void) { + enable_read = 1; + return enabled; +} + +/******************************************************************** + * main loop and alarm events + *******************************************************************/ + +int64_t simlib_current_time = 0; +int max_iterations_before_return = 10; + +int simlib_get_max_iterations_enabled(void) { + return max_iterations_before_return > 0; +} + +void simlib_main_loop_hook(int last_io) { + static bool startup_phase = true; + + if (!is_simlib_enabled()) + return; + static int iterations = 0; + iterations++; + + bool returnToSimlib = false; + bool maxIterationsTriggered = false; + if (last_io == 0 && my_all_cpu_threads_idle()) { + // regular return + returnToSimlib = true; + } else if (startup_phase) { + // first round - wait for startup signal + returnToSimlib = true; + } else if (max_iterations_before_return > 0 + && iterations >= max_iterations_before_return) { + /* threshold can change, therefore >= */ + //fprintf(stderr, + // "[SIMLIB] force return of control flow to simlib after %d iterations\n", + // iterations); + returnToSimlib = true; + maxIterationsTriggered = true; + } + + if (returnToSimlib) { + /* + * Do a blocking read on the simlib control fd here. It's difficult to + * assert-check other file descriptors this way, but it is much easier to implement. + */ + if (startup_phase) { // don't do anything in the first round + startup_phase = false; + fprintf(stdout, + "[SIMLIB] skipping idle signal in the first round (simlib_current_time==%ld, timer==%ld)\n", + (long) simlib_current_time, + (long) qemu_next_alarm_deadline()); + } else { + simlib_signal_idle(maxIterationsTriggered); + } + + int dummy; + simlib_read(&dummy); // DEBUG - read first message with timestamp update directly + + // call the alarm handler so that the alarm is reset + simlib_alarm_handler(); + + iterations = 0; + } +} + +bool simlib_waits_for_ack(void) { + return _simlib_waits_for_ack; +} + +int64_t simlib_get_time(void) { + return simlib_current_time; +} + +/******************************************************************** + * control messages SimLib -> QEMU + *******************************************************************/ + +static void simlib_read_completely(int fd, void * data, int len) { + char * ptr = data; + int pos = 0; + while (len > pos) { + int r = read(fd, ptr + pos, len - pos); + if (r > 0) { + pos += r; + } else if (r == 0) { // EOF + fprintf(stderr, "FATAL: EOF while reading from pipe\n"); + fflush(stderr); + exit(-1); + } else if (errno == EINTR) { + // ignore + } else { + fprintf(stderr, "FATAL: Error while reading (%s r=%d errno=%d )\n", + strerror(errno), r, errno); + fflush(stderr); + exit(-1); + } + } +} + +struct header { + int32_t type; + int32_t id; + int64_t time; + int32_t length; +}; + +#define HEADER_LENGTH (3*4 + 8) + +static void simlib_read(void * dummy) { + + unsigned char hbuf[HEADER_LENGTH]; + struct header header; + + simlib_read_completely(simlib_fd_r, hbuf, HEADER_LENGTH); + header.type = be32toh( *((int32_t *)(hbuf+0)) ); + header.id = be32toh( *((int32_t *)(hbuf+4)) ); + header.time = be64toh( *((int64_t *)(hbuf+8)) ); + header.length = be32toh( *((int32_t *)(hbuf+16)) ); + + simlib_current_time = header.time; + + if (simlib_expected_next_wakeup != -1) { + if (simlib_current_time > simlib_expected_next_wakeup) + fprintf(stderr, "[SIMLIB] wakeup delayed by %ld ns\n", + (long) (simlib_current_time - simlib_expected_next_wakeup)); + simlib_expected_next_wakeup = -1; + } + + // read remaining data + unsigned char * buf = alloca(header.length); + simlib_read_completely(simlib_fd_r, buf, header.length); + + // we will read a message, so we switch to the state which allows qemu to do something + _simlib_waits_for_ack = true; + + if (header.type == SIMLIB_MSG_NOP) { + if (header.length != 0) { + fprintf(stderr, "[SIMLIB] FATAL: protocol error (3)\n"); + fflush(stderr); + exit(-1); + } + } else if (header.type == SIMLIB_MSG_PACKET) { + // Network packet + int32_t vlan = be32toh( *(int32_t*)(buf)); + simlib_send_packet(vlan, buf + 4, header.length - 4); + } else if (header.type == SIMLIB_MSG_CONSOLE) { + // console message, consists of index and data + int32_t index = be32toh( *(int32_t*)(buf)); + simlib_chr_read(index, buf + 4, header.length - 4); + /* + } else if (header.type == SIMLIB_MSG_REQUEST_SCREENSHOT) { + vga_hw_screen_dump("simlib"); + } else if (header.type == SIMLIB_MSG_KEYCODE) { + // only two bytes allowed + if (header.length != 2) { + fprintf(stderr, "FATAL: invalid length %d for keycode msg\n", + header.length); + fflush(stderr); + exit(-1); + } + // read code + uint8_t action = *(uint8_t*) (buf); + uint8_t keycode = *(uint8_t*) (buf + 1); + + // do it (as in monitor.c) + if (action) { + // press + if (keycode & 0x80) + kbd_put_keycode(0xe0); + kbd_put_keycode(keycode & 0x7f); + } else { + // release + if (keycode & 0x80) + kbd_put_keycode(0xe0); + kbd_put_keycode(keycode | 0x80); + }*/ + } else if (header.type == SIMLIB_MSG_TIMEOUT) { + max_iterations_before_return = be32toh( *(int32_t*)(buf)); + } else { + fprintf(stderr, "FATAL: protocol error (99) message type=%d unknown\n", + header.type); + fflush(stderr); + exit(-1); + } +} + +/******************************************************************** + * control messages QEMU -> SimLib + *******************************************************************/ + +#define SIMLIB_QUEUE_LENGTH (10*4*1024*1024) +unsigned char queue[SIMLIB_QUEUE_LENGTH]; +int offset = 0; +int messages_in_queue = 0; + +static void simlib_flush_messages(void) { + int repeat = 0; + int pos = 0; + int len = offset; + while (len > 0) { + int r = write(simlib_fd_w, queue + pos, len); + if (r >= 0) { + pos += r; + len -= r; + } else if ((errno == EAGAIN) || (errno == EINTR)) { + fprintf(stderr, "[SIMLIB] write to pipe failed, trying again\n"); + repeat++; + if (repeat > 1000) { + fprintf(stderr, + "[SIMLIB] FATAL: write failed %d times - giving up\n", + repeat); + fflush(stderr); + exit(1); + } + } else { + fprintf(stderr, "FATAL: Error while writing (%s r=%d errno=%d )\n", + strerror(errno), r, errno); + fflush(stderr); + exit(1); + } + } + offset = 0; + messages_in_queue = 0; +} + +void simlib_write_message(const unsigned char * buf, int len) { + if (len > SIMLIB_QUEUE_LENGTH) { + fprintf(stderr, "message exceeds overall queue length\n"); + fflush(stderr); + exit(1); + } + if (offset + len > SIMLIB_QUEUE_LENGTH) { + fprintf(stderr, "message exceeds queue length\n"); + simlib_flush_messages(); + } + memcpy(queue + offset, buf, len); + offset += len; + messages_in_queue++; +} + +/* + * acknowledge last timestamp and tell the SimLib that we are idle + * in addition, we send the value of the timeout flag to count these + * events in the SimLib + */ +static void simlib_signal_idle(bool maxIterationsTriggered) { + + //sanity check + if (simlib_waits_for_ack() == false) { + fprintf(stderr, "We: we shouldn't send this again...\n"); + fflush(stderr); + _exit(15); + } + + // next timer deadline + int64_t next_deadline_ns = qemu_next_alarm_deadline(); + if (next_deadline_ns == INT64_MAX && !maxIterationsTriggered) { + /* + * if not max iterations triggered, we cannot assume that a timer has been set + */ + fprintf(stderr, "[SIMLIB] no qemu-internal timer programmed?\n"); + fflush(stderr); + abort(); + } + + simlib_expected_next_wakeup = simlib_current_time + next_deadline_ns; + + // the message to send + unsigned char msg[4 + 4 + 8 + 8 + 4]; + *((int32_t*) (msg + 0)) = htobe32(SIMLIB_MSG_IDLE); + *((int32_t*) (msg + 4)) = htobe32(8+8+4); + *((int64_t*) (msg + 8)) = htobe64(simlib_current_time); + *((int64_t*) (msg + 16)) = htobe64(next_deadline_ns); + *((int32_t*) (msg + 24)) = htobe32(!(maxIterationsTriggered)); + + simlib_write_message(msg, 4 + 4 + 8 + 8 + 4); + simlib_flush_messages(); + + // reset flags + _simlib_waits_for_ack = false; +} + +/******************************************************************** + * initialization + *******************************************************************/ + +const char * simlib_pipe_read = 0; +const char * simlib_pipe_write = 0; + +static void simlib_init(void) { + assert(simlib_fd_r == -1); + assert(simlib_fd_w == -1); + + if (!simlib_pipe_read || !simlib_pipe_write) { + fprintf(stderr, "FATAL: need -simlib pipe_read,pipe_write\n"); + fflush(stderr); + exit(-1); + } + + simlib_fd_r = open(simlib_pipe_read, O_RDONLY); + simlib_fd_w = open(simlib_pipe_write, O_WRONLY); + + if (simlib_fd_r < 0 || simlib_fd_w < 0) { + fprintf(stderr, "FATAL: couldn't open simlib control pipes\n"); + fflush(stderr); + _exit(-1); + } + + // register read callback + qemu_set_fd_handler2(simlib_fd_r, NULL, simlib_read, NULL, (void *) NULL); +} + +void simlib_configure(QemuOpts* opts) { + simlib_pipe_read = qemu_opt_get(opts, "read"); + simlib_pipe_write = qemu_opt_get(opts, "write"); + + if (!simlib_pipe_read || !simlib_pipe_write) { + fprintf(stderr, + "qemu: invalid option values for -simlib\nusage: -simlib read=$file,write=$file\n"); + fflush(stderr); + exit(1); + } else { + fprintf(stdout, "[SIMLIB] using read pipe %s and write pipe %s\n", + simlib_pipe_read, simlib_pipe_write); + } + // at this time we cannot open the pipes. why? + //TODO use chardev infrastructure instead??? +} diff --git a/simlib.h b/simlib.h new file mode 100644 index 000000000000..2fa4582242c3 --- /dev/null +++ b/simlib.h @@ -0,0 +1,64 @@ +#ifndef INCLUDE_SIMLIB_H +#define INCLUDE_SIMLIB_H + +#include +#include +#include "qemu/option.h" + +/******************************************************************** + * simlib things required elsewhere in the code + *******************************************************************/ + +#define SIMLIB_MSG_NOP 1 +#define SIMLIB_MSG_PACKET 2 +#define SIMLIB_MSG_IDLE 17 +#define SIMLIB_MSG_CONSOLE 3 +//#define SIMLIB_MSG_SCREENSHOT 4 +//#define SIMLIB_MSG_REQUEST_SCREENSHOT 5 +//#define SIMLIB_MSG_KEYCODE 6 +#define SIMLIB_MSG_TIMEOUT 7 + +#define CPU_EXEC_MAX_ITERATIONS 20000l + +// simlib enabled? +void enable_simlib(void); +bool is_simlib_enabled(void); + +// main loop and alarm events +void simlib_main_loop_hook(int last_io); +int simlib_get_max_iterations_enabled(void); +bool simlib_waits_for_ack(void); +int64_t simlib_get_time(void); + +// control messages QEMU -> SimLib +void simlib_write_message(const unsigned char * buf, int len); + +// initialization +void simlib_configure(QemuOpts* opts); + +/******************************************************************** + * external methods required for the simlib interaction + *******************************************************************/ + +/* + * defined in qemu-timer.c; unknown whether it is really needed to call this + */ +void simlib_alarm_handler(void); + +/* + * defined in cpu-exec.c; counts executed TCG blocks to be able to force control back to simlib + */ +extern volatile long long cpu_exec_iterations; + +/* + * defined in cpus.c; required to recognize that the CPU has become idle to give control back to simlib + */ +bool my_all_cpu_threads_idle(void); + +/* + * defined in qemu-timer.c; required to determine the next timer deadline to signal + * to the simlib when going idle + */ +int64_t qemu_next_alarm_deadline(void); + +#endif diff --git a/vl.c b/vl.c index f422a1cae43f..cb0d35cbf7ff 100644 --- a/vl.c +++ b/vl.c @@ -171,6 +171,10 @@ int main(int argc, char **argv) #include "ui/qemu-spice.h" #include "qapi/string-input-visitor.h" +#ifdef CONFIG_SIMLIB +#include "simlib.h" +#endif + //#define DEBUG_NET //#define DEBUG_SLIRP @@ -528,6 +532,24 @@ static QemuOptsList qemu_msg_opts = { }, }; +#ifdef CONFIG_SIMLIB +static QemuOptsList qemu_simlib_opts = { + .name = "simlib", + .head = QTAILQ_HEAD_INITIALIZER(qemu_simlib_opts.head), + .desc = { + { + .name = "read", + .type = QEMU_OPT_STRING, + }, + { + .name = "write", + .type = QEMU_OPT_STRING, + }, + { /* end of list */ } + }, +}; +#endif + /** * Get machine options * @@ -2083,6 +2105,9 @@ static void main_loop(void) int64_t ti; #endif do { +#ifdef CONFIG_SIMLIB + simlib_main_loop_hook(last_io); +#endif nonblocking = !kvm_enabled() && !xen_enabled() && last_io > 0; #ifdef CONFIG_PROFILER ti = profile_getclock(); @@ -2961,6 +2986,9 @@ int main(int argc, char **argv, char **envp) qemu_add_opts(&qemu_tpmdev_opts); qemu_add_opts(&qemu_realtime_opts); qemu_add_opts(&qemu_msg_opts); +#ifdef CONFIG_SIMLIB + qemu_add_opts(&qemu_simlib_opts); +#endif runstate_init(); @@ -3864,6 +3892,15 @@ int main(int argc, char **argv, char **envp) } configure_msg(opts); break; +#ifdef CONFIG_SIMLIB + case QEMU_OPTION_simlib: + opts = qemu_opts_parse(qemu_find_opts("simlib"), optarg, 0); + if (!opts) { + exit(1); + } + simlib_configure(opts); + break; +#endif default: os_parse_cmd_args(popt->index, optarg); }