Commit 5b32510a authored by Ryan Roberts's avatar Ryan Roberts Committed by Will Deacon
Browse files

arm64/mm: Add uffd write-protect support



Let's use the newly-free PTE SW bit (58) to add support for uffd-wp.

The standard handlers are implemented for set/test/clear for both pte
and pmd. Additionally we must also track the uffd-wp state as a pte swp
bit, so use a free swap pte bit (3).

Acked-by: default avatarPeter Xu <peterx@redhat.com>
Reviewed-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
Reviewed-by: default avatarDavid Hildenbrand <david@redhat.com>
Signed-off-by: default avatarRyan Roberts <ryan.roberts@arm.com>
Link: https://lore.kernel.org/r/20240503144604.151095-5-ryan.roberts@arm.com


Signed-off-by: default avatarWill Deacon <will@kernel.org>
parent 55564814
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -255,6 +255,7 @@ config ARM64
	select SYSCTL_EXCEPTION_TRACE
	select THREAD_INFO_IN_TASK
	select HAVE_ARCH_USERFAULTFD_MINOR if USERFAULTFD
	select HAVE_ARCH_USERFAULTFD_WP if USERFAULTFD
	select TRACE_IRQFLAGS_SUPPORT
	select TRACE_IRQFLAGS_NMI_SUPPORT
	select HAVE_SOFTIRQ_ON_OWN_STACK
+8 −0
Original line number Diff line number Diff line
@@ -26,6 +26,14 @@
 */
#define PTE_PRESENT_INVALID	(PTE_NG)		 /* only when !PTE_VALID */

#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_WP
#define PTE_UFFD_WP		(_AT(pteval_t, 1) << 58) /* uffd-wp tracking */
#define PTE_SWP_UFFD_WP		(_AT(pteval_t, 1) << 3)	 /* only for swp ptes */
#else
#define PTE_UFFD_WP		(_AT(pteval_t, 0))
#define PTE_SWP_UFFD_WP		(_AT(pteval_t, 0))
#endif /* CONFIG_HAVE_ARCH_USERFAULTFD_WP */

#define _PROT_DEFAULT		(PTE_TYPE_PAGE | PTE_AF | PTE_SHARED)
#define _PROT_SECT_DEFAULT	(PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S)

+44 −0
Original line number Diff line number Diff line
@@ -280,6 +280,23 @@ static inline pte_t pte_mkdevmap(pte_t pte)
	return set_pte_bit(pte, __pgprot(PTE_DEVMAP | PTE_SPECIAL));
}

#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_WP
static inline int pte_uffd_wp(pte_t pte)
{
	return !!(pte_val(pte) & PTE_UFFD_WP);
}

static inline pte_t pte_mkuffd_wp(pte_t pte)
{
	return pte_wrprotect(set_pte_bit(pte, __pgprot(PTE_UFFD_WP)));
}

static inline pte_t pte_clear_uffd_wp(pte_t pte)
{
	return clear_pte_bit(pte, __pgprot(PTE_UFFD_WP));
}
#endif /* CONFIG_HAVE_ARCH_USERFAULTFD_WP */

static inline void __set_pte_nosync(pte_t *ptep, pte_t pte)
{
	WRITE_ONCE(*ptep, pte);
@@ -477,6 +494,23 @@ static inline pte_t pte_swp_clear_exclusive(pte_t pte)
	return clear_pte_bit(pte, __pgprot(PTE_SWP_EXCLUSIVE));
}

#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_WP
static inline pte_t pte_swp_mkuffd_wp(pte_t pte)
{
	return set_pte_bit(pte, __pgprot(PTE_SWP_UFFD_WP));
}

static inline int pte_swp_uffd_wp(pte_t pte)
{
	return !!(pte_val(pte) & PTE_SWP_UFFD_WP);
}

static inline pte_t pte_swp_clear_uffd_wp(pte_t pte)
{
	return clear_pte_bit(pte, __pgprot(PTE_SWP_UFFD_WP));
}
#endif /* CONFIG_HAVE_ARCH_USERFAULTFD_WP */

#ifdef CONFIG_NUMA_BALANCING
/*
 * See the comment in include/linux/pgtable.h
@@ -527,6 +561,15 @@ static inline int pmd_trans_huge(pmd_t pmd)
#define pmd_mkdirty(pmd)	pte_pmd(pte_mkdirty(pmd_pte(pmd)))
#define pmd_mkyoung(pmd)	pte_pmd(pte_mkyoung(pmd_pte(pmd)))
#define pmd_mkinvalid(pmd)	pte_pmd(pte_mkinvalid(pmd_pte(pmd)))
#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_WP
#define pmd_uffd_wp(pmd)	pte_uffd_wp(pmd_pte(pmd))
#define pmd_mkuffd_wp(pmd)	pte_pmd(pte_mkuffd_wp(pmd_pte(pmd)))
#define pmd_clear_uffd_wp(pmd)	pte_pmd(pte_clear_uffd_wp(pmd_pte(pmd)))
#define pmd_swp_uffd_wp(pmd)	pte_swp_uffd_wp(pmd_pte(pmd))
#define pmd_swp_mkuffd_wp(pmd)	pte_pmd(pte_swp_mkuffd_wp(pmd_pte(pmd)))
#define pmd_swp_clear_uffd_wp(pmd) \
				pte_pmd(pte_swp_clear_uffd_wp(pmd_pte(pmd)))
#endif /* CONFIG_HAVE_ARCH_USERFAULTFD_WP */

#define pmd_thp_or_huge(pmd)	(pmd_huge(pmd) || pmd_trans_huge(pmd))

@@ -1261,6 +1304,7 @@ static inline pmd_t pmdp_establish(struct vm_area_struct *vma,
 * Encode and decode a swap entry:
 *	bits 0-1:	present (must be zero)
 *	bits 2:		remember PG_anon_exclusive
 *	bit  3:		remember uffd-wp state
 *	bits 6-10:	swap type
 *	bit  11:	PTE_PRESENT_INVALID (must be zero)
 *	bits 12-61:	swap offset