arm64/mm: Add uffd write-protect support
authorRyan Roberts <ryan.roberts@arm.com>
Fri, 3 May 2024 14:46:02 +0000 (15:46 +0100)
committerWill Deacon <will@kernel.org>
Fri, 3 May 2024 16:03:34 +0000 (17:03 +0100)
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: Peter Xu <peterx@redhat.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Reviewed-by: David Hildenbrand <david@redhat.com>
Signed-off-by: Ryan Roberts <ryan.roberts@arm.com>
Link: https://lore.kernel.org/r/20240503144604.151095-5-ryan.roberts@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
arch/arm64/Kconfig
arch/arm64/include/asm/pgtable-prot.h
arch/arm64/include/asm/pgtable.h

index 7b11c98b3e84bf76bcd6a33b34af257a9b7f48d7..763e221f21698fee88422d03f788639f32e7557d 100644 (file)
@@ -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
index 35c9de13f7ed41134c897987eacaf35a749a921a..b11cfb9fdd379ab2eb6b9797af192d0679495bf3 100644 (file)
  */
 #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)
 
index 6c0e322211cb1e78246a179c8040a82428ea824e..78fd3e2ef8c63a51d0a4ad8c359633663f397a24 100644 (file)
@@ -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