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 @@ -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,28 @@ #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 IGD_DEVID_COFFEELAKE 0x3E00 /* 0x3Exx */ +#define IGD_DEVID_KABYLAKE 0x5900 /* 0x59xx */ +#define IGD_DEVID_SKYLAKE 0x1900 /* 0x19xx */ +#define IGD_DEVID_BROADWELL 0x1600 /* 0x16xx */ +#define IGD_DEVID_HASWELL 0x0400 /* 0x04xx */ +#define IGD_DEVID_IVYBRIDGE_1 0x0160 /* 0x016x */ +#define IGD_DEVID_IVYBRIDGE_0 0x0150 /* 0x015x */ +#define IGD_DEVID_SANDYBRIDGE_2 0x0120 /* 0x012x */ +#define IGD_DEVID_SANDYBRIDGE_1 0x0110 /* 0x011x */ +#define IGD_DEVID_SANDYBRIDGE_0 0x0100 /* 0x010x */ +#define IGD_DEVID_WESTMERE 0x0040 /* 0x004x */ +#define PCIR_GGC 0x50 /* GMCH Graphics Control register */ +#define PCIR_BDSM 0x5C /* Base Data of Stolen Memory register */ +#define PCIR_ASLS_CTL 0xFC /* Opregion start address register */ +#define PCIM_GEN5_75_GGC_GMS_MASK 0x000000F0 /* Bits 7:4 contain Graphics Mode Select */ +#define PCIM_GEN6_GGC_GMS_MASK 0x000000F8 /* Bits 7:3 contain Graphics Mode Select */ +#define PCIM_GEN8_GGC_GMS_MASK 0x0000FF00 /* Bits 15:8 contain Graphics Mode Select */ +#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 (fallback if detection fails) */ +#define GPU_OPREGION_SIZE 0x00004000 /* Size 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,9 @@ 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); +uint64_t pci_emul_alloc_mmio(enum pcibar_type type, uint64_t size, uint64_t mask); +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,13 +690,57 @@ } 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 && idx != pci_msix_table_bar(pdi)) + register_bar(pdi, idx); + } + else { + if ((cmd & enbit) != enbit) + pci_set_cfgdata16(pdi, PCIR_COMMAND, cmd | enbit); + register_bar(pdi, idx); + } return (0); } +// mask should be a power of 2 minus 1 (e.g. 0x000FFFFF) +uint64_t +pci_emul_alloc_mmio(enum pcibar_type type, uint64_t size, uint64_t mask) +{ + int error; + + error = 1; + + uint64_t *baseptr, limit, base; + + switch (type) { + case PCIBAR_IO: + baseptr = &pci_emul_iobase; + limit = PCI_EMUL_IOLIMIT; + break; + case PCIBAR_MEM32: + baseptr = &pci_emul_membase32; + limit = PCI_EMUL_MEMLIMIT32; + break; + case PCIBAR_MEM64: + baseptr = &pci_emul_membase64; + limit = PCI_EMUL_MEMLIMIT64; + break; + default: + return 0; + } + + // align base + base = (*baseptr + mask) & ~mask; + + if (base + size > limit) + return 0; + + *baseptr = base + size; + + return base; +} + #define CAP_START_OFFSET 0x40 static int pci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen) @@ -1799,9 +1856,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 +1887,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 +1966,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,110 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Beckhoff Automation GmbH & Co. KG + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR OR CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#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 @@ -79,10 +80,19 @@ #define MSIX_TABLE_COUNT(ctrl) (((ctrl) & PCIM_MSIXCTRL_TABLE_SIZE) + 1) #define MSIX_CAPLEN 12 +#define KB (1024UL) +#define MB (1024 * 1024UL) +#define GB (1024 * 1024 * 1024UL) + static int pcifd = -1; static int iofd = -1; static int memfd = -1; +enum passthru_type { + PASSTHRU_DEFAULT = 0, + PASSTHRU_IGD +}; + struct passthru_softc { struct pci_devinst *psc_pi; struct pcibar psc_bar[PCI_BARMAX + 1]; @@ -95,6 +105,7 @@ int capoff; } psc_msix; struct pcisel psc_sel; + enum passthru_type psc_type; }; static int @@ -149,6 +160,180 @@ (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: + if (idx != pci_msix_table_bar(pi)) { + 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); + } + // special handling for msix table + else { + uint32_t table_offset, table_size; + uint32_t gpa, len; + + table_offset = rounddown2(pi->pi_msix.table_offset, 4096); + table_size = pi->pi_msix.table_offset - table_offset; + table_size += pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE; + table_size = roundup2(table_size, 4096); + + gpa = pi->pi_bar[idx].addr; + len = table_offset; + + // unmap everything bevor MSI-X table + if (len > 0) { + if ((error = vm_unmap_pptdev_mmio(pi->pi_vmctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, gpa, len)) != 0) + goto done; + } + + gpa += table_offset + table_size; + len = pi->pi_bar[idx].size - (table_offset + table_size); + + // unmap everything behind MSI-X table + if (len > 0) { + if ((error = vm_unmap_pptdev_mmio(pi->pi_vmctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, gpa, len)) != 0) + goto done; + } + } + break; + } + +done: + 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: + if (idx != pci_msix_table_bar(pi)) { + 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; + } + } + // special handling for msix table + else { + uint32_t table_offset, table_size; + uint32_t gpa, len, hpa; + + table_offset = rounddown2(pi->pi_msix.table_offset, 4096); + table_size = pi->pi_msix.table_offset - table_offset; + table_size += pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE; + table_size = roundup2(table_size, 4096); + + hpa = sc->psc_bar[idx].addr; + gpa = pi->pi_bar[idx].addr; + len = table_offset; + + // map everything bevor MSI-X table + if (len > 0) { + if ((error = vm_map_pptdev_mmio(pi->pi_vmctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, gpa, len, hpa)) != 0) + goto done; + } + + hpa += table_offset + table_size; + gpa += table_offset + table_size; + len = pi->pi_bar[idx].size - (table_offset + table_size); + + // map everything behind MSI-X table + if (len > 0) { + if ((error = vm_map_pptdev_mmio(pi->pi_vmctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, gpa, len, hpa)) != 0) + goto done; + } + } + break; + } + +done: + if (error != 0) + err(1, __func__); +} + #ifdef LEGACY_SUPPORT static int passthru_add_msicap(struct pci_devinst *pi, int msgnum, int nextptr) @@ -581,24 +766,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 +828,36 @@ 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_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 +929,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 +948,18 @@ 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) { + warnx("Failed to init igd"); + goto done; + } + } + + error = 0; /* success */ done: if (error) { free(sc); @@ -743,6 +968,349 @@ return (error); } +/* + * GVT-d: Handler for passthru of igd + */ +struct igd_funcs { + uint64_t (*get_opregion_hpa)(struct vmctx *ctx, struct passthru_softc *sc); + uint64_t (*get_gsm_hpa)(struct vmctx *ctx, struct passthru_softc *sc); + uint64_t (*get_opregion_size)(struct vmctx *ctx, struct passthru_softc *sc); + uint64_t (*get_gsm_size)(struct vmctx *ctx, struct passthru_softc *sc); + void (*set_opregion_gpa)(struct vmctx *ctx, struct passthru_softc *sc, uint64_t gpa); + void (*set_gsm_gpa)(struct vmctx *ctx, struct passthru_softc *sc, uint64_t gpa); +}; + +/* + * GVT-d: Handler for igd of gen5.75 (Westmere) + */ + +static uint64_t +igd_gen5_75_get_opregion_hpa(struct vmctx *ctx, struct passthru_softc *sc) +{ + return read_config(&sc->psc_sel, PCIR_ASLS_CTL, 4) & PCIM_ASLS_OPREGION_MASK; +} + +static uint64_t +igd_gen5_75_get_gsm_hpa(struct vmctx *ctx, struct passthru_softc *sc) +{ + return read_config(&sc->psc_sel, PCIR_BDSM, 4) & PCIM_BDSM_GSM_MASK; +} + +static uint64_t +igd_gen5_75_get_opregion_size(struct vmctx *ctx, struct passthru_softc *sc) +{ + return GPU_OPREGION_SIZE; +} + +static uint64_t +igd_gen5_75_get_gsm_size(struct vmctx *ctx, struct passthru_softc *sc) +{ + uint64_t gsm_size; + + uint16_t ggc_val = read_config(&sc->psc_sel, PCIR_GGC, 2); + uint8_t gms_val = (ggc_val & PCIM_GEN5_75_GGC_GMS_MASK) >> 4; /* Bits 7:4 contain Graphics Mode Select */ + switch (gms_val) { + case 0x05: + gsm_size = 32*MB; + break; + case 0x06: + gsm_size = 48*MB; + break; + case 0x07: + gsm_size = 64*MB; + break; + case 0x08: + gsm_size = 128*MB; + break; + case 0x09: + gsm_size = 256*MB; + break; + case 0x0A: + gsm_size = 96*MB; + break; + case 0x0B: + gsm_size = 160*MB; + break; + case 0x0C: + gsm_size = 224*MB; + break; + case 0x0D: + gsm_size = 352*MB; + break; + default: + gsm_size = GPU_GSM_SIZE; + warnx("Unknown Graphic Mode (%x): Fallback to %lu MB of Graphics Stolen Memory.", gms_val, gsm_size / MB); + break; + } + + return gsm_size; +} + +static void +igd_gen5_75_set_opregion_gpa(struct vmctx *ctx, struct passthru_softc *sc, uint64_t gpa) +{ + uint32_t asls_val = read_config(&sc->psc_sel, PCIR_ASLS_CTL, 4); + pci_set_cfgdata32(sc->psc_pi, PCIR_ASLS_CTL, gpa | (asls_val & ~PCIM_ASLS_OPREGION_MASK)); +} + +static void +igd_gen5_75_set_gsm_gpa(struct vmctx *ctx, struct passthru_softc *sc, uint64_t gpa) +{ + uint32_t bdsm_val = read_config(&sc->psc_sel, PCIR_BDSM, 4); + pci_set_cfgdata32(sc->psc_pi, PCIR_BDSM, gpa | (bdsm_val & ~PCIM_BDSM_GSM_MASK)); +} + +/* + * GVT-d: Handler for igd of gen6 (Sandy Bridge) + */ +static uint64_t +igd_gen6_get_gsm_size(struct vmctx *ctx, struct passthru_softc *sc) +{ + uint64_t gsm_size; + + uint16_t ggc_val = read_config(&sc->psc_sel, PCIR_GGC, 2); + uint8_t gms_val = (ggc_val & PCIM_GEN6_GGC_GMS_MASK) >> 3; /* Bits 7:3 contain Graphics Mode Select */ + if (gms_val <= 0x10) + gsm_size = gms_val * 32*MB; + else { + gsm_size = GPU_GSM_SIZE; + warnx("Unknown Graphic Mode (%x): Fallback to %lu MB of Graphics Stolen Memory.", gms_val, gsm_size / MB); + } + + return gsm_size; +} + +/* + * GVT-d: Handler for igd of gen8 (Broadwell) + */ +static uint64_t +igd_gen8_get_gsm_size(struct vmctx *ctx, struct passthru_softc *sc) +{ + uint64_t gsm_size; + + uint16_t ggc_val = read_config(&sc->psc_sel, PCIR_GGC, 2); + uint8_t gms_val = (ggc_val & PCIM_GEN8_GGC_GMS_MASK) >> 8; /* Bits 15:8 contain Graphics Mode Select */ + if (gms_val <= 0x10) + gsm_size = gms_val * 32*MB; + else if (gms_val == 0x20) + gsm_size = 1024*MB; + else if (gms_val == 0x30) + gsm_size = 1536*MB; + else if (gms_val == 0x3F) + gsm_size = 2016*MB; + else { + gsm_size = GPU_GSM_SIZE; + warnx("Unknown Graphic Mode (%x): Fallback to %lu MB of Graphics Stolen Memory.", gms_val, gsm_size / MB); + } + + return gsm_size; +} + +/* + * GVT-d: Handler for igd of gen9 (Skylake) + */ +static uint64_t +igd_gen9_get_gsm_size(struct vmctx *ctx, struct passthru_softc *sc) +{ + uint64_t gsm_size; + + uint16_t ggc_val = read_config(&sc->psc_sel, PCIR_GGC, 2); + uint8_t gms_val = (ggc_val & PCIM_GEN8_GGC_GMS_MASK) >> 8; /* Bits 15:8 contain Graphics Mode Select */ + if (gms_val <= 0x10) + gsm_size = gms_val * 32*MB; + else if (gms_val == 0x20) + gsm_size = 1024*MB; + else if (gms_val == 0x30) + gsm_size = 1536*MB; + else if (gms_val == 0x40) + gsm_size = 2048*MB; + else if (gms_val >= 0xF0 && gms_val <= 0xFE) + gsm_size = gms_val * 4*MB; + else { + gsm_size = GPU_GSM_SIZE; + warnx("Unknown Graphic Mode (%x): Fallback to %lu MB of Graphics Stolen Memory.", gms_val, gsm_size / MB); + } + + return gsm_size; +} + +// Westmere +struct igd_funcs igd_gen5_75 = { + .get_opregion_hpa = igd_gen5_75_get_opregion_hpa, + .get_gsm_hpa = igd_gen5_75_get_gsm_hpa, + .get_opregion_size = igd_gen5_75_get_opregion_size, + .get_gsm_size = igd_gen5_75_get_gsm_size, + .set_opregion_gpa = igd_gen5_75_set_opregion_gpa, + .set_gsm_gpa = igd_gen5_75_set_gsm_gpa +}; +// Sandy Bridge +struct igd_funcs igd_gen6 = { + .get_opregion_hpa = igd_gen5_75_get_opregion_hpa, + .get_gsm_hpa = igd_gen5_75_get_gsm_hpa, + .get_opregion_size = igd_gen5_75_get_opregion_size, + .get_gsm_size = igd_gen6_get_gsm_size, + .set_opregion_gpa = igd_gen5_75_set_opregion_gpa, + .set_gsm_gpa = igd_gen5_75_set_gsm_gpa +}; +// Ivy Bridge +struct igd_funcs igd_gen7 = { + .get_opregion_hpa = igd_gen5_75_get_opregion_hpa, + .get_gsm_hpa = igd_gen5_75_get_gsm_hpa, + .get_opregion_size = igd_gen5_75_get_opregion_size, + .get_gsm_size = igd_gen6_get_gsm_size, + .set_opregion_gpa = igd_gen5_75_set_opregion_gpa, + .set_gsm_gpa = igd_gen5_75_set_gsm_gpa +}; +// Haswell +struct igd_funcs igd_gen7_5 = { + .get_opregion_hpa = igd_gen5_75_get_opregion_hpa, + .get_gsm_hpa = igd_gen5_75_get_gsm_hpa, + .get_opregion_size = igd_gen5_75_get_opregion_size, + .get_gsm_size = igd_gen6_get_gsm_size, + .set_opregion_gpa = igd_gen5_75_set_opregion_gpa, + .set_gsm_gpa = igd_gen5_75_set_gsm_gpa +}; +// Broadwell +struct igd_funcs igd_gen8 = { + .get_opregion_hpa = igd_gen5_75_get_opregion_hpa, + .get_gsm_hpa = igd_gen5_75_get_gsm_hpa, + .get_opregion_size = igd_gen5_75_get_opregion_size, + .get_gsm_size = igd_gen8_get_gsm_size, + .set_opregion_gpa = igd_gen5_75_set_opregion_gpa, + .set_gsm_gpa = igd_gen5_75_set_gsm_gpa +}; +// Skylake +struct igd_funcs igd_gen9 = { + .get_opregion_hpa = igd_gen5_75_get_opregion_hpa, + .get_gsm_hpa = igd_gen5_75_get_gsm_hpa, + .get_opregion_size = igd_gen5_75_get_opregion_size, + .get_gsm_size = igd_gen9_get_gsm_size, + .set_opregion_gpa = igd_gen5_75_set_opregion_gpa, + .set_gsm_gpa = igd_gen5_75_set_gsm_gpa +}; +// Kabylake & Coffeelake +struct igd_funcs igd_gen9_5 = { + .get_opregion_hpa = igd_gen5_75_get_opregion_hpa, + .get_gsm_hpa = igd_gen5_75_get_gsm_hpa, + .get_opregion_size = igd_gen5_75_get_opregion_size, + .get_gsm_size = igd_gen9_get_gsm_size, + .set_opregion_gpa = igd_gen5_75_set_opregion_gpa, + .set_gsm_gpa = igd_gen5_75_set_gsm_gpa +}; + +static int +passthru_init_igd(struct vmctx *ctx, struct passthru_softc *sc) +{ + int error; + uint32_t opregion_hpa, opregion_gpa, opregion_size, gsm_hpa, gsm_gpa, gsm_size; + + error = 1; + + sc->psc_type = PASSTHRU_IGD; + + uint16_t dev_vendor = read_config(&sc->psc_sel, PCIR_VENDOR, 2); + uint16_t dev_id = read_config(&sc->psc_sel, PCIR_DEVICE, 2); + + if (dev_vendor != 0x8086) { + warnx("Unknown vendor (%x) of igd", dev_vendor); + goto done; + } + + /* + * 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) { + warnx("Failed to add igd-lpc"); + goto done; + } + + /* + * GVT-d: Get IGD funcs + */ + struct igd_funcs *igd; + + switch (dev_id & 0xFFF0) { + case IGD_DEVID_WESTMERE: + igd = &igd_gen5_75; + break; + case IGD_DEVID_SANDYBRIDGE_0: + case IGD_DEVID_SANDYBRIDGE_1: + case IGD_DEVID_SANDYBRIDGE_2: + igd = &igd_gen6; + break; + case IGD_DEVID_IVYBRIDGE_0: + case IGD_DEVID_IVYBRIDGE_1: + igd = &igd_gen7; + break; + default: + switch (dev_id & 0xFF00) { + case IGD_DEVID_HASWELL: + igd = &igd_gen7_5; + break; + case IGD_DEVID_BROADWELL: + igd = &igd_gen8; + break; + case IGD_DEVID_SKYLAKE: + igd = &igd_gen9; + break; + case IGD_DEVID_KABYLAKE: + case IGD_DEVID_COFFEELAKE: + igd = &igd_gen9_5; + break; + default: + warnx("Unsupported igd-device (%x): Try using gen9 graphics code path.", dev_id); + igd = &igd_gen9; + break; + } + break; + } + + /* + * GVT-d: Get hpa and size of Opregion and GSM + */ + opregion_hpa = igd->get_opregion_hpa(ctx, sc); + gsm_hpa = igd->get_gsm_hpa(ctx, sc); + opregion_size = igd->get_opregion_size(ctx, sc); + gsm_size = igd->get_gsm_size(ctx, sc); + + /* + * GVT-d: Allocate Opregion and GSM in guest space + */ + if ((opregion_gpa = pci_emul_alloc_mmio(PCIBAR_MEM32, opregion_size, ~PCIM_ASLS_OPREGION_MASK)) == 0) { + error = -ENOMEM; + goto done; + } + if ((gsm_gpa = pci_emul_alloc_mmio(PCIBAR_MEM32, gsm_size, ~PCIM_BDSM_GSM_MASK)) == 0) { + error = -ENOMEM; + goto done; + } + + /* + * GVT-d: Write address of Opregion and GSM into PCI register and protect their PCI register + */ + igd->set_opregion_gpa(ctx, sc, opregion_gpa); + igd->set_gsm_gpa(ctx, sc, gsm_gpa); + + /* + * GVT-d: Map Opregion and GSM into guest space + */ + if ((error = vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, opregion_gpa, opregion_size, opregion_hpa)) != 0) + goto done; + if ((error = vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, gsm_gpa, gsm_size, gsm_hpa)) != 0) + goto done; + + +done: + if (error) { + vm_unmap_pptdev_mmio(ctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, opregion_gpa, opregion_size); + vm_unmap_pptdev_mmio(ctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, gsm_gpa, gsm_size); + } + return (error); +} + static int bar_access(int coff) { @@ -779,7 +1347,7 @@ } static int -passthru_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, +passthru_cfgread_default(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int coff, int bytes, uint32_t *rv) { struct passthru_softc *sc; @@ -787,11 +1355,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,19 +1407,6 @@ } #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); @@ -823,7 +1414,35 @@ } static int -passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, +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_default(ctx, vcpu, pi, coff, bytes, rv); +} + +static int +passthru_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int coff, int bytes, uint32_t *rv) +{ + switch (((struct passthru_softc *)pi->pi_arg)->psc_type) { + case PASSTHRU_IGD: + return passthru_cfgread_igd(ctx, vcpu, pi, coff, bytes, rv); + default: + return passthru_cfgread_default(ctx, vcpu, pi, coff, bytes, rv); + } +} + +static int +passthru_cfgwrite_default(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int coff, int bytes, uint32_t val) { int error, msix_table_entries, i; @@ -832,11 +1451,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 +1566,42 @@ 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_default(ctx, vcpu, pi, coff, bytes, val); +} + +static int +passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int coff, int bytes, uint32_t val) +{ + switch (((struct passthru_softc *)pi->pi_arg)->psc_type) { + case PASSTHRU_IGD: + return passthru_cfgwrite_igd(ctx, vcpu, pi, coff, bytes, val); + default: + return passthru_cfgwrite_default(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)