Browse Source
Add virtio MMIO support. Add virtio-blk-test MMIO test case. Signed-off-by: Marc Marí <marc.mari.barcelo@gmail.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Message-id: 1424812915-25728-6-git-send-email-marc.mari.barcelo@gmail.com Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>pull/21/head
committed by
Kevin Wolf
4 changed files with 323 additions and 8 deletions
@ -0,0 +1,198 @@ |
|||
/*
|
|||
* libqos virtio MMIO driver |
|||
* |
|||
* Copyright (c) 2014 Marc Marí |
|||
* |
|||
* This work is licensed under the terms of the GNU GPL, version 2 or later. |
|||
* See the COPYING file in the top-level directory. |
|||
*/ |
|||
|
|||
#include <glib.h> |
|||
#include <stdio.h> |
|||
#include "libqtest.h" |
|||
#include "libqos/virtio.h" |
|||
#include "libqos/virtio-mmio.h" |
|||
#include "libqos/malloc.h" |
|||
#include "libqos/malloc-generic.h" |
|||
|
|||
static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t addr) |
|||
{ |
|||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; |
|||
return readb(dev->addr + addr); |
|||
} |
|||
|
|||
static uint16_t qvirtio_mmio_config_readw(QVirtioDevice *d, uint64_t addr) |
|||
{ |
|||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; |
|||
return readw(dev->addr + addr); |
|||
} |
|||
|
|||
static uint32_t qvirtio_mmio_config_readl(QVirtioDevice *d, uint64_t addr) |
|||
{ |
|||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; |
|||
return readl(dev->addr + addr); |
|||
} |
|||
|
|||
static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t addr) |
|||
{ |
|||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; |
|||
return readq(dev->addr + addr); |
|||
} |
|||
|
|||
static uint32_t qvirtio_mmio_get_features(QVirtioDevice *d) |
|||
{ |
|||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; |
|||
writel(dev->addr + QVIRTIO_MMIO_HOST_FEATURES_SEL, 0); |
|||
return readl(dev->addr + QVIRTIO_MMIO_HOST_FEATURES); |
|||
} |
|||
|
|||
static void qvirtio_mmio_set_features(QVirtioDevice *d, uint32_t features) |
|||
{ |
|||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; |
|||
dev->features = features; |
|||
writel(dev->addr + QVIRTIO_MMIO_GUEST_FEATURES_SEL, 0); |
|||
writel(dev->addr + QVIRTIO_MMIO_GUEST_FEATURES, features); |
|||
} |
|||
|
|||
static uint32_t qvirtio_mmio_get_guest_features(QVirtioDevice *d) |
|||
{ |
|||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; |
|||
return dev->features; |
|||
} |
|||
|
|||
static uint8_t qvirtio_mmio_get_status(QVirtioDevice *d) |
|||
{ |
|||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; |
|||
return (uint8_t)readl(dev->addr + QVIRTIO_MMIO_DEVICE_STATUS); |
|||
} |
|||
|
|||
static void qvirtio_mmio_set_status(QVirtioDevice *d, uint8_t status) |
|||
{ |
|||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; |
|||
writel(dev->addr + QVIRTIO_MMIO_DEVICE_STATUS, (uint32_t)status); |
|||
} |
|||
|
|||
static bool qvirtio_mmio_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) |
|||
{ |
|||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; |
|||
uint32_t isr; |
|||
|
|||
isr = readl(dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 1; |
|||
if (isr != 0) { |
|||
writel(dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 1); |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
static bool qvirtio_mmio_get_config_isr_status(QVirtioDevice *d) |
|||
{ |
|||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; |
|||
uint32_t isr; |
|||
|
|||
isr = readl(dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 2; |
|||
if (isr != 0) { |
|||
writel(dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 2); |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
static void qvirtio_mmio_queue_select(QVirtioDevice *d, uint16_t index) |
|||
{ |
|||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; |
|||
writel(dev->addr + QVIRTIO_MMIO_QUEUE_SEL, (uint32_t)index); |
|||
|
|||
g_assert_cmphex(readl(dev->addr + QVIRTIO_MMIO_QUEUE_PFN), ==, 0); |
|||
} |
|||
|
|||
static uint16_t qvirtio_mmio_get_queue_size(QVirtioDevice *d) |
|||
{ |
|||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; |
|||
return (uint16_t)readl(dev->addr + QVIRTIO_MMIO_QUEUE_NUM_MAX); |
|||
} |
|||
|
|||
static void qvirtio_mmio_set_queue_address(QVirtioDevice *d, uint32_t pfn) |
|||
{ |
|||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; |
|||
writel(dev->addr + QVIRTIO_MMIO_QUEUE_PFN, pfn); |
|||
} |
|||
|
|||
static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d, |
|||
QGuestAllocator *alloc, uint16_t index) |
|||
{ |
|||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; |
|||
QVirtQueue *vq; |
|||
uint64_t addr; |
|||
|
|||
vq = g_malloc0(sizeof(*vq)); |
|||
qvirtio_mmio_queue_select(d, index); |
|||
writel(dev->addr + QVIRTIO_MMIO_QUEUE_ALIGN, dev->page_size); |
|||
|
|||
vq->index = index; |
|||
vq->size = qvirtio_mmio_get_queue_size(d); |
|||
vq->free_head = 0; |
|||
vq->num_free = vq->size; |
|||
vq->align = dev->page_size; |
|||
vq->indirect = (dev->features & QVIRTIO_F_RING_INDIRECT_DESC) != 0; |
|||
vq->event = (dev->features & QVIRTIO_F_RING_EVENT_IDX) != 0; |
|||
|
|||
writel(dev->addr + QVIRTIO_MMIO_QUEUE_NUM, vq->size); |
|||
|
|||
/* Check different than 0 */ |
|||
g_assert_cmpint(vq->size, !=, 0); |
|||
|
|||
/* Check power of 2 */ |
|||
g_assert_cmpint(vq->size & (vq->size - 1), ==, 0); |
|||
|
|||
addr = guest_alloc(alloc, qvring_size(vq->size, dev->page_size)); |
|||
qvring_init(alloc, vq, addr); |
|||
qvirtio_mmio_set_queue_address(d, vq->desc / dev->page_size); |
|||
|
|||
return vq; |
|||
} |
|||
|
|||
static void qvirtio_mmio_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) |
|||
{ |
|||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; |
|||
writel(dev->addr + QVIRTIO_MMIO_QUEUE_NOTIFY, vq->index); |
|||
} |
|||
|
|||
const QVirtioBus qvirtio_mmio = { |
|||
.config_readb = qvirtio_mmio_config_readb, |
|||
.config_readw = qvirtio_mmio_config_readw, |
|||
.config_readl = qvirtio_mmio_config_readl, |
|||
.config_readq = qvirtio_mmio_config_readq, |
|||
.get_features = qvirtio_mmio_get_features, |
|||
.set_features = qvirtio_mmio_set_features, |
|||
.get_guest_features = qvirtio_mmio_get_guest_features, |
|||
.get_status = qvirtio_mmio_get_status, |
|||
.set_status = qvirtio_mmio_set_status, |
|||
.get_queue_isr_status = qvirtio_mmio_get_queue_isr_status, |
|||
.get_config_isr_status = qvirtio_mmio_get_config_isr_status, |
|||
.queue_select = qvirtio_mmio_queue_select, |
|||
.get_queue_size = qvirtio_mmio_get_queue_size, |
|||
.set_queue_address = qvirtio_mmio_set_queue_address, |
|||
.virtqueue_setup = qvirtio_mmio_virtqueue_setup, |
|||
.virtqueue_kick = qvirtio_mmio_virtqueue_kick, |
|||
}; |
|||
|
|||
QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size) |
|||
{ |
|||
QVirtioMMIODevice *dev; |
|||
uint32_t magic; |
|||
dev = g_malloc0(sizeof(*dev)); |
|||
|
|||
magic = readl(addr + QVIRTIO_MMIO_MAGIC_VALUE); |
|||
g_assert(magic == ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)); |
|||
|
|||
dev->addr = addr; |
|||
dev->page_size = page_size; |
|||
dev->vdev.device_type = readl(addr + QVIRTIO_MMIO_DEVICE_ID); |
|||
|
|||
writel(addr + QVIRTIO_MMIO_GUEST_PAGE_SIZE, page_size); |
|||
|
|||
return dev; |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
/*
|
|||
* libqos virtio MMIO definitions |
|||
* |
|||
* Copyright (c) 2014 Marc Marí |
|||
* |
|||
* This work is licensed under the terms of the GNU GPL, version 2 or later. |
|||
* See the COPYING file in the top-level directory. |
|||
*/ |
|||
|
|||
#ifndef LIBQOS_VIRTIO_MMIO_H |
|||
#define LIBQOS_VIRTIO_MMIO_H |
|||
|
|||
#include "libqos/virtio.h" |
|||
|
|||
#define QVIRTIO_MMIO_MAGIC_VALUE 0x000 |
|||
#define QVIRTIO_MMIO_VERSION 0x004 |
|||
#define QVIRTIO_MMIO_DEVICE_ID 0x008 |
|||
#define QVIRTIO_MMIO_VENDOR_ID 0x00C |
|||
#define QVIRTIO_MMIO_HOST_FEATURES 0x010 |
|||
#define QVIRTIO_MMIO_HOST_FEATURES_SEL 0x014 |
|||
#define QVIRTIO_MMIO_GUEST_FEATURES 0x020 |
|||
#define QVIRTIO_MMIO_GUEST_FEATURES_SEL 0x024 |
|||
#define QVIRTIO_MMIO_GUEST_PAGE_SIZE 0x028 |
|||
#define QVIRTIO_MMIO_QUEUE_SEL 0x030 |
|||
#define QVIRTIO_MMIO_QUEUE_NUM_MAX 0x034 |
|||
#define QVIRTIO_MMIO_QUEUE_NUM 0x038 |
|||
#define QVIRTIO_MMIO_QUEUE_ALIGN 0x03C |
|||
#define QVIRTIO_MMIO_QUEUE_PFN 0x040 |
|||
#define QVIRTIO_MMIO_QUEUE_NOTIFY 0x050 |
|||
#define QVIRTIO_MMIO_INTERRUPT_STATUS 0x060 |
|||
#define QVIRTIO_MMIO_INTERRUPT_ACK 0x064 |
|||
#define QVIRTIO_MMIO_DEVICE_STATUS 0x070 |
|||
#define QVIRTIO_MMIO_DEVICE_SPECIFIC 0x100 |
|||
|
|||
typedef struct QVirtioMMIODevice { |
|||
QVirtioDevice vdev; |
|||
uint64_t addr; |
|||
uint32_t page_size; |
|||
uint32_t features; /* As it cannot be read later, save it */ |
|||
} QVirtioMMIODevice; |
|||
|
|||
extern const QVirtioBus qvirtio_mmio; |
|||
|
|||
QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size); |
|||
|
|||
#endif |
|||
Loading…
Reference in new issue