Changeset View
Changeset View
Standalone View
Standalone View
sys/x86/iommu/intel_idpgtbl.c
Show First 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | |||||
#include <machine/md_var.h> | #include <machine/md_var.h> | ||||
#include <machine/specialreg.h> | #include <machine/specialreg.h> | ||||
#include <x86/include/busdma_impl.h> | #include <x86/include/busdma_impl.h> | ||||
#include <x86/iommu/intel_reg.h> | #include <x86/iommu/intel_reg.h> | ||||
#include <x86/iommu/busdma_dmar.h> | #include <x86/iommu/busdma_dmar.h> | ||||
#include <dev/pci/pcireg.h> | #include <dev/pci/pcireg.h> | ||||
#include <x86/iommu/intel_dmar.h> | #include <x86/iommu/intel_dmar.h> | ||||
static int domain_unmap_buf_locked(struct dmar_domain *domain, | static int domain_unmap_buf_locked(struct iommu_domain *domain, | ||||
dmar_gaddr_t base, dmar_gaddr_t size, int flags); | dmar_gaddr_t base, dmar_gaddr_t size, int flags); | ||||
/* | /* | ||||
* The cache of the identity mapping page tables for the DMARs. Using | * The cache of the identity mapping page tables for the DMARs. Using | ||||
* the cache saves significant amount of memory for page tables by | * the cache saves significant amount of memory for page tables by | ||||
* reusing the page tables, since usually DMARs are identical and have | * reusing the page tables, since usually DMARs are identical and have | ||||
* the same capabilities. Still, cache records the information needed | * the same capabilities. Still, cache records the information needed | ||||
* to match DMAR capabilities and page table format, to correctly | * to match DMAR capabilities and page table format, to correctly | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | |||||
* Find a ready and compatible identity-mapping page table in the | * Find a ready and compatible identity-mapping page table in the | ||||
* cache. If not found, populate the identity-mapping page table for | * cache. If not found, populate the identity-mapping page table for | ||||
* the context, up to the maxaddr. The maxaddr byte is allowed to be | * the context, up to the maxaddr. The maxaddr byte is allowed to be | ||||
* not mapped, which is aligned with the definition of Maxmem as the | * not mapped, which is aligned with the definition of Maxmem as the | ||||
* highest usable physical address + 1. If superpages are used, the | * highest usable physical address + 1. If superpages are used, the | ||||
* maxaddr is typically mapped. | * maxaddr is typically mapped. | ||||
*/ | */ | ||||
vm_object_t | vm_object_t | ||||
domain_get_idmap_pgtbl(struct dmar_domain *domain, dmar_gaddr_t maxaddr) | domain_get_idmap_pgtbl(struct iommu_domain *domain, dmar_gaddr_t maxaddr) | ||||
{ | { | ||||
struct dmar_unit *unit; | struct iommu_unit *unit; | ||||
struct idpgtbl *tbl; | struct idpgtbl *tbl; | ||||
vm_object_t res; | vm_object_t res; | ||||
vm_page_t m; | vm_page_t m; | ||||
int leaf, i; | int leaf, i; | ||||
leaf = 0; /* silence gcc */ | leaf = 0; /* silence gcc */ | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | end: | ||||
if (!DMAR_IS_COHERENT(unit)) { | if (!DMAR_IS_COHERENT(unit)) { | ||||
VM_OBJECT_WLOCK(res); | VM_OBJECT_WLOCK(res); | ||||
for (m = vm_page_lookup(res, 0); m != NULL; | for (m = vm_page_lookup(res, 0); m != NULL; | ||||
m = vm_page_next(m)) | m = vm_page_next(m)) | ||||
pmap_invalidate_cache_pages(&m, 1); | pmap_invalidate_cache_pages(&m, 1); | ||||
VM_OBJECT_WUNLOCK(res); | VM_OBJECT_WUNLOCK(res); | ||||
} | } | ||||
if ((unit->hw_cap & DMAR_CAP_RWBF) != 0) { | if ((unit->hw_cap & DMAR_CAP_RWBF) != 0) { | ||||
DMAR_LOCK(unit); | IOMMU_LOCK(unit); | ||||
dmar_flush_write_bufs(unit); | dmar_flush_write_bufs(unit); | ||||
DMAR_UNLOCK(unit); | IOMMU_UNLOCK(unit); | ||||
} | } | ||||
return (res); | return (res); | ||||
} | } | ||||
/* | /* | ||||
* Return a reference to the identity mapping page table to the cache. | * Return a reference to the identity mapping page table to the cache. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | |||||
* address. Support superpages. | * address. Support superpages. | ||||
*/ | */ | ||||
/* | /* | ||||
* Index of the pte for the guest address base in the page table at | * Index of the pte for the guest address base in the page table at | ||||
* the level lvl. | * the level lvl. | ||||
*/ | */ | ||||
static int | static int | ||||
domain_pgtbl_pte_off(struct dmar_domain *domain, dmar_gaddr_t base, int lvl) | domain_pgtbl_pte_off(struct iommu_domain *domain, dmar_gaddr_t base, int lvl) | ||||
{ | { | ||||
base >>= DMAR_PAGE_SHIFT + (domain->pglvl - lvl - 1) * | base >>= DMAR_PAGE_SHIFT + (domain->pglvl - lvl - 1) * | ||||
DMAR_NPTEPGSHIFT; | DMAR_NPTEPGSHIFT; | ||||
return (base & DMAR_PTEMASK); | return (base & DMAR_PTEMASK); | ||||
} | } | ||||
/* | /* | ||||
* Returns the page index of the page table page in the page table | * Returns the page index of the page table page in the page table | ||||
* object, which maps the given address base at the page table level | * object, which maps the given address base at the page table level | ||||
* lvl. | * lvl. | ||||
*/ | */ | ||||
static vm_pindex_t | static vm_pindex_t | ||||
domain_pgtbl_get_pindex(struct dmar_domain *domain, dmar_gaddr_t base, int lvl) | domain_pgtbl_get_pindex(struct iommu_domain *domain, dmar_gaddr_t base, int lvl) | ||||
{ | { | ||||
vm_pindex_t idx, pidx; | vm_pindex_t idx, pidx; | ||||
int i; | int i; | ||||
KASSERT(lvl >= 0 && lvl < domain->pglvl, | KASSERT(lvl >= 0 && lvl < domain->pglvl, | ||||
("wrong lvl %p %d", domain, lvl)); | ("wrong lvl %p %d", domain, lvl)); | ||||
for (pidx = idx = 0, i = 0; i < lvl; i++, pidx = idx) { | for (pidx = idx = 0, i = 0; i < lvl; i++, pidx = idx) { | ||||
idx = domain_pgtbl_pte_off(domain, base, i) + | idx = domain_pgtbl_pte_off(domain, base, i) + | ||||
pidx * DMAR_NPTEPG + 1; | pidx * DMAR_NPTEPG + 1; | ||||
} | } | ||||
return (idx); | return (idx); | ||||
} | } | ||||
static dmar_pte_t * | static dmar_pte_t * | ||||
domain_pgtbl_map_pte(struct dmar_domain *domain, dmar_gaddr_t base, int lvl, | domain_pgtbl_map_pte(struct iommu_domain *domain, dmar_gaddr_t base, int lvl, | ||||
int flags, vm_pindex_t *idxp, struct sf_buf **sf) | int flags, vm_pindex_t *idxp, struct sf_buf **sf) | ||||
{ | { | ||||
vm_page_t m; | vm_page_t m; | ||||
struct sf_buf *sfp; | struct sf_buf *sfp; | ||||
dmar_pte_t *pte, *ptep; | dmar_pte_t *pte, *ptep; | ||||
vm_pindex_t idx, idx1; | vm_pindex_t idx, idx1; | ||||
DMAR_DOMAIN_ASSERT_PGLOCKED(domain); | IOMMU_DOMAIN_ASSERT_PGLOCKED(domain); | ||||
KASSERT((flags & DMAR_PGF_OBJL) != 0, ("lost PGF_OBJL")); | KASSERT((flags & DMAR_PGF_OBJL) != 0, ("lost PGF_OBJL")); | ||||
idx = domain_pgtbl_get_pindex(domain, base, lvl); | idx = domain_pgtbl_get_pindex(domain, base, lvl); | ||||
if (*sf != NULL && idx == *idxp) { | if (*sf != NULL && idx == *idxp) { | ||||
pte = (dmar_pte_t *)sf_buf_kva(*sf); | pte = (dmar_pte_t *)sf_buf_kva(*sf); | ||||
} else { | } else { | ||||
if (*sf != NULL) | if (*sf != NULL) | ||||
dmar_unmap_pgtbl(*sf); | dmar_unmap_pgtbl(*sf); | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | if (pte == NULL) { | ||||
goto retry; | goto retry; | ||||
} | } | ||||
} | } | ||||
pte += domain_pgtbl_pte_off(domain, base, lvl); | pte += domain_pgtbl_pte_off(domain, base, lvl); | ||||
return (pte); | return (pte); | ||||
} | } | ||||
static int | static int | ||||
domain_map_buf_locked(struct dmar_domain *domain, dmar_gaddr_t base, | domain_map_buf_locked(struct iommu_domain *domain, dmar_gaddr_t base, | ||||
dmar_gaddr_t size, vm_page_t *ma, uint64_t pflags, int flags) | dmar_gaddr_t size, vm_page_t *ma, uint64_t pflags, int flags) | ||||
{ | { | ||||
dmar_pte_t *pte; | dmar_pte_t *pte; | ||||
struct sf_buf *sf; | struct sf_buf *sf; | ||||
dmar_gaddr_t pg_sz, base1, size1; | dmar_gaddr_t pg_sz, base1, size1; | ||||
vm_pindex_t pi, c, idx, run_sz; | vm_pindex_t pi, c, idx, run_sz; | ||||
int lvl; | int lvl; | ||||
bool superpage; | bool superpage; | ||||
DMAR_DOMAIN_ASSERT_PGLOCKED(domain); | IOMMU_DOMAIN_ASSERT_PGLOCKED(domain); | ||||
base1 = base; | base1 = base; | ||||
size1 = size; | size1 = size; | ||||
flags |= DMAR_PGF_OBJL; | flags |= DMAR_PGF_OBJL; | ||||
TD_PREP_PINNED_ASSERT; | TD_PREP_PINNED_ASSERT; | ||||
for (sf = NULL, pi = 0; size > 0; base += pg_sz, size -= pg_sz, | for (sf = NULL, pi = 0; size > 0; base += pg_sz, size -= pg_sz, | ||||
pi += run_sz) { | pi += run_sz) { | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | domain_map_buf_locked(struct iommu_domain *domain, dmar_gaddr_t base, | ||||
} | } | ||||
if (sf != NULL) | if (sf != NULL) | ||||
dmar_unmap_pgtbl(sf); | dmar_unmap_pgtbl(sf); | ||||
TD_PINNED_ASSERT; | TD_PINNED_ASSERT; | ||||
return (0); | return (0); | ||||
} | } | ||||
int | int | ||||
domain_map_buf(struct dmar_domain *domain, dmar_gaddr_t base, dmar_gaddr_t size, | domain_map_buf(struct iommu_domain *domain, dmar_gaddr_t base, dmar_gaddr_t size, | ||||
vm_page_t *ma, uint64_t pflags, int flags) | vm_page_t *ma, uint64_t pflags, int flags) | ||||
{ | { | ||||
struct dmar_unit *unit; | struct iommu_unit *unit; | ||||
int error; | int error; | ||||
unit = domain->dmar; | unit = domain->dmar; | ||||
KASSERT((domain->flags & DMAR_DOMAIN_IDMAP) == 0, | KASSERT((domain->flags & IOMMU_DOMAIN_IDMAP) == 0, | ||||
("modifying idmap pagetable domain %p", domain)); | ("modifying idmap pagetable domain %p", domain)); | ||||
KASSERT((base & DMAR_PAGE_MASK) == 0, | KASSERT((base & DMAR_PAGE_MASK) == 0, | ||||
("non-aligned base %p %jx %jx", domain, (uintmax_t)base, | ("non-aligned base %p %jx %jx", domain, (uintmax_t)base, | ||||
(uintmax_t)size)); | (uintmax_t)size)); | ||||
KASSERT((size & DMAR_PAGE_MASK) == 0, | KASSERT((size & DMAR_PAGE_MASK) == 0, | ||||
("non-aligned size %p %jx %jx", domain, (uintmax_t)base, | ("non-aligned size %p %jx %jx", domain, (uintmax_t)base, | ||||
(uintmax_t)size)); | (uintmax_t)size)); | ||||
KASSERT(size > 0, ("zero size %p %jx %jx", domain, (uintmax_t)base, | KASSERT(size > 0, ("zero size %p %jx %jx", domain, (uintmax_t)base, | ||||
Show All 17 Lines | KASSERT((pflags & DMAR_PTE_SNP) == 0 || | ||||
("PTE_SNP for dmar without snoop control %p %jx", | ("PTE_SNP for dmar without snoop control %p %jx", | ||||
domain, (uintmax_t)pflags)); | domain, (uintmax_t)pflags)); | ||||
KASSERT((pflags & DMAR_PTE_TM) == 0 || | KASSERT((pflags & DMAR_PTE_TM) == 0 || | ||||
(unit->hw_ecap & DMAR_ECAP_DI) != 0, | (unit->hw_ecap & DMAR_ECAP_DI) != 0, | ||||
("PTE_TM for dmar without DIOTLB %p %jx", | ("PTE_TM for dmar without DIOTLB %p %jx", | ||||
domain, (uintmax_t)pflags)); | domain, (uintmax_t)pflags)); | ||||
KASSERT((flags & ~DMAR_PGF_WAITOK) == 0, ("invalid flags %x", flags)); | KASSERT((flags & ~DMAR_PGF_WAITOK) == 0, ("invalid flags %x", flags)); | ||||
DMAR_DOMAIN_PGLOCK(domain); | IOMMU_DOMAIN_PGLOCK(domain); | ||||
error = domain_map_buf_locked(domain, base, size, ma, pflags, flags); | error = domain_map_buf_locked(domain, base, size, ma, pflags, flags); | ||||
DMAR_DOMAIN_PGUNLOCK(domain); | IOMMU_DOMAIN_PGUNLOCK(domain); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
if ((unit->hw_cap & DMAR_CAP_CM) != 0) | if ((unit->hw_cap & DMAR_CAP_CM) != 0) | ||||
domain_flush_iotlb_sync(domain, base, size); | domain_flush_iotlb_sync(domain, base, size); | ||||
else if ((unit->hw_cap & DMAR_CAP_RWBF) != 0) { | else if ((unit->hw_cap & DMAR_CAP_RWBF) != 0) { | ||||
/* See 11.1 Write Buffer Flushing. */ | /* See 11.1 Write Buffer Flushing. */ | ||||
DMAR_LOCK(unit); | IOMMU_LOCK(unit); | ||||
dmar_flush_write_bufs(unit); | dmar_flush_write_bufs(unit); | ||||
DMAR_UNLOCK(unit); | IOMMU_UNLOCK(unit); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
static void domain_unmap_clear_pte(struct dmar_domain *domain, | static void domain_unmap_clear_pte(struct iommu_domain *domain, | ||||
dmar_gaddr_t base, int lvl, int flags, dmar_pte_t *pte, | dmar_gaddr_t base, int lvl, int flags, dmar_pte_t *pte, | ||||
struct sf_buf **sf, bool free_fs); | struct sf_buf **sf, bool free_fs); | ||||
static void | static void | ||||
domain_free_pgtbl_pde(struct dmar_domain *domain, dmar_gaddr_t base, | domain_free_pgtbl_pde(struct iommu_domain *domain, dmar_gaddr_t base, | ||||
int lvl, int flags) | int lvl, int flags) | ||||
{ | { | ||||
struct sf_buf *sf; | struct sf_buf *sf; | ||||
dmar_pte_t *pde; | dmar_pte_t *pde; | ||||
vm_pindex_t idx; | vm_pindex_t idx; | ||||
sf = NULL; | sf = NULL; | ||||
pde = domain_pgtbl_map_pte(domain, base, lvl, flags, &idx, &sf); | pde = domain_pgtbl_map_pte(domain, base, lvl, flags, &idx, &sf); | ||||
domain_unmap_clear_pte(domain, base, lvl, flags, pde, &sf, true); | domain_unmap_clear_pte(domain, base, lvl, flags, pde, &sf, true); | ||||
} | } | ||||
static void | static void | ||||
domain_unmap_clear_pte(struct dmar_domain *domain, dmar_gaddr_t base, int lvl, | domain_unmap_clear_pte(struct iommu_domain *domain, dmar_gaddr_t base, int lvl, | ||||
int flags, dmar_pte_t *pte, struct sf_buf **sf, bool free_sf) | int flags, dmar_pte_t *pte, struct sf_buf **sf, bool free_sf) | ||||
{ | { | ||||
vm_page_t m; | vm_page_t m; | ||||
dmar_pte_clear(&pte->pte); | dmar_pte_clear(&pte->pte); | ||||
dmar_flush_pte_to_ram(domain->dmar, pte); | dmar_flush_pte_to_ram(domain->dmar, pte); | ||||
m = sf_buf_page(*sf); | m = sf_buf_page(*sf); | ||||
if (free_sf) { | if (free_sf) { | ||||
Show All 12 Lines | domain_unmap_clear_pte(struct iommu_domain *domain, dmar_gaddr_t base, int lvl, | ||||
dmar_pgfree(domain->pgtbl_obj, m->pindex, flags); | dmar_pgfree(domain->pgtbl_obj, m->pindex, flags); | ||||
domain_free_pgtbl_pde(domain, base, lvl - 1, flags); | domain_free_pgtbl_pde(domain, base, lvl - 1, flags); | ||||
} | } | ||||
/* | /* | ||||
* Assumes that the unmap is never partial. | * Assumes that the unmap is never partial. | ||||
*/ | */ | ||||
static int | static int | ||||
domain_unmap_buf_locked(struct dmar_domain *domain, dmar_gaddr_t base, | domain_unmap_buf_locked(struct iommu_domain *domain, dmar_gaddr_t base, | ||||
dmar_gaddr_t size, int flags) | dmar_gaddr_t size, int flags) | ||||
{ | { | ||||
dmar_pte_t *pte; | dmar_pte_t *pte; | ||||
struct sf_buf *sf; | struct sf_buf *sf; | ||||
vm_pindex_t idx; | vm_pindex_t idx; | ||||
dmar_gaddr_t pg_sz; | dmar_gaddr_t pg_sz; | ||||
int lvl; | int lvl; | ||||
DMAR_DOMAIN_ASSERT_PGLOCKED(domain); | IOMMU_DOMAIN_ASSERT_PGLOCKED(domain); | ||||
if (size == 0) | if (size == 0) | ||||
return (0); | return (0); | ||||
KASSERT((domain->flags & DMAR_DOMAIN_IDMAP) == 0, | KASSERT((domain->flags & IOMMU_DOMAIN_IDMAP) == 0, | ||||
("modifying idmap pagetable domain %p", domain)); | ("modifying idmap pagetable domain %p", domain)); | ||||
KASSERT((base & DMAR_PAGE_MASK) == 0, | KASSERT((base & DMAR_PAGE_MASK) == 0, | ||||
("non-aligned base %p %jx %jx", domain, (uintmax_t)base, | ("non-aligned base %p %jx %jx", domain, (uintmax_t)base, | ||||
(uintmax_t)size)); | (uintmax_t)size)); | ||||
KASSERT((size & DMAR_PAGE_MASK) == 0, | KASSERT((size & DMAR_PAGE_MASK) == 0, | ||||
("non-aligned size %p %jx %jx", domain, (uintmax_t)base, | ("non-aligned size %p %jx %jx", domain, (uintmax_t)base, | ||||
(uintmax_t)size)); | (uintmax_t)size)); | ||||
KASSERT(base < (1ULL << domain->agaw), | KASSERT(base < (1ULL << domain->agaw), | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | domain_unmap_buf_locked(struct iommu_domain *domain, dmar_gaddr_t base, | ||||
* can be ignored there. | * can be ignored there. | ||||
*/ | */ | ||||
TD_PINNED_ASSERT; | TD_PINNED_ASSERT; | ||||
return (0); | return (0); | ||||
} | } | ||||
int | int | ||||
domain_unmap_buf(struct dmar_domain *domain, dmar_gaddr_t base, | domain_unmap_buf(struct iommu_domain *domain, dmar_gaddr_t base, | ||||
dmar_gaddr_t size, int flags) | dmar_gaddr_t size, int flags) | ||||
{ | { | ||||
int error; | int error; | ||||
DMAR_DOMAIN_PGLOCK(domain); | IOMMU_DOMAIN_PGLOCK(domain); | ||||
error = domain_unmap_buf_locked(domain, base, size, flags); | error = domain_unmap_buf_locked(domain, base, size, flags); | ||||
DMAR_DOMAIN_PGUNLOCK(domain); | IOMMU_DOMAIN_PGUNLOCK(domain); | ||||
return (error); | return (error); | ||||
} | } | ||||
int | int | ||||
domain_alloc_pgtbl(struct dmar_domain *domain) | domain_alloc_pgtbl(struct iommu_domain *domain) | ||||
{ | { | ||||
vm_page_t m; | vm_page_t m; | ||||
KASSERT(domain->pgtbl_obj == NULL, | KASSERT(domain->pgtbl_obj == NULL, | ||||
("already initialized %p", domain)); | ("already initialized %p", domain)); | ||||
domain->pgtbl_obj = vm_pager_allocate(OBJT_PHYS, NULL, | domain->pgtbl_obj = vm_pager_allocate(OBJT_PHYS, NULL, | ||||
IDX_TO_OFF(pglvl_max_pages(domain->pglvl)), 0, 0, NULL); | IDX_TO_OFF(pglvl_max_pages(domain->pglvl)), 0, 0, NULL); | ||||
DMAR_DOMAIN_PGLOCK(domain); | IOMMU_DOMAIN_PGLOCK(domain); | ||||
m = dmar_pgalloc(domain->pgtbl_obj, 0, DMAR_PGF_WAITOK | | m = dmar_pgalloc(domain->pgtbl_obj, 0, DMAR_PGF_WAITOK | | ||||
DMAR_PGF_ZERO | DMAR_PGF_OBJL); | DMAR_PGF_ZERO | DMAR_PGF_OBJL); | ||||
/* No implicit free of the top level page table page. */ | /* No implicit free of the top level page table page. */ | ||||
m->ref_count = 1; | m->ref_count = 1; | ||||
DMAR_DOMAIN_PGUNLOCK(domain); | IOMMU_DOMAIN_PGUNLOCK(domain); | ||||
DMAR_LOCK(domain->dmar); | IOMMU_LOCK(domain->dmar); | ||||
domain->flags |= DMAR_DOMAIN_PGTBL_INITED; | domain->flags |= IOMMU_DOMAIN_PGTBL_INITED; | ||||
DMAR_UNLOCK(domain->dmar); | IOMMU_UNLOCK(domain->dmar); | ||||
return (0); | return (0); | ||||
} | } | ||||
void | void | ||||
domain_free_pgtbl(struct dmar_domain *domain) | domain_free_pgtbl(struct iommu_domain *domain) | ||||
{ | { | ||||
vm_object_t obj; | vm_object_t obj; | ||||
vm_page_t m; | vm_page_t m; | ||||
obj = domain->pgtbl_obj; | obj = domain->pgtbl_obj; | ||||
if (obj == NULL) { | if (obj == NULL) { | ||||
KASSERT((domain->dmar->hw_ecap & DMAR_ECAP_PT) != 0 && | KASSERT((domain->dmar->hw_ecap & DMAR_ECAP_PT) != 0 && | ||||
(domain->flags & DMAR_DOMAIN_IDMAP) != 0, | (domain->flags & IOMMU_DOMAIN_IDMAP) != 0, | ||||
("lost pagetable object domain %p", domain)); | ("lost pagetable object domain %p", domain)); | ||||
return; | return; | ||||
} | } | ||||
DMAR_DOMAIN_ASSERT_PGLOCKED(domain); | IOMMU_DOMAIN_ASSERT_PGLOCKED(domain); | ||||
domain->pgtbl_obj = NULL; | domain->pgtbl_obj = NULL; | ||||
if ((domain->flags & DMAR_DOMAIN_IDMAP) != 0) { | if ((domain->flags & IOMMU_DOMAIN_IDMAP) != 0) { | ||||
put_idmap_pgtbl(obj); | put_idmap_pgtbl(obj); | ||||
domain->flags &= ~DMAR_DOMAIN_IDMAP; | domain->flags &= ~IOMMU_DOMAIN_IDMAP; | ||||
return; | return; | ||||
} | } | ||||
/* Obliterate ref_counts */ | /* Obliterate ref_counts */ | ||||
VM_OBJECT_ASSERT_WLOCKED(obj); | VM_OBJECT_ASSERT_WLOCKED(obj); | ||||
for (m = vm_page_lookup(obj, 0); m != NULL; m = vm_page_next(m)) | for (m = vm_page_lookup(obj, 0); m != NULL; m = vm_page_next(m)) | ||||
m->ref_count = 0; | m->ref_count = 0; | ||||
VM_OBJECT_WUNLOCK(obj); | VM_OBJECT_WUNLOCK(obj); | ||||
vm_object_deallocate(obj); | vm_object_deallocate(obj); | ||||
} | } | ||||
static inline uint64_t | static inline uint64_t | ||||
domain_wait_iotlb_flush(struct dmar_unit *unit, uint64_t wt, int iro) | domain_wait_iotlb_flush(struct iommu_unit *unit, uint64_t wt, int iro) | ||||
{ | { | ||||
uint64_t iotlbr; | uint64_t iotlbr; | ||||
dmar_write8(unit, iro + DMAR_IOTLB_REG_OFF, DMAR_IOTLB_IVT | | dmar_write8(unit, iro + DMAR_IOTLB_REG_OFF, DMAR_IOTLB_IVT | | ||||
DMAR_IOTLB_DR | DMAR_IOTLB_DW | wt); | DMAR_IOTLB_DR | DMAR_IOTLB_DW | wt); | ||||
for (;;) { | for (;;) { | ||||
iotlbr = dmar_read8(unit, iro + DMAR_IOTLB_REG_OFF); | iotlbr = dmar_read8(unit, iro + DMAR_IOTLB_REG_OFF); | ||||
if ((iotlbr & DMAR_IOTLB_IVT) == 0) | if ((iotlbr & DMAR_IOTLB_IVT) == 0) | ||||
break; | break; | ||||
cpu_spinwait(); | cpu_spinwait(); | ||||
} | } | ||||
return (iotlbr); | return (iotlbr); | ||||
} | } | ||||
void | void | ||||
domain_flush_iotlb_sync(struct dmar_domain *domain, dmar_gaddr_t base, | domain_flush_iotlb_sync(struct iommu_domain *domain, dmar_gaddr_t base, | ||||
dmar_gaddr_t size) | dmar_gaddr_t size) | ||||
{ | { | ||||
struct dmar_unit *unit; | struct iommu_unit *unit; | ||||
dmar_gaddr_t isize; | dmar_gaddr_t isize; | ||||
uint64_t iotlbr; | uint64_t iotlbr; | ||||
int am, iro; | int am, iro; | ||||
unit = domain->dmar; | unit = domain->dmar; | ||||
KASSERT(!unit->qi_enabled, ("dmar%d: sync iotlb flush call", | KASSERT(!unit->qi_enabled, ("dmar%d: sync iotlb flush call", | ||||
unit->unit)); | unit->unit)); | ||||
iro = DMAR_ECAP_IRO(unit->hw_ecap) * 16; | iro = DMAR_ECAP_IRO(unit->hw_ecap) * 16; | ||||
DMAR_LOCK(unit); | IOMMU_LOCK(unit); | ||||
if ((unit->hw_cap & DMAR_CAP_PSI) == 0 || size > 2 * 1024 * 1024) { | if ((unit->hw_cap & DMAR_CAP_PSI) == 0 || size > 2 * 1024 * 1024) { | ||||
iotlbr = domain_wait_iotlb_flush(unit, DMAR_IOTLB_IIRG_DOM | | iotlbr = domain_wait_iotlb_flush(unit, DMAR_IOTLB_IIRG_DOM | | ||||
DMAR_IOTLB_DID(domain->domain), iro); | DMAR_IOTLB_DID(domain->domain), iro); | ||||
KASSERT((iotlbr & DMAR_IOTLB_IAIG_MASK) != | KASSERT((iotlbr & DMAR_IOTLB_IAIG_MASK) != | ||||
DMAR_IOTLB_IAIG_INVLD, | DMAR_IOTLB_IAIG_INVLD, | ||||
("dmar%d: invalidation failed %jx", unit->unit, | ("dmar%d: invalidation failed %jx", unit->unit, | ||||
(uintmax_t)iotlbr)); | (uintmax_t)iotlbr)); | ||||
} else { | } else { | ||||
Show All 13 Lines | for (; size > 0; base += isize, size -= isize) { | ||||
* Any non-page granularity covers whole guest | * Any non-page granularity covers whole guest | ||||
* address space for the domain. | * address space for the domain. | ||||
*/ | */ | ||||
if ((iotlbr & DMAR_IOTLB_IAIG_MASK) != | if ((iotlbr & DMAR_IOTLB_IAIG_MASK) != | ||||
DMAR_IOTLB_IAIG_PAGE) | DMAR_IOTLB_IAIG_PAGE) | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
DMAR_UNLOCK(unit); | IOMMU_UNLOCK(unit); | ||||
} | } |