Coreboot+Filo can be used in virtualized environments where the Virtio support is needed sometimes.
Signed-off-by: Himanshu Chauhan hschauhan@nulltrace.org --- Config.in | 13 +++ drivers/Makefile.inc | 2 + drivers/virtio-blk.c | 266 ++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/virtio-blk.h | 46 +++++++++ drivers/virtio-pci.c | 125 ++++++++++++++++++++++++ drivers/virtio-pci.h | 110 +++++++++++++++++++++ drivers/virtio-ring.c | 158 ++++++++++++++++++++++++++++++ drivers/virtio-ring.h | 130 ++++++++++++++++++++++++ fs/blockdev.c | 30 +++++- include/fs.h | 6 ++ 10 files changed, 885 insertions(+), 1 deletion(-) create mode 100644 drivers/virtio-blk.c create mode 100644 drivers/virtio-blk.h create mode 100644 drivers/virtio-pci.c create mode 100644 drivers/virtio-pci.h create mode 100644 drivers/virtio-ring.c create mode 100644 drivers/virtio-ring.h
diff --git a/Config.in b/Config.in index cc48bd3..6f13b96 100644 --- a/Config.in +++ b/Config.in @@ -143,6 +143,19 @@ config IDE_NEW_DISK help Jens Axboe's fine IDE driver
+config SUPPORT_VIRTIO + bool "Support Virtio Drivers" + default n + help + Enable the support for Virtio devices. + +config VIRTIO_DISK + bool "Support virtio block device" + default n + depends on SUPPORT_VIRTIO + help + Enable support for virtio block devices + config LIBPAYLOAD_STORAGE bool "Use libpayload's storage drivers" default n diff --git a/drivers/Makefile.inc b/drivers/Makefile.inc index 941c151..a407baf 100644 --- a/drivers/Makefile.inc +++ b/drivers/Makefile.inc @@ -24,3 +24,5 @@ TARGETS-$(CONFIG_VIA_SOUND) += drivers/via-sound.o TARGETS-$(CONFIG_USB_DISK) += drivers/usb.o TARGETS-$(CONFIG_TARGET_I386) += drivers/intel.o TARGETS-$(CONFIG_TARGET_I386) += drivers/amd.o drivers/sb600.o +TARGETS-$(CONFIG_SUPPORT_VIRTIO) += drivers/virtio-pci.o drivers/virtio-ring.o +TARGETS-$(CONFIG_VIRTIO_DISK) += drivers/virtio-blk.o \ No newline at end of file diff --git a/drivers/virtio-blk.c b/drivers/virtio-blk.c new file mode 100644 index 0000000..f31758d --- /dev/null +++ b/drivers/virtio-blk.c @@ -0,0 +1,266 @@ +// Virtio block boot support. +// +// Copyright (C) 2010 Red Hat Inc. +// Copyright (c) 2014 Himanshu chauhan hschauhan@nulltrace.org +// +// Authors: +// Gleb Natapov gnatapov@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 <lib.h> +#include <fs.h> +#include <arch/timer.h> +#include <arch/virtual.h> +#include <arch/io.h> +#ifdef CONFIG_SUPPORT_PCI +#include <pci.h> +#endif + +#define DEBUG_THIS CONFIG_DEBUG_VIRTIO_PCI +#include <debug.h> + +#include "virtio-pci.h" +#include "virtio-ring.h" +#include "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; + sector_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; + pcidev_t bdf; + 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, sector_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, sector_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 */ +/**/ +static int init_virtio_blk(struct virtiodrive_s *vdisk) +{ + u16 bdf = vdisk->bdf; + debug("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; +} + +#ifdef CONFIG_SUPPORT_PCI +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; +} + +int pci_find_virtio_disks(int bus) +{ + debug(" Scanning for Virtio Disks on Bus %d\n", 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)) { + debug("PCI Virtio Disks not found\n"); + return -1; + } + + return 0; +} +#else /* !CONFIG_SUPPORT_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) { + 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/drivers/virtio-blk.h b/drivers/virtio-blk.h new file mode 100644 index 0000000..626c4c0 --- /dev/null +++ b/drivers/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 process_virtio_blk_op(struct disk_op_s *op); +void virtio_blk_setup(void); + +#endif /* _VIRTIO_BLK_H */ diff --git a/drivers/virtio-pci.c b/drivers/virtio-pci.c new file mode 100644 index 0000000..e61c959 --- /dev/null +++ b/drivers/virtio-pci.c @@ -0,0 +1,125 @@ +/* virtio-pci.c - pci interface for virtio interface + * + * (c) Copyright 2008 Bull S.A.S. + * + * Author: Laurent Vivier Laurent.Vivier@bull.net + * + * some parts from Linux Virtio PCI driver + * + * Copyright IBM Corp. 2007 + * Authors: Anthony Liguori aliguori@us.ibm.com + * + * Adopted for Seabios: Gleb Natapov gleb@redhat.com + * + * Further adopted to FILO: Himanshu Chauhan hschauhan@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 <lib.h> +#include <fs.h> +#include <arch/timer.h> +#include <arch/virtual.h> +#include <arch/io.h> +#ifdef CONFIG_SUPPORT_PCI +#include <pci.h> +#endif + +#define DEBUG_THIS CONFIG_DEBUG_VIRTIO_PCI +#include <debug.h> + +#include "virtio-ring.h" +#include "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) { + debug("ERROR: queue size is 0\n"); + goto fail; + } + + if (num > MAX_QUEUE_NUM) { + debug("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)) { + debug("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; + + debug("%s: ioaddr: 0x%x\n", __func__, ioaddr); + vp_reset(ioaddr); + vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE | + VIRTIO_CONFIG_S_DRIVER ); + return ioaddr; +} diff --git a/drivers/virtio-pci.h b/drivers/virtio-pci.h new file mode 100644 index 0000000..19966b4 --- /dev/null +++ b/drivers/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/drivers/virtio-ring.c b/drivers/virtio-ring.c new file mode 100644 index 0000000..2edaa06 --- /dev/null +++ b/drivers/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@bull.net + * + * some parts from Linux Virtio Ring + * + * Copyright Rusty Russell IBM Corporation 2007 + * + * Adopted for Seabios: Gleb Natapov gleb@redhat.com + * + * Further adopted to FILO: Himanshu Chauhan hschauhan@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 <lib.h> +#include <fs.h> +#include <arch/timer.h> +#include <arch/virtual.h> +#include <arch/io.h> + +#include "virtio-ring.h" +#include "virtio-pci.h" + +#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/drivers/virtio-ring.h b/drivers/virtio-ring.h new file mode 100644 index 0000000..bef7926 --- /dev/null +++ b/drivers/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_ */ diff --git a/fs/blockdev.c b/fs/blockdev.c index 0105712..be5719e 100644 --- a/fs/blockdev.c +++ b/fs/blockdev.c @@ -190,7 +190,16 @@ static int parse_device_name(const char *name, int *type, int *drive, *type = DISK_MEM; name += 3; *drive = 0; - } else { + } else if (memcmp(name, "vd", 2) == 0) { + *type = DISK_VIRTIO; + name += 2; + if (*name < 'a' || *name > 'z') { + printf("Invalid drive\n"); + return 0; + } + *drive = *name - 'a'; + name++; + }else { printf("Unknown device type: %s\n", name); return 0; } @@ -283,6 +292,16 @@ int devopen(const char *name, int *reopen) break; #endif
+#ifdef CONFIG_VIRTIO_DISK + case DISK_VIRTIO: + if (virtio_disk_probe(drive) != 0) { + debug("Failed to open virtio disk.\n"); + return 0; + } + disk_size = (uint32_t) -1; + break; +#endif + #ifdef CONFIG_FLASH_DISK case DISK_FLASH: if (flash_probe(drive) != 0) { @@ -447,6 +466,15 @@ static void *read_sector(unsigned long sector) } #endif
+#ifdef CONFIG_VIRTIO_DISK + case DISK_VIRTIO: + { + if (virtio_blk_read(dev_drive, sector, buf) != 0) + goto readerr; + break; + } +#endif + #ifdef CONFIG_FLASH_DISK case DISK_FLASH: if (flash_read(dev_drive, sector, buf) != 0) diff --git a/include/fs.h b/include/fs.h index c91ace7..9b35ca7 100644 --- a/include/fs.h +++ b/include/fs.h @@ -48,10 +48,16 @@ int flash_read(int drive, sector_t sector, void *buffer); void NAND_close(void); #endif
+#ifdef CONFIG_VIRTIO_DISK +int virtio_disk_probe(int drive); +int virtio_blk_read(int drive, sector_t sector, void *buffer); +#endif + #define DISK_IDE 1 #define DISK_MEM 2 #define DISK_USB 3 #define DISK_FLASH 4 +#define DISK_VIRTIO 5
int devopen(const char *name, int *reopen); void devclose(void);