aboutsummaryrefslogtreecommitdiff
path: root/lib/libhvcall
diff options
context:
space:
mode:
authorThomas Huth <thuth@linux.vnet.ibm.com>2011-11-24 11:32:35 +0100
committerThomas Huth <thuth@linux.vnet.ibm.com>2011-11-24 11:32:35 +0100
commit008fe5610d27796bbf6ae5ef74e14314c237c4c1 (patch)
tree3582b4ce1bc2228fd2e9ff1f5df729e52fd1a814 /lib/libhvcall
parent4737144db1acbb1374616bafa4e569aac139c5aa (diff)
downloadSLOF-008fe5610d27796bbf6ae5ef74e14314c237c4c1.zip
SLOF-008fe5610d27796bbf6ae5ef74e14314c237c4c1.tar.gz
SLOF-008fe5610d27796bbf6ae5ef74e14314c237c4c1.tar.bz2
Support for virtio-block PCI devices
The virtio devices are preferred way of providing virtualized devices on KVM/qemu. Here's now the basic support for virtio block devices. Signed-off-by: Thomas Huth <thuth@linux.vnet.ibm.com>
Diffstat (limited to 'lib/libhvcall')
-rw-r--r--lib/libhvcall/Makefile6
-rw-r--r--lib/libhvcall/hvcall.code43
-rw-r--r--lib/libhvcall/hvcall.in7
-rw-r--r--lib/libhvcall/virtio-blk.c160
-rw-r--r--lib/libhvcall/virtio-blk.h60
-rw-r--r--lib/libhvcall/virtio.c186
-rw-r--r--lib/libhvcall/virtio.h84
7 files changed, 544 insertions, 2 deletions
diff --git a/lib/libhvcall/Makefile b/lib/libhvcall/Makefile
index 41ee633..da30a07 100644
--- a/lib/libhvcall/Makefile
+++ b/lib/libhvcall/Makefile
@@ -13,7 +13,8 @@
TOPCMNDIR ?= ../..
ASFLAGS = $(FLAG) $(RELEASE) $(CPUARCHDEF) -Wa,-mregnames
-CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) -I. -I../../include
+CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \
+ -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH)
LDFLAGS = -nostdlib
TARGET = ../libhvcall.a
@@ -21,6 +22,7 @@ TARGET = ../libhvcall.a
all: $(TARGET)
+SRCS = virtio.c virtio-blk.c
SRCSS = hvcall.S
@@ -46,7 +48,7 @@ depend:
$(MAKE) Makefile.dep
Makefile.dep: Makefile
- $(CC) -MM $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep
+ $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep
# Include dependency file if available:
-include Makefile.dep
diff --git a/lib/libhvcall/hvcall.code b/lib/libhvcall/hvcall.code
index 05f9cba..2fb9b3a 100644
--- a/lib/libhvcall/hvcall.code
+++ b/lib/libhvcall/hvcall.code
@@ -9,7 +9,10 @@
* Contributors:
* IBM Corporation - initial implementation
*****************************************************************************/
+
#include <libhvcall.h>
+#include <virtio.h>
+#include <virtio-blk.h>
// : hv-putchar ( char -- )
PRIM(hv_X2d_putchar)
@@ -94,3 +97,43 @@ PRIM(RX_X21)
unsigned long val = TOS.u; POP;
hv_logical_ci_store(8, qaddr, val);
MIRP
+
+
+// : virtio-vring-size ( queuesize -- ringsize )
+PRIM(virtio_X2d_vring_X2d_size)
+ TOS.u = virtio_vring_size(TOS.u);
+MIRP
+
+// : virtio-get-qsize ( dev queue -- queuesize )
+PRIM(virtio_X2d_get_X2d_qsize)
+ int queue = TOS.u; POP;
+ TOS.u = virtio_get_qsize(TOS.a, queue);
+MIRP
+
+// : virtio-get-config ( dev offset size -- val )
+PRIM(virtio_X2d_get_X2d_config)
+ int size = TOS.u; POP;
+ int offset = TOS.u; POP;
+ TOS.u = virtio_get_config(TOS.a, offset, size);
+MIRP
+
+// : virtio-blk-init ( dev -- )
+PRIM(virtio_X2d_blk_X2d_init)
+ void *dev = TOS.a; POP;
+ virtioblk_init(dev);
+MIRP
+
+// : virtio-blk-shutdown ( dev -- )
+PRIM(virtio_X2d_blk_X2d_shutdown)
+ void *dev = TOS.a; POP;
+ virtioblk_shutdown(dev);
+MIRP
+
+// : virtio-blk-read ( dev blkno cnt reg -- #read )
+PRIM(virtio_X2d_blk_X2d_read)
+ void *dev = TOS.a; POP;
+ long cnt = TOS.n; POP;
+ long blkno = TOS.n; POP;
+ void *buf = TOS.a;
+ TOS.n = virtioblk_read(dev, buf, blkno, cnt);
+MIRP
diff --git a/lib/libhvcall/hvcall.in b/lib/libhvcall/hvcall.in
index abbb2c8..b28eedf 100644
--- a/lib/libhvcall/hvcall.in
+++ b/lib/libhvcall/hvcall.in
@@ -26,3 +26,10 @@ cod(RL@)
cod(RL!)
cod(RX@)
cod(RX!)
+
+cod(virtio-vring-size)
+cod(virtio-get-qsize)
+cod(virtio-get-config)
+cod(virtio-blk-init)
+cod(virtio-blk-shutdown)
+cod(virtio-blk-read)
diff --git a/lib/libhvcall/virtio-blk.c b/lib/libhvcall/virtio-blk.c
new file mode 100644
index 0000000..d4cdb04
--- /dev/null
+++ b/lib/libhvcall/virtio-blk.c
@@ -0,0 +1,160 @@
+/******************************************************************************
+ * Copyright (c) 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdio.h>
+#include "virtio.h"
+#include "virtio-blk.h"
+#include "libhvcall.h"
+
+
+#define sync() asm volatile (" sync \n" ::: "memory")
+
+
+/**
+ * Initialize virtio-block device.
+ * @param dev pointer to virtio device information
+ */
+int
+virtioblk_init(struct virtio_device *dev)
+{
+ struct vring_avail *vq_avail;
+
+ /* Reset device */
+ // virtio_reset_device(dev);
+
+ /* Acknowledge device. */
+ virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE);
+
+ /* Tell HV that we know how to drive the device. */
+ virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER);
+
+ /* Device specific setup - we do not support special features right now */
+ virtio_set_guest_features(dev, 0);
+
+ vq_avail = virtio_get_vring_avail(dev, 0);
+ vq_avail->flags = VRING_AVAIL_F_NO_INTERRUPT;
+ vq_avail->idx = 0;
+
+ /* Tell HV that setup succeeded */
+ virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER
+ |VIRTIO_STAT_DRIVER_OK);
+
+ return 0;
+}
+
+
+/**
+ * Shutdown the virtio-block device.
+ * @param dev pointer to virtio device information
+ */
+void
+virtioblk_shutdown(struct virtio_device *dev)
+{
+ /* Quiesce device */
+ virtio_set_status(dev, VIRTIO_STAT_FAILED);
+
+ /* Reset device */
+ virtio_reset_device(dev);
+}
+
+
+/**
+ * Read blocks
+ * @param reg pointer to "reg" property
+ * @param buf pointer to destination buffer
+ * @param blocknum block number of the first block that should be read
+ * @param cnt amount of blocks that should be read
+ * @return number of blocks that have been read successfully
+ */
+int
+virtioblk_read(struct virtio_device *dev, char *buf, long blocknum, long cnt)
+{
+ struct vring_desc *desc;
+ int id, i;
+ static struct virtio_blk_req blkhdr;
+ //struct virtio_blk_config *blkconf;
+ uint64_t capacity;
+ uint32_t vq_size;
+ struct vring_desc *vq_desc; /* Descriptor vring */
+ struct vring_avail *vq_avail; /* "Available" vring */
+ struct vring_used *vq_used; /* "Used" vring */
+ volatile uint8_t status = -1;
+ volatile uint16_t *current_used_idx;
+ uint16_t last_used_idx;
+
+ //printf("virtioblk_read: dev=%p buf=%p blocknum=%li count=%li\n",
+ // dev, buf, blocknum, cnt);
+
+ /* Check whether request is within disk capacity */
+ capacity = virtio_get_config(dev, 0, sizeof(capacity));
+ if (blocknum + cnt - 1 > capacity) {
+ puts("virtioblk_read: Access beyond end of device!");
+ return 0;
+ }
+
+ vq_size = virtio_get_qsize(dev, 0);
+ vq_desc = virtio_get_vring_desc(dev, 0);
+ vq_avail = virtio_get_vring_avail(dev, 0);
+ vq_used = virtio_get_vring_used(dev, 0);
+
+ last_used_idx = vq_used->idx;
+ current_used_idx = &vq_used->idx;
+
+ /* Set up header */
+ blkhdr.type = VIRTIO_BLK_T_IN | VIRTIO_BLK_T_BARRIER;
+ blkhdr.ioprio = 1;
+ blkhdr.sector = blocknum;
+
+ /* Determine descriptor index */
+ id = (vq_avail->idx * 3) % vq_size;
+
+ /* Set up virtqueue descriptor for header */
+ desc = &vq_desc[id];
+ desc->addr = (uint64_t)&blkhdr;
+ desc->len = sizeof(struct virtio_blk_req);
+ desc->flags = VRING_DESC_F_NEXT;
+ desc->next = (id + 1) % vq_size;
+
+ /* Set up virtqueue descriptor for data */
+ desc = &vq_desc[(id + 1) % vq_size];
+ desc->addr = (uint64_t)buf;
+ desc->len = cnt * 512;
+ desc->flags = VRING_DESC_F_NEXT | VRING_DESC_F_WRITE;
+ desc->next = (id + 2) % vq_size;
+
+ /* Set up virtqueue descriptor for status */
+ desc = &vq_desc[(id + 2) % vq_size];
+ desc->addr = (uint64_t)&status;
+ desc->len = 1;
+ desc->flags = VRING_DESC_F_WRITE;
+ desc->next = 0;
+
+ vq_avail->ring[vq_avail->idx % vq_size] = id;
+ sync();
+ vq_avail->idx += 1;
+
+ /* Tell HV that the queue is ready */
+ virtio_queue_notify(dev, 0);
+
+ /* Wait for host to consume the descriptor */
+ i = 10000000;
+ while (*current_used_idx == last_used_idx && i-- > 0) {
+ sync();
+ }
+
+ if (status == 0)
+ return cnt;
+
+ printf("virtioblk_read failed! status = %i\n", status);
+
+ return 0;
+}
diff --git a/lib/libhvcall/virtio-blk.h b/lib/libhvcall/virtio-blk.h
new file mode 100644
index 0000000..7c2b7e0
--- /dev/null
+++ b/lib/libhvcall/virtio-blk.h
@@ -0,0 +1,60 @@
+/******************************************************************************
+ * Copyright (c) 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * Virtio block device definitions.
+ * See Virtio Spec, Appendix D, for details
+ */
+
+#ifndef _VIRTIO_BLK_H
+#define _VIRTIO_BLK_H
+
+#include <stdint.h>
+
+
+/* Device configuration layout */
+/*
+struct virtio_blk_config {
+ uint64_t capacity;
+ uint32_t size_max;
+ uint32_t seg_max;
+ struct virtio_blk_geometry {
+ uint16_t cylinders;
+ uint8_t heads;
+ uint8_t sectors;
+ } geometry;
+ uint32_t blk_size;
+ uint32_t sectors_max;
+};
+*/
+
+/* Block request */
+struct virtio_blk_req {
+ uint32_t type ;
+ uint32_t ioprio ;
+ uint64_t sector ;
+};
+
+/* Block request types */
+#define VIRTIO_BLK_T_IN 0
+#define VIRTIO_BLK_T_OUT 1
+#define VIRTIO_BLK_T_SCSI_CMD 2
+#define VIRTIO_BLK_T_SCSI_CMD_OUT 3
+#define VIRTIO_BLK_T_FLUSH 4
+#define VIRTIO_BLK_T_FLUSH_OUT 5
+#define VIRTIO_BLK_T_BARRIER 0x80000000
+
+extern int virtioblk_init(struct virtio_device *dev);
+extern void virtioblk_shutdown(struct virtio_device *dev);
+extern int virtioblk_read(struct virtio_device *dev, char *buf, long blocknum, long cnt);
+
+#endif /* _VIRTIO_BLK_H */
diff --git a/lib/libhvcall/virtio.c b/lib/libhvcall/virtio.c
new file mode 100644
index 0000000..91d60d2
--- /dev/null
+++ b/lib/libhvcall/virtio.c
@@ -0,0 +1,186 @@
+/******************************************************************************
+ * Copyright (c) 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <cpu.h>
+#include <cache.h>
+#include <byteorder.h>
+#include "virtio.h"
+#include "libhvcall.h"
+
+/* PCI virtio header offsets */
+#define VIRTIOHDR_DEVICE_FEATURES 0
+#define VIRTIOHDR_GUEST_FEATURES 4
+#define VIRTIOHDR_QUEUE_ADDRESS 8
+#define VIRTIOHDR_QUEUE_SIZE 12
+#define VIRTIOHDR_QUEUE_SELECT 14
+#define VIRTIOHDR_QUEUE_NOTIFY 16
+#define VIRTIOHDR_DEVICE_STATUS 18
+#define VIRTIOHDR_ISR_STATUS 19
+#define VIRTIOHDR_DEVICE_CONFIG 20
+
+
+/**
+ * Calculate ring size according to queue size number
+ */
+unsigned long virtio_vring_size(unsigned int qsize)
+{
+ return VQ_ALIGN(sizeof(struct vring_desc) * qsize + 2 * (2 + qsize))
+ + VQ_ALIGN(sizeof(struct vring_used_elem) * qsize);
+}
+
+
+/**
+ * Get number of elements in a vring
+ * @param dev pointer to virtio device information
+ * @param queue virtio queue number
+ * @return number of elements
+ */
+int virtio_get_qsize(struct virtio_device *dev, int queue)
+{
+ int size = 0;
+
+ if (dev->type == VIRTIO_TYPE_PCI) {
+ ci_write_16(dev->base+VIRTIOHDR_QUEUE_SELECT, cpu_to_le16(queue));
+ eieio();
+ size = le16_to_cpu(ci_read_16(dev->base+VIRTIOHDR_QUEUE_SIZE));
+ }
+
+ return size;
+}
+
+
+/**
+ * Get address of descriptor vring
+ * @param dev pointer to virtio device information
+ * @param queue virtio queue number
+ * @return pointer to the descriptor ring
+ */
+struct vring_desc *virtio_get_vring_desc(struct virtio_device *dev, int queue)
+{
+ struct vring_desc *desc = 0;
+
+ if (dev->type == VIRTIO_TYPE_PCI) {
+ ci_write_16(dev->base+VIRTIOHDR_QUEUE_SELECT, cpu_to_le16(queue));
+ eieio();
+ desc = (void*)(4096L *
+ le32_to_cpu(ci_read_32(dev->base+VIRTIOHDR_QUEUE_ADDRESS)));
+ }
+
+ return desc;
+}
+
+
+/**
+ * Get address of "available" vring
+ * @param dev pointer to virtio device information
+ * @param queue virtio queue number
+ * @return pointer to the "available" ring
+ */
+struct vring_avail *virtio_get_vring_avail(struct virtio_device *dev, int queue)
+{
+ return (void*)((uint64_t)virtio_get_vring_desc(dev, queue)
+ + virtio_get_qsize(dev, queue) * sizeof(struct vring_desc));
+}
+
+
+/**
+ * Get address of "used" vring
+ * @param dev pointer to virtio device information
+ * @param queue virtio queue number
+ * @return pointer to the "used" ring
+ */
+struct vring_used *virtio_get_vring_used(struct virtio_device *dev, int queue)
+{
+ return (void*)VQ_ALIGN((uint64_t)virtio_get_vring_avail(dev, queue)
+ + virtio_get_qsize(dev, queue)
+ * sizeof(struct vring_avail));
+}
+
+
+/**
+ * Reset virtio device
+ */
+void virtio_reset_device(struct virtio_device *dev)
+{
+ if (dev->type == VIRTIO_TYPE_PCI) {
+ ci_write_8(dev->base+VIRTIOHDR_DEVICE_STATUS, 0);
+ }
+}
+
+
+/**
+ * Notify hypervisor about queue update
+ */
+void virtio_queue_notify(struct virtio_device *dev, int queue)
+{
+ if (dev->type == VIRTIO_TYPE_PCI) {
+ ci_write_16(dev->base+VIRTIOHDR_QUEUE_NOTIFY, cpu_to_le16(queue));
+ }
+}
+
+
+/**
+ * Set device status bits
+ */
+void virtio_set_status(struct virtio_device *dev, int status)
+{
+ if (dev->type == VIRTIO_TYPE_PCI) {
+ ci_write_8(dev->base+VIRTIOHDR_DEVICE_STATUS, status);
+ }
+}
+
+
+/**
+ * Set guest feature bits
+ */
+void virtio_set_guest_features(struct virtio_device *dev, int features)
+
+{
+ if (dev->type == VIRTIO_TYPE_PCI) {
+ ci_write_32(dev->base+VIRTIOHDR_GUEST_FEATURES, features);
+ }
+}
+
+
+/**
+ * Get additional config values
+ */
+uint64_t virtio_get_config(struct virtio_device *dev, int offset, int size)
+{
+ uint64_t val = ~0ULL;
+ void *confbase;
+
+ switch (dev->type) {
+ case VIRTIO_TYPE_PCI:
+ confbase = dev->base+VIRTIOHDR_DEVICE_CONFIG;
+ break;
+ default:
+ return ~0ULL;
+ }
+
+ switch (size) {
+ case 1:
+ val = ci_read_8(confbase+offset);
+ break;
+ case 2:
+ val = ci_read_16(confbase+offset);
+ break;
+ case 4:
+ val = ci_read_32(confbase+offset);
+ break;
+ case 8:
+ val = ci_read_64(confbase+offset);
+ break;
+ }
+
+ return val;
+}
diff --git a/lib/libhvcall/virtio.h b/lib/libhvcall/virtio.h
new file mode 100644
index 0000000..7355043
--- /dev/null
+++ b/lib/libhvcall/virtio.h
@@ -0,0 +1,84 @@
+/******************************************************************************
+ * Copyright (c) 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _LIBVIRTIO_H
+#define _LIBVIRTIO_H
+
+#include <stdint.h>
+
+/* Device status bits */
+#define VIRTIO_STAT_ACKNOWLEDGE 1
+#define VIRTIO_STAT_DRIVER 2
+#define VIRTIO_STAT_DRIVER_OK 4
+#define VIRTIO_STAT_FAILED 128
+
+/* Definitions for vring_desc.flags */
+#define VRING_DESC_F_NEXT 1 /* buffer continues via the next field */
+#define VRING_DESC_F_WRITE 2 /* buffer is write-only (otherwise read-only) */
+#define VRING_DESC_F_INDIRECT 4 /* buffer contains a list of buffer descriptors */
+
+/* Descriptor table entry - see Virtio Spec chapter 2.3.2 */
+struct vring_desc {
+ uint64_t addr; /* Address (guest-physical) */
+ uint32_t len; /* Length */
+ uint16_t flags; /* The flags as indicated above */
+ uint16_t next; /* Next field if flags & NEXT */
+};
+
+/* Definitions for vring_avail.flags */
+#define VRING_AVAIL_F_NO_INTERRUPT 1
+
+/* Available ring - see Virtio Spec chapter 2.3.4 */
+struct vring_avail {
+ uint16_t flags;
+ uint16_t idx;
+ uint16_t ring[];
+};
+
+
+/* Definitions for vring_used.flags */
+#define VRING_USED_F_NO_NOTIFY 1
+
+struct vring_used_elem {
+ uint32_t id; /* Index of start of used descriptor chain */
+ uint32_t len; /* Total length of the descriptor chain which was used */
+};
+
+struct vring_used {
+ uint16_t flags;
+ uint16_t idx;
+ struct vring_used_elem ring[];
+};
+
+#define VIRTIO_TYPE_PCI 0 /* For virtio-pci interface */
+struct virtio_device {
+ void *base; /* base address */
+ int type; /* VIRTIO_TYPE_PCI or VIRTIO_TYPE_VIO */
+};
+
+/* Parts of the virtqueue are aligned on a 4096 byte page boundary */
+#define VQ_ALIGN(addr) (((addr) + 0xfff) & ~0xfff)
+
+extern unsigned long virtio_vring_size(unsigned int qsize);
+extern int virtio_get_qsize(struct virtio_device *dev, int queue);
+extern struct vring_desc *virtio_get_vring_desc(struct virtio_device *dev, int queue);
+extern struct vring_avail *virtio_get_vring_avail(struct virtio_device *dev, int queue);
+extern struct vring_used *virtio_get_vring_used(struct virtio_device *dev, int queue);
+
+extern void virtio_reset_device(struct virtio_device *dev);
+extern void virtio_queue_notify(struct virtio_device *dev, int queue);
+extern void virtio_set_status(struct virtio_device *dev, int status);
+extern void virtio_set_guest_features(struct virtio_device *dev, int features);
+extern uint64_t virtio_get_config(struct virtio_device *dev, int offset, int size);
+
+
+#endif /* _LIBVIRTIO_H */