blob: 777676ad36dc6a16d00351980b7620dab5ba29f7 [file] [log] [blame] [edit]
#include "kvm/virtio-pci.h"
#include "kvm/ioport.h"
#include "kvm/virtio.h"
#include "kvm/virtio-pci-dev.h"
#include <linux/virtio_config.h>
#define VPCI_CFG_COMMON_SIZE sizeof(struct virtio_pci_common_cfg)
#define VPCI_CFG_COMMON_START 0
#define VPCI_CFG_COMMON_END (VPCI_CFG_COMMON_SIZE - 1)
/*
* Use a naturally aligned 4-byte doorbell, in case we ever want to
* implement VIRTIO_F_NOTIFICATION_DATA
*/
#define VPCI_CFG_NOTIFY_SIZE 4
#define VPCI_CFG_NOTIFY_START (VPCI_CFG_COMMON_END + 1)
#define VPCI_CFG_NOTIFY_END (VPCI_CFG_COMMON_END + VPCI_CFG_NOTIFY_SIZE)
#define VPCI_CFG_ISR_SIZE 4
#define VPCI_CFG_ISR_START (VPCI_CFG_NOTIFY_END + 1)
#define VPCI_CFG_ISR_END (VPCI_CFG_NOTIFY_END + VPCI_CFG_ISR_SIZE)
/*
* We're at 64 bytes. Use the remaining 192 bytes in PCI_IO_SIZE for the
* device-specific config space. It's sufficient for the devices we
* currently implement (virtio_blk_config is 60 bytes) and, I think, all
* existing virtio 1.2 devices.
*/
#define VPCI_CFG_DEV_START (VPCI_CFG_ISR_END + 1)
#define VPCI_CFG_DEV_END ((PCI_IO_SIZE) - 1)
#define VPCI_CFG_DEV_SIZE (VPCI_CFG_DEV_END - VPCI_CFG_DEV_START + 1)
#define vpci_selected_vq(vpci) \
vdev->ops->get_vq((vpci)->kvm, (vpci)->dev, (vpci)->queue_selector)
typedef bool (*access_handler_t)(struct virtio_device *, unsigned long, void *, int);
static bool virtio_pci__common_write(struct virtio_device *vdev,
unsigned long offset, void *data, int size)
{
u64 features;
u32 val, gsi, vec;
struct virtio_pci *vpci = vdev->virtio;
switch (offset - VPCI_CFG_COMMON_START) {
case VIRTIO_PCI_COMMON_DFSELECT:
vpci->device_features_sel = ioport__read32(data);
break;
case VIRTIO_PCI_COMMON_GFSELECT:
vpci->driver_features_sel = ioport__read32(data);
break;
case VIRTIO_PCI_COMMON_GF:
val = ioport__read32(data);
if (vpci->driver_features_sel > 1)
break;
features = (u64)val << (32 * vpci->driver_features_sel);
virtio_set_guest_features(vpci->kvm, vdev, vpci->dev, features);
break;
case VIRTIO_PCI_COMMON_MSIX:
vec = vpci->config_vector = ioport__read16(data);
gsi = virtio_pci__add_msix_route(vpci, vec);
if (gsi < 0)
break;
vpci->config_gsi = gsi;
break;
case VIRTIO_PCI_COMMON_STATUS:
vpci->status = ioport__read8(data);
virtio_notify_status(vpci->kvm, vdev, vpci->dev, vpci->status);
break;
case VIRTIO_PCI_COMMON_Q_SELECT:
val = ioport__read16(data);
if (val >= (u32)vdev->ops->get_vq_count(vpci->kvm, vpci->dev))
pr_warning("invalid vq number %u", val);
else
vpci->queue_selector = val;
break;
case VIRTIO_PCI_COMMON_Q_SIZE:
vdev->ops->set_size_vq(vpci->kvm, vpci->dev,
vpci->queue_selector,
ioport__read16(data));
break;
case VIRTIO_PCI_COMMON_Q_MSIX:
vec = vpci->vq_vector[vpci->queue_selector] = ioport__read16(data);
gsi = virtio_pci__add_msix_route(vpci, vec);
if (gsi < 0)
break;
vpci->gsis[vpci->queue_selector] = gsi;
if (vdev->ops->notify_vq_gsi)
vdev->ops->notify_vq_gsi(vpci->kvm, vpci->dev,
vpci->queue_selector, gsi);
break;
case VIRTIO_PCI_COMMON_Q_ENABLE:
val = ioport__read16(data);
if (val)
virtio_pci_init_vq(vpci->kvm, vdev, vpci->queue_selector);
else
virtio_pci_exit_vq(vpci->kvm, vdev, vpci->queue_selector);
break;
case VIRTIO_PCI_COMMON_Q_DESCLO:
vpci_selected_vq(vpci)->vring_addr.desc_lo = ioport__read32(data);
break;
case VIRTIO_PCI_COMMON_Q_DESCHI:
vpci_selected_vq(vpci)->vring_addr.desc_hi = ioport__read32(data);
break;
case VIRTIO_PCI_COMMON_Q_AVAILLO:
vpci_selected_vq(vpci)->vring_addr.avail_lo = ioport__read32(data);
break;
case VIRTIO_PCI_COMMON_Q_AVAILHI:
vpci_selected_vq(vpci)->vring_addr.avail_hi = ioport__read32(data);
break;
case VIRTIO_PCI_COMMON_Q_USEDLO:
vpci_selected_vq(vpci)->vring_addr.used_lo = ioport__read32(data);
break;
case VIRTIO_PCI_COMMON_Q_USEDHI:
vpci_selected_vq(vpci)->vring_addr.used_hi = ioport__read32(data);
break;
}
return true;
}
static bool virtio_pci__notify_write(struct virtio_device *vdev,
unsigned long offset, void *data, int size)
{
u16 vq = ioport__read16(data);
struct virtio_pci *vpci = vdev->virtio;
vdev->ops->notify_vq(vpci->kvm, vpci->dev, vq);
return true;
}
static bool virtio_pci__config_write(struct virtio_device *vdev,
unsigned long offset, void *data, int size)
{
struct virtio_pci *vpci = vdev->virtio;
return virtio_access_config(vpci->kvm, vdev, vpci->dev,
offset - VPCI_CFG_DEV_START, data, size,
true);
}
static bool virtio_pci__common_read(struct virtio_device *vdev,
unsigned long offset, void *data, int size)
{
u32 val;
struct virtio_pci *vpci = vdev->virtio;
u64 features = 1ULL << VIRTIO_F_VERSION_1 |
1ULL << VIRTIO_F_ACCESS_PLATFORM;
switch (offset - VPCI_CFG_COMMON_START) {
case VIRTIO_PCI_COMMON_DFSELECT:
val = vpci->device_features_sel;
ioport__write32(data, val);
break;
case VIRTIO_PCI_COMMON_DF:
if (vpci->device_features_sel > 1)
break;
features |= vdev->ops->get_host_features(vpci->kvm, vpci->dev);
val = features >> (32 * vpci->device_features_sel);
ioport__write32(data, val);
break;
case VIRTIO_PCI_COMMON_GFSELECT:
val = vpci->driver_features_sel;
ioport__write32(data, val);
break;
case VIRTIO_PCI_COMMON_MSIX:
val = vpci->config_vector;
ioport__write32(data, val);
break;
case VIRTIO_PCI_COMMON_NUMQ:
val = vdev->ops->get_vq_count(vpci->kvm, vpci->dev);
ioport__write32(data, val);
break;
case VIRTIO_PCI_COMMON_STATUS:
ioport__write8(data, vpci->status);
break;
case VIRTIO_PCI_COMMON_CFGGENERATION:
/*
* The config generation changes when the device updates a
* config field larger than 32 bits, that the driver may read
* using multiple accesses. Since kvmtool doesn't use any
* mutable config field larger than 32 bits, the generation is
* constant.
*/
ioport__write8(data, 0);
break;
case VIRTIO_PCI_COMMON_Q_SELECT:
ioport__write16(data, vpci->queue_selector);
break;
case VIRTIO_PCI_COMMON_Q_SIZE:
val = vdev->ops->get_size_vq(vpci->kvm, vpci->dev,
vpci->queue_selector);
ioport__write16(data, val);
break;
case VIRTIO_PCI_COMMON_Q_MSIX:
val = vpci->vq_vector[vpci->queue_selector];
ioport__write16(data, val);
break;
case VIRTIO_PCI_COMMON_Q_ENABLE:
val = vpci_selected_vq(vpci)->enabled;
ioport__write16(data, val);
break;
case VIRTIO_PCI_COMMON_Q_NOFF:
val = vpci->queue_selector;
ioport__write16(data, val);
break;
case VIRTIO_PCI_COMMON_Q_DESCLO:
val = vpci_selected_vq(vpci)->vring_addr.desc_lo;
ioport__write32(data, val);
break;
case VIRTIO_PCI_COMMON_Q_DESCHI:
val = vpci_selected_vq(vpci)->vring_addr.desc_hi;
ioport__write32(data, val);
break;
case VIRTIO_PCI_COMMON_Q_AVAILLO:
val = vpci_selected_vq(vpci)->vring_addr.avail_lo;
ioport__write32(data, val);
break;
case VIRTIO_PCI_COMMON_Q_AVAILHI:
val = vpci_selected_vq(vpci)->vring_addr.avail_hi;
ioport__write32(data, val);
break;
case VIRTIO_PCI_COMMON_Q_USEDLO:
val = vpci_selected_vq(vpci)->vring_addr.used_lo;
ioport__write32(data, val);
break;
case VIRTIO_PCI_COMMON_Q_USEDHI:
val = vpci_selected_vq(vpci)->vring_addr.used_hi;
ioport__write32(data, val);
break;
};
return true;
}
static bool virtio_pci__isr_read(struct virtio_device *vdev,
unsigned long offset, void *data, int size)
{
struct virtio_pci *vpci = vdev->virtio;
if (WARN_ON(offset - VPCI_CFG_ISR_START != 0))
return false;
ioport__write8(data, vpci->isr);
kvm__irq_line(vpci->kvm, vpci->legacy_irq_line, VIRTIO_IRQ_LOW);
vpci->isr = 0;
return 0;
}
static bool virtio_pci__config_read(struct virtio_device *vdev,
unsigned long offset, void *data, int size)
{
struct virtio_pci *vpci = vdev->virtio;
return virtio_access_config(vpci->kvm, vdev, vpci->dev,
offset - VPCI_CFG_DEV_START, data, size,
false);
}
static bool virtio_pci_access(struct kvm_cpu *vcpu, struct virtio_device *vdev,
unsigned long offset, void *data, int size,
bool write)
{
access_handler_t handler = NULL;
switch (offset) {
case VPCI_CFG_COMMON_START...VPCI_CFG_COMMON_END:
if (write)
handler = virtio_pci__common_write;
else
handler = virtio_pci__common_read;
break;
case VPCI_CFG_NOTIFY_START...VPCI_CFG_NOTIFY_END:
if (write)
handler = virtio_pci__notify_write;
break;
case VPCI_CFG_ISR_START...VPCI_CFG_ISR_END:
if (!write)
handler = virtio_pci__isr_read;
break;
case VPCI_CFG_DEV_START...VPCI_CFG_DEV_END:
if (write)
handler = virtio_pci__config_write;
else
handler = virtio_pci__config_read;
break;
}
if (!handler)
return false;
return handler(vdev, offset, data, size);
}
void virtio_pci_modern__io_mmio_callback(struct kvm_cpu *vcpu, u64 addr,
u8 *data, u32 len, u8 is_write,
void *ptr)
{
struct virtio_device *vdev = ptr;
struct virtio_pci *vpci = vdev->virtio;
u32 mmio_addr = virtio_pci__mmio_addr(vpci);
virtio_pci_access(vcpu, vdev, addr - mmio_addr, data, len, is_write);
}
int virtio_pci_modern_init(struct virtio_device *vdev)
{
int subsys_id;
struct virtio_pci *vpci = vdev->virtio;
struct pci_device_header *hdr = &vpci->pci_hdr;
subsys_id = le16_to_cpu(hdr->subsys_id);
hdr->device_id = cpu_to_le16(PCI_DEVICE_ID_VIRTIO_BASE + subsys_id);
hdr->subsys_id = cpu_to_le16(PCI_SUBSYS_ID_VIRTIO_BASE + subsys_id);
vpci->doorbell_offset = VPCI_CFG_NOTIFY_START;
vdev->endian = VIRTIO_ENDIAN_LE;
hdr->msix.next = PCI_CAP_OFF(hdr, virtio);
hdr->virtio.common = (struct virtio_pci_cap) {
.cap_vndr = PCI_CAP_ID_VNDR,
.cap_next = PCI_CAP_OFF(hdr, virtio.notify),
.cap_len = sizeof(hdr->virtio.common),
.cfg_type = VIRTIO_PCI_CAP_COMMON_CFG,
.bar = 1,
.offset = cpu_to_le32(VPCI_CFG_COMMON_START),
.length = cpu_to_le32(VPCI_CFG_COMMON_SIZE),
};
BUILD_BUG_ON(VPCI_CFG_COMMON_START & 0x3);
hdr->virtio.notify = (struct virtio_pci_notify_cap) {
.cap.cap_vndr = PCI_CAP_ID_VNDR,
.cap.cap_next = PCI_CAP_OFF(hdr, virtio.isr),
.cap.cap_len = sizeof(hdr->virtio.notify),
.cap.cfg_type = VIRTIO_PCI_CAP_NOTIFY_CFG,
.cap.bar = 1,
.cap.offset = cpu_to_le32(VPCI_CFG_NOTIFY_START),
.cap.length = cpu_to_le32(VPCI_CFG_NOTIFY_SIZE),
/*
* Notify multiplier is 0, meaning that notifications are all on
* the same register
*/
};
BUILD_BUG_ON(VPCI_CFG_NOTIFY_START & 0x3);
hdr->virtio.isr = (struct virtio_pci_cap) {
.cap_vndr = PCI_CAP_ID_VNDR,
.cap_next = PCI_CAP_OFF(hdr, virtio.device),
.cap_len = sizeof(hdr->virtio.isr),
.cfg_type = VIRTIO_PCI_CAP_ISR_CFG,
.bar = 1,
.offset = cpu_to_le32(VPCI_CFG_ISR_START),
.length = cpu_to_le32(VPCI_CFG_ISR_SIZE),
};
hdr->virtio.device = (struct virtio_pci_cap) {
.cap_vndr = PCI_CAP_ID_VNDR,
.cap_next = PCI_CAP_OFF(hdr, virtio.pci),
.cap_len = sizeof(hdr->virtio.device),
.cfg_type = VIRTIO_PCI_CAP_DEVICE_CFG,
.bar = 1,
.offset = cpu_to_le32(VPCI_CFG_DEV_START),
.length = cpu_to_le32(VPCI_CFG_DEV_SIZE),
};
BUILD_BUG_ON(VPCI_CFG_DEV_START & 0x3);
/*
* TODO: implement this weird proxy capability (it is a "MUST" in the
* spec, but I don't know if anyone actually uses it).
* It doesn't use any BAR space. Instead the driver writes .cap.offset
* and .cap.length to access a register in a BAR.
*/
hdr->virtio.pci = (struct virtio_pci_cfg_cap) {
.cap.cap_vndr = PCI_CAP_ID_VNDR,
.cap.cap_next = 0,
.cap.cap_len = sizeof(hdr->virtio.pci),
.cap.cfg_type = VIRTIO_PCI_CAP_PCI_CFG,
};
return 0;
}