diff options
| author | Hans Rosenfeld <hans.rosenfeld@joyent.com> | 2018-06-11 23:08:09 +0000 |
|---|---|---|
| committer | Hans Rosenfeld <hans.rosenfeld@joyent.com> | 2018-06-12 08:24:25 +0000 |
| commit | 76d673bac97855d00448cd767b6f3439f4be8d12 (patch) | |
| tree | d782e4ff3a52158b0201765efaa6d694c303b8c4 | |
| parent | a33f24fd1b0665ec1cb9f57b5e860fed1d393793 (diff) | |
| download | illumos-joyent-76d673bac97855d00448cd767b6f3439f4be8d12.tar.gz | |
OS-6740 bhyve vtd leaks mapping resources
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
Approved by: Patrick Mooney <patrick.mooney@joyent.com>
| -rw-r--r-- | usr/src/uts/i86pc/Makefile.files | 1 | ||||
| -rw-r--r-- | usr/src/uts/i86pc/io/vmm/intel/vtd.c | 102 | ||||
| -rw-r--r-- | usr/src/uts/i86pc/io/vmm/intel/vtd_sol.c | 83 | ||||
| -rw-r--r-- | usr/src/uts/i86pc/io/vmm/io/iommu.c | 30 | ||||
| -rw-r--r-- | usr/src/uts/i86pc/io/vmm/vmm.c | 1 |
5 files changed, 160 insertions, 57 deletions
diff --git a/usr/src/uts/i86pc/Makefile.files b/usr/src/uts/i86pc/Makefile.files index 7fc3cfec14..768aa390b9 100644 --- a/usr/src/uts/i86pc/Makefile.files +++ b/usr/src/uts/i86pc/Makefile.files @@ -262,6 +262,7 @@ VMM_OBJS += vmm.o \ vmx.o \ vmx_support.o \ vtd.o \ + vtd_sol.o \ svm.o \ svm_msr.o \ npt.o \ diff --git a/usr/src/uts/i86pc/io/vmm/intel/vtd.c b/usr/src/uts/i86pc/io/vmm/intel/vtd.c index d8ad20ee46..902080e34c 100644 --- a/usr/src/uts/i86pc/io/vmm/intel/vtd.c +++ b/usr/src/uts/i86pc/io/vmm/intel/vtd.c @@ -122,6 +122,9 @@ static int drhd_num; static struct vtdmap *vtdmaps[DRHD_MAX_UNITS]; static int max_domains; typedef int (*drhd_ident_func_t)(void); +#ifndef __FreeBSD__ +static dev_info_t *vtddips[DRHD_MAX_UNITS]; +#endif static uint64_t root_table[PAGE_SIZE / sizeof(uint64_t)] __aligned(4096); static uint64_t ctx_tables[256][PAGE_SIZE / sizeof(uint64_t)] __aligned(4096); @@ -240,11 +243,8 @@ vtd_translation_disable(struct vtdmap *vtdmap) } static void * -vtd_map(ACPI_DMAR_HARDWARE_UNIT *drhd, int unit) +vtd_map(dev_info_t *dip) { - struct ddi_parent_private_data *pdptr; - struct regspec reg; - dev_info_t *dip; caddr_t regs; ddi_acc_handle_t hdl; int error; @@ -255,61 +255,33 @@ vtd_map(ACPI_DMAR_HARDWARE_UNIT *drhd, int unit) 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); + ddi_set_driver_private(dip, hdl); + return (regs); } +static void +vtd_unmap(dev_info_t *dip) +{ + ddi_acc_handle_t hdl = ddi_get_driver_private(dip); + + if (hdl != NULL) + ddi_regs_map_free(&hdl); +} + +#ifndef __FreeBSD__ +/* + * This lives in vtd_sol.c for license reasons. + */ +extern dev_info_t *vtd_get_dip(ACPI_DMAR_HARDWARE_UNIT *, int); +#endif + static int vtd_init(void) { @@ -373,7 +345,10 @@ vtd_init(void) #ifdef __FreeBSD__ vtdmaps[units++] = (struct vtdmap *)PHYS_TO_DMAP(drhd->Address); #else - vtdmaps[units] = (struct vtdmap *)vtd_map(drhd, units); + vtddips[units] = vtd_get_dip(drhd, units); + vtdmaps[units] = (struct vtdmap *)vtd_map(vtddips[units]); + if (vtdmaps[units] == NULL) + goto fail; units++; #endif if (units >= DRHD_MAX_UNITS) @@ -407,11 +382,36 @@ skip_dmar: } return (0); + +#ifndef __FreeBSD__ +fail: + for (i = 0; i <= units; i++) + vtd_unmap(vtddips[i]); + return (ENXIO); +#endif } static void vtd_cleanup(void) { +#ifndef __FreeBSD__ + int i; + + KASSERT(SLIST_EMPTY(&domhead), ("domain list not empty")); + + bzero(root_table, sizeof (root_table)); + + for (i = 0; i <= drhd_num; i++) { + vtdmaps[i] = NULL; + /* + * Unmap the vtd registers. Note that the devinfo nodes + * themselves aren't removed, they are considered system state + * and can be reused when the module is reloaded. + */ + if (vtddips[i] != NULL) + vtd_unmap(vtddips[i]); + } +#endif } static void diff --git a/usr/src/uts/i86pc/io/vmm/intel/vtd_sol.c b/usr/src/uts/i86pc/io/vmm/intel/vtd_sol.c new file mode 100644 index 0000000000..1dbe8ffa48 --- /dev/null +++ b/usr/src/uts/i86pc/io/vmm/intel/vtd_sol.c @@ -0,0 +1,83 @@ +/* + * 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 2018 Joyent, Inc. + */ + +#include <sys/sunndi.h> +#include <contrib/dev/acpica/include/acpi.h> + +dev_info_t * +vtd_get_dip(ACPI_DMAR_HARDWARE_UNIT *drhd, int unit) +{ + dev_info_t *dip; + struct ddi_parent_private_data *pdptr; + struct regspec reg; + int circ; + + /* + * Try to find an existing devinfo node for this vtd unit. + */ + ndi_devi_enter(ddi_root_node(), &circ); + dip = ddi_find_devinfo("vtd", unit, 0); + ndi_devi_exit(ddi_root_node(), circ); + + if (dip != NULL) + return (dip); + + /* + * None found, construct a devinfo node for this vtd unit. + */ + dip = ddi_add_child(ddi_root_node(), "vtd", + DEVI_SID_NODEID, unit); + + 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); + + return (dip); +} diff --git a/usr/src/uts/i86pc/io/vmm/io/iommu.c b/usr/src/uts/i86pc/io/vmm/io/iommu.c index 9081555031..5f686d3c62 100644 --- a/usr/src/uts/i86pc/io/vmm/io/iommu.c +++ b/usr/src/uts/i86pc/io/vmm/io/iommu.c @@ -68,6 +68,10 @@ static void *host_domain; static eventhandler_tag add_tag, delete_tag; #endif +#ifndef __FreeBSD__ +static volatile u_int iommu_initted; +#endif + static __inline int IOMMU_INIT(void) { @@ -179,14 +183,22 @@ iommu_pci_delete(void *arg, device_t dev) } #endif +#ifndef __FreeBSD__ static int -iommu_find_device(dev_info_t *dip, void *unused) +iommu_find_device(dev_info_t *dip, void *arg) { - if (pcie_is_pci_device(dip)) - iommu_add_device(host_domain, pci_get_rid(dip)); + boolean_t add = (boolean_t)arg; + + if (pcie_is_pci_device(dip)) { + if (add) + iommu_add_device(host_domain, pci_get_rid(dip)); + else + iommu_remove_device(host_domain, pci_get_rid(dip)); + } return (DDI_WALK_CONTINUE); } +#endif static void iommu_init(void) @@ -260,7 +272,7 @@ iommu_init(void) } } #else - ddi_walk_devs(ddi_root_node(), iommu_find_device, NULL); + ddi_walk_devs(ddi_root_node(), iommu_find_device, (void *)B_TRUE); #endif IOMMU_ENABLE(); @@ -278,17 +290,23 @@ iommu_cleanup(void) EVENTHANDLER_DEREGISTER(pci_delete_device, delete_tag); delete_tag = NULL; } +#else + atomic_store_rel_int(&iommu_initted, 0); #endif IOMMU_DISABLE(); +#ifndef __FreeBSD__ + ddi_walk_devs(ddi_root_node(), iommu_find_device, (void *)B_FALSE); +#endif IOMMU_DESTROY_DOMAIN(host_domain); IOMMU_CLEANUP(); +#ifndef __FreeBSD__ + ops = NULL; +#endif } void * iommu_create_domain(vm_paddr_t maxaddr) { - static volatile u_int iommu_initted; - if (iommu_initted < 2) { if (atomic_cmpset_int(&iommu_initted, 0, 1)) { iommu_init(); diff --git a/usr/src/uts/i86pc/io/vmm/vmm.c b/usr/src/uts/i86pc/io/vmm/vmm.c index 2bb50de66f..23c8e4983d 100644 --- a/usr/src/uts/i86pc/io/vmm/vmm.c +++ b/usr/src/uts/i86pc/io/vmm/vmm.c @@ -497,6 +497,7 @@ vmm_mod_unload() { int error; + iommu_cleanup(); error = VMM_CLEANUP(); if (error) return (error); |
