From: Andrew Jones <drjones@redhat.com>
To: Christoffer Dall <christoffer.dall@linaro.org>
Cc: kvmarm@lists.cs.columbia.edu, kvm@vger.kernel.org
Subject: Re: [PATCH 12/17] Introduce virtio-testdev
Date: Mon, 3 Feb 2014 16:44:31 +0100 [thread overview]
Message-ID: <20140203154430.GF6832@hawk.usersys.redhat.com> (raw)
In-Reply-To: <20140202022737.GV3570@cbox>
On Sat, Feb 01, 2014 at 06:27:37PM -0800, Christoffer Dall wrote:
> On Tue, Jan 21, 2014 at 05:21:58PM +0100, Andrew Jones wrote:
> > 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>
> >
> > ---
> > v3:
> > - switched from iomaps to devicetree
> > - fixed Christoffer's nits
> > - don't call halt() explicitly, let exit() fall back to it
> > - vm_{get,set} now {read,write} byte by byte like the kernel
> > v2:
> > - use readl/writel framework (libio) [Christoffer Dall]
> > - keep the virtio abstraction in virtio-testdev [Alexander Graf]
> > ---
> > lib/libcflat.h | 3 ++
> > lib/virtio-testdev.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++
> > lib/virtio-testdev.h | 9 ++++
> > lib/virtio.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++
> > lib/virtio.h | 74 +++++++++++++++++++++++++++
> > 5 files changed, 351 insertions(+)
> > create mode 100644 lib/virtio-testdev.c
> > create mode 100644 lib/virtio-testdev.h
> > create mode 100644 lib/virtio.c
> > create mode 100644 lib/virtio.h
> >
> > diff --git a/lib/libcflat.h b/lib/libcflat.h
> > index fdaaf2a8ab31d..84b8783bacfdc 100644
> > --- a/lib/libcflat.h
> > +++ b/lib/libcflat.h
> > @@ -60,6 +60,9 @@ extern long atol(const char *ptr);
> > #define ARRAY_SIZE(_a) (sizeof(_a)/sizeof((_a)[0]))
> >
> > #define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
> > +#define container_of(ptr, type, member) ({ \
> > + const typeof( ((type *)0)->member ) *__mptr = (ptr); \
> > + (type *)( (char *)__mptr - offsetof(type,member) );})
> >
> > #define __unused __attribute__((__unused__))
> > #define NULL ((void *)0UL)
> > diff --git a/lib/virtio-testdev.c b/lib/virtio-testdev.c
> > new file mode 100644
> > index 0000000000000..d4a69ede12a7a
> > --- /dev/null
> > +++ b/lib/virtio-testdev.c
> > @@ -0,0 +1,139 @@
> > +#include "libcflat.h"
> > +#include "virtio.h"
> > +
> > +#define TESTDEV_MAJOR_VER 1
> > +#define TESTDEV_MINOR_VER 1
> > +
> > +#define VIRTIO_ID_TESTDEV 0xffff
> > +
> > +#define 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 virtio_dev *testdev;
> > +
> > +static u32 testdev_readl(unsigned offset)
> > +{
> > + if (offset > (CONFIG_SIZE - 4)) {
> > + printf("%s: offset 0x%x to big!\n", __func__, offset);
> > + exit(EINVAL);
> > + }
> > +
> > + return virtio_config_readl(testdev, offset);
> > +}
> > +
> > +static void testdev_writel(unsigned offset, u32 val)
> > +{
> > + if (offset > (CONFIG_SIZE - 4)) {
> > + printf("%s: offset 0x%x to big!\n", __func__, offset);
> > + exit(EINVAL);
> > + }
> > +
> > + virtio_config_writel(testdev, 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 (!testdev)
> > + return;
> > +
> > + 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);
> > +
> > + /* sanity check */
> > + if (testdev_readl(TOKEN_OFFSET) != 0) {
> > + printf("virtio-testdev token should always read as zero!\n");
> > + exit(EIO);
> > + }
> > +
> > + off = __RET_OFFSET(nargs, 0);
> > + n = nrets;
> > + while (n--) {
> > + u32 *r = va_arg(va, unsigned *);
> > + *r = testdev_readl(off);
> > + off += 4;
> > + }
> > +
> > + 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;
> > +
> > + testdev = virtio_bind(VIRTIO_ID_TESTDEV);
> > + if (testdev == NULL) {
> > + printf("Can't find virtio-testdev. "
> > + "Is '-device virtio-testdev' "
> > + "on the qemu command line?\n");
> > + exit(ENODEV);
> > + }
> > +
> > + 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("Incompatible version of virtio-testdev: major = %d, "
> > + "minor = %d\n", major, minor);
> > + printf("Update %s.\n", u);
> > + exit(ENODEV);
> > + }
> > +
> > + if (minor > TESTDEV_MINOR_VER)
> > + printf("testdev has new features. "
> > + "An update of kvm-unit-tests may be possible.\n");
> > +}
> > diff --git a/lib/virtio-testdev.h b/lib/virtio-testdev.h
> > new file mode 100644
> > index 0000000000000..c10bbfb591d0b
> > --- /dev/null
> > +++ b/lib/virtio-testdev.h
> > @@ -0,0 +1,9 @@
> > +#ifndef _VIRTIO_TESTDEV_H_
> > +#define _VIRTIO_TESTDEV_H_
> > +#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
> > diff --git a/lib/virtio.c b/lib/virtio.c
> > new file mode 100644
> > index 0000000000000..8abc8b2399a08
> > --- /dev/null
> > +++ b/lib/virtio.c
> > @@ -0,0 +1,126 @@
> > +#include "libcflat.h"
> > +#include "devicetree.h"
> > +#include "libio.h"
> > +#include "heap.h"
> > +#include "virtio.h"
> > +
> > +struct virtio_dt_device_data {
> > + u32 device;
> > + struct dt_bus *bus;
> > + int node;
> > + /* cached variables */
> > + u32 address_cells, size_cells;
> > +};
> > +
> > +static void virtio_dt_device_data_init(struct virtio_dt_device_data *data,
> > + u32 device, struct dt_bus *bus)
> > +{
> > + memset(data, 0, sizeof(struct virtio_dt_device_data));
> > + data->device = device;
> > + data->bus = bus;
> > +}
> > +
> > +static int vm_dt_bus_match(const struct dt_bus *bus, int nodeoffset);
> > +static struct virtio_dev *vm_bind(struct virtio_dt_device_data *data);
> > +
> > +struct virtio_dev *virtio_bind(u32 device)
> > +{
> > + struct virtio_dt_device_data data;
> > + struct dt_bus bus;
> > + const char *compatible;
> > +
> > + /*
> > + * currently we only support virtio-mmio
> > + */
> > + compatible = "virtio,mmio";
> > + dt_bus_init_defaults(&bus, compatible);
> > + bus.match = vm_dt_bus_match;
> > + bus.private = (void *)&data;
> > + virtio_dt_device_data_init(&data, device, &bus);
> > +
> > + data.node = dt_bus_find_device_compatible(&bus, compatible);
> > + if (data.node < 0) {
> > + printf("virtio bind for device id 0x%x failed, "
> > + "fdt_error: %s\n", device, dt_strerror(data.node));
> > + return NULL;
> > + }
> > +
> > + return vm_bind(&data);
> > +}
> > +
> > +static int virtio_dt_bus_translate_reg(int nodeoffset,
> > + const struct dt_bus *bus,
> > + int regidx, void **addr,
> > + size_t *size)
>
> so this is really virtio_dt_bus_get_addr right?
right. Actually, we could probably always assume regidx will be zero,
remove that param, and then rename this.
>
> > +{
> > + struct virtio_dt_device_data *data =
> > + (struct virtio_dt_device_data *)bus->private;
> > +
> > + return __dt_bus_translate_reg(nodeoffset, bus, regidx,
> > + &data->address_cells, &data->size_cells, addr, size);
> > +}
> > +
> > +/******************************************************
> > + * virtio_mmio
> > + ******************************************************/
> > +
> > +static int vm_dt_bus_match(const struct dt_bus *bus, int nodeoffset)
> > +{
> > + struct virtio_dt_device_data *data =
> > + (struct virtio_dt_device_data *)bus->private;
> > + void *addr;
> > + int ret;
> > +
> > + ret = virtio_dt_bus_translate_reg(nodeoffset, bus, 0, &addr, NULL);
> > + if (ret < 0) {
> > + printf("can't get reg! fdt_error: %s\n", dt_strerror(ret));
> > + return ret;
> > + }
> > +
> > + return readl(addr + VIRTIO_MMIO_DEVICE_ID) == data->device;
> > +}
> > +
> > +#define to_virtio_mmio_dev(vdev_ptr) \
> > + container_of(vdev_ptr, struct virtio_mmio_dev, vdev)
> > +
> > +static void vm_get(struct virtio_dev *vdev, unsigned offset,
> > + void *buf, unsigned len)
> > +{
> > + struct virtio_mmio_dev *vmdev = to_virtio_mmio_dev(vdev);
> > + u8 *p = buf;
> > + unsigned i;
> > +
> > + for (i = 0; i < len; ++i)
> > + p[i] = readb(vmdev->base + VIRTIO_MMIO_CONFIG + offset + i);
> > +}
> > +
> > +static void vm_set(struct virtio_dev *vdev, unsigned offset,
> > + const void *buf, unsigned len)
> > +{
> > + struct virtio_mmio_dev *vmdev = to_virtio_mmio_dev(vdev);
> > + const u8 *p = buf;
> > + unsigned i;
> > +
> > + for (i = 0; i < len; ++i)
> > + writeb(p[i], vmdev->base + VIRTIO_MMIO_CONFIG + offset + i);
> > +}
> > +
> > +static struct virtio_dev *vm_bind(struct virtio_dt_device_data *data)
> > +{
> > + struct virtio_mmio_dev *vmdev;
> > + void *page;
> > +
> > + page = alloc_page();
> > + vmdev = page;
> > + vmdev->vdev.config = page + sizeof(struct virtio_mmio_dev);
> > +
> > + vmdev->vdev.id.device = data->device;
> > + vmdev->vdev.id.vendor = -1;
> > + vmdev->vdev.config->get = vm_get;
> > + vmdev->vdev.config->set = vm_set;
> > +
> > + (void)virtio_dt_bus_translate_reg(data->node, data->bus, 0,
> > + &vmdev->base, NULL);
>
> didn't you already do this in vm_dt_bus_mach() ?
yes, but we didn't save the result, so we need to go back to the
node's reg property again and get it. At least we know which node
to go to and {address,size}_cells are cached. Hmm, we might as
well add an addr field to virtio_dt_device_data, allowing match
to save it there. I'll do that.
>
> > +
> > + return &vmdev->vdev;
> > +}
> > diff --git a/lib/virtio.h b/lib/virtio.h
> > new file mode 100644
> > index 0000000000000..c30a8dcd105cd
> > --- /dev/null
> > +++ b/lib/virtio.h
> > @@ -0,0 +1,74 @@
> > +#ifndef _VIRTIO_H_
> > +#define _VIRTIO_H_
> > +#include "libcflat.h"
> > +
> > +#define VIRTIO_MMIO_DEVICE_ID 0x008
> > +#define VIRTIO_MMIO_CONFIG 0x100
> > +
> > +struct virtio_devid {
> > + u32 device;
> > + u32 vendor;
> > +};
> > +
> > +struct virtio_dev {
> > + struct virtio_devid id;
> > + struct virtio_conf_ops *config;
> > +};
> > +
> > +struct virtio_conf_ops {
> > + void (*get)(struct virtio_dev *vdev, unsigned offset,
> > + void *buf, unsigned len);
> > + void (*set)(struct virtio_dev *vdev, unsigned offset,
> > + const void *buf, unsigned len);
> > +};
> > +
> > +struct virtio_mmio_dev {
> > + struct virtio_dev vdev;
> > + void *base;
> > +};
> > +
> > +static inline u8
> > +virtio_config_readb(struct virtio_dev *vdev, unsigned offset)
> > +{
> > + u8 val;
> > + vdev->config->get(vdev, offset, &val, 1);
> > + return val;
> > +}
> > +
> > +static inline u16
> > +virtio_config_readw(struct virtio_dev *vdev, unsigned offset)
> > +{
> > + u16 val;
> > + vdev->config->get(vdev, offset, &val, 2);
> > + return val;
> > +}
> > +
> > +static inline u32
> > +virtio_config_readl(struct virtio_dev *vdev, unsigned offset)
> > +{
> > + u32 val;
> > + vdev->config->get(vdev, offset, &val, 4);
> > + return val;
> > +}
> > +
> > +static inline void
> > +virtio_config_writeb(struct virtio_dev *vdev, unsigned offset, u8 val)
> > +{
> > + vdev->config->set(vdev, offset, &val, 1);
> > +}
> > +
> > +static inline void
> > +virtio_config_writew(struct virtio_dev *vdev, unsigned offset, u16 val)
> > +{
> > + vdev->config->set(vdev, offset, &val, 2);
> > +}
> > +
> > +static inline void
> > +virtio_config_writel(struct virtio_dev *vdev, unsigned offset, u32 val)
> > +{
> > + vdev->config->set(vdev, offset, &val, 4);
> > +}
> > +
> > +extern struct virtio_dev *virtio_bind(u32 device);
> > +
> > +#endif
> > --
> > 1.8.1.4
> >
>
> otherwise:
>
> Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
next prev parent reply other threads:[~2014-02-03 15:44 UTC|newest]
Thread overview: 47+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-01-21 16:21 [PATCH 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
2014-01-21 16:21 ` [PATCH 01/17] remove unused files Andrew Jones
2014-01-21 16:21 ` [PATCH 02/17] makefile and run_tests tweaks Andrew Jones
2014-01-21 16:21 ` [PATCH 03/17] clean root dir of all x86-ness Andrew Jones
2014-01-21 16:21 ` [PATCH 04/17] gitignore: Ignore more Andrew Jones
2014-01-21 16:21 ` [PATCH 05/17] add 'make cscope' support Andrew Jones
2014-02-02 2:22 ` Christoffer Dall
2014-02-03 13:25 ` Andrew Jones
2014-01-21 16:21 ` [PATCH 06/17] Add halt() and some error codes Andrew Jones
2014-02-02 2:23 ` Christoffer Dall
2014-01-21 16:21 ` [PATCH 07/17] move x86's simple heap management to common code Andrew Jones
2014-02-02 2:23 ` Christoffer Dall
2014-01-21 16:21 ` [PATCH 08/17] Introduce libio to common code for io read/write Andrew Jones
2014-02-02 2:24 ` Christoffer Dall
2014-02-03 13:51 ` Andrew Jones
2014-02-03 16:30 ` Christoffer Dall
2014-01-21 16:21 ` [PATCH 10/17] libfdt: get libfdt to build Andrew Jones
2014-02-02 2:25 ` Christoffer Dall
2014-02-03 13:57 ` Andrew Jones
2014-01-21 16:21 ` [PATCH 11/17] add support for device trees Andrew Jones
2014-02-02 2:27 ` Christoffer Dall
2014-02-03 15:31 ` Andrew Jones
2014-02-03 16:36 ` Christoffer Dall
2014-01-21 16:21 ` [PATCH 12/17] Introduce virtio-testdev Andrew Jones
2014-02-02 2:27 ` Christoffer Dall
2014-02-03 15:44 ` Andrew Jones [this message]
2014-02-03 16:41 ` Christoffer Dall
2014-01-21 16:21 ` [PATCH 13/17] arm: initial drop Andrew Jones
2014-02-02 2:28 ` Christoffer Dall
2014-02-03 15:55 ` Andrew Jones
2014-01-21 16:22 ` [PATCH 14/17] arm: Add IO accessors to avoid register-writeback Andrew Jones
2014-01-21 16:22 ` [PATCH 15/17] printf: support field padding Andrew Jones
2014-02-02 2:28 ` Christoffer Dall
2014-01-21 16:22 ` [PATCH 16/17] arm: add useful headers from the linux kernel Andrew Jones
2014-02-02 2:29 ` Christoffer Dall
2014-02-03 16:46 ` Andrew Jones
2014-02-03 17:38 ` Christoffer Dall
2014-01-21 16:22 ` [PATCH 17/17] arm: vectors support Andrew Jones
2014-02-02 2:29 ` Christoffer Dall
2014-02-03 16:50 ` Andrew Jones
2014-02-03 21:19 ` Christoffer Dall
2014-02-04 7:12 ` Andrew Jones
[not found] ` <CABWnSnPMc_CrH8N28TScBVvQmCk+XD-bVWvdmJAxxVczHsVx_g@mail.gmail.com>
2014-01-29 15:35 ` [PATCH 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
2014-02-02 2:22 ` Christoffer Dall
2014-02-03 13:24 ` Andrew Jones
[not found] ` <CALxX4v-h+gOCZDukCnGK_GUQepu07KYw4BGjzjGNgA0SdDcLNw@mail.gmail.com>
2014-02-04 8:33 ` Andrew Jones
[not found] ` <1390321323-1855-10-git-send-email-drjones@redhat.com>
2014-02-02 2:25 ` [PATCH 09/17] libfdt: Import libfdt source Christoffer Dall
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20140203154430.GF6832@hawk.usersys.redhat.com \
--to=drjones@redhat.com \
--cc=christoffer.dall@linaro.org \
--cc=kvm@vger.kernel.org \
--cc=kvmarm@lists.cs.columbia.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox