arch/arm64/mm/pageattr.c

Source file repositories/reference/linux-study-clean/arch/arm64/mm/pageattr.c

File Facts

System
Linux kernel
Corpus path
arch/arm64/mm/pageattr.c
Extension
.c
Size
11032 bytes
Lines
426
Domain
Architecture Layer
Bucket
arch/arm64
Inferred role
Architecture Layer: implementation source
Status
source implementation candidate

Why This File Exists

CPU and platform-specific kernel glue: boot entry, traps, syscall entry, interrupts, page tables, context switch, and low-level barriers.

Dependency Surface

Detected Declarations

Annotated Snippet

struct page_change_data {
	pgprot_t set_mask;
	pgprot_t clear_mask;
};

static ptval_t set_pageattr_masks(ptval_t val, struct mm_walk *walk)
{
	struct page_change_data *masks = walk->private;

	/*
	 * Some users clear and set bits which alias each other (e.g. PTE_NG and
	 * PTE_PRESENT_INVALID). It is therefore important that we always clear
	 * first then set.
	 */
	val &= ~(pgprot_val(masks->clear_mask));
	val |= (pgprot_val(masks->set_mask));

	return val;
}

static int pageattr_pud_entry(pud_t *pud, unsigned long addr,
			      unsigned long next, struct mm_walk *walk)
{
	pud_t val = pudp_get(pud);

	if (pud_leaf(val)) {
		if (WARN_ON_ONCE((next - addr) != PUD_SIZE))
			return -EINVAL;
		val = __pud(set_pageattr_masks(pud_val(val), walk));
		set_pud(pud, val);
		walk->action = ACTION_CONTINUE;
	}

	return 0;
}

static int pageattr_pmd_entry(pmd_t *pmd, unsigned long addr,
			      unsigned long next, struct mm_walk *walk)
{
	pmd_t val = pmdp_get(pmd);

	if (pmd_leaf(val)) {
		if (WARN_ON_ONCE((next - addr) != PMD_SIZE))
			return -EINVAL;
		val = __pmd(set_pageattr_masks(pmd_val(val), walk));
		set_pmd(pmd, val);
		walk->action = ACTION_CONTINUE;
	}

	return 0;
}

static int pageattr_pte_entry(pte_t *pte, unsigned long addr,
			      unsigned long next, struct mm_walk *walk)
{
	pte_t val = __ptep_get(pte);

	val = __pte(set_pageattr_masks(pte_val(val), walk));
	__set_pte(pte, val);

	return 0;
}

static const struct mm_walk_ops pageattr_ops = {
	.pud_entry	= pageattr_pud_entry,
	.pmd_entry	= pageattr_pmd_entry,
	.pte_entry	= pageattr_pte_entry,
};

bool rodata_full __ro_after_init = true;

bool can_set_direct_map(void)
{
	/*
	 * rodata_full, DEBUG_PAGEALLOC and a Realm guest all require linear
	 * map to be mapped at page granularity, so that it is possible to
	 * protect/unprotect single pages.
	 *
	 * KFENCE pool requires page-granular mapping if initialized late.
	 *
	 * Realms need to make pages shared/protected at page granularity.
	 */
	return rodata_full || debug_pagealloc_enabled() ||
		arm64_kfence_can_set_direct_map() || is_realm_world();
}

static int update_range_prot(unsigned long start, unsigned long size,
			     pgprot_t set_mask, pgprot_t clear_mask)
{
	struct page_change_data data;

Annotation

Implementation Notes