lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-Id: <1314966257-7503-2-git-send-email-pawel.moll@arm.com>
Date:	Fri,  2 Sep 2011 13:24:16 +0100
From:	Pawel Moll <pawel.moll@....com>
To:	linux-kernel@...r.kernel.org, linux-arm-kernel@...ts.infradead.org
Cc:	Rusty Russell <rusty@...tcorp.com.au>,
	Magnus Damm <magnus.damm@...il.com>
Subject: [RFC 1/2] virtio: Add AMBA bus driver for virtio device

This patch, based on virtio PCI driver, adds support for virtio
AMBA device. This should allow environments like qemu to use
virtio-based block & network devices.

For example, one can define and register an AMBA device like
this (emulation environment must of course provide a correctly
mapped "Virtio Block Device Prime Cell"):

struct amba_device virtio_block = {
	.dev.init_name	= "virtio-block",
	.res		= {
		.start	= 0x1c0d0000,
		.end	= 0x1c0d0fff,
		.flags	= IORESOURCE_MEM,
	},
	.irq		= { 73, },
};

This should be soon replaced with Device Tree entry
supplied by the simulation environment.

Signed-off-by: Pawel Moll <pawel.moll@....com>
---
 drivers/virtio/Kconfig       |   11 +
 drivers/virtio/Makefile      |    1 +
 drivers/virtio/virtio_amba.c |  443 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/virtio_amba.h  |   62 ++++++
 4 files changed, 517 insertions(+), 0 deletions(-)
 create mode 100644 drivers/virtio/virtio_amba.c
 create mode 100644 include/linux/virtio_amba.h

diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index 57e493b..a8530ab 100644
--- a/drivers/virtio/Kconfig
+++ b/drivers/virtio/Kconfig
@@ -35,4 +35,15 @@ config VIRTIO_BALLOON
 
 	 If unsure, say M.
 
+ config VIRTIO_AMBA
+ 	tristate "AMBA bus driver for virtio devices (EXPERIMENTAL)"
+ 	depends on ARM_AMBA && EXPERIMENTAL
+ 	select VIRTIO
+ 	select VIRTIO_RING
+ 	---help---
+ 	 This drivers provides support for virtio based paravirtual device
+ 	 drivers over an AMBA bus.
+
+ 	 If unsure, say N.
+
 endmenu
diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
index 6738c44..49147cb 100644
--- a/drivers/virtio/Makefile
+++ b/drivers/virtio/Makefile
@@ -1,4 +1,5 @@
 obj-$(CONFIG_VIRTIO) += virtio.o
 obj-$(CONFIG_VIRTIO_RING) += virtio_ring.o
+obj-$(CONFIG_VIRTIO_AMBA) += virtio_amba.o
 obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
 obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o
diff --git a/drivers/virtio/virtio_amba.c b/drivers/virtio/virtio_amba.c
new file mode 100644
index 0000000..0cfbaec
--- /dev/null
+++ b/drivers/virtio/virtio_amba.c
@@ -0,0 +1,443 @@
+/*
+ * Virtio AMBA driver
+ *
+ * Copyright 2011, ARM Ltd.
+ *
+ * This module allows virtio devices to be used over a virtual AMBA device.
+ *
+ * Registers layout:
+ *
+ * offset width name          description
+ * ------ ----- ------------- -----------------
+ *
+ *  0x000   32  HostFeatures  Features supported by the host
+ *  0x004   32  GuestFeatures Features activated by the guest
+ *  0x008   32  QueuePFN      PFN for the currently selected queue
+ *  0x00c   32  QueueNum      Queue size for the currently selected queue
+ *  0x010   32  QueueSel      Queue selector
+ *  0x014   32  QueueNotify   Queue notifier
+ *  0x018   8   InterruptACK  Interrupt acknowledge register
+ *  0x01c   8   Status        Device status register
+ *
+ *  0x020
+ *   ...                      Device-specific configuration space
+ *  0xfdf
+ *
+ *  0xfe0   8   PeriphID0     0x30
+ *  0xfe4   8   PeriphID1     0x17
+ *  0xfe8   8   PeriphID2     0x04
+ *  0xfec   8   PeriphID3     VIRTIO_ID_* (see <linux/virtio_ids.h>)
+ *  0xff0   8   PCellID0      0x0d
+ *  0xff4   8   PCellID1      0xf0
+ *  0xff8   8   PCellID2      0x05
+ *  0xffc   8   PCellID3      0xb1
+ *
+ * Based on Virtio PCI driver by Anthony Liguori, copyright IBM Corp. 2007
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/amba/bus.h>
+#include <linux/interrupt.h>
+#include <linux/virtio.h>
+#include <linux/virtio_amba.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ring.h>
+#include <linux/highmem.h>
+#include <linux/spinlock.h>
+
+
+
+#define to_virtio_amba_device(_amba_dev) \
+	container_of(_amba_dev, struct virtio_amba_device, vdev)
+
+struct virtio_amba_device {
+	struct virtio_device vdev;
+	struct amba_device *amba_dev;
+
+	void __iomem *base;
+
+	/* a list of queues so we can dispatch IRQs */
+	spinlock_t lock;
+	struct list_head virtqueues;
+};
+
+struct virtio_amba_vq_info {
+	/* the actual virtqueue */
+	struct virtqueue *vq;
+
+	/* the number of entries in the queue */
+	int num;
+
+	/* the index of the queue */
+	int queue_index;
+
+	/* the virtual address of the ring queue */
+	void *queue;
+
+	/* the list node for the virtqueues list */
+	struct list_head node;
+};
+
+
+
+/* Configuration interface */
+
+static u32 va_get_features(struct virtio_device *vdev)
+{
+	struct virtio_amba_device *va_dev = to_virtio_amba_device(vdev);
+
+	/* When someone needs more than 32 feature bits, we'll need to
+	 * steal a bit to indicate that the rest are somewhere else. */
+	return readl(va_dev->base + VIRTIO_AMBA_HOST_FEATURES);
+}
+
+static void va_finalize_features(struct virtio_device *vdev)
+{
+	struct virtio_amba_device *va_dev = to_virtio_amba_device(vdev);
+
+	/* Give virtio_ring a chance to accept features. */
+	vring_transport_features(vdev);
+
+	/* We only support 32 feature bits. */
+	BUILD_BUG_ON(ARRAY_SIZE(vdev->features) != 1);
+	writel(vdev->features[0], va_dev->base + VIRTIO_AMBA_GUEST_FEATURES);
+}
+
+static void va_get(struct virtio_device *vdev, unsigned offset,
+		   void *buf, unsigned len)
+{
+	struct virtio_amba_device *va_dev = to_virtio_amba_device(vdev);
+	u8 *ptr = buf;
+	int i;
+
+	for (i = 0; i < len; i++)
+		ptr[i] = readb(va_dev->base + VIRTIO_AMBA_CONFIG + offset + i);
+}
+
+static void va_set(struct virtio_device *vdev, unsigned offset,
+		   const void *buf, unsigned len)
+{
+	struct virtio_amba_device *va_dev = to_virtio_amba_device(vdev);
+	const u8 *ptr = buf;
+	int i;
+
+	for (i = 0; i < len; i++)
+		writeb(ptr[i], va_dev->base + VIRTIO_AMBA_CONFIG + offset + i);
+}
+
+static u8 va_get_status(struct virtio_device *vdev)
+{
+	struct virtio_amba_device *va_dev = to_virtio_amba_device(vdev);
+
+	return readb(va_dev->base + VIRTIO_AMBA_STATUS) & 0xff;
+}
+
+static void va_set_status(struct virtio_device *vdev, u8 status)
+{
+	struct virtio_amba_device *va_dev = to_virtio_amba_device(vdev);
+
+	/* We should never be setting status to 0. */
+	BUG_ON(status == 0);
+
+	writeb(status, va_dev->base + VIRTIO_AMBA_STATUS);
+}
+
+static void va_reset(struct virtio_device *vdev)
+{
+	struct virtio_amba_device *va_dev = to_virtio_amba_device(vdev);
+
+	/* 0 status means a reset. */
+	writeb(0, va_dev->base + VIRTIO_AMBA_STATUS);
+}
+
+
+
+/* Transport interface */
+
+/* the notify function used when creating a virt queue */
+static void va_notify(struct virtqueue *vq)
+{
+	struct virtio_amba_device *va_dev = to_virtio_amba_device(vq->vdev);
+	struct virtio_amba_vq_info *info = vq->priv;
+
+	/* We write the queue's selector into the notification register to
+	 * signal the other end */
+	writel(info->queue_index, va_dev->base + VIRTIO_AMBA_QUEUE_NOTIFY);
+}
+
+/* Notify all virtqueues on an interrupt. */
+static irqreturn_t va_interrupt(int irq, void *opaque)
+{
+	struct virtio_amba_device *va_dev = opaque;
+	struct virtio_amba_vq_info *info;
+	irqreturn_t ret = IRQ_NONE;
+	unsigned long flags;
+
+	writeb(1, va_dev->base + VIRTIO_AMBA_INTERRUPT_ACK);
+
+	spin_lock_irqsave(&va_dev->lock, flags);
+	list_for_each_entry(info, &va_dev->virtqueues, node) {
+		if (vring_interrupt(irq, info->vq) == IRQ_HANDLED)
+			ret = IRQ_HANDLED;
+	}
+	spin_unlock_irqrestore(&va_dev->lock, flags);
+
+	return ret;
+}
+
+
+
+static void va_del_vq(struct virtqueue *vq)
+{
+	struct virtio_amba_device *va_dev = to_virtio_amba_device(vq->vdev);
+	struct virtio_amba_vq_info *info = vq->priv;
+	unsigned long flags, size;
+
+	spin_lock_irqsave(&va_dev->lock, flags);
+	list_del(&info->node);
+	spin_unlock_irqrestore(&va_dev->lock, flags);
+
+	writel(info->queue_index, va_dev->base + VIRTIO_AMBA_QUEUE_SEL);
+
+	vring_del_virtqueue(vq);
+
+	/* Select and deactivate the queue */
+	writel(0, va_dev->base + VIRTIO_AMBA_QUEUE_PFN);
+
+	size = PAGE_ALIGN(vring_size(info->num, VIRTIO_AMBA_VRING_ALIGN));
+	free_pages_exact(info->queue, size);
+	kfree(info);
+}
+
+static void va_del_vqs(struct virtio_device *vdev)
+{
+	struct virtio_amba_device *va_dev = to_virtio_amba_device(vdev);
+	struct virtqueue *vq, *n;
+
+	list_for_each_entry_safe(vq, n, &vdev->vqs, list)
+		va_del_vq(vq);
+
+	free_irq(va_dev->amba_dev->irq[0], va_dev);
+}
+
+
+
+static struct virtqueue *va_setup_vq(struct virtio_device *vdev, unsigned index,
+				  void (*callback)(struct virtqueue *vq),
+				  const char *name)
+{
+	struct virtio_amba_device *va_dev = to_virtio_amba_device(vdev);
+	struct virtio_amba_vq_info *info;
+	struct virtqueue *vq;
+	unsigned long flags, size;
+	u16 num;
+	int err;
+
+	/* Select the queue we're interested in */
+	writel(index, va_dev->base + VIRTIO_AMBA_QUEUE_SEL);
+
+	/* Check if queue is either not available or already active. */
+	num = readl(va_dev->base + VIRTIO_AMBA_QUEUE_NUM);
+	if (!num || readl(va_dev->base + VIRTIO_AMBA_QUEUE_PFN)) {
+		err = -ENOENT;
+		goto error_available;
+	}
+
+	/* Allocate and fill out our structure the represents an active
+	 * queue */
+	info = kmalloc(sizeof(struct virtio_amba_vq_info), GFP_KERNEL);
+	if (!info) {
+		err = -ENOMEM;
+		goto error_kmalloc;
+	}
+
+	info->queue_index = index;
+	info->num = num;
+
+	size = PAGE_ALIGN(vring_size(num, VIRTIO_AMBA_VRING_ALIGN));
+	info->queue = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
+	if (info->queue == NULL) {
+		err = -ENOMEM;
+		goto error_alloc_pages;
+	}
+
+	/* Activate the queue */
+	writel(virt_to_phys(info->queue) >> VIRTIO_AMBA_QUEUE_ADDR_SHIFT,
+		  va_dev->base + VIRTIO_AMBA_QUEUE_PFN);
+
+	/* Create the vring */
+	vq = vring_new_virtqueue(info->num, VIRTIO_AMBA_VRING_ALIGN,
+				 vdev, info->queue, va_notify, callback, name);
+	if (!vq) {
+		err = -ENOMEM;
+		goto error_new_virtqueue;
+	}
+
+	vq->priv = info;
+	info->vq = vq;
+
+	spin_lock_irqsave(&va_dev->lock, flags);
+	list_add(&info->node, &va_dev->virtqueues);
+	spin_unlock_irqrestore(&va_dev->lock, flags);
+
+	return vq;
+
+error_new_virtqueue:
+	writel(0, va_dev->base + VIRTIO_AMBA_QUEUE_PFN);
+	free_pages_exact(info->queue, size);
+error_alloc_pages:
+	kfree(info);
+error_kmalloc:
+error_available:
+	return ERR_PTR(err);
+}
+
+static int va_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+		       struct virtqueue *vqs[],
+		       vq_callback_t *callbacks[],
+		       const char *names[])
+{
+	struct virtio_amba_device *va_dev = to_virtio_amba_device(vdev);
+	int i, err;
+
+	err = request_irq(va_dev->amba_dev->irq[0], va_interrupt,
+			  IRQF_SHARED, dev_name(&vdev->dev), va_dev);
+	if (err)
+		goto error_request_irq;
+
+	for (i = 0; i < nvqs; ++i) {
+		vqs[i] = va_setup_vq(vdev, i, callbacks[i], names[i]);
+		if (IS_ERR(vqs[i])) {
+			err = PTR_ERR(vqs[i]);
+			goto error_va_setup_vq;
+		}
+	}
+
+	return 0;
+
+error_va_setup_vq:
+	va_del_vqs(vdev);
+error_request_irq:
+	return err;
+}
+
+
+
+static struct virtio_config_ops virtio_amba_config_ops = {
+	.get		= va_get,
+	.set		= va_set,
+	.get_status	= va_get_status,
+	.set_status	= va_set_status,
+	.reset		= va_reset,
+	.find_vqs	= va_find_vqs,
+	.del_vqs	= va_del_vqs,
+	.get_features	= va_get_features,
+	.finalize_features = va_finalize_features,
+};
+
+
+
+/* AMBA device */
+
+static int __devinit virtio_amba_probe(struct amba_device *amba_dev,
+		const struct amba_id *id)
+{
+	struct virtio_amba_device *va_dev;
+	int err;
+
+	va_dev = kzalloc(sizeof(struct virtio_amba_device), GFP_KERNEL);
+	if (va_dev == NULL) {
+		err = -ENOMEM;
+		goto error_kzalloc;
+	}
+
+	va_dev->vdev.dev.parent = &amba_dev->dev;
+	va_dev->vdev.config = &virtio_amba_config_ops;
+	va_dev->amba_dev = amba_dev;
+	INIT_LIST_HEAD(&va_dev->virtqueues);
+	spin_lock_init(&va_dev->lock);
+
+	err = amba_request_regions(amba_dev, "virtio-amba");
+	if (err)
+		goto error_request_regions;
+
+	va_dev->base = ioremap(amba_dev->res.start,
+			resource_size(&amba_dev->res));
+	if (va_dev->base == NULL)
+		goto error_ioremap;
+
+	/* We use the "Configuration" field in Prime Cell ID
+	 * as the virtio device id. */
+	va_dev->vdev.id.device = amba_config(amba_dev);
+
+	amba_set_drvdata(amba_dev, va_dev);
+
+	err = register_virtio_device(&va_dev->vdev);
+	if (err)
+		goto error_register_virtio_device;
+
+	return 0;
+
+error_register_virtio_device:
+	iounmap(va_dev->base);
+error_ioremap:
+	amba_release_regions(amba_dev);
+error_request_regions:
+	kfree(va_dev);
+error_kzalloc:
+	return err;
+}
+
+static int __devexit virtio_amba_remove(struct amba_device *amba_dev)
+{
+	struct virtio_amba_device *va_dev = amba_get_drvdata(amba_dev);
+
+	unregister_virtio_device(&va_dev->vdev);
+
+	iounmap(va_dev->base);
+	amba_release_regions(amba_dev);
+	kfree(va_dev);
+
+	return 0;
+}
+
+
+
+/* AMBA driver */
+
+static struct amba_id virtio_amba_ids[] = {
+	{
+		.id	= 0x00041730,
+		.mask	= 0x000fffff,
+	},
+	{ 0, 0 }
+};
+
+static struct amba_driver virtio_amba_driver = {
+	.drv.name	= "virtio-amba",
+	.probe		= virtio_amba_probe,
+	.remove		= __devexit_p(virtio_amba_remove),
+	.id_table	= virtio_amba_ids,
+};
+
+static int __init virtio_amba_init(void)
+{
+	return amba_driver_register(&virtio_amba_driver);
+}
+
+static void __exit virtio_amba_exit(void)
+{
+	amba_driver_unregister(&virtio_amba_driver);
+}
+
+module_init(virtio_amba_init);
+module_exit(virtio_amba_exit);
+
+MODULE_AUTHOR("Pawel Moll <pawel.moll@....com>");
+MODULE_DESCRIPTION("AMBA bus driver for virtio devices");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/virtio_amba.h b/include/linux/virtio_amba.h
new file mode 100644
index 0000000..c754b87
--- /dev/null
+++ b/include/linux/virtio_amba.h
@@ -0,0 +1,62 @@
+/*
+ * Virtio AMBA driver
+ *
+ * Copyright 2011, ARM Ltd.
+ *
+ * Based on Virtio PCI driver by Anthony Liguori, copyright IBM Corp. 2007
+ *
+ * This header is BSD licensed so anyone can use the definitions to implement
+ * compatible drivers/servers.
+ */
+
+#ifndef _LINUX_VIRTIO_AMBA_H
+#define _LINUX_VIRTIO_AMBA_H
+
+/* Bitmask of the features supported by the host (32-bit register) */
+#define VIRTIO_AMBA_HOST_FEATURES	0x000
+
+/* Bitmask of features activated by the guest (32-bit register) */
+#define VIRTIO_AMBA_GUEST_FEATURES	0x004
+
+/* PFN for the currently selected queue (32-bit register) */
+#define VIRTIO_AMBA_QUEUE_PFN		0x008
+
+/* Queue size for the currently selected queue (32-bit register) */
+#define VIRTIO_AMBA_QUEUE_NUM		0x00c
+
+/* Queue selector (32-bit register) */
+#define VIRTIO_AMBA_QUEUE_SEL		0x010
+
+/* Queue notifier (32-bit register) */
+#define VIRTIO_AMBA_QUEUE_NOTIFY	0x014
+
+/* Interrupt acknowledge (8-bit register) */
+#define VIRTIO_AMBA_INTERRUPT_ACK	0x018
+
+/* Device status register (8-bit register) */
+#define VIRTIO_AMBA_STATUS		0x01c
+
+/* The config space is defined by each driver as
+ * the per-driver configuration space */
+#define VIRTIO_AMBA_CONFIG		0x020
+
+/* PrimeCell identification registers (8-bit registers) */
+#define VIRTIO_AMBA_PERIPH_ID0		0xfe0
+#define VIRTIO_AMBA_PERIPH_ID1		0xfe4
+#define VIRTIO_AMBA_PERIPH_ID2		0xfe8
+#define VIRTIO_AMBA_PERIPH_ID3		0xfec
+#define VIRTIO_AMBA_PCELL_ID0		0xff0
+#define VIRTIO_AMBA_PCELL_ID1		0xff4
+#define VIRTIO_AMBA_PCELL_ID2		0xff8
+#define VIRTIO_AMBA_PCELL_ID3		0xffc
+
+
+/* How many bits to shift physical queue address written to QUEUE_PFN.
+ * 12 is historical, and due to 4kb page size. */
+#define VIRTIO_AMBA_QUEUE_ADDR_SHIFT	12
+
+/* The alignment to use between consumer and producer parts of vring.
+ * Page size again. */
+#define VIRTIO_AMBA_VRING_ALIGN		4096
+
+#endif
-- 
1.6.3.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ