Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F137504057
D25672.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
55 KB
Referenced Files
None
Subscribers
None
D25672.diff
View Options
diff --git a/sys/amd64/amd64/pmap.c b/sys/amd64/amd64/pmap.c
--- a/sys/amd64/amd64/pmap.c
+++ b/sys/amd64/amd64/pmap.c
@@ -12095,7 +12095,7 @@
continue;
}
if (PMAP_ADDRESS_IN_LARGEMAP(sva) &&
- vm_phys_paddr_to_vm_page(pa) == NULL) {
+ vm_phys_paddr_to_vm_page(pa, NULL) == NULL) {
/*
* Page table pages for the large map may be
* freed. Validate the next-level address
@@ -12124,7 +12124,8 @@
continue;
}
if (PMAP_ADDRESS_IN_LARGEMAP(sva) &&
- vm_phys_paddr_to_vm_page(pa) == NULL) {
+ vm_phys_paddr_to_vm_page(pa, NULL) ==
+ NULL) {
/*
* Page table pages for the large map
* may be freed. Validate the
diff --git a/sys/amd64/vmm/amd/amdsysdrv_iommu.c b/sys/amd64/vmm/amd/amdsysdrv_iommu.c
new file mode 100644
--- /dev/null
+++ b/sys/amd64/vmm/amd/amdsysdrv_iommu.c
@@ -0,0 +1,113 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2025 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * 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 AND 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.
+ */
+
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/memdesc.h>
+#include <sys/rman.h>
+#include <sys/sx.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+#include <sys/tree.h>
+#include <sys/vmem.h>
+#include <machine/bus.h>
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
+#include <dev/pci/pcireg.h>
+#include <x86/include/busdma_impl.h>
+#include <x86/iommu/amd_reg.h>
+#include <dev/iommu/busdma_iommu.h>
+#include <x86/iommu/x86_iommu.h>
+#include <x86/iommu/amd_iommu.h>
+
+#include "io/iommu.h"
+
+static int
+amdsysdrv_init(void)
+{
+
+ return (amdiommu_is_running());
+}
+
+static void
+amdsysdrv_cleanup(void)
+{
+
+ /* XXXKIB */
+}
+
+static void
+amdsysdrv_enable(void)
+{
+
+ /* XXXKIB */
+}
+
+static void
+amdsysdrv_disable(void)
+{
+
+ /* XXXKIB */
+}
+
+static bool
+amdsysdrv_get_swcaps(enum iommu_swcaps cap)
+{
+
+ switch (cap) {
+ case IOMMU_CAP_BULK:
+ return (true);
+ default:
+ return (false);
+ }
+}
+
+const struct iommu_ops iommu_ops_amdsysdrv = {
+ .init = amdsysdrv_init,
+ .cleanup = amdsysdrv_cleanup,
+ .enable = amdsysdrv_enable,
+ .disable = amdsysdrv_disable,
+ .create_domain = geniommu_create_domain,
+ .destroy_domain = geniommu_destroy_domain,
+ .create_mapping = geniommu_create_mapping,
+ .create_mapping_bulk = geniommu_create_mapping_bulk,
+ .remove_mapping = geniommu_remove_mapping,
+ .add_device = geniommu_add_device,
+ .remove_device = geniommu_remove_device,
+ .invalidate_tlb = geniommu_invalidate_tlb,
+ .get_swcaps = amdsysdrv_get_swcaps,
+};
diff --git a/sys/amd64/vmm/amd/amdvi_hw.c b/sys/amd64/vmm/amd/amdvi_hw.c
--- a/sys/amd64/vmm/amd/amdvi_hw.c
+++ b/sys/amd64/vmm/amd/amdvi_hw.c
@@ -994,7 +994,7 @@
}
static void *
-amdvi_create_domain(vm_paddr_t maxaddr)
+amdvi_create_domain(vm_paddr_t maxaddr, bool host_domain __unused)
{
struct amdvi_domain *dom;
diff --git a/sys/amd64/vmm/intel/dmar_iommu.c b/sys/amd64/vmm/intel/dmar_iommu.c
new file mode 100644
--- /dev/null
+++ b/sys/amd64/vmm/intel/dmar_iommu.c
@@ -0,0 +1,113 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * 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 AND 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.
+ */
+
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/memdesc.h>
+#include <sys/rman.h>
+#include <sys/sx.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+#include <sys/tree.h>
+#include <sys/vmem.h>
+#include <machine/bus.h>
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
+#include <dev/pci/pcireg.h>
+#include <x86/include/busdma_impl.h>
+#include <x86/iommu/intel_reg.h>
+#include <dev/iommu/busdma_iommu.h>
+#include <x86/iommu/x86_iommu.h>
+#include <x86/iommu/intel_dmar.h>
+
+#include "io/iommu.h"
+
+static int
+dmar_init(void)
+{
+
+ return (dmar_is_running());
+}
+
+static void
+dmar_cleanup(void)
+{
+
+ /* XXXKIB */
+}
+
+static void
+dmar_enable(void)
+{
+
+ /* XXXKIB */
+}
+
+static void
+dmar_disable(void)
+{
+
+ /* XXXKIB */
+}
+
+static bool
+dmar_get_swcaps(enum iommu_swcaps cap)
+{
+
+ switch (cap) {
+ case IOMMU_CAP_BULK:
+ return (true);
+ default:
+ return (false);
+ }
+}
+
+const struct iommu_ops iommu_ops_dmar = {
+ .init = dmar_init,
+ .cleanup = dmar_cleanup,
+ .enable = dmar_enable,
+ .disable = dmar_disable,
+ .create_domain = geniommu_create_domain,
+ .destroy_domain = geniommu_destroy_domain,
+ .create_mapping = geniommu_create_mapping,
+ .create_mapping_bulk = geniommu_create_mapping_bulk,
+ .remove_mapping = geniommu_remove_mapping,
+ .add_device = geniommu_add_device,
+ .remove_device = geniommu_remove_device,
+ .invalidate_tlb = geniommu_invalidate_tlb,
+ .get_swcaps = dmar_get_swcaps,
+};
diff --git a/sys/amd64/vmm/intel/vtd.c b/sys/amd64/vmm/intel/vtd.c
--- a/sys/amd64/vmm/intel/vtd.c
+++ b/sys/amd64/vmm/intel/vtd.c
@@ -637,7 +637,7 @@
}
static void *
-vtd_create_domain(vm_paddr_t maxaddr)
+vtd_create_domain(vm_paddr_t maxaddr, bool host_domain __unused)
{
struct domain *dom;
vm_paddr_t addr;
diff --git a/sys/amd64/vmm/io/iommu.h b/sys/amd64/vmm/io/iommu.h
--- a/sys/amd64/vmm/io/iommu.h
+++ b/sys/amd64/vmm/io/iommu.h
@@ -29,19 +29,27 @@
#ifndef _IO_IOMMU_H_
#define _IO_IOMMU_H_
+enum iommu_swcaps {
+ IOMMU_CAP_NOP,
+ IOMMU_CAP_BULK,
+};
+
typedef int (*iommu_init_func_t)(void);
typedef void (*iommu_cleanup_func_t)(void);
typedef void (*iommu_enable_func_t)(void);
typedef void (*iommu_disable_func_t)(void);
-typedef void *(*iommu_create_domain_t)(vm_paddr_t maxaddr);
+typedef void *(*iommu_create_domain_t)(vm_paddr_t maxaddr, bool host_domain);
typedef void (*iommu_destroy_domain_t)(void *domain);
typedef int (*iommu_create_mapping_t)(void *domain, vm_paddr_t gpa,
vm_paddr_t hpa, uint64_t len, uint64_t *res_len);
+typedef int (*iommu_create_mapping_bulk_t)(void *domain, vm_paddr_t gpa,
+ struct vm_page **ma, uint64_t len);
typedef int (*iommu_remove_mapping_t)(void *domain, vm_paddr_t gpa,
uint64_t len, uint64_t *res_len);
typedef int (*iommu_add_device_t)(void *domain, device_t dev, uint16_t rid);
typedef int (*iommu_remove_device_t)(void *dom, device_t dev, uint16_t rid);
typedef int (*iommu_invalidate_tlb_t)(void *dom);
+typedef bool (*iommu_get_swcaps_t)(enum iommu_swcaps);
struct iommu_ops {
iommu_init_func_t init; /* module wide */
@@ -52,14 +60,40 @@
iommu_create_domain_t create_domain; /* domain-specific */
iommu_destroy_domain_t destroy_domain;
iommu_create_mapping_t create_mapping;
+ iommu_create_mapping_bulk_t create_mapping_bulk;
iommu_remove_mapping_t remove_mapping;
iommu_add_device_t add_device;
iommu_remove_device_t remove_device;
iommu_invalidate_tlb_t invalidate_tlb;
+ iommu_get_swcaps_t get_swcaps;
};
-extern const struct iommu_ops iommu_ops_intel;
+struct geniommu_vmm_domain {
+ bool host_domain;
+ struct sx lock;
+ LIST_HEAD(, iommu_domain) iommu_domains;
+};
+
+void *geniommu_create_domain(vm_paddr_t maxaddr, bool host_domain);
+void geniommu_destroy_domain(void *domain);
+int geniommu_create_mapping_one(struct iommu_domain *iodom, vm_paddr_t gpa,
+ vm_paddr_t hpa, uint64_t len);
+int geniommu_create_mapping(void *dom1, vm_paddr_t gpa, vm_paddr_t hpa,
+ uint64_t len, uint64_t *res_len);
+int geniommu_create_mapping_bulk_one(struct iommu_domain *iodom, vm_paddr_t gpa,
+ struct vm_page **ma, uint64_t len);
+int geniommu_create_mapping_bulk(void *dom1, vm_paddr_t gpa, struct vm_page **ma,
+ uint64_t len);
+int geniommu_remove_mapping(void *dom1, vm_paddr_t gpa, uint64_t len,
+ uint64_t *res_len);
+int geniommu_add_device(void *dom1, device_t dev, uint16_t rid);
+int geniommu_remove_device(void *dom1, device_t dev, uint16_t rid);
+int geniommu_invalidate_tlb(void *dom);
+
+extern const struct iommu_ops iommu_ops_amdsysdrv;
extern const struct iommu_ops iommu_ops_amd;
+extern const struct iommu_ops iommu_ops_dmar;
+extern const struct iommu_ops iommu_ops_intel;
void iommu_cleanup(void);
void *iommu_host_domain(void);
@@ -67,8 +101,11 @@
void iommu_destroy_domain(void *dom);
int iommu_create_mapping(void *dom, vm_paddr_t gpa, vm_paddr_t hpa,
size_t len);
+int iommu_create_mapping_bulk(void *dom, vm_paddr_t gpa,
+ struct vm_page **ma, size_t len);
int iommu_remove_mapping(void *dom, vm_paddr_t gpa, size_t len);
int iommu_add_device(void *dom, device_t dev, uint16_t rid);
int iommu_remove_device(void *dom, device_t dev, uint16_t rid);
int iommu_invalidate_tlb(void *domain);
+bool iommu_get_swcaps(enum iommu_swcaps cap);
#endif
diff --git a/sys/amd64/vmm/io/iommu.c b/sys/amd64/vmm/io/iommu.c
--- a/sys/amd64/vmm/io/iommu.c
+++ b/sys/amd64/vmm/io/iommu.c
@@ -4,6 +4,12 @@
* Copyright (c) 2011 NetApp, Inc.
* All rights reserved.
*
+ * Copyright (c) 2019, 2025 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Part of this software was developed by Konstantin Belousov
+ * <kib@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -26,17 +32,30 @@
* SUCH DAMAGE.
*/
-#include <sys/param.h>
+#include "opt_iommu.h"
+
+#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/memdesc.h>
+#include <sys/rman.h>
+#include <sys/sx.h>
#include <sys/sysctl.h>
-#include <sys/systm.h>
+#include <sys/vmem.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <machine/cpu.h>
#include <machine/md_var.h>
+#include <machine/bus.h>
+
+#include <x86/include/busdma_impl.h>
+#include <dev/iommu/busdma_iommu.h>
+#include <x86/iommu/x86_iommu.h>
#include "vmm_util.h"
#include "vmm_mem.h"
@@ -77,11 +96,11 @@
}
static __inline void *
-IOMMU_CREATE_DOMAIN(vm_paddr_t maxaddr)
+IOMMU_CREATE_DOMAIN(vm_paddr_t maxaddr, bool host_domain)
{
if (ops != NULL && iommu_avail)
- return ((*ops->create_domain)(maxaddr));
+ return ((*ops->create_domain)(maxaddr, host_domain));
else
return (NULL);
}
@@ -104,6 +123,16 @@
return (EOPNOTSUPP);
}
+static __inline int
+IOMMU_CREATE_MAPPING_BULK(void *domain, vm_paddr_t gpa, struct vm_page **ma,
+ size_t len)
+{
+
+ if (ops != NULL && iommu_avail)
+ return ((*ops->create_mapping_bulk)(domain, gpa, ma, len));
+ return (EOPNOTSUPP);
+}
+
static __inline uint64_t
IOMMU_REMOVE_MAPPING(void *domain, vm_paddr_t gpa, uint64_t len,
uint64_t *res_len)
@@ -157,6 +186,15 @@
(*ops->disable)();
}
+static __inline bool
+IOMMU_GET_SWCAPS(enum iommu_swcaps cap)
+{
+
+ if (ops != NULL && ops->get_swcaps != NULL && iommu_avail)
+ return (*ops->get_swcaps)(cap);
+ return (false);
+}
+
static void
iommu_pci_add(void *arg, device_t dev)
{
@@ -183,16 +221,28 @@
if (!iommu_enable)
return;
- if (vmm_is_intel())
- ops = &iommu_ops_intel;
- else if (vmm_is_svm())
- ops = &iommu_ops_amd;
- else
+ if (vmm_is_intel()) {
+#ifdef IOMMU
+ if (dmar_is_running() == 0)
+ ops = &iommu_ops_dmar;
+ else
+#endif
+ ops = &iommu_ops_intel;
+ } else if (vmm_is_svm()) {
+#ifdef IOMMU
+ if (amdiommu_is_running() == 0)
+ ops = &iommu_ops_amdsysdrv;
+ else
+#endif
+ ops = &iommu_ops_amd;
+ } else
ops = NULL;
error = IOMMU_INIT();
- if (error)
+ if (error != 0) {
+ printf("iommu_init: error %d\n", error);
return;
+ }
iommu_avail = 1;
@@ -200,7 +250,7 @@
* Create a domain for the devices owned by the host
*/
maxaddr = vmm_mem_maxaddr();
- host_domain = IOMMU_CREATE_DOMAIN(maxaddr);
+ host_domain = IOMMU_CREATE_DOMAIN(maxaddr, true);
if (host_domain == NULL) {
printf("iommu_init: unable to create a host domain");
IOMMU_CLEANUP();
@@ -289,7 +339,7 @@
while (iommu_initted == 1)
cpu_spinwait();
}
- return (IOMMU_CREATE_DOMAIN(maxaddr));
+ return (IOMMU_CREATE_DOMAIN(maxaddr, false));
}
void
@@ -317,6 +367,14 @@
return (0);
}
+int
+iommu_create_mapping_bulk(void *dom, vm_paddr_t gpa, struct vm_page **ma,
+ size_t len)
+{
+
+ return (IOMMU_CREATE_MAPPING_BULK(dom, gpa, ma, len));
+}
+
int
iommu_remove_mapping(void *dom, vm_paddr_t gpa, size_t len)
{
@@ -361,3 +419,227 @@
return (IOMMU_INVALIDATE_TLB(domain));
}
+
+bool
+iommu_get_swcaps(enum iommu_swcaps cap)
+{
+
+ return (IOMMU_GET_SWCAPS(cap));
+}
+
+static MALLOC_DEFINE(M_GENIOMMU_VMM, "geniommu_vmm",
+ "Gen iommu driver VMM memory");
+
+void *
+geniommu_create_domain(vm_paddr_t maxaddr, bool host_domain)
+{
+ struct geniommu_vmm_domain *vmm_dom;
+
+ vmm_dom = malloc(sizeof(struct geniommu_vmm_domain), M_GENIOMMU_VMM,
+ M_WAITOK | M_ZERO);
+ LIST_INIT(&vmm_dom->iommu_domains);
+ sx_init(&vmm_dom->lock, "vmmdom");
+ vmm_dom->host_domain = host_domain;
+ return (vmm_dom);
+}
+
+void
+geniommu_destroy_domain(void *domain)
+{
+ struct geniommu_vmm_domain *vmm_dom;
+
+ vmm_dom = domain;
+#if 0
+ LIST_FOREACH_SAFE() {
+ }
+#endif
+ sx_destroy(&vmm_dom->lock);
+ free(vmm_dom, M_GENIOMMU_VMM);
+}
+
+int
+geniommu_create_mapping_one(struct iommu_domain *iodom, vm_paddr_t gpa,
+ vm_paddr_t hpa, uint64_t len)
+{
+ struct iommu_map_entry *e;
+ vm_page_t fm, m, *ma;
+ vm_paddr_t pa;
+ iommu_gaddr_t glen, gtotal_len;
+ u_long cnt_after;
+ int error;
+
+ fm = NULL;
+ for (pa = hpa, gtotal_len = 0; gtotal_len < len; pa += glen,
+ gpa += glen, gtotal_len += glen) {
+ e = iommu_gas_alloc_entry(iodom, IOMMU_PGF_WAITOK);
+ e->start = gpa;
+ m = vm_page_phys_to_vm_page(pa, &cnt_after);
+ if (m == NULL) {
+ if (fm == NULL)
+ fm = vm_page_getfake(pa, VM_MEMATTR_DEFAULT);
+ else
+ vm_page_updatefake(fm, pa, VM_MEMATTR_DEFAULT);
+ ma = &fm;
+ glen = PAGE_SIZE;
+ } else {
+ ma = &m;
+ glen = MIN(len - gtotal_len, ptoa(cnt_after + 1));
+ }
+ e->end = gpa + glen;
+ error = iommu_gas_map_region(iodom, e,
+ IOMMU_MAP_ENTRY_READ | IOMMU_MAP_ENTRY_WRITE,
+ IOMMU_MF_CANWAIT | IOMMU_MF_CANSPLIT | IOMMU_MF_VMM, ma);
+ if (error != 0)
+ break;
+ glen = e->end - e->start;
+ }
+ if (fm != NULL)
+ vm_page_putfake(fm);
+ return (error);
+}
+
+int
+geniommu_create_mapping(void *dom1, vm_paddr_t gpa, vm_paddr_t hpa,
+ uint64_t len, uint64_t *res_len)
+{
+ struct geniommu_vmm_domain *vmm_dom;
+ struct iommu_domain *dom;
+ int error;
+
+ vmm_dom = dom1;
+ error = 0;
+ if (vmm_dom->host_domain) {
+ sx_xlock(&vmm_dom->lock);
+ LIST_FOREACH(dom, &vmm_dom->iommu_domains, vmm_dom_link) {
+ error = geniommu_create_mapping_one(dom, gpa,
+ hpa, len);
+ if (error != 0)
+ break;
+ }
+ sx_xunlock(&vmm_dom->lock);
+ }
+ *res_len = len;
+ return (error);
+}
+
+int
+geniommu_create_mapping_bulk_one(struct iommu_domain *iodom, vm_paddr_t gpa,
+ struct vm_page **ma, uint64_t len)
+{
+ struct iommu_map_entry *e;
+ iommu_gaddr_t gtotal_len, glen;
+ int error;
+
+ for (gtotal_len = 0; gtotal_len < len; gpa += glen,
+ gtotal_len += glen) {
+ e = iommu_gas_alloc_entry(iodom, IOMMU_PGF_WAITOK);
+ glen = len - gtotal_len;
+ e->start = gpa;
+ e->end = gpa + glen;
+ error = iommu_gas_map_region(iodom, e,
+ IOMMU_MAP_ENTRY_READ | IOMMU_MAP_ENTRY_WRITE,
+ IOMMU_MF_CANWAIT | IOMMU_MF_CANSPLIT | IOMMU_MF_VMM,
+ ma + atop(gtotal_len));
+ if (error != 0)
+ break;
+ glen = e->end - e->start;
+ }
+ return (error);
+}
+
+int
+geniommu_create_mapping_bulk(void *dom1, vm_paddr_t gpa, struct vm_page **ma,
+ uint64_t len)
+{
+ struct geniommu_vmm_domain *vmm_dom;
+ struct iommu_domain *dom;
+ int error;
+
+ vmm_dom = dom1;
+ error = 0;
+ if (!vmm_dom->host_domain) {
+ sx_xlock(&vmm_dom->lock);
+ LIST_FOREACH(dom, &vmm_dom->iommu_domains, vmm_dom_link) {
+ error = geniommu_create_mapping_bulk_one(dom, gpa,
+ ma, len);
+ if (error != 0)
+ break;
+ }
+ sx_xunlock(&vmm_dom->lock);
+ }
+ return (error);
+}
+
+int
+geniommu_remove_mapping(void *dom1, vm_paddr_t gpa, uint64_t len,
+ uint64_t *res_len)
+{
+ struct geniommu_vmm_domain *vmm_dom;
+ struct iommu_domain *dom;
+
+ vmm_dom = dom1;
+ if (!vmm_dom->host_domain) {
+ sx_xlock(&vmm_dom->lock);
+ LIST_FOREACH(dom, &vmm_dom->iommu_domains, vmm_dom_link)
+ iommu_gas_remove(dom, gpa, len);
+ sx_xunlock(&vmm_dom->lock);
+ }
+ *res_len = len;
+ return (0);
+}
+
+int
+geniommu_add_device(void *dom1, device_t dev, uint16_t rid)
+{
+ struct geniommu_vmm_domain *vmm_dom;
+ struct iommu_unit *iommu;
+ struct iommu_domain *iodom, *rdomain;
+ int error;
+
+ vmm_dom = dom1;
+ if (vmm_dom->host_domain)
+ return (0);
+ error = 0;
+ sx_xlock(&vmm_dom->lock);
+ iommu = iommu_find(dev, true);
+ if (iommu == NULL) {
+ error = ENOTTY;
+ goto done;
+ }
+ LIST_FOREACH(iodom, &vmm_dom->iommu_domains, vmm_dom_link) {
+ if (iodom->iommu == iommu)
+ break;
+ }
+ rdomain = get_x86_iommu()->vmm_domain_add_dev(iommu, iodom, dev, rid);
+ if (rdomain == NULL) {
+ error = EBUSY;
+ goto done;
+ }
+ if (iodom == NULL || iodom != rdomain) {
+ LIST_INSERT_HEAD(&vmm_dom->iommu_domains, rdomain,
+ vmm_dom_link);
+ }
+done:
+ sx_xunlock(&vmm_dom->lock);
+ return (error);
+}
+
+int
+geniommu_remove_device(void *dom1, device_t dev, uint16_t rid)
+{
+ struct geniommu_vmm_domain *vmm_dom;
+
+ vmm_dom = dom1;
+ if (vmm_dom->host_domain)
+ return (0);
+ /* XXXKIB */
+ return (0);
+}
+
+int
+geniommu_invalidate_tlb(void *dom)
+{
+
+ /* XXXKIB: do nothing, rely on map/unmap ? */
+ return (0);
+}
diff --git a/sys/amd64/vmm/vmm.c b/sys/amd64/vmm/vmm.c
--- a/sys/amd64/vmm/vmm.c
+++ b/sys/amd64/vmm/vmm.c
@@ -640,8 +640,12 @@
return (0);
}
+#define NMA (1 << VM_LEVEL_0_ORDER)
+#define NMAP (NMA * sizeof(vm_page_t))
+_Static_assert(NMAP == PAGE_SIZE, "XXX");
+
static int
-vm_iommu_map(struct vm *vm)
+vm_iommu_map_pg(struct vm *vm)
{
pmap_t pmap;
vm_paddr_t gpa, hpa;
@@ -651,6 +655,7 @@
sx_assert(&vm->mem.mem_segs_lock, SX_LOCKED);
pmap = vmspace_pmap(vm_vmspace(vm));
+ error = 0;
for (i = 0; i < VM_MAX_MEMMAPS; i++) {
if (!vm_memseg_sysmem(vm, i))
continue;
@@ -682,22 +687,91 @@
("vm_iommu_map: vm %p gpa %jx hpa %jx not wired",
vm, (uintmax_t)gpa, (uintmax_t)hpa));
- iommu_create_mapping(vm->iommu, gpa, hpa, PAGE_SIZE);
+ error = iommu_create_mapping(vm->iommu, gpa, hpa,
+ PAGE_SIZE);
+ if (error != 0)
+ break;
}
}
-
- error = iommu_invalidate_tlb(iommu_host_domain());
return (error);
}
static int
-vm_iommu_unmap(struct vm *vm)
+vm_iommu_map_bulk(struct vm *vm)
+{
+ vm_paddr_t gpa, gpa_end, g;
+ struct vm_mem_map *mm;
+ vm_page_t *ma;
+ int error, i, j, n;
+
+ vm_assert_memseg_locked(vm);
+
+ error = 0;
+ ma = (void *)kmem_malloc(NMAP, M_WAITOK | M_ZERO);
+
+ for (i = 0; i < VM_MAX_MEMMAPS; i++) {
+ if (!vm_memseg_sysmem(vm, i))
+ continue;
+ mm = &vm_mem(vm)->mem_maps[i];
+ if (mm->len == 0)
+ continue;
+
+ KASSERT((mm->flags & VM_MEMMAP_F_IOMMU) == 0,
+ ("iommu map found invalid memmap %#lx/%#lx/%#x",
+ mm->gpa, mm->len, mm->flags));
+ if ((mm->flags & VM_MEMMAP_F_WIRED) == 0)
+ continue;
+ mm->flags |= VM_MEMMAP_F_IOMMU;
+
+ for (gpa = mm->gpa; gpa < mm->gpa + mm->len; gpa = gpa_end) {
+ gpa_end = min(ptoa(NMA), mm->gpa + mm->len);
+ if ((gpa_end & (ptoa(NMA) - 1)) != 0)
+ gpa_end &= ~(ptoa(NMA) - 1);
+ n = atop(gpa_end - gpa);
+
+ for (j = 0, g = gpa; j < n; j++, g += PAGE_SIZE) {
+ ma[j] = PHYS_TO_VM_PAGE(pmap_extract(
+ vmspace_pmap(vm_vmspace(vm)), g));
+ KASSERT(vm_page_wired(ma[j]),
+ ("vm_iommu_map: vm %p gpa %jx page %p not wired",
+ vm, (uintmax_t)g, ma[j]));
+ }
+ error = iommu_create_mapping_bulk(vm->iommu, gpa, ma,
+ gpa_end - gpa);
+ if (error != 0)
+ break;
+ }
+ }
+ kmem_free(ma, NMAP);
+ return (error);
+}
+
+
+static int
+vm_iommu_map(struct vm *vm)
+{
+ int error, error1;
+
+ vm_assert_memseg_xlocked(vm);
+
+ if (iommu_get_swcaps(IOMMU_CAP_BULK))
+ error = vm_iommu_map_bulk(vm);
+ else
+ error = vm_iommu_map_pg(vm);
+ error1 = iommu_invalidate_tlb(iommu_host_domain());
+ if (error == 0)
+ error = error1;
+ return (error);
+}
+
+static int
+vm_iommu_unmap_pg(struct vm *vm, bool bulk)
{
vm_paddr_t gpa;
struct vm_mem_map *mm;
- int error, i;
+ int i;
- sx_assert(&vm->mem.mem_segs_lock, SX_LOCKED);
+ vm_assert_memseg_xlocked(vm);
for (i = 0; i < VM_MAX_MEMMAPS; i++) {
if (!vm_memseg_sysmem(vm, i))
@@ -711,6 +785,10 @@
("iommu unmap found invalid memmap %#lx/%#lx/%#x",
mm->gpa, mm->len, mm->flags));
+ if (bulk) {
+ iommu_remove_mapping(vm->iommu, mm->gpa, mm->len);
+ continue;
+ }
for (gpa = mm->gpa; gpa < mm->gpa + mm->len; gpa += PAGE_SIZE) {
KASSERT(vm_page_wired(PHYS_TO_VM_PAGE(pmap_extract(
vmspace_pmap(vm_vmspace(vm)), gpa))),
@@ -719,12 +797,26 @@
iommu_remove_mapping(vm->iommu, gpa, PAGE_SIZE);
}
}
+ return (0);
+}
+
+static int
+vm_iommu_unmap(struct vm *vm)
+{
+ int error, error1;
+
+ vm_assert_memseg_xlocked(vm);
+
+ error = vm_iommu_unmap_pg(vm, iommu_get_swcaps(IOMMU_CAP_BULK));
/*
* Invalidate the cached translations associated with the domain
* from which pages were removed.
*/
- error = iommu_invalidate_tlb(vm->iommu);
+ error1 = iommu_invalidate_tlb(vm->iommu);
+ if (error == 0)
+ error = error1;
+
return (error);
}
diff --git a/sys/dev/iommu/iommu.h b/sys/dev/iommu/iommu.h
--- a/sys/dev/iommu/iommu.h
+++ b/sys/dev/iommu/iommu.h
@@ -122,6 +122,7 @@
vm_paddr_t msi_phys; /* (d) Arch-specific */
u_int flags; /* (u) */
LIST_HEAD(, iommu_ctx) contexts;/* (u) */
+ LIST_ENTRY(iommu_domain) vmm_dom_link; /* Member in external vmm dom */
};
struct iommu_ctx {
@@ -148,6 +149,8 @@
page table */
#define IOMMU_DOMAIN_RMRR 0x0020 /* Domain contains RMRR entry,
cannot be turned off */
+#define IOMMU_DOMAIN_VMM 0x0040 /* Used by VMM */
+#define IOMMU_DOMAIN_BUSDMA 0x0080 /* Used for busdma */
#define IOMMU_LOCK(unit) mtx_lock(&(unit)->lock)
#define IOMMU_UNLOCK(unit) mtx_unlock(&(unit)->lock)
diff --git a/sys/dev/iommu/iommu_gas.h b/sys/dev/iommu/iommu_gas.h
--- a/sys/dev/iommu/iommu_gas.h
+++ b/sys/dev/iommu/iommu_gas.h
@@ -35,6 +35,7 @@
#define IOMMU_MF_CANWAIT 0x0001
#define IOMMU_MF_CANSPLIT 0x0002
#define IOMMU_MF_RMRR 0x0004
+#define IOMMU_MF_VMM 0x0008
#define IOMMU_PGF_WAITOK 0x0001
#define IOMMU_PGF_ZERO 0x0002
@@ -47,6 +48,7 @@
dmamap_link */
#define IOMMU_MAP_ENTRY_MAP 0x0004 /* Busdma created, linked by
dmamap_link */
+#define IOMMU_MAP_ENTRY_VMM 0x0008 /* VMM created */
#define IOMMU_MAP_ENTRY_UNMAPPED 0x0010 /* No backing pages */
#define IOMMU_MAP_ENTRY_REMOVING 0x0020 /* In process of removal by
iommu_gas_remove() */
diff --git a/sys/dev/iommu/iommu_gas.c b/sys/dev/iommu/iommu_gas.c
--- a/sys/dev/iommu/iommu_gas.c
+++ b/sys/dev/iommu/iommu_gas.c
@@ -583,7 +583,9 @@
}
if ((flags & IOMMU_MF_RMRR) != 0)
- entry->flags = IOMMU_MAP_ENTRY_RMRR;
+ entry->flags |= IOMMU_MAP_ENTRY_RMRR;
+ if ((flags & IOMMU_MF_VMM) != 0)
+ entry->flags |= IOMMU_MAP_ENTRY_VMM;
#ifdef INVARIANTS
struct iommu_map_entry *ip, *in;
@@ -610,9 +612,10 @@
struct iommu_domain *domain;
domain = entry->domain;
- KASSERT((entry->flags & (IOMMU_MAP_ENTRY_PLACE | IOMMU_MAP_ENTRY_RMRR |
- IOMMU_MAP_ENTRY_MAP)) == IOMMU_MAP_ENTRY_MAP,
- ("permanent entry %p %p", domain, entry));
+ KASSERT((entry->flags & IOMMU_MAP_ENTRY_RMRR) == 0,
+ ("removing RMRR entry dom %p e %p (%#jx, %#jx) fl %#x", domain,
+ entry,
+ (uintmax_t)entry->start, (uintmax_t)entry->end, entry->flags));
IOMMU_DOMAIN_LOCK(domain);
iommu_gas_rb_remove(domain, entry);
@@ -852,6 +855,8 @@
iommu_gas_map_region(struct iommu_domain *domain, struct iommu_map_entry *entry,
u_int eflags, u_int flags, vm_page_t *ma)
{
+ struct iommu_map_entries_tailq gc;
+ struct iommu_map_entry *r1, *r2;
iommu_gaddr_t start;
int error;
@@ -860,20 +865,27 @@
entry, entry->domain));
KASSERT(entry->flags == 0, ("used RMRR entry %p %p %x", domain,
entry, entry->flags));
- KASSERT((flags & ~(IOMMU_MF_CANWAIT | IOMMU_MF_RMRR)) == 0,
+ KASSERT((flags & ~(IOMMU_MF_CANWAIT | IOMMU_MF_CANSPLIT |
+ IOMMU_MF_RMRR | IOMMU_MF_VMM)) == 0,
("invalid flags 0x%x", flags));
+ if ((flags & IOMMU_MF_VMM) != 0)
+ iommu_gas_remove_init(domain, &gc, &r1, &r2);
start = entry->start;
IOMMU_DOMAIN_LOCK(domain);
+ if ((flags & IOMMU_MF_VMM) != 0) {
+ iommu_gas_remove_locked(domain, entry->start, entry->end,
+ &gc, &r1, &r2);
+ }
error = iommu_gas_alloc_region(domain, entry, flags);
if (error != 0) {
IOMMU_DOMAIN_UNLOCK(domain);
- return (error);
+ goto done;
}
entry->flags |= eflags;
IOMMU_DOMAIN_UNLOCK(domain);
if (entry->end == entry->start)
- return (0);
+ goto done;
/*
* iommu_gas_alloc_region() might clipped the entry start and
@@ -886,12 +898,14 @@
if (error == ENOMEM) {
iommu_domain_unload_entry(entry, false,
(flags & IOMMU_MF_CANWAIT) != 0);
- return (error);
+ goto done;
}
KASSERT(error == 0,
("unexpected error %d from domain_map_buf", error));
-
- return (0);
+done:
+ if ((flags & IOMMU_MF_VMM) != 0)
+ iommu_gas_remove_cleanup(domain, &gc, &r1, &r2);
+ return (error);
}
static int
diff --git a/sys/modules/vmm/Makefile b/sys/modules/vmm/Makefile
--- a/sys/modules/vmm/Makefile
+++ b/sys/modules/vmm/Makefile
@@ -101,6 +101,7 @@
# intel-specific files
.PATH: ${SRCTOP}/sys/amd64/vmm/intel
SRCS+= ept.c \
+ dmar_iommu.c \
vmcs.c \
vmx_msr.c \
vmx_support.S \
@@ -111,6 +112,7 @@
.PATH: ${SRCTOP}/sys/amd64/vmm/amd
SRCS+= vmcb.c \
amdviiommu.c \
+ amdsysdrv_iommu.c \
ivhd_if.c \
ivhd_if.h \
svm.c \
diff --git a/sys/vm/device_pager.c b/sys/vm/device_pager.c
--- a/sys/vm/device_pager.c
+++ b/sys/vm/device_pager.c
@@ -443,7 +443,7 @@
}
/* If "paddr" is a real page, perform a sanity check on "memattr". */
- if ((m_paddr = vm_phys_paddr_to_vm_page(paddr)) != NULL &&
+ if ((m_paddr = vm_phys_paddr_to_vm_page(paddr, NULL)) != NULL &&
(memattr1 = pmap_page_get_memattr(m_paddr)) != memattr) {
/*
* For the /dev/mem d_mmap routine to return the
diff --git a/sys/vm/sg_pager.c b/sys/vm/sg_pager.c
--- a/sys/vm/sg_pager.c
+++ b/sys/vm/sg_pager.c
@@ -180,7 +180,7 @@
KASSERT(paddr != 1, ("invalid SG page index"));
/* If "paddr" is a real page, perform a sanity check on "memattr". */
- if ((m_paddr = vm_phys_paddr_to_vm_page(paddr)) != NULL &&
+ if ((m_paddr = vm_phys_paddr_to_vm_page(paddr, NULL)) != NULL &&
pmap_page_get_memattr(m_paddr) != memattr) {
memattr = pmap_page_get_memattr(m_paddr);
printf(
diff --git a/sys/vm/vm_page.h b/sys/vm/vm_page.h
--- a/sys/vm/vm_page.h
+++ b/sys/vm/vm_page.h
@@ -458,8 +458,11 @@
* PHYS_TO_VM_PAGE() returns the vm_page_t object that represents a memory
* page to which the given physical address belongs. The correct vm_page_t
* object is returned for addresses that are not page-aligned.
+ * vm_page_phys_to_vm_page() is same as PHYS_TO_VM_PAGE() but also can
+ * return the count of pages after m in the same physical segment.
*/
vm_page_t PHYS_TO_VM_PAGE(vm_paddr_t pa);
+vm_page_t vm_page_phys_to_vm_page(vm_paddr_t pa, u_long *cnt_after);
/*
* vm_page allocation arguments for the functions vm_page_alloc(),
diff --git a/sys/vm/vm_page.c b/sys/vm/vm_page.c
--- a/sys/vm/vm_page.c
+++ b/sys/vm/vm_page.c
@@ -334,7 +334,7 @@
vm_page_t m;
bool found;
- m = vm_phys_paddr_to_vm_page(pa);
+ m = vm_phys_paddr_to_vm_page(pa, NULL);
if (m == NULL)
return (true); /* page does not exist, no failure */
@@ -575,7 +575,7 @@
vm_offset_t mapped;
int witness_size;
#endif
-#if defined(__i386__) && defined(VM_PHYSSEG_DENSE)
+#if (defined(__i386__) || defined(__amd64__)) && defined(VM_PHYSSEG_DENSE)
long ii;
#endif
int pool;
@@ -772,7 +772,11 @@
* Initialize the page structures and add every available page to the
* physical memory allocator's free lists.
*/
-#if defined(__i386__) && defined(VM_PHYSSEG_DENSE)
+#if (defined(__i386__) || defined(__amd64__)) && defined(VM_PHYSSEG_DENSE)
+ /*
+ * i386 needs this for copyout(9) calling vm_fault_quick_hold_pages().
+ * amd64 requires that for DMAR busdma and bhyve IOMMU.
+ */
for (ii = 0; ii < vm_page_array_size; ii++) {
m = &vm_page_array[ii];
vm_page_init_page(m, (first_page + ii) << PAGE_SHIFT, 0,
@@ -1278,13 +1282,20 @@
vm_page_t
PHYS_TO_VM_PAGE(vm_paddr_t pa)
+{
+
+ return (vm_page_phys_to_vm_page(pa, NULL));
+}
+
+vm_page_t
+vm_page_phys_to_vm_page(vm_paddr_t pa, u_long *cnt_after)
{
vm_page_t m;
#ifdef VM_PHYSSEG_SPARSE
- m = vm_phys_paddr_to_vm_page(pa);
+ m = vm_phys_paddr_to_vm_page(pa, cnt_after);
if (m == NULL)
- m = vm_phys_fictitious_to_vm_page(pa);
+ m = vm_phys_fictitious_to_vm_page(pa, cnt_after);
return (m);
#elif defined(VM_PHYSSEG_DENSE)
long pi;
@@ -1292,9 +1303,11 @@
pi = atop(pa);
if (pi >= first_page && (pi - first_page) < vm_page_array_size) {
m = &vm_page_array[pi - first_page];
+ if (cnt_after != NULL)
+ *cnt_after = vm_page_array_size - pi - first_page;
return (m);
}
- return (vm_phys_fictitious_to_vm_page(pa));
+ return (vm_phys_fictitious_to_vm_page(pa, cnt_after));
#else
#error "Either VM_PHYSSEG_DENSE or VM_PHYSSEG_SPARSE must be defined."
#endif
diff --git a/sys/vm/vm_phys.h b/sys/vm/vm_phys.h
--- a/sys/vm/vm_phys.h
+++ b/sys/vm/vm_phys.h
@@ -68,13 +68,14 @@
int vm_phys_fictitious_reg_range(vm_paddr_t start, vm_paddr_t end,
vm_memattr_t memattr);
void vm_phys_fictitious_unreg_range(vm_paddr_t start, vm_paddr_t end);
-vm_page_t vm_phys_fictitious_to_vm_page(vm_paddr_t pa);
+vm_page_t vm_phys_fictitious_to_vm_page(vm_paddr_t pa, u_long *cnt_after);
int vm_phys_find_range(vm_page_t bounds[], int segind, int domain,
u_long npages, vm_paddr_t low, vm_paddr_t high);
+vm_page_t vm_phys_fictitious_to_vm_page(vm_paddr_t pa, u_long *cnt_after);
void vm_phys_free_contig(vm_page_t m, int pool, u_long npages);
void vm_phys_free_pages(vm_page_t m, int pool, int order);
void vm_phys_init(void);
-vm_page_t vm_phys_paddr_to_vm_page(vm_paddr_t pa);
+vm_page_t vm_phys_paddr_to_vm_page(vm_paddr_t pa, u_long *cnt_after);
vm_page_t vm_phys_seg_paddr_to_vm_page(struct vm_phys_seg *seg, vm_paddr_t pa);
void vm_phys_register_domains(int ndomains, struct mem_affinity *affinity,
int *locality);
diff --git a/sys/vm/vm_phys.c b/sys/vm/vm_phys.c
--- a/sys/vm/vm_phys.c
+++ b/sys/vm/vm_phys.c
@@ -1033,17 +1033,20 @@
* Find the vm_page corresponding to the given physical address.
*/
vm_page_t
-vm_phys_paddr_to_vm_page(vm_paddr_t pa)
+vm_phys_paddr_to_vm_page(vm_paddr_t pa, u_long *cnt_after)
{
struct vm_phys_seg *seg;
- if ((seg = vm_phys_paddr_to_seg(pa)) != NULL)
+ if ((seg = vm_phys_paddr_to_seg(pa)) != NULL) {
+ if (cnt_after != NULL)
+ *cnt_after = atop(seg->end - pa);
return (vm_phys_seg_paddr_to_vm_page(seg, pa));
+ }
return (NULL);
}
vm_page_t
-vm_phys_fictitious_to_vm_page(vm_paddr_t pa)
+vm_phys_fictitious_to_vm_page(vm_paddr_t pa, u_long *cnt_after)
{
struct vm_phys_fictitious_seg tmp, *seg;
vm_page_t m;
@@ -1061,6 +1064,8 @@
m = &seg->first_page[atop(pa - seg->start)];
KASSERT((m->flags & PG_FICTITIOUS) != 0, ("%p not fictitious", m));
+ if (cnt_after != NULL)
+ *cnt_after = atop(seg->end - pa);
return (m);
}
@@ -1523,7 +1528,7 @@
* physical pages containing the given physical page "m" and
* assign it to "m_set".
*/
- m = vm_phys_paddr_to_vm_page(pa);
+ m = vm_phys_paddr_to_vm_page(pa, NULL);
for (m_set = m, order = 0; m_set->order == VM_NFREEORDER &&
order < VM_NFREEORDER - 1; ) {
order++;
@@ -1905,7 +1910,7 @@
vm_page_t m;
int i;
- if ((m = vm_phys_paddr_to_vm_page(pa)) != NULL)
+ if ((m = vm_phys_paddr_to_vm_page(pa, NULL)) != NULL)
return ((m->flags & PG_NODUMP) == 0);
for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i += 2) {
diff --git a/sys/x86/iommu/amd_ctx.c b/sys/x86/iommu/amd_ctx.c
--- a/sys/x86/iommu/amd_ctx.c
+++ b/sys/x86/iommu/amd_ctx.c
@@ -501,11 +501,12 @@
}
}
-struct amdiommu_ctx *
-amdiommu_get_ctx_for_dev(struct amdiommu_unit *unit, device_t dev, uint16_t rid,
+static struct amdiommu_ctx *
+amdiommu_get_ctx_for_dev_locked(struct amdiommu_unit *unit,
+ struct amdiommu_domain *domain, device_t dev, uint16_t rid,
int dev_domain, bool id_mapped, bool rmrr_init, uint8_t dte, uint32_t edte)
{
- struct amdiommu_domain *domain, *domain1;
+ struct amdiommu_domain *domain1;
struct amdiommu_ctx *ctx, *ctx1;
int bus, slot, func;
@@ -518,7 +519,7 @@
slot = PCI_RID2SLOT(rid);
func = PCI_RID2FUNC(rid);
}
- AMDIOMMU_LOCK(unit);
+ AMDIOMMU_ASSERT_LOCKED(unit);
KASSERT(!iommu_is_buswide_ctx(AMD2IOMMU(unit), bus) ||
(slot == 0 && func == 0),
("iommu%d pci%d:%d:%d get_ctx for buswide", AMD2IOMMU(unit)->unit,
@@ -530,61 +531,172 @@
* higher chance to succeed if the sleep is allowed.
*/
AMDIOMMU_UNLOCK(unit);
- domain1 = amdiommu_domain_alloc(unit, id_mapped);
- if (domain1 == NULL)
- return (NULL);
- if (!id_mapped) {
- /*
- * XXXKIB IVMD seems to be less significant
- * and less used on AMD than RMRR on Intel.
- * Not implemented for now.
- */
- }
- ctx1 = amdiommu_ctx_alloc(domain1, rid);
- amdiommu_ctx_init_irte(ctx1);
- AMDIOMMU_LOCK(unit);
-
- /*
- * Recheck the contexts, other thread might have
- * already allocated needed one.
- */
- ctx = amdiommu_find_ctx_locked(unit, rid);
- if (ctx == NULL) {
- domain = domain1;
- ctx = ctx1;
- amdiommu_ctx_link(ctx);
- ctx->context.tag->owner = dev;
- iommu_device_tag_init(CTX2IOCTX(ctx), dev);
-
- LIST_INSERT_HEAD(&unit->domains, domain, link);
- dte_entry_init(ctx, false, dte, edte);
- amdiommu_qi_invalidate_ctx_locked(ctx);
- if (dev != NULL) {
- device_printf(dev,
- "amdiommu%d pci%d:%d:%d:%d rid %x domain %d "
- "%s-mapped\n",
- AMD2IOMMU(unit)->unit, unit->unit_dom,
- bus, slot, func, rid, domain->domain,
- id_mapped ? "id" : "re");
+ if (domain == NULL) {
+ domain1 = amdiommu_domain_alloc(unit, id_mapped);
+ if (domain1 == NULL)
+ return (NULL);
+ if (!id_mapped) {
+ /*
+ * XXXKIB IVMD seems to be less significant
+ * and less used on AMD than RMRR on Intel.
+ * Not implemented for now.
+ */
}
- } else {
- amdiommu_domain_destroy(domain1);
- /* Nothing needs to be done to destroy ctx1. */
- free(ctx1, M_AMDIOMMU_CTX);
- domain = CTX2DOM(ctx);
- ctx->context.refs++; /* tag referenced us */
+ ctx1 = amdiommu_ctx_alloc(domain1, rid);
+ amdiommu_ctx_init_irte(ctx1);
+ AMDIOMMU_LOCK(unit);
+
+ /*
+ * Recheck the contexts, other thread might have
+ * already allocated needed one.
+ */
+ ctx = amdiommu_find_ctx_locked(unit, rid);
+ if (ctx == NULL) {
+ domain = domain1;
+ ctx = ctx1;
+ amdiommu_ctx_link(ctx);
+ ctx->context.tag->owner = dev;
+ iommu_device_tag_init(CTX2IOCTX(ctx), dev);
+
+ LIST_INSERT_HEAD(&unit->domains, domain, link);
+ dte_entry_init(ctx, false, dte, edte);
+ amdiommu_qi_invalidate_ctx_locked(ctx);
+ if (dev != NULL) {
+ device_printf(dev,
+ "amdiommu%d pci%d:%d:%d:%d rid %x domain %d "
+ "%s-mapped\n",
+ AMD2IOMMU(unit)->unit,
+ unit->unit_dom,
+ bus, slot, func, rid,
+ domain->domain,
+ id_mapped ? "id" : "re");
+ }
+ } else {
+ amdiommu_domain_destroy(domain1);
+ /* Nothing needs to be done to destroy ctx1. */
+ free(ctx1, M_AMDIOMMU_CTX);
+ domain = CTX2DOM(ctx);
+ ctx->context.refs++; /* tag referenced us */
+ }
+ if (domain1 != NULL)
+ amdiommu_domain_destroy(domain1);
}
} else {
+ MPASS(domain == NULL);
domain = CTX2DOM(ctx);
if (ctx->context.tag->owner == NULL)
ctx->context.tag->owner = dev;
ctx->context.refs++; /* tag referenced us */
}
- AMDIOMMU_UNLOCK(unit);
-
return (ctx);
}
+struct amdiommu_ctx *
+amdiommu_get_ctx_for_dev(struct amdiommu_unit *unit,
+ struct amdiommu_domain *domain, device_t dev, uint16_t rid,
+ int dev_domain, bool id_mapped, bool rmrr_init, uint8_t dte, uint32_t edte)
+{
+ struct amdiommu_ctx *res;
+
+ AMDIOMMU_LOCK(unit);
+ res = amdiommu_get_ctx_for_dev_locked(unit, domain, dev, rid,
+ dev_domain, id_mapped, rmrr_init, dte, edte);
+ AMDIOMMU_UNLOCK(unit);
+ return (res);
+}
+
+static struct amdiommu_domain *
+amdiommu_move_ctx_to_domain(struct amdiommu_domain *domain,
+ struct amdiommu_ctx *ctx)
+{
+#if 0
+ struct amdiommu_unit *unit;
+ struct amdiommu_domain *old_domain;
+
+ /* XXXKIB */
+ dmar_ctx_entry_t *ctxp;
+ struct sf_buf *sf;
+
+ dmar = domain->dmar;
+ DMAR_ASSERT_LOCKED(dmar);
+ old_domain = CTX2DOM(ctx);
+ if (domain == old_domain) {
+ DMAR_UNLOCK(dmar);
+ return (0);
+ }
+ KASSERT(old_domain->iodom.iommu == domain->iodom.iommu,
+ ("domain %p %u moving between dmars %u %u", domain,
+ domain->domain, old_domain->dmar->iommu.unit,
+ domain->dmar->iommu.unit));
+
+ if ((old_domain->iodom.flags & IOMMU_DOMAIN_RMRR) != 0)
+ return (old_domain);
+
+ TD_PREP_PINNED_ASSERT;
+
+ ctxp = dmar_map_ctx_entry(ctx, &sf);
+ dmar_ctx_unlink(ctx);
+ ctx->context.domain = &domain->iodom;
+ dmar_ctx_link(ctx);
+ ctx_id_entry_init(ctx, ctxp, true, PCI_BUSMAX + 100);
+ iommu_unmap_pgtbl(sf);
+ (void)dmar_flush_for_ctx_entry(dmar, true);
+ /* If flush failed, rolling back would not work as well. */
+ printf("dmar%d rid %x domain %d->%d %s-mapped\n",
+ dmar->iommu.unit, ctx->context.rid, old_domain->domain,
+ domain->domain, (domain->iodom.flags & IOMMU_DOMAIN_IDMAP) != 0 ?
+ "id" : "re");
+ dmar_unref_domain_locked(dmar, old_domain);
+ TD_PINNED_ASSERT;
+#endif
+ return (domain);
+}
+
+struct iommu_domain *
+amdiommu_vmm_domain_add_dev(struct iommu_unit *iommu,
+ struct iommu_domain *domain, device_t dev, uint16_t rid)
+{
+ struct amdiommu_unit *unit;
+ struct amdiommu_domain *ddomain, *rdomain;
+ struct amdiommu_ctx *ctx;
+ bool drain;
+
+ unit = IOMMU2AMD(iommu);
+ ddomain = domain == NULL ? NULL : IODOM2DOM(domain);
+ MPASS(ddomain == NULL || ddomain->unit == unit);
+ rdomain = NULL;
+ drain = false;
+ AMDIOMMU_LOCK(unit);
+ ctx = amdiommu_find_ctx_locked(unit, rid);
+ if (ctx != NULL) {
+ rdomain = domain != NULL ? amdiommu_move_ctx_to_domain(ddomain,
+ ctx) : IODOM2DOM(ctx->context.domain);
+ } else {
+ ctx = amdiommu_get_ctx_for_dev_locked(unit, ddomain, dev, rid,
+ 0/*XXX dev_domain*/, false, true, 0/*XXXdte*/, 0/*XXXedte*/);
+ if (ctx != NULL) {
+ rdomain = IODOM2DOM(ctx->context.domain);
+ MPASS(domain == NULL || rdomain == ddomain);
+ }
+ }
+ if (rdomain != NULL) {
+ MPASS((rdomain->iodom.flags & (IOMMU_DOMAIN_BUSDMA |
+ IOMMU_DOMAIN_VMM)) != (IOMMU_DOMAIN_BUSDMA |
+ IOMMU_DOMAIN_VMM));
+ if ((rdomain->iodom.flags & IOMMU_DOMAIN_BUSDMA) != 0) {
+ rdomain->iodom.flags &= ~IOMMU_DOMAIN_BUSDMA;
+ drain = true;
+ }
+ rdomain->iodom.flags |= IOMMU_DOMAIN_VMM;
+ }
+ AMDIOMMU_UNLOCK(unit);
+ if (drain) {
+ taskqueue_drain(iommu->delayed_taskqueue,
+ &rdomain->iodom.unload_task);
+ }
+ return (DOM2IODOM(rdomain));
+}
+
struct iommu_ctx *
amdiommu_get_ctx(struct iommu_unit *iommu, device_t dev, uint16_t rid,
bool id_mapped, bool rmrr_init)
@@ -602,8 +714,8 @@
return (NULL);
if (AMD2IOMMU(unit) != iommu) /* XXX complain loudly */
return (NULL);
- ret = amdiommu_get_ctx_for_dev(unit, dev, rid1, pci_get_domain(dev),
- id_mapped, rmrr_init, dte, edte);
+ ret = amdiommu_get_ctx_for_dev(unit, NULL, dev, rid1,
+ pci_get_domain(dev), id_mapped, rmrr_init, dte, edte);
return (CTX2IOCTX(ret));
}
diff --git a/sys/x86/iommu/amd_drv.c b/sys/x86/iommu/amd_drv.c
--- a/sys/x86/iommu/amd_drv.c
+++ b/sys/x86/iommu/amd_drv.c
@@ -1094,6 +1094,7 @@
.domain_unload = amdiommu_domain_unload,
.get_ctx = amdiommu_get_ctx,
.free_ctx_locked = amdiommu_free_ctx_locked_method,
+ .vmm_domain_add_dev = amdiommu_vmm_domain_add_dev,
.alloc_msi_intr = amdiommu_alloc_msi_intr,
.map_msi_intr = amdiommu_map_msi_intr,
.unmap_msi_intr = amdiommu_unmap_msi_intr,
diff --git a/sys/x86/iommu/amd_iommu.h b/sys/x86/iommu/amd_iommu.h
--- a/sys/x86/iommu/amd_iommu.h
+++ b/sys/x86/iommu/amd_iommu.h
@@ -214,8 +214,9 @@
void amdiommu_domain_unload(struct iommu_domain *iodom,
struct iommu_map_entries_tailq *entries, bool cansleep);
struct amdiommu_ctx *amdiommu_get_ctx_for_dev(struct amdiommu_unit *unit,
- device_t dev, uint16_t rid, int dev_domain, bool id_mapped,
- bool rmrr_init, uint8_t dte, uint32_t edte);
+ struct amdiommu_domain *domain, device_t dev, uint16_t rid,
+ int dev_domain, bool id_mapped, bool rmrr_init,
+ uint8_t dte, uint32_t edte);
struct iommu_ctx *amdiommu_get_ctx(struct iommu_unit *iommu, device_t dev,
uint16_t rid, bool id_mapped, bool rmrr_init);
struct amdiommu_ctx *amdiommu_find_ctx_locked(struct amdiommu_unit *unit,
@@ -224,6 +225,8 @@
struct iommu_ctx *context);
struct amdiommu_domain *amdiommu_find_domain(struct amdiommu_unit *unit,
uint16_t rid);
+struct iommu_domain *amdiommu_vmm_domain_add_dev(struct iommu_unit *iommu,
+ struct iommu_domain *domain, device_t dev, uint16_t rid);
void amdiommu_qi_invalidate_ctx_locked(struct amdiommu_ctx *ctx);
void amdiommu_qi_invalidate_ctx_locked_nowait(struct amdiommu_ctx *ctx);
@@ -239,6 +242,4 @@
void amdiommu_domain_free_pgtbl(struct amdiommu_domain *domain);
extern const struct iommu_domain_map_ops amdiommu_domain_map_ops;
-int amdiommu_is_running(void);
-
#endif
diff --git a/sys/x86/iommu/intel_ctx.c b/sys/x86/iommu/intel_ctx.c
--- a/sys/x86/iommu/intel_ctx.c
+++ b/sys/x86/iommu/intel_ctx.c
@@ -498,11 +498,11 @@
}
static struct dmar_ctx *
-dmar_get_ctx_for_dev1(struct dmar_unit *dmar, device_t dev, uint16_t rid,
- int dev_domain, int dev_busno, const void *dev_path, int dev_path_len,
- bool id_mapped, bool rmrr_init)
+dmar_get_ctx_for_dev1(struct dmar_unit *dmar, struct dmar_domain *domain,
+ device_t dev, uint16_t rid, int dev_domain, int dev_busno,
+ const void *dev_path, int dev_path_len, bool id_mapped, bool rmrr_init)
{
- struct dmar_domain *domain, *domain1;
+ struct dmar_domain *domain1;
struct dmar_ctx *ctx, *ctx1;
struct iommu_unit *unit __diagused;
dmar_ctx_entry_t *ctxp;
@@ -520,9 +520,10 @@
func = PCI_RID2FUNC(rid);
}
enable = false;
+ domain1 = NULL;
+ DMAR_ASSERT_LOCKED(dmar);
TD_PREP_PINNED_ASSERT;
unit = DMAR2IOMMU(dmar);
- DMAR_LOCK(dmar);
KASSERT(!iommu_is_buswide_ctx(unit, bus) || (slot == 0 && func == 0),
("iommu%d pci%d:%d:%d get_ctx for buswide", dmar->iommu.unit, bus,
slot, func));
@@ -534,23 +535,27 @@
* higher chance to succeed if the sleep is allowed.
*/
DMAR_UNLOCK(dmar);
- dmar_ensure_ctx_page(dmar, PCI_RID2BUS(rid));
- domain1 = dmar_domain_alloc(dmar, id_mapped);
- if (domain1 == NULL) {
- TD_PINNED_ASSERT;
- return (NULL);
- }
- if (!id_mapped) {
- error = domain_init_rmrr(domain1, dev, bus,
- slot, func, dev_domain, dev_busno, dev_path,
- dev_path_len);
- if (error == 0 && dev != NULL)
- error = dmar_reserve_pci_regions(domain1, dev);
- if (error != 0) {
- dmar_domain_destroy(domain1);
+ if (domain == NULL) {
+ dmar_ensure_ctx_page(dmar, PCI_RID2BUS(rid));
+ domain1 = dmar_domain_alloc(dmar, id_mapped);
+ if (domain1 == NULL) {
TD_PINNED_ASSERT;
return (NULL);
}
+ if (!id_mapped) {
+ error = domain_init_rmrr(domain1, dev, bus,
+ slot, func, dev_domain, dev_busno, dev_path,
+ dev_path_len);
+ if (error == 0 && dev != NULL) {
+ error = dmar_reserve_pci_regions(
+ domain1, dev);
+ }
+ if (error != 0) {
+ dmar_domain_destroy(domain1);
+ TD_PINNED_ASSERT;
+ return (NULL);
+ }
+ }
}
ctx1 = dmar_ctx_alloc(domain1, rid);
ctxp = dmar_map_ctx_entry(ctx1, &sf);
@@ -562,7 +567,15 @@
*/
ctx = dmar_find_ctx_locked(dmar, rid);
if (ctx == NULL) {
- domain = domain1;
+ if (LIST_EMPTY(&dmar->domains)) {
+ MPASS(domain == NULL);
+ enable = true;
+ }
+ if (domain == NULL) {
+ domain = domain1;
+ domain1 = NULL;
+ LIST_INSERT_HEAD(&dmar->domains, domain, link);
+ }
ctx = ctx1;
dmar_ctx_link(ctx);
ctx->context.tag->owner = dev;
@@ -573,9 +586,6 @@
* DMAR unit. Enable the translation after
* everything is set up.
*/
- if (LIST_EMPTY(&dmar->domains))
- enable = true;
- LIST_INSERT_HEAD(&dmar->domains, domain, link);
ctx_id_entry_init(ctx, ctxp, false, bus);
if (dev != NULL) {
device_printf(dev,
@@ -587,14 +597,17 @@
}
iommu_unmap_pgtbl(sf);
} else {
- iommu_unmap_pgtbl(sf);
- dmar_domain_destroy(domain1);
/* Nothing needs to be done to destroy ctx1. */
free(ctx1, M_DMAR_CTX);
domain = CTX2DOM(ctx);
ctx->context.refs++; /* tag referenced us */
}
+ if (domain1 != NULL) {
+ iommu_unmap_pgtbl(sf);
+ dmar_domain_destroy(domain1);
+ }
} else {
+ MPASS(domain == NULL);
domain = CTX2DOM(ctx);
if (ctx->context.tag->owner == NULL)
ctx->context.tag->owner = dev;
@@ -632,13 +645,13 @@
return (NULL);
}
}
- DMAR_UNLOCK(dmar);
TD_PINNED_ASSERT;
return (ctx);
}
-struct dmar_ctx *
-dmar_get_ctx_for_dev(struct dmar_unit *dmar, device_t dev, uint16_t rid,
+static struct dmar_ctx *
+dmar_get_ctx_for_dev_locked(struct dmar_unit *dmar,
+ struct dmar_domain *domain, device_t dev, uint16_t rid,
bool id_mapped, bool rmrr_init)
{
int dev_domain, dev_path_len, dev_busno;
@@ -647,8 +660,25 @@
dev_path_len = dmar_dev_depth(dev);
ACPI_DMAR_PCI_PATH dev_path[dev_path_len];
dmar_dev_path(dev, &dev_busno, dev_path, dev_path_len);
- return (dmar_get_ctx_for_dev1(dmar, dev, rid, dev_domain, dev_busno,
- dev_path, dev_path_len, id_mapped, rmrr_init));
+ return (dmar_get_ctx_for_dev1(dmar, domain, dev, rid, dev_domain,
+ dev_busno, dev_path, dev_path_len, id_mapped, rmrr_init));
+}
+
+struct dmar_ctx *
+dmar_get_ctx_for_dev(struct dmar_unit *dmar, struct dmar_domain *domain,
+ device_t dev, uint16_t rid, bool id_mapped, bool rmrr_init)
+{
+ struct dmar_ctx *ctx;
+
+ DMAR_LOCK(dmar);
+ ctx = dmar_get_ctx_for_dev_locked(dmar, domain, dev, rid,
+ id_mapped, rmrr_init);
+ if (ctx != NULL) {
+ MPASS((ctx->context.domain->flags & IOMMU_DOMAIN_VMM) == 0);
+ ctx->context.domain->flags |= IOMMU_DOMAIN_BUSDMA;
+ }
+ DMAR_UNLOCK(dmar);
+ return (ctx);
}
struct dmar_ctx *
@@ -657,38 +687,51 @@
const void *dev_path, int dev_path_len,
bool id_mapped, bool rmrr_init)
{
+ struct dmar_ctx *ctx;
- return (dmar_get_ctx_for_dev1(dmar, NULL, rid, dev_domain, dev_busno,
- dev_path, dev_path_len, id_mapped, rmrr_init));
+ DMAR_LOCK(dmar);
+ ctx = dmar_get_ctx_for_dev1(dmar, NULL, NULL, rid, dev_domain,
+ dev_busno, dev_path, dev_path_len, id_mapped, rmrr_init);
+ if (ctx != NULL) {
+ MPASS((ctx->context.domain->flags & IOMMU_DOMAIN_VMM) == 0);
+ ctx->context.domain->flags |= IOMMU_DOMAIN_BUSDMA;
+ }
+ DMAR_UNLOCK(dmar);
+ return (ctx);
}
-int
+static struct dmar_domain *
dmar_move_ctx_to_domain(struct dmar_domain *domain, struct dmar_ctx *ctx)
{
struct dmar_unit *dmar;
struct dmar_domain *old_domain;
dmar_ctx_entry_t *ctxp;
struct sf_buf *sf;
- int error;
dmar = domain->dmar;
+ DMAR_ASSERT_LOCKED(dmar);
old_domain = CTX2DOM(ctx);
- if (domain == old_domain)
+ if (domain == old_domain) {
+ DMAR_UNLOCK(dmar);
return (0);
+ }
KASSERT(old_domain->iodom.iommu == domain->iodom.iommu,
("domain %p %u moving between dmars %u %u", domain,
- domain->domain, old_domain->iodom.iommu->unit,
- domain->iodom.iommu->unit));
+ domain->domain, old_domain->dmar->iommu.unit,
+ domain->dmar->iommu.unit));
+
+ if ((old_domain->iodom.flags & IOMMU_DOMAIN_RMRR) != 0)
+ return (old_domain);
+
TD_PREP_PINNED_ASSERT;
ctxp = dmar_map_ctx_entry(ctx, &sf);
- DMAR_LOCK(dmar);
dmar_ctx_unlink(ctx);
ctx->context.domain = &domain->iodom;
dmar_ctx_link(ctx);
ctx_id_entry_init(ctx, ctxp, true, PCI_BUSMAX + 100);
iommu_unmap_pgtbl(sf);
- error = dmar_flush_for_ctx_entry(dmar, true);
+ (void)dmar_flush_for_ctx_entry(dmar, true);
/* If flush failed, rolling back would not work as well. */
printf("dmar%d rid %x domain %d->%d %s-mapped\n",
dmar->iommu.unit, ctx->context.rid, old_domain->domain,
@@ -696,7 +739,59 @@
"id" : "re");
dmar_unref_domain_locked(dmar, old_domain);
TD_PINNED_ASSERT;
- return (error);
+ return (domain);
+}
+
+/*
+ * Create a VMM domain for the given device. Keep on private domain
+ * if the device needs RMRR. Otherwise coalesce VMM domains to reduce
+ * number of maintained page tables. If this is the first domain on
+ * this dmar on this VM (domain == NULL), reuse already created busdma
+ * domain if possible.
+ */
+struct iommu_domain *
+dmar_vmm_domain_add_dev(struct iommu_unit *iommu, struct iommu_domain *domain,
+ device_t dev, uint16_t rid)
+{
+ struct dmar_unit *dmar;
+ struct dmar_domain *ddomain, *rdomain;
+ struct dmar_ctx *ctx;
+ bool drain;
+
+ dmar = IOMMU2DMAR(iommu);
+ ddomain = domain == NULL ? NULL : IODOM2DOM(domain);
+ MPASS(ddomain == NULL || ddomain->dmar == dmar);
+ rdomain = NULL;
+ drain = false;
+ DMAR_LOCK(dmar);
+ ctx = dmar_find_ctx_locked(dmar, rid);
+ if (ctx != NULL) {
+ rdomain = domain != NULL ? dmar_move_ctx_to_domain(ddomain,
+ ctx) : IODOM2DOM(ctx->context.domain);
+ } else {
+ ctx = dmar_get_ctx_for_dev_locked(dmar, ddomain, dev, rid,
+ false, true);
+ if (ctx != NULL) {
+ rdomain = IODOM2DOM(ctx->context.domain);
+ MPASS(domain == NULL || rdomain == ddomain);
+ }
+ }
+ if (rdomain != NULL) {
+ MPASS((rdomain->iodom.flags & (IOMMU_DOMAIN_BUSDMA |
+ IOMMU_DOMAIN_VMM)) != (IOMMU_DOMAIN_BUSDMA |
+ IOMMU_DOMAIN_VMM));
+ if ((rdomain->iodom.flags & IOMMU_DOMAIN_BUSDMA) != 0) {
+ rdomain->iodom.flags &= ~IOMMU_DOMAIN_BUSDMA;
+ drain = true;
+ }
+ rdomain->iodom.flags |= IOMMU_DOMAIN_VMM;
+ }
+ DMAR_UNLOCK(dmar);
+ if (drain) {
+ taskqueue_drain(dmar->iommu.delayed_taskqueue,
+ &rdomain->iodom.unload_task);
+ }
+ return (DOM2IODOM(rdomain));
}
static void
@@ -915,7 +1010,7 @@
struct dmar_ctx *ret;
dmar = IOMMU2DMAR(iommu);
- ret = dmar_get_ctx_for_dev(dmar, dev, rid, id_mapped, rmrr_init);
+ ret = dmar_get_ctx_for_dev(dmar, NULL, dev, rid, id_mapped, rmrr_init);
return (CTX2IOCTX(ret));
}
diff --git a/sys/x86/iommu/intel_dmar.h b/sys/x86/iommu/intel_dmar.h
--- a/sys/x86/iommu/intel_dmar.h
+++ b/sys/x86/iommu/intel_dmar.h
@@ -229,12 +229,12 @@
int dmar_dev_depth(device_t child);
void dmar_dev_path(device_t child, int *busno, void *path1, int depth);
-struct dmar_ctx *dmar_get_ctx_for_dev(struct dmar_unit *dmar, device_t dev,
- uint16_t rid, bool id_mapped, bool rmrr_init);
+struct dmar_ctx *dmar_get_ctx_for_dev(struct dmar_unit *dmar,
+ struct dmar_domain *domain, device_t dev, uint16_t rid, bool id_mapped,
+ bool rmrr_init);
struct dmar_ctx *dmar_get_ctx_for_devpath(struct dmar_unit *dmar, uint16_t rid,
int dev_domain, int dev_busno, const void *dev_path, int dev_path_len,
bool id_mapped, bool rmrr_init);
-int dmar_move_ctx_to_domain(struct dmar_domain *domain, struct dmar_ctx *ctx);
void dmar_free_ctx_locked_method(struct iommu_unit *dmar,
struct iommu_ctx *ctx);
struct dmar_ctx *dmar_find_ctx_locked(struct dmar_unit *dmar, uint16_t rid);
@@ -244,6 +244,8 @@
bool cansleep);
void dmar_domain_unload(struct iommu_domain *iodom,
struct iommu_map_entries_tailq *entries, bool cansleep);
+struct iommu_domain *dmar_vmm_domain_add_dev(struct iommu_unit *iommu,
+ struct iommu_domain *domain, device_t dev, uint16_t rid);
void dmar_dev_parse_rmrr(struct dmar_domain *domain, int dev_domain,
int dev_busno, const void *dev_path, int dev_path_len,
@@ -263,8 +265,6 @@
bool activehi, int irq, u_int *cookie, uint32_t *hi, uint32_t *lo);
int dmar_unmap_ioapic_intr(u_int ioapic_id, u_int *cookie);
-int dmar_is_running(void);
-
extern int haw;
extern int dmar_rmrr_enable;
diff --git a/sys/x86/iommu/intel_drv.c b/sys/x86/iommu/intel_drv.c
--- a/sys/x86/iommu/intel_drv.c
+++ b/sys/x86/iommu/intel_drv.c
@@ -1329,6 +1329,7 @@
.get_ctx = dmar_get_ctx,
.free_ctx_locked = dmar_free_ctx_locked_method,
.find = dmar_find_method,
+ .vmm_domain_add_dev = dmar_vmm_domain_add_dev,
.alloc_msi_intr = dmar_alloc_msi_intr,
.map_msi_intr = dmar_map_msi_intr,
.unmap_msi_intr = dmar_unmap_msi_intr,
diff --git a/sys/x86/iommu/x86_iommu.h b/sys/x86/iommu/x86_iommu.h
--- a/sys/x86/iommu/x86_iommu.h
+++ b/sys/x86/iommu/x86_iommu.h
@@ -82,6 +82,8 @@
void (*free_ctx_locked)(struct iommu_unit *iommu,
struct iommu_ctx *context);
struct iommu_unit *(*find)(device_t dev, bool verbose);
+ struct iommu_domain *(*vmm_domain_add_dev)(struct iommu_unit *iommu,
+ struct iommu_domain *domain, device_t dev, uint16_t rid);
int (*alloc_msi_intr)(device_t src, u_int *cookies, u_int count);
int (*map_msi_intr)(device_t src, u_int cpu, u_int vector,
u_int cookie, uint64_t *addr, uint32_t *data);
@@ -199,4 +201,7 @@
void iommu_db_domain_print_contexts(struct iommu_domain *iodom);
void iommu_db_domain_print_mappings(struct iommu_domain *iodom);
+int amdiommu_is_running(void);
+int dmar_is_running(void);
+
#endif
diff --git a/usr.sbin/bhyve/pci_passthru.c b/usr.sbin/bhyve/pci_passthru.c
--- a/usr.sbin/bhyve/pci_passthru.c
+++ b/usr.sbin/bhyve/pci_passthru.c
@@ -582,7 +582,7 @@
struct pci_devinst *pi;
struct pci_bar_io bar;
enum pcibar_type bartype;
- uint64_t base, size;
+ uint64_t base, old_base, size;
pi = sc->psc_pi;
@@ -621,8 +621,14 @@
"base %#lx or size %#lx not page aligned\n",
sc->psc_sel.pc_bus, sc->psc_sel.pc_dev,
sc->psc_sel.pc_func, i, base, size);
- return (-1);
}
+ if ((base & PAGE_MASK) != 0) {
+ old_base = base;
+ base = trunc_page(base);
+ size += old_base - base;
+ }
+ if ((size & PAGE_MASK) != 0)
+ size = round_page(size);
}
/* Cache information about the "real" BAR */
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Nov 25, 12:43 AM (6 h, 54 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
26086441
Default Alt Text
D25672.diff (55 KB)
Attached To
Mode
D25672: bhyve, DMAR: integrate
Attached
Detach File
Event Timeline
Log In to Comment