This is a virtio version of hw/misc/debugexit and should evolve into a virtio version of pc-testdev. pc-testdev uses the PC's ISA bus, whereas this testdev can be plugged into a virtio-mmio transport, which is needed for kvm-unit-tests/arm. virtio-testdev uses the virtio device config space as a communication channel, and implements an RTAS-like protocol through it allowing guests to execute commands. Only three commands are currently implemented; 1) VERSION: for version compatibility checks 2) CLEAR: set all the config space back to zero 3) EXIT: exit() from qemu with a status code
Note, the protocol also requires all data passing through the config space to be in little-endian. See [1] for an example of a driver for this device. [1] https://github.com/rhdrjones/kvm-unit-tests/blob/ff8df5378ffccfbdf25fe79241837e61eb2258e1/lib/virtio-testdev.c Signed-off-by: Andrew Jones <drjo...@redhat.com> --- default-configs/arm-softmmu.mak | 2 + hw/virtio/Makefile.objs | 1 + hw/virtio/virtio-testdev.c | 117 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 hw/virtio/virtio-testdev.c diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index ac0815d66310f..56f8086e61974 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -80,3 +80,5 @@ CONFIG_VERSATILE_PCI=y CONFIG_VERSATILE_I2C=y CONFIG_SDHCI=y + +CONFIG_VIRTIO_TESTDEV=y diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs index 1ba53d9cc316c..b3d16d522f54b 100644 --- a/hw/virtio/Makefile.objs +++ b/hw/virtio/Makefile.objs @@ -3,6 +3,7 @@ common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o common-obj-y += virtio-bus.o common-obj-y += virtio-mmio.o common-obj-$(CONFIG_VIRTIO_BLK_DATA_PLANE) += dataplane/ +common-obj-$(CONFIG_VIRTIO_TESTDEV) += virtio-testdev.o obj-y += virtio.o virtio-balloon.o obj-$(CONFIG_LINUX) += vhost.o diff --git a/hw/virtio/virtio-testdev.c b/hw/virtio/virtio-testdev.c new file mode 100644 index 0000000000000..d6852d563702e --- /dev/null +++ b/hw/virtio/virtio-testdev.c @@ -0,0 +1,117 @@ +#include "hw/virtio/virtio-bus.h" + +#define VIRTIO_ID_TESTDEV 0xffff + +#define TYPE_VIRTIO_TESTDEV "virtio-testdev" +#define VIRTIO_TESTDEV(obj) \ + OBJECT_CHECK(VirtIOTestdev, (obj), TYPE_VIRTIO_TESTDEV) + +#define TESTDEV_MAJOR_VER 1 +#define TESTDEV_MINOR_VER 1 + +#define CONFIG_SIZE 0x100 + +enum { + VERSION = 1, + CLEAR, + EXIT, +}; + +enum { TOKEN, NARGS, NRETS, ARG1, ARG2, ARG3, ARG4, }; + +#define RET1(nargs) (ARG1 + (nargs) + 0) +#define RET2(nargs) (ARG1 + (nargs) + 1) +#define RET3(nargs) (ARG1 + (nargs) + 2) +#define RET4(nargs) (ARG1 + (nargs) + 3) + +#define calc_len(nargs, nrets) ((3 + (nargs) + (nrets)) * 4) + +typedef struct VirtIOTestdev { + VirtIODevice parent_obj; + uint8_t config[CONFIG_SIZE]; + size_t len; /* currently used bytes */ +} VirtIOTestdev; + +static void virtio_testdev_get_config(VirtIODevice *vdev, uint8_t *config_data) +{ + VirtIOTestdev *dev = VIRTIO_TESTDEV(vdev); + memcpy(config_data, &dev->config[0], dev->len); +} + +static void virtio_testdev_set_config(VirtIODevice *vdev, + const uint8_t *config_data) +{ + VirtIOTestdev *dev = VIRTIO_TESTDEV(vdev); + uint32_t *c32 = (uint32_t *)&dev->config[0]; + uint32_t token, nargs, nrets; + + memcpy(c32, config_data, 32); /* assume write is in first 32 bytes, + we can grab more later, if needed */ + token = le32_to_cpu(c32[TOKEN]); + nargs = le32_to_cpu(c32[NARGS]); + nrets = le32_to_cpu(c32[NRETS]); + + if (!token) { + return; + } + + switch (token) { + case VERSION: + c32[RET1(nargs)] = + cpu_to_le32((TESTDEV_MAJOR_VER << 16) | TESTDEV_MINOR_VER); + break; + case CLEAR: + memset(c32, 0, CONFIG_SIZE); + break; + case EXIT: + exit((le32_to_cpu(c32[ARG1]) << 1) | 1); + default: + break; + } + + c32[TOKEN] = 0; + dev->len = calc_len(nargs, nrets); +} + +static uint32_t virtio_testdev_get_features(VirtIODevice *vdev, uint32_t f) +{ + return f; +} + +static int virtio_testdev_init(VirtIODevice *vdev) +{ + virtio_init(vdev, "virtio-testdev", VIRTIO_ID_TESTDEV, CONFIG_SIZE); + return 0; +} + +static int virtio_testdev_exit(DeviceState *qdev) +{ + virtio_cleanup(VIRTIO_DEVICE(qdev)); + return 0; +} + +static void virtio_testdev_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + dc->exit = virtio_testdev_exit; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + vdc->init = virtio_testdev_init; + vdc->get_config = virtio_testdev_get_config; + vdc->set_config = virtio_testdev_set_config; + vdc->get_features = virtio_testdev_get_features; +} + +static const TypeInfo virtio_testdev_info = { + .name = TYPE_VIRTIO_TESTDEV, + .parent = TYPE_VIRTIO_DEVICE, + .instance_size = sizeof(VirtIOTestdev), + .class_init = virtio_testdev_class_init, +}; + +static void virtio_register_types(void) +{ + type_register_static(&virtio_testdev_info); +} + +type_init(virtio_register_types) -- 1.8.3.1