From: Yoni Bettan <ybettan@redhat.com>
To: Stefan Hajnoczi <stefanha@gmail.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>,
ailan@redhat.com, qemu-devel@nongnu.org, ehabkost@redhat.com
Subject: Re: [Qemu-devel] [PATCH V1] Introducing virtio-example.
Date: Sun, 16 Jun 2019 15:04:01 +0300 [thread overview]
Message-ID: <d314afe9-9cfd-cba5-6aaf-ff632efb6180@redhat.com> (raw)
In-Reply-To: <20190515094344.GA29507@stefanha-x1.localdomain>
Hi Stefan and thank you for your review.
I am sorry for my late response, I have updated the specification
according to your review (and Eduardo's review) and sent it to the
virtio-comment mailing list.
On 5/15/19 12:43 PM, Stefan Hajnoczi wrote:
> On Sun, Apr 28, 2019 at 04:26:31PM +0300, Yoni Bettan wrote:
>> The main goal is to create an example to be used as template or
>> guideline for contributors when they wish to create a new virtio
>> device and to document "the right way" to do so.
>>
>> It consists of several parts:
>>
>> 1. The device specification
>> * it can be found in the device header
>> * it will hopefully be added to the official virtio specification
>>
>> 2. The device implementation for Qemu-KVM hypervisor
>> * this patch content
>>
>> 3. The device driver for linux
>> * it will hopefully be added to linux
>> * for now it can be found at https://github.com/ybettan/\
>> QemuDeviceDrivers/blob/master/virtio/virtio_example_driver.c
>>
>> 4. A blog on virtio
>> * introducing the virtio concept
>> * gives some motivation for virtio-devices to be used
>> * bring extra documentation on "how to write":
>> - device specification
>> - device implementation
>> - device driver for linux
>> * it can be found at https://howtovms.wordpress.com
>>
>> Signed-off-by: Yoni Bettan <ybettan@redhat.com>
>> ---
> Existing VIRTIO devices provide plenty of examples of how to implement
> device emulation and guest drivers. This device is trivial and doesn't
> address the interesting decisions that device designers face. Its
> usefulness is limited. I don't think it should go into the spec, Linux,
> or QEMU.
>
> The following areas would be more helpful than an example device:
>
> * Expanding "Appendix B. Creating New Device Types" in the spec:
> https://docs.oasis-open.org/virtio/virtio/v1.1/cs01/virtio-v1.1-cs01.html#x1-444000B
>
> * A code commentary of an existing device like virtio-net or
> virtio-scsi since they are non-trivial. That would be a good fit for
> a series of blog posts.
>
> * Improving the doc comments in Linux and QEMU.
>
>> RFC -> V1:
>>
>> * Updated the commit message to be more informative about the full
>> working flow.
>>
>> * Added the device specification to the device header.
>>
>> * Removed the PCI-ID given for the device.
>> This was done by forcing the device to be in modern-only mode therefore
>> the PCI-ID is now generated automatically.
>>
>> * Made all requests consist of both input and output buffer instead
>> of separating them into 2 different requests.
>>
>> * Made the device IO deal with integers instead of strings.
>> The user have read/write access to the device using sysfs,
>> therefore the driver's input are strings, in the RFC version
>> those strings where passed directly to the device and the integer
>> conversion was done inside the device, now the driver is handling those
>> conversions and the device is unaware of them.
>>
>> * Added more documentation for the get_features() function.
>>
>> * Simplified the error propagation in virtio_example_pci_realize()
>> function.
>>
>> * Removed all code of previous previous patch from standard-headers.
>>
>>
>> hw/virtio/Makefile.objs | 1 +
>> hw/virtio/virtio-example.c | 110 +++++++++++++++++++++++++++++
>> hw/virtio/virtio-pci.c | 47 ++++++++++++
>> hw/virtio/virtio-pci.h | 14 ++++
>> include/hw/virtio/virtio-example.h | 92 ++++++++++++++++++++++++
>> 5 files changed, 264 insertions(+)
>> create mode 100644 hw/virtio/virtio-example.c
>> create mode 100644 include/hw/virtio/virtio-example.h
>>
>> diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs
>> index 1b2799cfd8..7a6fb2505c 100644
>> --- a/hw/virtio/Makefile.objs
>> +++ b/hw/virtio/Makefile.objs
>> @@ -7,6 +7,7 @@ common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
>> common-obj-$(CONFIG_VIRTIO_MMIO) += virtio-mmio.o
>> obj-$(CONFIG_VIRTIO_BALLOON) += virtio-balloon.o
>> obj-$(CONFIG_VIRTIO_CRYPTO) += virtio-crypto.o
>> +obj-$(CONFIG_VIRTIO_CRYPTO) += virtio-example.o
> CRYPTO? :)
>
>> obj-$(call land,$(CONFIG_VIRTIO_CRYPTO),$(CONFIG_VIRTIO_PCI)) += virtio-crypto-pci.o
>>
>> obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o
>> diff --git a/hw/virtio/virtio-example.c b/hw/virtio/virtio-example.c
>> new file mode 100644
>> index 0000000000..fd72f7c3a5
>> --- /dev/null
>> +++ b/hw/virtio/virtio-example.c
>> @@ -0,0 +1,110 @@
>> +/*
>> + * A virtio device example.
>> + *
>> + * Copyright 2019 Red Hat, Inc.
>> + * Copyright 2019 Yoni Bettan <ybettan@redhat.com>
>> + *
>> + * This work is licensed under the terms of the GNU GPL, version 2 or
>> + * (at your option) any later version. See the COPYING file in the
>> + * top-level directory.
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qemu/iov.h"
>> +#include "hw/virtio/virtio.h"
>> +#include "hw/virtio/virtio-example.h"
>> +
>> +
>> +/*
>> + * this function is called when the driver 'kick' the virtqueue.
>> + * since we can have more than 1 virtqueue we need the vq argument in order to
>> + * know which one was kicked by the driver.
>> + */
>> +static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
>> +{
>> + VirtQueueElement *elem;
>> + int data;
>> +
>> + /*
>> + * get the virtqueue element sent from the driver.
>> + * in_sg are the driver inputs (device outputs)
>> + * out_sg are the driver output (device input)
>> + */
>> + elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
> virtqueue_pop() can fail. The NULL return value must be handled.
>
>> +
>> + /* read the driver output sg (device input sg) into a buffer */
>> + iov_to_buf(elem->out_sg, elem->out_num, 0, &data, sizeof(int));
>> +
>> + /* process the data */
>> + data++;
>> +
>> + /* write the result to the driver input sg (device output sg) */
>> + iov_from_buf(elem->in_sg, elem->in_num, 0, &data, sizeof(int));
> This exposes uninitialized stack memory from QEMU to the guest when
> elem->out_num = 0 or the buffer is smaller than sizeof(int). This is a
> security bug.
>
> To avoid it, handle iov_to_buf() failure with virtio_error() and return
> early. The device will be left in the "broken" state and further
> operation will fail (e.g. virtqueue_pop() will fail until the next
> device reset).
>
>> +
>> + /* push back the result into the virtqueue */
>> + virtqueue_push(vq, elem, 1);
> Why 1? Normally this is the number of bytes transferred so it should be
> sizeof(int).
>
>> +
>> + /* interrupt the driver */
>> + virtio_notify(vdev, vq);
>> +
>> + return;
>> +}
>> +
>> +/*
>> + * This function gets the host features as a parameter and add to it all the
>> + * features supported by the device.
>> + * This example-device has no currently defined feature bits but we still need
>> + * this function because when a device is plugged this function is called to
>> + * check the features offer by the device so it must exist and return the
>> + * host features without any change.
>> + */
>> +static uint64_t
>> +get_features(VirtIODevice *vdev, uint64_t features, Error **errp)
>> +{
>> + return features;
>> +}
>> +
>> +static void virtio_example_device_realize(DeviceState *dev, Error **errp)
>> +{
>> + VirtIODevice *vdev = VIRTIO_DEVICE(dev);
>> + VirtIOEXAMPLE *vexmp = VIRTIO_EXAMPLE(dev);
>> +
>> + /* common virtio device initialization */
>> + virtio_init(vdev, "virtio-example", VIRTIO_ID_EXAMPLE, 0);
>> +
>> + /* this device suppot 1 virtqueue */
>> + vexmp->vq = virtio_add_queue(vdev, 1, handle_input);
>> +}
>> +
>> +static void virtio_example_device_unrealize(DeviceState *dev, Error **errp)
>> +{
>> + VirtIODevice *vdev = VIRTIO_DEVICE(dev);
>> +
>> + /* common virtio device cleanup */
>> + virtio_cleanup(vdev);
>> +}
>> +
>> +static void virtio_example_class_init(ObjectClass *klass, void *data)
>> +{
>> + DeviceClass *dc = DEVICE_CLASS(klass);
>> + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
>> +
>> + set_bit(DEVICE_CATEGORY_MISC, dc->categories);
>> + vdc->realize = virtio_example_device_realize;
>> + vdc->unrealize = virtio_example_device_unrealize;
>> + vdc->get_features = get_features;
>> +}
>> +
>> +static const TypeInfo virtio_example_info = {
>> + .name = TYPE_VIRTIO_EXAMPLE,
>> + .parent = TYPE_VIRTIO_DEVICE,
>> + .instance_size = sizeof(VirtIOEXAMPLE),
>> + .class_init = virtio_example_class_init,
>> +};
>> +
>> +static void virtio_register_types(void)
>> +{
>> + type_register_static(&virtio_example_info);
>> +}
>> +
>> +type_init(virtio_register_types)
>> diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
>> index 3a01fe90f0..36333168ad 100644
>> --- a/hw/virtio/virtio-pci.c
>> +++ b/hw/virtio/virtio-pci.c
>> @@ -2521,6 +2521,52 @@ static const TypeInfo virtio_rng_pci_info = {
>> .class_init = virtio_rng_pci_class_init,
>> };
>>
>> +/* virtio-example-pci */
>> +
>> +static void virtio_example_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
>> +{
>> + VirtIOExamplePCI *vexmp = VIRTIO_EXAMPLE_PCI(vpci_dev);
>> + DeviceState *vdev = DEVICE(&vexmp->vdev);
>> +
>> + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
>> + /*
>> + * force modern-only mode on the device, now a PCI-ID will be generated
>> + * automatically according to the VIRTIO-ID.
>> + */
>> + virtio_pci_force_virtio_1(vpci_dev);
>> + object_property_set_bool(OBJECT(vdev), true, "realized", errp);
>> +}
>> +
>> +static void virtio_example_pci_class_init(ObjectClass *klass, void *data)
>> +{
>> + DeviceClass *dc = DEVICE_CLASS(klass);
>> + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
>> + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
>> +
>> + k->realize = virtio_example_pci_realize;
>> + set_bit(DEVICE_CATEGORY_MISC, dc->categories);
>> +
>> + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
>> + pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
>> + pcidev_k->class_id = PCI_CLASS_OTHERS;
>> +}
>> +
>> +static void virtio_example_initfn(Object *obj)
>> +{
>> + VirtIOExamplePCI *dev = VIRTIO_EXAMPLE_PCI(obj);
>> +
>> + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
>> + TYPE_VIRTIO_EXAMPLE);
>> +}
>> +
>> +static const TypeInfo virtio_example_pci_info = {
>> + .name = TYPE_VIRTIO_EXAMPLE_PCI,
>> + .parent = TYPE_VIRTIO_PCI,
>> + .instance_size = sizeof(VirtIOExamplePCI),
>> + .instance_init = virtio_example_initfn,
>> + .class_init = virtio_example_pci_class_init,
>> +};
>> +
>> /* virtio-input-pci */
>>
>> static Property virtio_input_pci_properties[] = {
>> @@ -2693,6 +2739,7 @@ static const TypeInfo virtio_pci_bus_info = {
>> static void virtio_pci_register_types(void)
>> {
>> type_register_static(&virtio_rng_pci_info);
>> + type_register_static(&virtio_example_pci_info);
>> type_register_static(&virtio_input_pci_info);
>> type_register_static(&virtio_input_hid_pci_info);
>> type_register_static(&virtio_keyboard_pci_info);
>> diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
>> index 813082b0d7..db3f5ec17d 100644
>> --- a/hw/virtio/virtio-pci.h
>> +++ b/hw/virtio/virtio-pci.h
>> @@ -19,6 +19,7 @@
>> #include "hw/virtio/virtio-blk.h"
>> #include "hw/virtio/virtio-net.h"
>> #include "hw/virtio/virtio-rng.h"
>> +#include "hw/virtio/virtio-example.h"
>> #include "hw/virtio/virtio-serial.h"
>> #include "hw/virtio/virtio-scsi.h"
>> #include "hw/virtio/virtio-balloon.h"
>> @@ -51,6 +52,7 @@ typedef struct VHostSCSIPCI VHostSCSIPCI;
>> typedef struct VHostUserSCSIPCI VHostUserSCSIPCI;
>> typedef struct VHostUserBlkPCI VHostUserBlkPCI;
>> typedef struct VirtIORngPCI VirtIORngPCI;
>> +typedef struct VirtIOExamplePCI VirtIOExamplePCI;
>> typedef struct VirtIOInputPCI VirtIOInputPCI;
>> typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI;
>> typedef struct VirtIOInputHostPCI VirtIOInputHostPCI;
>> @@ -339,6 +341,18 @@ struct VirtIORngPCI {
>> VirtIORNG vdev;
>> };
>>
>> +/*
>> + * virtio-example-pci: This extends VirtioPCIProxy.
>> + */
>> +#define TYPE_VIRTIO_EXAMPLE_PCI "virtio-example-pci"
>> +#define VIRTIO_EXAMPLE_PCI(obj) \
>> + OBJECT_CHECK(VirtIOExamplePCI, (obj), TYPE_VIRTIO_EXAMPLE_PCI)
>> +
>> +struct VirtIOExamplePCI {
>> + VirtIOPCIProxy parent_obj;
>> + VirtIOEXAMPLE vdev;
>> +};
>> +
>> /*
>> * virtio-input-pci: This extends VirtioPCIProxy.
>> */
>> diff --git a/include/hw/virtio/virtio-example.h b/include/hw/virtio/virtio-example.h
>> new file mode 100644
>> index 0000000000..48397af4e1
>> --- /dev/null
>> +++ b/include/hw/virtio/virtio-example.h
>> @@ -0,0 +1,92 @@
>> +/*
>> + * Virtio EXAMPLE Support
>> + *
>> + * Copyright Red Hat, Inc. 2019
>> + * Copyright Yoni Bettan <ybettan@redhat.com>
>> + *
>> + * This work is licensed under the terms of the GNU GPL, version 2 or
>> + * (at your option) any later version. See the COPYING file in the
>> + * top-level directory.
>> + */
>> +
>> +
>> +/*
>> + * ============================================================================
>> + * Device specification
>> + * ============================================================================
>> + *
>> + * 5.2 Example Device
>> + *
>> + * The virtio example device is a simple virtual block device. Read and write
>> + * requests are placed in the queue, and serviced (probably out of order) by
>> + * the device except where noted.
> This was copy-pasted from virtio-blk and does not accurately describe
> this example device.
>
>> + *
>> + * 5.2.1 Device ID
>> + *
>> + * 21
> A new ID is probably required. See the latest ID requests on the virtio
> mailing list.
>
>> + *
>> + * 5.2.2 Virtqueues
>> + *
>> + * 0 requestq
>> + *
>> + * 5.2.3 Feature bits
>> + *
>> + * None currently defined.
>> + *
>> + * 5.2.4 Device configuration layout
>> + *
>> + * None currently defined.
>> + *
>> + * 5.2.5 Device Initialization
>> + *
>> + * 1. The virtqueue is initialized.
>> + *
>> + * 5.2.6 Device Operation
>> + *
>> + * When the driver need computation, it places a request that consist of both
>> + * input and output buffer into the queue.
>> + * The first buffer is used as an input for the device and contain a single
>> + * integer, represented by 4 or 8 bytes (depends on the architecture), and the
>> + * second is given for the device to fill the result in it.
> Modern VIRTIO devices have a well-defined ABI and do not contain
> architecture-dependent data layout (with few exceptions). An example
> device should demonstrate this by using a little-endian 4-byte integer.
>
>> + *
>> + * The device is increasing this integer by 1, meaning the LSB will be
>> + * increased by 1 and if needed the carry will be carried to the next byte.
>> + *
>> + * If the device get a number that is out of the range of an integer only the
>> + * lower bytes that fit the size of an integer will represent the input and the
>> + * upper bytes will be ignored.
>> + * If the result is out of range then only the lower bytes will be written as
>> + * result as well.
>> + *
>> + * 5.2.6.1 Driver Requirements: Device Operation
>> + *
>> + * The driver MUST place the 2 buffers in the same request in order to make a
>> + * request atomically handled by the device, the first buffer contains the
>> + * input and should be read-only and the second should be empty and write-only.
> Please use the terminology from the specification:
> s/buffer/element/
>
> Here is the definition from 3.2.1.1 Placing Buffers Into The Descriptor
> Table:
> "A buffer consists of zero or more device-readable physically-contiguous
> elements followed by zero or more physically-contiguous device-writable
> elements (each has at least one element)."
>
> In other words, a "buffer" is an entire request-response and consists of
> a scatter-gather list of driver->device and device->driver "elements".
>
>> + *
>> + * 5.2.6.2 Device Requirements: Device Operation
>> + *
>> + * The device MUST place the result inside the output buffer allocated by the
>> + * driver.
>> + */
>> +
>> +#ifndef QEMU_VIRTIO_EXAMPLE_H
>> +#define QEMU_VIRTIO_EXAMPLE_H
>> +
>> +#define VIRTIO_ID_EXAMPLE 21
>> +
>> +#define TYPE_VIRTIO_EXAMPLE "virtio-example-device"
>> +#define VIRTIO_EXAMPLE(obj) \
>> + OBJECT_CHECK(VirtIOEXAMPLE, (obj), TYPE_VIRTIO_EXAMPLE)
>> +#define VIRTIO_EXAMPLE_GET_PARENT_CLASS(obj) \
>> + OBJECT_GET_PARENT_CLASS(obj, TYPE_VIRTIO_EXAMPLE)
>> +
>> +typedef struct VirtIOEXAMPLE {
>> + VirtIODevice parent_obj;
>> +
>> + /* Only one vq - guest puts buffer(s) on it when it needs computation */
>> + VirtQueue *vq;
>> +
>> +} VirtIOEXAMPLE;
>> +
>> +#endif
>> --
>> 2.17.2
>>
prev parent reply other threads:[~2019-06-16 12:05 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-04-28 13:26 [Qemu-devel] [PATCH V1] Introducing virtio-example Yoni Bettan
2019-04-28 13:26 ` Yoni Bettan
2019-05-15 9:43 ` Stefan Hajnoczi
2019-06-16 12:04 ` Yoni Bettan [this message]
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=d314afe9-9cfd-cba5-6aaf-ff632efb6180@redhat.com \
--to=ybettan@redhat.com \
--cc=ailan@redhat.com \
--cc=ehabkost@redhat.com \
--cc=mst@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=stefanha@gmail.com \
/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;
as well as URLs for NNTP newsgroup(s).