X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/9ed5dacbfa0f3f74238854776385f150b68e78b9..bce33948178e338eac2629284b199c0c1f05f8a3:/hw/xen/xen_pt.c diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index d58cb616b1..d0199683be 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -56,6 +56,7 @@ #include "hw/pci/pci.h" #include "hw/xen/xen.h" +#include "hw/i386/pc.h" #include "hw/xen/xen_backend.h" #include "xen_pt.h" #include "qemu/range.h" @@ -125,7 +126,7 @@ int xen_pt_bar_offset_to_index(uint32_t offset) static uint32_t xen_pt_pci_read_config(PCIDevice *d, uint32_t addr, int len) { - XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); + XenPCIPassthroughState *s = XEN_PT_DEVICE(d); uint32_t val = 0; XenPTRegGroup *reg_grp_entry = NULL; XenPTReg *reg_entry = NULL; @@ -230,15 +231,16 @@ exit: static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int len) { - XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); + XenPCIPassthroughState *s = XEN_PT_DEVICE(d); int index = 0; XenPTRegGroup *reg_grp_entry = NULL; int rc = 0; - uint32_t read_val = 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; @@ -248,10 +250,18 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr, /* check unused BAR register */ index = xen_pt_bar_offset_to_index(addr); - if ((index >= 0) && (val > 0 && val < 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); + 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 */ @@ -271,10 +281,17 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr, 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; } @@ -295,9 +312,17 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr, 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) { @@ -339,24 +364,50 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr, } 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_device */ + /* need to shift back before passing them to xen_host_pci_set_block. */ val >>= (addr & 3) << 3; memory_region_transaction_commit(); out: - if (!(reg && reg->no_wb)) { + 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 */ - rc = xen_host_pci_set_block(&s->real_device, addr, - (uint8_t *)&val, len); + 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, "pci_write_block failed. return value: %d.\n", rc); + XEN_PT_ERR(d, "xen_host_pci_set_block failed. return value: %d.\n", rc); } } } @@ -388,7 +439,7 @@ static const MemoryRegionOps ops = { .write = xen_pt_bar_write, }; -static int xen_pt_register_regions(XenPCIPassthroughState *s) +static int xen_pt_register_regions(XenPCIPassthroughState *s, uint16_t *cmd) { int i = 0; XenHostPCIDevice *d = &s->real_device; @@ -406,6 +457,7 @@ static int xen_pt_register_regions(XenPCIPassthroughState *s) 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) { @@ -414,14 +466,15 @@ static int xen_pt_register_regions(XenPCIPassthroughState *s) 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%lx"PRIx64 - " base_addr=0x%lx"PRIx64" type: %#x)\n", + XEN_PT_LOG(&s->dev, "IO region %i registered (size=0x%08"PRIx64 + " base_addr=0x%08"PRIx64" type: %#x)\n", i, r->size, r->base_addr, type); } @@ -440,8 +493,8 @@ static int xen_pt_register_regions(XenPCIPassthroughState *s) s->bases[PCI_ROM_SLOT].access.maddr = d->rom.base_addr; - memory_region_init_rom_device(&s->rom, OBJECT(s), NULL, NULL, - "xen-pci-pt-rom", d->rom.size); + 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); @@ -450,28 +503,10 @@ static int xen_pt_register_regions(XenPCIPassthroughState *s) d->rom.size, d->rom.base_addr); } + xen_pt_register_vga_regions(d); return 0; } -static void xen_pt_unregister_regions(XenPCIPassthroughState *s) -{ - XenHostPCIDevice *d = &s->real_device; - int i; - - for (i = 0; i < PCI_NUM_REGIONS - 1; i++) { - XenHostPCIIORegion *r = &d->io_regions[i]; - - if (r->base_addr == 0 || r->size == 0) { - continue; - } - - memory_region_destroy(&s->bar[i]); - } - if (d->rom.base_addr && d->rom.size) { - memory_region_destroy(&s->rom); - } -} - /* region mapping */ static int xen_pt_bar_from_region(XenPCIPassthroughState *s, MemoryRegion *mr) @@ -582,8 +617,8 @@ static void xen_pt_region_update(XenPCIPassthroughState *s, guest_port, machine_port, size, op); if (rc) { - XEN_PT_ERR(d, "%s ioport mapping failed! (rc: %i)\n", - adding ? "create new" : "remove old", 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; @@ -596,8 +631,8 @@ static void xen_pt_region_update(XenPCIPassthroughState *s, XEN_PFN(size + XC_PAGE_SIZE - 1), op); if (rc) { - XEN_PT_ERR(d, "%s mem mapping failed! (rc: %i)\n", - adding ? "create new" : "remove old", rc); + XEN_PT_ERR(d, "%s mem mapping failed! (err: %i)\n", + adding ? "create new" : "remove old", errno); } } } @@ -650,13 +685,25 @@ static const MemoryListener xen_pt_io_listener = { .priority = 10, }; +static void +xen_igd_passthrough_isa_bridge_create(XenPCIPassthroughState *s, + XenHostPCIDevice *dev) +{ + uint16_t gpu_dev_id; + PCIDevice *d = &s->dev; + + gpu_dev_id = dev->device_id; + igd_passthrough_isa_bridge_create(d->bus, gpu_dev_id); +} + /* init */ static int xen_pt_initfn(PCIDevice *d) { - XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); + XenPCIPassthroughState *s = XEN_PT_DEVICE(d); int rc = 0; - uint8_t machine_irq = 0; + uint8_t machine_irq = 0, scratch; + uint16_t cmd = 0; int pirq = XEN_PT_UNASSIGNED_PIRQ; /* register real device */ @@ -682,7 +729,7 @@ static int xen_pt_initfn(PCIDevice *d) /* Initialize virtualized PCI configuration (Extended 256 Bytes) */ if (xen_host_pci_get_block(&s->real_device, 0, d->config, - PCI_CONFIG_SPACE_SIZE) == -1) { + PCI_CONFIG_SPACE_SIZE) < 0) { xen_host_pci_device_put(&s->real_device); return -1; } @@ -690,8 +737,28 @@ static int xen_pt_initfn(PCIDevice *d) s->memory_listener = xen_pt_memory_listener; s->io_listener = xen_pt_io_listener; + /* Setup VGA bios for passthrough GFX */ + if ((s->real_device.domain == 0) && (s->real_device.bus == 0) && + (s->real_device.dev == 2) && (s->real_device.func == 0)) { + if (!is_igd_vga_passthrough(&s->real_device)) { + XEN_PT_ERR(d, "Need to enable igd-passthru if you're trying" + " to passthrough IGD GFX.\n"); + xen_host_pci_device_put(&s->real_device); + return -1; + } + + if (xen_pt_setup_vga(s, &s->real_device) < 0) { + XEN_PT_ERR(d, "Setup VGA BIOS of passthrough GFX failed!\n"); + xen_host_pci_device_put(&s->real_device); + return -1; + } + + /* 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); + xen_pt_register_regions(s, &cmd); /* reinitialize each config register to be emulated */ if (xen_pt_config_init(s)) { @@ -701,7 +768,12 @@ static int xen_pt_initfn(PCIDevice *d) } /* Bind interrupt */ - if (!s->dev.config[PCI_INTERRUPT_PIN]) { + rc = xen_host_pci_get_byte(&s->real_device, PCI_INTERRUPT_PIN, &scratch); + if (rc) { + XEN_PT_ERR(d, "Failed to read PCI_INTERRUPT_PIN! (rc:%d)\n", rc); + scratch = 0; + } + if (!scratch) { XEN_PT_LOG(d, "no pin interrupt\n"); goto out; } @@ -710,14 +782,11 @@ static int xen_pt_initfn(PCIDevice *d) 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, (rc: %d)\n", - machine_irq, pirq, rc); + 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) */ - xen_host_pci_set_word(&s->real_device, - PCI_COMMAND, - pci_get_word(s->dev.config + PCI_COMMAND) - | PCI_COMMAND_INTX_DISABLE); + cmd |= PCI_COMMAND_INTX_DISABLE; machine_irq = 0; s->machine_irq = 0; } else { @@ -735,19 +804,17 @@ static int xen_pt_initfn(PCIDevice *d) PCI_SLOT(d->devfn), e_intx); if (rc < 0) { - XEN_PT_ERR(d, "Binding of interrupt %i failed! (rc: %d)\n", - e_intx, rc); + XEN_PT_ERR(d, "Binding of interrupt %i failed! (err: %d)\n", + e_intx, errno); /* Disable PCI intx assertion (turn on bit10 of devctl) */ - xen_host_pci_set_word(&s->real_device, PCI_COMMAND, - *(uint16_t *)(&s->dev.config[PCI_COMMAND]) - | PCI_COMMAND_INTX_DISABLE); + 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!" - " (rc: %d)\n", machine_irq, rc); + " (err: %d)\n", machine_irq, errno); } } s->machine_irq = 0; @@ -755,8 +822,25 @@ static int xen_pt_initfn(PCIDevice *d) } out: - memory_listener_register(&s->memory_listener, &address_space_memory); + if (cmd) { + uint16_t val; + + rc = xen_host_pci_get_word(&s->real_device, PCI_COMMAND, &val); + if (rc) { + XEN_PT_ERR(d, "Failed to read PCI_COMMAND! (rc: %d)\n", rc); + } else { + val |= cmd; + rc = xen_host_pci_set_word(&s->real_device, PCI_COMMAND, val); + if (rc) { + XEN_PT_ERR(d, "Failed to write PCI_COMMAND val=0x%x!(rc: %d)\n", + val, rc); + } + } + } + + memory_listener_register(&s->memory_listener, &s->dev.bus_master_as); 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); @@ -766,12 +850,14 @@ out: static void xen_pt_unregister_device(PCIDevice *d) { - XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); + XenPCIPassthroughState *s = XEN_PT_DEVICE(d); + XenHostPCIDevice *host_dev = &s->real_device; uint8_t machine_irq = s->machine_irq; - uint8_t intx = xen_pt_pci_intx(s); + uint8_t intx; int rc; - if (machine_irq) { + 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_bus_num(d->bus), @@ -780,12 +866,13 @@ static void xen_pt_unregister_device(PCIDevice *d) 0 /* isa_irq */); if (rc < 0) { XEN_PT_ERR(d, "unbinding of interrupt INT%c failed." - " (machine irq: %i, rc: %d)" + " (machine irq: %i, err: %d)" " But bravely continuing on..\n", - 'a' + intx, machine_irq, rc); + 'a' + intx, machine_irq, errno); } } + /* N.B. xen_pt_config_delete takes care of freeing them. */ if (s->msi) { xen_pt_msi_disable(s); } @@ -800,25 +887,32 @@ static void xen_pt_unregister_device(PCIDevice *d) rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq); if (rc < 0) { - XEN_PT_ERR(d, "unmapping of interrupt %i failed. (rc: %d)" + XEN_PT_ERR(d, "unmapping of interrupt %i failed. (err: %d)" " But bravely continuing on..\n", - machine_irq, rc); + machine_irq, errno); } } + s->machine_irq = 0; } /* delete all emulated config registers */ xen_pt_config_delete(s); - xen_pt_unregister_regions(s); - memory_listener_unregister(&s->memory_listener); - memory_listener_unregister(&s->io_listener); + xen_pt_unregister_vga_regions(host_dev); - xen_host_pci_device_put(&s->real_device); + 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); + } } 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(), }; @@ -837,7 +931,7 @@ static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data) }; static const TypeInfo xen_pci_passthrough_info = { - .name = "xen-pci-passthrough", + .name = TYPE_XEN_PT_DEVICE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(XenPCIPassthroughState), .class_init = xen_pci_passthrough_class_init,