From: Bin Meng Date: Mon, 15 Oct 2018 09:21:17 +0000 (-0700) Subject: virtio: Add virtio over pci transport driver X-Git-Url: http://git.dujemihanovic.xyz/html/index.html?a=commitdiff_plain;h=4135e10732a0eb3c66b34d2fde32eb54d6323fc6;p=u-boot.git virtio: Add virtio over pci transport driver This adds a transport driver that implements UCLASS_VIRTIO for virtio over pci, which is commonly used on x86. It only supports the legacy interface of the pci transport, which is the default device that QEMU emulates. Signed-off-by: Bin Meng Reviewed-by: Simon Glass --- diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index b72477ae28..753912f0f9 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -29,6 +29,14 @@ config VIRTIO_MMIO This driver provides support for memory mapped virtio platform device driver. +config VIRTIO_PCI + bool "PCI driver for virtio devices" + depends on DM_PCI + select VIRTIO + help + This driver provides support for virtio based paravirtual device + drivers over PCI. + config VIRTIO_NET bool "virtio net driver" depends on VIRTIO diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index 5fe742815b..5ee6183e60 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -5,5 +5,6 @@ obj-y += virtio-uclass.o virtio_ring.o obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o +obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o obj-$(CONFIG_VIRTIO_NET) += virtio_net.o obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c new file mode 100644 index 0000000000..a3f1083153 --- /dev/null +++ b/drivers/virtio/virtio_pci.c @@ -0,0 +1,421 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018, Bin Meng + * + * VirtIO PCI bus transport driver + * Ported from Linux drivers/virtio/virtio_pci*.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "virtio_pci.h" + +#define VIRTIO_PCI_DRV_NAME "virtio-pci" + +/* PCI device ID in the range 0x1000 to 0x103f */ +#define VIRTIO_PCI_VENDOR_ID 0x1af4 +#define VIRTIO_PCI_DEVICE_ID00 0x1000 +#define VIRTIO_PCI_DEVICE_ID01 0x1001 +#define VIRTIO_PCI_DEVICE_ID02 0x1002 +#define VIRTIO_PCI_DEVICE_ID03 0x1003 +#define VIRTIO_PCI_DEVICE_ID04 0x1004 +#define VIRTIO_PCI_DEVICE_ID05 0x1005 +#define VIRTIO_PCI_DEVICE_ID06 0x1006 +#define VIRTIO_PCI_DEVICE_ID07 0x1007 +#define VIRTIO_PCI_DEVICE_ID08 0x1008 +#define VIRTIO_PCI_DEVICE_ID09 0x1009 +#define VIRTIO_PCI_DEVICE_ID0A 0x100a +#define VIRTIO_PCI_DEVICE_ID0B 0x100b +#define VIRTIO_PCI_DEVICE_ID0C 0x100c +#define VIRTIO_PCI_DEVICE_ID0D 0x100d +#define VIRTIO_PCI_DEVICE_ID0E 0x100e +#define VIRTIO_PCI_DEVICE_ID0F 0x100f +#define VIRTIO_PCI_DEVICE_ID10 0x1010 +#define VIRTIO_PCI_DEVICE_ID11 0x1011 +#define VIRTIO_PCI_DEVICE_ID12 0x1012 +#define VIRTIO_PCI_DEVICE_ID13 0x1013 +#define VIRTIO_PCI_DEVICE_ID14 0x1014 +#define VIRTIO_PCI_DEVICE_ID15 0x1015 +#define VIRTIO_PCI_DEVICE_ID16 0x1016 +#define VIRTIO_PCI_DEVICE_ID17 0x1017 +#define VIRTIO_PCI_DEVICE_ID18 0x1018 +#define VIRTIO_PCI_DEVICE_ID19 0x1019 +#define VIRTIO_PCI_DEVICE_ID1A 0x101a +#define VIRTIO_PCI_DEVICE_ID1B 0x101b +#define VIRTIO_PCI_DEVICE_ID1C 0x101c +#define VIRTIO_PCI_DEVICE_ID1D 0x101d +#define VIRTIO_PCI_DEVICE_ID1E 0x101e +#define VIRTIO_PCI_DEVICE_ID1F 0x101f +#define VIRTIO_PCI_DEVICE_ID20 0x1020 +#define VIRTIO_PCI_DEVICE_ID21 0x1021 +#define VIRTIO_PCI_DEVICE_ID22 0x1022 +#define VIRTIO_PCI_DEVICE_ID23 0x1023 +#define VIRTIO_PCI_DEVICE_ID24 0x1024 +#define VIRTIO_PCI_DEVICE_ID25 0x1025 +#define VIRTIO_PCI_DEVICE_ID26 0x1026 +#define VIRTIO_PCI_DEVICE_ID27 0x1027 +#define VIRTIO_PCI_DEVICE_ID28 0x1028 +#define VIRTIO_PCI_DEVICE_ID29 0x1029 +#define VIRTIO_PCI_DEVICE_ID2A 0x102a +#define VIRTIO_PCI_DEVICE_ID2B 0x102b +#define VIRTIO_PCI_DEVICE_ID2C 0x102c +#define VIRTIO_PCI_DEVICE_ID2D 0x102d +#define VIRTIO_PCI_DEVICE_ID2E 0x102e +#define VIRTIO_PCI_DEVICE_ID2F 0x102f +#define VIRTIO_PCI_DEVICE_ID30 0x1030 +#define VIRTIO_PCI_DEVICE_ID31 0x1031 +#define VIRTIO_PCI_DEVICE_ID32 0x1032 +#define VIRTIO_PCI_DEVICE_ID33 0x1033 +#define VIRTIO_PCI_DEVICE_ID34 0x1034 +#define VIRTIO_PCI_DEVICE_ID35 0x1035 +#define VIRTIO_PCI_DEVICE_ID36 0x1036 +#define VIRTIO_PCI_DEVICE_ID37 0x1037 +#define VIRTIO_PCI_DEVICE_ID38 0x1038 +#define VIRTIO_PCI_DEVICE_ID39 0x1039 +#define VIRTIO_PCI_DEVICE_ID3A 0x103a +#define VIRTIO_PCI_DEVICE_ID3B 0x103b +#define VIRTIO_PCI_DEVICE_ID3C 0x103c +#define VIRTIO_PCI_DEVICE_ID3D 0x103d +#define VIRTIO_PCI_DEVICE_ID3E 0x103e +#define VIRTIO_PCI_DEVICE_ID3F 0x103f + +/** + * virtio pci transport driver private data + * + * @ioaddr: pci transport device register base + * @version: pci transport device version + */ +struct virtio_pci_priv { + void __iomem *ioaddr; +}; + +static int virtio_pci_get_config(struct udevice *udev, unsigned int offset, + void *buf, unsigned int len) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + void __iomem *ioaddr = priv->ioaddr + VIRTIO_PCI_CONFIG_OFF(false); + u8 *ptr = buf; + int i; + + for (i = 0; i < len; i++) + ptr[i] = ioread8(ioaddr + i); + + return 0; +} + +static int virtio_pci_set_config(struct udevice *udev, unsigned int offset, + const void *buf, unsigned int len) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + void __iomem *ioaddr = priv->ioaddr + VIRTIO_PCI_CONFIG_OFF(false); + const u8 *ptr = buf; + int i; + + for (i = 0; i < len; i++) + iowrite8(ptr[i], ioaddr + i); + + return 0; +} + +static int virtio_pci_get_status(struct udevice *udev, u8 *status) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + + *status = ioread8(priv->ioaddr + VIRTIO_PCI_STATUS); + + return 0; +} + +static int virtio_pci_set_status(struct udevice *udev, u8 status) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + + /* We should never be setting status to 0 */ + WARN_ON(status == 0); + + iowrite8(status, priv->ioaddr + VIRTIO_PCI_STATUS); + + return 0; +} + +static int virtio_pci_reset(struct udevice *udev) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + + /* 0 status means a reset */ + iowrite8(0, priv->ioaddr + VIRTIO_PCI_STATUS); + + /* + * Flush out the status write, and flush in device writes, + * including MSI-X interrupts, if any. + */ + ioread8(priv->ioaddr + VIRTIO_PCI_STATUS); + + return 0; +} + +static int virtio_pci_get_features(struct udevice *udev, u64 *features) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + + /* + * When someone needs more than 32 feature bits, we'll need to + * steal a bit to indicate that the rest are somewhere else. + */ + *features = ioread32(priv->ioaddr + VIRTIO_PCI_HOST_FEATURES); + + return 0; +} + +static int virtio_pci_set_features(struct udevice *udev) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); + + /* Make sure we don't have any features > 32 bits! */ + WARN_ON((u32)uc_priv->features != uc_priv->features); + + /* We only support 32 feature bits */ + iowrite32(uc_priv->features, priv->ioaddr + VIRTIO_PCI_GUEST_FEATURES); + + return 0; +} + +static struct virtqueue *virtio_pci_setup_vq(struct udevice *udev, + unsigned int index) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + struct virtqueue *vq; + unsigned int num; + int err; + + /* Select the queue we're interested in */ + iowrite16(index, priv->ioaddr + VIRTIO_PCI_QUEUE_SEL); + + /* Check if queue is either not available or already active */ + num = ioread16(priv->ioaddr + VIRTIO_PCI_QUEUE_NUM); + if (!num || ioread32(priv->ioaddr + VIRTIO_PCI_QUEUE_PFN)) { + err = -ENOENT; + goto error_available; + } + + /* Create the vring */ + vq = vring_create_virtqueue(index, num, VIRTIO_PCI_VRING_ALIGN, udev); + if (!vq) { + err = -ENOMEM; + goto error_available; + } + + /* Activate the queue */ + iowrite32(virtqueue_get_desc_addr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT, + priv->ioaddr + VIRTIO_PCI_QUEUE_PFN); + + return vq; + +error_available: + return ERR_PTR(err); +} + +static void virtio_pci_del_vq(struct virtqueue *vq) +{ + struct virtio_pci_priv *priv = dev_get_priv(vq->vdev); + unsigned int index = vq->index; + + iowrite16(index, priv->ioaddr + VIRTIO_PCI_QUEUE_SEL); + + /* Select and deactivate the queue */ + iowrite32(0, priv->ioaddr + VIRTIO_PCI_QUEUE_PFN); + + vring_del_virtqueue(vq); +} + +static int virtio_pci_del_vqs(struct udevice *udev) +{ + struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); + struct virtqueue *vq, *n; + + list_for_each_entry_safe(vq, n, &uc_priv->vqs, list) + virtio_pci_del_vq(vq); + + return 0; +} + +static int virtio_pci_find_vqs(struct udevice *udev, unsigned int nvqs, + struct virtqueue *vqs[]) +{ + int i; + + for (i = 0; i < nvqs; ++i) { + vqs[i] = virtio_pci_setup_vq(udev, i); + if (IS_ERR(vqs[i])) { + virtio_pci_del_vqs(udev); + return PTR_ERR(vqs[i]); + } + } + + return 0; +} + +static int virtio_pci_notify(struct udevice *udev, struct virtqueue *vq) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + + /* + * We write the queue's selector into the notification register + * to signal the other end + */ + iowrite16(vq->index, priv->ioaddr + VIRTIO_PCI_QUEUE_NOTIFY); + + return 0; +} + +static int virtio_pci_bind(struct udevice *udev) +{ + static int num_devs; + char name[20]; + + /* Create a unique device name for PCI type devices */ + sprintf(name, "%s#%u", VIRTIO_PCI_DRV_NAME, num_devs++); + device_set_name(udev, name); + + return 0; +} + +static int virtio_pci_probe(struct udevice *udev) +{ + struct pci_child_platdata *pplat = dev_get_parent_platdata(udev); + struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); + struct virtio_pci_priv *priv = dev_get_priv(udev); + u16 subvendor, subdevice; + u8 revision; + + /* We only own devices >= 0x1000 and <= 0x103f: leave the rest. */ + if (pplat->device < 0x1000 || pplat->device > 0x103f) + return -ENODEV; + + /* Transitional devices must have a PCI revision ID of 0 */ + dm_pci_read_config8(udev, PCI_REVISION_ID, &revision); + if (revision != VIRTIO_PCI_ABI_VERSION) { + printf("(%s): virtio_pci expected ABI version %d, got %d\n", + udev->name, VIRTIO_PCI_ABI_VERSION, revision); + return -ENODEV; + } + + /* + * Transitional devices must have the PCI subsystem device ID matching + * the virtio device ID + */ + dm_pci_read_config16(udev, PCI_SUBSYSTEM_ID, &subdevice); + dm_pci_read_config16(udev, PCI_SUBSYSTEM_VENDOR_ID, &subvendor); + uc_priv->device = subdevice; + uc_priv->vendor = subvendor; + + priv->ioaddr = dm_pci_map_bar(udev, PCI_BASE_ADDRESS_0, PCI_REGION_IO); + if (!priv->ioaddr) + return -ENXIO; + debug("(%s): virtio legacy device reg base %04lx\n", + udev->name, (ulong)priv->ioaddr); + + debug("(%s): device (%d) vendor (%08x) version (%d)\n", udev->name, + uc_priv->device, uc_priv->vendor, revision); + + return 0; +} + +static const struct dm_virtio_ops virtio_pci_ops = { + .get_config = virtio_pci_get_config, + .set_config = virtio_pci_set_config, + .get_status = virtio_pci_get_status, + .set_status = virtio_pci_set_status, + .reset = virtio_pci_reset, + .get_features = virtio_pci_get_features, + .set_features = virtio_pci_set_features, + .find_vqs = virtio_pci_find_vqs, + .del_vqs = virtio_pci_del_vqs, + .notify = virtio_pci_notify, +}; + +U_BOOT_DRIVER(virtio_pci) = { + .name = VIRTIO_PCI_DRV_NAME, + .id = UCLASS_VIRTIO, + .ops = &virtio_pci_ops, + .bind = virtio_pci_bind, + .probe = virtio_pci_probe, + .priv_auto_alloc_size = sizeof(struct virtio_pci_priv), +}; + +static struct pci_device_id virtio_pci_supported[] = { + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID00) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID01) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID02) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID03) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID04) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID05) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID06) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID07) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID08) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID09) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0A) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0B) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0C) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0D) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0E) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID0F) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID10) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID11) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID12) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID13) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID14) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID15) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID16) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID17) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID18) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID19) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1A) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1B) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1C) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1D) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1E) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID1F) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID20) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID21) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID22) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID23) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID24) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID25) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID26) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID27) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID28) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID29) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2A) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2B) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2C) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2D) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2E) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID2F) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID30) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID31) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID32) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID33) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID34) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID35) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID36) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID37) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID38) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID39) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3A) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3B) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3C) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3D) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3E) }, + { PCI_DEVICE(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID3F) }, + {}, +}; + +U_BOOT_PCI_DEVICE(virtio_pci, virtio_pci_supported); diff --git a/drivers/virtio/virtio_pci.h b/drivers/virtio/virtio_pci.h new file mode 100644 index 0000000000..cc753ed7b3 --- /dev/null +++ b/drivers/virtio/virtio_pci.h @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (C) 2018, Bin Meng + * + * From Linux kernel include/uapi/linux/virtio_pci.h + */ + +#ifndef _LINUX_VIRTIO_PCI_H +#define _LINUX_VIRTIO_PCI_H + +#ifndef VIRTIO_PCI_NO_LEGACY + +/* A 32-bit r/o bitmask of the features supported by the host */ +#define VIRTIO_PCI_HOST_FEATURES 0 + +/* A 32-bit r/w bitmask of features activated by the guest */ +#define VIRTIO_PCI_GUEST_FEATURES 4 + +/* A 32-bit r/w PFN for the currently selected queue */ +#define VIRTIO_PCI_QUEUE_PFN 8 + +/* A 16-bit r/o queue size for the currently selected queue */ +#define VIRTIO_PCI_QUEUE_NUM 12 + +/* A 16-bit r/w queue selector */ +#define VIRTIO_PCI_QUEUE_SEL 14 + +/* A 16-bit r/w queue notifier */ +#define VIRTIO_PCI_QUEUE_NOTIFY 16 + +/* An 8-bit device status register */ +#define VIRTIO_PCI_STATUS 18 + +/* + * An 8-bit r/o interrupt status register. Reading the value will return the + * current contents of the ISR and will also clear it. This is effectively + * a read-and-acknowledge. + */ +#define VIRTIO_PCI_ISR 19 + +/* MSI-X registers: only enabled if MSI-X is enabled */ + +/* A 16-bit vector for configuration changes */ +#define VIRTIO_MSI_CONFIG_VECTOR 20 +/* A 16-bit vector for selected queue notifications */ +#define VIRTIO_MSI_QUEUE_VECTOR 22 + +/* + * The remaining space is defined by each driver as the per-driver + * configuration space + */ +#define VIRTIO_PCI_CONFIG_OFF(msix) ((msix) ? 24 : 20) + +/* Virtio ABI version, this must match exactly */ +#define VIRTIO_PCI_ABI_VERSION 0 + +/* + * How many bits to shift physical queue address written to QUEUE_PFN. + * 12 is historical, and due to x86 page size. + */ +#define VIRTIO_PCI_QUEUE_ADDR_SHIFT 12 + +/* + * The alignment to use between consumer and producer parts of vring. + * x86 pagesize again. + */ +#define VIRTIO_PCI_VRING_ALIGN 4096 + +#endif /* VIRTIO_PCI_NO_LEGACY */ + +/* The bit of the ISR which indicates a device configuration change */ +#define VIRTIO_PCI_ISR_CONFIG 0x2 +/* Vector value used to disable MSI for queue */ +#define VIRTIO_MSI_NO_VECTOR 0xffff + +#ifndef VIRTIO_PCI_NO_MODERN + +/* IDs for different capabilities. Must all exist. */ + +/* Common configuration */ +#define VIRTIO_PCI_CAP_COMMON_CFG 1 +/* Notifications */ +#define VIRTIO_PCI_CAP_NOTIFY_CFG 2 +/* ISR access */ +#define VIRTIO_PCI_CAP_ISR_CFG 3 +/* Device specific configuration */ +#define VIRTIO_PCI_CAP_DEVICE_CFG 4 +/* PCI configuration access */ +#define VIRTIO_PCI_CAP_PCI_CFG 5 + +/* This is the PCI capability header: */ +struct virtio_pci_cap { + __u8 cap_vndr; /* Generic PCI field: PCI_CAP_ID_VNDR */ + __u8 cap_next; /* Generic PCI field: next ptr */ + __u8 cap_len; /* Generic PCI field: capability length */ + __u8 cfg_type; /* Identifies the structure */ + __u8 bar; /* Where to find it */ + __u8 padding[3]; /* Pad to full dword */ + __le32 offset; /* Offset within bar */ + __le32 length; /* Length of the structure, in bytes */ +}; + +struct virtio_pci_notify_cap { + struct virtio_pci_cap cap; + __le32 notify_off_multiplier; /* Multiplier for queue_notify_off */ +}; + +/* Fields in VIRTIO_PCI_CAP_COMMON_CFG: */ +struct virtio_pci_common_cfg { + /* About the whole device */ + __le32 device_feature_select; /* read-write */ + __le32 device_feature; /* read-only */ + __le32 guest_feature_select; /* read-write */ + __le32 guest_feature; /* read-write */ + __le16 msix_config; /* read-write */ + __le16 num_queues; /* read-only */ + __u8 device_status; /* read-write */ + __u8 config_generation; /* read-only */ + + /* About a specific virtqueue */ + __le16 queue_select; /* read-write */ + __le16 queue_size; /* read-write, power of 2 */ + __le16 queue_msix_vector; /* read-write */ + __le16 queue_enable; /* read-write */ + __le16 queue_notify_off; /* read-only */ + __le32 queue_desc_lo; /* read-write */ + __le32 queue_desc_hi; /* read-write */ + __le32 queue_avail_lo; /* read-write */ + __le32 queue_avail_hi; /* read-write */ + __le32 queue_used_lo; /* read-write */ + __le32 queue_used_hi; /* read-write */ +}; + +/* Fields in VIRTIO_PCI_CAP_PCI_CFG: */ +struct virtio_pci_cfg_cap { + struct virtio_pci_cap cap; + __u8 pci_cfg_data[4]; /* Data for BAR access */ +}; + +/* Macro versions of offsets for the Old Timers! */ +#define VIRTIO_PCI_CAP_VNDR 0 +#define VIRTIO_PCI_CAP_NEXT 1 +#define VIRTIO_PCI_CAP_LEN 2 +#define VIRTIO_PCI_CAP_CFG_TYPE 3 +#define VIRTIO_PCI_CAP_BAR 4 +#define VIRTIO_PCI_CAP_OFFSET 8 +#define VIRTIO_PCI_CAP_LENGTH 12 + +#define VIRTIO_PCI_NOTIFY_CAP_MULT 16 + +#define VIRTIO_PCI_COMMON_DFSELECT 0 +#define VIRTIO_PCI_COMMON_DF 4 +#define VIRTIO_PCI_COMMON_GFSELECT 8 +#define VIRTIO_PCI_COMMON_GF 12 +#define VIRTIO_PCI_COMMON_MSIX 16 +#define VIRTIO_PCI_COMMON_NUMQ 18 +#define VIRTIO_PCI_COMMON_STATUS 20 +#define VIRTIO_PCI_COMMON_CFGGENERATION 21 +#define VIRTIO_PCI_COMMON_Q_SELECT 22 +#define VIRTIO_PCI_COMMON_Q_SIZE 24 +#define VIRTIO_PCI_COMMON_Q_MSIX 26 +#define VIRTIO_PCI_COMMON_Q_ENABLE 28 +#define VIRTIO_PCI_COMMON_Q_NOFF 30 +#define VIRTIO_PCI_COMMON_Q_DESCLO 32 +#define VIRTIO_PCI_COMMON_Q_DESCHI 36 +#define VIRTIO_PCI_COMMON_Q_AVAILLO 40 +#define VIRTIO_PCI_COMMON_Q_AVAILHI 44 +#define VIRTIO_PCI_COMMON_Q_USEDLO 48 +#define VIRTIO_PCI_COMMON_Q_USEDHI 52 + +#endif /* VIRTIO_PCI_NO_MODERN */ + +#endif /* _LINUX_VIRTIO_PCI_H */