* [Qemu-devel] [PATCH 0/7] Qemu scenario engine
@ 2015-09-11 12:50 Victor CLEMENT
2015-09-11 12:50 ` [Qemu-devel] [PATCH 1/7] configure: add --enable-scenario-engine option Victor CLEMENT
` (7 more replies)
0 siblings, 8 replies; 10+ messages in thread
From: Victor CLEMENT @ 2015-09-11 12:50 UTC (permalink / raw)
To: qemu-devel; +Cc: victor.clement, Victor CLEMENT, julien.viarddegalbert
This patch introduce a testing component for Qemu guest OS: the scenario
engine.
The Qemu scenario engine is a framework designed to test the software running
on the guest machine. It allows interacting with the guest virtual devices by
stimulating inputs and reading outputs from outside of the virtual machine.
It also provides an utility to schedule precise time based events to run
through a "test scenario".
As it is a whole subsystem, a github repository providing a complete
documentation and a demonstration is available at the following address:
https://github.com/Openwide-Ingenierie/qemu-scenario-engine-demo
Victor CLEMENT (7):
configure: add --enable-scenario-engine option
gpio-pl061: add a scenario engine interaction API
chardev: add a scenario engine backend
scenario-engine: add utilities
scenario-engine: add a time based event scheduler
scenario engine: provide a scenario file template
scenario engine: add a Qemu option to start it
Makefile.objs | 8 ++
backends/Makefile.objs | 1 +
backends/scenario.c | 107 +++++++++++++++++++++++++
configure | 8 ++
hw/gpio/pl061.c | 38 +++++++++
include/hw/gpio/pl061_simu.h | 20 +++++
include/scenario/scenario.h | 5 ++
include/scenario/scheduler.h | 26 ++++++
include/scenario/utils.h | 23 ++++++
include/sysemu/char.h | 13 +++
qapi-schema.json | 3 +-
qemu-char.c | 5 ++
qemu-options.hx | 12 +++
scenario/scenario.c | 168 ++++++++++++++++++++++++++++++++++++++
scenario/scheduler.c | 186 +++++++++++++++++++++++++++++++++++++++++++
scenario/utils.c | 65 +++++++++++++++
vl.c | 45 ++++++++++-
17 files changed, 731 insertions(+), 2 deletions(-)
create mode 100644 backends/scenario.c
create mode 100644 include/hw/gpio/pl061_simu.h
create mode 100644 include/scenario/scenario.h
create mode 100644 include/scenario/scheduler.h
create mode 100644 include/scenario/utils.h
create mode 100644 scenario/scenario.c
create mode 100644 scenario/scheduler.c
create mode 100644 scenario/utils.c
--
2.5.1
^ permalink raw reply [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH 1/7] configure: add --enable-scenario-engine option
2015-09-11 12:50 [Qemu-devel] [PATCH 0/7] Qemu scenario engine Victor CLEMENT
@ 2015-09-11 12:50 ` Victor CLEMENT
2015-09-11 12:50 ` [Qemu-devel] [PATCH 2/7] gpio-pl061: add a scenario engine interaction API Victor CLEMENT
` (6 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Victor CLEMENT @ 2015-09-11 12:50 UTC (permalink / raw)
To: qemu-devel; +Cc: victor.clement, Victor CLEMENT, julien.viarddegalbert
Add an option to ./configure script to enable the scenario engine.
It defines the CONFIG_SCENARIO constant.
Signed-off-by: Victor CLEMENT <victor.clement@openwide.fr>
---
configure | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/configure b/configure
index cd219d8..466a71f 100755
--- a/configure
+++ b/configure
@@ -300,6 +300,7 @@ pkgversion=""
pie=""
zero_malloc=""
qom_cast_debug="yes"
+scenario="no"
trace_backends="nop"
trace_file="trace"
spice=""
@@ -788,6 +789,8 @@ for opt do
;;
--target-list=*) target_list="$optarg"
;;
+ --enable-scenario-engine) scenario="yes"
+ ;;
--enable-trace-backends=*) trace_backends="$optarg"
;;
# XXX: backwards compatibility
@@ -4552,6 +4555,7 @@ echo "uuid support $uuid"
echo "libcap-ng support $cap_ng"
echo "vhost-net support $vhost_net"
echo "vhost-scsi support $vhost_scsi"
+echo "Scenario engine $scenario"
echo "Trace backends $trace_backends"
if test "$trace_backend" = "simple"; then
echo "Trace output file $trace_file-<pid>"
@@ -5084,6 +5088,10 @@ if test "$tpm" = "yes"; then
fi
fi
+if test "$scenario" = "yes" ; then
+ echo "CONFIG_SCENARIO=y" >> $config_host_mak
+fi
+
echo "TRACE_BACKENDS=$trace_backends" >> $config_host_mak
if have_backend "nop"; then
echo "CONFIG_TRACE_NOP=y" >> $config_host_mak
--
2.5.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH 2/7] gpio-pl061: add a scenario engine interaction API
2015-09-11 12:50 [Qemu-devel] [PATCH 0/7] Qemu scenario engine Victor CLEMENT
2015-09-11 12:50 ` [Qemu-devel] [PATCH 1/7] configure: add --enable-scenario-engine option Victor CLEMENT
@ 2015-09-11 12:50 ` Victor CLEMENT
2015-09-11 12:50 ` [Qemu-devel] [PATCH 3/7] chardev: add a scenario engine backend Victor CLEMENT
` (5 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Victor CLEMENT @ 2015-09-11 12:50 UTC (permalink / raw)
To: qemu-devel; +Cc: victor.clement, Victor CLEMENT, julien.viarddegalbert
Add a scenario API to interact with guest GPIOs.
It implements read and write functions in order to check or modify the
guest GPIO data register from the scenario test file.
It also provides an event notification callback to the scenario engine
by calling a user-defined handler after each "pl061_update".
Signed-off-by: Victor CLEMENT <victor.clement@openwide.fr>
---
hw/gpio/pl061.c | 38 ++++++++++++++++++++++++++++++++++++++
include/hw/gpio/pl061_simu.h | 20 ++++++++++++++++++++
2 files changed, 58 insertions(+)
create mode 100644 include/hw/gpio/pl061_simu.h
diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c
index 4ba730b..b20885a 100644
--- a/hw/gpio/pl061.c
+++ b/hw/gpio/pl061.c
@@ -9,6 +9,7 @@
*/
#include "hw/sysbus.h"
+#include "hw/gpio/pl061_simu.h"
//#define DEBUG_PL061 1
@@ -60,6 +61,10 @@ typedef struct PL061State {
qemu_irq irq;
qemu_irq out[8];
const unsigned char *id;
+#ifdef CONFIG_SCENARIO
+ simu_event_cb_t *simu_cb;
+ unsigned int simu_id;
+#endif
} PL061State;
static const VMStateDescription vmstate_pl061 = {
@@ -145,6 +150,13 @@ static void pl061_update(PL061State *s)
DPRINTF("istate = %02X\n", s->istate);
+#ifdef CONFIG_SCENARIO
+ if (s->simu_cb) {
+ s->simu_cb(s, TYPE_PL061, s->simu_id);
+ }
+#endif
+
+
qemu_set_irq(s->irq, (s->istate & s->im) != 0);
}
@@ -205,6 +217,32 @@ static uint64_t pl061_read(void *opaque, hwaddr offset,
}
}
+#ifdef CONFIG_SCENARIO
+void pl061_simu_write(void *opaque, uint8_t mask, uint8_t value)
+{
+ PL061State *s = (PL061State *)opaque;
+
+ s->data = (s->data & ~mask) | (value & mask);
+ pl061_update(s);
+}
+uint8_t pl061_simu_read(void *opaque, uint8_t mask)
+{
+ PL061State *s = (PL061State *)opaque;
+
+ return s->data & mask;
+}
+void pl061_simu_register_cb(void *opaque, unsigned int simu_id, void *opaque_cb)
+{
+ PL061State *s = PL061(opaque);
+ simu_event_cb_t *cb = (simu_event_cb_t *) opaque_cb;
+
+ if (!(s->simu_cb)) {
+ s->simu_cb = cb;
+ s->simu_id = simu_id;
+ }
+}
+#endif
+
static void pl061_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
diff --git a/include/hw/gpio/pl061_simu.h b/include/hw/gpio/pl061_simu.h
new file mode 100644
index 0000000..66e20bf
--- /dev/null
+++ b/include/hw/gpio/pl061_simu.h
@@ -0,0 +1,20 @@
+#ifdef CONFIG_SCENARIO
+
+#ifndef PL061_SIMU_H
+#define PL061_SIMU_H
+
+typedef void simu_event_cb_t(void *opaque,
+ const char *type,
+ unsigned int simu_id);
+
+void pl061_simu_write(void *opaque, uint8_t mask, uint8_t value);
+
+uint8_t pl061_simu_read(void *opaque, uint8_t mask);
+
+void pl061_simu_register_cb(void *opaque,
+ unsigned int simu_id,
+ void *opaque_cb);
+
+#endif
+
+#endif
--
2.5.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH 3/7] chardev: add a scenario engine backend
2015-09-11 12:50 [Qemu-devel] [PATCH 0/7] Qemu scenario engine Victor CLEMENT
2015-09-11 12:50 ` [Qemu-devel] [PATCH 1/7] configure: add --enable-scenario-engine option Victor CLEMENT
2015-09-11 12:50 ` [Qemu-devel] [PATCH 2/7] gpio-pl061: add a scenario engine interaction API Victor CLEMENT
@ 2015-09-11 12:50 ` Victor CLEMENT
2015-09-11 13:11 ` Eric Blake
2015-09-11 12:50 ` [Qemu-devel] [PATCH 4/7] scenario-engine: add utilities Victor CLEMENT
` (4 subsequent siblings)
7 siblings, 1 reply; 10+ messages in thread
From: Victor CLEMENT @ 2015-09-11 12:50 UTC (permalink / raw)
To: qemu-devel; +Cc: victor.clement, Victor CLEMENT, julien.viarddegalbert
This scenario backend implements the scenario interaction API for all
character device emulators.
It provides a callback registration function which registers a chr_write
callback. It will call the user defined callback on incoming data.
To write to this character device, qemu_chr_be_write can be used.
Signed-off-by: Victor CLEMENT <victor.clement@openwide.fr>
---
backends/Makefile.objs | 1 +
backends/scenario.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++
include/sysemu/char.h | 13 ++++++
qapi-schema.json | 3 +-
qemu-char.c | 5 +++
5 files changed, 128 insertions(+), 1 deletion(-)
create mode 100644 backends/scenario.c
diff --git a/backends/Makefile.objs b/backends/Makefile.objs
index 31a3a89..6be07dc 100644
--- a/backends/Makefile.objs
+++ b/backends/Makefile.objs
@@ -1,6 +1,7 @@
common-obj-y += rng.o rng-egd.o
common-obj-$(CONFIG_POSIX) += rng-random.o
+common-obj-$(CONFIG_SCENARIO) += scenario.o
common-obj-y += msmouse.o testdev.o
common-obj-$(CONFIG_BRLAPI) += baum.o
baum.o-cflags := $(SDL_CFLAGS)
diff --git a/backends/scenario.c b/backends/scenario.c
new file mode 100644
index 0000000..35dd9d2
--- /dev/null
+++ b/backends/scenario.c
@@ -0,0 +1,107 @@
+/*
+ * QEMU Scenario engine chardev backend
+ *
+ * Copyright (c) 2015 Open Wide Ingénierie
+ *
+ * Author: Victor Clément
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include "qemu-common.h"
+#include "qmp-commands.h"
+#include "sysemu/char.h"
+
+static int scenario_chr_write(struct CharDriverState *s,
+ const uint8_t *buf,
+ int len)
+{
+ static bool notified;
+ if (!notified) {
+ printf("Write to scenario chardev %s with no custom callback\n",
+ s->label);
+ notified = true;
+ }
+ return len;
+}
+
+
+static bool scenario_create_chardev(const char *name)
+{
+ ChardevBackend *backend = g_new0(ChardevBackend, 1);
+ backend->kind = CHARDEV_BACKEND_KIND_SCENARIO;
+ if (qmp_chardev_add(name, backend, NULL)) {
+ printf("scenario: successfull creation of scenario chardev: %s\n",
+ name);
+ return true;
+ } else {
+ printf("scenario: error creating scenario chardev: %s\n", name);
+ return false;
+ }
+ qapi_free_ChardevBackend(backend);
+}
+
+void scenario_register_chardev_cb(const char *name, scenario_chardev_cb_t *cb,
+ bool create)
+{
+ CharDriverState *chr;
+ chr = qemu_chr_find(name);
+ if (chr) {
+ chr->chr_write = cb;
+ } else {
+ if (create) {
+ printf("scenario: WARNING: chardev \"%s\" not found, creating it\n",
+ name);
+ if (scenario_create_chardev(name)) {
+ chr = qemu_chr_find(name);
+ chr->chr_write = cb;
+ printf("scenario: INFO: chardev \"%s\" created\n", name);
+ } else {
+ printf("scenario: ERROR: chardev \"%s\" cannot be created\n",
+ name);
+ }
+ } else {
+ printf("scenario: WARNING: chardev \"%s\" not found\n", name);
+ }
+ }
+}
+
+static void scenario_chr_close(struct CharDriverState *chr)
+{
+ g_free(chr);
+}
+
+CharDriverState *qemu_chr_open_scenario(void)
+{
+ CharDriverState *chr;
+
+ chr = qemu_chr_alloc();
+ chr->chr_write = scenario_chr_write;
+ chr->chr_close = scenario_chr_close;
+ chr->explicit_be_open = true;
+
+ return chr;
+}
+
+static void register_types(void)
+{
+ register_char_driver("scenario", CHARDEV_BACKEND_KIND_SCENARIO, NULL);
+}
+
+type_init(register_types);
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 832b7fe..94bebf2 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -368,4 +368,17 @@ typedef CharDriverState *(VcHandler)(ChardevVC *vc);
void register_vc_handler(VcHandler *handler);
CharDriverState *vc_init(ChardevVC *vc);
+
+/* scenario.c */
+#ifdef CONFIG_SCENARIO
+CharDriverState *qemu_chr_open_scenario(void);
+
+typedef int scenario_chardev_cb_t(struct CharDriverState *s,
+ const uint8_t *buf,
+ int len);
+void scenario_register_chardev_cb(const char *name,
+ scenario_chardev_cb_t *cb,
+ bool create);
+#endif
+
#endif
diff --git a/qapi-schema.json b/qapi-schema.json
index 4342a08..2aed9da 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3042,7 +3042,8 @@
'vc' : 'ChardevVC',
'ringbuf': 'ChardevRingbuf',
# next one is just for compatibility
- 'memory' : 'ChardevRingbuf' } }
+ 'memory' : 'ChardevRingbuf',
+ 'scenario': 'ChardevDummy' } }
##
# @ChardevReturn:
diff --git a/qemu-char.c b/qemu-char.c
index d956f8d..717fafd 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -4295,6 +4295,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_SCENARIO
+ case CHARDEV_BACKEND_KIND_SCENARIO:
+ chr = qemu_chr_open_scenario();
+ break;
+#endif
default:
error_setg(errp, "unknown chardev backend (%d)", backend->kind);
break;
--
2.5.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH 4/7] scenario-engine: add utilities
2015-09-11 12:50 [Qemu-devel] [PATCH 0/7] Qemu scenario engine Victor CLEMENT
` (2 preceding siblings ...)
2015-09-11 12:50 ` [Qemu-devel] [PATCH 3/7] chardev: add a scenario engine backend Victor CLEMENT
@ 2015-09-11 12:50 ` Victor CLEMENT
2015-09-11 12:50 ` [Qemu-devel] [PATCH 5/7] scenario-engine: add a time based event scheduler Victor CLEMENT
` (3 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Victor CLEMENT @ 2015-09-11 12:50 UTC (permalink / raw)
To: qemu-devel; +Cc: victor.clement, Victor CLEMENT, julien.viarddegalbert
This scenario utilities file currently implements one function.
This function is the event callback registration function which walks
through all guest devices to find those of a given type then registers
their event callback.
Signed-off-by: Victor CLEMENT <victor.clement@openwide.fr>
---
Makefile.objs | 6 +++++
include/scenario/utils.h | 23 +++++++++++++++++
scenario/utils.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 94 insertions(+)
create mode 100644 include/scenario/utils.h
create mode 100644 scenario/utils.c
diff --git a/Makefile.objs b/Makefile.objs
index f094eff..3ebb694 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -105,6 +105,12 @@ util-obj-y += trace/
target-obj-y += trace/
######################################################################
+
+######################################################################
+# scenario
+util-obj-$(CONFIG_SCENARIO) += scenario/utils.o
+
+######################################################################
# guest agent
# FIXME: a few definitions from qapi-types.o/qapi-visit.o are needed
diff --git a/include/scenario/utils.h b/include/scenario/utils.h
new file mode 100644
index 0000000..56066e9
--- /dev/null
+++ b/include/scenario/utils.h
@@ -0,0 +1,23 @@
+#ifndef SCENARIO_UTILS_H
+#define SCENARIO_UTILS_H
+
+/*
+ * Every emulator callback register should implement this prototype in order to
+ * allow the recursive callback registration function to call it
+ */
+typedef void scenario_emulator_cb_register_t \
+ (void *opaque, unsigned int id, void *emulator_cb);
+
+/*
+ * Registers emulator callbacks of every devices of specified type
+ *
+ * type: a string representing the target device emulator type (ex. "pl061")
+ * cb_register: the device emulator scenario API callback register function
+ * emulator_cb: the user implemented callback to register (its prototype should
+ * be predefined in the device emulator API)
+ */
+unsigned int scenario_register_emulator_cb(const char *type,
+ scenario_emulator_cb_register_t *cb_register,
+ void *emulator_cb);
+
+#endif
diff --git a/scenario/utils.c b/scenario/utils.c
new file mode 100644
index 0000000..e64699a
--- /dev/null
+++ b/scenario/utils.c
@@ -0,0 +1,65 @@
+#include "scenario/utils.h"
+#include "hw/qdev-core.h"
+#include "stdio.h"
+
+// #define DEBUG_SCENARIO_UTILS
+
+#ifdef DEBUG_SCENARIO_UTILS
+#define DPRINTF(fmt, ...) \
+ do { printf("scenario: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+/*
+ * Recursively walk through all devices from system bus to find devices
+ * corresponding to name parameter then registers their scenario callbacks
+ *
+ * bus: current bus
+ * name: target device type name
+ * n: current scenario device identifier
+ * cb_register: emulator callaback register function
+ * emulator_cb: emulator callback to set
+ */
+static DeviceState *register_simu_callback_recursive(BusState *bus,
+ const char *name,
+ unsigned int *n,
+ scenario_emulator_cb_register_t *cb_register,
+ void *emulator_cb)
+{
+ BusChild *kid;
+ DeviceState *ret;
+ BusState *child;
+
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ DeviceState *dev = kid->child;
+
+ if (strcmp(object_get_typename(OBJECT(dev)), name) == 0) {
+ (*n)++;
+ DPRINTF("Device %s #%d found\n", name, *n);
+ cb_register(dev, *n, emulator_cb);
+ }
+
+ QLIST_FOREACH(child, &dev->child_bus, sibling) {
+ ret = register_simu_callback_recursive(
+ child, name, n, cb_register, emulator_cb);
+ if (ret) {
+ return ret;
+ }
+ }
+ }
+ return NULL;
+}
+
+unsigned int scenario_register_emulator_cb(const char *type,
+ scenario_emulator_cb_register_t *cb_register,
+ void *emulator_cb)
+{
+ unsigned int n = 0;
+ register_simu_callback_recursive(sysbus_get_default(), type, &n,
+ cb_register, emulator_cb);
+ if (!n) {
+ printf("scenario: WARNING: no \"%s\" devices found\n", type);
+ }
+ return n;
+}
--
2.5.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH 5/7] scenario-engine: add a time based event scheduler
2015-09-11 12:50 [Qemu-devel] [PATCH 0/7] Qemu scenario engine Victor CLEMENT
` (3 preceding siblings ...)
2015-09-11 12:50 ` [Qemu-devel] [PATCH 4/7] scenario-engine: add utilities Victor CLEMENT
@ 2015-09-11 12:50 ` Victor CLEMENT
2015-09-11 12:50 ` [Qemu-devel] [PATCH 6/7] scenario engine: provide a scenario file template Victor CLEMENT
` (2 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Victor CLEMENT @ 2015-09-11 12:50 UTC (permalink / raw)
To: qemu-devel; +Cc: victor.clement, Victor CLEMENT, julien.viarddegalbert
The scheduler reads an event description file containing timestamps and
parameters then schedules those events. A user defined function is
called when those events expire. It is used for precise time
simulations.
Signed-off-by: Victor CLEMENT <victor.clement@openwide.fr>
---
Makefile.objs | 1 +
include/scenario/scheduler.h | 26 ++++++
scenario/scheduler.c | 186 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 213 insertions(+)
create mode 100644 include/scenario/scheduler.h
create mode 100644 scenario/scheduler.c
diff --git a/Makefile.objs b/Makefile.objs
index 3ebb694..112794d 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -108,6 +108,7 @@ target-obj-y += trace/
######################################################################
# scenario
+util-obj-$(CONFIG_SCENARIO) += scenario/scheduler.o
util-obj-$(CONFIG_SCENARIO) += scenario/utils.o
######################################################################
diff --git a/include/scenario/scheduler.h b/include/scenario/scheduler.h
new file mode 100644
index 0000000..03a0e92
--- /dev/null
+++ b/include/scenario/scheduler.h
@@ -0,0 +1,26 @@
+#include "qemu/timer.h"
+#ifndef SCENARIO_H
+#define SCENARIO_H
+
+/*
+ * Prototype of callback function to implement in a scenario using scheduler
+ *
+ * opaque: an opaque pointer you choose when calling sched_start
+ * ts: the timestamp triggering this callback
+ * param: the parameter string associated to this timestamp
+ */
+typedef void scenario_scheduler_cb_t(void *opaque, uint64_t ts, char *param);
+
+/*
+ * Call this function to start the scheduler
+ *
+ * cb: the callback function to call when a scheduled event occurs
+ * opaque: the opaque pointer to give to the callback
+ * file: a file with timestamps and optional parameters associated
+ */
+int scenario_scheduler_start(scenario_scheduler_cb_t *cb,
+ void *opaque,
+ const char *file);
+
+#endif
+
diff --git a/scenario/scheduler.c b/scenario/scheduler.c
new file mode 100644
index 0000000..342a73a
--- /dev/null
+++ b/scenario/scheduler.c
@@ -0,0 +1,186 @@
+#include "scenario/scheduler.h"
+
+// #define DEBUG_SCHEDULER
+
+#ifdef DEBUG_SCHEDULER
+#define DPRINTF(fmt, ...) \
+ do { printf("scheduler: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define EPRINTF(fmt, ...) \
+ do { printf("scheduler: " fmt, ## __VA_ARGS__); } while (0)
+
+#define MAX_TS 1000
+#define MAX_LINE 1000
+
+struct simu_event_t {
+ uint64_t ts;
+ char param[MAX_LINE+1];
+};
+
+/*
+ * This type of struct will be passed to timers.
+ *
+ * It contains the callback, its opaque pointer, and every information to
+ * schedule the next timer.
+ */
+struct scheduler_struct {
+ scenario_scheduler_cb_t *cb;
+ void *opaque;
+ QEMUTimer *timer;
+ unsigned int next_ts;
+ struct simu_event_t event_list[MAX_TS];
+ uint64_t start_ts;
+};
+
+/*
+ * Function to parse "vcd" like files
+ * Those file will contain a list of timestamp, on per line beginning with '#'.
+ * Each timestamp could be followed by one parameter line beginning with '%',
+ * the rest of this line will be passed to the scenario callback.
+ *
+ * vcd_fname: the filename
+ * event_list: a pointer to an allocated array of simu_event_t
+ * max_length: the maximal number of events in the file
+ * (aka the event_list length)
+ *
+ * returns the number of events read or -1 if the simulation file is not found
+ */
+static int parse_vcd(const char *vcd_fname,
+ struct simu_event_t *event_list,
+ unsigned int max_length)
+{
+ FILE *vcd_file;
+ char line[MAX_LINE];
+ uint32_t line_len;
+ unsigned int i;
+ int n;
+ uint64_t timestamp = 0;
+ uint64_t last_timestamp = 0;
+ bool was_timestamp = false;
+
+ vcd_file = fopen(vcd_fname, "r");
+ if (vcd_file) {
+ DPRINTF("Event file opened\n");
+
+ i = 1;
+ n = -1;
+ while (!feof(vcd_file) && (i < max_length)) {
+ if (fgets(line, MAX_LINE+1, vcd_file)) {
+ line_len = strlen(line);
+ if (line_len < MAX_LINE) {
+ if ((line_len == 0) || (line[0] == '\n')) {
+ DPRINTF("Line %u is empty\n", i);
+ } else if (line[0] == '#') {
+ timestamp = strtoul(line+1, NULL, 0);
+ if (timestamp) {
+ if (timestamp <= last_timestamp) {
+ EPRINTF("Erroneous timestamp dropped (%lu)\n",
+ timestamp);
+ } else {
+ DPRINTF("Timestamp found: %lu\n", timestamp);
+ n++;
+ event_list[n].ts = timestamp;
+ event_list[n].param[0] = '\0';
+ last_timestamp = timestamp;
+ was_timestamp = true;
+ }
+ } else {
+ EPRINTF("Bad timestamp dropped at line %u: %s",
+ i, line);
+ }
+ } else if (line[0] == '%') {
+ if (was_timestamp) {
+ DPRINTF("Parameter found: %s", line+1);
+ pstrcpy(event_list[n].param, line_len-1, line+1);
+ was_timestamp = false;
+ } else {
+ EPRINTF("Bad parameter dropped at line %u: %s",
+ i, line);
+ }
+ } else if (line[0] == ';') {
+ DPRINTF("Skip comment: %s", line+1);
+ } else {
+ EPRINTF("Unknown line beginning char\n");
+ }
+ } else {
+ EPRINTF("Line #%u is too long\n", i);
+ while (!feof(vcd_file) && fgetc(vcd_file) != '\n') {
+ }
+ }
+ } else if (!feof(vcd_file)) {
+ EPRINTF("Error, reading line #%u: ", i);
+ perror("");
+ }
+ i++;
+ }
+ event_list[n+1].ts = 0;
+ DPRINTF("EOF\n");
+
+ } else {
+ event_list[0].ts = 0;
+ return -1;
+ }
+ fclose(vcd_file);
+ return ++n;
+}
+
+/*
+ * This callback is called when a scheduled timer expires
+ * It will call the "real" callback then schedule the next "non zero" event
+ */
+static void scheduler_cb(void *opaque)
+{
+ struct scheduler_struct *sched_opaque = (struct scheduler_struct *)opaque;
+
+ DPRINTF("Event @%luns\n",
+ sched_opaque->event_list[(sched_opaque->next_ts)-1].ts);
+ sched_opaque->cb(sched_opaque->opaque,
+ sched_opaque->event_list[(sched_opaque->next_ts)-1].ts,
+ sched_opaque->event_list[(sched_opaque->next_ts)-1].param);
+
+ if (sched_opaque->event_list[sched_opaque->next_ts].ts != 0) {
+ timer_mod(sched_opaque->timer,
+ sched_opaque->start_ts + \
+ sched_opaque->event_list[(sched_opaque->next_ts)++].ts);
+ }
+}
+
+int scenario_scheduler_start(scenario_scheduler_cb_t *cb,
+ void *opaque,
+ const char *file)
+{
+ static struct scheduler_struct sched_opaque;
+ static bool started;
+ int parsed;
+
+ if (!started) {
+ DPRINTF("Starting\n");
+ started = true;
+ parsed = parse_vcd(file, sched_opaque.event_list, MAX_TS);
+ if (parsed > 0) {
+ sched_opaque.cb = cb;
+ sched_opaque.opaque = opaque;
+ sched_opaque.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ scheduler_cb, &sched_opaque);
+ sched_opaque.start_ts = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ sched_opaque.next_ts = 0;
+ timer_mod(sched_opaque.timer,
+ sched_opaque.start_ts + \
+ sched_opaque.event_list[(sched_opaque.next_ts)++].ts);
+ DPRINTF("Started\n");
+
+ return 1;
+ } else if (parsed == 0) {
+ EPRINTF("error while reading file: no events found\n");
+ return 0;
+ } else {
+ EPRINTF("error while reading file: \"%s\" not found\n", file);
+ return -1;
+ }
+ } else {
+ return 2;
+ }
+}
--
2.5.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH 6/7] scenario engine: provide a scenario file template
2015-09-11 12:50 [Qemu-devel] [PATCH 0/7] Qemu scenario engine Victor CLEMENT
` (4 preceding siblings ...)
2015-09-11 12:50 ` [Qemu-devel] [PATCH 5/7] scenario-engine: add a time based event scheduler Victor CLEMENT
@ 2015-09-11 12:50 ` Victor CLEMENT
2015-09-11 12:50 ` [Qemu-devel] [PATCH 7/7] scenario engine: add a Qemu option to start it Victor CLEMENT
2015-09-11 13:11 ` [Qemu-devel] [PATCH 0/7] Qemu scenario engine Andreas Färber
7 siblings, 0 replies; 10+ messages in thread
From: Victor CLEMENT @ 2015-09-11 12:50 UTC (permalink / raw)
To: qemu-devel; +Cc: victor.clement, Victor CLEMENT, julien.viarddegalbert
This template implements the mandatory functions of the main scenario
source file and provides an example use case.
Signed-off-by: Victor CLEMENT <victor.clement@openwide.fr>
---
Makefile.objs | 1 +
include/scenario/scenario.h | 5 ++
scenario/scenario.c | 168 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 174 insertions(+)
create mode 100644 include/scenario/scenario.h
create mode 100644 scenario/scenario.c
diff --git a/Makefile.objs b/Makefile.objs
index 112794d..aa38f42 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -108,6 +108,7 @@ target-obj-y += trace/
######################################################################
# scenario
+util-obj-$(CONFIG_SCENARIO) += scenario/scenario.o
util-obj-$(CONFIG_SCENARIO) += scenario/scheduler.o
util-obj-$(CONFIG_SCENARIO) += scenario/utils.o
diff --git a/include/scenario/scenario.h b/include/scenario/scenario.h
new file mode 100644
index 0000000..50ffc6d
--- /dev/null
+++ b/include/scenario/scenario.h
@@ -0,0 +1,5 @@
+#include "scenario/scheduler.h"
+#include "hw/gpio/pl061_simu.h"
+
+void init_scenario_engine(const char *filename);
+
diff --git a/scenario/scenario.c b/scenario/scenario.c
new file mode 100644
index 0000000..a237ecb
--- /dev/null
+++ b/scenario/scenario.c
@@ -0,0 +1,168 @@
+/*
+ * This is the scenario example file
+ *
+ * This example writes on a gpio pin for each event in the simulation file
+ *
+ * The simulation file should contain timestamps and a (unique) optional
+ * parameter for each timestamp.
+ * A timestamp line begins with '#' and a parameter line begins with '%', the
+ * whole line after '%' will be passed to the callback.
+ * Timestamps unit is nanosecond after sched_start
+ *
+ * ex.:
+ * #10000
+ * %parameter for timestamp 10000
+ * %this parameter line will be ignored
+ * #20000
+ * #30000
+ * ...
+ */
+
+#include "scenario/scenario.h"
+#include "scenario/utils.h"
+#include "sysemu/char.h"
+#include "trace.h"
+
+// #define DEBUG_SCENARIO
+
+#ifdef DEBUG_SCENARIO
+#define DPRINTF(fmt, ...) \
+ do { printf("scenario: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+static const char *simu_file;
+
+/*
+ * This callback will be called for each event in the simulation file if the
+ * the scheduler is used
+ *
+ * opaque: the opaque pointer passed to sched_start
+ * ts: the value of the timestamp which triggered this callback
+ * param: the parameter associated whith this timestamp
+ */
+static void scenario_scheduler_cb(void *opaque, uint64_t ts, char *param)
+{
+ static uint8_t old_value = 0xFF;
+ uint8_t value;
+
+ DPRINTF("Scheduler callback\n");
+ /*
+ * Checking the parameter
+ * "" -> send a signal on the GPIO2 pin
+ * "check" -> check the value of the GPIO1 pin
+ */
+ if (!strcmp(param, "check")) {
+ value = pl061_simu_read(opaque, 01);
+ if (old_value == 0xFF) {
+ old_value = value;
+ } else {
+ if (((old_value^value) & 01) == 01) {
+ printf("Toogle okay @%lu\n", ts);
+ } else {
+ printf("Toogle error @%lu\n", ts);
+ }
+ old_value = old_value^01;
+ }
+ } else {
+ /*
+ * Send a signal to the GPIO2 pin
+ */
+ pl061_simu_write(opaque, 02, 02);
+ pl061_simu_write(opaque, 02, 00);
+ }
+
+ return;
+}
+
+/*
+ * The virtual device callback
+ * This function will be called on each event in the tested device emulator
+ *
+ * Its prototype have to be the same as the one defined in the scenario header
+ * of your emulator
+ * e.x: for the pl061, the header is located in include/hw/gpio/pl061_simu.h
+ * Here, there is a typedef which defines the callback prototype you would
+ * have to follow
+ *
+ * See scenario_register_emulator_cb documentation for more details
+ */
+static void scenario_device_cb(void *opaque,
+ const char *type,
+ unsigned int simu_id)
+{
+ DPRINTF("Device event callabck: (%s #%d)\n", type, simu_id);
+ /*
+ * Tracing events from here
+ */
+ trace_gpio_write(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
+ 0, pl061_simu_read(opaque, 0xFF));
+ /*
+ * Then try to start the scheduler at the first emulator event
+ *
+ * This call has no effect if the scheduler is already started (returns 2)
+ */
+ if (scenario_scheduler_start(scenario_scheduler_cb, opaque, simu_file) < 1)
+ {
+ printf("Warning: scenario scheduler did not start\n");
+ }
+}
+
+/*
+ * Function to send characters one by one to a character device
+ */
+static int send_chars(struct CharDriverState *chr, const char* buffer, int len)
+{
+ int i;
+ for (i = 0 ; i < len ; i++) {
+ qemu_chr_be_write(chr, (uint8_t *)(buffer+i), 1);
+ }
+ return len;
+}
+
+/*
+ * The character device emulator callback.
+ * It is called everytime data is written in the associated chardev frontend
+ * (ex. serial port)
+ *
+ * Its prototype is defined in include/sysemu/char.h (at the end)
+ *
+ * This callback have to be registered with the scenario_register_chardev_cb
+ * function
+ */
+static int char_simu_handler(struct CharDriverState *chr,
+ const uint8_t *buf,
+ int len)
+{
+ DPRINTF("Char custom handler called\n");
+ send_chars(chr, "Hi!\r\n", 5);
+ return len;
+}
+
+/*
+ * This is the entry point.
+ * This initialisation function is called when qemu starts, after devices init
+ *
+ * filename: the file name of the simulation file given in qemu invocation
+ */
+void init_scenario_engine(const char *filename)
+{
+ printf("Init simulation\n");
+ simu_file = filename;
+ /*
+ * Register device event callback
+ *
+ * e.x. pl061 event callback is registered to the function
+ * scenario_device_cb here
+ */
+ scenario_register_emulator_cb("pl061", pl061_simu_register_cb,
+ scenario_device_cb);
+ /*
+ * Register chardev event callback
+ *
+ * Here the event callback of the chardev identified by "scen0"
+ * is registered to char_simu_handler function
+ */
+ scenario_register_chardev_cb("scen0", char_simu_handler, true);
+}
--
2.5.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH 7/7] scenario engine: add a Qemu option to start it
2015-09-11 12:50 [Qemu-devel] [PATCH 0/7] Qemu scenario engine Victor CLEMENT
` (5 preceding siblings ...)
2015-09-11 12:50 ` [Qemu-devel] [PATCH 6/7] scenario engine: provide a scenario file template Victor CLEMENT
@ 2015-09-11 12:50 ` Victor CLEMENT
2015-09-11 13:11 ` [Qemu-devel] [PATCH 0/7] Qemu scenario engine Andreas Färber
7 siblings, 0 replies; 10+ messages in thread
From: Victor CLEMENT @ 2015-09-11 12:50 UTC (permalink / raw)
To: qemu-devel; +Cc: victor.clement, Victor CLEMENT, julien.viarddegalbert
Add an option to Qemu in order to start the scenario engine at the end
of Qemu initialization.
Signed-off-by: Victor CLEMENT <victor.clement@openwide.fr>
---
qemu-options.hx | 12 ++++++++++++
vl.c | 45 ++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 56 insertions(+), 1 deletion(-)
diff --git a/qemu-options.hx b/qemu-options.hx
index 77f5853..d157c2e 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3436,6 +3436,18 @@ The @code{-no-user-config} option makes QEMU not load any of the user-provided
config files on @var{sysconfdir}, but won't make it skip the QEMU-provided config
files from @var{datadir}.
ETEXI
+DEF("scenario", HAS_ARG, QEMU_OPTION_scenario,
+ "-scenario [file=<file>]\n"
+ " enable scenario engine\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -scenario [file=@var{file}]
+@findex -scenario
+
+Enable scenario engine.
+
+This option is only available if QEMU has been compiled with scenario engine.
+ETEXI
DEF("trace", HAS_ARG, QEMU_OPTION_trace,
"-trace [events=<file>][,file=<file>]\n"
" specify tracing options\n",
diff --git a/vl.c b/vl.c
index 0adbbd6..4fc6953 100644
--- a/vl.c
+++ b/vl.c
@@ -108,6 +108,8 @@ int main(int argc, char **argv)
#include "slirp/libslirp.h"
+#include "scenario/scenario.h"
+
#include "trace.h"
#include "trace/control.h"
#include "qemu/queue.h"
@@ -283,6 +285,22 @@ static QemuOptsList qemu_trace_opts = {
},
};
+static QemuOptsList qemu_scenario_opts = {
+ .name = "scenario",
+ .implied_opt_name = "file",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_scenario_opts.head),
+ .desc = {
+ {
+ .name = "file",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "param",
+ .type = QEMU_OPT_STRING,
+ },
+ { /* end of list */ }
+ },
+};
+
static QemuOptsList qemu_option_rom_opts = {
.name = "option-rom",
.implied_opt_name = "romfile",
@@ -2971,6 +2989,10 @@ int main(int argc, char **argv, char **envp)
.realloc = realloc_and_trace,
.free = free_and_trace,
};
+#ifdef CONFIG_SCENARIO
+ bool use_scenario_engine = false;
+ const char *simulation_file = NULL;
+#endif
const char *trace_events = NULL;
const char *trace_file = NULL;
ram_addr_t maxram_size;
@@ -3002,6 +3024,7 @@ int main(int argc, char **argv, char **envp)
qemu_add_opts(&qemu_global_opts);
qemu_add_opts(&qemu_mon_opts);
qemu_add_opts(&qemu_trace_opts);
+ qemu_add_opts(&qemu_scenario_opts);
qemu_add_opts(&qemu_option_rom_opts);
qemu_add_opts(&qemu_machine_opts);
qemu_add_opts(&qemu_mem_opts);
@@ -3902,6 +3925,22 @@ int main(int argc, char **argv, char **envp)
trace_file = qemu_opt_get(opts, "file");
break;
}
+ case QEMU_OPTION_scenario:
+ {
+#ifdef CONFIG_SCENARIO
+ opts = qemu_opts_parse_noisily(qemu_find_opts("scenario"),
+ optarg, false);
+ if (!opts) {
+ exit(1);
+ }
+ use_scenario_engine = true;
+ simulation_file = qemu_opt_get(opts, "file");
+#else
+ fprintf(stderr, "Scenario engine is disabled\n");
+ exit(1);
+#endif
+ break;
+ }
case QEMU_OPTION_readconfig:
{
int ret = qemu_read_config_file(optarg);
@@ -4649,7 +4688,11 @@ int main(int argc, char **argv, char **envp)
exit(1);
}
}
-
+#ifdef CONFIG_SCENARIO
+ if (use_scenario_engine) {
+ init_scenario_engine(simulation_file);
+ }
+#endif
main_loop();
bdrv_close_all();
pause_all_vcpus();
--
2.5.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH 3/7] chardev: add a scenario engine backend
2015-09-11 12:50 ` [Qemu-devel] [PATCH 3/7] chardev: add a scenario engine backend Victor CLEMENT
@ 2015-09-11 13:11 ` Eric Blake
0 siblings, 0 replies; 10+ messages in thread
From: Eric Blake @ 2015-09-11 13:11 UTC (permalink / raw)
To: Victor CLEMENT, qemu-devel; +Cc: victor.clement, julien.viarddegalbert
[-- Attachment #1: Type: text/plain, Size: 1138 bytes --]
On 09/11/2015 06:50 AM, Victor CLEMENT wrote:
> This scenario backend implements the scenario interaction API for all
> character device emulators.
> It provides a callback registration function which registers a chr_write
> callback. It will call the user defined callback on incoming data.
> To write to this character device, qemu_chr_be_write can be used.
>
> Signed-off-by: Victor CLEMENT <victor.clement@openwide.fr>
> ---
> +++ b/qapi-schema.json
> @@ -3042,7 +3042,8 @@
> 'vc' : 'ChardevVC',
> 'ringbuf': 'ChardevRingbuf',
> # next one is just for compatibility
> - 'memory' : 'ChardevRingbuf' } }
> + 'memory' : 'ChardevRingbuf',
> + 'scenario': 'ChardevDummy' } }
Missing documentation of the new union branch, including mention that it
was added in 2.5.
--
Eric Blake eblake redhat com +1-919-301-3266
Libvirt virtualization library http://libvirt.org
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH 0/7] Qemu scenario engine
2015-09-11 12:50 [Qemu-devel] [PATCH 0/7] Qemu scenario engine Victor CLEMENT
` (6 preceding siblings ...)
2015-09-11 12:50 ` [Qemu-devel] [PATCH 7/7] scenario engine: add a Qemu option to start it Victor CLEMENT
@ 2015-09-11 13:11 ` Andreas Färber
7 siblings, 0 replies; 10+ messages in thread
From: Andreas Färber @ 2015-09-11 13:11 UTC (permalink / raw)
To: Victor CLEMENT, qemu-devel; +Cc: victor.clement, julien.viarddegalbert
Am 11.09.2015 um 14:50 schrieb Victor CLEMENT:
> This patch introduce a testing component for Qemu guest OS: the scenario
> engine.
>
> The Qemu scenario engine is a framework designed to test the software running
> on the guest machine. It allows interacting with the guest virtual devices by
> stimulating inputs and reading outputs from outside of the virtual machine.
> It also provides an utility to schedule precise time based events to run
> through a "test scenario".
>
> As it is a whole subsystem, a github repository providing a complete
> documentation and a demonstration is available at the following address:
> https://github.com/Openwide-Ingenierie/qemu-scenario-engine-demo
This is lacking an explanation of why this cannot be done with our
existing QTest interface.
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton; HRB 21284 (AG Nürnberg)
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2015-09-11 13:11 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-09-11 12:50 [Qemu-devel] [PATCH 0/7] Qemu scenario engine Victor CLEMENT
2015-09-11 12:50 ` [Qemu-devel] [PATCH 1/7] configure: add --enable-scenario-engine option Victor CLEMENT
2015-09-11 12:50 ` [Qemu-devel] [PATCH 2/7] gpio-pl061: add a scenario engine interaction API Victor CLEMENT
2015-09-11 12:50 ` [Qemu-devel] [PATCH 3/7] chardev: add a scenario engine backend Victor CLEMENT
2015-09-11 13:11 ` Eric Blake
2015-09-11 12:50 ` [Qemu-devel] [PATCH 4/7] scenario-engine: add utilities Victor CLEMENT
2015-09-11 12:50 ` [Qemu-devel] [PATCH 5/7] scenario-engine: add a time based event scheduler Victor CLEMENT
2015-09-11 12:50 ` [Qemu-devel] [PATCH 6/7] scenario engine: provide a scenario file template Victor CLEMENT
2015-09-11 12:50 ` [Qemu-devel] [PATCH 7/7] scenario engine: add a Qemu option to start it Victor CLEMENT
2015-09-11 13:11 ` [Qemu-devel] [PATCH 0/7] Qemu scenario engine Andreas Färber
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).