qemu-e2k/hw/xen/xen_pt.c

1035 lines
32 KiB
C
Raw Normal View History

/*
* Copyright (c) 2007, Neocleus Corporation.
* Copyright (c) 2007, Intel Corporation.
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
* Alex Novik <alex@neocleus.com>
* Allen Kay <allen.m.kay@intel.com>
* Guy Zana <guy@neocleus.com>
*
* This file implements direct PCI assignment to a HVM guest
*/
/*
* Interrupt Disable policy:
*
* INTx interrupt:
* Initialize(register_real_device)
* Map INTx(xc_physdev_map_pirq):
* <fail>
* - Set real Interrupt Disable bit to '1'.
* - Set machine_irq and assigned_device->machine_irq to '0'.
* * Don't bind INTx.
*
* Bind INTx(xc_domain_bind_pt_pci_irq):
* <fail>
* - Set real Interrupt Disable bit to '1'.
* - Unmap INTx.
* - Decrement xen_pt_mapped_machine_irq[machine_irq]
* - Set assigned_device->machine_irq to '0'.
*
* Write to Interrupt Disable bit by guest software(xen_pt_cmd_reg_write)
* Write '0'
* - Set real bit to '0' if assigned_device->machine_irq isn't '0'.
*
* Write '1'
* - Set real bit to '1'.
*
* MSI interrupt:
* Initialize MSI register(xen_pt_msi_setup, xen_pt_msi_update)
* Bind MSI(xc_domain_update_msi_irq)
* <fail>
* - Unmap MSI.
* - Set dev->msi->pirq to '-1'.
*
* MSI-X interrupt:
* Initialize MSI-X register(xen_pt_msix_update_one)
* Bind MSI-X(xc_domain_update_msi_irq)
* <fail>
* - Unmap MSI-X.
* - Set entry->pirq to '-1'.
*/
#include "qemu/osdep.h"
2016-03-14 09:01:28 +01:00
#include "qapi/error.h"
#include <sys/ioctl.h>
#include "hw/pci/pci.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "xen_pt.h"
#include "hw/xen/xen.h"
#include "hw/xen/xen-legacy-backend.h"
#include "qemu/range.h"
static bool has_igd_gfx_passthru;
bool xen_igd_gfx_pt_enabled(void)
{
return has_igd_gfx_passthru;
}
void xen_igd_gfx_pt_set(bool value, Error **errp)
{
has_igd_gfx_passthru = value;
}
#define XEN_PT_NR_IRQS (256)
static uint8_t xen_pt_mapped_machine_irq[XEN_PT_NR_IRQS] = {0};
void xen_pt_log(const PCIDevice *d, const char *f, ...)
{
va_list ap;
va_start(ap, f);
if (d) {
fprintf(stderr, "[%02x:%02x.%d] ", pci_dev_bus_num(d),
PCI_SLOT(d->devfn), PCI_FUNC(d->devfn));
}
vfprintf(stderr, f, ap);
va_end(ap);
}
/* Config Space */
static int xen_pt_pci_config_access_check(PCIDevice *d, uint32_t addr, int len)
{
/* check offset range */
if (addr > 0xFF) {
XEN_PT_ERR(d, "Failed to access register with offset exceeding 0xFF. "
"(addr: 0x%02x, len: %d)\n", addr, len);
return -1;
}
/* check read size */
if ((len != 1) && (len != 2) && (len != 4)) {
XEN_PT_ERR(d, "Failed to access register with invalid access length. "
"(addr: 0x%02x, len: %d)\n", addr, len);
return -1;
}
/* check offset alignment */
if (addr & (len - 1)) {
XEN_PT_ERR(d, "Failed to access register with invalid access size "
"alignment. (addr: 0x%02x, len: %d)\n", addr, len);
return -1;
}
return 0;
}
int xen_pt_bar_offset_to_index(uint32_t offset)
{
int index = 0;
/* check Exp ROM BAR */
if (offset == PCI_ROM_ADDRESS) {
return PCI_ROM_SLOT;
}
/* calculate BAR index */
index = (offset - PCI_BASE_ADDRESS_0) >> 2;
if (index >= PCI_NUM_REGIONS) {
return -1;
}
return index;
}
static uint32_t xen_pt_pci_read_config(PCIDevice *d, uint32_t addr, int len)
{
XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
uint32_t val = 0;
XenPTRegGroup *reg_grp_entry = NULL;
XenPTReg *reg_entry = NULL;
int rc = 0;
int emul_len = 0;
uint32_t find_addr = addr;
if (xen_pt_pci_config_access_check(d, addr, len)) {
goto exit;
}
/* find register group entry */
reg_grp_entry = xen_pt_find_reg_grp(s, addr);
if (reg_grp_entry) {
/* check 0-Hardwired register group */
if (reg_grp_entry->reg_grp->grp_type == XEN_PT_GRP_TYPE_HARDWIRED) {
/* no need to emulate, just return 0 */
val = 0;
goto exit;
}
}
/* read I/O device register value */
rc = xen_host_pci_get_block(&s->real_device, addr, (uint8_t *)&val, len);
if (rc < 0) {
XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc);
memset(&val, 0xff, len);
}
/* just return the I/O device register value for
* passthrough type register group */
if (reg_grp_entry == NULL) {
goto exit;
}
/* adjust the read value to appropriate CFC-CFF window */
val <<= (addr & 3) << 3;
emul_len = len;
/* loop around the guest requested size */
while (emul_len > 0) {
/* find register entry to be emulated */
reg_entry = xen_pt_find_reg(reg_grp_entry, find_addr);
if (reg_entry) {
XenPTRegInfo *reg = reg_entry->reg;
uint32_t real_offset = reg_grp_entry->base_offset + reg->offset;
uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3);
uint8_t *ptr_val = NULL;
valid_mask <<= (find_addr - real_offset) << 3;
ptr_val = (uint8_t *)&val + (real_offset & 3);
/* do emulation based on register size */
switch (reg->size) {
case 1:
if (reg->u.b.read) {
rc = reg->u.b.read(s, reg_entry, ptr_val, valid_mask);
}
break;
case 2:
if (reg->u.w.read) {
rc = reg->u.w.read(s, reg_entry,
(uint16_t *)ptr_val, valid_mask);
}
break;
case 4:
if (reg->u.dw.read) {
rc = reg->u.dw.read(s, reg_entry,
(uint32_t *)ptr_val, valid_mask);
}
break;
}
if (rc < 0) {
xen_shutdown_fatal_error("Internal error: Invalid read "
"emulation. (%s, rc: %d)\n",
__func__, rc);
return 0;
}
/* calculate next address to find */
emul_len -= reg->size;
if (emul_len > 0) {
find_addr = real_offset + reg->size;
}
} else {
/* nothing to do with passthrough type register,
* continue to find next byte */
emul_len--;
find_addr++;
}
}
/* need to shift back before returning them to pci bus emulator */
val >>= ((addr & 3) << 3);
exit:
XEN_PT_LOG_CONFIG(d, addr, val, len);
return val;
}
static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr,
uint32_t val, int len)
{
XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
int index = 0;
XenPTRegGroup *reg_grp_entry = NULL;
int rc = 0;
uint32_t read_val = 0, wb_mask;
int emul_len = 0;
XenPTReg *reg_entry = NULL;
uint32_t find_addr = addr;
XenPTRegInfo *reg = NULL;
bool wp_flag = false;
if (xen_pt_pci_config_access_check(d, addr, len)) {
return;
}
XEN_PT_LOG_CONFIG(d, addr, val, len);
/* check unused BAR register */
index = xen_pt_bar_offset_to_index(addr);
if ((index >= 0) && (val != 0)) {
uint32_t chk = val;
if (index == PCI_ROM_SLOT)
chk |= (uint32_t)~PCI_ROM_ADDRESS_MASK;
if ((chk != XEN_PT_BAR_ALLF) &&
(s->bases[index].bar_flag == XEN_PT_BAR_FLAG_UNUSED)) {
XEN_PT_WARN(d, "Guest attempt to set address to unused "
"Base Address Register. (addr: 0x%02x, len: %d)\n",
addr, len);
}
}
/* find register group entry */
reg_grp_entry = xen_pt_find_reg_grp(s, addr);
if (reg_grp_entry) {
/* check 0-Hardwired register group */
if (reg_grp_entry->reg_grp->grp_type == XEN_PT_GRP_TYPE_HARDWIRED) {
/* ignore silently */
XEN_PT_WARN(d, "Access to 0-Hardwired register. "
"(addr: 0x%02x, len: %d)\n", addr, len);
return;
}
}
rc = xen_host_pci_get_block(&s->real_device, addr,
(uint8_t *)&read_val, len);
if (rc < 0) {
XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc);
memset(&read_val, 0xff, len);
wb_mask = 0;
} else {
wb_mask = 0xFFFFFFFF >> ((4 - len) << 3);
}
/* pass directly to the real device for passthrough type register group */
if (reg_grp_entry == NULL) {
if (!s->permissive) {
wb_mask = 0;
wp_flag = true;
}
goto out;
}
memory_region_transaction_begin();
pci_default_write_config(d, addr, val, len);
/* adjust the read and write value to appropriate CFC-CFF window */
read_val <<= (addr & 3) << 3;
val <<= (addr & 3) << 3;
emul_len = len;
/* loop around the guest requested size */
while (emul_len > 0) {
/* find register entry to be emulated */
reg_entry = xen_pt_find_reg(reg_grp_entry, find_addr);
if (reg_entry) {
reg = reg_entry->reg;
uint32_t real_offset = reg_grp_entry->base_offset + reg->offset;
uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3);
uint8_t *ptr_val = NULL;
uint32_t wp_mask = reg->emu_mask | reg->ro_mask;
valid_mask <<= (find_addr - real_offset) << 3;
ptr_val = (uint8_t *)&val + (real_offset & 3);
if (!s->permissive) {
wp_mask |= reg->res_mask;
}
if (wp_mask == (0xFFFFFFFF >> ((4 - reg->size) << 3))) {
wb_mask &= ~((wp_mask >> ((find_addr - real_offset) << 3))
<< ((len - emul_len) << 3));
}
/* do emulation based on register size */
switch (reg->size) {
case 1:
if (reg->u.b.write) {
rc = reg->u.b.write(s, reg_entry, ptr_val,
read_val >> ((real_offset & 3) << 3),
valid_mask);
}
break;
case 2:
if (reg->u.w.write) {
rc = reg->u.w.write(s, reg_entry, (uint16_t *)ptr_val,
(read_val >> ((real_offset & 3) << 3)),
valid_mask);
}
break;
case 4:
if (reg->u.dw.write) {
rc = reg->u.dw.write(s, reg_entry, (uint32_t *)ptr_val,
(read_val >> ((real_offset & 3) << 3)),
valid_mask);
}
break;
}
if (rc < 0) {
xen_shutdown_fatal_error("Internal error: Invalid write"
" emulation. (%s, rc: %d)\n",
__func__, rc);
return;
}
/* calculate next address to find */
emul_len -= reg->size;
if (emul_len > 0) {
find_addr = real_offset + reg->size;
}
} else {
/* nothing to do with passthrough type register,
* continue to find next byte */
if (!s->permissive) {
wb_mask &= ~(0xff << ((len - emul_len) << 3));
/* Unused BARs will make it here, but we don't want to issue
* warnings for writes to them (bogus writes get dealt with
* above).
*/
if (index < 0) {
wp_flag = true;
}
}
emul_len--;
find_addr++;
}
}
/* need to shift back before passing them to xen_host_pci_set_block. */
val >>= (addr & 3) << 3;
memory_region_transaction_commit();
out:
if (wp_flag && !s->permissive_warned) {
s->permissive_warned = true;
xen_pt_log(d, "Write-back to unknown field 0x%02x (partially) inhibited (0x%0*x)\n",
addr, len * 2, wb_mask);
xen_pt_log(d, "If the device doesn't work, try enabling permissive mode\n");
xen_pt_log(d, "(unsafe) and if it helps report the problem to xen-devel\n");
}
for (index = 0; wb_mask; index += len) {
/* unknown regs are passed through */
while (!(wb_mask & 0xff)) {
index++;
wb_mask >>= 8;
}
len = 0;
do {
len++;
wb_mask >>= 8;
} while (wb_mask & 0xff);
rc = xen_host_pci_set_block(&s->real_device, addr + index,
(uint8_t *)&val + index, len);
if (rc < 0) {
XEN_PT_ERR(d, "xen_host_pci_set_block failed. return value: %d.\n", rc);
}
}
}
/* register regions */
static uint64_t xen_pt_bar_read(void *o, hwaddr addr,
unsigned size)
{
PCIDevice *d = o;
/* if this function is called, that probably means that there is a
* misconfiguration of the IOMMU. */
XEN_PT_ERR(d, "Should not read BAR through QEMU. @0x"HWADDR_FMT_plx"\n",
addr);
return 0;
}
static void xen_pt_bar_write(void *o, hwaddr addr, uint64_t val,
unsigned size)
{
PCIDevice *d = o;
/* Same comment as xen_pt_bar_read function */
XEN_PT_ERR(d, "Should not write BAR through QEMU. @0x"HWADDR_FMT_plx"\n",
addr);
}
static const MemoryRegionOps ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
.read = xen_pt_bar_read,
.write = xen_pt_bar_write,
};
static int xen_pt_register_regions(XenPCIPassthroughState *s, uint16_t *cmd)
{
int i = 0;
XenHostPCIDevice *d = &s->real_device;
/* Register PIO/MMIO BARs */
for (i = 0; i < PCI_ROM_SLOT; i++) {
XenHostPCIIORegion *r = &d->io_regions[i];
uint8_t type;
if (r->base_addr == 0 || r->size == 0) {
continue;
}
s->bases[i].access.u = r->base_addr;
if (r->type & XEN_HOST_PCI_REGION_TYPE_IO) {
type = PCI_BASE_ADDRESS_SPACE_IO;
*cmd |= PCI_COMMAND_IO;
} else {
type = PCI_BASE_ADDRESS_SPACE_MEMORY;
if (r->type & XEN_HOST_PCI_REGION_TYPE_PREFETCH) {
type |= PCI_BASE_ADDRESS_MEM_PREFETCH;
}
if (r->type & XEN_HOST_PCI_REGION_TYPE_MEM_64) {
type |= PCI_BASE_ADDRESS_MEM_TYPE_64;
}
*cmd |= PCI_COMMAND_MEMORY;
}
memory_region_init_io(&s->bar[i], OBJECT(s), &ops, &s->dev,
"xen-pci-pt-bar", r->size);
pci_register_bar(&s->dev, i, type, &s->bar[i]);
XEN_PT_LOG(&s->dev, "IO region %i registered (size=0x%08"PRIx64
" base_addr=0x%08"PRIx64" type: 0x%x)\n",
i, r->size, r->base_addr, type);
}
/* Register expansion ROM address */
if (d->rom.base_addr && d->rom.size) {
uint32_t bar_data = 0;
/* Re-set BAR reported by OS, otherwise ROM can't be read. */
if (xen_host_pci_get_long(d, PCI_ROM_ADDRESS, &bar_data)) {
return 0;
}
if ((bar_data & PCI_ROM_ADDRESS_MASK) == 0) {
bar_data |= d->rom.base_addr & PCI_ROM_ADDRESS_MASK;
xen_host_pci_set_long(d, PCI_ROM_ADDRESS, bar_data);
}
s->bases[PCI_ROM_SLOT].access.maddr = d->rom.base_addr;
memory_region_init_io(&s->rom, OBJECT(s), &ops, &s->dev,
"xen-pci-pt-rom", d->rom.size);
pci_register_bar(&s->dev, PCI_ROM_SLOT, PCI_BASE_ADDRESS_MEM_PREFETCH,
&s->rom);
XEN_PT_LOG(&s->dev, "Expansion ROM registered (size=0x%08"PRIx64
" base_addr=0x%08"PRIx64")\n",
d->rom.size, d->rom.base_addr);
}
xen_pt_register_vga_regions(d);
return 0;
}
/* region mapping */
static int xen_pt_bar_from_region(XenPCIPassthroughState *s, MemoryRegion *mr)
{
int i = 0;
for (i = 0; i < PCI_NUM_REGIONS - 1; i++) {
if (mr == &s->bar[i]) {
return i;
}
}
if (mr == &s->rom) {
return PCI_ROM_SLOT;
}
return -1;
}
/*
* This function checks if an io_region overlaps an io_region from another
* device. The io_region to check is provided with (addr, size and type)
* A callback can be provided and will be called for every region that is
* overlapped.
* The return value indicates if the region is overlappsed */
struct CheckBarArgs {
XenPCIPassthroughState *s;
pcibus_t addr;
pcibus_t size;
uint8_t type;
bool rc;
};
static void xen_pt_check_bar_overlap(PCIBus *bus, PCIDevice *d, void *opaque)
{
struct CheckBarArgs *arg = opaque;
XenPCIPassthroughState *s = arg->s;
uint8_t type = arg->type;
int i;
if (d->devfn == s->dev.devfn) {
return;
}
/* xxx: This ignores bridges. */
for (i = 0; i < PCI_NUM_REGIONS; i++) {
const PCIIORegion *r = &d->io_regions[i];
if (!r->size) {
continue;
}
if ((type & PCI_BASE_ADDRESS_SPACE_IO)
!= (r->type & PCI_BASE_ADDRESS_SPACE_IO)) {
continue;
}
if (ranges_overlap(arg->addr, arg->size, r->addr, r->size)) {
XEN_PT_WARN(&s->dev,
"Overlapped to device [%02x:%02x.%d] Region: %i"
" (addr: 0x%"FMT_PCIBUS", len: 0x%"FMT_PCIBUS")\n",
pci_bus_num(bus), PCI_SLOT(d->devfn),
PCI_FUNC(d->devfn), i, r->addr, r->size);
arg->rc = true;
}
}
}
static void xen_pt_region_update(XenPCIPassthroughState *s,
MemoryRegionSection *sec, bool adding)
{
PCIDevice *d = &s->dev;
MemoryRegion *mr = sec->mr;
int bar = -1;
int rc;
int op = adding ? DPCI_ADD_MAPPING : DPCI_REMOVE_MAPPING;
struct CheckBarArgs args = {
.s = s,
.addr = sec->offset_within_address_space,
.size = int128_get64(sec->size),
.rc = false,
};
bar = xen_pt_bar_from_region(s, mr);
if (bar == -1 && (!s->msix || &s->msix->mmio != mr)) {
return;
}
if (s->msix && &s->msix->mmio == mr) {
if (adding) {
s->msix->mmio_base_addr = sec->offset_within_address_space;
rc = xen_pt_msix_update_remap(s, s->msix->bar_index);
}
return;
}
args.type = d->io_regions[bar].type;
pci_for_each_device_under_bus(pci_get_bus(d),
xen_pt_check_bar_overlap, &args);
if (args.rc) {
XEN_PT_WARN(d, "Region: %d (addr: 0x%"FMT_PCIBUS
", len: 0x%"FMT_PCIBUS") is overlapped.\n",
bar, sec->offset_within_address_space,
int128_get64(sec->size));
}
if (d->io_regions[bar].type & PCI_BASE_ADDRESS_SPACE_IO) {
uint32_t guest_port = sec->offset_within_address_space;
uint32_t machine_port = s->bases[bar].access.pio_base;
uint32_t size = int128_get64(sec->size);
rc = xc_domain_ioport_mapping(xen_xc, xen_domid,
guest_port, machine_port, size,
op);
if (rc) {
XEN_PT_ERR(d, "%s ioport mapping failed! (err: %i)\n",
adding ? "create new" : "remove old", errno);
}
} else {
pcibus_t guest_addr = sec->offset_within_address_space;
pcibus_t machine_addr = s->bases[bar].access.maddr
+ sec->offset_within_region;
pcibus_t size = int128_get64(sec->size);
rc = xc_domain_memory_mapping(xen_xc, xen_domid,
XEN_PFN(guest_addr + XC_PAGE_SIZE - 1),
XEN_PFN(machine_addr + XC_PAGE_SIZE - 1),
XEN_PFN(size + XC_PAGE_SIZE - 1),
op);
if (rc) {
XEN_PT_ERR(d, "%s mem mapping failed! (err: %i)\n",
adding ? "create new" : "remove old", errno);
}
}
}
static void xen_pt_region_add(MemoryListener *l, MemoryRegionSection *sec)
{
XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState,
memory_listener);
memory_region_ref(sec->mr);
xen_pt_region_update(s, sec, true);
}
static void xen_pt_region_del(MemoryListener *l, MemoryRegionSection *sec)
{
XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState,
memory_listener);
xen_pt_region_update(s, sec, false);
memory_region_unref(sec->mr);
}
static void xen_pt_io_region_add(MemoryListener *l, MemoryRegionSection *sec)
{
XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState,
io_listener);
memory_region_ref(sec->mr);
xen_pt_region_update(s, sec, true);
}
static void xen_pt_io_region_del(MemoryListener *l, MemoryRegionSection *sec)
{
XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState,
io_listener);
xen_pt_region_update(s, sec, false);
memory_region_unref(sec->mr);
}
static const MemoryListener xen_pt_memory_listener = {
.name = "xen-pt-mem",
.region_add = xen_pt_region_add,
.region_del = xen_pt_region_del,
.priority = MEMORY_LISTENER_PRIORITY_ACCEL,
};
static const MemoryListener xen_pt_io_listener = {
.name = "xen-pt-io",
.region_add = xen_pt_io_region_add,
.region_del = xen_pt_io_region_del,
.priority = MEMORY_LISTENER_PRIORITY_ACCEL,
};
/* destroy. */
static void xen_pt_destroy(PCIDevice *d) {
XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
XenHostPCIDevice *host_dev = &s->real_device;
uint8_t machine_irq = s->machine_irq;
uint8_t intx;
int rc;
if (machine_irq && !xen_host_pci_device_closed(&s->real_device)) {
intx = xen_pt_pci_intx(s);
rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq,
PT_IRQ_TYPE_PCI,
pci_dev_bus_num(d),
PCI_SLOT(s->dev.devfn),
intx,
0 /* isa_irq */);
if (rc < 0) {
XEN_PT_ERR(d, "unbinding of interrupt INT%c failed."
" (machine irq: %i, err: %d)"
" But bravely continuing on..\n",
'a' + intx, machine_irq, errno);
}
}
/* N.B. xen_pt_config_delete takes care of freeing them. */
if (s->msi) {
xen_pt_msi_disable(s);
}
if (s->msix) {
xen_pt_msix_disable(s);
}
if (machine_irq) {
xen_pt_mapped_machine_irq[machine_irq]--;
if (xen_pt_mapped_machine_irq[machine_irq] == 0) {
rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq);
if (rc < 0) {
XEN_PT_ERR(d, "unmapping of interrupt %i failed. (err: %d)"
" But bravely continuing on..\n",
machine_irq, errno);
}
}
s->machine_irq = 0;
}
/* delete all emulated config registers */
xen_pt_config_delete(s);
xen_pt_unregister_vga_regions(host_dev);
if (s->listener_set) {
memory_listener_unregister(&s->memory_listener);
memory_listener_unregister(&s->io_listener);
s->listener_set = false;
}
if (!xen_host_pci_device_closed(&s->real_device)) {
xen_host_pci_device_put(&s->real_device);
}
}
/* init */
static void xen_pt_realize(PCIDevice *d, Error **errp)
{
xen: Use ERRP_GUARD() If we want to check error after errp-function call, we need to introduce local_err and then propagate it to errp. Instead, use the ERRP_GUARD() macro, benefits are: 1. No need of explicit error_propagate call 2. No need of explicit local_err variable: use errp directly 3. ERRP_GUARD() leaves errp as is if it's not NULL or &error_fatal, this means that we don't break error_abort (we'll abort on error_set, not on error_propagate) If we want to add some info to errp (by error_prepend() or error_append_hint()), we must use the ERRP_GUARD() macro. Otherwise, this info will not be added when errp == &error_fatal (the program will exit prior to the error_append_hint() or error_prepend() call). No such cases are being fixed here. This commit is generated by command sed -n '/^X86 Xen CPUs$/,/^$/{s/^F: //p}' MAINTAINERS | \ xargs git ls-files | grep '\.[hc]$' | \ xargs spatch \ --sp-file scripts/coccinelle/errp-guard.cocci \ --macro-file scripts/cocci-macro-file.h \ --in-place --no-show-diff --max-width 80 Reported-by: Kevin Wolf <kwolf@redhat.com> Reported-by: Greg Kurz <groug@kaod.org> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> [Commit message tweaked] Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-Id: <20200707165037.1026246-9-armbru@redhat.com> [ERRP_AUTO_PROPAGATE() renamed to ERRP_GUARD(), and auto-propagated-errp.cocci to errp-guard.cocci. Commit message tweaked again.]
2020-07-07 18:50:37 +02:00
ERRP_GUARD();
XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
int i, rc = 0;
uint8_t machine_irq = 0, scratch;
uint16_t cmd = 0;
int pirq = XEN_PT_UNASSIGNED_PIRQ;
/* register real device */
XEN_PT_LOG(d, "Assigning real physical device %02x:%02x.%d"
" to devfn 0x%x\n",
s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function,
s->dev.devfn);
s->is_virtfn = s->real_device.is_virtfn;
if (s->is_virtfn) {
XEN_PT_LOG(d, "%04x:%02x:%02x.%d is a SR-IOV Virtual Function\n",
s->real_device.domain, s->real_device.bus,
s->real_device.dev, s->real_device.func);
}
/* Initialize virtualized PCI configuration (Extended 256 Bytes) */
memset(d->config, 0, PCI_CONFIG_SPACE_SIZE);
s->memory_listener = xen_pt_memory_listener;
s->io_listener = xen_pt_io_listener;
/* Setup VGA bios for passthrough GFX */
xen/pt: reserve PCI slot 2 for Intel igd-passthru Intel specifies that the Intel IGD must occupy slot 2 on the PCI bus, as noted in docs/igd-assign.txt in the Qemu source code. Currently, when the xl toolstack is used to configure a Xen HVM guest with Intel IGD passthrough to the guest with the Qemu upstream device model, a Qemu emulated PCI device will occupy slot 2 and the Intel IGD will occupy a different slot. This problem often prevents the guest from booting. The only available workarounds are not good: Configure Xen HVM guests to use the old and no longer maintained Qemu traditional device model available from xenbits.xen.org which does reserve slot 2 for the Intel IGD or use the "pc" machine type instead of the "xenfv" machine type and add the xen platform device at slot 3 using a command line option instead of patching qemu to fix the "xenfv" machine type directly. The second workaround causes some degredation in startup performance such as a longer boot time and reduced resolution of the grub menu that is displayed on the monitor. This patch avoids that reduced startup performance when using the Qemu upstream device model for Xen HVM guests configured with the igd-passthru=on option. To implement this feature in the Qemu upstream device model for Xen HVM guests, introduce the following new functions, types, and macros: * XEN_PT_DEVICE_CLASS declaration, based on the existing TYPE_XEN_PT_DEVICE * XEN_PT_DEVICE_GET_CLASS macro helper function for XEN_PT_DEVICE_CLASS * typedef XenPTQdevRealize function pointer * XEN_PCI_IGD_SLOT_MASK, the value of slot_reserved_mask to reserve slot 2 * xen_igd_reserve_slot and xen_igd_clear_slot functions Michael Tsirkin: * Introduce XEN_PCI_IGD_DOMAIN, XEN_PCI_IGD_BUS, XEN_PCI_IGD_DEV, and XEN_PCI_IGD_FN - use them to compute the value of XEN_PCI_IGD_SLOT_MASK The new xen_igd_reserve_slot function uses the existing slot_reserved_mask member of PCIBus to reserve PCI slot 2 for Xen HVM guests configured using the xl toolstack with the gfx_passthru option enabled, which sets the igd-passthru=on option to Qemu for the Xen HVM machine type. The new xen_igd_reserve_slot function also needs to be implemented in hw/xen/xen_pt_stub.c to prevent FTBFS during the link stage for the case when Qemu is configured with --enable-xen and --disable-xen-pci-passthrough, in which case it does nothing. The new xen_igd_clear_slot function overrides qdev->realize of the parent PCI device class to enable the Intel IGD to occupy slot 2 on the PCI bus since slot 2 was reserved by xen_igd_reserve_slot when the PCI bus was created in hw/i386/pc_piix.c for the case when igd-passthru=on. Move the call to xen_host_pci_device_get, and the associated error handling, from xen_pt_realize to the new xen_igd_clear_slot function to initialize the device class and vendor values which enables the checks for the Intel IGD to succeed. The verification that the host device is an Intel IGD to be passed through is done by checking the domain, bus, slot, and function values as well as by checking that gfx_passthru is enabled, the device class is VGA, and the device vendor in Intel. Signed-off-by: Chuck Zmudzinski <brchuckz@aol.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> Message-Id: <b1b4a21fe9a600b1322742dda55a40e9961daa57.1674346505.git.brchuckz@aol.com> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
2023-01-22 01:57:02 +01:00
if ((s->real_device.domain == XEN_PCI_IGD_DOMAIN) &&
(s->real_device.bus == XEN_PCI_IGD_BUS) &&
(s->real_device.dev == XEN_PCI_IGD_DEV) &&
(s->real_device.func == XEN_PCI_IGD_FN)) {
if (!is_igd_vga_passthrough(&s->real_device)) {
error_setg(errp, "Need to enable igd-passthru if you're trying"
" to passthrough IGD GFX");
xen_host_pci_device_put(&s->real_device);
return;
}
xen: Use ERRP_GUARD() If we want to check error after errp-function call, we need to introduce local_err and then propagate it to errp. Instead, use the ERRP_GUARD() macro, benefits are: 1. No need of explicit error_propagate call 2. No need of explicit local_err variable: use errp directly 3. ERRP_GUARD() leaves errp as is if it's not NULL or &error_fatal, this means that we don't break error_abort (we'll abort on error_set, not on error_propagate) If we want to add some info to errp (by error_prepend() or error_append_hint()), we must use the ERRP_GUARD() macro. Otherwise, this info will not be added when errp == &error_fatal (the program will exit prior to the error_append_hint() or error_prepend() call). No such cases are being fixed here. This commit is generated by command sed -n '/^X86 Xen CPUs$/,/^$/{s/^F: //p}' MAINTAINERS | \ xargs git ls-files | grep '\.[hc]$' | \ xargs spatch \ --sp-file scripts/coccinelle/errp-guard.cocci \ --macro-file scripts/cocci-macro-file.h \ --in-place --no-show-diff --max-width 80 Reported-by: Kevin Wolf <kwolf@redhat.com> Reported-by: Greg Kurz <groug@kaod.org> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> [Commit message tweaked] Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-Id: <20200707165037.1026246-9-armbru@redhat.com> [ERRP_AUTO_PROPAGATE() renamed to ERRP_GUARD(), and auto-propagated-errp.cocci to errp-guard.cocci. Commit message tweaked again.]
2020-07-07 18:50:37 +02:00
xen_pt_setup_vga(s, &s->real_device, errp);
if (*errp) {
error_append_hint(errp, "Setup VGA BIOS of passthrough"
" GFX failed");
xen_host_pci_device_put(&s->real_device);
return;
}
/* Register ISA bridge for passthrough GFX. */
xen_igd_passthrough_isa_bridge_create(s, &s->real_device);
}
/* Handle real device's MMIO/PIO BARs */
xen_pt_register_regions(s, &cmd);
/* reinitialize each config register to be emulated */
xen: Use ERRP_GUARD() If we want to check error after errp-function call, we need to introduce local_err and then propagate it to errp. Instead, use the ERRP_GUARD() macro, benefits are: 1. No need of explicit error_propagate call 2. No need of explicit local_err variable: use errp directly 3. ERRP_GUARD() leaves errp as is if it's not NULL or &error_fatal, this means that we don't break error_abort (we'll abort on error_set, not on error_propagate) If we want to add some info to errp (by error_prepend() or error_append_hint()), we must use the ERRP_GUARD() macro. Otherwise, this info will not be added when errp == &error_fatal (the program will exit prior to the error_append_hint() or error_prepend() call). No such cases are being fixed here. This commit is generated by command sed -n '/^X86 Xen CPUs$/,/^$/{s/^F: //p}' MAINTAINERS | \ xargs git ls-files | grep '\.[hc]$' | \ xargs spatch \ --sp-file scripts/coccinelle/errp-guard.cocci \ --macro-file scripts/cocci-macro-file.h \ --in-place --no-show-diff --max-width 80 Reported-by: Kevin Wolf <kwolf@redhat.com> Reported-by: Greg Kurz <groug@kaod.org> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> [Commit message tweaked] Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-Id: <20200707165037.1026246-9-armbru@redhat.com> [ERRP_AUTO_PROPAGATE() renamed to ERRP_GUARD(), and auto-propagated-errp.cocci to errp-guard.cocci. Commit message tweaked again.]
2020-07-07 18:50:37 +02:00
xen_pt_config_init(s, errp);
if (*errp) {
error_append_hint(errp, "PCI Config space initialisation failed");
rc = -1;
goto err_out;
}
/* Bind interrupt */
rc = xen_host_pci_get_byte(&s->real_device, PCI_INTERRUPT_PIN, &scratch);
if (rc) {
error_setg_errno(errp, errno, "Failed to read PCI_INTERRUPT_PIN");
goto err_out;
}
if (!scratch) {
XEN_PT_LOG(d, "no pin interrupt\n");
goto out;
}
machine_irq = s->real_device.irq;
if (machine_irq == 0) {
XEN_PT_LOG(d, "machine irq is 0\n");
cmd |= PCI_COMMAND_INTX_DISABLE;
goto out;
}
rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq);
if (rc < 0) {
XEN_PT_ERR(d, "Mapping machine irq %u to pirq %i failed, (err: %d)\n",
machine_irq, pirq, errno);
/* Disable PCI intx assertion (turn on bit10 of devctl) */
cmd |= PCI_COMMAND_INTX_DISABLE;
machine_irq = 0;
s->machine_irq = 0;
} else {
machine_irq = pirq;
s->machine_irq = pirq;
xen_pt_mapped_machine_irq[machine_irq]++;
}
/* bind machine_irq to device */
if (machine_irq != 0) {
uint8_t e_intx = xen_pt_pci_intx(s);
rc = xc_domain_bind_pt_pci_irq(xen_xc, xen_domid, machine_irq,
pci_dev_bus_num(d),
PCI_SLOT(d->devfn),
e_intx);
if (rc < 0) {
XEN_PT_ERR(d, "Binding of interrupt %i failed! (err: %d)\n",
e_intx, errno);
/* Disable PCI intx assertion (turn on bit10 of devctl) */
cmd |= PCI_COMMAND_INTX_DISABLE;
xen_pt_mapped_machine_irq[machine_irq]--;
if (xen_pt_mapped_machine_irq[machine_irq] == 0) {
if (xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq)) {
XEN_PT_ERR(d, "Unmapping of machine interrupt %i failed!"
" (err: %d)\n", machine_irq, errno);
}
}
s->machine_irq = 0;
}
}
out:
if (cmd) {
uint16_t val;
rc = xen_host_pci_get_word(&s->real_device, PCI_COMMAND, &val);
if (rc) {
error_setg_errno(errp, errno, "Failed to read PCI_COMMAND");
goto err_out;
} else {
val |= cmd;
rc = xen_host_pci_set_word(&s->real_device, PCI_COMMAND, val);
if (rc) {
error_setg_errno(errp, errno, "Failed to write PCI_COMMAND"
" val = 0x%x", val);
goto err_out;
}
}
}
memory_listener_register(&s->memory_listener, &address_space_memory);
memory_listener_register(&s->io_listener, &address_space_io);
s->listener_set = true;
XEN_PT_LOG(d,
"Real physical device %02x:%02x.%d registered successfully\n",
s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function);
return;
err_out:
for (i = 0; i < PCI_ROM_SLOT; i++) {
object_unparent(OBJECT(&s->bar[i]));
}
object_unparent(OBJECT(&s->rom));
xen_pt_destroy(d);
assert(rc);
}
static void xen_pt_unregister_device(PCIDevice *d)
{
xen_pt_destroy(d);
}
static Property xen_pci_passthrough_properties[] = {
DEFINE_PROP_PCI_HOST_DEVADDR("hostaddr", XenPCIPassthroughState, hostaddr),
DEFINE_PROP_BOOL("permissive", XenPCIPassthroughState, permissive, false),
DEFINE_PROP_END_OF_LIST(),
};
static void xen_pci_passthrough_instance_init(Object *obj)
{
/* QEMU_PCI_CAP_EXPRESS initialization does not depend on QEMU command
* line, therefore, no need to wait to realize like other devices */
PCI_DEVICE(obj)->cap_present |= QEMU_PCI_CAP_EXPRESS;
}
xen/pt: reserve PCI slot 2 for Intel igd-passthru Intel specifies that the Intel IGD must occupy slot 2 on the PCI bus, as noted in docs/igd-assign.txt in the Qemu source code. Currently, when the xl toolstack is used to configure a Xen HVM guest with Intel IGD passthrough to the guest with the Qemu upstream device model, a Qemu emulated PCI device will occupy slot 2 and the Intel IGD will occupy a different slot. This problem often prevents the guest from booting. The only available workarounds are not good: Configure Xen HVM guests to use the old and no longer maintained Qemu traditional device model available from xenbits.xen.org which does reserve slot 2 for the Intel IGD or use the "pc" machine type instead of the "xenfv" machine type and add the xen platform device at slot 3 using a command line option instead of patching qemu to fix the "xenfv" machine type directly. The second workaround causes some degredation in startup performance such as a longer boot time and reduced resolution of the grub menu that is displayed on the monitor. This patch avoids that reduced startup performance when using the Qemu upstream device model for Xen HVM guests configured with the igd-passthru=on option. To implement this feature in the Qemu upstream device model for Xen HVM guests, introduce the following new functions, types, and macros: * XEN_PT_DEVICE_CLASS declaration, based on the existing TYPE_XEN_PT_DEVICE * XEN_PT_DEVICE_GET_CLASS macro helper function for XEN_PT_DEVICE_CLASS * typedef XenPTQdevRealize function pointer * XEN_PCI_IGD_SLOT_MASK, the value of slot_reserved_mask to reserve slot 2 * xen_igd_reserve_slot and xen_igd_clear_slot functions Michael Tsirkin: * Introduce XEN_PCI_IGD_DOMAIN, XEN_PCI_IGD_BUS, XEN_PCI_IGD_DEV, and XEN_PCI_IGD_FN - use them to compute the value of XEN_PCI_IGD_SLOT_MASK The new xen_igd_reserve_slot function uses the existing slot_reserved_mask member of PCIBus to reserve PCI slot 2 for Xen HVM guests configured using the xl toolstack with the gfx_passthru option enabled, which sets the igd-passthru=on option to Qemu for the Xen HVM machine type. The new xen_igd_reserve_slot function also needs to be implemented in hw/xen/xen_pt_stub.c to prevent FTBFS during the link stage for the case when Qemu is configured with --enable-xen and --disable-xen-pci-passthrough, in which case it does nothing. The new xen_igd_clear_slot function overrides qdev->realize of the parent PCI device class to enable the Intel IGD to occupy slot 2 on the PCI bus since slot 2 was reserved by xen_igd_reserve_slot when the PCI bus was created in hw/i386/pc_piix.c for the case when igd-passthru=on. Move the call to xen_host_pci_device_get, and the associated error handling, from xen_pt_realize to the new xen_igd_clear_slot function to initialize the device class and vendor values which enables the checks for the Intel IGD to succeed. The verification that the host device is an Intel IGD to be passed through is done by checking the domain, bus, slot, and function values as well as by checking that gfx_passthru is enabled, the device class is VGA, and the device vendor in Intel. Signed-off-by: Chuck Zmudzinski <brchuckz@aol.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> Message-Id: <b1b4a21fe9a600b1322742dda55a40e9961daa57.1674346505.git.brchuckz@aol.com> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
2023-01-22 01:57:02 +01:00
void xen_igd_reserve_slot(PCIBus *pci_bus)
{
if (!xen_igd_gfx_pt_enabled()) {
return;
}
XEN_PT_LOG(0, "Reserving PCI slot 2 for IGD\n");
pci_bus_set_slot_reserved_mask(pci_bus, XEN_PCI_IGD_SLOT_MASK);
xen/pt: reserve PCI slot 2 for Intel igd-passthru Intel specifies that the Intel IGD must occupy slot 2 on the PCI bus, as noted in docs/igd-assign.txt in the Qemu source code. Currently, when the xl toolstack is used to configure a Xen HVM guest with Intel IGD passthrough to the guest with the Qemu upstream device model, a Qemu emulated PCI device will occupy slot 2 and the Intel IGD will occupy a different slot. This problem often prevents the guest from booting. The only available workarounds are not good: Configure Xen HVM guests to use the old and no longer maintained Qemu traditional device model available from xenbits.xen.org which does reserve slot 2 for the Intel IGD or use the "pc" machine type instead of the "xenfv" machine type and add the xen platform device at slot 3 using a command line option instead of patching qemu to fix the "xenfv" machine type directly. The second workaround causes some degredation in startup performance such as a longer boot time and reduced resolution of the grub menu that is displayed on the monitor. This patch avoids that reduced startup performance when using the Qemu upstream device model for Xen HVM guests configured with the igd-passthru=on option. To implement this feature in the Qemu upstream device model for Xen HVM guests, introduce the following new functions, types, and macros: * XEN_PT_DEVICE_CLASS declaration, based on the existing TYPE_XEN_PT_DEVICE * XEN_PT_DEVICE_GET_CLASS macro helper function for XEN_PT_DEVICE_CLASS * typedef XenPTQdevRealize function pointer * XEN_PCI_IGD_SLOT_MASK, the value of slot_reserved_mask to reserve slot 2 * xen_igd_reserve_slot and xen_igd_clear_slot functions Michael Tsirkin: * Introduce XEN_PCI_IGD_DOMAIN, XEN_PCI_IGD_BUS, XEN_PCI_IGD_DEV, and XEN_PCI_IGD_FN - use them to compute the value of XEN_PCI_IGD_SLOT_MASK The new xen_igd_reserve_slot function uses the existing slot_reserved_mask member of PCIBus to reserve PCI slot 2 for Xen HVM guests configured using the xl toolstack with the gfx_passthru option enabled, which sets the igd-passthru=on option to Qemu for the Xen HVM machine type. The new xen_igd_reserve_slot function also needs to be implemented in hw/xen/xen_pt_stub.c to prevent FTBFS during the link stage for the case when Qemu is configured with --enable-xen and --disable-xen-pci-passthrough, in which case it does nothing. The new xen_igd_clear_slot function overrides qdev->realize of the parent PCI device class to enable the Intel IGD to occupy slot 2 on the PCI bus since slot 2 was reserved by xen_igd_reserve_slot when the PCI bus was created in hw/i386/pc_piix.c for the case when igd-passthru=on. Move the call to xen_host_pci_device_get, and the associated error handling, from xen_pt_realize to the new xen_igd_clear_slot function to initialize the device class and vendor values which enables the checks for the Intel IGD to succeed. The verification that the host device is an Intel IGD to be passed through is done by checking the domain, bus, slot, and function values as well as by checking that gfx_passthru is enabled, the device class is VGA, and the device vendor in Intel. Signed-off-by: Chuck Zmudzinski <brchuckz@aol.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> Message-Id: <b1b4a21fe9a600b1322742dda55a40e9961daa57.1674346505.git.brchuckz@aol.com> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
2023-01-22 01:57:02 +01:00
}
static void xen_igd_clear_slot(DeviceState *qdev, Error **errp)
{
ERRP_GUARD();
PCIDevice *pci_dev = (PCIDevice *)qdev;
XenPCIPassthroughState *s = XEN_PT_DEVICE(pci_dev);
XenPTDeviceClass *xpdc = XEN_PT_DEVICE_GET_CLASS(s);
PCIBus *pci_bus = pci_get_bus(pci_dev);
xen_host_pci_device_get(&s->real_device,
s->hostaddr.domain, s->hostaddr.bus,
s->hostaddr.slot, s->hostaddr.function,
errp);
if (*errp) {
error_append_hint(errp, "Failed to \"open\" the real pci device");
return;
}
if (!(pci_bus_get_slot_reserved_mask(pci_bus) & XEN_PCI_IGD_SLOT_MASK)) {
xen/pt: reserve PCI slot 2 for Intel igd-passthru Intel specifies that the Intel IGD must occupy slot 2 on the PCI bus, as noted in docs/igd-assign.txt in the Qemu source code. Currently, when the xl toolstack is used to configure a Xen HVM guest with Intel IGD passthrough to the guest with the Qemu upstream device model, a Qemu emulated PCI device will occupy slot 2 and the Intel IGD will occupy a different slot. This problem often prevents the guest from booting. The only available workarounds are not good: Configure Xen HVM guests to use the old and no longer maintained Qemu traditional device model available from xenbits.xen.org which does reserve slot 2 for the Intel IGD or use the "pc" machine type instead of the "xenfv" machine type and add the xen platform device at slot 3 using a command line option instead of patching qemu to fix the "xenfv" machine type directly. The second workaround causes some degredation in startup performance such as a longer boot time and reduced resolution of the grub menu that is displayed on the monitor. This patch avoids that reduced startup performance when using the Qemu upstream device model for Xen HVM guests configured with the igd-passthru=on option. To implement this feature in the Qemu upstream device model for Xen HVM guests, introduce the following new functions, types, and macros: * XEN_PT_DEVICE_CLASS declaration, based on the existing TYPE_XEN_PT_DEVICE * XEN_PT_DEVICE_GET_CLASS macro helper function for XEN_PT_DEVICE_CLASS * typedef XenPTQdevRealize function pointer * XEN_PCI_IGD_SLOT_MASK, the value of slot_reserved_mask to reserve slot 2 * xen_igd_reserve_slot and xen_igd_clear_slot functions Michael Tsirkin: * Introduce XEN_PCI_IGD_DOMAIN, XEN_PCI_IGD_BUS, XEN_PCI_IGD_DEV, and XEN_PCI_IGD_FN - use them to compute the value of XEN_PCI_IGD_SLOT_MASK The new xen_igd_reserve_slot function uses the existing slot_reserved_mask member of PCIBus to reserve PCI slot 2 for Xen HVM guests configured using the xl toolstack with the gfx_passthru option enabled, which sets the igd-passthru=on option to Qemu for the Xen HVM machine type. The new xen_igd_reserve_slot function also needs to be implemented in hw/xen/xen_pt_stub.c to prevent FTBFS during the link stage for the case when Qemu is configured with --enable-xen and --disable-xen-pci-passthrough, in which case it does nothing. The new xen_igd_clear_slot function overrides qdev->realize of the parent PCI device class to enable the Intel IGD to occupy slot 2 on the PCI bus since slot 2 was reserved by xen_igd_reserve_slot when the PCI bus was created in hw/i386/pc_piix.c for the case when igd-passthru=on. Move the call to xen_host_pci_device_get, and the associated error handling, from xen_pt_realize to the new xen_igd_clear_slot function to initialize the device class and vendor values which enables the checks for the Intel IGD to succeed. The verification that the host device is an Intel IGD to be passed through is done by checking the domain, bus, slot, and function values as well as by checking that gfx_passthru is enabled, the device class is VGA, and the device vendor in Intel. Signed-off-by: Chuck Zmudzinski <brchuckz@aol.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> Message-Id: <b1b4a21fe9a600b1322742dda55a40e9961daa57.1674346505.git.brchuckz@aol.com> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
2023-01-22 01:57:02 +01:00
xpdc->pci_qdev_realize(qdev, errp);
return;
}
if (is_igd_vga_passthrough(&s->real_device) &&
s->real_device.domain == XEN_PCI_IGD_DOMAIN &&
s->real_device.bus == XEN_PCI_IGD_BUS &&
s->real_device.dev == XEN_PCI_IGD_DEV &&
s->real_device.func == XEN_PCI_IGD_FN &&
s->real_device.vendor_id == PCI_VENDOR_ID_INTEL) {
pci_bus_clear_slot_reserved_mask(pci_bus, XEN_PCI_IGD_SLOT_MASK);
xen/pt: reserve PCI slot 2 for Intel igd-passthru Intel specifies that the Intel IGD must occupy slot 2 on the PCI bus, as noted in docs/igd-assign.txt in the Qemu source code. Currently, when the xl toolstack is used to configure a Xen HVM guest with Intel IGD passthrough to the guest with the Qemu upstream device model, a Qemu emulated PCI device will occupy slot 2 and the Intel IGD will occupy a different slot. This problem often prevents the guest from booting. The only available workarounds are not good: Configure Xen HVM guests to use the old and no longer maintained Qemu traditional device model available from xenbits.xen.org which does reserve slot 2 for the Intel IGD or use the "pc" machine type instead of the "xenfv" machine type and add the xen platform device at slot 3 using a command line option instead of patching qemu to fix the "xenfv" machine type directly. The second workaround causes some degredation in startup performance such as a longer boot time and reduced resolution of the grub menu that is displayed on the monitor. This patch avoids that reduced startup performance when using the Qemu upstream device model for Xen HVM guests configured with the igd-passthru=on option. To implement this feature in the Qemu upstream device model for Xen HVM guests, introduce the following new functions, types, and macros: * XEN_PT_DEVICE_CLASS declaration, based on the existing TYPE_XEN_PT_DEVICE * XEN_PT_DEVICE_GET_CLASS macro helper function for XEN_PT_DEVICE_CLASS * typedef XenPTQdevRealize function pointer * XEN_PCI_IGD_SLOT_MASK, the value of slot_reserved_mask to reserve slot 2 * xen_igd_reserve_slot and xen_igd_clear_slot functions Michael Tsirkin: * Introduce XEN_PCI_IGD_DOMAIN, XEN_PCI_IGD_BUS, XEN_PCI_IGD_DEV, and XEN_PCI_IGD_FN - use them to compute the value of XEN_PCI_IGD_SLOT_MASK The new xen_igd_reserve_slot function uses the existing slot_reserved_mask member of PCIBus to reserve PCI slot 2 for Xen HVM guests configured using the xl toolstack with the gfx_passthru option enabled, which sets the igd-passthru=on option to Qemu for the Xen HVM machine type. The new xen_igd_reserve_slot function also needs to be implemented in hw/xen/xen_pt_stub.c to prevent FTBFS during the link stage for the case when Qemu is configured with --enable-xen and --disable-xen-pci-passthrough, in which case it does nothing. The new xen_igd_clear_slot function overrides qdev->realize of the parent PCI device class to enable the Intel IGD to occupy slot 2 on the PCI bus since slot 2 was reserved by xen_igd_reserve_slot when the PCI bus was created in hw/i386/pc_piix.c for the case when igd-passthru=on. Move the call to xen_host_pci_device_get, and the associated error handling, from xen_pt_realize to the new xen_igd_clear_slot function to initialize the device class and vendor values which enables the checks for the Intel IGD to succeed. The verification that the host device is an Intel IGD to be passed through is done by checking the domain, bus, slot, and function values as well as by checking that gfx_passthru is enabled, the device class is VGA, and the device vendor in Intel. Signed-off-by: Chuck Zmudzinski <brchuckz@aol.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> Message-Id: <b1b4a21fe9a600b1322742dda55a40e9961daa57.1674346505.git.brchuckz@aol.com> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
2023-01-22 01:57:02 +01:00
XEN_PT_LOG(pci_dev, "Intel IGD found, using slot 2\n");
}
xpdc->pci_qdev_realize(qdev, errp);
}
static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
xen/pt: reserve PCI slot 2 for Intel igd-passthru Intel specifies that the Intel IGD must occupy slot 2 on the PCI bus, as noted in docs/igd-assign.txt in the Qemu source code. Currently, when the xl toolstack is used to configure a Xen HVM guest with Intel IGD passthrough to the guest with the Qemu upstream device model, a Qemu emulated PCI device will occupy slot 2 and the Intel IGD will occupy a different slot. This problem often prevents the guest from booting. The only available workarounds are not good: Configure Xen HVM guests to use the old and no longer maintained Qemu traditional device model available from xenbits.xen.org which does reserve slot 2 for the Intel IGD or use the "pc" machine type instead of the "xenfv" machine type and add the xen platform device at slot 3 using a command line option instead of patching qemu to fix the "xenfv" machine type directly. The second workaround causes some degredation in startup performance such as a longer boot time and reduced resolution of the grub menu that is displayed on the monitor. This patch avoids that reduced startup performance when using the Qemu upstream device model for Xen HVM guests configured with the igd-passthru=on option. To implement this feature in the Qemu upstream device model for Xen HVM guests, introduce the following new functions, types, and macros: * XEN_PT_DEVICE_CLASS declaration, based on the existing TYPE_XEN_PT_DEVICE * XEN_PT_DEVICE_GET_CLASS macro helper function for XEN_PT_DEVICE_CLASS * typedef XenPTQdevRealize function pointer * XEN_PCI_IGD_SLOT_MASK, the value of slot_reserved_mask to reserve slot 2 * xen_igd_reserve_slot and xen_igd_clear_slot functions Michael Tsirkin: * Introduce XEN_PCI_IGD_DOMAIN, XEN_PCI_IGD_BUS, XEN_PCI_IGD_DEV, and XEN_PCI_IGD_FN - use them to compute the value of XEN_PCI_IGD_SLOT_MASK The new xen_igd_reserve_slot function uses the existing slot_reserved_mask member of PCIBus to reserve PCI slot 2 for Xen HVM guests configured using the xl toolstack with the gfx_passthru option enabled, which sets the igd-passthru=on option to Qemu for the Xen HVM machine type. The new xen_igd_reserve_slot function also needs to be implemented in hw/xen/xen_pt_stub.c to prevent FTBFS during the link stage for the case when Qemu is configured with --enable-xen and --disable-xen-pci-passthrough, in which case it does nothing. The new xen_igd_clear_slot function overrides qdev->realize of the parent PCI device class to enable the Intel IGD to occupy slot 2 on the PCI bus since slot 2 was reserved by xen_igd_reserve_slot when the PCI bus was created in hw/i386/pc_piix.c for the case when igd-passthru=on. Move the call to xen_host_pci_device_get, and the associated error handling, from xen_pt_realize to the new xen_igd_clear_slot function to initialize the device class and vendor values which enables the checks for the Intel IGD to succeed. The verification that the host device is an Intel IGD to be passed through is done by checking the domain, bus, slot, and function values as well as by checking that gfx_passthru is enabled, the device class is VGA, and the device vendor in Intel. Signed-off-by: Chuck Zmudzinski <brchuckz@aol.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> Message-Id: <b1b4a21fe9a600b1322742dda55a40e9961daa57.1674346505.git.brchuckz@aol.com> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
2023-01-22 01:57:02 +01:00
XenPTDeviceClass *xpdc = XEN_PT_DEVICE_CLASS(klass);
xpdc->pci_qdev_realize = dc->realize;
dc->realize = xen_igd_clear_slot;
k->realize = xen_pt_realize;
k->exit = xen_pt_unregister_device;
k->config_read = xen_pt_pci_read_config;
k->config_write = xen_pt_pci_write_config;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
dc->desc = "Assign an host PCI device with Xen";
device_class_set_props(dc, xen_pci_passthrough_properties);
};
static void xen_pci_passthrough_finalize(Object *obj)
{
XenPCIPassthroughState *s = XEN_PT_DEVICE(obj);
xen_pt_msix_delete(s);
}
static const TypeInfo xen_pci_passthrough_info = {
.name = TYPE_XEN_PT_DEVICE,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(XenPCIPassthroughState),
.instance_finalize = xen_pci_passthrough_finalize,
.class_init = xen_pci_passthrough_class_init,
xen/pt: reserve PCI slot 2 for Intel igd-passthru Intel specifies that the Intel IGD must occupy slot 2 on the PCI bus, as noted in docs/igd-assign.txt in the Qemu source code. Currently, when the xl toolstack is used to configure a Xen HVM guest with Intel IGD passthrough to the guest with the Qemu upstream device model, a Qemu emulated PCI device will occupy slot 2 and the Intel IGD will occupy a different slot. This problem often prevents the guest from booting. The only available workarounds are not good: Configure Xen HVM guests to use the old and no longer maintained Qemu traditional device model available from xenbits.xen.org which does reserve slot 2 for the Intel IGD or use the "pc" machine type instead of the "xenfv" machine type and add the xen platform device at slot 3 using a command line option instead of patching qemu to fix the "xenfv" machine type directly. The second workaround causes some degredation in startup performance such as a longer boot time and reduced resolution of the grub menu that is displayed on the monitor. This patch avoids that reduced startup performance when using the Qemu upstream device model for Xen HVM guests configured with the igd-passthru=on option. To implement this feature in the Qemu upstream device model for Xen HVM guests, introduce the following new functions, types, and macros: * XEN_PT_DEVICE_CLASS declaration, based on the existing TYPE_XEN_PT_DEVICE * XEN_PT_DEVICE_GET_CLASS macro helper function for XEN_PT_DEVICE_CLASS * typedef XenPTQdevRealize function pointer * XEN_PCI_IGD_SLOT_MASK, the value of slot_reserved_mask to reserve slot 2 * xen_igd_reserve_slot and xen_igd_clear_slot functions Michael Tsirkin: * Introduce XEN_PCI_IGD_DOMAIN, XEN_PCI_IGD_BUS, XEN_PCI_IGD_DEV, and XEN_PCI_IGD_FN - use them to compute the value of XEN_PCI_IGD_SLOT_MASK The new xen_igd_reserve_slot function uses the existing slot_reserved_mask member of PCIBus to reserve PCI slot 2 for Xen HVM guests configured using the xl toolstack with the gfx_passthru option enabled, which sets the igd-passthru=on option to Qemu for the Xen HVM machine type. The new xen_igd_reserve_slot function also needs to be implemented in hw/xen/xen_pt_stub.c to prevent FTBFS during the link stage for the case when Qemu is configured with --enable-xen and --disable-xen-pci-passthrough, in which case it does nothing. The new xen_igd_clear_slot function overrides qdev->realize of the parent PCI device class to enable the Intel IGD to occupy slot 2 on the PCI bus since slot 2 was reserved by xen_igd_reserve_slot when the PCI bus was created in hw/i386/pc_piix.c for the case when igd-passthru=on. Move the call to xen_host_pci_device_get, and the associated error handling, from xen_pt_realize to the new xen_igd_clear_slot function to initialize the device class and vendor values which enables the checks for the Intel IGD to succeed. The verification that the host device is an Intel IGD to be passed through is done by checking the domain, bus, slot, and function values as well as by checking that gfx_passthru is enabled, the device class is VGA, and the device vendor in Intel. Signed-off-by: Chuck Zmudzinski <brchuckz@aol.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> Message-Id: <b1b4a21fe9a600b1322742dda55a40e9961daa57.1674346505.git.brchuckz@aol.com> Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
2023-01-22 01:57:02 +01:00
.class_size = sizeof(XenPTDeviceClass),
.instance_init = xen_pci_passthrough_instance_init,
pci: Add INTERFACE_CONVENTIONAL_PCI_DEVICE to Conventional PCI devices Add INTERFACE_CONVENTIONAL_PCI_DEVICE to all direct subtypes of TYPE_PCI_DEVICE, except: 1) The ones that already have INTERFACE_PCIE_DEVICE set: * base-xhci * e1000e * nvme * pvscsi * vfio-pci * virtio-pci * vmxnet3 2) base-pci-bridge Not all PCI bridges are Conventional PCI devices, so INTERFACE_CONVENTIONAL_PCI_DEVICE is added only to the subtypes that are actually Conventional PCI: * dec-21154-p2p-bridge * i82801b11-bridge * pbm-bridge * pci-bridge The direct subtypes of base-pci-bridge not touched by this patch are: * xilinx-pcie-root: Already marked as PCIe-only. * pcie-pci-bridge: Already marked as PCIe-only. * pcie-port: all non-abstract subtypes of pcie-port are already marked as PCIe-only devices. 3) megasas-base Not all megasas devices are Conventional PCI devices, so the interface names are added to the subclasses registered by megasas_register_types(), according to information in the megasas_devices[] array. "megasas-gen2" already implements INTERFACE_PCIE_DEVICE, so add INTERFACE_CONVENTIONAL_PCI_DEVICE only to "megasas". Acked-by: Alberto Garcia <berto@igalia.com> Acked-by: John Snow <jsnow@redhat.com> Acked-by: Anthony PERARD <anthony.perard@citrix.com> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com> Reviewed-by: David Gibson <david@gibson.dropbear.id.au> Acked-by: David Gibson <david@gibson.dropbear.id.au> Reviewed-by: Marcel Apfelbaum <marcel@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2017-09-27 21:56:34 +02:00
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ INTERFACE_PCIE_DEVICE },
pci: Add INTERFACE_CONVENTIONAL_PCI_DEVICE to Conventional PCI devices Add INTERFACE_CONVENTIONAL_PCI_DEVICE to all direct subtypes of TYPE_PCI_DEVICE, except: 1) The ones that already have INTERFACE_PCIE_DEVICE set: * base-xhci * e1000e * nvme * pvscsi * vfio-pci * virtio-pci * vmxnet3 2) base-pci-bridge Not all PCI bridges are Conventional PCI devices, so INTERFACE_CONVENTIONAL_PCI_DEVICE is added only to the subtypes that are actually Conventional PCI: * dec-21154-p2p-bridge * i82801b11-bridge * pbm-bridge * pci-bridge The direct subtypes of base-pci-bridge not touched by this patch are: * xilinx-pcie-root: Already marked as PCIe-only. * pcie-pci-bridge: Already marked as PCIe-only. * pcie-port: all non-abstract subtypes of pcie-port are already marked as PCIe-only devices. 3) megasas-base Not all megasas devices are Conventional PCI devices, so the interface names are added to the subclasses registered by megasas_register_types(), according to information in the megasas_devices[] array. "megasas-gen2" already implements INTERFACE_PCIE_DEVICE, so add INTERFACE_CONVENTIONAL_PCI_DEVICE only to "megasas". Acked-by: Alberto Garcia <berto@igalia.com> Acked-by: John Snow <jsnow@redhat.com> Acked-by: Anthony PERARD <anthony.perard@citrix.com> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com> Reviewed-by: David Gibson <david@gibson.dropbear.id.au> Acked-by: David Gibson <david@gibson.dropbear.id.au> Reviewed-by: Marcel Apfelbaum <marcel@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2017-09-27 21:56:34 +02:00
{ },
},
};
static void xen_pci_passthrough_register_types(void)
{
type_register_static(&xen_pci_passthrough_info);
}
type_init(xen_pci_passthrough_register_types)