diff options
| author | Andrew Turner <andrew@FreeBSD.org> | 2026-04-13 11:50:47 +0000 |
|---|---|---|
| committer | Andrew Turner <andrew@FreeBSD.org> | 2026-04-13 14:23:05 +0000 |
| commit | 5809584275363d6b2f44981d8561a126a1344360 (patch) | |
| tree | 92700aa007c557268b2ed54cd3b5753db7fed930 | |
| parent | c208439cdb588d91aead9403cec3d4acf4a8bebf (diff) | |
arm64: Handle changing self-referential DMAP pages
Support changing the property of a DMAP page that holds it's own page
table entry.
Because we need to perform a break-before-make sequence to change the
properties of pages a page that also holds it's own page table entry
will fault in the make part of the sequence.
Handle this by mapping the page with a temporary mapping as we already
do when demoting a superpage.
Reviewed by: kib
Sponsored by: Arm Ltd
Differential Revision: https://reviews.freebsd.org/D55943
| -rw-r--r-- | sys/arm64/arm64/pmap.c | 26 |
1 files changed, 26 insertions, 0 deletions
diff --git a/sys/arm64/arm64/pmap.c b/sys/arm64/arm64/pmap.c index 7d19c927d369..a23905994846 100644 --- a/sys/arm64/arm64/pmap.c +++ b/sys/arm64/arm64/pmap.c @@ -8248,6 +8248,7 @@ pmap_change_props_locked(vm_offset_t va, vm_size_t size, vm_prot_t prot, vm_paddr_t pa; pt_entry_t pte, *ptep, *newpte; pt_entry_t bits, mask; + char *tmpptep; int lvl, rv; PMAP_LOCK_ASSERT(kernel_pmap, MA_OWNED); @@ -8377,6 +8378,24 @@ pmap_change_props_locked(vm_offset_t va, vm_size_t size, vm_prot_t prot, break; } + tmpptep = 0; + if (tmpva <= (vm_offset_t)ptep && + tmpva + pte_size > (vm_offset_t)ptep) { + vm_paddr_t pte_pa; + + mtx_lock(&cmap_lock); + tmpptep = cmap1_addr; + pte_pa = DMAP_TO_PHYS((vm_offset_t)ptep); + pmap_store(cmap1_pte, ATTR_AF | + pmap_sh_attr | ATTR_S1_AP(ATTR_S1_AP_RW) | + ATTR_S1_XN | ATTR_KERN_GP | + ATTR_S1_IDX(VM_MEMATTR_WRITE_BACK) | + PHYS_TO_PTE(pte_pa &~L3_OFFSET) | L3_PAGE); + dsb(ishst); + ptep = (pt_entry_t *)(tmpptep + + ((vm_offset_t)ptep & PAGE_MASK)); + } + /* Update the entry */ pte = pmap_load(ptep); pte &= ~mask; @@ -8403,6 +8422,13 @@ pmap_change_props_locked(vm_offset_t va, vm_size_t size, vm_prot_t prot, break; } + if (tmpptep != 0) { + pmap_clear(cmap1_pte); + pmap_s1_invalidate_page(kernel_pmap, + (vm_offset_t)tmpptep, true); + mtx_unlock(&cmap_lock); + } + pa = PTE_TO_PHYS(pte); if (!VIRT_IN_DMAP(tmpva) && PHYS_IN_DMAP(pa)) { /* |
