Changeset View
Changeset View
Standalone View
Standalone View
sys/x86/iommu/intel_idpgtbl.c
Show First 20 Lines • Show All 159 Lines • ▼ Show 20 Lines | |||||
* 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 dmar_domain *domain, dmar_gaddr_t maxaddr) | ||||
{ | { | ||||
struct dmar_unit *unit; | struct dmar_unit *dmar; | ||||
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 All 12 Lines | domain_get_idmap_pgtbl(struct dmar_domain *domain, dmar_gaddr_t maxaddr) | ||||
* supported by the DMAR and leaf should be equal to the | * supported by the DMAR and leaf should be equal to the | ||||
* calculated value. The later restriction could be lifted | * calculated value. The later restriction could be lifted | ||||
* but I believe it is currently impossible to have any | * but I believe it is currently impossible to have any | ||||
* deviations for existing hardware. | * deviations for existing hardware. | ||||
*/ | */ | ||||
sx_slock(&idpgtbl_lock); | sx_slock(&idpgtbl_lock); | ||||
LIST_FOREACH(tbl, &idpgtbls, link) { | LIST_FOREACH(tbl, &idpgtbls, link) { | ||||
if (tbl->maxaddr >= maxaddr && | if (tbl->maxaddr >= maxaddr && | ||||
dmar_pglvl_supported(domain->dmar, tbl->pglvl) && | dmar_pglvl_supported(domain->iodom.iommu, tbl->pglvl) && | ||||
tbl->leaf == leaf) { | tbl->leaf == leaf) { | ||||
res = tbl->pgtbl_obj; | res = tbl->pgtbl_obj; | ||||
vm_object_reference(res); | vm_object_reference(res); | ||||
sx_sunlock(&idpgtbl_lock); | sx_sunlock(&idpgtbl_lock); | ||||
domain->pglvl = tbl->pglvl; /* XXXKIB ? */ | domain->pglvl = tbl->pglvl; /* XXXKIB ? */ | ||||
goto end; | goto end; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Not found in cache, relock the cache into exclusive mode to | * Not found in cache, relock the cache into exclusive mode to | ||||
* be able to add element, and recheck cache again after the | * be able to add element, and recheck cache again after the | ||||
* relock. | * relock. | ||||
*/ | */ | ||||
sx_sunlock(&idpgtbl_lock); | sx_sunlock(&idpgtbl_lock); | ||||
sx_xlock(&idpgtbl_lock); | sx_xlock(&idpgtbl_lock); | ||||
LIST_FOREACH(tbl, &idpgtbls, link) { | LIST_FOREACH(tbl, &idpgtbls, link) { | ||||
if (tbl->maxaddr >= maxaddr && | if (tbl->maxaddr >= maxaddr && | ||||
dmar_pglvl_supported(domain->dmar, tbl->pglvl) && | dmar_pglvl_supported(domain->iodom.iommu, tbl->pglvl) && | ||||
tbl->leaf == leaf) { | tbl->leaf == leaf) { | ||||
res = tbl->pgtbl_obj; | res = tbl->pgtbl_obj; | ||||
vm_object_reference(res); | vm_object_reference(res); | ||||
sx_xunlock(&idpgtbl_lock); | sx_xunlock(&idpgtbl_lock); | ||||
domain->pglvl = tbl->pglvl; /* XXXKIB ? */ | domain->pglvl = tbl->pglvl; /* XXXKIB ? */ | ||||
return (res); | return (res); | ||||
} | } | ||||
} | } | ||||
Show All 24 Lines | end: | ||||
* argument was possibly invalid at the time of the identity | * argument was possibly invalid at the time of the identity | ||||
* page table creation, since DMAR which was passed at the | * page table creation, since DMAR which was passed at the | ||||
* time of creation could be coherent, while current DMAR is | * time of creation could be coherent, while current DMAR is | ||||
* not. | * not. | ||||
* | * | ||||
* If DMAR cannot look into the chipset write buffer, flush it | * If DMAR cannot look into the chipset write buffer, flush it | ||||
* as well. | * as well. | ||||
*/ | */ | ||||
unit = domain->dmar; | dmar = (struct dmar_unit *)domain->iodom.iommu; | ||||
if (!DMAR_IS_COHERENT(unit)) { | if (!DMAR_IS_COHERENT(dmar)) { | ||||
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 ((dmar->hw_cap & DMAR_CAP_RWBF) != 0) { | ||||
DMAR_LOCK(unit); | DMAR_LOCK(dmar); | ||||
dmar_flush_write_bufs(unit); | dmar_flush_write_bufs(dmar); | ||||
DMAR_UNLOCK(unit); | DMAR_UNLOCK(dmar); | ||||
} | } | ||||
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 79 Lines • ▼ Show 20 Lines | |||||
domain_pgtbl_map_pte(struct dmar_domain *domain, dmar_gaddr_t base, int lvl, | domain_pgtbl_map_pte(struct dmar_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 All 30 Lines | if (pte == NULL) { | ||||
("loosing root page %p", domain)); | ("loosing root page %p", domain)); | ||||
m->ref_count--; | m->ref_count--; | ||||
dmar_pgfree(domain->pgtbl_obj, m->pindex, | dmar_pgfree(domain->pgtbl_obj, m->pindex, | ||||
flags); | flags); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
dmar_pte_store(&ptep->pte, DMAR_PTE_R | DMAR_PTE_W | | dmar_pte_store(&ptep->pte, DMAR_PTE_R | DMAR_PTE_W | | ||||
VM_PAGE_TO_PHYS(m)); | VM_PAGE_TO_PHYS(m)); | ||||
dmar_flush_pte_to_ram(domain->dmar, ptep); | dmar_flush_pte_to_ram( | ||||
(struct dmar_unit *)domain->iodom.iommu, ptep); | |||||
sf_buf_page(sfp)->ref_count += 1; | sf_buf_page(sfp)->ref_count += 1; | ||||
m->ref_count--; | m->ref_count--; | ||||
dmar_unmap_pgtbl(sfp); | dmar_unmap_pgtbl(sfp); | ||||
/* Only executed once. */ | /* Only executed once. */ | ||||
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 dmar_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 41 Lines • ▼ Show 20 Lines | if (pte == NULL) { | ||||
dmar_unmap_pgtbl(sf); | dmar_unmap_pgtbl(sf); | ||||
domain_unmap_buf_locked(domain, base1, base - base1, | domain_unmap_buf_locked(domain, base1, base - base1, | ||||
flags); | flags); | ||||
TD_PINNED_ASSERT; | TD_PINNED_ASSERT; | ||||
return (ENOMEM); | return (ENOMEM); | ||||
} | } | ||||
dmar_pte_store(&pte->pte, VM_PAGE_TO_PHYS(ma[pi]) | pflags | | dmar_pte_store(&pte->pte, VM_PAGE_TO_PHYS(ma[pi]) | pflags | | ||||
(superpage ? DMAR_PTE_SP : 0)); | (superpage ? DMAR_PTE_SP : 0)); | ||||
dmar_flush_pte_to_ram(domain->dmar, pte); | dmar_flush_pte_to_ram( | ||||
(struct dmar_unit *)domain->iodom.iommu, pte); | |||||
sf_buf_page(sf)->ref_count += 1; | sf_buf_page(sf)->ref_count += 1; | ||||
} | } | ||||
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 dmar_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 dmar_unit *dmar; | ||||
int error; | int error; | ||||
unit = domain->dmar; | dmar = (struct dmar_unit *)domain->iodom.iommu; | ||||
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, | ||||
(uintmax_t)size)); | (uintmax_t)size)); | ||||
KASSERT(base < (1ULL << domain->agaw), | KASSERT(base < (1ULL << domain->agaw), | ||||
("base too high %p %jx %jx agaw %d", domain, (uintmax_t)base, | ("base too high %p %jx %jx agaw %d", domain, (uintmax_t)base, | ||||
(uintmax_t)size, domain->agaw)); | (uintmax_t)size, domain->agaw)); | ||||
KASSERT(base + size < (1ULL << domain->agaw), | KASSERT(base + size < (1ULL << domain->agaw), | ||||
("end too high %p %jx %jx agaw %d", domain, (uintmax_t)base, | ("end too high %p %jx %jx agaw %d", domain, (uintmax_t)base, | ||||
(uintmax_t)size, domain->agaw)); | (uintmax_t)size, domain->agaw)); | ||||
KASSERT(base + size > base, | KASSERT(base + size > base, | ||||
("size overflow %p %jx %jx", domain, (uintmax_t)base, | ("size overflow %p %jx %jx", domain, (uintmax_t)base, | ||||
(uintmax_t)size)); | (uintmax_t)size)); | ||||
KASSERT((pflags & (DMAR_PTE_R | DMAR_PTE_W)) != 0, | KASSERT((pflags & (DMAR_PTE_R | DMAR_PTE_W)) != 0, | ||||
("neither read nor write %jx", (uintmax_t)pflags)); | ("neither read nor write %jx", (uintmax_t)pflags)); | ||||
KASSERT((pflags & ~(DMAR_PTE_R | DMAR_PTE_W | DMAR_PTE_SNP | | KASSERT((pflags & ~(DMAR_PTE_R | DMAR_PTE_W | DMAR_PTE_SNP | | ||||
DMAR_PTE_TM)) == 0, | DMAR_PTE_TM)) == 0, | ||||
("invalid pte flags %jx", (uintmax_t)pflags)); | ("invalid pte flags %jx", (uintmax_t)pflags)); | ||||
KASSERT((pflags & DMAR_PTE_SNP) == 0 || | KASSERT((pflags & DMAR_PTE_SNP) == 0 || | ||||
(unit->hw_ecap & DMAR_ECAP_SC) != 0, | (dmar->hw_ecap & DMAR_ECAP_SC) != 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, | (dmar->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 ((dmar->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 ((dmar->hw_cap & DMAR_CAP_RWBF) != 0) { | ||||
/* See 11.1 Write Buffer Flushing. */ | /* See 11.1 Write Buffer Flushing. */ | ||||
DMAR_LOCK(unit); | DMAR_LOCK(dmar); | ||||
dmar_flush_write_bufs(unit); | dmar_flush_write_bufs(dmar); | ||||
DMAR_UNLOCK(unit); | DMAR_UNLOCK(dmar); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
static void domain_unmap_clear_pte(struct dmar_domain *domain, | static void domain_unmap_clear_pte(struct dmar_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); | ||||
Show All 12 Lines | |||||
static void | static void | ||||
domain_unmap_clear_pte(struct dmar_domain *domain, dmar_gaddr_t base, int lvl, | domain_unmap_clear_pte(struct dmar_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((struct dmar_unit *)domain->iodom.iommu, pte); | ||||
m = sf_buf_page(*sf); | m = sf_buf_page(*sf); | ||||
if (free_sf) { | if (free_sf) { | ||||
dmar_unmap_pgtbl(*sf); | dmar_unmap_pgtbl(*sf); | ||||
*sf = NULL; | *sf = NULL; | ||||
} | } | ||||
m->ref_count--; | m->ref_count--; | ||||
if (m->ref_count != 0) | if (m->ref_count != 0) | ||||
return; | return; | ||||
Show All 15 Lines | domain_unmap_buf_locked(struct dmar_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 47 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
int | int | ||||
domain_unmap_buf(struct dmar_domain *domain, dmar_gaddr_t base, | domain_unmap_buf(struct dmar_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 dmar_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->iodom.iommu); | ||||
domain->flags |= DMAR_DOMAIN_PGTBL_INITED; | domain->flags |= IOMMU_DOMAIN_PGTBL_INITED; | ||||
DMAR_UNLOCK(domain->dmar); | IOMMU_UNLOCK(domain->iodom.iommu); | ||||
return (0); | return (0); | ||||
} | } | ||||
void | void | ||||
domain_free_pgtbl(struct dmar_domain *domain) | domain_free_pgtbl(struct dmar_domain *domain) | ||||
{ | { | ||||
struct dmar_unit *dmar; | |||||
vm_object_t obj; | vm_object_t obj; | ||||
vm_page_t m; | vm_page_t m; | ||||
dmar = (struct dmar_unit *)domain->iodom.iommu; | |||||
obj = domain->pgtbl_obj; | obj = domain->pgtbl_obj; | ||||
if (obj == NULL) { | if (obj == NULL) { | ||||
KASSERT((domain->dmar->hw_ecap & DMAR_ECAP_PT) != 0 && | KASSERT((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); | ||||
Show All 20 Lines | |||||
domain_flush_iotlb_sync(struct dmar_domain *domain, dmar_gaddr_t base, | domain_flush_iotlb_sync(struct dmar_domain *domain, dmar_gaddr_t base, | ||||
dmar_gaddr_t size) | dmar_gaddr_t size) | ||||
{ | { | ||||
struct dmar_unit *unit; | struct dmar_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 = (struct dmar_unit *)domain->iodom.iommu; | ||||
KASSERT(!unit->qi_enabled, ("dmar%d: sync iotlb flush call", | KASSERT(!unit->qi_enabled, ("dmar%d: sync iotlb flush call", | ||||
unit->unit)); | unit->iommu.unit)); | ||||
iro = DMAR_ECAP_IRO(unit->hw_ecap) * 16; | iro = DMAR_ECAP_IRO(unit->hw_ecap) * 16; | ||||
DMAR_LOCK(unit); | DMAR_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->iommu.unit, | ||||
(uintmax_t)iotlbr)); | (uintmax_t)iotlbr)); | ||||
} else { | } else { | ||||
for (; size > 0; base += isize, size -= isize) { | for (; size > 0; base += isize, size -= isize) { | ||||
am = calc_am(unit, base, size, &isize); | am = calc_am(unit, base, size, &isize); | ||||
dmar_write8(unit, iro, base | am); | dmar_write8(unit, iro, base | am); | ||||
iotlbr = domain_wait_iotlb_flush(unit, | iotlbr = domain_wait_iotlb_flush(unit, | ||||
DMAR_IOTLB_IIRG_PAGE | | DMAR_IOTLB_IIRG_PAGE | | ||||
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: PSI invalidation failed " | ("dmar%d: PSI invalidation failed " | ||||
"iotlbr 0x%jx base 0x%jx size 0x%jx am %d", | "iotlbr 0x%jx base 0x%jx size 0x%jx am %d", | ||||
unit->unit, (uintmax_t)iotlbr, | unit->iommu.unit, (uintmax_t)iotlbr, | ||||
(uintmax_t)base, (uintmax_t)size, am)); | (uintmax_t)base, (uintmax_t)size, am)); | ||||
/* | /* | ||||
* 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); | DMAR_UNLOCK(unit); | ||||
} | } |