mirror of https://github.com/xemu-project/xemu.git
xen-hvm: stop faking I/O to access PCI config space
This patch removes the current hackery where IOREQ_TYPE_PCI_CONFIG requests are handled by faking PIO to 0xcf8 and 0xcfc and replaces it with direct calls to pci_host_config_read/write_common(). Doing so necessitates mapping BDFs to PCIDevices but maintaining a simple QLIST in xen_device_realize/unrealize() will suffice. NOTE: whilst config space accesses are currently limited to PCI_CONFIG_SPACE_SIZE, this patch paves the way to increasing the limit to PCIE_CONFIG_SPACE_SIZE when Xen gains the ability to emulate MCFG table accesses. Signed-off-by: Paul Durrant <paul.durrant@citrix.com> Reviewed-by: Anthony PERARD <anthony.perard@citrix.com> Signed-off-by: Stefano Stabellini <sstabellini@kernel.org>
This commit is contained in:
parent
d3c49ebbe2
commit
dfb6578d69
hw/i386/xen
|
@ -16,6 +16,8 @@ cpu_ioreq_pio_read_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "
|
||||||
cpu_ioreq_pio_write_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio write reg data=0x%"PRIx64" port=0x%"PRIx64" size=%d"
|
cpu_ioreq_pio_write_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio write reg data=0x%"PRIx64" port=0x%"PRIx64" size=%d"
|
||||||
cpu_ioreq_move(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p copy dir=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d"
|
cpu_ioreq_move(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p copy dir=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d"
|
||||||
xen_map_resource_ioreq(uint32_t id, void *addr) "id: %u addr: %p"
|
xen_map_resource_ioreq(uint32_t id, void *addr) "id: %u addr: %p"
|
||||||
|
cpu_ioreq_config_read(void *req, uint32_t sbdf, uint32_t reg, uint32_t size, uint32_t data) "I/O=%p sbdf=0x%x reg=%u size=%u data=0x%x"
|
||||||
|
cpu_ioreq_config_write(void *req, uint32_t sbdf, uint32_t reg, uint32_t size, uint32_t data) "I/O=%p sbdf=0x%x reg=%u size=%u data=0x%x"
|
||||||
|
|
||||||
# xen-mapcache.c
|
# xen-mapcache.c
|
||||||
xen_map_cache(uint64_t phys_addr) "want 0x%"PRIx64
|
xen_map_cache(uint64_t phys_addr) "want 0x%"PRIx64
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "hw/pci/pci.h"
|
#include "hw/pci/pci.h"
|
||||||
|
#include "hw/pci/pci_host.h"
|
||||||
#include "hw/i386/pc.h"
|
#include "hw/i386/pc.h"
|
||||||
#include "hw/i386/apic-msidef.h"
|
#include "hw/i386/apic-msidef.h"
|
||||||
#include "hw/xen/xen_common.h"
|
#include "hw/xen/xen_common.h"
|
||||||
|
@ -88,6 +89,12 @@ typedef struct XenPhysmap {
|
||||||
|
|
||||||
static QLIST_HEAD(, XenPhysmap) xen_physmap;
|
static QLIST_HEAD(, XenPhysmap) xen_physmap;
|
||||||
|
|
||||||
|
typedef struct XenPciDevice {
|
||||||
|
PCIDevice *pci_dev;
|
||||||
|
uint32_t sbdf;
|
||||||
|
QLIST_ENTRY(XenPciDevice) entry;
|
||||||
|
} XenPciDevice;
|
||||||
|
|
||||||
typedef struct XenIOState {
|
typedef struct XenIOState {
|
||||||
ioservid_t ioservid;
|
ioservid_t ioservid;
|
||||||
shared_iopage_t *shared_page;
|
shared_iopage_t *shared_page;
|
||||||
|
@ -108,6 +115,7 @@ typedef struct XenIOState {
|
||||||
struct xs_handle *xenstore;
|
struct xs_handle *xenstore;
|
||||||
MemoryListener memory_listener;
|
MemoryListener memory_listener;
|
||||||
MemoryListener io_listener;
|
MemoryListener io_listener;
|
||||||
|
QLIST_HEAD(, XenPciDevice) dev_list;
|
||||||
DeviceListener device_listener;
|
DeviceListener device_listener;
|
||||||
hwaddr free_phys_offset;
|
hwaddr free_phys_offset;
|
||||||
const XenPhysmap *log_for_dirtybit;
|
const XenPhysmap *log_for_dirtybit;
|
||||||
|
@ -568,6 +576,12 @@ static void xen_device_realize(DeviceListener *listener,
|
||||||
|
|
||||||
if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
|
if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
|
||||||
PCIDevice *pci_dev = PCI_DEVICE(dev);
|
PCIDevice *pci_dev = PCI_DEVICE(dev);
|
||||||
|
XenPciDevice *xendev = g_new(XenPciDevice, 1);
|
||||||
|
|
||||||
|
xendev->pci_dev = pci_dev;
|
||||||
|
xendev->sbdf = PCI_BUILD_BDF(pci_dev_bus_num(pci_dev),
|
||||||
|
pci_dev->devfn);
|
||||||
|
QLIST_INSERT_HEAD(&state->dev_list, xendev, entry);
|
||||||
|
|
||||||
xen_map_pcidev(xen_domid, state->ioservid, pci_dev);
|
xen_map_pcidev(xen_domid, state->ioservid, pci_dev);
|
||||||
}
|
}
|
||||||
|
@ -580,8 +594,17 @@ static void xen_device_unrealize(DeviceListener *listener,
|
||||||
|
|
||||||
if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
|
if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
|
||||||
PCIDevice *pci_dev = PCI_DEVICE(dev);
|
PCIDevice *pci_dev = PCI_DEVICE(dev);
|
||||||
|
XenPciDevice *xendev, *next;
|
||||||
|
|
||||||
xen_unmap_pcidev(xen_domid, state->ioservid, pci_dev);
|
xen_unmap_pcidev(xen_domid, state->ioservid, pci_dev);
|
||||||
|
|
||||||
|
QLIST_FOREACH_SAFE(xendev, &state->dev_list, entry, next) {
|
||||||
|
if (xendev->pci_dev == pci_dev) {
|
||||||
|
QLIST_REMOVE(xendev, entry);
|
||||||
|
g_free(xendev);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -902,6 +925,62 @@ static void cpu_ioreq_move(ioreq_t *req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cpu_ioreq_config(XenIOState *state, ioreq_t *req)
|
||||||
|
{
|
||||||
|
uint32_t sbdf = req->addr >> 32;
|
||||||
|
uint32_t reg = req->addr;
|
||||||
|
XenPciDevice *xendev;
|
||||||
|
|
||||||
|
if (req->size != sizeof(uint8_t) && req->size != sizeof(uint16_t) &&
|
||||||
|
req->size != sizeof(uint32_t)) {
|
||||||
|
hw_error("PCI config access: bad size (%u)", req->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req->count != 1) {
|
||||||
|
hw_error("PCI config access: bad count (%u)", req->count);
|
||||||
|
}
|
||||||
|
|
||||||
|
QLIST_FOREACH(xendev, &state->dev_list, entry) {
|
||||||
|
if (xendev->sbdf != sbdf) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!req->data_is_ptr) {
|
||||||
|
if (req->dir == IOREQ_READ) {
|
||||||
|
req->data = pci_host_config_read_common(
|
||||||
|
xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE,
|
||||||
|
req->size);
|
||||||
|
trace_cpu_ioreq_config_read(req, xendev->sbdf, reg,
|
||||||
|
req->size, req->data);
|
||||||
|
} else if (req->dir == IOREQ_WRITE) {
|
||||||
|
trace_cpu_ioreq_config_write(req, xendev->sbdf, reg,
|
||||||
|
req->size, req->data);
|
||||||
|
pci_host_config_write_common(
|
||||||
|
xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE,
|
||||||
|
req->data, req->size);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint32_t tmp;
|
||||||
|
|
||||||
|
if (req->dir == IOREQ_READ) {
|
||||||
|
tmp = pci_host_config_read_common(
|
||||||
|
xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE,
|
||||||
|
req->size);
|
||||||
|
trace_cpu_ioreq_config_read(req, xendev->sbdf, reg,
|
||||||
|
req->size, tmp);
|
||||||
|
write_phys_req_item(req->data, req, 0, &tmp);
|
||||||
|
} else if (req->dir == IOREQ_WRITE) {
|
||||||
|
read_phys_req_item(req->data, req, 0, &tmp);
|
||||||
|
trace_cpu_ioreq_config_write(req, xendev->sbdf, reg,
|
||||||
|
req->size, tmp);
|
||||||
|
pci_host_config_write_common(
|
||||||
|
xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE,
|
||||||
|
tmp, req->size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void regs_to_cpu(vmware_regs_t *vmport_regs, ioreq_t *req)
|
static void regs_to_cpu(vmware_regs_t *vmport_regs, ioreq_t *req)
|
||||||
{
|
{
|
||||||
X86CPU *cpu;
|
X86CPU *cpu;
|
||||||
|
@ -974,27 +1053,9 @@ static void handle_ioreq(XenIOState *state, ioreq_t *req)
|
||||||
case IOREQ_TYPE_INVALIDATE:
|
case IOREQ_TYPE_INVALIDATE:
|
||||||
xen_invalidate_map_cache();
|
xen_invalidate_map_cache();
|
||||||
break;
|
break;
|
||||||
case IOREQ_TYPE_PCI_CONFIG: {
|
case IOREQ_TYPE_PCI_CONFIG:
|
||||||
uint32_t sbdf = req->addr >> 32;
|
cpu_ioreq_config(state, req);
|
||||||
uint32_t val;
|
|
||||||
|
|
||||||
/* Fake a write to port 0xCF8 so that
|
|
||||||
* the config space access will target the
|
|
||||||
* correct device model.
|
|
||||||
*/
|
|
||||||
val = (1u << 31) |
|
|
||||||
((req->addr & 0x0f00) << 16) |
|
|
||||||
((sbdf & 0xffff) << 8) |
|
|
||||||
(req->addr & 0xfc);
|
|
||||||
do_outp(0xcf8, 4, val);
|
|
||||||
|
|
||||||
/* Now issue the config space access via
|
|
||||||
* port 0xCFC
|
|
||||||
*/
|
|
||||||
req->addr = 0xcfc | (req->addr & 0x03);
|
|
||||||
cpu_ioreq_pio(req);
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
hw_error("Invalid ioreq type 0x%x\n", req->type);
|
hw_error("Invalid ioreq type 0x%x\n", req->type);
|
||||||
}
|
}
|
||||||
|
@ -1415,6 +1476,7 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory)
|
||||||
memory_listener_register(&state->io_listener, &address_space_io);
|
memory_listener_register(&state->io_listener, &address_space_io);
|
||||||
|
|
||||||
state->device_listener = xen_device_listener;
|
state->device_listener = xen_device_listener;
|
||||||
|
QLIST_INIT(&state->dev_list);
|
||||||
device_listener_register(&state->device_listener);
|
device_listener_register(&state->device_listener);
|
||||||
|
|
||||||
/* Initialize backend core & drivers */
|
/* Initialize backend core & drivers */
|
||||||
|
|
Loading…
Reference in New Issue