[PATCH v5 12/19] Introduce virtio-testdev

Andrew Jones
2014-06-11T14:01:58Z

virtio-testdev is a communication channel to qemu through virtio that
can be used by test code to send commands. The only command currently
implemented is EXIT, which allows the test code to exit with a given
status code.

Signed-off-by: Andrew Jones <drjones@redhat.com>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
---
v4: couple tweaks (added spinlock), no functional changes
---
lib/virtio-testdev.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++
lib/virtio-testdev.h | 18 +++++++
2 files changed, 155 insertions(+)
create mode 100644 lib/virtio-testdev.c
create mode 100644 lib/virtio-testdev.h

diff --git a/lib/virtio-testdev.c b/lib/virtio-testdev.c
new file mode 100644
index 0000000000000..422e0bf4fc35a
--- /dev/null
+++ b/lib/virtio-testdev.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+#include "libcflat.h"
+#include "asm/spinlock.h"
+#include "virtio.h"
+
+#define TESTDEV_NAME "virtio-testdev"
+#define VIRTIO_ID_TESTDEV 0xffff
+#define TESTDEV_MAJOR_VER 1
+#define TESTDEV_MINOR_VER 1
+
+#define VIRTIO_CONFIG_SIZE 64
+
+enum {
+ VERSION = 1,
+ CLEAR,
+ EXIT,
+};
+
+#define TOKEN_OFFSET 0x0
+#define NARGS_OFFSET 0x4
+#define NRETS_OFFSET 0x8
+#define ARG_OFFSET(n) (0xc + (n) * 4)
+#define __RET_OFFSET(nargs, n) (ARG_OFFSET(nargs) + (n) * 4)
+
+static struct spinlock lock;
+static struct virtio_device *vdev;
+
+static u32 testdev_readl(unsigned offset)
+{
+ assert(offset <= (VIRTIO_CONFIG_SIZE - 4));
+ return virtio_config_readl(vdev, offset);
+}
+
+static void testdev_writel(unsigned offset, u32 val)
+{
+ assert(offset <= (VIRTIO_CONFIG_SIZE - 4));
+ virtio_config_writel(vdev, offset, val);
+}
+
+/*
+ * We have to write all args; nargs, nrets, ... first to avoid executing
+ * the token's operation until all args are in place. Then issue the op,
+ * and then read the return values. Reading the return values (or just
+ * sanity checking by reading token) will read a zero into qemu's copy
+ * of the token, which allows us to prepare additional ops without
+ * re-executing the last one.
+ */
+void virtio_testdev(u32 token, u32 nargs, u32 nrets, ...)
+{
+ va_list va;
+ unsigned off;
+ u32 n;
+
+ if (!vdev)
+ return;
+
+ spin_lock(&lock);
+
+ testdev_writel(NARGS_OFFSET, nargs);
+ testdev_writel(NRETS_OFFSET, nrets);
+
+ va_start(va, nrets);
+
+ off = ARG_OFFSET(0);
+ n = nargs;
+ while (n--) {
+ testdev_writel(off, va_arg(va, unsigned));
+ off += 4;
+ }
+
+ /* this runs the op, but then resets token to zero */
+ testdev_writel(TOKEN_OFFSET, token);
+ assert(testdev_readl(TOKEN_OFFSET) == 0);
+
+ off = __RET_OFFSET(nargs, 0);
+ n = nrets;
+ while (n--) {
+ u32 *r = va_arg(va, unsigned *);
+ *r = testdev_readl(off);
+ off += 4;
+ }
+
+ spin_unlock(&lock);
+ va_end(va);
+}
+
+void virtio_testdev_version(u32 *version)
+{
+ virtio_testdev(VERSION, 0, 1, version);
+}
+
+void virtio_testdev_clear(void)
+{
+ virtio_testdev(CLEAR, 0, 0);
+}
+
+void virtio_testdev_exit(int code)
+{
+ virtio_testdev(EXIT, 1, 0, code);
+}
+
+void virtio_testdev_init(void)
+{
+ u16 major, minor;
+ u32 version;
+
+ vdev = virtio_bind(VIRTIO_ID_TESTDEV);
+ if (vdev == NULL) {
+ printf("%s: can't find " TESTDEV_NAME ". "
+ "Is '-device " TESTDEV_NAME "' "
+ "on the qemu command line?\n", __func__);
+ abort();
+ }
+
+ virtio_testdev_version(&version);
+ major = version >> 16;
+ minor = version & 0xffff;
+
+ if (major != TESTDEV_MAJOR_VER || minor < TESTDEV_MINOR_VER) {
+ char *u = "qemu";
+ if (major > TESTDEV_MAJOR_VER)
+ u = "kvm-unit-tests";
+ printf("%s: incompatible version of " TESTDEV_NAME ": "
+ "major = %d, minor = %d. Update %s\n",
+ __func__, major, minor, u);
+ abort();
+ }
+
+ if (minor > TESTDEV_MINOR_VER)
+ printf("%s: " TESTDEV_NAME " has new features. "
+ "An update of kvm-unit-tests may be possible.\n",
+ __func__);
+}
diff --git a/lib/virtio-testdev.h b/lib/virtio-testdev.h
new file mode 100644
index 0000000000000..bed9eaf4aca4b
--- /dev/null
+++ b/lib/virtio-testdev.h
@@ -0,0 +1,18 @@
+#ifndef _VIRTIO_TESTDEV_H_
+#define _VIRTIO_TESTDEV_H_
+/*
+ * virtio-testdev is a driver for the virtio-testdev qemu device.
+ * The virtio-testdev device exposes a simple control interface to
+ * qemu for kvm-unit-tests through virtio.
+ *
+ * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+#include "libcflat.h"
+
+extern void virtio_testdev_init(void);
+extern void virtio_testdev_version(u32 *version);
+extern void virtio_testdev_clear(void);
+extern void virtio_testdev_exit(int code);
+#endif
--
1.9.3