Skip to content

Commit

Permalink
arm64 pmap: Add ATTR_CONTIGUOUS support [Part 2]
Browse files Browse the repository at this point in the history
Create ATTR_CONTIGUOUS mappings in pmap_enter_object().  As a result,
when the base page size is 4 KB, the read-only data and text sections
of large (2 MB+) executables, e.g., clang, can be mapped using 64 KB
pages.  Similarly, when the base page size is 16 KB, the read-only
data section of large executables can be mapped using 2 MB pages.

Rename pmap_enter_2mpage().  Given that we have grown support for 16 KB
base pages, we should no longer include page sizes that may vary, e.g.,
2mpage, in pmap function names.  Requested by: andrew

Co-authored-by: Eliot Solomon <ehs3@rice.edu>
Differential Revision:	https://reviews.freebsd.org/D44575
  • Loading branch information
alcriceedu committed Apr 9, 2024
1 parent e205fd3 commit 841cf52
Showing 1 changed file with 245 additions and 7 deletions.
252 changes: 245 additions & 7 deletions sys/arm64/arm64/pmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,8 @@ static vm_page_t pmap_enter_quick_locked(pmap_t pmap, vm_offset_t va,
vm_page_t m, vm_prot_t prot, vm_page_t mpte, struct rwlock **lockp);
static int pmap_enter_l2(pmap_t pmap, vm_offset_t va, pd_entry_t new_l2,
u_int flags, vm_page_t m, struct rwlock **lockp);
static int pmap_enter_l3c(pmap_t pmap, vm_offset_t va, pt_entry_t l3e, u_int flags,
vm_page_t m, vm_page_t *ml3p, struct rwlock **lockp);
static bool pmap_every_pte_zero(vm_paddr_t pa);
static int pmap_insert_pt_page(pmap_t pmap, vm_page_t mpte, bool promoted,
bool all_l3e_AF_set);
Expand Down Expand Up @@ -5177,13 +5179,13 @@ pmap_enter(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot,
}

/*
* Tries to create a read- and/or execute-only 2MB page mapping. Returns
* Tries to create a read- and/or execute-only L2 page mapping. Returns
* KERN_SUCCESS if the mapping was created. Otherwise, returns an error
* value. See pmap_enter_l2() for the possible error values when "no sleep",
* "no replace", and "no reclaim" are specified.
*/
static int
pmap_enter_2mpage(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot,
pmap_enter_l2_rx(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot,
struct rwlock **lockp)
{
pd_entry_t new_l2;
Expand Down Expand Up @@ -5233,13 +5235,13 @@ pmap_every_pte_zero(vm_paddr_t pa)
}

/*
* Tries to create the specified 2MB page mapping. Returns KERN_SUCCESS if
* Tries to create the specified L2 page mapping. Returns KERN_SUCCESS if
* the mapping was created, and one of KERN_FAILURE, KERN_NO_SPACE, or
* KERN_RESOURCE_SHORTAGE otherwise. Returns KERN_FAILURE if
* PMAP_ENTER_NOREPLACE was specified and a 4KB page mapping already exists
* within the 2MB virtual address range starting at the specified virtual
* PMAP_ENTER_NOREPLACE was specified and a base page mapping already exists
* within the L2 virtual address range starting at the specified virtual
* address. Returns KERN_NO_SPACE if PMAP_ENTER_NOREPLACE was specified and a
* 2MB page mapping already exists at the specified virtual address. Returns
* L2 page mapping already exists at the specified virtual address. Returns
* KERN_RESOURCE_SHORTAGE if either (1) PMAP_ENTER_NOSLEEP was specified and a
* page table page allocation failed or (2) PMAP_ENTER_NORECLAIM was specified
* and a PV entry allocation failed.
Expand Down Expand Up @@ -5405,6 +5407,235 @@ pmap_enter_l2(pmap_t pmap, vm_offset_t va, pd_entry_t new_l2, u_int flags,
return (KERN_SUCCESS);
}

/*
* Tries to create a read- and/or execute-only L3C page mapping. Returns
* KERN_SUCCESS if the mapping was created. Otherwise, returns an error
* value.
*/
static int
pmap_enter_l3c_rx(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_page_t *ml3p,
vm_prot_t prot, struct rwlock **lockp)
{
pt_entry_t l3e;

PMAP_LOCK_ASSERT(pmap, MA_OWNED);
PMAP_ASSERT_STAGE1(pmap);
KASSERT(ADDR_IS_CANONICAL(va),
("%s: Address not in canonical form: %lx", __func__, va));

l3e = PHYS_TO_PTE(VM_PAGE_TO_PHYS(m)) | ATTR_DEFAULT |
ATTR_S1_IDX(m->md.pv_memattr) | ATTR_S1_AP(ATTR_S1_AP_RO) |
ATTR_CONTIGUOUS | L3_PAGE;
l3e |= pmap_pte_bti(pmap, va);
if ((m->oflags & VPO_UNMANAGED) == 0) {
l3e |= ATTR_SW_MANAGED;
l3e &= ~ATTR_AF;
}
if ((prot & VM_PROT_EXECUTE) == 0 ||
m->md.pv_memattr == VM_MEMATTR_DEVICE)
l3e |= ATTR_S1_XN;
if (!ADDR_IS_KERNEL(va))
l3e |= ATTR_S1_AP(ATTR_S1_AP_USER) | ATTR_S1_PXN;
else
l3e |= ATTR_S1_UXN;
if (pmap != kernel_pmap)
l3e |= ATTR_S1_nG;
return (pmap_enter_l3c(pmap, va, l3e, PMAP_ENTER_NOSLEEP |
PMAP_ENTER_NOREPLACE | PMAP_ENTER_NORECLAIM, m, ml3p, lockp));
}

static int
pmap_enter_l3c(pmap_t pmap, vm_offset_t va, pt_entry_t l3e, u_int flags,
vm_page_t m, vm_page_t *ml3p, struct rwlock **lockp)
{
pd_entry_t *l2p, *pde;
pt_entry_t *l3p, *tl3p;
vm_page_t mt;
vm_paddr_t pa;
vm_pindex_t l2pindex;
int lvl;

PMAP_LOCK_ASSERT(pmap, MA_OWNED);
KASSERT((va & L3C_OFFSET) == 0,
("pmap_enter_l3c: va is not aligned"));
KASSERT(!VA_IS_CLEANMAP(va) || (l3e & ATTR_SW_MANAGED) == 0,
("pmap_enter_l3c: managed mapping within the clean submap"));

/*
* If the L3 PTP is not resident, we attempt to create it here.
*/
if (!ADDR_IS_KERNEL(va)) {
/*
* Were we given the correct L3 PTP? If so, we can simply
* increment its ref count.
*/
l2pindex = pmap_l2_pindex(va);
if (*ml3p != NULL && (*ml3p)->pindex == l2pindex) {
(*ml3p)->ref_count += L3C_ENTRIES;
} else {
retry:
/*
* Get the L2 entry.
*/
pde = pmap_pde(pmap, va, &lvl);

/*
* If the L2 entry is a superpage, we either abort or
* demote depending on the given flags.
*/
if (lvl == 1) {
l2p = pmap_l1_to_l2(pde, va);
if ((pmap_load(l2p) & ATTR_DESCR_MASK) ==
L2_BLOCK) {
if ((flags & PMAP_ENTER_NOREPLACE) != 0)
return (KERN_FAILURE);
l3p = pmap_demote_l2_locked(pmap, l2p,
va, lockp);
if (l3p != NULL) {
*ml3p = PHYS_TO_VM_PAGE(
PTE_TO_PHYS(pmap_load(
l2p)));
(*ml3p)->ref_count +=
L3C_ENTRIES;
goto have_l3p;
}
}
/* We need to allocate an L3 PTP. */
}

/*
* If the L3 PTP is mapped, we just increment its ref
* count. Otherwise, we attempt to allocate it.
*/
if (lvl == 2 && pmap_load(pde) != 0) {
*ml3p = PHYS_TO_VM_PAGE(PTE_TO_PHYS(
pmap_load(pde)));
(*ml3p)->ref_count += L3C_ENTRIES;
} else {
*ml3p = _pmap_alloc_l3(pmap, l2pindex, (flags &
PMAP_ENTER_NOSLEEP) != 0 ? NULL : lockp);
if (*ml3p == NULL) {
if ((flags & PMAP_ENTER_NOSLEEP) != 0)
return (KERN_FAILURE);

/*
* The page table may have changed
* while we slept.
*/
goto retry;
}
(*ml3p)->ref_count += L3C_ENTRIES - 1;
}
}
l3p = (pt_entry_t *)PHYS_TO_DMAP(VM_PAGE_TO_PHYS(*ml3p));
} else {
*ml3p = NULL;

/*
* If the L2 entry is a superpage, we either abort or demote
* depending on the given flags.
*/
pde = pmap_pde(kernel_pmap, va, &lvl);
if (lvl == 1) {
l2p = pmap_l1_to_l2(pde, va);
KASSERT((pmap_load(l2p) & ATTR_DESCR_MASK) == L2_BLOCK,
("pmap_enter_l3c: missing L2 block"));
if ((flags & PMAP_ENTER_NOREPLACE) != 0)
return (KERN_FAILURE);
l3p = pmap_demote_l2_locked(pmap, l2p, va, lockp);
} else {
KASSERT(lvl == 2,
("pmap_enter_l3c: Invalid level %d", lvl));
l3p = (pt_entry_t *)PHYS_TO_DMAP(PTE_TO_PHYS(
pmap_load(pde)));
}
}
have_l3p:
l3p = &l3p[pmap_l3_index(va)];

/*
* If bti is not the same for the whole L3C range, return failure
* and let vm_fault() cope. Check after L3 allocation, since
* it could sleep.
*/
if (!pmap_bti_same(pmap, va, va + L3C_SIZE)) {
KASSERT(*ml3p != NULL, ("pmap_enter_l3c: missing L3 PTP"));
(*ml3p)->ref_count -= L3C_ENTRIES - 1;
pmap_abort_ptp(pmap, va, *ml3p);
*ml3p = NULL;
return (KERN_PROTECTION_FAILURE);
}

/*
* If there are existing mappings, either abort or remove them.
*/
if ((flags & PMAP_ENTER_NOREPLACE) != 0) {
for (tl3p = l3p; tl3p < &l3p[L3C_ENTRIES]; tl3p++) {
if (pmap_load(tl3p) != 0) {
if (*ml3p != NULL)
(*ml3p)->ref_count -= L3C_ENTRIES;
return (KERN_FAILURE);
}
}
} else {
/*
* Because we increment the L3 page's reference count above,
* it is guaranteed not to be freed here and we can pass NULL
* instead of a valid free list.
*/
pmap_remove_l3_range(pmap, pmap_load(pmap_l2(pmap, va)), va,
va + L3C_SIZE, NULL, lockp);
}

/*
* Enter on the PV list if part of our managed memory.
*/
if ((l3e & ATTR_SW_MANAGED) != 0) {
if (!pmap_pv_insert_l3c(pmap, va, m, lockp)) {
if (*ml3p != NULL) {
(*ml3p)->ref_count -= L3C_ENTRIES - 1;
pmap_abort_ptp(pmap, va, *ml3p);
*ml3p = NULL;
}
return (KERN_RESOURCE_SHORTAGE);
}
if ((l3e & ATTR_SW_DBM) != 0)
for (mt = m; mt < &m[L3C_ENTRIES]; mt++)
vm_page_aflag_set(mt, PGA_WRITEABLE);
}

/*
* Increment counters.
*/
if ((l3e & ATTR_SW_WIRED) != 0)
pmap->pm_stats.wired_count += L3C_ENTRIES;
pmap_resident_count_inc(pmap, L3C_ENTRIES);

pa = VM_PAGE_TO_PHYS(m);
KASSERT((pa & L3C_OFFSET) == 0, ("pmap_enter_l3c: pa is not aligned"));

/*
* Sync the icache before the mapping is stored.
*/
if ((l3e & ATTR_S1_XN) == 0 && pmap != kernel_pmap &&
m->md.pv_memattr == VM_MEMATTR_WRITE_BACK)
cpu_icache_sync_range((void *)PHYS_TO_DMAP(pa), L3C_SIZE);

/*
* Map the superpage.
*/
for (tl3p = l3p; tl3p < &l3p[L3C_ENTRIES]; tl3p++) {
pmap_store(tl3p, l3e);
l3e += L3_SIZE;
}
dsb(ishst);

atomic_add_long(&pmap_l3c_mappings, 1);
CTR2(KTR_PMAP, "pmap_enter_l3c: success for va %#lx in pmap %p",
va, pmap);
return (KERN_SUCCESS);
}

/*
* Maps a sequence of resident pages belonging to the same object.
* The sequence begins with the given page m_start. This page is
Expand Down Expand Up @@ -5438,9 +5669,16 @@ pmap_enter_object(pmap_t pmap, vm_offset_t start, vm_offset_t end,
va = start + ptoa(diff);
if ((va & L2_OFFSET) == 0 && va + L2_SIZE <= end &&
m->psind == 1 && pmap_ps_enabled(pmap) &&
((rv = pmap_enter_2mpage(pmap, va, m, prot, &lock)) ==
((rv = pmap_enter_l2_rx(pmap, va, m, prot, &lock)) ==
KERN_SUCCESS || rv == KERN_NO_SPACE))
m = &m[L2_SIZE / PAGE_SIZE - 1];
else if ((va & L3C_OFFSET) == 0 && va + L3C_SIZE <= end &&
(VM_PAGE_TO_PHYS(m) & L3C_OFFSET) == 0 &&
vm_reserv_is_populated(m, L3C_ENTRIES) &&
pmap_ps_enabled(pmap) &&
((rv = pmap_enter_l3c_rx(pmap, va, m, &mpte, prot,
&lock)) == KERN_SUCCESS || rv == KERN_NO_SPACE))
m = &m[L3C_ENTRIES - 1];
else
mpte = pmap_enter_quick_locked(pmap, va, m, prot, mpte,
&lock);
Expand Down

0 comments on commit 841cf52

Please sign in to comment.