diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2012-01-13 12:07:42 +1100 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2012-01-19 17:17:50 +1100 |
commit | ca610c931e2b0004c44f748cb813580b09c60489 (patch) | |
tree | cf17ebaa02078827068b534bb95d4540bd848678 /lib/libvirtio | |
parent | adde61dfebe5ba4c519b120e8b33f1f8a3c93151 (diff) | |
download | SLOF-ca610c931e2b0004c44f748cb813580b09c60489.zip SLOF-ca610c931e2b0004c44f748cb813580b09c60489.tar.gz SLOF-ca610c931e2b0004c44f748cb813580b09c60489.tar.bz2 |
Move virtio to a separate library
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'lib/libvirtio')
-rw-r--r-- | lib/libvirtio/Makefile | 53 | ||||
-rw-r--r-- | lib/libvirtio/virtio-blk.c | 158 | ||||
-rw-r--r-- | lib/libvirtio/virtio-blk.h | 60 | ||||
-rw-r--r-- | lib/libvirtio/virtio.c | 191 | ||||
-rw-r--r-- | lib/libvirtio/virtio.code | 53 | ||||
-rw-r--r-- | lib/libvirtio/virtio.h | 84 | ||||
-rw-r--r-- | lib/libvirtio/virtio.in | 18 |
7 files changed, 617 insertions, 0 deletions
diff --git a/lib/libvirtio/Makefile b/lib/libvirtio/Makefile new file mode 100644 index 0000000..e4fb34e --- /dev/null +++ b/lib/libvirtio/Makefile @@ -0,0 +1,53 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 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 +# ****************************************************************************/ + +TOPCMNDIR ?= ../.. + +ASFLAGS = $(FLAG) $(RELEASE) $(CPUARCHDEF) -Wa,-mregnames +CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \ + -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH) +LDFLAGS = -nostdlib + +TARGET = ../libvirtio.a + + +all: $(TARGET) + +SRCS = virtio.c virtio-blk.c + +OBJS = $(SRCS:%.c=%.o) + +$(TARGET): $(OBJS) + $(AR) -rc $@ $(OBJS) + $(RANLIB) $@ + +%.o: %.S + $(CC) $(CPPFLAGS) $(ASFLAGS) -c $< -o $@ + +clean: + $(RM) $(TARGET) $(OBJS) + +distclean: clean + $(RM) Makefile.dep + + +# Rules for creating the dependency file: +depend: + $(RM) Makefile.dep + $(MAKE) Makefile.dep + +Makefile.dep: Makefile + $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep + +# Include dependency file if available: +-include Makefile.dep + diff --git a/lib/libvirtio/virtio-blk.c b/lib/libvirtio/virtio-blk.c new file mode 100644 index 0000000..7b1e795 --- /dev/null +++ b/lib/libvirtio/virtio-blk.c @@ -0,0 +1,158 @@ +/****************************************************************************** + * 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" + +#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/libvirtio/virtio-blk.h b/lib/libvirtio/virtio-blk.h new file mode 100644 index 0000000..7c2b7e0 --- /dev/null +++ b/lib/libvirtio/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/libvirtio/virtio.c b/lib/libvirtio/virtio.c new file mode 100644 index 0000000..4e70053 --- /dev/null +++ b/lib/libvirtio/virtio.c @@ -0,0 +1,191 @@ +/****************************************************************************** + * 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" + +/* 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: + /* We don't support 8 bytes PIO accesses + * in qemu and this is all PIO + */ + val = ci_read_32(confbase+offset); + val <<= 32; + val |= ci_read_32(confbase+offset+4); + break; + } + + return val; +} diff --git a/lib/libvirtio/virtio.code b/lib/libvirtio/virtio.code new file mode 100644 index 0000000..0ed3dc7 --- /dev/null +++ b/lib/libvirtio/virtio.code @@ -0,0 +1,53 @@ +/****************************************************************************** + * Copyright (c) 2004, 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 <virtio.h> +#include <virtio-blk.h> + +// : 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/libvirtio/virtio.h b/lib/libvirtio/virtio.h new file mode 100644 index 0000000..7355043 --- /dev/null +++ b/lib/libvirtio/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 */ diff --git a/lib/libvirtio/virtio.in b/lib/libvirtio/virtio.in new file mode 100644 index 0000000..1f2cfd1 --- /dev/null +++ b/lib/libvirtio/virtio.in @@ -0,0 +1,18 @@ +/****************************************************************************** + * Copyright (c) 2004, 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 + *****************************************************************************/ + +cod(virtio-vring-size) +cod(virtio-get-qsize) +cod(virtio-get-config) +cod(virtio-blk-init) +cod(virtio-blk-shutdown) +cod(virtio-blk-read) |