Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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>
- Loading branch information
Showing
4 changed files
with
323 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters