[coreboot-gerrit] Patch set updated for coreboot: 45d8f0d libpayload: Add Virtio layer and storage device support

Himanshu Chauhan (hschauhan@nulltrace.org) gerrit at coreboot.org
Fri Sep 19 05:37:12 CEST 2014


Himanshu Chauhan (hschauhan at nulltrace.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/6934

-gerrit

commit 45d8f0d38352fa4df8c6dadd936d871d58c16c05
Author: Himanshu Chauhan <hschauhan at nulltrace.org>
Date:   Thu Sep 18 10:07:11 2014 +0530

    libpayload: Add Virtio layer and storage device support
    
    Coreboot+FILO can be used in virtualized environment in
    which case it would need to support Virtio devices. Many
    hosts, for performance purpose, emulate only Virtio devices.
    
    The files virtio-pci.c, virtio-blk.c, virtio-ring.c have
    been taken and adapted from Seabios. The adaptation changes
    are mainly the structural changes to fit the code in libpayload
    infrastructure.
    
    As of now, writes to devices are not implemented.
    
    Change-Id: I0d2fd295c612f4d3c05b8a6028b440a39b173a92
    Signed-off-by: Himanshu Chauhan <hschauhan at nulltrace.org>
---
 payloads/libpayload/Config.in                    |  18 ++
 payloads/libpayload/drivers/Makefile.inc         |   5 +
 payloads/libpayload/drivers/storage/virtio-blk.c | 264 +++++++++++++++++++++++
 payloads/libpayload/drivers/virtio/virtio-pci.c  | 118 ++++++++++
 payloads/libpayload/drivers/virtio/virtio-ring.c | 158 ++++++++++++++
 payloads/libpayload/include/storage/virtio-blk.h |  46 ++++
 payloads/libpayload/include/virtio/virtio-pci.h  | 110 ++++++++++
 payloads/libpayload/include/virtio/virtio-ring.h | 130 +++++++++++
 8 files changed, 849 insertions(+)

diff --git a/payloads/libpayload/Config.in b/payloads/libpayload/Config.in
index 2a5048c..1c7fc2b 100644
--- a/payloads/libpayload/Config.in
+++ b/payloads/libpayload/Config.in
@@ -269,6 +269,18 @@ config SPEAKER
 	depends on ARCH_X86
 	default y
 
+config VIRTIO
+	bool "Support Virtio devices"
+	help
+	  When running in hypervisor, this enables the set of virtual
+	  IO devices like Console, network, Block device.
+
+config VIRTIO_PCI
+	bool "Support Virtio PCI Transport"
+	depends on VIRTIO && PCI
+	help
+	  Use PCI to detect Virtio devices.
+
 config STORAGE
 	bool "Support for storage devices"
 	default y
@@ -317,6 +329,12 @@ config STORAGE_AHCI_ONLY_TESTED
 	  If this option is selected only AHCI controllers which are known
 	  to work will be used.
 
+config STORAGE_VIRTIO
+	bool "Support Virtio block device"
+	depends on VIRTIO && STORAGE && (VIRTIO_PCI)
+	help
+	  Enable read/write to a virtio compliant disk drive.
+
 config USB
 	bool "USB Support"
 	default n
diff --git a/payloads/libpayload/drivers/Makefile.inc b/payloads/libpayload/drivers/Makefile.inc
index 881c801..11d629a 100644
--- a/payloads/libpayload/drivers/Makefile.inc
+++ b/payloads/libpayload/drivers/Makefile.inc
@@ -91,3 +91,8 @@ libc-$(CONFIG_LP_USB_MSC) += usb/usbmsc.c
 
 # used by both USB HID and keyboard
 libc-y += hid.c
+
+# For Virtio layer & devices
+libc-$(CONFIG_LP_VIRTIO) += virtio/virtio-ring.c
+libc-$(CONFIG_LP_VIRTIO_PCI) += virtio/virtio-pci.c
+libc-$(CONFIG_LP_STORAGE_VIRTIO) += storage/virtio-blk.c
diff --git a/payloads/libpayload/drivers/storage/virtio-blk.c b/payloads/libpayload/drivers/storage/virtio-blk.c
new file mode 100644
index 0000000..e83b619
--- /dev/null
+++ b/payloads/libpayload/drivers/storage/virtio-blk.c
@@ -0,0 +1,264 @@
+// Virtio block boot support.
+//
+// Copyright (C) 2010 Red Hat Inc.
+// Copyright (c) 2014 Himanshu chauhan <hschauhan at nulltrace.org>
+//
+// Authors:
+//  Gleb Natapov <gnatapov at redhat.com>
+//
+// This file may be distributed under the terms of the GNU LGPLv3 license.
+//
+// Himanshu: Ported from seabios virtio.
+//
+
+#include <libpayload.h>
+#include <config.h>
+#include <arch/io.h>
+#ifdef CONFIG_LP_PCI
+#include <pci.h>
+#endif
+
+#ifdef CONFIG_LP_VIRTIO_PCI
+#include <virtio/virtio-pci.h>
+#endif
+#include <virtio/virtio-ring.h>
+#include <storage/virtio-blk.h>
+
+struct disk_info {
+	uint16_t type;
+	uint16_t heads;
+	uint16_t cylinders;
+	uint16_t sectors_per_track;
+	uint8_t  model_number[41];
+	uint8_t  slave;
+	uint64_t sectors;
+	int  address_mode;
+#define ADDRESS_MODE_CHS    0
+#define ADDRESS_MODE_LBA    1
+#define ADDRESS_MODE_LBA48  2
+#define ADDRESS_MODE_PACKET 3
+	uint32_t blksize;
+	unsigned present : 1;
+};
+
+struct virtiodrive_s {
+	struct disk_info drive;
+	struct vring_virtqueue *vq;
+#if defined(CONFIG_LP_VIRTIO_PCI)
+	pcidev_t bdf;
+#endif
+	uint16_t ioaddr;
+	int init_done;
+};
+
+#define MAX_VIRTIO_DEVICES	4
+
+struct virtio_drives {
+	int scan_done;
+	int found;
+	struct virtiodrive_s disks[MAX_VIRTIO_DEVICES];
+} vdrives = { .scan_done = 0, .found = 0 };
+
+static int
+virtio_blk_op(struct virtiodrive_s *vdrive_g, uint64_t sector, void *buf_fl,
+	      int write)
+{
+	struct vring_virtqueue *vq = vdrive_g->vq;
+	struct virtio_blk_outhdr hdr = {
+		.type = write ? VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN,
+		.ioprio = 0,
+		.sector = sector,
+	};
+
+	u8 status = VIRTIO_BLK_S_UNSUPP;
+	struct vring_list sg[] = {
+		{
+			.addr       = (char *)&hdr,
+			.length     = sizeof(hdr),
+		},
+		{
+			.addr       = buf_fl,
+			.length     = vdrive_g->drive.blksize,
+		},
+		{
+			.addr       = (char *)&status,
+			.length     = sizeof(status),
+		},
+	};
+
+	/* Add to virtqueue and kick host */
+	if (write) {
+		vring_add_buf(vq, sg, 2, 1, 0, 0);
+	} else {
+		vring_add_buf(vq, sg, 1, 2, 0, 0);
+	}
+
+	vring_kick(vdrive_g->ioaddr, vq, 1);
+
+	/* Wait for reply */
+	while (!vring_more_used(vq))
+		mdelay(5);
+
+	barrier();
+
+	/* Reclaim virtqueue element */
+	vring_get_buf(vq, NULL);
+
+	/* Clear interrupt status register.  Avoid leaving interrupts stuck if
+	 * VRING_AVAIL_F_NO_INTERRUPT was ignored and interrupts were raised.
+	 */
+	vp_get_isr(vdrive_g->ioaddr);
+
+	if (status != VIRTIO_BLK_S_OK)
+		return -1;
+
+	return 0;
+}
+
+int virtio_blk_read(int drive, uint64_t sector, void *buffer)
+{
+	int ret;
+
+	if (drive > MAX_VIRTIO_DEVICES || drive >= vdrives.found)
+		return 0;
+
+	ret = virtio_blk_op(&vdrives.disks[drive], sector, buffer, 0);
+	return ret;
+}
+
+/**/
+/* FIXME: FILO Will Never Write it seems, if it does later add write routine */
+/**/
+#ifdef CONFIG_LP_VIRTIO_PCI
+static int init_virtio_blk(struct virtiodrive_s *vdisk)
+{
+	u16 bdf = vdisk->bdf;
+	printf("found virtio-blk at %x:%x\n", PCI_BUS(bdf), PCI_SLOT(bdf));
+
+	if (vdisk->init_done) return 0;
+
+	memset(vdisk, 0, sizeof(struct virtiodrive_s));
+	vdisk->drive.type = DTYPE_VIRTIO_BLK;
+
+	u16 ioaddr = vp_init_simple(bdf);
+	vdisk->ioaddr = ioaddr;
+	if (vp_find_vq(ioaddr, 0, &vdisk->vq) < 0 ) {
+		printf("fail to find vq for virtio-blk %x:%x\n",
+		       PCI_BUS(bdf), PCI_SLOT(bdf));
+		goto fail;
+	}
+
+	struct virtio_blk_config cfg;
+	vp_get(ioaddr, 0, &cfg, sizeof(cfg));
+
+	u32 f = vp_get_features(ioaddr);
+	vdisk->drive.blksize = (f & (1 << VIRTIO_BLK_F_BLK_SIZE)) ?
+		cfg.blk_size : DISK_SECTOR_SIZE;
+
+	vdisk->drive.sectors = cfg.capacity;
+	printf("virtio-blk %x:%x blksize=%d sectors=%u\n",
+	       PCI_BUS(bdf), PCI_SLOT(bdf),
+	       vdisk->drive.blksize, (u32)vdisk->drive.sectors);
+
+	if (vdisk->drive.blksize != DISK_SECTOR_SIZE) {
+		printf("virtio-blk %x:%x block size %d is unsupported\n",
+		       PCI_BUS(bdf), PCI_SLOT(bdf),
+		       vdisk->drive.blksize);
+		goto fail;
+	}
+
+	vdisk->drive.cylinders = cfg.cylinders;
+	vdisk->drive.heads = cfg.heads;
+	vdisk->drive.sectors = cfg.sectors;
+	vdisk->drive.present = 1;
+	vdisk->init_done = 1;
+
+	vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE |
+		      VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK);
+
+	return 0;
+
+fail:
+	free(vdisk->vq);
+
+	return -1;
+}
+
+static int pci_find_virtio_disk_on_bus(int bus)
+{
+	int device, func;
+	u32 vendor, devid;
+	u32 val;
+
+        for (device = 0; device < 32; device++) {
+		for (func = 0; func < 8; func++) {
+			pcidev_t currdev = PCI_DEV(bus, device, func);
+
+			val = pci_read_config32(currdev, REG_VENDOR_ID);
+			vendor = val & 0xFFFF;
+			devid = val >> 16;
+
+			if (vendor == PCI_VENDOR_ID_REDHAT_QUMRANET
+			    && devid == PCI_DEVICE_ID_VIRTIO_BLK) {
+				if (vdrives.found < MAX_VIRTIO_DEVICES) {
+					vdrives.disks[vdrives.found].bdf = currdev;
+					vdrives.found++;
+				}
+			}
+		}
+	}
+
+	vdrives.scan_done = 1;
+
+	if (vdrives.found) {
+		return vdrives.found;
+	}
+
+	return -1;
+}
+
+static int pci_find_virtio_disks(int bus)
+{
+	return pci_find_virtio_disk_on_bus(bus);
+}
+
+static int find_virtio_disks(void)
+{
+	/* Find a Virtio storage device */
+	if (!pci_find_virtio_disks(0)) {
+		printf("PCI Virtio Disks not found\n");
+		return -1;
+	}
+
+	return 0;
+}
+#else /* !CONFIG_LP_VIRTIO_PCI */
+# define find_virtio_disks()	(-1)
+#endif
+
+int virtio_disk_probe(int drive)
+{
+	if (drive >= MAX_VIRTIO_DEVICES) {
+		printf("Unsupported drive number\n");
+		return -1;
+	}
+
+	if (!vdrives.scan_done) {
+		printf("Scanning virtio disks\n");
+		if (find_virtio_disks() != 0) {
+			return -1;
+		}
+	}
+
+	if (drive >= vdrives.found) {
+		printf("No drive at number %d\n", drive);
+		return -1;
+	}
+
+	if (init_virtio_blk(&vdrives.disks[drive]) != 0) {
+		printf("No drive detected at virtio index %d\n", drive);
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/payloads/libpayload/drivers/virtio/virtio-pci.c b/payloads/libpayload/drivers/virtio/virtio-pci.c
new file mode 100644
index 0000000..097a5da
--- /dev/null
+++ b/payloads/libpayload/drivers/virtio/virtio-pci.c
@@ -0,0 +1,118 @@
+/* virtio-pci.c - pci interface for virtio interface
+ *
+ * (c) Copyright 2008 Bull S.A.S.
+ *
+ *  Author: Laurent Vivier <Laurent.Vivier at bull.net>
+ *
+ * some parts from Linux Virtio PCI driver
+ *
+ *  Copyright IBM Corp. 2007
+ *  Authors: Anthony Liguori  <aliguori at us.ibm.com>
+ *
+ *  Adopted for Seabios: Gleb Natapov <gleb at redhat.com>
+ *
+ *  Further adopted to FILO: Himanshu Chauhan <hschauhan at nulltrace.org>
+ *
+ * This work is licensed under the terms of the GNU LGPLv3
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <libpayload.h>
+#include <config.h>
+#include <arch/virtual.h>
+#include <arch/io.h>
+#ifdef CONFIG_LP_PCI
+#include <pci.h>
+#endif
+
+#include <virtio/virtio-ring.h>
+#include <virtio/virtio-pci.h>
+
+/*
+ * Base addresses specify locations in memory or I/O space.
+ * Decoded size can be determined by writing a value of
+ * 0xffffffff to the register, and reading it back.  Only
+ * 1 bits are decoded.
+ */
+#define PCI_BASE_ADDRESS_0      0x10    /* 32 bits */
+#define PCI_BASE_ADDRESS_1      0x14    /* 32 bits [htype 0,1 only] */
+#define PCI_BASE_ADDRESS_2      0x18    /* 32 bits [htype 0 only] */
+#define PCI_BASE_ADDRESS_3      0x1c    /* 32 bits */
+#define PCI_BASE_ADDRESS_4      0x20    /* 32 bits */
+#define PCI_BASE_ADDRESS_5      0x24    /* 32 bits */
+#define  PCI_BASE_ADDRESS_SPACE         0x01    /* 0 = memory, 1 = I/O */
+#define  PCI_BASE_ADDRESS_SPACE_IO      0x01
+#define  PCI_BASE_ADDRESS_SPACE_MEMORY  0x00
+#define  PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06
+#define  PCI_BASE_ADDRESS_MEM_TYPE_32   0x00    /* 32 bit address */
+#define  PCI_BASE_ADDRESS_MEM_TYPE_1M   0x02    /* Below 1M [obsolete] */
+#define  PCI_BASE_ADDRESS_MEM_TYPE_64   0x04    /* 64 bit address */
+#define  PCI_BASE_ADDRESS_MEM_PREFETCH  0x08    /* prefetchable? */
+#define  PCI_BASE_ADDRESS_MEM_MASK      (~0x0fUL)
+#define  PCI_BASE_ADDRESS_IO_MASK       (~0x03UL)
+
+int vp_find_vq(unsigned int ioaddr, int queue_index,
+               struct vring_virtqueue **p_vq)
+{
+	uint16_t num;
+
+	struct vring_virtqueue *vq = *p_vq = malloc(sizeof(*vq));
+	if (!vq) {
+		goto fail;
+	}
+	memset(vq, 0, sizeof(*vq));
+
+	/* select the queue */
+	outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_SEL);
+
+	/* check if the queue is available */
+	num = inw(ioaddr + VIRTIO_PCI_QUEUE_NUM);
+	if (!num) {
+		printf("ERROR: queue size is 0\n");
+		goto fail;
+	}
+
+	if (num > MAX_QUEUE_NUM) {
+		printf("ERROR: queue size %d > %d\n", num, MAX_QUEUE_NUM);
+		goto fail;
+	}
+
+	/* check if the queue is already active */
+	if (inl(ioaddr + VIRTIO_PCI_QUEUE_PFN)) {
+		printf("ERROR: queue already active\n");
+		goto fail;
+	}
+
+	vq->queue_index = queue_index;
+
+	/* initialize the queue */
+
+	struct vring * vr = &vq->vring;
+	vring_init(vr, num, (unsigned char*)&vq->queue);
+
+	/* activate the queue
+	 *
+	 * NOTE: vr->desc is initialized by vring_init()
+	 */
+
+	outl((unsigned long)virt_to_phys(vr->desc) >> PAGE_SHIFT,
+	     ioaddr + VIRTIO_PCI_QUEUE_PFN);
+
+	return num;
+
+fail:
+	free(vq);
+	*p_vq = NULL;
+	return -1;
+}
+
+uint16_t vp_init_simple(uint16_t bdf)
+{
+	uint16_t ioaddr = pci_read_config32((0x80000000 | bdf), PCI_BASE_ADDRESS_0) &
+		PCI_BASE_ADDRESS_IO_MASK;
+
+	vp_reset(ioaddr);
+	vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE |
+		      VIRTIO_CONFIG_S_DRIVER );
+	return ioaddr;
+}
diff --git a/payloads/libpayload/drivers/virtio/virtio-ring.c b/payloads/libpayload/drivers/virtio/virtio-ring.c
new file mode 100644
index 0000000..debe0da
--- /dev/null
+++ b/payloads/libpayload/drivers/virtio/virtio-ring.c
@@ -0,0 +1,158 @@
+/* virtio-pci.c - virtio ring management
+ *
+ * (c) Copyright 2008 Bull S.A.S.
+ *
+ *  Author: Laurent Vivier <Laurent.Vivier at bull.net>
+ *
+ *  some parts from Linux Virtio Ring
+ *
+ *  Copyright Rusty Russell IBM Corporation 2007
+ *
+ *  Adopted for Seabios: Gleb Natapov <gleb at redhat.com>
+ *
+ *  Further adopted to FILO: Himanshu Chauhan <hschauhan at nulltrace.org>
+ *
+ * This work is licensed under the terms of the GNU LGPLv3
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <libpayload.h>
+#include <config.h>
+#include <arch/virtual.h>
+#include <arch/io.h>
+
+#include <virtio/virtio-ring.h>
+
+#if defined(CONFIG_LP_VIRTIO_PCI)
+#include <virtio/virtio-pci.h>
+#endif
+
+#define barrier() asm volatile("":::"memory");
+
+#define BUG() do {                                                      \
+		printf("BUG: failure at %d/%s()!\n", __LINE__, __func__); \
+		while(1);						\
+        } while (0)
+#define BUG_ON(condition) do { if (condition) BUG(); } while (0)
+
+/*
+ * vring_more_used
+ *
+ * is there some used buffers ?
+ *
+ */
+
+int vring_more_used(struct vring_virtqueue *vq)
+{
+	struct vring_used *used = vq->vring.used;
+	int more = vq->last_used_idx != used->idx;
+	/* Make sure ring reads are done after idx read above. */
+	smp_rmb();
+	return more;
+}
+
+/*
+ * vring_free
+ *
+ * put at the begin of the free list the current desc[head]
+ */
+
+void vring_detach(struct vring_virtqueue *vq, unsigned int head)
+{
+	struct vring *vr = &vq->vring;
+	struct vring_desc *desc = vr->desc;
+	unsigned int i;
+
+	/* find end of given descriptor */
+
+	i = head;
+	while (desc[i].flags & VRING_DESC_F_NEXT)
+		i = desc[i].next;
+
+	/* link it with free list and point to it */
+
+	desc[i].next = vq->free_head;
+	vq->free_head = head;
+}
+
+/*
+ * vring_get_buf
+ *
+ * get a buffer from the used list
+ *
+ */
+
+int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len)
+{
+	struct vring *vr = &vq->vring;
+	struct vring_used_elem *elem;
+	struct vring_used *used = vq->vring.used;
+	uint32_t id;
+	int ret;
+
+	BUG_ON(!vring_more_used(vq));
+
+	elem = &used->ring[vq->last_used_idx % vr->num];
+	id = elem->id;
+	if (len != NULL)
+		*len = elem->len;
+
+	ret = vq->vdata[id];
+
+	vring_detach(vq, id);
+
+	vq->last_used_idx = vq->last_used_idx + 1;
+
+	return ret;
+}
+
+void vring_add_buf(struct vring_virtqueue *vq,
+                   struct vring_list list[],
+                   unsigned int out, unsigned int in,
+                   int index, int num_added)
+{
+	struct vring *vr = &vq->vring;
+	int i, av, head, prev; //, j;
+	struct vring_desc *desc = vr->desc;
+	struct vring_avail *avail = vr->avail;
+
+	BUG_ON(out + in == 0);
+
+	prev = 0;
+	head = vq->free_head;
+	for (i = head; out; i = desc[i].next, out--) {
+		desc[i].flags = VRING_DESC_F_NEXT;
+		desc[i].addr = (uint64_t)virt_to_phys(list->addr);
+		desc[i].len = list->length;
+		prev = i;
+		list++;
+	}
+	for ( ; in; i = desc[i].next, in--) {
+		desc[i].flags = (VRING_DESC_F_NEXT|VRING_DESC_F_WRITE);
+		desc[i].addr = (u64)virt_to_phys(list->addr);
+		desc[i].len = list->length;
+		prev = i;
+		list++;
+	}
+	desc[prev].flags =
+		(desc[prev].flags & ~VRING_DESC_F_NEXT);
+
+	vq->free_head = i;
+
+	vq->vdata[head] = index;
+
+	av = (avail->idx + num_added) % (vr->num);
+	avail->ring[av] = head;
+}
+
+void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added)
+{
+	struct vring *vr = &vq->vring;
+	struct vring_avail *avail = vr->avail;
+
+	/* Make sure idx update is done after ring write. */
+	smp_wmb();
+	avail->idx = (avail->idx + num_added);
+
+	vp_notify(ioaddr, vq->queue_index);
+}
diff --git a/payloads/libpayload/include/storage/virtio-blk.h b/payloads/libpayload/include/storage/virtio-blk.h
new file mode 100644
index 0000000..12abc7d
--- /dev/null
+++ b/payloads/libpayload/include/storage/virtio-blk.h
@@ -0,0 +1,46 @@
+#ifndef _VIRTIO_BLK_H
+#define _VIRTIO_BLK_H
+
+struct virtio_blk_config
+{
+    u64 capacity;
+    u32 size_max;
+    u32 seg_max;
+    u16 cylinders;
+    u8 heads;
+    u8 sectors;
+    u32 blk_size;
+    u8 physical_block_exp;
+    u8 alignment_offset;
+    u16 min_io_size;
+    u32 opt_io_size;
+} __attribute__((packed));
+
+#define VIRTIO_BLK_F_BLK_SIZE 6
+
+/* These two define direction. */
+#define VIRTIO_BLK_T_IN         0
+#define VIRTIO_BLK_T_OUT        1
+
+/* This is the first element of the read scatter-gather list. */
+struct virtio_blk_outhdr {
+    /* VIRTIO_BLK_T* */
+    u32 type;
+    /* io priority. */
+    u32 ioprio;
+    /* Sector (ie. 512 byte offset) */
+    u64 sector;
+};
+
+#define VIRTIO_BLK_S_OK         0
+#define VIRTIO_BLK_S_IOERR      1
+#define VIRTIO_BLK_S_UNSUPP     2
+
+#define DTYPE_VIRTIO_BLK	0x09
+#define DISK_SECTOR_SIZE	512
+
+struct disk_op_s;
+int virtio_blk_read(int drive, u64 sector, void *buffer);
+int virtio_disk_probe(int drive);
+
+#endif /* _VIRTIO_BLK_H */
diff --git a/payloads/libpayload/include/virtio/virtio-pci.h b/payloads/libpayload/include/virtio/virtio-pci.h
new file mode 100644
index 0000000..19966b4
--- /dev/null
+++ b/payloads/libpayload/include/virtio/virtio-pci.h
@@ -0,0 +1,110 @@
+#ifndef _VIRTIO_PCI_H
+#define _VIRTIO_PCI_H
+
+#include <arch/types.h>
+#include <arch/virtual.h>
+#include <arch/io.h>
+
+/* 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
+
+/* The bit of the ISR which indicates a device configuration change. */
+#define VIRTIO_PCI_ISR_CONFIG           0x2
+
+/* The remaining space is defined by each driver as the per-driver
+ * configuration space */
+#define VIRTIO_PCI_CONFIG               20
+
+/* Virtio ABI version, this must match exactly */
+#define VIRTIO_PCI_ABI_VERSION          0
+
+#define PCI_VENDOR_ID_REDHAT_QUMRANET   0x1af4
+#define PCI_DEVICE_ID_VIRTIO_BLK        0x1001
+
+static inline uint32_t vp_get_features(unsigned int ioaddr)
+{
+	return inl(ioaddr + VIRTIO_PCI_HOST_FEATURES);
+}
+
+static inline void vp_set_features(unsigned int ioaddr, uint32_t features)
+{
+        outl(features, ioaddr + VIRTIO_PCI_GUEST_FEATURES);
+}
+
+static inline void vp_get(unsigned int ioaddr, unsigned offset,
+			  void *buf, unsigned len)
+{
+	uint8_t *ptr = buf;
+	unsigned i;
+
+	for (i = 0; i < len; i++)
+		ptr[i] = inb(ioaddr + VIRTIO_PCI_CONFIG + offset + i);
+}
+
+static inline uint8_t vp_get_status(unsigned int ioaddr)
+{
+	return inb(ioaddr + VIRTIO_PCI_STATUS);
+}
+
+static inline void vp_set_status(unsigned int ioaddr, uint8_t status)
+{
+	if (status == 0)        /* reset */
+		return;
+	outb(status, ioaddr + VIRTIO_PCI_STATUS);
+}
+
+static inline uint8_t vp_get_isr(unsigned int ioaddr)
+{
+	return inb(ioaddr + VIRTIO_PCI_ISR);
+}
+
+static inline void vp_reset(unsigned int ioaddr)
+{
+	outb(0, ioaddr + VIRTIO_PCI_STATUS);
+	(void)inb(ioaddr + VIRTIO_PCI_ISR);
+}
+
+static inline void vp_notify(unsigned int ioaddr, int queue_index)
+{
+	outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_NOTIFY);
+}
+
+static inline void vp_del_vq(unsigned int ioaddr, int queue_index)
+{
+	/* select the queue */
+
+	outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_SEL);
+
+	/* deactivate the queue */
+
+	outl(0, ioaddr + VIRTIO_PCI_QUEUE_PFN);
+}
+
+struct vring_virtqueue;
+uint16_t vp_init_simple(uint16_t bdf);
+int vp_find_vq(unsigned int ioaddr, int queue_index,
+               struct vring_virtqueue **p_vq);
+#endif /* _VIRTIO_PCI_H_ */
diff --git a/payloads/libpayload/include/virtio/virtio-ring.h b/payloads/libpayload/include/virtio/virtio-ring.h
new file mode 100644
index 0000000..bef7926
--- /dev/null
+++ b/payloads/libpayload/include/virtio/virtio-ring.h
@@ -0,0 +1,130 @@
+#ifndef _VIRTIO_RING_H
+#define _VIRTIO_RING_H
+
+#include <arch/types.h>
+#include <arch/virtual.h>
+#include <arch/io.h>
+
+#define PAGE_SIZE	4096
+#define PAGE_SHIFT 12
+#define PAGE_MASK  (PAGE_SIZE-1)
+
+#define barrier()	asm volatile("":::"memory");
+
+/* Compiler barrier is enough as an x86 CPU does not reorder reads or writes */
+#define smp_rmb() barrier()
+#define smp_wmb() barrier()
+
+/* Status byte for guest to report progress, and synchronize features. */
+/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */
+#define VIRTIO_CONFIG_S_ACKNOWLEDGE     1
+/* We have found a driver for the device. */
+#define VIRTIO_CONFIG_S_DRIVER          2
+/* Driver has used its parts of the config, and is happy */
+#define VIRTIO_CONFIG_S_DRIVER_OK       4
+/* We've given up on this device. */
+#define VIRTIO_CONFIG_S_FAILED          0x80
+
+#define MAX_QUEUE_NUM      (128)
+
+#define VRING_DESC_F_NEXT  1
+#define VRING_DESC_F_WRITE 2
+
+#define VRING_AVAIL_F_NO_INTERRUPT 1
+
+#define VRING_USED_F_NO_NOTIFY     1
+
+struct vring_desc
+{
+	uint64_t addr;
+	uint32_t len;
+	uint16_t flags;
+	uint16_t next;
+};
+
+struct vring_avail
+{
+	uint16_t flags;
+	uint16_t idx;
+	uint16_t ring[0];
+};
+
+struct vring_used_elem
+{
+	uint32_t id;
+	uint32_t len;
+};
+
+struct vring_used
+{
+	uint16_t flags;
+	uint16_t idx;
+	struct vring_used_elem ring[];
+};
+
+struct vring {
+	unsigned int num;
+	struct vring_desc *desc;
+	struct vring_avail *avail;
+	struct vring_used *used;
+};
+
+#define vring_size(num)			   \
+	(((((sizeof(struct vring_desc) * num) +		\
+	    (sizeof(struct vring_avail) + sizeof(u16) * num))	\
+	   + PAGE_MASK) & ~PAGE_MASK) +					\
+         (sizeof(struct vring_used) + sizeof(struct vring_used_elem) * num))
+
+typedef unsigned char virtio_queue_t[vring_size(MAX_QUEUE_NUM)];
+
+struct vring_virtqueue {
+	struct vring vring;
+	uint16_t free_head;
+	uint16_t last_used_idx;
+	uint16_t vdata[MAX_QUEUE_NUM];
+	/* PCI */
+	int queue_index;
+	virtio_queue_t queue;
+};
+
+struct vring_list {
+	char *addr;
+	unsigned int length;
+};
+
+static inline void vring_init(struct vring *vr,
+			      unsigned int num, unsigned char *queue)
+{
+	unsigned int i;
+	unsigned long pa;
+
+	vr->num = num;
+
+	/* physical address of desc must be page aligned */
+	pa = virt_to_phys(queue);
+	pa = (pa + PAGE_MASK) & ~PAGE_MASK;
+	vr->desc = phys_to_virt(pa);
+
+	vr->avail = (struct vring_avail *)&vr->desc[num];
+	/* disable interrupts */
+	vr->avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
+
+	/* physical address of used must be page aligned */
+	pa = virt_to_phys(&vr->avail->ring[num]);
+	pa = (pa + PAGE_MASK) & ~PAGE_MASK;
+	vr->used = phys_to_virt(pa);
+
+	for (i = 0; i < num - 1; i++)
+		vr->desc[i].next = i + 1;
+	vr->desc[i].next = 0;
+}
+
+int vring_more_used(struct vring_virtqueue *vq);
+void vring_detach(struct vring_virtqueue *vq, unsigned int head);
+int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len);
+void vring_add_buf(struct vring_virtqueue *vq, struct vring_list list[],
+                   unsigned int out, unsigned int in,
+                   int index, int num_added);
+void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added);
+
+#endif /* _VIRTIO_RING_H_ */



More information about the coreboot-gerrit mailing list