diff options
-rw-r--r-- | manifest | 1 | ||||
-rw-r--r-- | usr/src/cmd/bhyve/Makefile.com | 2 | ||||
-rw-r--r-- | usr/src/cmd/bhyve/pci_passthru.c | 219 | ||||
-rw-r--r-- | usr/src/uts/i86pc/Makefile.files | 6 | ||||
-rw-r--r-- | usr/src/uts/i86pc/Makefile.i86pc | 1 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/vmm/intel/vtd.c | 90 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/vmm/io/iommu.c | 26 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/vmm/io/ppt.c | 311 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/vmm/io/ppt.conf | 14 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/vmm/io/ppt_sol_glue.c | 177 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/vmm/io/sol_iommu.c | 86 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/vmm/io/sol_ppt.c | 92 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c | 37 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/vmm/vmm_sol_glue.c | 15 | ||||
-rw-r--r-- | usr/src/uts/i86pc/ppt/Makefile | 75 | ||||
-rw-r--r-- | usr/src/uts/i86pc/vmm/Makefile | 4 | ||||
-rw-r--r-- | usr/src/uts/intel/ia32/ml/modstubs.s | 17 |
17 files changed, 959 insertions, 214 deletions
@@ -4574,6 +4574,7 @@ f usr/kernel/drv/amd64/lxautofs 0755 root sys f usr/kernel/drv/amd64/nsmb 0755 root sys f usr/kernel/drv/amd64/pm 0755 root sys f usr/kernel/drv/amd64/pool 0755 root sys +f usr/kernel/drv/amd64/ppt 0755 root sys f usr/kernel/drv/amd64/ptm 0755 root sys f usr/kernel/drv/amd64/pts 0755 root sys f usr/kernel/drv/amd64/signalfd 0755 root sys diff --git a/usr/src/cmd/bhyve/Makefile.com b/usr/src/cmd/bhyve/Makefile.com index aacf8421a1..c1c4113a95 100644 --- a/usr/src/cmd/bhyve/Makefile.com +++ b/usr/src/cmd/bhyve/Makefile.com @@ -81,7 +81,7 @@ CPPFLAGS = -I$(COMPAT)/freebsd -I$(CONTRIB)/freebsd \ -I$(SRC)/uts/i86pc \ -I$(SRC)/lib/libdladm/common \ -DWITHOUT_CAPSICUM -LDLIBS += -lsocket -lnsl -ldlpi -ldladm -lkstat -lmd -luuid -lvmmapi -lz +LDLIBS += -lsocket -lnsl -ldlpi -ldladm -lkstat -lmd -luuid -lvmmapi -lz -ldevinfo POST_PROCESS += ; $(GENSETDEFS) $@ diff --git a/usr/src/cmd/bhyve/pci_passthru.c b/usr/src/cmd/bhyve/pci_passthru.c index f314679d91..fd07e0f99b 100644 --- a/usr/src/cmd/bhyve/pci_passthru.c +++ b/usr/src/cmd/bhyve/pci_passthru.c @@ -38,6 +38,10 @@ __FBSDID("$FreeBSD$"); #include <sys/pciio.h> #include <sys/ioctl.h> +#include <sys/pci.h> +#include <sys/pci_tools.h> +#include <libdevinfo.h> + #include <dev/io/iodev.h> #include <dev/pci/pcireg.h> @@ -57,6 +61,7 @@ __FBSDID("$FreeBSD$"); #include "pci_emul.h" #include "mem.h" +#ifdef __FreeBSD__ #ifndef _PATH_DEVPCI #define _PATH_DEVPCI "/dev/pci" #endif @@ -64,6 +69,7 @@ __FBSDID("$FreeBSD$"); #ifndef _PATH_DEVIO #define _PATH_DEVIO "/dev/io" #endif +#endif #ifndef _PATH_MEM #define _PATH_MEM "/dev/mem" @@ -74,8 +80,10 @@ __FBSDID("$FreeBSD$"); #define MSIX_TABLE_COUNT(ctrl) (((ctrl) & PCIM_MSIXCTRL_TABLE_SIZE) + 1) #define MSIX_CAPLEN 12 +#ifdef __FreeBSD__ static int pcifd = -1; static int iofd = -1; +#endif static int memfd = -1; struct passthru_softc { @@ -90,6 +98,8 @@ struct passthru_softc { int capoff; } psc_msix; struct pcisel psc_sel; + di_node_t devnode; + int nexfd; }; static int @@ -115,12 +125,51 @@ msi_caplen(int msgctrl) } static uint32_t -read_config(const struct pcisel *sel, long reg, int width) +pcitool_reg_rw(const struct passthru_softc *sc, int bar, uint64_t reg, int width, + uint64_t data, int req) +{ + struct pcitool_reg pr = { 0 }; + + pr.user_version = PCITOOL_VERSION; + pr.acc_attr = PCITOOL_ACC_ATTR_ENDN_LTL; + pr.barnum = bar; + pr.bus_no = sc->psc_sel.pc_bus; + pr.dev_no = sc->psc_sel.pc_dev; + pr.func_no = sc->psc_sel.pc_func; + pr.offset = reg; + pr.data = data; + + switch (width) { + case 1: + pr.acc_attr += PCITOOL_ACC_ATTR_SIZE_1; + break; + case 2: + pr.acc_attr += PCITOOL_ACC_ATTR_SIZE_2; + break; + case 4: + pr.acc_attr += PCITOOL_ACC_ATTR_SIZE_4; + break; + case 8: + pr.acc_attr += PCITOOL_ACC_ATTR_SIZE_8; + break; + default: + return (0); + } + + if (ioctl(sc->nexfd, req, &pr) != 0) + return (0); + else + return (pr.data); +} + +static uint32_t +read_config(const struct passthru_softc *sc, long reg, int width) { +#ifdef __FreeBSD__ struct pci_io pi; bzero(&pi, sizeof(pi)); - pi.pi_sel = *sel; + pi.pi_sel = sc->sel; pi.pi_reg = reg; pi.pi_width = width; @@ -128,20 +177,29 @@ read_config(const struct pcisel *sel, long reg, int width) return (0); /* XXX */ else return (pi.pi_data); +#else + return (pcitool_reg_rw(sc, PCITOOL_CONFIG, reg, width, 0, + PCITOOL_DEVICE_GET_REG)); +#endif } static void -write_config(const struct pcisel *sel, long reg, int width, uint32_t data) +write_config(const struct passthru_softc *sc, long reg, int width, uint32_t data) { +#ifdef __FreeBSD__ struct pci_io pi; bzero(&pi, sizeof(pi)); - pi.pi_sel = *sel; + pi.pi_sel = sc->sel; pi.pi_reg = reg; pi.pi_width = width; pi.pi_data = data; (void)ioctl(pcifd, PCIOCWRITE, &pi); /* XXX */ +#else + (void) pcitool_reg_rw(sc, PCITOOL_CONFIG, reg, width, data, + PCITOOL_DEVICE_SET_REG); +#endif } #ifdef LEGACY_SUPPORT @@ -186,24 +244,24 @@ cfginitmsi(struct passthru_softc *sc) * Parse the capabilities and cache the location of the MSI * and MSI-X capabilities. */ - sts = read_config(&sel, PCIR_STATUS, 2); + sts = read_config(sc, PCIR_STATUS, 2); if (sts & PCIM_STATUS_CAPPRESENT) { - ptr = read_config(&sel, PCIR_CAP_PTR, 1); + ptr = read_config(sc, PCIR_CAP_PTR, 1); while (ptr != 0 && ptr != 0xff) { - cap = read_config(&sel, ptr + PCICAP_ID, 1); + cap = read_config(sc, ptr + PCICAP_ID, 1); if (cap == PCIY_MSI) { /* * Copy the MSI capability into the config * space of the emulated pci device */ sc->psc_msi.capoff = ptr; - sc->psc_msi.msgctrl = read_config(&sel, + sc->psc_msi.msgctrl = read_config(sc, ptr + 2, 2); sc->psc_msi.emulated = 0; caplen = msi_caplen(sc->psc_msi.msgctrl); capptr = ptr; while (caplen > 0) { - u32 = read_config(&sel, capptr, 4); + u32 = read_config(sc, capptr, 4); pci_set_cfgdata32(pi, capptr, u32); caplen -= 4; capptr += 4; @@ -217,7 +275,7 @@ cfginitmsi(struct passthru_softc *sc) msixcap_ptr = (uint32_t*) &msixcap; capptr = ptr; while (caplen > 0) { - u32 = read_config(&sel, capptr, 4); + u32 = read_config(sc, capptr, 4); *msixcap_ptr = u32; pci_set_cfgdata32(pi, capptr, u32); caplen -= 4; @@ -225,7 +283,7 @@ cfginitmsi(struct passthru_softc *sc) msixcap_ptr++; } } - ptr = read_config(&sel, ptr + PCICAP_NEXTPTR, 1); + ptr = read_config(sc, ptr + PCICAP_NEXTPTR, 1); } } @@ -260,7 +318,7 @@ cfginitmsi(struct passthru_softc *sc) */ if ((sts & PCIM_STATUS_CAPPRESENT) != 0 && sc->psc_msi.capoff == 0) { int origptr, msiptr; - origptr = read_config(&sel, PCIR_CAP_PTR, 1); + origptr = read_config(sc, PCIR_CAP_PTR, 1); msiptr = passthru_add_msicap(pi, 1, origptr); sc->psc_msi.capoff = msiptr; sc->psc_msi.msgctrl = pci_get_cfgdata16(pi, msiptr + 2); @@ -525,11 +583,55 @@ init_msix_table(struct vmctx *ctx, struct passthru_softc *sc, uint64_t base) } static int +devinfo_getbar(di_node_t node, int bar, enum pcibar_type *type, uint64_t *base, + uint64_t *size) +{ + int len, i; + int *regbuf; + int num; + + len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, + "assigned-addresses", ®buf); + + for (i = 0; i < len; + i += sizeof (pci_regspec_t) / sizeof (uint_t)) { + pci_regspec_t *reg = (pci_regspec_t *)®buf[i]; + + if (PCI_REG_REG_G(reg->pci_phys_hi) < PCI_CONF_BASE0 || + PCI_REG_REG_G(reg->pci_phys_hi) > PCI_CONF_BASE5) + continue; + num = PCI_REG_REG_G(reg->pci_phys_hi) >> 2; + if (num != bar) + continue; + + *base = ((uint64_t)reg->pci_phys_mid << 32) | reg->pci_phys_low; + *size = ((uint64_t)reg->pci_size_hi << 32) | reg->pci_size_low; + switch (reg->pci_phys_hi & PCI_REG_ADDR_M) { + case PCI_ADDR_IO: + *type = PCIBAR_IO; + break; + case PCI_ADDR_MEM32: + *type = PCIBAR_MEM32; + break; + case PCI_ADDR_MEM64: + *type = PCIBAR_MEM64; + break; + } + + return (0); + } + + return (-1); +} + +static int cfginitbar(struct vmctx *ctx, struct passthru_softc *sc) { int i, error; struct pci_devinst *pi; +#ifdef __FreeBSD__ struct pci_bar_io bar; +#endif enum pcibar_type bartype; uint64_t base, size; @@ -539,6 +641,7 @@ cfginitbar(struct vmctx *ctx, struct passthru_softc *sc) * Initialize BAR registers */ for (i = 0; i <= PCI_BARMAX; i++) { +#ifdef __FreeBSD__ bzero(&bar, sizeof(bar)); bar.pbi_sel = sc->psc_sel; bar.pbi_reg = PCIR_BAR(i); @@ -561,6 +664,10 @@ cfginitbar(struct vmctx *ctx, struct passthru_softc *sc) base = bar.pbi_base & PCIM_BAR_MEM_BASE; } size = bar.pbi_length; +#else + if (devinfo_getbar(sc->devnode, i, &bartype, &base, &size) != 0) + continue; +#endif if (bartype != PCIBAR_IO) { if (((base | size) & PAGE_MASK) != 0) { @@ -640,6 +747,60 @@ done: } static int +devinfo_open(char *path, di_node_t *devnode, int *bus, int *slot, int *func) +{ + di_node_t rootnode, nexnode, node; + char *devfspath, *tmp; + int nexfd; + int len, *regbuf; + pci_regspec_t *reg; + + rootnode = di_init("/", DINFOCPYALL); + + if (rootnode == DI_NODE_NIL) + return (-1); + + for (*devnode = di_drv_first_node("ppt", rootnode); + *devnode != DI_NODE_NIL; + *devnode = di_drv_next_node(*devnode)) { + devfspath = di_devfs_path(*devnode); + if (strcmp(devfspath, path) == 0) { + /* + * Walk up device path. Last node before the root node + * is the nexus node. + */ + node = di_parent_node(*devnode); + while (node != rootnode) { + nexnode = node; + node = di_parent_node(node); + } + + di_devfs_path_free(devfspath); + break; + } + di_devfs_path_free(path); + } + + if (*devnode == DI_NODE_NIL || nexnode == rootnode) + return (-1); + + devfspath = di_devfs_path(nexnode); + (void) asprintf(&tmp, "/devices%s:reg", devfspath); + nexfd = open(tmp, O_RDWR); + free(tmp); + di_devfs_path_free(devfspath); + + len = di_prop_lookup_ints(DDI_DEV_T_ANY, *devnode, "reg", ®buf); + reg = (pci_regspec_t *)regbuf; + + *bus = (uchar_t)PCI_REG_BUS_G(reg->pci_phys_hi); + *slot = (uchar_t)PCI_REG_DEV_G(reg->pci_phys_hi); + *func = (uchar_t)PCI_REG_FUNC_G(reg->pci_phys_hi); + + return (nexfd); +} + +static int passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) { int bus, slot, func, error, memflags; @@ -649,6 +810,8 @@ passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) cap_ioctl_t pci_ioctls[] = { PCIOCREAD, PCIOCWRITE, PCIOCGETBAR }; cap_ioctl_t io_ioctls[] = { IODEV_PIO }; #endif + di_node_t devnode; + int nexfd; sc = NULL; error = 1; @@ -663,6 +826,7 @@ passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) goto done; } +#ifdef __FreeBSD__ if (pcifd < 0) { pcifd = open(_PATH_DEVPCI, O_RDWR, 0); if (pcifd < 0) { @@ -670,6 +834,7 @@ passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) goto done; } } +#endif #ifndef WITHOUT_CAPSICUM if (cap_rights_limit(pcifd, &rights) == -1 && errno != ENOSYS) @@ -678,6 +843,7 @@ passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) errx(EX_OSERR, "Unable to apply rights for sandbox"); #endif +#ifdef __FreeBSD__ if (iofd < 0) { iofd = open(_PATH_DEVIO, O_RDWR, 0); if (iofd < 0) { @@ -685,6 +851,7 @@ passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) goto done; } } +#endif #ifndef WITHOUT_CAPSICUM if (cap_rights_limit(iofd, &rights) == -1 && errno != ENOSYS) @@ -708,11 +875,19 @@ passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) errx(EX_OSERR, "Unable to apply rights for sandbox"); #endif +#ifdef __FreeBSD__ if (opts == NULL || sscanf(opts, "%d/%d/%d", &bus, &slot, &func) != 3) { warnx("invalid passthru options"); goto done; } +#else + if (opts == NULL || + (nexfd = devinfo_open(opts, &devnode, &bus, &slot, &func)) == -1) { + warnx("invalid passthru options"); + goto done; + } +#endif if (vm_assign_pptdev(ctx, bus, slot, func) != 0) { warnx("PCI device at %d/%d/%d is not using the ppt(4) driver", @@ -724,6 +899,8 @@ passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) pi->pi_arg = sc; sc->psc_pi = pi; + sc->devnode = devnode; + sc->nexfd = nexfd; /* initialize config space */ if ((error = cfginit(ctx, pi, bus, slot, func)) != 0) @@ -799,7 +976,7 @@ passthru_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, #endif /* Everything else just read from the device's config space */ - *rv = read_config(&sc->psc_sel, coff, bytes); + *rv = read_config(sc, coff, bytes); return (0); } @@ -865,7 +1042,7 @@ passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, } #endif - write_config(&sc->psc_sel, coff, bytes, val); + write_config(sc, coff, bytes, val); return (0); } @@ -875,7 +1052,9 @@ passthru_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, uint64_t offset, int size, uint64_t value) { struct passthru_softc *sc; +#ifdef __FreeBSD__ struct iodev_pio_req pio; +#endif sc = pi->pi_arg; @@ -883,6 +1062,7 @@ passthru_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, msix_table_write(ctx, vcpu, sc, offset, size, value); } else { assert(pi->pi_bar[baridx].type == PCIBAR_IO); +#ifdef __FreeBSD__ bzero(&pio, sizeof(struct iodev_pio_req)); pio.access = IODEV_PIO_WRITE; pio.port = sc->psc_bar[baridx].addr + offset; @@ -890,6 +1070,10 @@ passthru_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, pio.val = value; (void)ioctl(iofd, IODEV_PIO, &pio); +#else + (void) pcitool_reg_rw(sc, baridx, offset, size, value, + PCITOOL_DEVICE_SET_REG); +#endif } } @@ -898,7 +1082,9 @@ passthru_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, uint64_t offset, int size) { struct passthru_softc *sc; +#ifdef __FreeBSD__ struct iodev_pio_req pio; +#endif uint64_t val; sc = pi->pi_arg; @@ -907,6 +1093,7 @@ passthru_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, val = msix_table_read(sc, offset, size); } else { assert(pi->pi_bar[baridx].type == PCIBAR_IO); +#ifdef __FreeBSD__ bzero(&pio, sizeof(struct iodev_pio_req)); pio.access = IODEV_PIO_READ; pio.port = sc->psc_bar[baridx].addr + offset; @@ -916,6 +1103,10 @@ passthru_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, (void)ioctl(iofd, IODEV_PIO, &pio); val = pio.val; +#else + val = pcitool_reg_rw(sc, baridx, offset, size, 0, + PCITOOL_DEVICE_GET_REG); +#endif } return (val); diff --git a/usr/src/uts/i86pc/Makefile.files b/usr/src/uts/i86pc/Makefile.files index 4e8c4313d7..7f19c231f1 100644 --- a/usr/src/uts/i86pc/Makefile.files +++ b/usr/src/uts/i86pc/Makefile.files @@ -247,6 +247,7 @@ VMM_OBJS += vmm.o \ vmm_stat.o \ vmm_util.o \ x86.o \ + iommu.o \ vdev.o \ vatpic.o \ vatpit.o \ @@ -260,19 +261,20 @@ VMM_OBJS += vmm.o \ vmx_msr.o \ vmx.o \ vmx_support.o \ + vtd.o \ svm.o \ svm_msr.o \ npt.o \ vmcb.o \ svm_support.o \ amdv.o \ - iommu.o \ - sol_ppt.o \ vmm_sol_vm.o \ vmm_sol_glue.o VIONA_OBJS += viona.o +PPT_OBJS += ppt.o ppt_sol_glue.o + # # Build up defines and paths. # diff --git a/usr/src/uts/i86pc/Makefile.i86pc b/usr/src/uts/i86pc/Makefile.i86pc index b5673b182b..68cca2de49 100644 --- a/usr/src/uts/i86pc/Makefile.i86pc +++ b/usr/src/uts/i86pc/Makefile.i86pc @@ -268,6 +268,7 @@ DRV_KMODS += fipe # XXX: should be DRV_KMODS_64, but that doesn't work DRV_KMODS += vmm DRV_KMODS += viona +DRV_KMODS += ppt DRV_KMODS += cpudrv diff --git a/usr/src/uts/i86pc/io/vmm/intel/vtd.c b/usr/src/uts/i86pc/io/vmm/intel/vtd.c index f3b7a98a9d..73779733d0 100644 --- a/usr/src/uts/i86pc/io/vmm/intel/vtd.c +++ b/usr/src/uts/i86pc/io/vmm/intel/vtd.c @@ -42,6 +42,8 @@ __FBSDID("$FreeBSD$"); #include <machine/vmparam.h> #include <contrib/dev/acpica/include/acpi.h> +#include <sys/sunndi.h> + #include "io/iommu.h" /* @@ -235,19 +237,94 @@ vtd_translation_disable(struct vtdmap *vtdmap) ; } +static void * +vtd_map(ACPI_DMAR_HARDWARE_UNIT *drhd, int unit) +{ + struct ddi_parent_private_data *pdptr; + struct regspec reg; + dev_info_t *dip; + caddr_t regs; + ddi_acc_handle_t hdl; + int error; + + static ddi_device_acc_attr_t regs_attr = { + DDI_DEVICE_ATTR_V0, + DDI_NEVERSWAP_ACC, + DDI_STRICTORDER_ACC, + }; + + dip = ddi_add_child(ddi_root_node(), "vtd", + DEVI_SID_NODEID, unit); + +#if 0 + drhd->dr_dip = dip; +#endif + + reg.regspec_bustype = 0; + reg.regspec_addr = drhd->Address; + reg.regspec_size = PAGE_SIZE; + + /* + * update the reg properties + * + * reg property will be used for register + * set access + * + * refer to the bus_map of root nexus driver + * I/O or memory mapping: + * + * <bustype=0, addr=x, len=x>: memory + * <bustype=1, addr=x, len=x>: i/o + * <bustype>1, addr=0, len=x>: x86-compatibility i/o + */ + (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, + dip, "reg", (int *)®, + sizeof (struct regspec) / sizeof (int)); + + /* + * This is an artificially constructed dev_info, and we + * need to set a few more things to be able to use it + * for ddi_dma_alloc_handle/free_handle. + */ + ddi_set_driver(dip, ddi_get_driver(ddi_root_node())); + DEVI(dip)->devi_bus_dma_allochdl = + DEVI(ddi_get_driver((ddi_root_node()))); + + pdptr = kmem_zalloc(sizeof (struct ddi_parent_private_data) + + sizeof (struct regspec), KM_SLEEP); + pdptr->par_nreg = 1; + pdptr->par_reg = (struct regspec *)(pdptr + 1); + pdptr->par_reg->regspec_bustype = 0; + pdptr->par_reg->regspec_addr = drhd->Address; + pdptr->par_reg->regspec_size = PAGE_SIZE; + ddi_set_parent_data(dip, pdptr); + + error = ddi_regs_map_setup(dip, 0, ®s, 0, PAGE_SIZE, ®s_attr, + &hdl); + + if (error != DDI_SUCCESS) + return (NULL); + + return (regs); +} + static int vtd_init(void) { int i, units, remaining; struct vtdmap *vtdmap; vm_paddr_t ctx_paddr; - char *end, envname[32]; + char *end; +#ifdef __FreeBSD__ + char envname[32]; unsigned long mapaddr; +#endif ACPI_STATUS status; ACPI_TABLE_DMAR *dmar; ACPI_DMAR_HEADER *hdr; ACPI_DMAR_HARDWARE_UNIT *drhd; +#ifdef __FreeBSD__ /* * Allow the user to override the ACPI DMAR table by specifying the * physical address of each remapping unit. @@ -266,7 +343,9 @@ vtd_init(void) if (units > 0) goto skip_dmar; - +#else + units = 0; +#endif /* Search for DMAR table. */ status = AcpiGetTable(ACPI_SIG_DMAR, 0, (ACPI_TABLE_HEADER **)&dmar); if (ACPI_FAILURE(status)) @@ -289,7 +368,12 @@ vtd_init(void) break; drhd = (ACPI_DMAR_HARDWARE_UNIT *)hdr; +#ifdef __FreeBSD__ vtdmaps[units++] = (struct vtdmap *)PHYS_TO_DMAP(drhd->Address); +#else + vtdmaps[units] = (struct vtdmap *)vtd_map(drhd, units); + units++; +#endif if (units >= DRHD_MAX_UNITS) break; remaining -= hdr->Length; @@ -298,7 +382,9 @@ vtd_init(void) if (units <= 0) return (ENXIO); +#ifdef __FreeBSD__ skip_dmar: +#endif drhd_num = units; vtdmap = vtdmaps[0]; diff --git a/usr/src/uts/i86pc/io/vmm/io/iommu.c b/usr/src/uts/i86pc/io/vmm/io/iommu.c index 75cf1eccee..11ad6ebabd 100644 --- a/usr/src/uts/i86pc/io/vmm/io/iommu.c +++ b/usr/src/uts/i86pc/io/vmm/io/iommu.c @@ -41,6 +41,10 @@ __FBSDID("$FreeBSD$"); #include <machine/cpu.h> #include <machine/md_var.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/pci.h> + #include "vmm_util.h" #include "vmm_mem.h" #include "iommu.h" @@ -58,7 +62,9 @@ SYSCTL_INT(_hw_vmm_iommu, OID_AUTO, enable, CTLFLAG_RDTUN, &iommu_enable, 0, static struct iommu_ops *ops; static void *host_domain; +#ifdef __FreeBSD__ static eventhandler_tag add_tag, delete_tag; +#endif static __inline int IOMMU_INIT(void) @@ -154,6 +160,7 @@ IOMMU_DISABLE(void) (*ops->disable)(); } +#ifdef __FreeBSD__ static void iommu_pci_add(void *arg, device_t dev) { @@ -168,6 +175,16 @@ iommu_pci_delete(void *arg, device_t dev) iommu_remove_device(host_domain, pci_get_rid(dev)); } +#endif + +static int +iommu_find_device(dev_info_t *dip, void *unused) +{ + if (pcie_is_pci_device(dip)) + iommu_add_device(host_domain, pci_get_rid(dip)); + + return (DDI_WALK_CONTINUE); +} static void iommu_init(void) @@ -211,6 +228,7 @@ iommu_init(void) */ iommu_create_mapping(host_domain, 0, 0, maxaddr); +#ifdef __FreeBSD__ add_tag = EVENTHANDLER_REGISTER(pci_add_device, iommu_pci_add, NULL, 0); delete_tag = EVENTHANDLER_REGISTER(pci_delete_device, iommu_pci_delete, NULL, 0); @@ -227,6 +245,9 @@ iommu_init(void) } } } +#else + ddi_walk_devs(ddi_root_node(), iommu_find_device, NULL); +#endif IOMMU_ENABLE(); } @@ -234,7 +255,7 @@ iommu_init(void) void iommu_cleanup(void) { - +#ifdef __FreeBSD__ if (add_tag != NULL) { EVENTHANDLER_DEREGISTER(pci_add_device, add_tag); add_tag = NULL; @@ -243,6 +264,7 @@ iommu_cleanup(void) EVENTHANDLER_DEREGISTER(pci_delete_device, delete_tag); delete_tag = NULL; } +#endif IOMMU_DISABLE(); IOMMU_DESTROY_DOMAIN(host_domain); IOMMU_CLEANUP(); @@ -251,7 +273,7 @@ iommu_cleanup(void) void * iommu_create_domain(vm_paddr_t maxaddr) { - static volatile int iommu_initted; + static volatile u_int iommu_initted; if (iommu_initted < 2) { if (atomic_cmpset_int(&iommu_initted, 0, 1)) { diff --git a/usr/src/uts/i86pc/io/vmm/io/ppt.c b/usr/src/uts/i86pc/io/vmm/io/ppt.c index 4c9ff47458..bcf09209ed 100644 --- a/usr/src/uts/i86pc/io/vmm/io/ppt.c +++ b/usr/src/uts/i86pc/io/vmm/io/ppt.c @@ -36,18 +36,26 @@ __FBSDID("$FreeBSD$"); #include <sys/module.h> #include <sys/bus.h> #include <sys/pciio.h> +#ifdef __FreeBSD__ #include <sys/rman.h> +#endif #include <sys/smp.h> #include <sys/sysctl.h> #include <dev/pci/pcivar.h> #include <dev/pci/pcireg.h> +#ifdef __FreeBSD__ #include <machine/resource.h> +#endif #include <machine/vmm.h> #include <machine/vmm_dev.h> +#include <sys/conf.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> + #include "vmm_lapic.h" #include "vmm_ktr.h" @@ -89,20 +97,31 @@ struct pptdev { struct pptseg mmio[MAX_MMIOSEGS]; struct { int num_msgs; /* guest state */ - +#ifdef __FreeBSD__ int startrid; /* host state */ struct resource *res[MAX_MSIMSGS]; void *cookie[MAX_MSIMSGS]; +#else + boolean_t is_fixed; + size_t inth_sz; + ddi_intr_handle_t *inth; +#endif struct pptintr_arg arg[MAX_MSIMSGS]; } msi; struct { int num_msgs; +#ifdef __FreeBSD__ int startrid; int msix_table_rid; struct resource *msix_table_res; struct resource **res; void **cookie; +#else + size_t inth_sz; + size_t arg_sz; + ddi_intr_handle_t *inth; +#endif struct pptintr_arg *arg; } msix; }; @@ -116,6 +135,7 @@ SYSCTL_INT(_hw_vmm_ppt, OID_AUTO, devices, CTLFLAG_RD, &num_pptdevs, 0, static TAILQ_HEAD(, pptdev) pptdev_list = TAILQ_HEAD_INITIALIZER(pptdev_list); +#ifdef __FreeBSD__ static int ppt_probe(device_t dev) { @@ -146,6 +166,7 @@ ppt_probe(device_t dev) */ return (BUS_PROBE_NOWILDCARD); } +#endif static int ppt_attach(device_t dev) @@ -158,8 +179,10 @@ ppt_attach(device_t dev) TAILQ_INSERT_TAIL(&pptdev_list, ppt, next); ppt->dev = dev; +#ifdef __FreeBSD__ if (bootverbose) device_printf(dev, "attached\n"); +#endif return (0); } @@ -179,6 +202,7 @@ ppt_detach(device_t dev) return (0); } +#ifdef __FreeBSD__ static device_method_t ppt_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ppt_probe), @@ -190,6 +214,112 @@ static device_method_t ppt_methods[] = { static devclass_t ppt_devclass; DEFINE_CLASS_0(ppt, ppt_driver, ppt_methods, sizeof(struct pptdev)); DRIVER_MODULE(ppt, pci, ppt_driver, ppt_devclass, NULL, NULL); +#endif + +static void *ppt_state; + +static int +ppt_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + struct pptdev *ppt; + + if (cmd != DDI_ATTACH) + return (DDI_FAILURE); + + ddi_soft_state_zalloc(ppt_state, ddi_get_instance(dip)); + + ppt = ddi_get_soft_state(ppt_state, ddi_get_instance(dip)); + ppt->dev = dip; + + ddi_set_driver_private(dip, ppt); + + if (ppt_attach(dip) == 0) + return (DDI_SUCCESS); + + ddi_set_driver_private(dip, NULL); + + ddi_soft_state_free(ppt_state, ddi_get_instance(dip)); + + return (DDI_FAILURE); +} + +static int +ppt_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + if (cmd != DDI_DETACH) + return (DDI_FAILURE); + + if (ppt_detach(dip) != 0) + return (DDI_FAILURE); + + ddi_set_driver_private(dip, NULL); + + ddi_soft_state_free(ppt_state, ddi_get_instance(dip)); + + return (DDI_SUCCESS); +} + +static struct dev_ops ppt_ops = { + DEVO_REV, + 0, + ddi_no_info, + nulldev, /* identify */ + nulldev, /* probe */ + ppt_ddi_attach, + ppt_ddi_detach, + nodev, /* reset */ + (struct cb_ops *)NULL, + (struct bus_ops *)NULL +}; + +static struct modldrv modldrv = { + &mod_driverops, + "ppt", + &ppt_ops +}; + +static struct modlinkage modlinkage = { + MODREV_1, + &modldrv, + NULL +}; + +int +_init(void) +{ + int error; + + error = ddi_soft_state_init(&ppt_state, sizeof (struct pptdev), 0); + if (error) + return (error); + + error = mod_install(&modlinkage); + if (error) + ddi_soft_state_fini(&ppt_state); + + return (error); +} + +int +_fini(void) +{ + int error; + + error = mod_remove(&modlinkage); + if (error) + return (error); + + ddi_soft_state_fini(&ppt_state); + + return (0); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + static struct pptdev * ppt_find(int bus, int slot, int func) @@ -228,13 +358,17 @@ static void ppt_teardown_msi(struct pptdev *ppt) { int i, rid; +#ifdef __FreeBSD__ void *cookie; struct resource *res; +#endif + int intr_cap = 0; if (ppt->msi.num_msgs == 0) return; for (i = 0; i < ppt->msi.num_msgs; i++) { +#ifdef __FreeBSD__ rid = ppt->msi.startrid + i; res = ppt->msi.res[i]; cookie = ppt->msi.cookie[i]; @@ -247,10 +381,29 @@ ppt_teardown_msi(struct pptdev *ppt) ppt->msi.res[i] = NULL; ppt->msi.cookie[i] = NULL; +#else + (void) ddi_intr_get_cap(ppt->msi.inth[i], &intr_cap); + if (intr_cap & DDI_INTR_FLAG_BLOCK) + ddi_intr_block_disable(&ppt->msi.inth[i], 1); + else + ddi_intr_disable(ppt->msi.inth[i]); + + ddi_intr_remove_handler(ppt->msi.inth[i]); + ddi_intr_free(ppt->msi.inth[i]); + + ppt->msi.inth[i] = NULL; +#endif } +#ifdef __FreeBSD__ if (ppt->msi.startrid == 1) pci_release_msi(ppt->dev); +#else + kmem_free(ppt->msi.inth, ppt->msi.inth_sz); + ppt->msi.inth = NULL; + ppt->msi.inth_sz = 0; + ppt->msi.is_fixed = B_FALSE; +#endif ppt->msi.num_msgs = 0; } @@ -258,6 +411,7 @@ ppt_teardown_msi(struct pptdev *ppt) static void ppt_teardown_msix_intr(struct pptdev *ppt, int idx) { +#ifdef __FreeBSD__ int rid; struct resource *res; void *cookie; @@ -274,6 +428,22 @@ ppt_teardown_msix_intr(struct pptdev *ppt, int idx) ppt->msix.res[idx] = NULL; ppt->msix.cookie[idx] = NULL; +#else + if (ppt->msix.inth != NULL && ppt->msix.inth[idx] != NULL) { + int intr_cap; + + (void) ddi_intr_get_cap(ppt->msix.inth[idx], &intr_cap); + if (intr_cap & DDI_INTR_FLAG_BLOCK) + ddi_intr_block_disable(&ppt->msix.inth[idx], 1); + else + ddi_intr_disable(ppt->msix.inth[idx]); + + ddi_intr_remove_handler(ppt->msix.inth[idx]); + ddi_intr_free(ppt->msix.inth[idx]); + + ppt->msix.inth[idx] = NULL; + } +#endif } static void @@ -287,6 +457,7 @@ ppt_teardown_msix(struct pptdev *ppt) for (i = 0; i < ppt->msix.num_msgs; i++) ppt_teardown_msix_intr(ppt, i); +#ifdef __FreeBSD__ if (ppt->msix.msix_table_res) { bus_release_resource(ppt->dev, SYS_RES_MEMORY, ppt->msix.msix_table_rid, @@ -300,6 +471,16 @@ ppt_teardown_msix(struct pptdev *ppt) free(ppt->msix.arg, M_PPTMSIX); pci_release_msi(ppt->dev); +#else + if (ppt->msix.inth) { + kmem_free(ppt->msix.inth, ppt->msix.inth_sz); + ppt->msix.inth = NULL; + ppt->msix.inth_sz = 0; + kmem_free(ppt->msix.arg, ppt->msix.arg_sz); + ppt->msix.arg = NULL; + ppt->msix.arg_sz = 0; + } +#endif ppt->msix.num_msgs = 0; } @@ -453,13 +634,18 @@ ppt_map_mmio(struct vm *vm, int bus, int slot, int func, return (ENOENT); } +#ifdef __FreeBSD__ static int pptintr(void *arg) +#else +static uint_t +pptintr(char *arg, char *unused) +#endif { struct pptdev *ppt; struct pptintr_arg *pptarg; - pptarg = arg; + pptarg = (struct pptintr_arg *)arg; ppt = pptarg->pptdev; if (ppt->vm != NULL) @@ -475,10 +661,14 @@ pptintr(void *arg) * For legacy interrupts give other filters a chance in case * the interrupt was not generated by the passthrough device. */ +#ifdef __FreeBSD__ if (ppt->msi.startrid == 0) return (FILTER_STRAY); else return (FILTER_HANDLED); +#else + return (ppt->msi.is_fixed ? DDI_INTR_UNCLAIMED : DDI_INTR_CLAIMED); +#endif } int @@ -487,6 +677,7 @@ ppt_setup_msi(struct vm *vm, int vcpu, int bus, int slot, int func, { int i, rid, flags; int msi_count, startrid, error, tmp; + int intr_type, intr_cap = 0; struct pptdev *ppt; if (numvec < 0 || numvec > MAX_MSIMSGS) @@ -504,6 +695,7 @@ ppt_setup_msi(struct vm *vm, int vcpu, int bus, int slot, int func, if (numvec == 0) /* nothing more to do */ return (0); +#ifdef __FreeBSD__ flags = RF_ACTIVE; msi_count = pci_msi_count(ppt->dev); if (msi_count == 0) { @@ -563,6 +755,65 @@ ppt_setup_msi(struct vm *vm, int vcpu, int bus, int slot, int func, if (error != 0) break; } +#else + if (ddi_intr_get_navail(ppt->dev, DDI_INTR_TYPE_MSI, &msi_count) != + DDI_SUCCESS) { + if (ddi_intr_get_navail(ppt->dev, DDI_INTR_TYPE_FIXED, + &msi_count) != DDI_SUCCESS) + return (EINVAL); + + intr_type = DDI_INTR_TYPE_FIXED; + ppt->msi.is_fixed = B_TRUE; + } else { + intr_type = DDI_INTR_TYPE_MSI; + } + + /* + * The device must be capable of supporting the number of vectors + * the guest wants to allocate. + */ + if (numvec > msi_count) + return (EINVAL); + + ppt->msi.inth_sz = numvec * sizeof (ddi_intr_handle_t); + ppt->msi.inth = kmem_zalloc(ppt->msi.inth_sz, KM_SLEEP); + if (ddi_intr_alloc(ppt->dev, ppt->msi.inth, intr_type, 0, + numvec, &msi_count, 0) != DDI_SUCCESS) { + kmem_free(ppt->msi.inth, ppt->msi.inth_sz); + return (EINVAL); + } + + /* + * Again, make sure we actually got as many vectors as the guest wanted + * to allocate. + */ + if (numvec != msi_count) { + ppt_teardown_msi(ppt); + return (EINVAL); + } + /* + * Set up & enable interrupt handler for each vector. + */ + for (i = 0; i < numvec; i++) { + ppt->msi.num_msgs = i + 1; + ppt->msi.arg[i].pptdev = ppt; + ppt->msi.arg[i].addr = addr; + ppt->msi.arg[i].msg_data = msg + i; + + if (ddi_intr_add_handler(ppt->msi.inth[i], pptintr, + &ppt->msi.arg[i], NULL) != DDI_SUCCESS) + break; + + (void) ddi_intr_get_cap(ppt->msi.inth[i], &intr_cap); + if (intr_cap & DDI_INTR_FLAG_BLOCK) + error = ddi_intr_block_enable(&ppt->msi.inth[i], 1); + else + error = ddi_intr_enable(ppt->msi.inth[i]); + + if (error != DDI_SUCCESS) + break; + } +#endif if (i < numvec) { ppt_teardown_msi(ppt); @@ -580,6 +831,7 @@ ppt_setup_msix(struct vm *vm, int vcpu, int bus, int slot, int func, struct pci_devinfo *dinfo; int numvec, alloced, rid, error; size_t res_size, cookie_size, arg_size; + int intr_cap; ppt = ppt_find(bus, slot, func); if (ppt == NULL) @@ -587,6 +839,7 @@ ppt_setup_msix(struct vm *vm, int vcpu, int bus, int slot, int func, if (ppt->vm != vm) /* Make sure we own this device */ return (EBUSY); +#ifdef __FreeBSD__ dinfo = device_get_ivars(ppt->dev); if (!dinfo) return (ENXIO); @@ -631,11 +884,46 @@ ppt_setup_msix(struct vm *vm, int vcpu, int bus, int slot, int func, return (error == 0 ? ENOSPC: error); } } +#else + /* + * First-time configuration: + * Allocate the MSI-X table + * Allocate the IRQ resources + * Set up some variables in ppt->msix + */ + if (ppt->msix.num_msgs == 0) { + if (ddi_intr_get_navail(ppt->dev, DDI_INTR_TYPE_MSIX, &numvec) != + DDI_SUCCESS) + return (EINVAL); + + ppt->msix.num_msgs = numvec; + ppt->msix.arg_sz = numvec * sizeof(ppt->msix.arg[0]); + ppt->msix.arg = kmem_zalloc(ppt->msix.arg_sz, KM_SLEEP); + ppt->msix.inth_sz = numvec * sizeof(ddi_intr_handle_t); + ppt->msix.inth = kmem_zalloc(ppt->msix.inth_sz, KM_SLEEP); + + if (ddi_intr_alloc(ppt->dev, ppt->msix.inth, DDI_INTR_TYPE_MSIX, + 0, numvec, &alloced, 0) != DDI_SUCCESS) { + kmem_free(ppt->msix.arg, ppt->msix.arg_sz); + kmem_free(ppt->msix.inth, ppt->msix.inth_sz); + ppt->msix.arg = NULL; + ppt->msix.inth = NULL; + ppt->msix.arg_sz = ppt->msix.inth_sz = 0; + return (EINVAL); + } + + if (numvec != alloced) { + ppt_teardown_msix(ppt); + return (EINVAL); + } + } +#endif if ((vector_control & PCIM_MSIX_VCTRL_MASK) == 0) { /* Tear down the IRQ if it's already set up */ ppt_teardown_msix_intr(ppt, idx); +#ifdef __FreeBSD__ /* Allocate the IRQ resource */ ppt->msix.cookie[idx] = NULL; rid = ppt->msix.startrid + idx; @@ -643,12 +931,13 @@ ppt_setup_msix(struct vm *vm, int vcpu, int bus, int slot, int func, &rid, RF_ACTIVE); if (ppt->msix.res[idx] == NULL) return (ENXIO); - +#endif ppt->msix.arg[idx].pptdev = ppt; ppt->msix.arg[idx].addr = addr; ppt->msix.arg[idx].msg_data = msg; /* Setup the MSI-X interrupt */ +#ifdef __FreeBSD__ error = bus_setup_intr(ppt->dev, ppt->msix.res[idx], INTR_TYPE_NET | INTR_MPSAFE, pptintr, NULL, &ppt->msix.arg[idx], @@ -661,6 +950,22 @@ ppt_setup_msix(struct vm *vm, int vcpu, int bus, int slot, int func, ppt->msix.res[idx] = NULL; return (ENXIO); } +#else + if (ddi_intr_add_handler(ppt->msix.inth[idx], pptintr, + &ppt->msix.arg[idx], NULL) != DDI_SUCCESS) + return (ENXIO); + + (void) ddi_intr_get_cap(ppt->msix.inth[idx], &intr_cap); + if (intr_cap & DDI_INTR_FLAG_BLOCK) + error = ddi_intr_block_enable(&ppt->msix.inth[idx], 1); + else + error = ddi_intr_enable(ppt->msix.inth[idx]); + + if (error != DDI_SUCCESS) { + ddi_intr_remove_handler(ppt->msix.inth[idx]); + return (ENXIO); + } +#endif } else { /* Masked, tear it down if it's already been set up */ ppt_teardown_msix_intr(ppt, idx); diff --git a/usr/src/uts/i86pc/io/vmm/io/ppt.conf b/usr/src/uts/i86pc/io/vmm/io/ppt.conf new file mode 100644 index 0000000000..698cecb6f8 --- /dev/null +++ b/usr/src/uts/i86pc/io/vmm/io/ppt.conf @@ -0,0 +1,14 @@ +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2017 Joyent, Inc. +# + diff --git a/usr/src/uts/i86pc/io/vmm/io/ppt_sol_glue.c b/usr/src/uts/i86pc/io/vmm/io/ppt_sol_glue.c new file mode 100644 index 0000000000..3fa38d209f --- /dev/null +++ b/usr/src/uts/i86pc/io/vmm/io/ppt_sol_glue.c @@ -0,0 +1,177 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2017 Joyent, Inc. + */ + +/* + * PCI/PCIe interfaces needed by ppt + */ + +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/pcie.h> +#include <sys/pci_cap.h> + +#include <sys/bus.h> +#include <dev/pci/pcivar.h> + +static bool +pcie_wait_for_pending_transactions(dev_info_t *dip, u_int max_delay) +{ + uint16_t cap_ptr, devsts; + ddi_acc_handle_t hdl; + + if (pci_config_setup(dip, &hdl) != DDI_SUCCESS) + return (false); + + if (PCI_CAP_LOCATE(hdl, PCI_CAP_ID_PCI_E, &cap_ptr) != DDI_SUCCESS) { + pci_config_teardown(&hdl); + return (false); + } + + devsts = PCI_CAP_GET16(hdl, NULL, cap_ptr, PCIE_DEVSTS); + while ((devsts & PCIE_DEVSTS_TRANS_PENDING) != 0) { + if (max_delay == 0) { + pci_config_teardown(&hdl); + return (false); + } + + /* Poll once every 100 milliseconds up to the timeout. */ + if (max_delay > 100) { + delay(drv_usectohz(100 * 1000)); + max_delay -= 100; + } else { + delay(drv_usectohz(max_delay * 1000)); + max_delay = 0; + } + devsts = PCI_CAP_GET16(hdl, NULL, cap_ptr, PCIE_DEVSTS); + } + + pci_config_teardown(&hdl); + return (true); +} + +int +pcie_get_max_completion_timeout(device_t dev) +{ + dev_info_t *dip = dev; + int timo = 0; + uint16_t cap_ptr; + ddi_acc_handle_t hdl; + int timo_ranges[] = { /* timeout ranges */ + 50000, /* 50ms */ + 100, /* 100us */ + 10000, /* 10ms */ + 0, + 0, + 55000, /* 55ms */ + 210000, /* 210ms */ + 0, + 0, + 900000, /* 900ms */ + 3500000, /* 3.5s */ + 0, + 0, + 13000000, /* 13s */ + 64000000, /* 64s */ + 0 + }; + + if (pci_config_setup(dip, &hdl) != DDI_SUCCESS) + return (50000); /* default 50ms */ + + if (PCI_CAP_LOCATE(hdl, PCI_CAP_ID_PCI_E, &cap_ptr) != DDI_SUCCESS) + goto out; + + if ((PCI_CAP_GET16(hdl, NULL, cap_ptr, PCIE_PCIECAP) & + PCIE_PCIECAP_VER_MASK) < PCIE_PCIECAP_VER_2_0) + goto out; + + if ((PCI_CAP_GET16(hdl, NULL, cap_ptr, PCIE_DEVCAP2) & + PCIE_DEVCTL2_COM_TO_RANGE_MASK) == 0) + goto out; + + timo = timo_ranges[PCI_CAP_GET16(hdl, NULL, cap_ptr, PCIE_DEVCTL2) & + PCIE_DEVCAP2_COM_TO_RANGE_MASK]; + +out: + if (timo == 0) + timo = 50000; /* default 50ms */ + + pci_config_teardown(&hdl); + return (timo); +} + +bool +pcie_flr(device_t dev, u_int max_delay, bool force) +{ + dev_info_t *dip = dev; + bool ret = false; + uint16_t cap_ptr, ctl, cmd; + ddi_acc_handle_t hdl; + int compl_delay; + + if (pci_config_setup(dip, &hdl) != DDI_SUCCESS) + return (false); + + if (PCI_CAP_LOCATE(hdl, PCI_CAP_ID_PCI_E, &cap_ptr) != DDI_SUCCESS) + goto fail; + + if ((PCI_CAP_GET16(hdl, NULL, cap_ptr, PCIE_DEVCAP) & PCIE_DEVCAP_FLR) + == 0) + goto fail; + + /* + * Disable busmastering to prevent generation of new + * transactions while waiting for the device to go idle. If + * the idle timeout fails, the command register is restored + * which will re-enable busmastering. + */ + cmd = pci_config_get16(hdl, PCI_CONF_COMM); + pci_config_put16(hdl, PCI_CONF_COMM, cmd & ~PCI_COMM_ME); + if (!pcie_wait_for_pending_transactions(dev, max_delay)) { + if (!force) { + pci_config_put16(hdl, PCI_CONF_COMM, cmd); + goto fail; + } + dev_err(dip, CE_WARN, + "?Resetting with transactions pending after %d ms\n", + max_delay); + + /* + * Extend the post-FLR delay to cover the maximum + * Completion Timeout delay of anything in flight + * during the FLR delay. Enforce a minimum delay of + * at least 10ms. + */ + compl_delay = pcie_get_max_completion_timeout(dev) / 1000; + if (compl_delay < 10) + compl_delay = 10; + } else + compl_delay = 0; + + /* Initiate the reset. */ + ctl = PCI_CAP_GET16(hdl, NULL, cap_ptr, PCIE_DEVCTL); + (void) PCI_CAP_PUT16(hdl, NULL, cap_ptr, PCIE_DEVCTL, + ctl | PCIE_DEVCTL_INITIATE_FLR); + + /* Wait for at least 100ms */ + delay(drv_usectohz((100 + compl_delay) * 1000)); + + pci_config_teardown(&hdl); + return (true); + +fail: + pci_config_teardown(&hdl); + return (ret); +} diff --git a/usr/src/uts/i86pc/io/vmm/io/sol_iommu.c b/usr/src/uts/i86pc/io/vmm/io/sol_iommu.c deleted file mode 100644 index 989e88e17b..0000000000 --- a/usr/src/uts/i86pc/io/vmm/io/sol_iommu.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This file and its contents are supplied under the terms of the - * Common Development and Distribution License ("CDDL"), version 1.0. - * You may only use this file in accordance with the terms of version - * 1.0 of the CDDL. - * - * A full copy of the text of the CDDL should have accompanied this - * source. A copy of the CDDL is also available via the Internet at - * http://www.illumos.org/license/CDDL. - */ - -/* - * Copyright 2017 Joyent, Inc. - */ - -#include <sys/types.h> -#include <sys/param.h> -#include <sys/cmn_err.h> - -/* - * IOMMU Stub - * - * Until proper iommu support can be wired into bhyve, stub out all the - * functions to either fail, if reasonable, or panic. - */ - -void -iommu_cleanup(void) -{ -} - -void * -iommu_host_domain(void) -{ - return (NULL); -} - -/*ARGSUSED*/ -void * -iommu_create_domain(vm_paddr_t maxaddr) -{ - return (NULL); -} - -/*ARGSUSED*/ -void -iommu_destroy_domain(void *dom) -{ - panic("unimplemented"); -} - -/*ARGSUSED*/ -void -iommu_create_mapping(void *dom, vm_paddr_t gpa, vm_paddr_t hpa, size_t len) -{ - panic("unimplemented"); -} - -/*ARGSUSED*/ -void -iommu_remove_mapping(void *dom, vm_paddr_t gpa, size_t len) -{ - panic("unimplemented"); -} - -/*ARGSUSED*/ -void -iommu_add_device(void *dom, uint16_t rid) -{ - panic("unimplemented"); -} - -/*ARGSUSED*/ -void -iommu_remove_device(void *dom, uint16_t rid) -{ - panic("unimplemented"); -} - -/*ARGSUSED*/ -void -iommu_invalidate_tlb(void *domain) -{ - panic("unimplemented"); -} - diff --git a/usr/src/uts/i86pc/io/vmm/io/sol_ppt.c b/usr/src/uts/i86pc/io/vmm/io/sol_ppt.c deleted file mode 100644 index ba8bf9de36..0000000000 --- a/usr/src/uts/i86pc/io/vmm/io/sol_ppt.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This file and its contents are supplied under the terms of the - * Common Development and Distribution License ("CDDL"), version 1.0. - * You may only use this file in accordance with the terms of version - * 1.0 of the CDDL. - * - * A full copy of the text of the CDDL should have accompanied this - * source. A copy of the CDDL is also available via the Internet at - * http://www.illumos.org/license/CDDL. - */ - -/* - * Copyright 2017 Joyent, Inc. - */ - -#include <sys/types.h> -#include <sys/errno.h> -#include <sys/cmn_err.h> - -#include <sys/vmm.h> - -/* - * PCI Pass-Through Stub - * - * Until proper passthrough support can be wired into bhyve, stub out all the - * functions to either fail. - */ - -int -ppt_unassign_all(struct vm *vm) -{ - return (0); -} - -/*ARGSUSED*/ -int -ppt_map_mmio(struct vm *vm, int bus, int slot, int func, vm_paddr_t gpa, - size_t len, vm_paddr_t hpa) -{ - return (ENXIO); -} - -/*ARGSUSED*/ -int -ppt_setup_msi(struct vm *vm, int vcpu, int bus, int slot, int func, - uint64_t addr, uint64_t msg, int numvec) -{ - return (ENXIO); -} - -/*ARGSUSED*/ -int -ppt_setup_msix(struct vm *vm, int vcpu, int bus, int slot, int func, int idx, - uint64_t addr, uint64_t msg, uint32_t vector_control) -{ - return (ENXIO); -} - -/*ARGSUSED*/ -int -ppt_assigned_devices(struct vm *vm) -{ - return (0); -} - -/*ARGSUSED*/ -boolean_t -ppt_is_mmio(struct vm *vm, vm_paddr_t gpa) -{ - return (B_FALSE); -} - -/*ARGSUSED*/ -int -ppt_avail_devices(void) -{ - return (0); -} - -/*ARGSUSED*/ -int -ppt_assign_device(struct vm *vm, int bus, int slot, int func) -{ - return (ENOENT); -} - -/*ARGSUSED*/ -int -ppt_unassign_device(struct vm *vm, int bus, int slot, int func) -{ - return (ENXIO); -} diff --git a/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c b/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c index 77e547e564..2e3d15222d 100644 --- a/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c +++ b/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c @@ -31,6 +31,7 @@ #include <vm/vm.h> #include <vm/seg_dev.h> +#include "io/ppt.h" #include "io/vatpic.h" #include "io/vioapic.h" #include "io/vrtc.h" @@ -584,48 +585,60 @@ vmmdev_do_ioctl(vmm_softc_t *sc, int cmd, intptr_t arg, int md, break; } - /* XXXJOY: punt on these for now */ case VM_PPTDEV_MSI: { struct vm_pptdev_msi pptmsi; - if (ddi_copyin(datap, &pptmsi, sizeof (pptmsi), md)) { error = EFAULT; break; } - return (ENOTTY); + error = ppt_setup_msi(sc->vmm_vm, pptmsi.vcpu, pptmsi.bus, + pptmsi.slot, pptmsi.func, pptmsi.addr, pptmsi.msg, + pptmsi.numvec); + break; } case VM_PPTDEV_MSIX: { struct vm_pptdev_msix pptmsix; - if (ddi_copyin(datap, &pptmsix, sizeof (pptmsix), md)) { error = EFAULT; break; } - return (ENOTTY); + error = ppt_setup_msix(sc->vmm_vm, pptmsix.vcpu, pptmsix.bus, + pptmsix.slot, pptmsix.func, pptmsix.idx, pptmsix.addr, + pptmsix.msg, pptmsix.vector_control); + break; } case VM_MAP_PPTDEV_MMIO: { struct vm_pptdev_mmio pptmmio; - if (ddi_copyin(datap, &pptmmio, sizeof (pptmmio), md)) { error = EFAULT; break; } - return (ENOTTY); + error = ppt_map_mmio(sc->vmm_vm, pptmmio.bus, pptmmio.slot, + pptmmio.func, pptmmio.gpa, pptmmio.len, pptmmio.hpa); + break; + } + case VM_BIND_PPTDEV: { + struct vm_pptdev pptdev; + if (ddi_copyin(datap, &pptdev, sizeof (pptdev), md)) { + error = EFAULT; + break; + } + error = vm_assign_pptdev(sc->vmm_vm, pptdev.bus, pptdev.slot, + pptdev.func); + break; } - case VM_BIND_PPTDEV: case VM_UNBIND_PPTDEV: { struct vm_pptdev pptdev; - if (ddi_copyin(datap, &pptdev, sizeof (pptdev), md)) { error = EFAULT; break; } - return (ENOTTY); + error = vm_unassign_pptdev(sc->vmm_vm, pptdev.bus, pptdev.slot, + pptdev.func); + break; } - case VM_INJECT_EXCEPTION: { struct vm_exception vmexc; - if (ddi_copyin(datap, &vmexc, sizeof (vmexc), md)) { error = EFAULT; break; diff --git a/usr/src/uts/i86pc/io/vmm/vmm_sol_glue.c b/usr/src/uts/i86pc/io/vmm/vmm_sol_glue.c index 4c33909d16..8ade66e819 100644 --- a/usr/src/uts/i86pc/io/vmm/vmm_sol_glue.c +++ b/usr/src/uts/i86pc/io/vmm/vmm_sol_glue.c @@ -76,9 +76,12 @@ #include <sys/psm_defs.h> #include <sys/smp_impldefs.h> +#include <sys/x86_archext.h> + #include <machine/cpufunc.h> #include <machine/fpu.h> #include <machine/md_var.h> +#include <machine/pmap.h> #include <machine/specialreg.h> #include <machine/vmm.h> #include <sys/vmm_impl.h> @@ -100,6 +103,18 @@ u_char const bin2bcd_data[] = { 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99 }; +void +pmap_invalidate_cache(void) +{ + cpuset_t cpuset; + + kpreempt_disable(); + cpuset_all_but(&cpuset, CPU->cpu_id); + xc_call(NULL, NULL, NULL, CPUSET2BV(cpuset), (xc_func_t)invalidate_cache); + invalidate_cache(); + kpreempt_enable(); +} + vm_paddr_t pmap_kextract(vm_offset_t va) { diff --git a/usr/src/uts/i86pc/ppt/Makefile b/usr/src/uts/i86pc/ppt/Makefile new file mode 100644 index 0000000000..3d4de2a8f9 --- /dev/null +++ b/usr/src/uts/i86pc/ppt/Makefile @@ -0,0 +1,75 @@ +# +# COPYRIGHT 2013 Pluribus Networks Inc. +# +# All rights reserved. This copyright notice is Copyright Management +# Information under 17 USC 1202 and is included to protect this work and +# deter copyright infringement. Removal or alteration of this Copyright +# Management Information without the express written permission from +# Pluribus Networks Inc is prohibited, and any such unauthorized removal +# or alteration will be a violation of federal law. + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = ppt +OBJECTS = $(PPT_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(PPT_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(USR_DRV_DIR)/$(MODULE) +CONF_SRCDIR = $(UTSBASE)/i86pc/io/vmm/io + +# +# Include common rules. +# +include $(UTSBASE)/i86pc/Makefile.i86pc + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) + +# +# Overrides and additions +# +ALL_BUILDS = $(ALL_BUILDSONLY64) +DEF_BUILDS = $(DEF_BUILDSONLY64) +PRE_INC_PATH = -I$(COMPAT)/freebsd -I$(COMPAT)/freebsd/amd64 \ + -I$(CONTRIB)/freebsd -I$(CONTRIB)/freebsd/amd64 +INC_PATH += -I$(UTSBASE)/i86pc/io/vmm -I$(UTSBASE)/i86pc/io/vmm/io +AS_INC_PATH += -I$(UTSBASE)/i86pc/io/vmm -I$(OBJS_DIR) + +LDFLAGS += -dy -N drv/vmm -N misc/pcie + +$(OBJS_DIR)/ppt.o := CERRWARN += -_gcc=-Wno-unused-variable + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/i86pc/Makefile.targ diff --git a/usr/src/uts/i86pc/vmm/Makefile b/usr/src/uts/i86pc/vmm/Makefile index 603821d008..368f32ddb6 100644 --- a/usr/src/uts/i86pc/vmm/Makefile +++ b/usr/src/uts/i86pc/vmm/Makefile @@ -88,6 +88,10 @@ CFLAGS += -_gcc=-Wno-format $(OBJS_DIR)/vmm.o := CERRWARN += -_gcc=-Wno-pointer-sign -_gcc=-Wno-type-limits $(OBJS_DIR)/svm.o := CERRWARN += -_gcc=-Wno-pointer-sign -_gcc=-Wno-type-limits +$(OBJS_DIR)/vmx.o := CERRWARN += -_gcc=-Wno-unused-variable +$(OBJS_DIR)/iommu.o := CERRWARN += -_gcc=-Wno-unused-variable + +LDFLAGS += -dy -N misc/acpica -N misc/pcie OFFSETS_VMX = $(CONF_SRCDIR)/intel/offsets.in OFFSETS_SVM = $(CONF_SRCDIR)/amd/offsets.in diff --git a/usr/src/uts/intel/ia32/ml/modstubs.s b/usr/src/uts/intel/ia32/ml/modstubs.s index f2bc9b9a8a..764816e8b8 100644 --- a/usr/src/uts/intel/ia32/ml/modstubs.s +++ b/usr/src/uts/intel/ia32/ml/modstubs.s @@ -1429,6 +1429,23 @@ fcnname/**/_info: \ END_MODULE(apix); #endif +/* + * Stubs for ppt module (bhyve PCI passthrough driver) + */ +#ifndef PPT_MODULE + MODULE(ppt,drv); + WSTUB(ppt, ppt_unassign_all, nomod_zero); + WSTUB(ppt, ppt_map_mmio, nomod_einval); + WSTUB(ppt, ppt_setup_msi, nomod_einval); + WSTUB(ppt, ppt_setup_msix, nomod_einval); + WSTUB(ppt, ppt_assigned_devices, nomod_zero); + WSTUB(ppt, ppt_is_mmio, nomod_zero); + WSTUB(ppt, ppt_avail_devices, nomod_zero); + WSTUB(ppt, ppt_assign_device, nomod_einval); + WSTUB(ppt, ppt_unassign_device, nomod_einval); + END_MODULE(ppt); +#endif + / this is just a marker for the area of text that contains stubs ENTRY_NP(stubs_end) |