Hi,
As discussed this implements virtio regions access via pci cfg space window. Used in case bars are mapped above 4G. Patch #3 is for testing and not intended to be merged.
cheers, Gerd
Gerd Hoffmann (3): virtio: uninline _vp_{read,write} virtio: pci cfg access [testing] map 64bit bars high unconditionally.
src/fw/pciinit.c | 2 +- src/hw/virtio-pci.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++--- src/hw/virtio-pci.h | 92 ++++----------------- 3 files changed, 227 insertions(+), 91 deletions(-)
Next patch makes it larger, and I don't think it makes sense to continue inlining it. Uninline and move from header to c file.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/hw/virtio-pci.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/hw/virtio-pci.h | 79 ++--------------------------------------------------- 2 files changed, 80 insertions(+), 77 deletions(-)
diff --git a/src/hw/virtio-pci.c b/src/hw/virtio-pci.c index 6df5194..769710a 100644 --- a/src/hw/virtio-pci.c +++ b/src/hw/virtio-pci.c @@ -24,6 +24,84 @@ #include "virtio-pci.h" #include "virtio-ring.h"
+u64 _vp_read(struct vp_cap *cap, u32 offset, u8 size) +{ + u32 addr = cap->addr + offset; + u64 var; + + if (cap->is_io) { + switch (size) { + case 8: + var = inl(addr); + var |= (u64)inl(addr+4) << 32; + break; + case 4: + var = inl(addr); + break; + case 2: + var = inw(addr); + break; + case 1: + var = inb(addr); + break; + default: + var = 0; + } + } else { + switch (size) { + case 8: + var = readl((void*)addr); + var |= (u64)readl((void*)(addr+4)) << 32; + break; + case 4: + var = readl((void*)addr); + break; + case 2: + var = readw((void*)addr); + break; + case 1: + var = readb((void*)addr); + break; + default: + var = 0; + } + } + dprintf(9, "vp read %x (%d) -> 0x%llx\n", addr, size, var); + return var; +} + +void _vp_write(struct vp_cap *cap, u32 offset, u8 size, u64 var) +{ + u32 addr = cap->addr + offset; + + dprintf(9, "vp write %x (%d) <- 0x%llx\n", addr, size, var); + if (cap->is_io) { + switch (size) { + case 4: + outl(var, addr); + break; + case 2: + outw(var, addr); + break; + case 1: + outb(var, addr); + break; + } + } else { + switch (size) { + case 4: + writel((void*)addr, var); + break; + case 2: + writew((void*)addr, var); + break; + case 1: + writeb((void*)addr, var); + break; + } + } +} + u64 vp_get_features(struct vp_device *vp) { u32 f0, f1; diff --git a/src/hw/virtio-pci.h b/src/hw/virtio-pci.h index b11c355..8d4ebe3 100644 --- a/src/hw/virtio-pci.h +++ b/src/hw/virtio-pci.h @@ -98,83 +98,8 @@ struct vp_device { u8 use_modern; };
-static inline u64 _vp_read(struct vp_cap *cap, u32 offset, u8 size) -{ - u32 addr = cap->addr + offset; - u64 var; - - if (cap->is_io) { - switch (size) { - case 8: - var = inl(addr); - var |= (u64)inl(addr+4) << 32; - break; - case 4: - var = inl(addr); - break; - case 2: - var = inw(addr); - break; - case 1: - var = inb(addr); - break; - default: - var = 0; - } - } else { - switch (size) { - case 8: - var = readl((void*)addr); - var |= (u64)readl((void*)(addr+4)) << 32; - break; - case 4: - var = readl((void*)addr); - break; - case 2: - var = readw((void*)addr); - break; - case 1: - var = readb((void*)addr); - break; - default: - var = 0; - } - } - dprintf(9, "vp read %x (%d) -> 0x%llx\n", addr, size, var); - return var; -} - -static inline void _vp_write(struct vp_cap *cap, u32 offset, u8 size, u64 var) -{ - u32 addr = cap->addr + offset; - - dprintf(9, "vp write %x (%d) <- 0x%llx\n", addr, size, var); - if (cap->is_io) { - switch (size) { - case 4: - outl(var, addr); - break; - case 2: - outw(var, addr); - break; - case 1: - outb(var, addr); - break; - } - } else { - switch (size) { - case 4: - writel((void*)addr, var); - break; - case 2: - writew((void*)addr, var); - break; - case 1: - writeb((void*)addr, var); - break; - } - } -} +u64 _vp_read(struct vp_cap *cap, u32 offset, u8 size); +void _vp_write(struct vp_cap *cap, u32 offset, u8 size, u64 var);
#define vp_read(_cap, _struct, _field) \ _vp_read(_cap, offsetof(_struct, _field), \
virtio regions can also be accessed using a window in pci cfg space. Add support for it. Enable it in case the virtio regions are mapped high (above 4g), so direct mmio access doesn't work for us even in 32bit mode.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/hw/virtio-pci.c | 164 +++++++++++++++++++++++++++++++++++++++++++++------- src/hw/virtio-pci.h | 13 ++++- 2 files changed, 155 insertions(+), 22 deletions(-)
diff --git a/src/hw/virtio-pci.c b/src/hw/virtio-pci.c index 769710a..55a766f 100644 --- a/src/hw/virtio-pci.c +++ b/src/hw/virtio-pci.c @@ -27,9 +27,10 @@ u64 _vp_read(struct vp_cap *cap, u32 offset, u8 size) { u32 addr = cap->addr + offset; - u64 var; + u64 var = 0;
- if (cap->is_io) { + switch (cap->mode) { + case VP_ACCESS_IO: switch (size) { case 8: var = inl(addr); @@ -44,10 +45,10 @@ u64 _vp_read(struct vp_cap *cap, u32 offset, u8 size) case 1: var = inb(addr); break; - default: - var = 0; } - } else { + break; + + case VP_ACCESS_MMIO: switch (size) { case 8: var = readl((void*)addr); @@ -62,9 +63,43 @@ u64 _vp_read(struct vp_cap *cap, u32 offset, u8 size) case 1: var = readb((void*)addr); break; - default: - var = 0; } + break; + + case VP_ACCESS_PCICFG: + pci_config_writeb(cap->bdf, cap->cfg + + offsetof(struct virtio_pci_cfg_cap, cap.bar), + cap->bar); + pci_config_writel(cap->bdf, cap->cfg + + offsetof(struct virtio_pci_cfg_cap, cap.offset), + addr); + pci_config_writel(cap->bdf, cap->cfg + + offsetof(struct virtio_pci_cfg_cap, cap.length), + (size > 4) ? 4 : size); + switch (size) { + case 8: + var = pci_config_readl(cap->bdf, cap->cfg + + offsetof(struct virtio_pci_cfg_cap, pci_cfg_data)); + pci_config_writel(cap->bdf, cap->cfg + + offsetof(struct virtio_pci_cfg_cap, cap.offset), + addr + 4); + var |= (u64)pci_config_readl(cap->bdf, cap->cfg + + offsetof(struct virtio_pci_cfg_cap, pci_cfg_data)) << 32; + break; + case 4: + var = pci_config_readl(cap->bdf, cap->cfg + + offsetof(struct virtio_pci_cfg_cap, pci_cfg_data)); + break; + case 2: + var = pci_config_readw(cap->bdf, cap->cfg + + offsetof(struct virtio_pci_cfg_cap, pci_cfg_data)); + break; + case 1: + var = pci_config_readb(cap->bdf, cap->cfg + + offsetof(struct virtio_pci_cfg_cap, pci_cfg_data)); + break; + } + } dprintf(9, "vp read %x (%d) -> 0x%llx\n", addr, size, var); return var; @@ -75,7 +110,8 @@ void _vp_write(struct vp_cap *cap, u32 offset, u8 size, u64 var) u32 addr = cap->addr + offset;
dprintf(9, "vp write %x (%d) <- 0x%llx\n", addr, size, var); - if (cap->is_io) { + switch (cap->mode) { + case VP_ACCESS_IO: switch (size) { case 4: outl(var, addr); @@ -87,7 +123,9 @@ void _vp_write(struct vp_cap *cap, u32 offset, u8 size, u64 var) outb(var, addr); break; } - } else { + break; + + case VP_ACCESS_MMIO: switch (size) { case 4: writel((void*)addr, var); @@ -99,6 +137,36 @@ void _vp_write(struct vp_cap *cap, u32 offset, u8 size, u64 var) writeb((void*)addr, var); break; } + break; + + case VP_ACCESS_PCICFG: + pci_config_writeb(cap->bdf, cap->cfg + + offsetof(struct virtio_pci_cfg_cap, cap.bar), + cap->bar); + pci_config_writel(cap->bdf, cap->cfg + + offsetof(struct virtio_pci_cfg_cap, cap.offset), + addr); + pci_config_writel(cap->bdf, cap->cfg + + offsetof(struct virtio_pci_cfg_cap, cap.length), + size); + switch (size) { + case 4: + pci_config_writel(cap->bdf, cap->cfg + + offsetof(struct virtio_pci_cfg_cap, pci_cfg_data), + var); + break; + case 2: + pci_config_writew(cap->bdf, cap->cfg + + offsetof(struct virtio_pci_cfg_cap, pci_cfg_data), + var); + break; + case 1: + pci_config_writeb(cap->bdf, cap->cfg + + offsetof(struct virtio_pci_cfg_cap, pci_cfg_data), + var); + break; + } + } }
@@ -181,10 +249,26 @@ void vp_notify(struct vp_device *vp, struct vring_virtqueue *vq) u32 addr = vp->notify.addr + vq->queue_notify_off * vp->notify_off_multiplier; - if (vp->notify.is_io) { + switch (vp->notify.mode) { + case VP_ACCESS_IO: outw(vq->queue_index, addr); - } else { + break; + case VP_ACCESS_MMIO: writew((void*)addr, vq->queue_index); + break; + case VP_ACCESS_PCICFG: + pci_config_writeb(vp->notify.bdf, vp->notify.cfg + + offsetof(struct virtio_pci_cfg_cap, cap.bar), + vp->notify.bar); + pci_config_writel(vp->notify.bdf, vp->notify.cfg + + offsetof(struct virtio_pci_cfg_cap, cap.offset), + addr); + pci_config_writel(vp->notify.bdf, vp->notify.cfg + + offsetof(struct virtio_pci_cfg_cap, cap.length), + 2); + pci_config_writew(vp->notify.bdf, vp->notify.cfg + + offsetof(struct virtio_pci_cfg_cap, pci_cfg_data), + vq->queue_index); } dprintf(9, "vp notify %x (%d) -- 0x%x\n", addr, 2, vq->queue_index); @@ -286,7 +370,9 @@ void vp_init_simple(struct vp_device *vp, struct pci_device *pci) { u8 cap = pci_find_capability(pci, PCI_CAP_ID_VNDR, 0); struct vp_cap *vp_cap; - u32 addr, offset, mul; + const char *mode; + u32 offset, base, mul; + u64 addr; u8 type;
memset(vp, 0, sizeof(*vp)); @@ -308,6 +394,20 @@ void vp_init_simple(struct vp_device *vp, struct pci_device *pci) case VIRTIO_PCI_CAP_DEVICE_CFG: vp_cap = &vp->device; break; + case VIRTIO_PCI_CAP_PCI_CFG: + vp->common.cfg = cap; + vp->common.bdf = pci->bdf; + vp->notify.cfg = cap; + vp->notify.bdf = pci->bdf; + vp->isr.cfg = cap; + vp->isr.bdf = pci->bdf; + vp->device.cfg = cap; + vp->device.bdf = pci->bdf; + vp_cap = NULL; + dprintf(1, "pci dev %x:%x virtio cap at 0x%x type %d [pci cfg access]\n", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + cap, type); + break; default: vp_cap = NULL; break; @@ -318,20 +418,42 @@ void vp_init_simple(struct vp_device *vp, struct pci_device *pci) offsetof(struct virtio_pci_cap, bar)); offset = pci_config_readl(pci->bdf, cap + offsetof(struct virtio_pci_cap, offset)); - addr = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0 + 4 * vp_cap->bar); + base = PCI_BASE_ADDRESS_0 + 4 * vp_cap->bar; + addr = pci_config_readl(pci->bdf, base); if (addr & PCI_BASE_ADDRESS_SPACE_IO) { - vp_cap->is_io = 1; addr &= PCI_BASE_ADDRESS_IO_MASK; + vp_cap->mode = VP_ACCESS_IO; + } else if ((addr & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == + PCI_BASE_ADDRESS_MEM_TYPE_64) { + addr &= PCI_BASE_ADDRESS_MEM_MASK; + addr |= (u64)pci_config_readl(pci->bdf, base + 4) << 32; + vp_cap->mode = (addr > 0xffffffffll) ? + VP_ACCESS_PCICFG : VP_ACCESS_MMIO; } else { - vp_cap->is_io = 0; addr &= PCI_BASE_ADDRESS_MEM_MASK; + vp_cap->mode = VP_ACCESS_MMIO; } - vp_cap->addr = addr + offset; - dprintf(3, "pci dev %x:%x virtio cap at 0x%x type %d " - "bar %d at 0x%08x off +0x%04x [%s]\n", + switch (vp_cap->mode) { + case VP_ACCESS_IO: + mode = "io"; + vp_cap->addr = addr + offset; // io addr + break; + case VP_ACCESS_MMIO: + mode = "mmio"; + vp_cap->addr = addr + offset; // mem addr + break; + case VP_ACCESS_PCICFG: + mode = "pcicfg"; + vp_cap->addr = offset; // bar offset + break; + default: + mode = "Huh?"; + break; + } + dprintf(1, "pci dev %x:%x virtio cap at 0x%x type %d " + "bar %d at 0x%08llx off +0x%04x [%s]\n", pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), - vp_cap->cap, type, vp_cap->bar, addr, offset, - vp_cap->is_io ? "io" : "mmio"); + vp_cap->cap, type, vp_cap->bar, addr, offset, mode); }
cap = pci_find_capability(pci, PCI_CAP_ID_VNDR, cap); @@ -347,7 +469,7 @@ void vp_init_simple(struct vp_device *vp, struct pci_device *pci) vp->legacy.bar = 0; vp->legacy.addr = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) & PCI_BASE_ADDRESS_IO_MASK; - vp->legacy.is_io = 1; + vp->legacy.mode = VP_ACCESS_IO; }
vp_reset(vp); diff --git a/src/hw/virtio-pci.h b/src/hw/virtio-pci.h index 8d4ebe3..5ab2ec8 100644 --- a/src/hw/virtio-pci.h +++ b/src/hw/virtio-pci.h @@ -54,6 +54,11 @@ struct virtio_pci_notify_cap { u32 notify_off_multiplier; /* Multiplier for queue_notify_off. */ };
+struct virtio_pci_cfg_cap { + struct virtio_pci_cap cap; + u8 pci_cfg_data[4]; /* Data for BAR access. */ +}; + typedef struct virtio_pci_common_cfg { /* About the whole device. */ u32 device_feature_select; /* read-write */ @@ -85,11 +90,17 @@ typedef struct virtio_pci_isr {
/* --- driver structs ----------------------------------------------- */
+#define VP_ACCESS_IO 1 +#define VP_ACCESS_MMIO 2 +#define VP_ACCESS_PCICFG 3 + struct vp_cap { u32 addr; + u16 bdf; u8 cap; + u8 cfg; u8 bar; - u8 is_io; + u8 mode; };
struct vp_device {
--- src/fw/pciinit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/fw/pciinit.c b/src/fw/pciinit.c index 45870f2..ba1c84c 100644 --- a/src/fw/pciinit.c +++ b/src/fw/pciinit.c @@ -867,7 +867,7 @@ static void pci_bios_map_devices(struct pci_bus *busses) panic("PCI: out of I/O address space\n");
dprintf(1, "PCI: 32: %016llx - %016llx\n", pcimem_start, pcimem_end); - if (pci_bios_init_root_regions_mem(busses)) { + if (1 /* pci_bios_init_root_regions_mem(busses) */) { struct pci_region r64_mem, r64_pref; r64_mem.list.first = NULL; r64_pref.list.first = NULL;