* [PATCH 0/6] arm64: distinguish HW PTE pointers from SW PTE value pointers
@ 2026-09-14 13:51 Muhammad Usama Anjum
2026-09-14 13:51 ` [PATCH 1/6] arm64: use hw_pte_t for HW PTE pointers Muhammad Usama Anjum
` (5 more replies)
0 siblings, 6 replies; 11+ messages in thread
From: Muhammad Usama Anjum @ 2026-09-14 13:51 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Mark Rutland, Ard Biesheuvel,
Ilias Apalodimas, Andrey Ryabinin, Alexander Potapenko,
Andrey Konovalov, Dmitry Vyukov, Vincenzo Frascino,
Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko
Cc: linux-arm-kernel, linux-kernel, linux-efi, kasan-dev, linux-mm,
Muhammad Usama Anjum
Hi,
The generic series introduces hw_pte_t for HW PTEs, while SW PTE values
use pte_t. This series enables the distinct type on arm64 so the
compiler can distinguish HW PTE pointers (hw_pte_t *) from SW PTE value
pointers (pte_t *).
The conversion updates HW PTE pointers and accessors, including atomic
updates, fixmap, and early page-table construction, before selecting
ARCH_HAS_HW_PTE_T. Higher-level entry types remain out of scope, and
shared helpers retain explicit casts.
The linked generic series is a prerequisite. Its cover contains the
Coccinelle script used for most pointer conversions; the remaining sites
were converted by hand.
Link: https://lore.kernel.org/all/20260903103002.1091859-1-usama.anjum@arm.com/
Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
---
Muhammad Usama Anjum (6):
arm64: use hw_pte_t for HW PTE pointers
arm64: use hw_pte_val for HW PTE atomics
arm64: convert between HW PTEs and SW PTE values
arm64: use hw_pte_t for fixmap HW PTEs
arm64: use HW PTE accessors in early map_range()
arm64: enable a distinct type for HW PTEs
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/hugetlb.h | 19 ++---
arch/arm64/include/asm/pgalloc.h | 2 +-
arch/arm64/include/asm/pgtable.h | 157 ++++++++++++++++++++------------------
arch/arm64/include/asm/vmalloc.h | 2 +-
arch/arm64/kernel/efi.c | 3 +-
arch/arm64/kernel/pi/map_kernel.c | 4 +-
arch/arm64/kernel/pi/map_range.c | 20 +++--
arch/arm64/kernel/pi/pi.h | 3 +-
arch/arm64/mm/contpte.c | 49 ++++++------
arch/arm64/mm/fault.c | 11 +--
arch/arm64/mm/fixmap.c | 8 +-
arch/arm64/mm/hugetlbpage.c | 48 ++++++------
arch/arm64/mm/kasan_init.c | 4 +-
arch/arm64/mm/mmu.c | 46 ++++++-----
arch/arm64/mm/pageattr.c | 4 +-
arch/arm64/mm/trans_pgd.c | 4 +-
include/linux/pgtable_types.h | 6 ++
18 files changed, 215 insertions(+), 176 deletions(-)
---
base-commit: ead700ca770c82167af32622cf8b68c9f87c3c7c
change-id: 20260911-pte0_arm-f1b7df7c0b51
prerequisite-message-id: 20260903103002.1091859-1-usama.anjum@arm.com
prerequisite-patch-id: 0114fcc6ab0dbb739b5bb6fc0fe6b1c8f1fa37a1
prerequisite-patch-id: 09fcf65970074620dd3a9cce6850b901beb01db8
prerequisite-patch-id: 9b2fdab97c99effabb85526fa7f141c933b3aa23
prerequisite-patch-id: 1aa309a26d98a1e93a54742705a03567ee93be71
prerequisite-patch-id: ebe32f52c95ad0542982f0b37798d209595e25e1
prerequisite-patch-id: a60d249f52d092524176851b2570d7245bb24434
prerequisite-patch-id: 81ebd1378e25d40a1771b53973abcc4defa87535
prerequisite-patch-id: d982f2b00c5eddb2459f69008a73e4f4ae4d8299
Best regards,
--
Usama
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/6] arm64: use hw_pte_t for HW PTE pointers
2026-09-14 13:51 [PATCH 0/6] arm64: distinguish HW PTE pointers from SW PTE value pointers Muhammad Usama Anjum
@ 2026-09-14 13:51 ` Muhammad Usama Anjum
2026-09-14 13:51 ` [PATCH 2/6] arm64: use hw_pte_val for HW PTE atomics Muhammad Usama Anjum
` (4 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Muhammad Usama Anjum @ 2026-09-14 13:51 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Mark Rutland, Ard Biesheuvel,
Ilias Apalodimas, Andrey Ryabinin, Alexander Potapenko,
Andrey Konovalov, Dmitry Vyukov, Vincenzo Frascino,
Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko
Cc: linux-arm-kernel, linux-kernel, linux-efi, kasan-dev, linux-mm,
Muhammad Usama Anjum
With pte_t * used for HW PTE pointers, the compiler cannot distinguish
them from SW PTE value pointers. The generic HW PTE interfaces already
use hw_pte_t *, so arm64 HW PTE pointers must use that type before
ARCH_HAS_HW_PTE_T can make it distinct.
Convert HW PTE pointers to hw_pte_t *, retaining pte_t for SW PTE
values. Helpers shared with PMD and PUD operations keep explicit casts
because this series does not introduce distinct entry types for those
levels.
arm64 still uses the hw_pte_t alias in this patch. The new pointer
spelling therefore denotes the same type until the final opt-in.
Include linux/pgtable_types.h in pi.h to make hw_pte_t visible.
Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
---
arch/arm64/include/asm/hugetlb.h | 19 ++---
arch/arm64/include/asm/pgalloc.h | 2 +-
arch/arm64/include/asm/pgtable.h | 143 ++++++++++++++++++++------------------
arch/arm64/include/asm/vmalloc.h | 2 +-
arch/arm64/kernel/efi.c | 3 +-
arch/arm64/kernel/pi/map_kernel.c | 4 +-
arch/arm64/kernel/pi/map_range.c | 9 +--
arch/arm64/kernel/pi/pi.h | 3 +-
arch/arm64/mm/contpte.c | 49 +++++++------
arch/arm64/mm/fault.c | 5 +-
arch/arm64/mm/fixmap.c | 6 +-
arch/arm64/mm/hugetlbpage.c | 48 +++++++------
arch/arm64/mm/kasan_init.c | 4 +-
arch/arm64/mm/mmu.c | 46 +++++++-----
arch/arm64/mm/pageattr.c | 4 +-
arch/arm64/mm/trans_pgd.c | 4 +-
16 files changed, 190 insertions(+), 161 deletions(-)
diff --git a/arch/arm64/include/asm/hugetlb.h b/arch/arm64/include/asm/hugetlb.h
index d038ff14d16ca..15b3cf53aa2ca 100644
--- a/arch/arm64/include/asm/hugetlb.h
+++ b/arch/arm64/include/asm/hugetlb.h
@@ -36,33 +36,34 @@ pte_t arch_make_huge_pte(pte_t entry, unsigned int shift, vm_flags_t flags);
#define arch_make_huge_pte arch_make_huge_pte
#define __HAVE_ARCH_HUGE_SET_HUGE_PTE_AT
extern void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, pte_t pte, unsigned long sz);
+ hw_pte_t *ptep, pte_t pte, unsigned long sz);
#define __HAVE_ARCH_HUGE_PTEP_SET_ACCESS_FLAGS
extern int huge_ptep_set_access_flags(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep,
+ unsigned long addr, hw_pte_t *ptep,
pte_t pte, int dirty);
#define __HAVE_ARCH_HUGE_PTEP_GET_AND_CLEAR
extern pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, unsigned long sz);
+ hw_pte_t *ptep, unsigned long sz);
#define __HAVE_ARCH_HUGE_PTEP_SET_WRPROTECT
extern void huge_ptep_set_wrprotect(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep);
+ unsigned long addr, hw_pte_t *ptep);
#define __HAVE_ARCH_HUGE_PTEP_CLEAR_FLUSH
extern pte_t huge_ptep_clear_flush(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep);
+ unsigned long addr, hw_pte_t *ptep);
#define __HAVE_ARCH_HUGE_PTE_CLEAR
extern void huge_pte_clear(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, unsigned long sz);
+ hw_pte_t *ptep, unsigned long sz);
#define __HAVE_ARCH_HUGE_PTEP_GET
-extern pte_t huge_ptep_get(struct mm_struct *mm, unsigned long addr, pte_t *ptep);
+extern pte_t huge_ptep_get(struct mm_struct *mm, unsigned long addr,
+ hw_pte_t *ptep);
#define huge_ptep_modify_prot_start huge_ptep_modify_prot_start
extern pte_t huge_ptep_modify_prot_start(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep);
+ unsigned long addr, hw_pte_t *ptep);
#define huge_ptep_modify_prot_commit huge_ptep_modify_prot_commit
extern void huge_ptep_modify_prot_commit(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep,
+ unsigned long addr, hw_pte_t *ptep,
pte_t old_pte, pte_t new_pte);
#include <asm-generic/hugetlb.h>
diff --git a/arch/arm64/include/asm/pgalloc.h b/arch/arm64/include/asm/pgalloc.h
index 1b4509d3382c6..80063dd4d2722 100644
--- a/arch/arm64/include/asm/pgalloc.h
+++ b/arch/arm64/include/asm/pgalloc.h
@@ -106,7 +106,7 @@ static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t ptep,
* of the mm address space.
*/
static inline void
-pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmdp, pte_t *ptep)
+pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmdp, hw_pte_t *ptep)
{
VM_BUG_ON(mm && mm != &init_mm);
__pmd_populate(pmdp, __pa(ptep),
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index e89ec5f4787b4..652ce413be389 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -358,7 +358,7 @@ static inline pte_t pte_clear_uffd(pte_t pte)
}
#endif /* CONFIG_HAVE_ARCH_USERFAULTFD_WP */
-static inline void __set_pte_nosync(pte_t *ptep, pte_t pte)
+static inline void __set_pte_nosync(hw_pte_t *ptep, pte_t pte)
{
WRITE_ONCE(*ptep, pte);
}
@@ -373,13 +373,13 @@ static inline void __set_pte_complete(pte_t pte)
queue_pte_barriers();
}
-static inline void __set_pte(pte_t *ptep, pte_t pte)
+static inline void __set_pte(hw_pte_t *ptep, pte_t pte)
{
__set_pte_nosync(ptep, pte);
__set_pte_complete(pte);
}
-static inline pte_t __ptep_get(pte_t *ptep)
+static inline pte_t __ptep_get(hw_pte_t *ptep)
{
return READ_ONCE(*ptep);
}
@@ -403,7 +403,8 @@ bool pgattr_change_is_safe(pteval_t old, pteval_t new);
* PTE_DIRTY || (PTE_WRITE && !PTE_RDONLY)
*/
-static inline void __check_safe_pte_update(struct mm_struct *mm, pte_t *ptep,
+static inline void __check_safe_pte_update(struct mm_struct *mm,
+ hw_pte_t *ptep,
pte_t pte)
{
pte_t old_pte;
@@ -669,7 +670,8 @@ static inline pgprot_t pud_pgprot(pud_t pud)
}
static inline void __set_ptes_anysz(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, pte_t pte, unsigned int nr,
+ hw_pte_t *ptep, pte_t pte,
+ unsigned int nr,
unsigned long pgsize)
{
unsigned long stride = pgsize >> PAGE_SHIFT;
@@ -707,7 +709,7 @@ static inline void __set_ptes_anysz(struct mm_struct *mm, unsigned long addr,
}
static inline void __set_ptes(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, pte_t pte, unsigned int nr)
+ hw_pte_t *ptep, pte_t pte, unsigned int nr)
{
__set_ptes_anysz(mm, addr, ptep, pte, nr, PAGE_SIZE);
}
@@ -715,14 +717,16 @@ static inline void __set_ptes(struct mm_struct *mm, unsigned long addr,
static inline void __set_pmds(struct mm_struct *mm, unsigned long addr,
pmd_t *pmdp, pmd_t pmd, unsigned int nr)
{
- __set_ptes_anysz(mm, addr, (pte_t *)pmdp, pmd_pte(pmd), nr, PMD_SIZE);
+ __set_ptes_anysz(mm, addr, (hw_pte_t *)pmdp, pmd_pte(pmd), nr,
+ PMD_SIZE);
}
#define set_pmd_at(mm, addr, pmdp, pmd) __set_pmds(mm, addr, pmdp, pmd, 1)
static inline void __set_puds(struct mm_struct *mm, unsigned long addr,
pud_t *pudp, pud_t pud, unsigned int nr)
{
- __set_ptes_anysz(mm, addr, (pte_t *)pudp, pud_pte(pud), nr, PUD_SIZE);
+ __set_ptes_anysz(mm, addr, (hw_pte_t *)pudp, pud_pte(pud), nr,
+ PUD_SIZE);
}
#define set_pud_at(mm, addr, pudp, pud) __set_puds(mm, addr, pudp, pud, 1)
@@ -850,16 +854,16 @@ static inline unsigned long pmd_page_vaddr(pmd_t pmd)
}
/* Find an entry in the third-level page table. */
-#define pte_offset_phys(dir,addr) (pmd_page_paddr(READ_ONCE(*(dir))) + pte_index(addr) * sizeof(pte_t))
+#define pte_offset_phys(dir, addr) (pmd_page_paddr(READ_ONCE(*(dir))) + pte_index(addr) * sizeof(hw_pte_t))
-#define pte_set_fixmap(addr) ((pte_t *)set_fixmap_offset(FIX_PTE, addr))
+#define pte_set_fixmap(addr) ((hw_pte_t *)set_fixmap_offset(FIX_PTE, addr))
#define pte_set_fixmap_offset(pmd, addr) pte_set_fixmap(pte_offset_phys(pmd, addr))
#define pte_clear_fixmap() clear_fixmap(FIX_PTE)
#define pmd_page(pmd) phys_to_page(__pmd_to_phys(pmd))
/* use ONLY for statically allocated translation tables */
-#define pte_offset_kimg(dir,addr) ((pte_t *)__phys_to_kimg(pte_offset_phys((dir), (addr))))
+#define pte_offset_kimg(dir, addr) ((hw_pte_t *)__phys_to_kimg(pte_offset_phys((dir), (addr))))
#if CONFIG_PGTABLE_LEVELS > 2
@@ -1238,12 +1242,14 @@ static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot)
}
extern int __ptep_set_access_flags_anysz(struct vm_area_struct *vma,
- unsigned long address, pte_t *ptep,
+ unsigned long address,
+ hw_pte_t *ptep,
pte_t entry, int dirty,
unsigned long pgsize);
static inline int __ptep_set_access_flags(struct vm_area_struct *vma,
- unsigned long address, pte_t *ptep,
+ unsigned long address,
+ hw_pte_t *ptep,
pte_t entry, int dirty)
{
return __ptep_set_access_flags_anysz(vma, address, ptep, entry, dirty,
@@ -1256,7 +1262,7 @@ static inline int pmdp_set_access_flags(struct vm_area_struct *vma,
unsigned long address, pmd_t *pmdp,
pmd_t entry, int dirty)
{
- return __ptep_set_access_flags_anysz(vma, address, (pte_t *)pmdp,
+ return __ptep_set_access_flags_anysz(vma, address, (hw_pte_t *)pmdp,
pmd_pte(entry), dirty, PMD_SIZE);
}
#endif
@@ -1283,13 +1289,13 @@ static inline bool pud_user_accessible_page(struct mm_struct *mm, unsigned long
*/
static inline void __pte_clear(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep)
+ unsigned long addr, hw_pte_t *ptep)
{
__set_pte(ptep, __pte(0));
}
static inline bool __ptep_test_and_clear_young(struct vm_area_struct *vma,
- unsigned long address, pte_t *ptep)
+ unsigned long address, hw_pte_t *ptep)
{
pte_t old_pte, pte;
@@ -1305,7 +1311,7 @@ static inline bool __ptep_test_and_clear_young(struct vm_area_struct *vma,
}
static inline bool __ptep_clear_flush_young(struct vm_area_struct *vma,
- unsigned long address, pte_t *ptep)
+ unsigned long address, hw_pte_t *ptep)
{
bool young = __ptep_test_and_clear_young(vma, address, ptep);
@@ -1331,13 +1337,13 @@ static inline bool pmdp_test_and_clear_young(struct vm_area_struct *vma,
{
/* Operation applies to PMD table entry only if FEAT_HAFT is enabled */
VM_WARN_ON(pmd_table(READ_ONCE(*pmdp)) && !system_supports_haft());
- return __ptep_test_and_clear_young(vma, address, (pte_t *)pmdp);
+ return __ptep_test_and_clear_young(vma, address, (hw_pte_t *)pmdp);
}
#endif /* CONFIG_TRANSPARENT_HUGEPAGE || CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG */
static inline pte_t __ptep_get_and_clear_anysz(struct mm_struct *mm,
unsigned long address,
- pte_t *ptep,
+ hw_pte_t *ptep,
unsigned long pgsize)
{
pte_t pte = __pte(xchg_relaxed(&pte_val(*ptep), 0));
@@ -1362,13 +1368,13 @@ static inline pte_t __ptep_get_and_clear_anysz(struct mm_struct *mm,
}
static inline pte_t __ptep_get_and_clear(struct mm_struct *mm,
- unsigned long address, pte_t *ptep)
+ unsigned long address, hw_pte_t *ptep)
{
return __ptep_get_and_clear_anysz(mm, address, ptep, PAGE_SIZE);
}
static inline void __clear_full_ptes(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, unsigned int nr, int full)
+ hw_pte_t *ptep, unsigned int nr, int full)
{
for (;;) {
__ptep_get_and_clear(mm, addr, ptep);
@@ -1380,7 +1386,7 @@ static inline void __clear_full_ptes(struct mm_struct *mm, unsigned long addr,
}
static inline pte_t __get_and_clear_full_ptes(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep,
+ unsigned long addr, hw_pte_t *ptep,
unsigned int nr, int full)
{
pte_t pte, tmp_pte;
@@ -1403,12 +1409,12 @@ static inline pte_t __get_and_clear_full_ptes(struct mm_struct *mm,
static inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm,
unsigned long address, pmd_t *pmdp)
{
- return pte_pmd(__ptep_get_and_clear_anysz(mm, address, (pte_t *)pmdp, PMD_SIZE));
+ return pte_pmd(__ptep_get_and_clear_anysz(mm, address, (hw_pte_t *)pmdp, PMD_SIZE));
}
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
static inline void ___ptep_set_wrprotect(struct mm_struct *mm,
- unsigned long address, pte_t *ptep,
+ unsigned long address, hw_pte_t *ptep,
pte_t pte)
{
pte_t old_pte;
@@ -1426,13 +1432,13 @@ static inline void ___ptep_set_wrprotect(struct mm_struct *mm,
* dirty status (PTE_DBM && !PTE_RDONLY) to the software PTE_DIRTY bit.
*/
static inline void __ptep_set_wrprotect(struct mm_struct *mm,
- unsigned long address, pte_t *ptep)
+ unsigned long address, hw_pte_t *ptep)
{
___ptep_set_wrprotect(mm, address, ptep, __ptep_get(ptep));
}
static inline void __wrprotect_ptes(struct mm_struct *mm, unsigned long address,
- pte_t *ptep, unsigned int nr)
+ hw_pte_t *ptep, unsigned int nr)
{
unsigned int i;
@@ -1441,7 +1447,7 @@ static inline void __wrprotect_ptes(struct mm_struct *mm, unsigned long address,
}
static inline void __clear_young_dirty_pte(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep,
+ unsigned long addr, hw_pte_t *ptep,
pte_t pte, cydp_t flags)
{
pte_t old_pte;
@@ -1460,7 +1466,8 @@ static inline void __clear_young_dirty_pte(struct vm_area_struct *vma,
}
static inline void __clear_young_dirty_ptes(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep,
+ unsigned long addr,
+ hw_pte_t *ptep,
unsigned int nr, cydp_t flags)
{
pte_t pte;
@@ -1485,7 +1492,7 @@ static inline void __clear_young_dirty_ptes(struct vm_area_struct *vma,
static inline void pmdp_set_wrprotect(struct mm_struct *mm,
unsigned long address, pmd_t *pmdp)
{
- __ptep_set_wrprotect(mm, address, (pte_t *)pmdp);
+ __ptep_set_wrprotect(mm, address, (hw_pte_t *)pmdp);
}
#define pmdp_establish pmdp_establish
@@ -1558,7 +1565,8 @@ extern void arch_swap_restore(swp_entry_t entry, struct folio *folio);
* On AArch64, the cache coherency is handled via the __set_ptes() function.
*/
static inline void update_mmu_cache_range(struct vm_fault *vmf,
- struct vm_area_struct *vma, unsigned long addr, pte_t *ptep,
+ struct vm_area_struct *vma, unsigned long addr,
+ hw_pte_t *ptep,
unsigned int nr)
{
/*
@@ -1613,21 +1621,21 @@ static inline bool pud_sect_supported(void)
#define __HAVE_ARCH_PTEP_MODIFY_PROT_TRANSACTION
#define ptep_modify_prot_start ptep_modify_prot_start
extern pte_t ptep_modify_prot_start(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep);
+ unsigned long addr, hw_pte_t *ptep);
#define ptep_modify_prot_commit ptep_modify_prot_commit
extern void ptep_modify_prot_commit(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep,
+ unsigned long addr, hw_pte_t *ptep,
pte_t old_pte, pte_t new_pte);
#define modify_prot_start_ptes modify_prot_start_ptes
extern pte_t modify_prot_start_ptes(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep,
+ unsigned long addr, hw_pte_t *ptep,
unsigned int nr);
#define modify_prot_commit_ptes modify_prot_commit_ptes
extern void modify_prot_commit_ptes(struct vm_area_struct *vma, unsigned long addr,
- pte_t *ptep, pte_t old_pte, pte_t pte,
+ hw_pte_t *ptep, pte_t old_pte, pte_t pte,
unsigned int nr);
#ifdef CONFIG_ARM64_CONTPTE
@@ -1638,33 +1646,35 @@ extern void modify_prot_commit_ptes(struct vm_area_struct *vma, unsigned long ad
* a private implementation detail of the public ptep API (see below).
*/
extern void __contpte_try_fold(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, pte_t pte);
+ hw_pte_t *ptep, pte_t pte);
extern void __contpte_try_unfold(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, pte_t pte);
-extern pte_t contpte_ptep_get(pte_t *ptep, pte_t orig_pte);
-extern pte_t contpte_ptep_get_lockless(pte_t *orig_ptep);
+ hw_pte_t *ptep, pte_t pte);
+extern pte_t contpte_ptep_get(hw_pte_t *ptep, pte_t orig_pte);
+extern pte_t contpte_ptep_get_lockless(hw_pte_t *orig_ptep);
extern void contpte_set_ptes(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, pte_t pte, unsigned int nr);
+ hw_pte_t *ptep, pte_t pte, unsigned int nr);
extern void contpte_clear_full_ptes(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, unsigned int nr, int full);
+ hw_pte_t *ptep, unsigned int nr, int full);
extern pte_t contpte_get_and_clear_full_ptes(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep,
+ unsigned long addr, hw_pte_t *ptep,
unsigned int nr, int full);
bool contpte_test_and_clear_young_ptes(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep, unsigned int nr);
+ unsigned long addr, hw_pte_t *ptep,
+ unsigned int nr);
bool contpte_clear_flush_young_ptes(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep, unsigned int nr);
+ unsigned long addr, hw_pte_t *ptep,
+ unsigned int nr);
extern void contpte_wrprotect_ptes(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, unsigned int nr);
+ hw_pte_t *ptep, unsigned int nr);
extern int contpte_ptep_set_access_flags(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep,
+ unsigned long addr, hw_pte_t *ptep,
pte_t entry, int dirty);
extern void contpte_clear_young_dirty_ptes(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep,
+ unsigned long addr, hw_pte_t *ptep,
unsigned int nr, cydp_t flags);
static __always_inline void contpte_try_fold(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep, pte_t pte)
+ unsigned long addr, hw_pte_t *ptep, pte_t pte)
{
/*
* Only bother trying if both the virtual and physical addresses are
@@ -1687,14 +1697,14 @@ static __always_inline void contpte_try_fold(struct mm_struct *mm,
}
static __always_inline void contpte_try_unfold(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep, pte_t pte)
+ unsigned long addr, hw_pte_t *ptep, pte_t pte)
{
if (unlikely(pte_valid_cont(pte)))
__contpte_try_unfold(mm, addr, ptep, pte);
}
#define pte_batch_hint pte_batch_hint
-static inline unsigned int pte_batch_hint(pte_t *ptep, pte_t pte)
+static inline unsigned int pte_batch_hint(hw_pte_t *ptep, pte_t pte)
{
if (!pte_valid_cont(pte))
return 1;
@@ -1721,7 +1731,7 @@ static inline unsigned int pte_batch_hint(pte_t *ptep, pte_t pte)
*/
#define ptep_get ptep_get
-static inline pte_t ptep_get(pte_t *ptep)
+static inline pte_t ptep_get(hw_pte_t *ptep)
{
pte_t pte = __ptep_get(ptep);
@@ -1732,7 +1742,7 @@ static inline pte_t ptep_get(pte_t *ptep)
}
#define ptep_get_lockless ptep_get_lockless
-static inline pte_t ptep_get_lockless(pte_t *ptep)
+static inline pte_t ptep_get_lockless(hw_pte_t *ptep)
{
pte_t pte = __ptep_get(ptep);
@@ -1742,7 +1752,7 @@ static inline pte_t ptep_get_lockless(pte_t *ptep)
return contpte_ptep_get_lockless(ptep);
}
-static inline void set_pte(pte_t *ptep, pte_t pte)
+static inline void set_pte(hw_pte_t *ptep, pte_t pte)
{
/*
* We don't have the mm or vaddr so cannot unfold contig entries (since
@@ -1759,7 +1769,7 @@ static inline void set_pte(pte_t *ptep, pte_t pte)
#define set_ptes set_ptes
static __always_inline void set_ptes(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, pte_t pte, unsigned int nr)
+ hw_pte_t *ptep, pte_t pte, unsigned int nr)
{
pte = pte_mknoncont(pte);
@@ -1773,7 +1783,7 @@ static __always_inline void set_ptes(struct mm_struct *mm, unsigned long addr,
}
static inline void pte_clear(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep)
+ unsigned long addr, hw_pte_t *ptep)
{
contpte_try_unfold(mm, addr, ptep, __ptep_get(ptep));
__pte_clear(mm, addr, ptep);
@@ -1781,7 +1791,7 @@ static inline void pte_clear(struct mm_struct *mm,
#define clear_full_ptes clear_full_ptes
static inline void clear_full_ptes(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, unsigned int nr, int full)
+ hw_pte_t *ptep, unsigned int nr, int full)
{
if (likely(nr == 1)) {
contpte_try_unfold(mm, addr, ptep, __ptep_get(ptep));
@@ -1793,7 +1803,7 @@ static inline void clear_full_ptes(struct mm_struct *mm, unsigned long addr,
#define get_and_clear_full_ptes get_and_clear_full_ptes
static inline pte_t get_and_clear_full_ptes(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep,
+ unsigned long addr, hw_pte_t *ptep,
unsigned int nr, int full)
{
pte_t pte;
@@ -1810,13 +1820,13 @@ static inline pte_t get_and_clear_full_ptes(struct mm_struct *mm,
#define __HAVE_ARCH_PTEP_GET_AND_CLEAR
static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep)
+ unsigned long addr, hw_pte_t *ptep)
{
contpte_try_unfold(mm, addr, ptep, __ptep_get(ptep));
return __ptep_get_and_clear(mm, addr, ptep);
}
-static inline bool ptep_try_set(pte_t *ptep, pte_t new_pte)
+static inline bool ptep_try_set(hw_pte_t *ptep, pte_t new_pte)
{
pteval_t old = 0;
@@ -1846,7 +1856,7 @@ static inline void flush_tlb_before_set(unsigned long addr)
#define test_and_clear_young_ptes test_and_clear_young_ptes
static inline bool test_and_clear_young_ptes(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep, unsigned int nr)
+ unsigned long addr, hw_pte_t *ptep, unsigned int nr)
{
if (likely(nr == 1 && !pte_cont(__ptep_get(ptep))))
return __ptep_test_and_clear_young(vma, addr, ptep);
@@ -1856,14 +1866,14 @@ static inline bool test_and_clear_young_ptes(struct vm_area_struct *vma,
#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG
static inline bool ptep_test_and_clear_young(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep)
+ unsigned long addr, hw_pte_t *ptep)
{
return test_and_clear_young_ptes(vma, addr, ptep, 1);
}
#define __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH
static inline bool ptep_clear_flush_young(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep)
+ unsigned long addr, hw_pte_t *ptep)
{
pte_t orig_pte = __ptep_get(ptep);
@@ -1875,7 +1885,7 @@ static inline bool ptep_clear_flush_young(struct vm_area_struct *vma,
#define clear_flush_young_ptes clear_flush_young_ptes
static inline bool clear_flush_young_ptes(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep, unsigned int nr)
+ unsigned long addr, hw_pte_t *ptep, unsigned int nr)
{
if (likely(nr == 1 && !pte_cont(__ptep_get(ptep))))
return __ptep_clear_flush_young(vma, addr, ptep);
@@ -1885,7 +1895,8 @@ static inline bool clear_flush_young_ptes(struct vm_area_struct *vma,
#define wrprotect_ptes wrprotect_ptes
static __always_inline void wrprotect_ptes(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep, unsigned int nr)
+ unsigned long addr, hw_pte_t *ptep,
+ unsigned int nr)
{
if (likely(nr == 1)) {
/*
@@ -1909,14 +1920,14 @@ static __always_inline void wrprotect_ptes(struct mm_struct *mm,
#define __HAVE_ARCH_PTEP_SET_WRPROTECT
static inline void ptep_set_wrprotect(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep)
+ unsigned long addr, hw_pte_t *ptep)
{
wrprotect_ptes(mm, addr, ptep, 1);
}
#define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS
static inline int ptep_set_access_flags(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep,
+ unsigned long addr, hw_pte_t *ptep,
pte_t entry, int dirty)
{
pte_t orig_pte = __ptep_get(ptep);
@@ -1931,7 +1942,7 @@ static inline int ptep_set_access_flags(struct vm_area_struct *vma,
#define clear_young_dirty_ptes clear_young_dirty_ptes
static inline void clear_young_dirty_ptes(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep,
+ unsigned long addr, hw_pte_t *ptep,
unsigned int nr, cydp_t flags)
{
if (likely(nr == 1 && !pte_cont(__ptep_get(ptep))))
diff --git a/arch/arm64/include/asm/vmalloc.h b/arch/arm64/include/asm/vmalloc.h
index 4ec1acd3c1b34..28d3e89f90e43 100644
--- a/arch/arm64/include/asm/vmalloc.h
+++ b/arch/arm64/include/asm/vmalloc.h
@@ -45,7 +45,7 @@ static inline unsigned long arch_vmap_pte_range_map_size(unsigned long addr,
#define arch_vmap_pte_range_unmap_size arch_vmap_pte_range_unmap_size
static inline unsigned long arch_vmap_pte_range_unmap_size(unsigned long addr,
- pte_t *ptep)
+ hw_pte_t *ptep)
{
/*
* The caller handles alignment so it's sufficient just to check
diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
index 0ec90fd1754ed..68ff21310604d 100644
--- a/arch/arm64/kernel/efi.c
+++ b/arch/arm64/kernel/efi.c
@@ -111,7 +111,8 @@ struct set_perm_data {
bool has_bti;
};
-static int __init set_permissions(pte_t *ptep, unsigned long addr, void *data)
+static int __init set_permissions(hw_pte_t *ptep, unsigned long addr,
+ void *data)
{
struct set_perm_data *spd = data;
const efi_memory_desc_t *md = spd->md;
diff --git a/arch/arm64/kernel/pi/map_kernel.c b/arch/arm64/kernel/pi/map_kernel.c
index fb44cbdd2f292..2b7e7be5fd6f3 100644
--- a/arch/arm64/kernel/pi/map_kernel.c
+++ b/arch/arm64/kernel/pi/map_kernel.c
@@ -26,7 +26,7 @@ static void __init map_segment(pgd_t *pg_dir, phys_addr_t *pgd, u64 va_offset,
{
map_range(pgd, ((u64)start + va_offset) & ~PAGE_OFFSET,
((u64)end + va_offset) & ~PAGE_OFFSET, (u64)start,
- prot, root_level, (pte_t *)pg_dir, may_use_cont, 0);
+ prot, root_level, (hw_pte_t *)pg_dir, may_use_cont, 0);
}
static void __init unmap_segment(pgd_t *pg_dir, u64 va_offset, void *start,
@@ -209,7 +209,7 @@ static void *__init map_fdt(phys_addr_t fdt)
*/
map_range(&ptep, fdt, (u64)_text > fdt ? min((u64)_text, efdt) : efdt,
fdt, PAGE_KERNEL, IDMAP_ROOT_LEVEL,
- (pte_t *)init_idmap_pg_dir, false, 0);
+ (hw_pte_t *)init_idmap_pg_dir, false, 0);
dsb(ishst);
return (void *)fdt;
diff --git a/arch/arm64/kernel/pi/map_range.c b/arch/arm64/kernel/pi/map_range.c
index 761b14893f748..94f06da76e333 100644
--- a/arch/arm64/kernel/pi/map_range.c
+++ b/arch/arm64/kernel/pi/map_range.c
@@ -27,7 +27,8 @@
* in the VA space
*/
void __init map_range(phys_addr_t *pte, u64 start, u64 end, phys_addr_t pa,
- pgprot_t prot, int level, pte_t *tbl, bool may_use_cont,
+ pgprot_t prot, int level, hw_pte_t *tbl,
+ bool may_use_cont,
u64 va_offset)
{
u64 cmask = (level == 3) ? CONT_PTE_SIZE - 1 : U64_MAX;
@@ -62,7 +63,7 @@ void __init map_range(phys_addr_t *pte, u64 start, u64 end, phys_addr_t pa,
*pte += PTRS_PER_PTE * sizeof(pte_t);
}
map_range(pte, start, next, pa, prot, level + 1,
- (pte_t *)(__pte_to_phys(*tbl) + va_offset),
+ (hw_pte_t *)(__pte_to_phys(*tbl) + va_offset),
may_use_cont, va_offset);
} else {
/*
@@ -100,10 +101,10 @@ asmlinkage phys_addr_t __init create_init_idmap(pgd_t *pg_dir, ptval_t clrmask)
/* MMU is off; pointer casts to phys_addr_t are safe */
map_range(&ptep, (u64)_stext, (u64)__initdata_begin,
(phys_addr_t)_stext, text_prot, IDMAP_ROOT_LEVEL,
- (pte_t *)pg_dir, false, 0);
+ (hw_pte_t *)pg_dir, false, 0);
map_range(&ptep, (u64)__initdata_begin, (u64)_end,
(phys_addr_t)__initdata_begin, data_prot, IDMAP_ROOT_LEVEL,
- (pte_t *)pg_dir, false, 0);
+ (hw_pte_t *)pg_dir, false, 0);
return ptep;
}
diff --git a/arch/arm64/kernel/pi/pi.h b/arch/arm64/kernel/pi/pi.h
index 5dfd8484d2005..a7d09a2fa8f9f 100644
--- a/arch/arm64/kernel/pi/pi.h
+++ b/arch/arm64/kernel/pi/pi.h
@@ -2,6 +2,7 @@
// Copyright 2023 Google LLC
// Author: Ard Biesheuvel <ardb@google.com>
+#include <linux/pgtable_types.h>
#include <linux/types.h>
#define __prel64_initconst __section(".init.rodata.prel64")
@@ -30,7 +31,7 @@ void relocate_kernel(u64 offset);
int scs_patch(const u8 eh_frame[], int size, bool skip_dry_run);
void map_range(phys_addr_t *pte, u64 start, u64 end, phys_addr_t pa,
- pgprot_t prot, int level, pte_t *tbl, bool may_use_cont,
+ pgprot_t prot, int level, hw_pte_t *tbl, bool may_use_cont,
u64 va_offset);
asmlinkage void early_map_kernel(u64 boot_status, phys_addr_t fdt);
diff --git a/arch/arm64/mm/contpte.c b/arch/arm64/mm/contpte.c
index 0acab179fc1a5..753267c0b41e5 100644
--- a/arch/arm64/mm/contpte.c
+++ b/arch/arm64/mm/contpte.c
@@ -21,13 +21,14 @@ static inline bool mm_is_user(struct mm_struct *mm)
return mm != &init_mm;
}
-static inline pte_t *contpte_align_down(pte_t *ptep)
+static inline hw_pte_t *contpte_align_down(hw_pte_t *ptep)
{
return PTR_ALIGN_DOWN(ptep, sizeof(*ptep) * CONT_PTES);
}
-static inline pte_t *contpte_align_addr_ptep(unsigned long *start,
- unsigned long *end, pte_t *ptep,
+static inline hw_pte_t *contpte_align_addr_ptep(unsigned long *start,
+ unsigned long *end,
+ hw_pte_t *ptep,
unsigned int nr)
{
/*
@@ -47,7 +48,7 @@ static inline pte_t *contpte_align_addr_ptep(unsigned long *start,
}
static void contpte_try_unfold_partial(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, unsigned int nr)
+ hw_pte_t *ptep, unsigned int nr)
{
/*
* Unfold any partially covered contpte block at the beginning and end
@@ -59,7 +60,7 @@ static void contpte_try_unfold_partial(struct mm_struct *mm, unsigned long addr,
if (ptep + nr != contpte_align_down(ptep + nr)) {
unsigned long last_addr = addr + PAGE_SIZE * (nr - 1);
- pte_t *last_ptep = ptep + nr - 1;
+ hw_pte_t *last_ptep = ptep + nr - 1;
contpte_try_unfold(mm, last_addr, last_ptep,
__ptep_get(last_ptep));
@@ -67,11 +68,11 @@ static void contpte_try_unfold_partial(struct mm_struct *mm, unsigned long addr,
}
static void contpte_convert(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, pte_t pte)
+ hw_pte_t *ptep, pte_t pte)
{
struct vm_area_struct vma = TLB_FLUSH_VMA(mm, 0);
unsigned long start_addr;
- pte_t *start_ptep;
+ hw_pte_t *start_ptep;
int i;
start_ptep = ptep = contpte_align_down(ptep);
@@ -227,7 +228,7 @@ static void contpte_convert(struct mm_struct *mm, unsigned long addr,
}
void __contpte_try_fold(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, pte_t pte)
+ hw_pte_t *ptep, pte_t pte)
{
/*
* We have already checked that the virtual and pysical addresses are
@@ -253,7 +254,7 @@ void __contpte_try_fold(struct mm_struct *mm, unsigned long addr,
struct folio *folio;
struct page *page;
unsigned long pfn;
- pte_t *orig_ptep;
+ hw_pte_t *orig_ptep;
pgprot_t prot;
int i;
@@ -291,7 +292,7 @@ void __contpte_try_fold(struct mm_struct *mm, unsigned long addr,
EXPORT_SYMBOL_GPL(__contpte_try_fold);
void __contpte_try_unfold(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, pte_t pte)
+ hw_pte_t *ptep, pte_t pte)
{
/*
* We have already checked that the ptes are contiguous in
@@ -305,7 +306,7 @@ void __contpte_try_unfold(struct mm_struct *mm, unsigned long addr,
}
EXPORT_SYMBOL_GPL(__contpte_try_unfold);
-pte_t contpte_ptep_get(pte_t *ptep, pte_t orig_pte)
+pte_t contpte_ptep_get(hw_pte_t *ptep, pte_t orig_pte)
{
/*
* Gather access/dirty bits, which may be populated in any of the ptes
@@ -362,7 +363,7 @@ static inline bool contpte_is_consistent(pte_t pte, unsigned long pfn,
pgprot_val(prot) == pgprot_val(orig_prot);
}
-pte_t contpte_ptep_get_lockless(pte_t *orig_ptep)
+pte_t contpte_ptep_get_lockless(hw_pte_t *orig_ptep)
{
/*
* The ptep_get_lockless() API requires us to read and return *orig_ptep
@@ -384,7 +385,7 @@ pte_t contpte_ptep_get_lockless(pte_t *orig_ptep)
pgprot_t orig_prot;
unsigned long pfn;
pte_t orig_pte;
- pte_t *ptep;
+ hw_pte_t *ptep;
pte_t pte;
int i;
@@ -445,7 +446,8 @@ pte_t contpte_ptep_get_lockless(pte_t *orig_ptep)
EXPORT_SYMBOL_GPL(contpte_ptep_get_lockless);
void contpte_set_ptes(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, pte_t pte, unsigned int nr)
+ hw_pte_t *ptep, pte_t pte,
+ unsigned int nr)
{
unsigned long next;
unsigned long end;
@@ -488,7 +490,7 @@ void contpte_set_ptes(struct mm_struct *mm, unsigned long addr,
EXPORT_SYMBOL_GPL(contpte_set_ptes);
void contpte_clear_full_ptes(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, unsigned int nr, int full)
+ hw_pte_t *ptep, unsigned int nr, int full)
{
contpte_try_unfold_partial(mm, addr, ptep, nr);
__clear_full_ptes(mm, addr, ptep, nr, full);
@@ -496,7 +498,7 @@ void contpte_clear_full_ptes(struct mm_struct *mm, unsigned long addr,
EXPORT_SYMBOL_GPL(contpte_clear_full_ptes);
pte_t contpte_get_and_clear_full_ptes(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep,
+ unsigned long addr, hw_pte_t *ptep,
unsigned int nr, int full)
{
contpte_try_unfold_partial(mm, addr, ptep, nr);
@@ -505,7 +507,7 @@ pte_t contpte_get_and_clear_full_ptes(struct mm_struct *mm,
EXPORT_SYMBOL_GPL(contpte_get_and_clear_full_ptes);
bool contpte_test_and_clear_young_ptes(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep, unsigned int nr)
+ unsigned long addr, hw_pte_t *ptep, unsigned int nr)
{
/*
* ptep_clear_flush_young() technically requires us to clear the access
@@ -531,7 +533,7 @@ bool contpte_test_and_clear_young_ptes(struct vm_area_struct *vma,
EXPORT_SYMBOL_GPL(contpte_test_and_clear_young_ptes);
bool contpte_clear_flush_young_ptes(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep, unsigned int nr)
+ unsigned long addr, hw_pte_t *ptep, unsigned int nr)
{
bool young;
@@ -554,7 +556,7 @@ bool contpte_clear_flush_young_ptes(struct vm_area_struct *vma,
EXPORT_SYMBOL_GPL(contpte_clear_flush_young_ptes);
void contpte_wrprotect_ptes(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, unsigned int nr)
+ hw_pte_t *ptep, unsigned int nr)
{
/*
* If wrprotecting an entire contig range, we can avoid unfolding. Just
@@ -572,7 +574,7 @@ void contpte_wrprotect_ptes(struct mm_struct *mm, unsigned long addr,
EXPORT_SYMBOL_GPL(contpte_wrprotect_ptes);
void contpte_clear_young_dirty_ptes(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep,
+ unsigned long addr, hw_pte_t *ptep,
unsigned int nr, cydp_t flags)
{
/*
@@ -593,9 +595,10 @@ void contpte_clear_young_dirty_ptes(struct vm_area_struct *vma,
}
EXPORT_SYMBOL_GPL(contpte_clear_young_dirty_ptes);
-static bool contpte_all_subptes_match_access_flags(pte_t *ptep, pte_t entry)
+static bool contpte_all_subptes_match_access_flags(hw_pte_t *ptep,
+ pte_t entry)
{
- pte_t *cont_ptep = contpte_align_down(ptep);
+ hw_pte_t *cont_ptep = contpte_align_down(ptep);
/*
* PFNs differ per sub-PTE. Match only bits consumed by
* __ptep_set_access_flags(): AF, DIRTY and write permission.
@@ -615,7 +618,7 @@ static bool contpte_all_subptes_match_access_flags(pte_t *ptep, pte_t entry)
}
int contpte_ptep_set_access_flags(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep,
+ unsigned long addr, hw_pte_t *ptep,
pte_t entry, int dirty)
{
unsigned long start_addr;
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 0b52557652be6..b77f4be88e3ea 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -162,7 +162,8 @@ static void show_pte(unsigned long addr)
p4d_t *p4dp, p4d;
pud_t *pudp, pud;
pmd_t *pmdp, pmd;
- pte_t *ptep, pte;
+ hw_pte_t *ptep;
+ pte_t pte;
if (pgd_none(pgd) || pgd_bad(pgd))
break;
@@ -208,7 +209,7 @@ static void show_pte(unsigned long addr)
* Returns whether or not the PTE actually changed.
*/
int __ptep_set_access_flags_anysz(struct vm_area_struct *vma,
- unsigned long address, pte_t *ptep,
+ unsigned long address, hw_pte_t *ptep,
pte_t entry, int dirty, unsigned long pgsize)
{
pteval_t old_pteval, pteval;
diff --git a/arch/arm64/mm/fixmap.c b/arch/arm64/mm/fixmap.c
index f66a0016dd02d..237a9136bc73b 100644
--- a/arch/arm64/mm/fixmap.c
+++ b/arch/arm64/mm/fixmap.c
@@ -35,7 +35,7 @@ static pte_t bm_pte[NR_BM_PTE_TABLES][PTRS_PER_PTE] __bss_pgtbl;
static pmd_t bm_pmd[PTRS_PER_PMD] __bss_pgtbl __maybe_unused;
static pud_t bm_pud[PTRS_PER_PUD] __bss_pgtbl __maybe_unused;
-static inline pte_t *fixmap_pte(unsigned long addr)
+static inline hw_pte_t *fixmap_pte(unsigned long addr)
{
return &bm_pte[BM_PTE_TABLE_IDX(addr)][pte_index(addr)];
}
@@ -43,7 +43,7 @@ static inline pte_t *fixmap_pte(unsigned long addr)
static void __init early_fixmap_init_pte(pmd_t *pmdp, unsigned long addr)
{
pmd_t pmd = READ_ONCE(*pmdp);
- pte_t *ptep;
+ hw_pte_t *ptep;
if (pmd_none(pmd)) {
ptep = bm_pte[BM_PTE_TABLE_IDX(addr)];
@@ -120,7 +120,7 @@ void __set_fixmap(enum fixed_addresses idx,
phys_addr_t phys, pgprot_t flags)
{
unsigned long addr = __fix_to_virt(idx);
- pte_t *ptep;
+ hw_pte_t *ptep;
BUG_ON(idx <= FIX_HOLE || idx >= __end_of_fixed_addresses);
diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c
index 8e799c1fe0aa6..560c0f16cd8e4 100644
--- a/arch/arm64/mm/hugetlbpage.c
+++ b/arch/arm64/mm/hugetlbpage.c
@@ -76,7 +76,7 @@ bool arch_hugetlb_migration_supported(struct hstate *h)
#endif
static int find_num_contig(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, size_t *pgsize)
+ hw_pte_t *ptep, size_t *pgsize)
{
pgd_t *pgdp = pgd_offset(mm, addr);
p4d_t *p4dp;
@@ -87,7 +87,7 @@ static int find_num_contig(struct mm_struct *mm, unsigned long addr,
p4dp = p4d_offset(pgdp, addr);
pudp = pud_offset(p4dp, addr);
pmdp = pmd_offset(pudp, addr);
- if ((pte_t *)PTR_ALIGN_DOWN(pmdp, sizeof(*pmdp) * CONT_PMDS) == ptep) {
+ if ((hw_pte_t *)PTR_ALIGN_DOWN(pmdp, sizeof(*pmdp) * CONT_PMDS) == ptep) {
*pgsize = PMD_SIZE;
return CONT_PMDS;
}
@@ -116,7 +116,7 @@ static inline int num_contig_ptes(unsigned long size, size_t *pgsize)
return contig_ptes;
}
-pte_t huge_ptep_get(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
+pte_t huge_ptep_get(struct mm_struct *mm, unsigned long addr, hw_pte_t *ptep)
{
int ncontig, i;
size_t pgsize;
@@ -148,7 +148,7 @@ pte_t huge_ptep_get(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
*/
static pte_t get_clear_contig(struct mm_struct *mm,
unsigned long addr,
- pte_t *ptep,
+ hw_pte_t *ptep,
unsigned long pgsize,
unsigned long ncontig)
{
@@ -173,7 +173,7 @@ static pte_t get_clear_contig(struct mm_struct *mm,
static pte_t get_clear_contig_flush(struct mm_struct *mm,
unsigned long addr,
- pte_t *ptep,
+ hw_pte_t *ptep,
unsigned long pgsize,
unsigned long ncontig)
{
@@ -196,7 +196,7 @@ static pte_t get_clear_contig_flush(struct mm_struct *mm,
*/
static void clear_flush(struct mm_struct *mm,
unsigned long addr,
- pte_t *ptep,
+ hw_pte_t *ptep,
unsigned long pgsize,
unsigned long ncontig)
{
@@ -213,7 +213,7 @@ static void clear_flush(struct mm_struct *mm,
}
void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, pte_t pte, unsigned long sz)
+ hw_pte_t *ptep, pte_t pte, unsigned long sz)
{
size_t pgsize;
int i;
@@ -234,14 +234,14 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
__set_ptes_anysz(mm, addr, ptep, pte, ncontig, pgsize);
}
-pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,
+hw_pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long addr, unsigned long sz)
{
pgd_t *pgdp;
p4d_t *p4dp;
pud_t *pudp;
pmd_t *pmdp;
- pte_t *ptep = NULL;
+ hw_pte_t *ptep = NULL;
pgdp = pgd_offset(mm, addr);
p4dp = p4d_alloc(mm, pgdp, addr);
@@ -253,7 +253,7 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,
return NULL;
if (sz == PUD_SIZE) {
- ptep = (pte_t *)pudp;
+ ptep = (hw_pte_t *)pudp;
} else if (sz == (CONT_PTE_SIZE)) {
pmdp = pmd_alloc(mm, pudp, addr);
if (!pmdp)
@@ -265,17 +265,17 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,
if (want_pmd_share(vma, addr) && pud_none(READ_ONCE(*pudp)))
ptep = huge_pmd_share(mm, vma, addr, pudp);
else
- ptep = (pte_t *)pmd_alloc(mm, pudp, addr);
+ ptep = (hw_pte_t *)pmd_alloc(mm, pudp, addr);
} else if (sz == (CONT_PMD_SIZE)) {
pmdp = pmd_alloc(mm, pudp, addr);
WARN_ON(addr & (sz - 1));
- return (pte_t *)pmdp;
+ return (hw_pte_t *)pmdp;
}
return ptep;
}
-pte_t *huge_pte_offset(struct mm_struct *mm,
+hw_pte_t *huge_pte_offset(struct mm_struct *mm,
unsigned long addr, unsigned long sz)
{
pgd_t *pgdp;
@@ -297,7 +297,7 @@ pte_t *huge_pte_offset(struct mm_struct *mm,
return NULL;
/* hugepage or swap? */
if (pud_leaf(pud) || !pud_present(pud))
- return (pte_t *)pudp;
+ return (hw_pte_t *)pudp;
/* table; check the next level */
if (sz == CONT_PMD_SIZE)
@@ -309,7 +309,7 @@ pte_t *huge_pte_offset(struct mm_struct *mm,
pmd_none(pmd))
return NULL;
if (pmd_leaf(pmd) || !pmd_present(pmd))
- return (pte_t *)pmdp;
+ return (hw_pte_t *)pmdp;
if (sz == CONT_PTE_SIZE)
return pte_offset_huge(pmdp, (addr & CONT_PTE_MASK));
@@ -367,7 +367,7 @@ pte_t arch_make_huge_pte(pte_t entry, unsigned int shift, vm_flags_t flags)
}
void huge_pte_clear(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, unsigned long sz)
+ hw_pte_t *ptep, unsigned long sz)
{
int i, ncontig;
size_t pgsize;
@@ -379,7 +379,7 @@ void huge_pte_clear(struct mm_struct *mm, unsigned long addr,
}
pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, unsigned long sz)
+ hw_pte_t *ptep, unsigned long sz)
{
int ncontig;
size_t pgsize;
@@ -397,7 +397,7 @@ pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr,
* all the contiguous ptes we need to check whether or not there is a
* discrepancy between dirty or young.
*/
-static int __cont_access_flags_changed(pte_t *ptep, pte_t pte, int ncontig)
+static int __cont_access_flags_changed(hw_pte_t *ptep, pte_t pte, int ncontig)
{
int i;
@@ -418,7 +418,7 @@ static int __cont_access_flags_changed(pte_t *ptep, pte_t pte, int ncontig)
}
int huge_ptep_set_access_flags(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep,
+ unsigned long addr, hw_pte_t *ptep,
pte_t pte, int dirty)
{
int ncontig;
@@ -451,7 +451,7 @@ int huge_ptep_set_access_flags(struct vm_area_struct *vma,
}
void huge_ptep_set_wrprotect(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep)
+ unsigned long addr, hw_pte_t *ptep)
{
int ncontig;
size_t pgsize;
@@ -474,7 +474,7 @@ void huge_ptep_set_wrprotect(struct mm_struct *mm,
}
pte_t huge_ptep_clear_flush(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep)
+ unsigned long addr, hw_pte_t *ptep)
{
struct mm_struct *mm = vma->vm_mm;
size_t pgsize;
@@ -514,7 +514,8 @@ bool __init arch_hugetlb_valid_size(unsigned long size)
return __hugetlb_valid_size(size);
}
-pte_t huge_ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep)
+pte_t huge_ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long addr,
+ hw_pte_t *ptep)
{
unsigned long psize = huge_page_size(hstate_vma(vma));
@@ -530,7 +531,8 @@ pte_t huge_ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long addr
return huge_ptep_get_and_clear(vma->vm_mm, addr, ptep, psize);
}
-void huge_ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep,
+void huge_ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr,
+ hw_pte_t *ptep,
pte_t old_pte, pte_t pte)
{
unsigned long psize = huge_page_size(hstate_vma(vma));
diff --git a/arch/arm64/mm/kasan_init.c b/arch/arm64/mm/kasan_init.c
index 45fbdce684c89..dccb736b32e9b 100644
--- a/arch/arm64/mm/kasan_init.c
+++ b/arch/arm64/mm/kasan_init.c
@@ -59,7 +59,7 @@ static phys_addr_t __init kasan_alloc_raw_page(int node)
return __pa(p);
}
-static pte_t *__init kasan_pte_offset(pmd_t *pmdp, unsigned long addr, int node,
+static hw_pte_t *__init kasan_pte_offset(pmd_t *pmdp, unsigned long addr, int node,
bool early)
{
if (pmd_none(READ_ONCE(*pmdp))) {
@@ -116,7 +116,7 @@ static void __init kasan_pte_populate(pmd_t *pmdp, unsigned long addr,
unsigned long end, int node, bool early)
{
unsigned long next;
- pte_t *ptep = kasan_pte_offset(pmdp, addr, node, early);
+ hw_pte_t *ptep = kasan_pte_offset(pmdp, addr, node, early);
do {
phys_addr_t page_phys = early ?
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index 79d90226fd5dc..f02b974ad64df 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -159,7 +159,7 @@ static void init_clear_pgtable(void *table)
dsb(ishst);
}
-static void init_pte(pte_t *ptep, unsigned long addr, unsigned long end,
+static void init_pte(hw_pte_t *ptep, unsigned long addr, unsigned long end,
phys_addr_t phys, pgprot_t prot)
{
do {
@@ -182,7 +182,7 @@ static void init_pte(pte_t *ptep, unsigned long addr, unsigned long end,
} while (ptep++, addr += PAGE_SIZE, addr != end);
}
-static bool pte_range_has_valid_noncont(pte_t *ptep)
+static bool pte_range_has_valid_noncont(hw_pte_t *ptep)
{
for (int i = 0; i < CONT_PTES; i++) {
pte_t pte = __ptep_get(&ptep[i]);
@@ -201,7 +201,7 @@ static int alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
{
unsigned long next;
pmd_t pmd = READ_ONCE(*pmdp);
- pte_t *ptep;
+ hw_pte_t *ptep;
BUG_ON(pmd_leaf(pmd));
if (pmd_none(pmd)) {
@@ -592,7 +592,7 @@ pgd_pgtable_alloc_special_mm(enum pgtable_level pgtable_level)
return __pgd_pgtable_alloc(NULL, GFP_PGTABLE_KERNEL, pgtable_level);
}
-static void split_contpte(pte_t *ptep)
+static void split_contpte(hw_pte_t *ptep)
{
int i;
@@ -607,13 +607,13 @@ static int split_pmd(pmd_t *pmdp, pmd_t pmd, gfp_t gfp, bool to_cont)
unsigned long pfn = pmd_pfn(pmd);
pgprot_t prot = pmd_pgprot(pmd);
phys_addr_t pte_phys;
- pte_t *ptep;
+ hw_pte_t *ptep;
int i;
pte_phys = pgd_pgtable_alloc_init_mm_gfp(PGTABLE_LEVEL_PTE, gfp);
if (pte_phys == INVALID_PHYS_ADDR)
return -ENOMEM;
- ptep = (pte_t *)phys_to_virt(pte_phys);
+ ptep = (hw_pte_t *)phys_to_virt(pte_phys);
if (pgprot_val(prot) & PMD_SECT_PXN)
tableprot |= PMD_TABLE_PXN;
@@ -691,7 +691,8 @@ static int split_kernel_leaf_mapping_locked(unsigned long addr)
p4d_t *p4dp, p4d;
pud_t *pudp, pud;
pmd_t *pmdp, pmd;
- pte_t *ptep, pte;
+ hw_pte_t *ptep;
+ pte_t pte;
int ret = 0;
/*
@@ -904,7 +905,7 @@ static int split_to_ptes_pmd_entry(pmd_t *pmdp, unsigned long addr,
return ret;
}
-static int split_to_ptes_pte_entry(pte_t *ptep, unsigned long addr,
+static int split_to_ptes_pte_entry(hw_pte_t *ptep, unsigned long addr,
unsigned long next, struct mm_walk *walk)
{
pte_t pte = __ptep_get(ptep);
@@ -1438,7 +1439,8 @@ static void __init declare_kernel_vmas(void)
}
void __pi_map_range(phys_addr_t *pte, u64 start, u64 end, phys_addr_t pa,
- pgprot_t prot, int level, pte_t *tbl, bool may_use_cont,
+ pgprot_t prot, int level, hw_pte_t *tbl,
+ bool may_use_cont,
u64 va_offset);
static u8 idmap_ptes[IDMAP_LEVELS - 1][PAGE_SIZE] __aligned(PAGE_SIZE) __ro_after_init,
@@ -1451,7 +1453,7 @@ static void __init create_idmap(void)
phys_addr_t ptep = __pa_symbol(idmap_ptes);
__pi_map_range(&ptep, start, end, start, PAGE_KERNEL_ROX,
- IDMAP_ROOT_LEVEL, (pte_t *)idmap_pg_dir, false,
+ IDMAP_ROOT_LEVEL, (hw_pte_t *)idmap_pg_dir, false,
__phys_to_virt(ptep) - ptep);
if (linear_map_requires_bbml3 ||
@@ -1466,7 +1468,8 @@ static void __init create_idmap(void)
*/
ptep = __pa_symbol(kpti_bbml3_ptes);
__pi_map_range(&ptep, pa, pa + sizeof(u32), pa, PAGE_KERNEL,
- IDMAP_ROOT_LEVEL, (pte_t *)idmap_pg_dir, false,
+ IDMAP_ROOT_LEVEL, (hw_pte_t *)idmap_pg_dir,
+ false,
__phys_to_virt(ptep) - ptep);
}
}
@@ -1522,7 +1525,8 @@ static void unmap_hotplug_pte_range(pmd_t *pmdp, unsigned long addr,
unsigned long end, bool free_mapped,
struct vmem_altmap *altmap)
{
- pte_t *ptep, pte;
+ hw_pte_t *ptep;
+ pte_t pte;
do {
ptep = pte_offset_kernel(pmdp, addr);
@@ -1665,7 +1669,8 @@ static void free_empty_pte_table(pmd_t *pmdp, unsigned long addr,
unsigned long end, unsigned long floor,
unsigned long ceiling)
{
- pte_t *ptep, pte;
+ hw_pte_t *ptep;
+ pte_t pte;
unsigned long i, start = addr;
do {
@@ -1914,7 +1919,7 @@ int pmd_clear_huge(pmd_t *pmdp)
int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr)
{
- pte_t *table;
+ hw_pte_t *table;
pmd_t pmd;
pmd = READ_ONCE(*pmdp);
@@ -2056,7 +2061,8 @@ static bool addr_splits_kernel_leaf(unsigned long addr)
p4d_t *p4dp, p4d;
pud_t *pudp, pud;
pmd_t *pmdp, pmd;
- pte_t *ptep, pte;
+ hw_pte_t *ptep;
+ pte_t pte;
/*
* If the given address points at a the start address of
@@ -2284,7 +2290,7 @@ early_initcall(prevent_memory_remove_init);
#endif
pte_t modify_prot_start_ptes(struct vm_area_struct *vma, unsigned long addr,
- pte_t *ptep, unsigned int nr)
+ hw_pte_t *ptep, unsigned int nr)
{
pte_t pte = get_and_clear_ptes(vma->vm_mm, addr, ptep, nr);
@@ -2302,19 +2308,21 @@ pte_t modify_prot_start_ptes(struct vm_area_struct *vma, unsigned long addr,
return pte;
}
-pte_t ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep)
+pte_t ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long addr,
+ hw_pte_t *ptep)
{
return modify_prot_start_ptes(vma, addr, ptep, 1);
}
void modify_prot_commit_ptes(struct vm_area_struct *vma, unsigned long addr,
- pte_t *ptep, pte_t old_pte, pte_t pte,
+ hw_pte_t *ptep, pte_t old_pte, pte_t pte,
unsigned int nr)
{
set_ptes(vma->vm_mm, addr, ptep, pte, nr);
}
-void ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep,
+void ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr,
+ hw_pte_t *ptep,
pte_t old_pte, pte_t pte)
{
modify_prot_commit_ptes(vma, addr, ptep, old_pte, pte, 1);
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index 132938b32eb16..895014212a81c 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -68,7 +68,7 @@ static int pageattr_pmd_entry(pmd_t *pmd, unsigned long addr,
return 0;
}
-static int pageattr_pte_entry(pte_t *pte, unsigned long addr,
+static int pageattr_pte_entry(hw_pte_t *pte, unsigned long addr,
unsigned long next, struct mm_walk *walk)
{
pte_t val = __ptep_get(pte);
@@ -379,7 +379,7 @@ bool kernel_page_present(struct page *page)
p4d_t *p4dp;
pud_t *pudp, pud;
pmd_t *pmdp, pmd;
- pte_t *ptep;
+ hw_pte_t *ptep;
unsigned long addr = (unsigned long)page_address(page);
pgdp = pgd_offset_k(addr);
diff --git a/arch/arm64/mm/trans_pgd.c b/arch/arm64/mm/trans_pgd.c
index cca9706a875c3..845417792ad48 100644
--- a/arch/arm64/mm/trans_pgd.c
+++ b/arch/arm64/mm/trans_pgd.c
@@ -34,8 +34,8 @@ static void *trans_alloc(struct trans_pgd_info *info)
static int copy_pte(struct trans_pgd_info *info, pmd_t *dst_pmdp,
pmd_t *src_pmdp, unsigned long start, unsigned long end)
{
- pte_t *src_ptep;
- pte_t *dst_ptep;
+ hw_pte_t *src_ptep;
+ hw_pte_t *dst_ptep;
unsigned long addr = start;
dst_ptep = trans_alloc(info);
--
2.47.3
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 2/6] arm64: use hw_pte_val for HW PTE atomics
2026-09-14 13:51 [PATCH 0/6] arm64: distinguish HW PTE pointers from SW PTE value pointers Muhammad Usama Anjum
2026-09-14 13:51 ` [PATCH 1/6] arm64: use hw_pte_t for HW PTE pointers Muhammad Usama Anjum
@ 2026-09-14 13:51 ` Muhammad Usama Anjum
2026-09-18 15:55 ` Ryan Roberts
2026-09-14 13:51 ` [PATCH 3/6] arm64: convert between HW PTEs and SW PTE values Muhammad Usama Anjum
` (3 subsequent siblings)
5 siblings, 1 reply; 11+ messages in thread
From: Muhammad Usama Anjum @ 2026-09-14 13:51 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Mark Rutland, Ard Biesheuvel,
Ilias Apalodimas, Andrey Ryabinin, Alexander Potapenko,
Andrey Konovalov, Dmitry Vyukov, Vincenzo Frascino,
Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko
Cc: linux-arm-kernel, linux-kernel, linux-efi, kasan-dev, linux-mm,
Muhammad Usama Anjum
Add hw_pte_val() to preserve an lvalue for the HW PTE bits, so atomic
updates can take their address. pte_val() expects a SW PTE value and
cannot operate directly on a distinct hw_pte_t.
With ARCH_HAS_HW_PTE_T, use pte_val() on the wrapper's __pte member;
otherwise, use pte_val() directly.
The atomic operations and their ordering are unchanged.
Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
---
arch/arm64/include/asm/pgtable.h | 10 +++++-----
arch/arm64/mm/fault.c | 6 +++---
include/linux/pgtable_types.h | 4 ++++
3 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 652ce413be389..4768ec59de555 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -1303,7 +1303,7 @@ static inline bool __ptep_test_and_clear_young(struct vm_area_struct *vma,
do {
old_pte = pte;
pte = pte_mkold(pte);
- pte_val(pte) = cmpxchg_relaxed(&pte_val(*ptep),
+ pte_val(pte) = cmpxchg_relaxed(&hw_pte_val(*ptep),
pte_val(old_pte), pte_val(pte));
} while (pte_val(pte) != pte_val(old_pte));
@@ -1346,7 +1346,7 @@ static inline pte_t __ptep_get_and_clear_anysz(struct mm_struct *mm,
hw_pte_t *ptep,
unsigned long pgsize)
{
- pte_t pte = __pte(xchg_relaxed(&pte_val(*ptep), 0));
+ pte_t pte = __pte(xchg_relaxed(&hw_pte_val(*ptep), 0));
switch (pgsize) {
case PAGE_SIZE:
@@ -1422,7 +1422,7 @@ static inline void ___ptep_set_wrprotect(struct mm_struct *mm,
do {
old_pte = pte;
pte = pte_wrprotect(pte);
- pte_val(pte) = cmpxchg_relaxed(&pte_val(*ptep),
+ pte_val(pte) = cmpxchg_relaxed(&hw_pte_val(*ptep),
pte_val(old_pte), pte_val(pte));
} while (pte_val(pte) != pte_val(old_pte));
}
@@ -1460,7 +1460,7 @@ static inline void __clear_young_dirty_pte(struct vm_area_struct *vma,
if (flags & CYDP_CLEAR_DIRTY)
pte = pte_mkclean(pte);
- pte_val(pte) = cmpxchg_relaxed(&pte_val(*ptep),
+ pte_val(pte) = cmpxchg_relaxed(&hw_pte_val(*ptep),
pte_val(old_pte), pte_val(pte));
} while (pte_val(pte) != pte_val(old_pte));
}
@@ -1830,7 +1830,7 @@ static inline bool ptep_try_set(hw_pte_t *ptep, pte_t new_pte)
{
pteval_t old = 0;
- if (!try_cmpxchg(&pte_val(*ptep), &old, pte_val(new_pte)))
+ if (!try_cmpxchg(&hw_pte_val(*ptep), &old, pte_val(new_pte)))
return false;
/*
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index b77f4be88e3ea..7d6c30f27214e 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -225,8 +225,8 @@ int __ptep_set_access_flags_anysz(struct vm_area_struct *vma,
/*
* Setting the flags must be done atomically to avoid racing with the
* hardware update of the access/dirty state. The PTE_RDONLY bit must
- * be set to the most permissive (lowest value) of *ptep and entry
- * (calculated as: a & b == ~(~a | ~b)).
+ * be set to the most permissive (lowest value) of the current PTE and
+ * entry (calculated as: a & b == ~(~a | ~b)).
*/
pte_val(entry) ^= PTE_RDONLY;
pteval = pte_val(pte);
@@ -235,7 +235,7 @@ int __ptep_set_access_flags_anysz(struct vm_area_struct *vma,
pteval ^= PTE_RDONLY;
pteval |= pte_val(entry);
pteval ^= PTE_RDONLY;
- pteval = cmpxchg_relaxed(&pte_val(*ptep), old_pteval, pteval);
+ pteval = cmpxchg_relaxed(&hw_pte_val(*ptep), old_pteval, pteval);
} while (pteval != old_pteval);
/*
diff --git a/include/linux/pgtable_types.h b/include/linux/pgtable_types.h
index d6c5a7548550b..ee4eace5c3e1c 100644
--- a/include/linux/pgtable_types.h
+++ b/include/linux/pgtable_types.h
@@ -9,9 +9,13 @@
#ifdef CONFIG_ARCH_HAS_HW_PTE_T
typedef struct __hw_pte_t { pte_t __pte; } hw_pte_t;
#define __pte_from_hw(pte) ((pte).__pte)
+
+#define hw_pte_val(x) pte_val((x).__pte)
#else
#define hw_pte_t pte_t
#define __pte_from_hw(pte) (pte)
+
+#define hw_pte_val(x) pte_val(x)
#endif
#endif /* !__ASSEMBLY__ */
--
2.47.3
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 3/6] arm64: convert between HW PTEs and SW PTE values
2026-09-14 13:51 [PATCH 0/6] arm64: distinguish HW PTE pointers from SW PTE value pointers Muhammad Usama Anjum
2026-09-14 13:51 ` [PATCH 1/6] arm64: use hw_pte_t for HW PTE pointers Muhammad Usama Anjum
2026-09-14 13:51 ` [PATCH 2/6] arm64: use hw_pte_val for HW PTE atomics Muhammad Usama Anjum
@ 2026-09-14 13:51 ` Muhammad Usama Anjum
2026-09-18 15:56 ` Ryan Roberts
2026-09-14 13:51 ` [PATCH 4/6] arm64: use hw_pte_t for fixmap HW PTEs Muhammad Usama Anjum
` (2 subsequent siblings)
5 siblings, 1 reply; 11+ messages in thread
From: Muhammad Usama Anjum @ 2026-09-14 13:51 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Mark Rutland, Ard Biesheuvel,
Ilias Apalodimas, Andrey Ryabinin, Alexander Potapenko,
Andrey Konovalov, Dmitry Vyukov, Vincenzo Frascino,
Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko
Cc: linux-arm-kernel, linux-kernel, linux-efi, kasan-dev, linux-mm,
Muhammad Usama Anjum
__ptep_get() returns a SW PTE value, and __set_pte_nosync() accepts one.
When HW PTEs use a distinct hw_pte_t, directly reading or writing *ptep
as a SW PTE value no longer satisfies those interfaces.
Use __pte_from_hw after READ_ONCE() to obtain a SW PTE value and add
__hw_pte before WRITE_ONCE() to form an HW PTE.
Define __hw_pte for both the wrapper and alias configurations so the
same accessor code works in either case.
Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
---
arch/arm64/include/asm/pgtable.h | 4 ++--
include/linux/pgtable_types.h | 2 ++
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 4768ec59de555..67c4a6179e154 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -360,7 +360,7 @@ static inline pte_t pte_clear_uffd(pte_t pte)
static inline void __set_pte_nosync(hw_pte_t *ptep, pte_t pte)
{
- WRITE_ONCE(*ptep, pte);
+ WRITE_ONCE(*ptep, __hw_pte(pte));
}
static inline void __set_pte_complete(pte_t pte)
@@ -381,7 +381,7 @@ static inline void __set_pte(hw_pte_t *ptep, pte_t pte)
static inline pte_t __ptep_get(hw_pte_t *ptep)
{
- return READ_ONCE(*ptep);
+ return __pte_from_hw(READ_ONCE(*ptep));
}
extern void __sync_icache_dcache(pte_t pteval);
diff --git a/include/linux/pgtable_types.h b/include/linux/pgtable_types.h
index ee4eace5c3e1c..b5862a16aa497 100644
--- a/include/linux/pgtable_types.h
+++ b/include/linux/pgtable_types.h
@@ -9,11 +9,13 @@
#ifdef CONFIG_ARCH_HAS_HW_PTE_T
typedef struct __hw_pte_t { pte_t __pte; } hw_pte_t;
#define __pte_from_hw(pte) ((pte).__pte)
+#define __hw_pte(pte) ((hw_pte_t) { (pte) })
#define hw_pte_val(x) pte_val((x).__pte)
#else
#define hw_pte_t pte_t
#define __pte_from_hw(pte) (pte)
+#define __hw_pte(pte) (pte)
#define hw_pte_val(x) pte_val(x)
#endif
--
2.47.3
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 4/6] arm64: use hw_pte_t for fixmap HW PTEs
2026-09-14 13:51 [PATCH 0/6] arm64: distinguish HW PTE pointers from SW PTE value pointers Muhammad Usama Anjum
` (2 preceding siblings ...)
2026-09-14 13:51 ` [PATCH 3/6] arm64: convert between HW PTEs and SW PTE values Muhammad Usama Anjum
@ 2026-09-14 13:51 ` Muhammad Usama Anjum
2026-09-18 16:03 ` Ryan Roberts
2026-09-14 13:51 ` [PATCH 5/6] arm64: use HW PTE accessors in early map_range() Muhammad Usama Anjum
2026-09-14 13:51 ` [PATCH 6/6] arm64: enable a distinct type for HW PTEs Muhammad Usama Anjum
5 siblings, 1 reply; 11+ messages in thread
From: Muhammad Usama Anjum @ 2026-09-14 13:51 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Mark Rutland, Ard Biesheuvel,
Ilias Apalodimas, Andrey Ryabinin, Alexander Potapenko,
Andrey Konovalov, Dmitry Vyukov, Vincenzo Frascino,
Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko
Cc: linux-arm-kernel, linux-kernel, linux-efi, kasan-dev, linux-mm,
Muhammad Usama Anjum
fixmap_pte() returns a pointer into bm_pte, and early_fixmap_init_pte()
installs those arrays as page tables. Their elements are therefore
HW PTEs.
Change the element type of bm_pte to hw_pte_t so it matches the HW PTE
pointers returned and passed to the accessors. This is needed before
ARCH_HAS_HW_PTE_T makes HW PTEs and SW PTE values distinct types; the
array dimensions and placement are unchanged.
Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
---
arch/arm64/mm/fixmap.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/mm/fixmap.c b/arch/arm64/mm/fixmap.c
index 237a9136bc73b..709a97fe327d8 100644
--- a/arch/arm64/mm/fixmap.c
+++ b/arch/arm64/mm/fixmap.c
@@ -31,7 +31,7 @@ static_assert(NR_BM_PMD_TABLES == 1);
#define BM_PTE_TABLE_IDX(addr) __BM_TABLE_IDX(addr, PMD_SHIFT)
-static pte_t bm_pte[NR_BM_PTE_TABLES][PTRS_PER_PTE] __bss_pgtbl;
+static hw_pte_t bm_pte[NR_BM_PTE_TABLES][PTRS_PER_PTE] __bss_pgtbl;
static pmd_t bm_pmd[PTRS_PER_PMD] __bss_pgtbl __maybe_unused;
static pud_t bm_pud[PTRS_PER_PUD] __bss_pgtbl __maybe_unused;
--
2.47.3
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 5/6] arm64: use HW PTE accessors in early map_range()
2026-09-14 13:51 [PATCH 0/6] arm64: distinguish HW PTE pointers from SW PTE value pointers Muhammad Usama Anjum
` (3 preceding siblings ...)
2026-09-14 13:51 ` [PATCH 4/6] arm64: use hw_pte_t for fixmap HW PTEs Muhammad Usama Anjum
@ 2026-09-14 13:51 ` Muhammad Usama Anjum
2026-09-14 13:51 ` [PATCH 6/6] arm64: enable a distinct type for HW PTEs Muhammad Usama Anjum
5 siblings, 0 replies; 11+ messages in thread
From: Muhammad Usama Anjum @ 2026-09-14 13:51 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Mark Rutland, Ard Biesheuvel,
Ilias Apalodimas, Andrey Ryabinin, Alexander Potapenko,
Andrey Konovalov, Dmitry Vyukov, Vincenzo Frascino,
Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko
Cc: linux-arm-kernel, linux-kernel, linux-efi, kasan-dev, linux-mm,
Muhammad Usama Anjum
map_range() uses HW PTE pointers to build early translation tables at
each level. Directly testing, decoding, or assigning *tbl treats an
HW PTE as a SW PTE value, which requires explicit conversion once
hw_pte_t becomes distinct.
Use __ptep_get() to load a SW PTE value for the empty-entry check and
child-table address, and __set_pte_nosync() to convert SW PTE values to
HW PTEs on stores. The no-sync helper performs WRITE_ONCE() without
adding the completion barriers used by __set_pte().
Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
---
arch/arm64/kernel/pi/map_range.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/kernel/pi/map_range.c b/arch/arm64/kernel/pi/map_range.c
index 94f06da76e333..9c4c8060c5df4 100644
--- a/arch/arm64/kernel/pi/map_range.c
+++ b/arch/arm64/kernel/pi/map_range.c
@@ -51,19 +51,21 @@ void __init map_range(phys_addr_t *pte, u64 start, u64 end, phys_addr_t pa,
while (start < end) {
u64 next = min((start | lmask) + 1, PAGE_ALIGN(end));
+ pte_t entry = __ptep_get(tbl);
if (level < 2 || (level == 2 && (start | next | pa) & lmask)) {
/*
* This chunk needs a finer grained mapping. Create a
* table mapping if necessary and recurse.
*/
- if (pte_none(*tbl)) {
- *tbl = __pte(__phys_to_pte_val(*pte) |
+ if (pte_none(entry)) {
+ entry = __pte(__phys_to_pte_val(*pte) |
PMD_TYPE_TABLE | PMD_TABLE_UXN);
- *pte += PTRS_PER_PTE * sizeof(pte_t);
+ __set_pte_nosync(tbl, entry);
+ *pte += PTRS_PER_PTE * sizeof(hw_pte_t);
}
map_range(pte, start, next, pa, prot, level + 1,
- (hw_pte_t *)(__pte_to_phys(*tbl) + va_offset),
+ (hw_pte_t *)(__pte_to_phys(entry) + va_offset),
may_use_cont, va_offset);
} else {
/*
@@ -81,7 +83,8 @@ void __init map_range(phys_addr_t *pte, u64 start, u64 end, phys_addr_t pa,
protval &= ~PTE_CONT;
/* Put down a block or page mapping */
- *tbl = __pte(__phys_to_pte_val(pa) | protval);
+ entry = __pte(__phys_to_pte_val(pa) | protval);
+ __set_pte_nosync(tbl, entry);
}
pa += next - start;
start = next;
--
2.47.3
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 6/6] arm64: enable a distinct type for HW PTEs
2026-09-14 13:51 [PATCH 0/6] arm64: distinguish HW PTE pointers from SW PTE value pointers Muhammad Usama Anjum
` (4 preceding siblings ...)
2026-09-14 13:51 ` [PATCH 5/6] arm64: use HW PTE accessors in early map_range() Muhammad Usama Anjum
@ 2026-09-14 13:51 ` Muhammad Usama Anjum
5 siblings, 0 replies; 11+ messages in thread
From: Muhammad Usama Anjum @ 2026-09-14 13:51 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Mark Rutland, Ard Biesheuvel,
Ilias Apalodimas, Andrey Ryabinin, Alexander Potapenko,
Andrey Konovalov, Dmitry Vyukov, Vincenzo Frascino,
Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko
Cc: linux-arm-kernel, linux-kernel, linux-efi, kasan-dev, linux-mm,
Muhammad Usama Anjum
With HW PTE pointers and direct HW PTE accesses converted, arm64 can use
the generic hw_pte_t wrapper instead of the pte_t alias.
Select ARCH_HAS_HW_PTE_T to give HW PTEs and SW PTE values distinct C
types.
Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
---
arch/arm64/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 2bbeded33da0d..42841d5e2ed26 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -31,6 +31,7 @@ config ARM64
select ARCH_HAS_FORTIFY_SOURCE
select ARCH_HAS_GCOV_PROFILE_ALL
select ARCH_HAS_GIGANTIC_PAGE
+ select ARCH_HAS_HW_PTE_T
select ARCH_HAS_KCOV
select ARCH_HAS_KERNEL_FPU_SUPPORT if KERNEL_MODE_NEON
select ARCH_HAS_KEEPINITRD
--
2.47.3
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/6] arm64: use hw_pte_val for HW PTE atomics
2026-09-14 13:51 ` [PATCH 2/6] arm64: use hw_pte_val for HW PTE atomics Muhammad Usama Anjum
@ 2026-09-18 15:55 ` Ryan Roberts
0 siblings, 0 replies; 11+ messages in thread
From: Ryan Roberts @ 2026-09-18 15:55 UTC (permalink / raw)
To: Muhammad Usama Anjum, Catalin Marinas, Will Deacon, Mark Rutland,
Ard Biesheuvel, Ilias Apalodimas, Andrey Ryabinin,
Alexander Potapenko, Andrey Konovalov, Dmitry Vyukov,
Vincenzo Frascino, Andrew Morton, David Hildenbrand,
Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko
Cc: linux-arm-kernel, linux-kernel, linux-efi, kasan-dev, linux-mm
On 14/09/2026 14:51, Muhammad Usama Anjum wrote:
> Add hw_pte_val() to preserve an lvalue for the HW PTE bits, so atomic
> updates can take their address. pte_val() expects a SW PTE value and
> cannot operate directly on a distinct hw_pte_t.
>
> With ARCH_HAS_HW_PTE_T, use pte_val() on the wrapper's __pte member;
> otherwise, use pte_val() directly.
>
> The atomic operations and their ordering are unchanged.
>
> Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
> ---
> arch/arm64/include/asm/pgtable.h | 10 +++++-----
> arch/arm64/mm/fault.c | 6 +++---
> include/linux/pgtable_types.h | 4 ++++
> 3 files changed, 12 insertions(+), 8 deletions(-)
>
> diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
> index 652ce413be389..4768ec59de555 100644
> --- a/arch/arm64/include/asm/pgtable.h
> +++ b/arch/arm64/include/asm/pgtable.h
> @@ -1303,7 +1303,7 @@ static inline bool __ptep_test_and_clear_young(struct vm_area_struct *vma,
> do {
> old_pte = pte;
> pte = pte_mkold(pte);
> - pte_val(pte) = cmpxchg_relaxed(&pte_val(*ptep),
> + pte_val(pte) = cmpxchg_relaxed(&hw_pte_val(*ptep),
> pte_val(old_pte), pte_val(pte));
> } while (pte_val(pte) != pte_val(old_pte));
>
> @@ -1346,7 +1346,7 @@ static inline pte_t __ptep_get_and_clear_anysz(struct mm_struct *mm,
> hw_pte_t *ptep,
> unsigned long pgsize)
> {
> - pte_t pte = __pte(xchg_relaxed(&pte_val(*ptep), 0));
> + pte_t pte = __pte(xchg_relaxed(&hw_pte_val(*ptep), 0));
>
> switch (pgsize) {
> case PAGE_SIZE:
> @@ -1422,7 +1422,7 @@ static inline void ___ptep_set_wrprotect(struct mm_struct *mm,
> do {
> old_pte = pte;
> pte = pte_wrprotect(pte);
> - pte_val(pte) = cmpxchg_relaxed(&pte_val(*ptep),
> + pte_val(pte) = cmpxchg_relaxed(&hw_pte_val(*ptep),
> pte_val(old_pte), pte_val(pte));
> } while (pte_val(pte) != pte_val(old_pte));
> }
> @@ -1460,7 +1460,7 @@ static inline void __clear_young_dirty_pte(struct vm_area_struct *vma,
> if (flags & CYDP_CLEAR_DIRTY)
> pte = pte_mkclean(pte);
>
> - pte_val(pte) = cmpxchg_relaxed(&pte_val(*ptep),
> + pte_val(pte) = cmpxchg_relaxed(&hw_pte_val(*ptep),
> pte_val(old_pte), pte_val(pte));
> } while (pte_val(pte) != pte_val(old_pte));
> }
> @@ -1830,7 +1830,7 @@ static inline bool ptep_try_set(hw_pte_t *ptep, pte_t new_pte)
> {
> pteval_t old = 0;
>
> - if (!try_cmpxchg(&pte_val(*ptep), &old, pte_val(new_pte)))
> + if (!try_cmpxchg(&hw_pte_val(*ptep), &old, pte_val(new_pte)))
> return false;
>
> /*
> diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
> index b77f4be88e3ea..7d6c30f27214e 100644
> --- a/arch/arm64/mm/fault.c
> +++ b/arch/arm64/mm/fault.c
> @@ -225,8 +225,8 @@ int __ptep_set_access_flags_anysz(struct vm_area_struct *vma,
> /*
> * Setting the flags must be done atomically to avoid racing with the
> * hardware update of the access/dirty state. The PTE_RDONLY bit must
> - * be set to the most permissive (lowest value) of *ptep and entry
> - * (calculated as: a & b == ~(~a | ~b)).
> + * be set to the most permissive (lowest value) of the current PTE and
> + * entry (calculated as: a & b == ~(~a | ~b)).
This seems like an unrelated and unecessary comment change?
> */
> pte_val(entry) ^= PTE_RDONLY;
> pteval = pte_val(pte);
> @@ -235,7 +235,7 @@ int __ptep_set_access_flags_anysz(struct vm_area_struct *vma,
> pteval ^= PTE_RDONLY;
> pteval |= pte_val(entry);
> pteval ^= PTE_RDONLY;
> - pteval = cmpxchg_relaxed(&pte_val(*ptep), old_pteval, pteval);
> + pteval = cmpxchg_relaxed(&hw_pte_val(*ptep), old_pteval, pteval);
> } while (pteval != old_pteval);
>
> /*
> diff --git a/include/linux/pgtable_types.h b/include/linux/pgtable_types.h
> index d6c5a7548550b..ee4eace5c3e1c 100644
> --- a/include/linux/pgtable_types.h
> +++ b/include/linux/pgtable_types.h
> @@ -9,9 +9,13 @@
> #ifdef CONFIG_ARCH_HAS_HW_PTE_T
> typedef struct __hw_pte_t { pte_t __pte; } hw_pte_t;
> #define __pte_from_hw(pte) ((pte).__pte)
> +
nit: why the newline here (and equivalent below)?
> +#define hw_pte_val(x) pte_val((x).__pte)
Wouldn't it be better to add these as part of the generic series? I know we
prefer to add an api along with its first user, but in this case it seems odd,
because you're effectively requiring that arm64 is the first merged arch to
support this? You could also use the same argument to say that none of this
should be merged until the commit where an arch turns on ARCH_HAS_HW_PTE_T.
Thanks,
Ryan
> #else
> #define hw_pte_t pte_t
> #define __pte_from_hw(pte) (pte)
> +
> +#define hw_pte_val(x) pte_val(x)
> #endif
>
> #endif /* !__ASSEMBLY__ */
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 3/6] arm64: convert between HW PTEs and SW PTE values
2026-09-14 13:51 ` [PATCH 3/6] arm64: convert between HW PTEs and SW PTE values Muhammad Usama Anjum
@ 2026-09-18 15:56 ` Ryan Roberts
0 siblings, 0 replies; 11+ messages in thread
From: Ryan Roberts @ 2026-09-18 15:56 UTC (permalink / raw)
To: Muhammad Usama Anjum, Catalin Marinas, Will Deacon, Mark Rutland,
Ard Biesheuvel, Ilias Apalodimas, Andrey Ryabinin,
Alexander Potapenko, Andrey Konovalov, Dmitry Vyukov,
Vincenzo Frascino, Andrew Morton, David Hildenbrand,
Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko
Cc: linux-arm-kernel, linux-kernel, linux-efi, kasan-dev, linux-mm
On 14/09/2026 14:51, Muhammad Usama Anjum wrote:
> __ptep_get() returns a SW PTE value, and __set_pte_nosync() accepts one.
> When HW PTEs use a distinct hw_pte_t, directly reading or writing *ptep
> as a SW PTE value no longer satisfies those interfaces.
>
> Use __pte_from_hw after READ_ONCE() to obtain a SW PTE value and add
> __hw_pte before WRITE_ONCE() to form an HW PTE.
>
> Define __hw_pte for both the wrapper and alias configurations so the
> same accessor code works in either case.
>
> Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
> ---
> arch/arm64/include/asm/pgtable.h | 4 ++--
> include/linux/pgtable_types.h | 2 ++
> 2 files changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
> index 4768ec59de555..67c4a6179e154 100644
> --- a/arch/arm64/include/asm/pgtable.h
> +++ b/arch/arm64/include/asm/pgtable.h
> @@ -360,7 +360,7 @@ static inline pte_t pte_clear_uffd(pte_t pte)
>
> static inline void __set_pte_nosync(hw_pte_t *ptep, pte_t pte)
> {
> - WRITE_ONCE(*ptep, pte);
> + WRITE_ONCE(*ptep, __hw_pte(pte));
> }
>
> static inline void __set_pte_complete(pte_t pte)
> @@ -381,7 +381,7 @@ static inline void __set_pte(hw_pte_t *ptep, pte_t pte)
>
> static inline pte_t __ptep_get(hw_pte_t *ptep)
> {
> - return READ_ONCE(*ptep);
> + return __pte_from_hw(READ_ONCE(*ptep));
> }
>
> extern void __sync_icache_dcache(pte_t pteval);
> diff --git a/include/linux/pgtable_types.h b/include/linux/pgtable_types.h
> index ee4eace5c3e1c..b5862a16aa497 100644
> --- a/include/linux/pgtable_types.h
> +++ b/include/linux/pgtable_types.h
> @@ -9,11 +9,13 @@
> #ifdef CONFIG_ARCH_HAS_HW_PTE_T
> typedef struct __hw_pte_t { pte_t __pte; } hw_pte_t;
> #define __pte_from_hw(pte) ((pte).__pte)
> +#define __hw_pte(pte) ((hw_pte_t) { (pte) })
same comment as for patch 2; wouldn't this be better off in the generic series?
Thanks,
Ryan
>
> #define hw_pte_val(x) pte_val((x).__pte)
> #else
> #define hw_pte_t pte_t
> #define __pte_from_hw(pte) (pte)
> +#define __hw_pte(pte) (pte)
>
> #define hw_pte_val(x) pte_val(x)
> #endif
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 4/6] arm64: use hw_pte_t for fixmap HW PTEs
2026-09-14 13:51 ` [PATCH 4/6] arm64: use hw_pte_t for fixmap HW PTEs Muhammad Usama Anjum
@ 2026-09-18 16:03 ` Ryan Roberts
2026-09-18 20:00 ` David Hildenbrand (Arm)
0 siblings, 1 reply; 11+ messages in thread
From: Ryan Roberts @ 2026-09-18 16:03 UTC (permalink / raw)
To: Muhammad Usama Anjum, Catalin Marinas, Will Deacon, Mark Rutland,
Ard Biesheuvel, Ilias Apalodimas, Andrey Ryabinin,
Alexander Potapenko, Andrey Konovalov, Dmitry Vyukov,
Vincenzo Frascino, Andrew Morton, David Hildenbrand,
Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko
Cc: linux-arm-kernel, linux-kernel, linux-efi, kasan-dev, linux-mm
On 14/09/2026 14:51, Muhammad Usama Anjum wrote:
> fixmap_pte() returns a pointer into bm_pte, and early_fixmap_init_pte()
> installs those arrays as page tables. Their elements are therefore
> HW PTEs.
>
> Change the element type of bm_pte to hw_pte_t so it matches the HW PTE
> pointers returned and passed to the accessors. This is needed before
> ARCH_HAS_HW_PTE_T makes HW PTEs and SW PTE values distinct types; the
> array dimensions and placement are unchanged.
>
> Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
> ---
> arch/arm64/mm/fixmap.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/arm64/mm/fixmap.c b/arch/arm64/mm/fixmap.c
> index 237a9136bc73b..709a97fe327d8 100644
> --- a/arch/arm64/mm/fixmap.c
> +++ b/arch/arm64/mm/fixmap.c
> @@ -31,7 +31,7 @@ static_assert(NR_BM_PMD_TABLES == 1);
>
> #define BM_PTE_TABLE_IDX(addr) __BM_TABLE_IDX(addr, PMD_SHIFT)
>
> -static pte_t bm_pte[NR_BM_PTE_TABLES][PTRS_PER_PTE] __bss_pgtbl;
> +static hw_pte_t bm_pte[NR_BM_PTE_TABLES][PTRS_PER_PTE] __bss_pgtbl;
I think in my original proposal it was impossible to have a hw_pte value; only a
hw_pte pointer was possible. Being able to create hw_pte values means that it is
possible that a hw_pte_t pointer is not actually pointing to an entry in a HW
pgtable. The main motivation for this is that we want to dereference neighbours
of a hw pte based on it's pointer and be confident that it is safe. I think this
removes some of the safety.
Clearly in this instance, bm_pte is still defined such that we have an aligned
page worth of ptes, so its ok. I'm just concerned about the potential for
changes that don't follow the rules (and don't get picked up by the compiler) in
future.
I guess that's the trade off for having something that looks like a pointer
instead of an opaque handle.
Thanks,
Ryan
> static pmd_t bm_pmd[PTRS_PER_PMD] __bss_pgtbl __maybe_unused;
> static pud_t bm_pud[PTRS_PER_PUD] __bss_pgtbl __maybe_unused;
>
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 4/6] arm64: use hw_pte_t for fixmap HW PTEs
2026-09-18 16:03 ` Ryan Roberts
@ 2026-09-18 20:00 ` David Hildenbrand (Arm)
0 siblings, 0 replies; 11+ messages in thread
From: David Hildenbrand (Arm) @ 2026-09-18 20:00 UTC (permalink / raw)
To: Ryan Roberts, Muhammad Usama Anjum, Catalin Marinas, Will Deacon,
Mark Rutland, Ard Biesheuvel, Ilias Apalodimas, Andrey Ryabinin,
Alexander Potapenko, Andrey Konovalov, Dmitry Vyukov,
Vincenzo Frascino, Andrew Morton, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko
Cc: linux-arm-kernel, linux-kernel, linux-efi, kasan-dev, linux-mm
On 9/18/26 18:03, Ryan Roberts wrote:
> On 14/09/2026 14:51, Muhammad Usama Anjum wrote:
>> fixmap_pte() returns a pointer into bm_pte, and early_fixmap_init_pte()
>> installs those arrays as page tables. Their elements are therefore
>> HW PTEs.
>>
>> Change the element type of bm_pte to hw_pte_t so it matches the HW PTE
>> pointers returned and passed to the accessors. This is needed before
>> ARCH_HAS_HW_PTE_T makes HW PTEs and SW PTE values distinct types; the
>> array dimensions and placement are unchanged.
>>
>> Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
>> ---
>> arch/arm64/mm/fixmap.c | 2 +-
>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/arch/arm64/mm/fixmap.c b/arch/arm64/mm/fixmap.c
>> index 237a9136bc73b..709a97fe327d8 100644
>> --- a/arch/arm64/mm/fixmap.c
>> +++ b/arch/arm64/mm/fixmap.c
>> @@ -31,7 +31,7 @@ static_assert(NR_BM_PMD_TABLES == 1);
>>
>> #define BM_PTE_TABLE_IDX(addr) __BM_TABLE_IDX(addr, PMD_SHIFT)
>>
>> -static pte_t bm_pte[NR_BM_PTE_TABLES][PTRS_PER_PTE] __bss_pgtbl;
>> +static hw_pte_t bm_pte[NR_BM_PTE_TABLES][PTRS_PER_PTE] __bss_pgtbl;
>
> I think in my original proposal it was impossible to have a hw_pte value; only a
> hw_pte pointer was possible. Being able to create hw_pte values means that it is
> possible that a hw_pte_t pointer is not actually pointing to an entry in a HW
> pgtable. The main motivation for this is that we want to dereference neighbours
> of a hw pte based on it's pointer and be confident that it is safe. I think this
> removes some of the safety.
If you follow some of the related discussions on the core-mm series, there is a
path forward to go further into that direction.
People instantiating a hw_pte_t on the stack are not really a concern right now.
--
Cheers,
David
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-09-18 20:00 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-14 13:51 [PATCH 0/6] arm64: distinguish HW PTE pointers from SW PTE value pointers Muhammad Usama Anjum
2026-09-14 13:51 ` [PATCH 1/6] arm64: use hw_pte_t for HW PTE pointers Muhammad Usama Anjum
2026-09-14 13:51 ` [PATCH 2/6] arm64: use hw_pte_val for HW PTE atomics Muhammad Usama Anjum
2026-09-18 15:55 ` Ryan Roberts
2026-09-14 13:51 ` [PATCH 3/6] arm64: convert between HW PTEs and SW PTE values Muhammad Usama Anjum
2026-09-18 15:56 ` Ryan Roberts
2026-09-14 13:51 ` [PATCH 4/6] arm64: use hw_pte_t for fixmap HW PTEs Muhammad Usama Anjum
2026-09-18 16:03 ` Ryan Roberts
2026-09-18 20:00 ` David Hildenbrand (Arm)
2026-09-14 13:51 ` [PATCH 5/6] arm64: use HW PTE accessors in early map_range() Muhammad Usama Anjum
2026-09-14 13:51 ` [PATCH 6/6] arm64: enable a distinct type for HW PTEs Muhammad Usama Anjum
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®