dm/VBS-U: implement generic PCI barread/barwrite

This patch implements the generic PCI barread/barwrite callbacks.
Specific barread/barwrite interfaces are called based on the baridx.
Virtio legacy devices, transitional devices and modern devices can
be handled in an unified way.

Signed-off-by: Jian Jun Chen <jian.jun.chen@intel.com>
Reviewed-by: Hao Li <hao.l.li@intel.com>
Reviewed-by: Yin Fengwei <fengwei.yin@intel.com>
Reviewed-by: Zhao Yakui <yakui.zhao@intel.com>
Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
Jian Jun Chen 2018-03-29 10:56:28 +08:00 committed by lijinxia
parent 2acbebf6dc
commit 884f83f54f

View File

@ -541,9 +541,9 @@ virtio_find_cr(int offset) {
* If it's part of the virtio standard stuff, do that. * If it's part of the virtio standard stuff, do that.
* Otherwise dispatch to the actual driver. * Otherwise dispatch to the actual driver.
*/ */
uint64_t static uint64_t
virtio_pci_read(struct vmctx *ctx, int vcpu, struct pci_vdev *dev, virtio_pci_legacy_read(struct vmctx *ctx, int vcpu, struct pci_vdev *dev,
int baridx, uint64_t offset, int size) int baridx, uint64_t offset, int size)
{ {
struct virtio_base *base = dev->arg; struct virtio_base *base = dev->arg;
struct virtio_ops *vops; struct virtio_ops *vops;
@ -554,13 +554,6 @@ virtio_pci_read(struct vmctx *ctx, int vcpu, struct pci_vdev *dev,
uint32_t value; uint32_t value;
int error; int error;
if (base->flags & VIRTIO_USE_MSIX) {
if (baridx == pci_msix_table_bar(dev) ||
baridx == pci_msix_pba_bar(dev)) {
return pci_emul_msix_tread(dev, offset, size);
}
}
/* XXX probably should do something better than just assert() */ /* XXX probably should do something better than just assert() */
assert(baridx == base->legacy_pio_bar_idx); assert(baridx == base->legacy_pio_bar_idx);
@ -662,9 +655,9 @@ done:
* If it's part of the virtio standard stuff, do that. * If it's part of the virtio standard stuff, do that.
* Otherwise dispatch to the actual driver. * Otherwise dispatch to the actual driver.
*/ */
void static void
virtio_pci_write(struct vmctx *ctx, int vcpu, struct pci_vdev *dev, virtio_pci_legacy_write(struct vmctx *ctx, int vcpu, struct pci_vdev *dev,
int baridx, uint64_t offset, int size, uint64_t value) int baridx, uint64_t offset, int size, uint64_t value)
{ {
struct virtio_base *base = dev->arg; struct virtio_base *base = dev->arg;
struct virtio_vq_info *vq; struct virtio_vq_info *vq;
@ -675,14 +668,6 @@ virtio_pci_write(struct vmctx *ctx, int vcpu, struct pci_vdev *dev,
uint32_t newoff; uint32_t newoff;
int error; int error;
if (base->flags & VIRTIO_USE_MSIX) {
if (baridx == pci_msix_table_bar(dev) ||
baridx == pci_msix_pba_bar(dev)) {
pci_emul_msix_twrite(dev, offset, size, value);
return;
}
}
/* XXX probably should do something better than just assert() */ /* XXX probably should do something better than just assert() */
assert(baridx == base->legacy_pio_bar_idx); assert(baridx == base->legacy_pio_bar_idx);
@ -930,3 +915,318 @@ virtio_set_modern_bar(struct virtio_base *base, bool use_notify_pio)
return rc; return rc;
} }
static struct cap_region {
uint64_t cap_offset; /* offset of capability region */
int cap_size; /* size of capability region */
int cap_id; /* capability id */
} cap_regions[] = {
{VIRTIO_CAP_COMMON_OFFSET, VIRTIO_CAP_COMMON_SIZE,
VIRTIO_PCI_CAP_COMMON_CFG},
{VIRTIO_CAP_ISR_OFFSET, VIRTIO_CAP_ISR_SIZE,
VIRTIO_PCI_CAP_ISR_CFG},
{VIRTIO_CAP_DEVICE_OFFSET, VIRTIO_CAP_DEVICE_SIZE,
VIRTIO_PCI_CAP_DEVICE_CFG},
{VIRTIO_CAP_NOTIFY_OFFSET, VIRTIO_CAP_NOTIFY_SIZE,
VIRTIO_PCI_CAP_NOTIFY_CFG},
};
static inline int
virtio_get_cap_id(uint64_t offset, int size)
{
int i, rc = -1;
for (i = 0; i < ARRAY_SIZE(cap_regions); i++) {
if (offset >= cap_regions[i].cap_offset &&
offset + size <= cap_regions[i].cap_offset +
cap_regions[i].cap_size)
return cap_regions[i].cap_id;
}
return rc;
}
static uint32_t
virtio_common_cfg_read(struct pci_vdev *dev, uint64_t offset, int size)
{
/* TODO: to be implemented */
return 0;
}
static void
virtio_common_cfg_write(struct pci_vdev *dev, uint64_t offset, int size,
uint64_t value)
{
/* TODO: to be implemented */
}
/* ignore driver writes to ISR region, and only support ISR region read */
static uint32_t
virtio_isr_cfg_read(struct pci_vdev *dev, uint64_t offset, int size)
{
/* TODO: to be implemented */
return 0;
}
static uint32_t
virtio_device_cfg_read(struct pci_vdev *dev, uint64_t offset, int size)
{
/* TODO: to be implemented */
return 0;
}
static void
virtio_device_cfg_write(struct pci_vdev *dev, uint64_t offset, int size,
uint64_t value)
{
/* TODO: to be implemented */
}
/*
* ignore driver reads from notify region, and only support notify region
* write
*/
static void
virtio_notify_cfg_write(struct pci_vdev *dev, uint64_t offset, int size,
uint64_t value)
{
/* TODO: to be implemented */
}
static uint32_t
virtio_pci_modern_mmio_read(struct vmctx *ctx, int vcpu, struct pci_vdev *dev,
int baridx, uint64_t offset, int size)
{
struct virtio_base *base = dev->arg;
struct virtio_ops *vops;
const char *name;
uint32_t value;
int capid;
assert(base->modern_mmio_bar_idx == baridx);
vops = base->vops;
name = vops->name;
value = size == 1 ? 0xff : size == 2 ? 0xffff : 0xffffffff;
if (size != 1 && size != 2 && size != 4) {
fprintf(stderr,
"%s: read from [%d:0x%lx] bad size %d\r\n",
name, baridx, offset, size);
return value;
}
capid = virtio_get_cap_id(offset, size);
if (capid < 0) {
fprintf(stderr,
"%s: read from [%d:0x%lx] bad range %d\r\n",
name, baridx, offset, size);
return value;
}
if (base->mtx)
pthread_mutex_lock(base->mtx);
switch (capid) {
case VIRTIO_PCI_CAP_COMMON_CFG:
offset -= VIRTIO_CAP_COMMON_OFFSET;
value = virtio_common_cfg_read(dev, offset, size);
break;
case VIRTIO_PCI_CAP_ISR_CFG:
offset -= VIRTIO_CAP_ISR_OFFSET;
value = virtio_isr_cfg_read(dev, offset, size);
break;
case VIRTIO_PCI_CAP_DEVICE_CFG:
offset -= VIRTIO_CAP_DEVICE_OFFSET;
value = virtio_device_cfg_read(dev, offset, size);
break;
default: /* guest driver should not read from notify region */
fprintf(stderr,
"%s: read from [%d:0x%lx] size %d not supported\r\n",
name, baridx, offset, size);
}
if (base->mtx)
pthread_mutex_unlock(base->mtx);
return value;
}
static void
virtio_pci_modern_mmio_write(struct vmctx *ctx, int vcpu, struct pci_vdev *dev,
int baridx, uint64_t offset, int size,
uint64_t value)
{
struct virtio_base *base = dev->arg;
struct virtio_ops *vops;
const char *name;
int capid;
assert(base->modern_mmio_bar_idx == baridx);
vops = base->vops;
name = vops->name;
if (size != 1 && size != 2 && size != 4) {
fprintf(stderr,
"%s: write to [%d:0x%lx] bad size %d\r\n",
name, baridx, offset, size);
return;
}
capid = virtio_get_cap_id(offset, size);
if (capid < 0) {
fprintf(stderr,
"%s: write to [%d:0x%lx] bad range %d\r\n",
name, baridx, offset, size);
return;
}
if (base->mtx)
pthread_mutex_lock(base->mtx);
switch (capid) {
case VIRTIO_PCI_CAP_COMMON_CFG:
offset -= VIRTIO_CAP_COMMON_OFFSET;
virtio_common_cfg_write(dev, offset, size, value);
break;
case VIRTIO_PCI_CAP_DEVICE_CFG:
offset -= VIRTIO_CAP_DEVICE_OFFSET;
virtio_device_cfg_write(dev, offset, size, value);
break;
case VIRTIO_PCI_CAP_NOTIFY_CFG:
offset -= VIRTIO_CAP_NOTIFY_OFFSET;
virtio_notify_cfg_write(dev, offset, size, value);
break;
default: /* guest driver should not write to ISR region */
fprintf(stderr,
"%s: write to [%d:0x%lx] size %d not supported\r\n",
name, baridx, offset, size);
}
if (base->mtx)
pthread_mutex_unlock(base->mtx);
}
static uint32_t
virtio_pci_modern_pio_read(struct vmctx *ctx, int vcpu, struct pci_vdev *dev,
int baridx, uint64_t offset, int size)
{
struct virtio_base *base = dev->arg;
assert(base->modern_pio_bar_idx == baridx);
/* guest driver should not read notify pio */
return 0;
}
static void
virtio_pci_modern_pio_write(struct vmctx *ctx, int vcpu, struct pci_vdev *dev,
int baridx, uint64_t offset, int size,
uint64_t value)
{
struct virtio_base *base = dev->arg;
struct virtio_vq_info *vq;
struct virtio_ops *vops;
const char *name;
uint64_t idx;
assert(base->modern_pio_bar_idx == baridx);
vops = base->vops;
name = vops->name;
idx = value;
if (size != 1 && size != 2 && size != 4) {
fprintf(stderr,
"%s: write to [%d:0x%lx] bad size %d\r\n",
name, baridx, offset, size);
return;
}
if (idx >= vops->nvq) {
fprintf(stderr,
"%s: queue %lu notify out of range\r\n", name, idx);
return;
}
if (base->mtx)
pthread_mutex_lock(base->mtx);
vq = &base->queues[idx];
if (vq->notify)
(*vq->notify)(DEV_STRUCT(base), vq);
else if (vops->qnotify)
(*vops->qnotify)(DEV_STRUCT(base), vq);
else
fprintf(stderr,
"%s: qnotify queue %lu: missing vq/vops notify\r\n",
name, idx);
if (base->mtx)
pthread_mutex_unlock(base->mtx);
}
uint64_t
virtio_pci_read(struct vmctx *ctx, int vcpu, struct pci_vdev *dev,
int baridx, uint64_t offset, int size)
{
struct virtio_base *base = dev->arg;
if (base->flags & VIRTIO_USE_MSIX) {
if (baridx == pci_msix_table_bar(dev) ||
baridx == pci_msix_pba_bar(dev)) {
return pci_emul_msix_tread(dev, offset, size);
}
}
if (baridx == base->legacy_pio_bar_idx)
return virtio_pci_legacy_read(ctx, vcpu, dev, baridx,
offset, size);
if (baridx == base->modern_mmio_bar_idx)
return virtio_pci_modern_mmio_read(ctx, vcpu, dev, baridx,
offset, size);
if (baridx == base->modern_pio_bar_idx)
return virtio_pci_modern_pio_read(ctx, vcpu, dev, baridx,
offset, size);
fprintf(stderr, "%s: read unexpected baridx %d\r\n",
base->vops->name, baridx);
return size == 1 ? 0xff : size == 2 ? 0xffff : 0xffffffff;
}
void
virtio_pci_write(struct vmctx *ctx, int vcpu, struct pci_vdev *dev,
int baridx, uint64_t offset, int size, uint64_t value)
{
struct virtio_base *base = dev->arg;
if (base->flags & VIRTIO_USE_MSIX) {
if (baridx == pci_msix_table_bar(dev) ||
baridx == pci_msix_pba_bar(dev)) {
pci_emul_msix_twrite(dev, offset, size, value);
return;
}
}
if (baridx == base->legacy_pio_bar_idx) {
virtio_pci_legacy_write(ctx, vcpu, dev, baridx,
offset, size, value);
return;
}
if (baridx == base->modern_mmio_bar_idx) {
virtio_pci_modern_mmio_write(ctx, vcpu, dev, baridx,
offset, size, value);
return;
}
if (baridx == base->modern_pio_bar_idx) {
virtio_pci_modern_pio_write(ctx, vcpu, dev, baridx,
offset, size, value);
return;
}
fprintf(stderr, "%s: write unexpected baridx %d\r\n",
base->vops->name, baridx);
}