Index: lib/libvmmapi/vmmapi.h =================================================================== --- lib/libvmmapi/vmmapi.h +++ lib/libvmmapi/vmmapi.h @@ -176,6 +176,8 @@ int vm_unassign_pptdev(struct vmctx *ctx, int bus, int slot, int func); int vm_map_pptdev_mmio(struct vmctx *ctx, int bus, int slot, int func, vm_paddr_t gpa, size_t len, vm_paddr_t hpa); +int vm_unmap_pptdev_mmio(struct vmctx *ctx, int bus, int slot, int func, + vm_paddr_t gpa, size_t len); int vm_setup_pptdev_msi(struct vmctx *ctx, int vcpu, int bus, int slot, int func, uint64_t addr, uint64_t msg, int numvec); int vm_setup_pptdev_msix(struct vmctx *ctx, int vcpu, int bus, int slot, Index: lib/libvmmapi/vmmapi.c =================================================================== --- lib/libvmmapi/vmmapi.c +++ lib/libvmmapi/vmmapi.c @@ -124,7 +124,7 @@ vm->fd = -1; vm->memflags = 0; - vm->lowmem_limit = 3 * GB; + vm->lowmem_limit = 2 * GB; vm->name = (char *)(vm + 1); strcpy(vm->name, name); @@ -980,6 +980,26 @@ return (ioctl(ctx->fd, VM_MAP_PPTDEV_MMIO, &pptmmio)); } +int +vm_unmap_pptdev_mmio(struct vmctx *ctx, int bus, int slot, int func, + vm_paddr_t gpa, size_t len) +{ + struct vm_pptdev_mmio pptmmio; + + bzero(&pptmmio, sizeof(pptmmio)); + pptmmio.bus = bus; + pptmmio.slot = slot; + pptmmio.func = func; + pptmmio.gpa = gpa; + pptmmio.len = len; + pptmmio.hpa = 0; + + if (gpa == 0) + return (0); + + return (ioctl(ctx->fd, VM_UNMAP_PPTDEV_MMIO, &pptmmio)); +} + int vm_setup_pptdev_msi(struct vmctx *ctx, int vcpu, int bus, int slot, int func, uint64_t addr, uint64_t msg, int numvec) @@ -1640,7 +1660,7 @@ VM_IOAPIC_PULSE_IRQ, VM_IOAPIC_PINCOUNT, VM_ISA_ASSERT_IRQ, VM_ISA_DEASSERT_IRQ, VM_ISA_PULSE_IRQ, VM_ISA_SET_IRQ_TRIGGER, VM_SET_CAPABILITY, VM_GET_CAPABILITY, VM_BIND_PPTDEV, - VM_UNBIND_PPTDEV, VM_MAP_PPTDEV_MMIO, VM_PPTDEV_MSI, + VM_UNBIND_PPTDEV, VM_MAP_PPTDEV_MMIO, VM_UNMAP_PPTDEV_MMIO, VM_PPTDEV_MSI, VM_PPTDEV_MSIX, VM_INJECT_NMI, VM_STATS, VM_STAT_DESC, VM_SET_X2APIC_STATE, VM_GET_X2APIC_STATE, VM_GET_HPET_CAPABILITIES, VM_GET_GPA_PMAP, VM_GLA2GPA, Index: sys/amd64/include/vmm_dev.h =================================================================== --- sys/amd64/include/vmm_dev.h +++ sys/amd64/include/vmm_dev.h @@ -299,6 +299,7 @@ IOCNUM_BIND_PPTDEV = 40, IOCNUM_UNBIND_PPTDEV = 41, IOCNUM_MAP_PPTDEV_MMIO = 42, + IOCNUM_UNMAP_PPTDEV_MMIO = 45, IOCNUM_PPTDEV_MSI = 43, IOCNUM_PPTDEV_MSIX = 44, @@ -409,6 +410,8 @@ _IOW('v', IOCNUM_UNBIND_PPTDEV, struct vm_pptdev) #define VM_MAP_PPTDEV_MMIO \ _IOW('v', IOCNUM_MAP_PPTDEV_MMIO, struct vm_pptdev_mmio) +#define VM_UNMAP_PPTDEV_MMIO \ + _IOW('v', IOCNUM_UNMAP_PPTDEV_MMIO, struct vm_pptdev_mmio) #define VM_PPTDEV_MSI \ _IOW('v', IOCNUM_PPTDEV_MSI, struct vm_pptdev_msi) #define VM_PPTDEV_MSIX \ Index: sys/amd64/vmm/io/ppt.h =================================================================== --- sys/amd64/vmm/io/ppt.h +++ sys/amd64/vmm/io/ppt.h @@ -34,6 +34,8 @@ int ppt_unassign_all(struct vm *vm); int ppt_map_mmio(struct vm *vm, int bus, int slot, int func, vm_paddr_t gpa, size_t len, vm_paddr_t hpa); +int ppt_unmap_mmio(struct vm *vm, int bus, int slot, int func, + vm_paddr_t gpa, size_t len); int ppt_setup_msi(struct vm *vm, int vcpu, int bus, int slot, int func, uint64_t addr, uint64_t msg, int numvec); int ppt_setup_msix(struct vm *vm, int vcpu, int bus, int slot, int func, Index: sys/amd64/vmm/io/ppt.c =================================================================== --- sys/amd64/vmm/io/ppt.c +++ sys/amd64/vmm/io/ppt.c @@ -218,7 +218,7 @@ } static void -ppt_unmap_mmio(struct vm *vm, struct pptdev *ppt) +ppt_unmap_mmio_all(struct vm *vm, struct pptdev *ppt) { int i; struct pptseg *seg; @@ -414,7 +414,7 @@ pci_save_state(ppt->dev); ppt_pci_reset(ppt->dev); pci_restore_state(ppt->dev); - ppt_unmap_mmio(vm, ppt); + ppt_unmap_mmio_all(vm, ppt); ppt_teardown_msi(ppt); ppt_teardown_msix(ppt); iommu_remove_device(vm_iommu_domain(vm), pci_get_rid(ppt->dev)); @@ -473,6 +473,35 @@ return (ENOENT); } +int +ppt_unmap_mmio(struct vm *vm, int bus, int slot, int func, + vm_paddr_t gpa, size_t len) +{ + int i, error; + struct pptseg *seg; + struct pptdev *ppt; + + ppt = ppt_find(bus, slot, func); + if (ppt != NULL) { + if (ppt->vm != vm) + return (EBUSY); + + for (i = 0; i < MAX_MMIOSEGS; i++) { + seg = &ppt->mmio[i]; + if (seg->gpa == gpa && seg->len == len) { + error = vm_unmap_mmio(vm, gpa, len); + if (error == 0) { + seg->gpa = 0; + seg->len = 0; + } + return (error); + } + } + return (ENOSPC); + } + return (ENOENT); +} + static int pptintr(void *arg) { Index: sys/amd64/vmm/vmm_dev.c =================================================================== --- sys/amd64/vmm/vmm_dev.c +++ sys/amd64/vmm/vmm_dev.c @@ -435,6 +435,7 @@ break; case VM_MAP_PPTDEV_MMIO: + case VM_UNMAP_PPTDEV_MMIO: case VM_BIND_PPTDEV: case VM_UNBIND_PPTDEV: #ifdef COMPAT_FREEBSD12 @@ -520,6 +521,11 @@ pptmmio->func, pptmmio->gpa, pptmmio->len, pptmmio->hpa); break; + case VM_UNMAP_PPTDEV_MMIO: + pptmmio = (struct vm_pptdev_mmio *)data; + error = ppt_unmap_mmio(sc->vm, pptmmio->bus, pptmmio->slot, + pptmmio->func, pptmmio->gpa, pptmmio->len); + break; case VM_BIND_PPTDEV: pptdev = (struct vm_pptdev *)data; error = vm_assign_pptdev(sc->vm, pptdev->bus, pptdev->slot, Index: sys/dev/pci/pcireg.h =================================================================== --- sys/dev/pci/pcireg.h +++ sys/dev/pci/pcireg.h @@ -1098,3 +1098,15 @@ #define PCIM_OSC_CTL_PCIE_PME 0x04 /* PCIe Native Power Mgt Events */ #define PCIM_OSC_CTL_PCIE_AER 0x08 /* PCIe Advanced Error Reporting */ #define PCIM_OSC_CTL_PCIE_CAP_STRUCT 0x10 /* Various Capability Structures */ + +/* + * GVT-d definitions + */ +#define PCIR_BDSM 0x5C /* Base Data of Stolen Memory register */ +#define PCIR_ASLS_CTL 0xFC /* Opregion start address register */ +#define PCIM_BDSM_GSM_MASK 0xFFF00000 /* Bits 31:20 contain base address of gsm */ +#define PCIM_ASLS_OPREGION_MASK 0xFFFFF000 /* Opregion is 4k aligned */ +#define GPU_GSM_SIZE 0x04000000 /* Size of Graphics Stolen Memory */ +#define GPU_GSM_GPA 0x9B000000 /* Guest Physical Address of Graphics Stolen Memory */ +#define GPU_OPREGION_SIZE 0x00003000 /* Size of Opregion */ +#define GPU_OPREGION_GPA 0x9FFFD000 /* Guest Physical Address of Opregion */ Index: usr.sbin/bhyve/Makefile =================================================================== --- usr.sbin/bhyve/Makefile +++ usr.sbin/bhyve/Makefile @@ -43,6 +43,7 @@ pci_hda.c \ pci_fbuf.c \ pci_hostbridge.c \ + pci_igd_lpc.c \ pci_irq.c \ pci_lpc.c \ pci_nvme.c \ Index: usr.sbin/bhyve/pci_emul.h =================================================================== --- usr.sbin/bhyve/pci_emul.h +++ usr.sbin/bhyve/pci_emul.h @@ -92,6 +92,7 @@ enum pcibar_type type; /* io or memory */ uint64_t size; uint64_t addr; + uint8_t lobits; }; #define PI_NAMESZ 40 @@ -223,6 +224,8 @@ enum pcibar_type type, uint64_t size); int pci_emul_alloc_pbar(struct pci_devinst *pdi, int idx, uint64_t hostbase, enum pcibar_type type, uint64_t size); +void unregister_bar_passthru(struct pci_devinst *pi, int idx); +void register_bar_passthru(struct pci_devinst *pi, int idx); int pci_emul_add_msicap(struct pci_devinst *pi, int msgnum); int pci_emul_add_pciecap(struct pci_devinst *pi, int pcie_device_type); void pci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, Index: usr.sbin/bhyve/pci_emul.c =================================================================== --- usr.sbin/bhyve/pci_emul.c +++ usr.sbin/bhyve/pci_emul.c @@ -459,6 +459,15 @@ return (pci_emul_alloc_pbar(pdi, idx, 0, type, size)); } +static bool +is_passthru(struct pci_devinst *pi) +{ + if (strcmp(pi->pi_d->pe_emu, "passthru") == 0) + return true; + else + return false; +} + /* * Register (or unregister) the MMIO or I/O region associated with the BAR * register 'idx' of an emulated pci device. @@ -509,15 +518,19 @@ static void unregister_bar(struct pci_devinst *pi, int idx) { - - modify_bar_registration(pi, idx, 0); + if (!is_passthru(pi)) + modify_bar_registration(pi, idx, 0); + else + unregister_bar_passthru(pi, idx); } static void register_bar(struct pci_devinst *pi, int idx) { - - modify_bar_registration(pi, idx, 1); + if (!is_passthru(pi)) + modify_bar_registration(pi, idx, 1); + else + register_bar_passthru(pi, idx); } /* Are we decoding i/o port accesses for the emulated pci device? */ @@ -677,9 +690,15 @@ } cmd = pci_get_cfgdata16(pdi, PCIR_COMMAND); - if ((cmd & enbit) != enbit) - pci_set_cfgdata16(pdi, PCIR_COMMAND, cmd | enbit); - register_bar(pdi, idx); + if (is_passthru(pdi)) { + if ((cmd & enbit) == enbit) + register_bar(pdi, idx); + } + else { + if ((cmd & enbit) != enbit) + pci_set_cfgdata16(pdi, PCIR_COMMAND, cmd | enbit); + register_bar(pdi, idx); + } return (0); } @@ -1799,9 +1818,9 @@ /* * Ignore all writes beyond the standard config space and return all - * ones on reads. + * ones on reads for non passthru devices. */ - if (coff >= PCI_REGMAX + 1) { + if (coff >= PCI_REGMAX + 1 && !is_passthru(pi)) { if (in) { *eax = 0xffffffff; /* @@ -1830,8 +1849,14 @@ needcfg = 1; } - if (needcfg) - *eax = CFGREAD(pi, coff, bytes); + if (needcfg) { + if (coff <= PCI_REGMAX) + *eax = CFGREAD(pi, coff, bytes); + else if (coff <= PCI_REGMAX + 4) + *eax = 0x00000000; + else + *eax = 0xFFFFFFFF; + } pci_emul_hdrtype_fixup(bus, slot, coff, bytes, eax); } else { @@ -1903,7 +1928,7 @@ pci_emul_capwrite(pi, coff, bytes, *eax, 0, 0); } else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) { pci_emul_cmdsts_write(pi, coff, *eax, bytes); - } else { + } else if (coff <= PCI_REGMAX) { CFGWRITE(pi, coff, *eax, bytes); } } Index: usr.sbin/bhyve/pci_igd_lpc.c =================================================================== --- /dev/null +++ usr.sbin/bhyve/pci_igd_lpc.c @@ -0,0 +1,80 @@ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "pci_emul.h" + +#ifndef _PATH_DEVPCI +#define _PATH_DEVPCI "/dev/pci" +#endif + +int pcifd = -1; + +static uint32_t +read_config(long reg, int width) +{ + struct pci_io pi; + + bzero(&pi, sizeof(pi)); + // igd-lpc is always connected to 0:1f.0 + pi.pi_sel.pc_domain = 0; + pi.pi_sel.pc_bus = 0; + pi.pi_sel.pc_dev = 0x1f; + pi.pi_sel.pc_func = 0; + pi.pi_reg = reg; + pi.pi_width = width; + + if (ioctl(pcifd, PCIOCREAD, &pi) < 0) + return (0); + else + return (pi.pi_data); +} + +static int +pci_igd_lpc_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) +{ + // only allow igd-lpc on 0:1f.0 + if (pi->pi_bus != 0 || pi->pi_slot != 0x1f || pi->pi_func != 0x00) { + warn("igd-lpc only allowed on 0:1f.0"); + return (-1); + } + + // open host device + if (pcifd < 0) { + pcifd = open(_PATH_DEVPCI, O_RDWR, 0); + if (pcifd < 0) { + warn("failed to open %s", _PATH_DEVPCI); + return (-1); + } + } + + /* + * The VID, DID, REVID, SUBVID and SUBDID of igd-lpc need aligned with physical one. + * Without these physical values, GVT-d GOP driver couldn't work. + */ + pci_set_cfgdata16(pi, PCIR_DEVICE, read_config(PCIR_DEVICE, 2)); + pci_set_cfgdata16(pi, PCIR_VENDOR, read_config(PCIR_VENDOR, 2)); + pci_set_cfgdata8(pi, PCIR_REVID, read_config(PCIR_REVID, 1)); + pci_set_cfgdata16(pi, PCIR_SUBVEND_0, read_config(PCIR_SUBVEND_0, 2)); + pci_set_cfgdata16(pi, PCIR_SUBDEV_0, read_config(PCIR_SUBDEV_0, 2)); + + return (0); +} + +struct pci_devemu pci_de_igd_lpc = { + .pe_emu = "igd-lpc", + .pe_init = pci_igd_lpc_init +}; +PCI_EMUL_SET(pci_de_igd_lpc); Index: usr.sbin/bhyve/pci_passthru.c =================================================================== --- usr.sbin/bhyve/pci_passthru.c +++ usr.sbin/bhyve/pci_passthru.c @@ -60,6 +60,7 @@ #include #include #include "pci_emul.h" +#include "inout.h" #include "mem.h" #ifndef _PATH_DEVPCI @@ -149,6 +150,116 @@ (void)ioctl(pcifd, PCIOCWRITE, &pi); /* XXX */ } +static int +pci_emul_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, + uint32_t *eax, void *arg) +{ + struct pci_devinst *pdi = arg; + struct pci_devemu *pe = pdi->pi_d; + uint64_t offset; + int i; + + for (i = 0; i <= PCI_BARMAX; i++) { + if (pdi->pi_bar[i].type == PCIBAR_IO && + port >= pdi->pi_bar[i].addr && + port + bytes <= pdi->pi_bar[i].addr + pdi->pi_bar[i].size) { + offset = port - pdi->pi_bar[i].addr; + if (in) + *eax = (*pe->pe_barread)(ctx, vcpu, pdi, i, + offset, bytes); + else + (*pe->pe_barwrite)(ctx, vcpu, pdi, i, offset, + bytes, *eax); + return (0); + } + } + return (-1); +} + +void unregister_bar_passthru(struct pci_devinst *pi, int idx) +{ + int error; + struct passthru_softc *sc; + struct inout_port iop; + + if (pi->pi_bar[idx].addr == 0) + return; + + sc = pi->pi_arg; + + switch (pi->pi_bar[idx].type) { + case PCIBAR_NONE: + case PCIBAR_MEMHI64: + break; + case PCIBAR_IO: + /* + * ToDo: Passthrough IO + * + * Use IO-Bitmap to emulate access to IO ports + * Prevent VM_EXIT on access to specified IO ports + */ + bzero(&iop, sizeof(struct inout_port)); + iop.name = pi->pi_name; + iop.port = pi->pi_bar[idx].addr; + iop.size = pi->pi_bar[idx].size; + error = unregister_inout(&iop); + break; + case PCIBAR_MEM32: + case PCIBAR_MEM64: + error = vm_unmap_pptdev_mmio(pi->pi_vmctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, pi->pi_bar[idx].addr, pi->pi_bar[idx].size); + break; + } + + if (error != 0) + err(1, __func__); +} + +void register_bar_passthru(struct pci_devinst *pi, int idx) +{ + int error; + struct passthru_softc *sc; + struct inout_port iop; + + sc = pi->pi_arg; + + switch (pi->pi_bar[idx].type) { + case PCIBAR_NONE: + case PCIBAR_MEMHI64: + break; + case PCIBAR_IO: + /* + * ToDo: Passthrough IO + * + * Use IO-Bitmap to emulate access to IO ports + * Prevent VM_EXIT on access to specified IO ports + */ + bzero(&iop, sizeof(struct inout_port)); + iop.name = pi->pi_name; + iop.port = pi->pi_bar[idx].addr; + iop.size = pi->pi_bar[idx].size; + iop.flags = IOPORT_F_INOUT; + iop.handler = pci_emul_io_handler; + iop.arg = pi; + error = register_inout(&iop); + break; + case PCIBAR_MEM32: + case PCIBAR_MEM64: + error = vm_map_pptdev_mmio(pi->pi_vmctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, pi->pi_bar[idx].addr, pi->pi_bar[idx].size, sc->psc_bar[idx].addr); + /* + * If the guest writes a new value to a 64-bit BAR, two writes are neccessary. + * vm_map_pptdev_mmio can fail in that case due to an invalid address after the first write. + */ + if (error != 0) { + pi->pi_bar[idx].addr = 0; + error = 0; + } + break; + } + + if (error != 0) + err(1, __func__); +} + #ifdef LEGACY_SUPPORT static int passthru_add_msicap(struct pci_devinst *pi, int msgnum, int nextptr) @@ -581,24 +692,34 @@ sc->psc_bar[i].type = bartype; sc->psc_bar[i].size = size; sc->psc_bar[i].addr = base; + sc->psc_bar[i].lobits = 0; /* Allocate the BAR in the guest I/O or MMIO space */ error = pci_emul_alloc_pbar(pi, i, base, bartype, size); if (error) return (-1); + /* + * For passthru devices use same prefetchable property as physical bar + */ + if (bartype == PCIBAR_MEM32 || bartype == PCIBAR_MEM64) + { + uint8_t lobits = pci_get_cfgdata8(pi, 0x10 + i * 0x04); + if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH) + lobits |= PCIM_BAR_MEM_PREFETCH; + else + lobits &= ~PCIM_BAR_MEM_PREFETCH; + sc->psc_bar[i].lobits = lobits & 0xF; + pci_set_cfgdata8(pi, 0x10 + i * 0x04, lobits); + } + else + sc->psc_bar[i].lobits = PCIM_BAR_IO_SPACE; + /* The MSI-X table needs special handling */ if (i == pci_msix_table_bar(pi)) { error = init_msix_table(ctx, sc, base); if (error) return (-1); - } else if (bartype != PCIBAR_IO) { - /* Map the physical BAR in the guest MMIO space */ - error = vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus, - sc->psc_sel.pc_dev, sc->psc_sel.pc_func, - pi->pi_bar[i].addr, pi->pi_bar[i].size, base); - if (error) - return (-1); } /* @@ -633,23 +754,42 @@ goto done; } + /* + * Set command register before init of BARs + * + * cfginitbar checks command register to decide whether to register a new BAR or not + */ + pci_set_cfgdata16(pi, PCIR_COMMAND, read_config(&sc->psc_sel, + PCIR_COMMAND, 2)); + if (cfginitbar(ctx, sc) != 0) { warnx("failed to initialize BARs for PCI %d/%d/%d", bus, slot, func); goto done; } - pci_set_cfgdata16(pi, PCIR_COMMAND, read_config(&sc->psc_sel, - PCIR_COMMAND, 2)); - error = 0; /* success */ done: return (error); } + +/* + * GVT-d: Declare modified funcs for passthrough of igd-device + */ +static int +passthru_init_igd(struct vmctx *ctx, struct passthru_softc *sc); +static int +passthru_cfgread_igd(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int coff, int bytes, uint32_t *rv); +static int +passthru_cfgwrite_igd(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int coff, int bytes, uint32_t val); + static int passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) { + char *opt; int bus, slot, func, error, memflags; struct passthru_softc *sc; #ifndef WITHOUT_CAPSICUM @@ -721,6 +861,12 @@ warnx("invalid passthru options"); return (error); } + + if ((opt = strchr(opts, ',')) != NULL) + { + *opt = '\0'; + opt = opt + 1; + } if (vm_assign_pptdev(ctx, bus, slot, func) != 0) { warnx("PCI device at %d/%d/%d is not using the ppt(4) driver", @@ -734,7 +880,16 @@ sc->psc_pi = pi; /* initialize config space */ - error = cfginit(ctx, pi, bus, slot, func); + if ((error = cfginit(ctx, pi, bus, slot, func)) != 0) + goto done; + + // init igd (integrated graphics device) + if (opt != NULL && strcmp(opt, "igd") == 0) { + if ((error = passthru_init_igd(ctx, sc)) != 0) + goto done; + } + + error = 0; /* success */ done: if (error) { free(sc); @@ -743,6 +898,52 @@ return (error); } +static int +passthru_init_igd(struct vmctx *ctx, struct passthru_softc *sc) +{ + int error; + + /* + * GVT-d: Create LPC-Device at 0:1f.0 + * + * Otherwise GOP-Driver wouldn't work for Windows + */ + printf("Add igd-lpc at slot 0:1f.0 to enable GVT-d for igd\n"); + if ((error = pci_parse_slot("0:31:0,igd-lpc")) != 0) + goto done; + + /* + * GVT-d: Passthrough GPU DSM (Data Stolen Memory) + */ + uint32_t bdsm_val; + bdsm_val = read_config(&sc->psc_sel, PCIR_BDSM, 4); + if ((error = vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, GPU_GSM_GPA, GPU_GSM_SIZE, bdsm_val & PCIM_BDSM_GSM_MASK)) != 0) + goto done; + pci_set_cfgdata32(sc->psc_pi, PCIR_BDSM, GPU_GSM_GPA | (bdsm_val & ~PCIM_BDSM_GSM_MASK)); + + /* + * GVT-d: Passthrough Opregion + */ + uint32_t asls_val; + asls_val = read_config(&sc->psc_sel, PCIR_ASLS_CTL, 4); + if ((error = vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, GPU_OPREGION_GPA, GPU_OPREGION_SIZE, asls_val & PCIM_ASLS_OPREGION_MASK)) != 0) + goto done; + pci_set_cfgdata32(sc->psc_pi, PCIR_ASLS_CTL, GPU_OPREGION_GPA | (asls_val & ~PCIM_ASLS_OPREGION_MASK)); + + /* + * GVT-d: Change handler for cfg read and writes + */ + sc->psc_pi->pi_d->pe_cfgread = passthru_cfgread_igd; + sc->psc_pi->pi_d->pe_cfgwrite = passthru_cfgwrite_igd; + +done: + if (error) { + vm_unmap_pptdev_mmio(ctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, GPU_OPREGION_GPA, GPU_OPREGION_SIZE); + vm_unmap_pptdev_mmio(ctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, GPU_GSM_GPA, GPU_GSM_SIZE); + } + return (error); +} + static int bar_access(int coff) { @@ -787,11 +988,47 @@ sc = pi->pi_arg; /* - * PCI BARs and MSI capability is emulated. + * MSI capability is emulated. */ - if (bar_access(coff) || msicap_access(sc, coff)) + if (msicap_access(sc, coff)) return (-1); + if (bar_access(coff)) + { + int idx, update_idx; + + idx = (coff - PCIR_BAR(0)) / 4; + + if (pi->pi_bar[idx].type == PCIBAR_MEMHI64) + update_idx = idx - 1; + else + update_idx = idx; + + if (pci_get_cfgdata32(pi, 0x10 + idx * 0x04) == ~0U) { + uint64_t size = ~(uint64_t)(pi->pi_bar[update_idx].size - 1); + size |= sc->psc_bar[update_idx].lobits; + if (pi->pi_bar[idx].type == PCIBAR_MEMHI64) + *rv = size >> 32; + else + *rv = size; + if (bytes == 1) + *rv = *rv >> (coff & 0x3); + else if (bytes == 2) + *rv = *rv >> (coff & 0x1); + else + *rv = *rv; + } + else { + if (bytes == 1) + *rv = pci_get_cfgdata8(pi, coff); + else if (bytes == 2) + *rv = pci_get_cfgdata16(pi, coff); + else + *rv = pci_get_cfgdata32(pi, coff); + } + return (0); + } + #ifdef LEGACY_SUPPORT /* * Emulate PCIR_CAP_PTR if this device does not support MSI capability @@ -803,25 +1040,28 @@ } #endif - /* - * Emulate the command register. If a single read reads both the - * command and status registers, read the status register from the - * device's config space. - */ - if (coff == PCIR_COMMAND) { - if (bytes <= 2) - return (-1); - *rv = read_config(&sc->psc_sel, PCIR_STATUS, 2) << 16 | - pci_get_cfgdata16(pi, PCIR_COMMAND); - return (0); - } - /* Everything else just read from the device's config space */ *rv = read_config(&sc->psc_sel, coff, bytes); return (0); } +static int +passthru_cfgread_igd(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int coff, int bytes, uint32_t *rv) +{ + /* + * GVT-d: Emulate BDSM and ASLS_CTL + * + * BDSM: contains Base of Data Stolen Memory + * ASLS_CTL: contains address of Opregion + */ + if ((coff >= PCIR_BDSM && coff < PCIR_BDSM + 4) || (coff >= PCIR_ASLS_CTL && coff < PCIR_ASLS_CTL + 4)) + return (-1); + else + return passthru_cfgread(ctx, vcpu, pi, coff, bytes, rv); +} + static int passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int coff, int bytes, uint32_t val) @@ -832,11 +1072,66 @@ sc = pi->pi_arg; - /* - * PCI BARs are emulated - */ if (bar_access(coff)) - return (-1); + { + int idx, update_idx; + idx = (coff - PCIR_BAR(0)) / 4; + switch (pi->pi_bar[idx].type) + { + case PCIBAR_NONE: + pi->pi_bar[idx].addr = 0; + break; + case PCIBAR_IO: + case PCIBAR_MEM32: + case PCIBAR_MEM64: + case PCIBAR_MEMHI64: + if (pi->pi_bar[idx].type == PCIBAR_MEMHI64) + update_idx = idx - 1; + else + update_idx = idx; + + uint16_t cmd = read_config(&sc->psc_sel, PCIR_COMMAND, 2); + if ((cmd & PCIM_CMD_MEMEN && pi->pi_bar[idx].type != PCIBAR_IO) || + (cmd & PCIM_CMD_PORTEN && pi->pi_bar[idx].type == PCIBAR_IO)) { + unregister_bar_passthru(pi, update_idx); + } + + if (val != ~0U) { + uint64_t mask, bar; + mask = ~(pi->pi_bar[update_idx].size - 1); + if (pi->pi_bar[idx].type == PCIBAR_MEMHI64) + mask >>= 32; + bar = val & mask; + if (pi->pi_bar[idx].type != PCIBAR_MEMHI64) + bar |= sc->psc_bar[update_idx].lobits; + pci_set_cfgdata32(pi, coff, bar); + + uint32_t lo, hi; + lo = pci_get_cfgdata32(pi, 0x10 + update_idx * 0x04) & ~0x0F; + if (pi->pi_bar[update_idx].type == PCIBAR_MEM64) + hi = pci_get_cfgdata32(pi, 0x10 + (update_idx + 1) * 0x04); + else + hi = 0; + if (lo != ~0U && hi != ~0U) { + pi->pi_bar[update_idx].addr = (uint64_t)lo | ((uint64_t)hi << 32U); + if ((cmd & PCIM_CMD_MEMEN && pi->pi_bar[idx].type != PCIBAR_IO) || + (cmd & PCIM_CMD_PORTEN && pi->pi_bar[idx].type == PCIBAR_IO)) { + register_bar_passthru(pi, update_idx); + } + } + else + pi->pi_bar[update_idx].addr = 0; + } + else { + pci_set_cfgdata32(pi, coff, ~0U); + pi->pi_bar[update_idx].addr = 0; + } + break; + default: + break; + } + return (0); + } /* * MSI capability is emulated @@ -892,12 +1187,30 @@ pci_set_cfgdata8(pi, PCIR_COMMAND, val); else if (bytes == 2) pci_set_cfgdata16(pi, PCIR_COMMAND, val); + else + pci_set_cfgdata32(pi, PCIR_COMMAND, val); pci_emul_cmd_changed(pi, cmd_old); } return (0); } +static int +passthru_cfgwrite_igd(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int coff, int bytes, uint32_t val) +{ + /* + * GVT-d: Prevent write to BDSM and ASLS_CTL + * + * BDSM: contains Base of Data Stolen Memory + * ASLS_CTL: contains address of Opregion + */ + if (coff == PCIR_BDSM || coff == PCIR_ASLS_CTL) + return (0); + else + return passthru_cfgwrite(ctx, vcpu, pi, coff, bytes, val); +} + static void passthru_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, uint64_t offset, int size, uint64_t value)