// SPDX-License-Identifier: GPL-2.0-only/* * This kernel test validates architecture page table helpers and * accessors and helps in verifying their continued compliance with * expected generic MM semantics. * * Copyright (C) 2019 ARM Ltd. * * Author: Anshuman Khandual <anshuman.khandual@arm.com> */#define pr_fmt(fmt) "debug_vm_pgtable: [%-25s]: " fmt, __func__ #include <linux/gfp.h>#include <linux/highmem.h>#include <linux/hugetlb.h>#include <linux/kernel.h>#include <linux/kconfig.h>#include <linux/mm.h>#include <linux/mman.h>#include <linux/mm_types.h>#include <linux/module.h>#include <linux/pfn_t.h>#include <linux/printk.h>#include <linux/pgtable.h>#include <linux/random.h>#include <linux/spinlock.h>#include <linux/swap.h>#include <linux/swapops.h>#include <linux/start_kernel.h>#include <linux/sched/mm.h>#include <linux/io.h> #include <asm/cacheflush.h>#include <asm/pgalloc.h>#include <asm/tlbflush.h> /* * Please refer Documentation/vm/arch_pgtable_helpers.rst for the semantics * expectations that are being validated here. All future changes in here * or the documentation need to be in sync. */ #define VMFLAGS (VM_READ|VM_WRITE|VM_EXEC) /* * On s390 platform, the lower 4 bits are used to identify given page table * entry type. But these bits might affect the ability to clear entries with * pxx_clear() because of how dynamic page table folding works on s390. So * while loading up the entries do not change the lower 4 bits. It does not * have affect any other platform. Also avoid the 62nd bit on ppc64 that is * used to mark a pte entry. */#define S390_SKIP_MASK GENMASK(3, 0)#if __BITS_PER_LONG == 64#define PPC64_SKIP_MASK GENMASK(62, 62)#else#define PPC64_SKIP_MASK 0x0#endif#define ARCH_SKIP_MASK (S390_SKIP_MASK | PPC64_SKIP_MASK)#define RANDOM_ORVALUE (GENMASK(BITS_PER_LONG - 1, 0) & ~ARCH_SKIP_MASK)#define RANDOM_NZVALUE GENMASK(7, 0) struct pgtable_debug_args { struct mm_struct *mm; struct vm_area_struct *vma; pgd_t *pgdp; p4d_t *p4dp; pud_t *pudp; pmd_t *pmdp; pte_t *ptep; p4d_t *start_p4dp; pud_t *start_pudp; pmd_t *start_pmdp; pgtable_t start_ptep; unsigned long vaddr; pgprot_t page_prot; pgprot_t page_prot_none; bool is_contiguous_page; unsigned long pud_pfn; unsigned long pmd_pfn; unsigned long pte_pfn; unsigned long fixed_pgd_pfn; unsigned long fixed_p4d_pfn; unsigned long fixed_pud_pfn; unsigned long fixed_pmd_pfn; unsigned long fixed_pte_pfn;}; static void __init pte_basic_tests(struct pgtable_debug_args *args, int idx){ pgprot_t prot = protection_map[idx]; pte_t pte = pfn_pte(args->fixed_pte_pfn, prot); unsigned long val = idx, *ptr = &val; pr_debug("Validating PTE basic (%pGv)\n", ptr); /* * This test needs to be executed after the given page table entry * is created with pfn_pte() to make sure that protection_map[idx] * does not have the dirty bit enabled from the beginning. This is * important for platforms like arm64 where (!PTE_RDONLY) indicate * dirty bit being set. */ WARN_ON(pte_dirty(pte_wrprotect(pte))); WARN_ON(!pte_same(pte, pte)); WARN_ON(!pte_young(pte_mkyoung(pte_mkold(pte)))); WARN_ON(!pte_dirty(pte_mkdirty(pte_mkclean(pte)))); WARN_ON(!pte_write(pte_mkwrite(pte_wrprotect(pte)))); WARN_ON(pte_young(pte_mkold(pte_mkyoung(pte)))); WARN_ON(pte_dirty(pte_mkclean(pte_mkdirty(pte)))); WARN_ON(pte_write(pte_wrprotect(pte_mkwrite(pte)))); WARN_ON(pte_dirty(pte_wrprotect(pte_mkclean(pte)))); WARN_ON(!pte_dirty(pte_wrprotect(pte_mkdirty(pte))));} static void __init pte_advanced_tests(struct pgtable_debug_args *args){ struct page *page; pte_t pte; /* * Architectures optimize set_pte_at by avoiding TLB flush. * This requires set_pte_at to be not used to update an * existing pte entry. Clear pte before we do set_pte_at * * flush_dcache_page() is called after set_pte_at() to clear * PG_arch_1 for the page on ARM64. The page flag isn't cleared * when it's released and page allocation check will fail when * the page is allocated again. For architectures other than ARM64, * the unexpected overhead of cache flushing is acceptable. */ page = (args->pte_pfn != ULONG_MAX) ? pfn_to_page(args->pte_pfn) : NULL; if (!page) return; pr_debug("Validating PTE advanced\n"); pte = pfn_pte(args->pte_pfn, args->page_prot); set_pte_at(args->mm, args->vaddr, args->ptep, pte); flush_dcache_page(page); ptep_set_wrprotect(args->mm, args->vaddr, args->ptep); pte = ptep_get(args->ptep); WARN_ON(pte_write(pte)); ptep_get_and_clear(args->mm, args->vaddr, args->ptep); pte = ptep_get(args->ptep); WARN_ON(!pte_none(pte)); pte = pfn_pte(args->pte_pfn, args->page_prot); pte = pte_wrprotect(pte); pte = pte_mkclean(pte); set_pte_at(args->mm, args->vaddr, args->ptep, pte); flush_dcache_page(page); pte = pte_mkwrite(pte); pte = pte_mkdirty(pte); ptep_set_access_flags(args->vma, args->vaddr, args->ptep, pte, 1); pte = ptep_get(args->ptep); WARN_ON(!(pte_write(pte) && pte_dirty(pte))); ptep_get_and_clear_full(args->mm, args->vaddr, args->ptep, 1); pte = ptep_get(args->ptep); WARN_ON(!pte_none(pte)); pte = pfn_pte(args->pte_pfn, args->page_prot); pte = pte_mkyoung(pte); set_pte_at(args->mm, args->vaddr, args->ptep, pte); flush_dcache_page(page); ptep_test_and_clear_young(args->vma, args->vaddr, args->ptep); pte = ptep_get(args->ptep); WARN_ON(pte_young(pte)); ptep_get_and_clear_full(args->mm, args->vaddr, args->ptep, 1);} static void __init pte_savedwrite_tests(struct pgtable_debug_args *args){ pte_t pte = pfn_pte(args->fixed_pte_pfn, args->page_prot_none); if (!IS_ENABLED(CONFIG_NUMA_BALANCING)) return; pr_debug("Validating PTE saved write\n"); WARN_ON(!pte_savedwrite(pte_mk_savedwrite(pte_clear_savedwrite(pte)))); WARN_ON(pte_savedwrite(pte_clear_savedwrite(pte_mk_savedwrite(pte))));} #ifdef CONFIG_TRANSPARENT_HUGEPAGEstatic void __init pmd_basic_tests(struct pgtable_debug_args *args, int idx){ pgprot_t prot = protection_map[idx]; unsigned long val = idx, *ptr = &val; pmd_t pmd; if (!has_transparent_hugepage()) return; pr_debug("Validating PMD basic (%pGv)\n", ptr); pmd = pfn_pmd(args->fixed_pmd_pfn, prot); /* * This test needs to be executed after the given page table entry * is created with pfn_pmd() to make sure that protection_map[idx] * does not have the dirty bit enabled from the beginning. This is * important for platforms like arm64 where (!PTE_RDONLY) indicate * dirty bit being set. */ WARN_ON(pmd_dirty(pmd_wrprotect(pmd))); WARN_ON(!pmd_same(pmd, pmd)); WARN_ON(!pmd_young(pmd_mkyoung(pmd_mkold(pmd)))); WARN_ON(!pmd_dirty(pmd_mkdirty(pmd_mkclean(pmd)))); WARN_ON(!pmd_write(pmd_mkwrite(pmd_wrprotect(pmd)))); WARN_ON(pmd_young(pmd_mkold(pmd_mkyoung(pmd)))); WARN_ON(pmd_dirty(pmd_mkclean(pmd_mkdirty(pmd)))); WARN_ON(pmd_write(pmd_wrprotect(pmd_mkwrite(pmd)))); WARN_ON(pmd_dirty(pmd_wrprotect(pmd_mkclean(pmd)))); WARN_ON(!pmd_dirty(pmd_wrprotect(pmd_mkdirty(pmd)))); /* * A huge page does not point to next level page table * entry. Hence this must qualify as pmd_bad(). */ WARN_ON(!pmd_bad(pmd_mkhuge(pmd)));} static void __init pmd_advanced_tests(struct pgtable_debug_args *args){ struct page *page; pmd_t pmd; unsigned long vaddr = args->vaddr; if (!has_transparent_hugepage()) return; page = (args->pmd_pfn != ULONG_MAX) ? pfn_to_page(args->pmd_pfn) : NULL; if (!page) return; /* * flush_dcache_page() is called after set_pmd_at() to clear * PG_arch_1 for the page on ARM64. The page flag isn't cleared * when it's released and page allocation check will fail when * the page is allocated again. For architectures other than ARM64, * the unexpected overhead of cache flushing is acceptable. */ pr_debug("Validating PMD advanced\n"); /* Align the address wrt HPAGE_PMD_SIZE */ vaddr &= HPAGE_PMD_MASK; pgtable_trans_huge_deposit(args->mm, args->pmdp, args->start_ptep); pmd = pfn_pmd(args->pmd_pfn, args->page_prot); set_pmd_at(args->mm, vaddr, args->pmdp, pmd); flush_dcache_page(page); pmdp_set_wrprotect(args->mm, vaddr, args->pmdp); pmd = READ_ONCE(*args->pmdp); WARN_ON(pmd_write(pmd)); pmdp_huge_get_and_clear(args->mm, vaddr, args->pmdp); pmd = READ_ONCE(*args->pmdp); WARN_ON(!pmd_none(pmd)); pmd = pfn_pmd(args->pmd_pfn, args->page_prot); pmd = pmd_wrprotect(pmd); pmd = pmd_mkclean(pmd); set_pmd_at(args->mm, vaddr, args->pmdp, pmd); flush_dcache_page(page); pmd = pmd_mkwrite(pmd); pmd = pmd_mkdirty(pmd); pmdp_set_access_flags(args->vma, vaddr, args->pmdp, pmd, 1); pmd = READ_ONCE(*args->pmdp); WARN_ON(!(pmd_write(pmd) && pmd_dirty(pmd))); pmdp_huge_get_and_clear_full(args->vma, vaddr, args->pmdp, 1); pmd = READ_ONCE(*args->pmdp); WARN_ON(!pmd_none(pmd)); pmd = pmd_mkhuge(pfn_pmd(args->pmd_pfn, args->page_prot)); pmd = pmd_mkyoung(pmd); set_pmd_at(args->mm, vaddr, args->pmdp, pmd); flush_dcache_page(page); pmdp_test_and_clear_young(args->vma, vaddr, args->pmdp); pmd = READ_ONCE(*args->pmdp); WARN_ON(pmd_young(pmd)); /* Clear the pte entries */ pmdp_huge_get_and_clear(args->mm, vaddr, args->pmdp); pgtable_trans_huge_withdraw(args->mm, args->pmdp);} static void __init pmd_leaf_tests(struct pgtable_debug_args *args){ pmd_t pmd; if (!has_transparent_hugepage()) return; pr_debug("Validating PMD leaf\n"); pmd = pfn_pmd(args->fixed_pmd_pfn, args->page_prot); /* * PMD based THP is a leaf entry. */ pmd = pmd_mkhuge(pmd); WARN_ON(!pmd_leaf(pmd));} static void __init pmd_savedwrite_tests(struct pgtable_debug_args *args){ pmd_t pmd; if (!IS_ENABLED(CONFIG_NUMA_BALANCING)) return; if (!has_transparent_hugepage()) return; pr_debug("Validating PMD saved write\n"); pmd = pfn_pmd(args->fixed_pmd_pfn, args->page_prot_none); WARN_ON(!pmd_savedwrite(pmd_mk_savedwrite(pmd_clear_savedwrite(pmd)))); WARN_ON(pmd_savedwrite(pmd_clear_savedwrite(pmd_mk_savedwrite(pmd))));} #ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUDstatic void __init pud_basic_tests(struct pgtable_debug_args *args, int idx){ pgprot_t prot = protection_map[idx]; unsigned long val = idx, *ptr = &val; pud_t pud; if (!has_transparent_hugepage()) return; pr_debug("Validating PUD basic (%pGv)\n", ptr); pud = pfn_pud(args->fixed_pud_pfn, prot); /* * This test needs to be executed after the given page table entry * is created with pfn_pud() to make sure that protection_map[idx] * does not have the dirty bit enabled from the beginning. This is * important for platforms like arm64 where (!PTE_RDONLY) indicate * dirty bit being set. */ WARN_ON(pud_dirty(pud_wrprotect(pud))); WARN_ON(!pud_same(pud, pud)); WARN_ON(!pud_young(pud_mkyoung(pud_mkold(pud)))); WARN_ON(!pud_dirty(pud_mkdirty(pud_mkclean(pud)))); WARN_ON(pud_dirty(pud_mkclean(pud_mkdirty(pud)))); WARN_ON(!pud_write(pud_mkwrite(pud_wrprotect(pud)))); WARN_ON(pud_write(pud_wrprotect(pud_mkwrite(pud)))); WARN_ON(pud_young(pud_mkold(pud_mkyoung(pud)))); WARN_ON(pud_dirty(pud_wrprotect(pud_mkclean(pud)))); WARN_ON(!pud_dirty(pud_wrprotect(pud_mkdirty(pud)))); if (mm_pmd_folded(args->mm)) return; /* * A huge page does not point to next level page table * entry. Hence this must qualify as pud_bad(). */ WARN_ON(!pud_bad(pud_mkhuge(pud)));} static void __init pud_advanced_tests(struct pgtable_debug_args *args){ struct page *page; unsigned long vaddr = args->vaddr; pud_t pud; if (!has_transparent_hugepage()) return; page = (args->pud_pfn != ULONG_MAX) ? pfn_to_page(args->pud_pfn) : NULL; if (!page) return; /* * flush_dcache_page() is called after set_pud_at() to clear * PG_arch_1 for the page on ARM64. The page flag isn't cleared * when it's released and page allocation check will fail when * the page is allocated again. For architectures other than ARM64, * the unexpected overhead of cache flushing is acceptable. */ pr_debug("Validating PUD advanced\n"); /* Align the address wrt HPAGE_PUD_SIZE */ vaddr &= HPAGE_PUD_MASK; pud = pfn_pud(args->pud_pfn, args->page_prot); set_pud_at(args->mm, vaddr, args->pudp, pud); flush_dcache_page(page); pudp_set_wrprotect(args->mm, vaddr, args->pudp); pud = READ_ONCE(*args->pudp); WARN_ON(pud_write(pud)); #ifndef __PAGETABLE_PMD_FOLDED pudp_huge_get_and_clear(args->mm, vaddr, args->pudp); pud = READ_ONCE(*args->pudp); WARN_ON(!pud_none(pud));#endif /* __PAGETABLE_PMD_FOLDED */ pud = pfn_pud(args->pud_pfn, args->page_prot); pud = pud_wrprotect(pud); pud = pud_mkclean(pud); set_pud_at(args->mm, vaddr, args->pudp, pud); flush_dcache_page(page); pud = pud_mkwrite(pud); pud = pud_mkdirty(pud); pudp_set_access_flags(args->vma, vaddr, args->pudp, pud, 1); pud = READ_ONCE(*args->pudp); WARN_ON(!(pud_write(pud) && pud_dirty(pud))); #ifndef __PAGETABLE_PMD_FOLDED pudp_huge_get_and_clear_full(args->mm, vaddr, args->pudp, 1); pud = READ_ONCE(*args->pudp); WARN_ON(!pud_none(pud));#endif /* __PAGETABLE_PMD_FOLDED */ pud = pfn_pud(args->pud_pfn, args->page_prot); pud = pud_mkyoung(pud); set_pud_at(args->mm, vaddr, args->pudp, pud); flush_dcache_page(page); pudp_test_and_clear_young(args->vma, vaddr, args->pudp); pud = READ_ONCE(*args->pudp); WARN_ON(pud_young(pud)); pudp_huge_get_and_clear(args->mm, vaddr, args->pudp);} static void __init pud_leaf_tests(struct pgtable_debug_args *args){ pud_t pud; if (!has_transparent_hugepage()) return; pr_debug("Validating PUD leaf\n"); pud = pfn_pud(args->fixed_pud_pfn, args->page_prot); /* * PUD based THP is a leaf entry. */ pud = pud_mkhuge(pud); WARN_ON(!pud_leaf(pud));}#else /* !CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */static void __init pud_basic_tests(struct pgtable_debug_args *args, int idx) { }static void __init pud_advanced_tests(struct pgtable_debug_args *args) { }static void __init pud_leaf_tests(struct pgtable_debug_args *args) { }#endif /* CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */#else /* !CONFIG_TRANSPARENT_HUGEPAGE */static void __init pmd_basic_tests(struct pgtable_debug_args *args, int idx) { }static void __init pud_basic_tests(struct pgtable_debug_args *args, int idx) { }static void __init pmd_advanced_tests(struct pgtable_debug_args *args) { }static void __init pud_advanced_tests(struct pgtable_debug_args *args) { }static void __init pmd_leaf_tests(struct pgtable_debug_args *args) { }static void __init pud_leaf_tests(struct pgtable_debug_args *args) { }static void __init pmd_savedwrite_tests(struct pgtable_debug_args *args) { }#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ #ifdef CONFIG_HAVE_ARCH_HUGE_VMAPstatic void __init pmd_huge_tests(struct pgtable_debug_args *args){ pmd_t pmd; if (!arch_vmap_pmd_supported(args->page_prot)) return; pr_debug("Validating PMD huge\n"); /* * X86 defined pmd_set_huge() verifies that the given * PMD is not a populated non-leaf entry. */ WRITE_ONCE(*args->pmdp, __pmd(0)); WARN_ON(!pmd_set_huge(args->pmdp, __pfn_to_phys(args->fixed_pmd_pfn), args->page_prot)); WARN_ON(!pmd_clear_huge(args->pmdp)); pmd = READ_ONCE(*args->pmdp); WARN_ON(!pmd_none(pmd));} static void __init pud_huge_tests(struct pgtable_debug_args *args){ pud_t pud; if (!arch_vmap_pud_supported(args->page_prot)) return; pr_debug("Validating PUD huge\n"); /* * X86 defined pud_set_huge() verifies that the given * PUD is not a populated non-leaf entry. */ WRITE_ONCE(*args->pudp, __pud(0)); WARN_ON(!pud_set_huge(args->pudp, __pfn_to_phys(args->fixed_pud_pfn), args->page_prot)); WARN_ON(!pud_clear_huge(args->pudp)); pud = READ_ONCE(*args->pudp); WARN_ON(!pud_none(pud));}#else /* !CONFIG_HAVE_ARCH_HUGE_VMAP */static void __init pmd_huge_tests(struct pgtable_debug_args *args) { }Lines 1–500 of 1335. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * This kernel test validates architecture page table helpers and * accessors and helps in verifying their continued compliance with * expected generic MM semantics. * * Copyright (C) 2019 ARM Ltd. * * Author: Anshuman Khandual <anshuman.khandual@arm.com> */ #define pr_fmt(fmt) "debug_vm_pgtable: [%-25s]: " fmt, __func__ #include <linux/gfp.h> #include <linux/highmem.h> #include <linux/hugetlb.h> #include <linux/kernel.h> #include <linux/kconfig.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/mm_types.h> #include <linux/module.h> #include <linux/pfn_t.h> #include <linux/printk.h> #include <linux/pgtable.h> #include <linux/random.h> #include <linux/spinlock.h> #include <linux/swap.h> #include <linux/swapops.h> #include <linux/start_kernel.h> #include <linux/sched/mm.h> #include <linux/io.h> #include <asm/cacheflush.h> #include <asm/pgalloc.h> #include <asm/tlbflush.h> /* * Please refer Documentation/vm/arch_pgtable_helpers.rst for the semantics * expectations that are being validated here. All future changes in here * or the documentation need to be in sync. */ #define VMFLAGS (VM_READ|VM_WRITE|VM_EXEC) /* * On s390 platform, the lower 4 bits are used to identify given page table * entry type. But these bits might affect the ability to clear entries with * pxx_clear() because of how dynamic page table folding works on s390. So * while loading up the entries do not change the lower 4 bits. It does not * have affect any other platform. Also avoid the 62nd bit on ppc64 that is * used to mark a pte entry. */ #define S390_SKIP_MASK GENMASK(3, 0) #if __BITS_PER_LONG == 64 #define PPC64_SKIP_MASK GENMASK(62, 62) #else #define PPC64_SKIP_MASK 0x0 #endif #define ARCH_SKIP_MASK (S390_SKIP_MASK | PPC64_SKIP_MASK) #define RANDOM_ORVALUE (GENMASK(BITS_PER_LONG - 1, 0) & ~ARCH_SKIP_MASK) #define RANDOM_NZVALUE GENMASK(7, 0) struct pgtable_debug_args { struct mm_struct *mm; struct vm_area_struct *vma; pgd_t *pgdp; p4d_t *p4dp; pud_t *pudp; pmd_t *pmdp; pte_t *ptep; p4d_t *start_p4dp; pud_t *start_pudp; pmd_t *start_pmdp; pgtable_t start_ptep; unsigned long vaddr; pgprot_t page_prot; pgprot_t page_prot_none; bool is_contiguous_page; unsigned long pud_pfn; unsigned long pmd_pfn; unsigned long pte_pfn; unsigned long fixed_pgd_pfn; unsigned long fixed_p4d_pfn; unsigned long fixed_pud_pfn; unsigned long fixed_pmd_pfn; unsigned long fixed_pte_pfn; }; static void __init pte_basic_tests(struct pgtable_debug_args *args, int idx) { pgprot_t prot = protection_map[idx]; pte_t pte = pfn_pte(args->fixed_pte_pfn, prot); unsigned long val = idx, *ptr = &val; pr_debug("Validating PTE basic (%pGv)\n", ptr); /* * This test needs to be executed after the given page table entry * is created with pfn_pte() to make sure that protection_map[idx] * does not have the dirty bit enabled from the beginning. This is * important for platforms like arm64 where (!PTE_RDONLY) indicate * dirty bit being set. */ WARN_ON(pte_dirty(pte_wrprotect(pte))); WARN_ON(!pte_same(pte, pte)); WARN_ON(!pte_young(pte_mkyoung(pte_mkold(pte)))); WARN_ON(!pte_dirty(pte_mkdirty(pte_mkclean(pte)))); WARN_ON(!pte_write(pte_mkwrite(pte_wrprotect(pte)))); WARN_ON(pte_young(pte_mkold(pte_mkyoung(pte)))); WARN_ON(pte_dirty(pte_mkclean(pte_mkdirty(pte)))); WARN_ON(pte_write(pte_wrprotect(pte_mkwrite(pte)))); WARN_ON(pte_dirty(pte_wrprotect(pte_mkclean(pte)))); WARN_ON(!pte_dirty(pte_wrprotect(pte_mkdirty(pte)))); } static void __init pte_advanced_tests(struct pgtable_debug_args *args) { struct page *page; pte_t pte; /* * Architectures optimize set_pte_at by avoiding TLB flush. * This requires set_pte_at to be not used to update an * existing pte entry. Clear pte before we do set_pte_at * * flush_dcache_page() is called after set_pte_at() to clear * PG_arch_1 for the page on ARM64. The page flag isn't cleared * when it's released and page allocation check will fail when * the page is allocated again. For architectures other than ARM64, * the unexpected overhead of cache flushing is acceptable. */ page = (args->pte_pfn != ULONG_MAX) ? pfn_to_page(args->pte_pfn) : NULL; if (!page) return; pr_debug("Validating PTE advanced\n"); pte = pfn_pte(args->pte_pfn, args->page_prot); set_pte_at(args->mm, args->vaddr, args->ptep, pte); flush_dcache_page(page); ptep_set_wrprotect(args->mm, args->vaddr, args->ptep); pte = ptep_get(args->ptep); WARN_ON(pte_write(pte)); ptep_get_and_clear(args->mm, args->vaddr, args->ptep); pte = ptep_get(args->ptep); WARN_ON(!pte_none(pte)); pte = pfn_pte(args->pte_pfn, args->page_prot); pte = pte_wrprotect(pte); pte = pte_mkclean(pte); set_pte_at(args->mm, args->vaddr, args->ptep, pte); flush_dcache_page(page); pte = pte_mkwrite(pte); pte = pte_mkdirty(pte); ptep_set_access_flags(args->vma, args->vaddr, args->ptep, pte, 1); pte = ptep_get(args->ptep); WARN_ON(!(pte_write(pte) && pte_dirty(pte))); ptep_get_and_clear_full(args->mm, args->vaddr, args->ptep, 1); pte = ptep_get(args->ptep); WARN_ON(!pte_none(pte)); pte = pfn_pte(args->pte_pfn, args->page_prot); pte = pte_mkyoung(pte); set_pte_at(args->mm, args->vaddr, args->ptep, pte); flush_dcache_page(page); ptep_test_and_clear_young(args->vma, args->vaddr, args->ptep); pte = ptep_get(args->ptep); WARN_ON(pte_young(pte)); ptep_get_and_clear_full(args->mm, args->vaddr, args->ptep, 1); } static void __init pte_savedwrite_tests(struct pgtable_debug_args *args) { pte_t pte = pfn_pte(args->fixed_pte_pfn, args->page_prot_none); if (!IS_ENABLED(CONFIG_NUMA_BALANCING)) return; pr_debug("Validating PTE saved write\n"); WARN_ON(!pte_savedwrite(pte_mk_savedwrite(pte_clear_savedwrite(pte)))); WARN_ON(pte_savedwrite(pte_clear_savedwrite(pte_mk_savedwrite(pte)))); } #ifdef CONFIG_TRANSPARENT_HUGEPAGE static void __init pmd_basic_tests(struct pgtable_debug_args *args, int idx) { pgprot_t prot = protection_map[idx]; unsigned long val = idx, *ptr = &val; pmd_t pmd; if (!has_transparent_hugepage()) return; pr_debug("Validating PMD basic (%pGv)\n", ptr); pmd = pfn_pmd(args->fixed_pmd_pfn, prot); /* * This test needs to be executed after the given page table entry * is created with pfn_pmd() to make sure that protection_map[idx] * does not have the dirty bit enabled from the beginning. This is * important for platforms like arm64 where (!PTE_RDONLY) indicate * dirty bit being set. */ WARN_ON(pmd_dirty(pmd_wrprotect(pmd))); WARN_ON(!pmd_same(pmd, pmd)); WARN_ON(!pmd_young(pmd_mkyoung(pmd_mkold(pmd)))); WARN_ON(!pmd_dirty(pmd_mkdirty(pmd_mkclean(pmd)))); WARN_ON(!pmd_write(pmd_mkwrite(pmd_wrprotect(pmd)))); WARN_ON(pmd_young(pmd_mkold(pmd_mkyoung(pmd)))); WARN_ON(pmd_dirty(pmd_mkclean(pmd_mkdirty(pmd)))); WARN_ON(pmd_write(pmd_wrprotect(pmd_mkwrite(pmd)))); WARN_ON(pmd_dirty(pmd_wrprotect(pmd_mkclean(pmd)))); WARN_ON(!pmd_dirty(pmd_wrprotect(pmd_mkdirty(pmd)))); /* * A huge page does not point to next level page table * entry. Hence this must qualify as pmd_bad(). */ WARN_ON(!pmd_bad(pmd_mkhuge(pmd))); } static void __init pmd_advanced_tests(struct pgtable_debug_args *args) { struct page *page; pmd_t pmd; unsigned long vaddr = args->vaddr; if (!has_transparent_hugepage()) return; page = (args->pmd_pfn != ULONG_MAX) ? pfn_to_page(args->pmd_pfn) : NULL; if (!page) return; /* * flush_dcache_page() is called after set_pmd_at() to clear * PG_arch_1 for the page on ARM64. The page flag isn't cleared * when it's released and page allocation check will fail when * the page is allocated again. For architectures other than ARM64, * the unexpected overhead of cache flushing is acceptable. */ pr_debug("Validating PMD advanced\n"); /* Align the address wrt HPAGE_PMD_SIZE */ vaddr &= HPAGE_PMD_MASK; pgtable_trans_huge_deposit(args->mm, args->pmdp, args->start_ptep); pmd = pfn_pmd(args->pmd_pfn, args->page_prot); set_pmd_at(args->mm, vaddr, args->pmdp, pmd); flush_dcache_page(page); pmdp_set_wrprotect(args->mm, vaddr, args->pmdp); pmd = READ_ONCE(*args->pmdp); WARN_ON(pmd_write(pmd)); pmdp_huge_get_and_clear(args->mm, vaddr, args->pmdp); pmd = READ_ONCE(*args->pmdp); WARN_ON(!pmd_none(pmd)); pmd = pfn_pmd(args->pmd_pfn, args->page_prot); pmd = pmd_wrprotect(pmd); pmd = pmd_mkclean(pmd); set_pmd_at(args->mm, vaddr, args->pmdp, pmd); flush_dcache_page(page); pmd = pmd_mkwrite(pmd); pmd = pmd_mkdirty(pmd); pmdp_set_access_flags(args->vma, vaddr, args->pmdp, pmd, 1); pmd = READ_ONCE(*args->pmdp); WARN_ON(!(pmd_write(pmd) && pmd_dirty(pmd))); pmdp_huge_get_and_clear_full(args->vma, vaddr, args->pmdp, 1); pmd = READ_ONCE(*args->pmdp); WARN_ON(!pmd_none(pmd)); pmd = pmd_mkhuge(pfn_pmd(args->pmd_pfn, args->page_prot)); pmd = pmd_mkyoung(pmd); set_pmd_at(args->mm, vaddr, args->pmdp, pmd); flush_dcache_page(page); pmdp_test_and_clear_young(args->vma, vaddr, args->pmdp); pmd = READ_ONCE(*args->pmdp); WARN_ON(pmd_young(pmd)); /* Clear the pte entries */ pmdp_huge_get_and_clear(args->mm, vaddr, args->pmdp); pgtable_trans_huge_withdraw(args->mm, args->pmdp); } static void __init pmd_leaf_tests(struct pgtable_debug_args *args) { pmd_t pmd; if (!has_transparent_hugepage()) return; pr_debug("Validating PMD leaf\n"); pmd = pfn_pmd(args->fixed_pmd_pfn, args->page_prot); /* * PMD based THP is a leaf entry. */ pmd = pmd_mkhuge(pmd); WARN_ON(!pmd_leaf(pmd)); } static void __init pmd_savedwrite_tests(struct pgtable_debug_args *args) { pmd_t pmd; if (!IS_ENABLED(CONFIG_NUMA_BALANCING)) return; if (!has_transparent_hugepage()) return; pr_debug("Validating PMD saved write\n"); pmd = pfn_pmd(args->fixed_pmd_pfn, args->page_prot_none); WARN_ON(!pmd_savedwrite(pmd_mk_savedwrite(pmd_clear_savedwrite(pmd)))); WARN_ON(pmd_savedwrite(pmd_clear_savedwrite(pmd_mk_savedwrite(pmd)))); } #ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD static void __init pud_basic_tests(struct pgtable_debug_args *args, int idx) { pgprot_t prot = protection_map[idx]; unsigned long val = idx, *ptr = &val; pud_t pud; if (!has_transparent_hugepage()) return; pr_debug("Validating PUD basic (%pGv)\n", ptr); pud = pfn_pud(args->fixed_pud_pfn, prot); /* * This test needs to be executed after the given page table entry * is created with pfn_pud() to make sure that protection_map[idx] * does not have the dirty bit enabled from the beginning. This is * important for platforms like arm64 where (!PTE_RDONLY) indicate * dirty bit being set. */ WARN_ON(pud_dirty(pud_wrprotect(pud))); WARN_ON(!pud_same(pud, pud)); WARN_ON(!pud_young(pud_mkyoung(pud_mkold(pud)))); WARN_ON(!pud_dirty(pud_mkdirty(pud_mkclean(pud)))); WARN_ON(pud_dirty(pud_mkclean(pud_mkdirty(pud)))); WARN_ON(!pud_write(pud_mkwrite(pud_wrprotect(pud)))); WARN_ON(pud_write(pud_wrprotect(pud_mkwrite(pud)))); WARN_ON(pud_young(pud_mkold(pud_mkyoung(pud)))); WARN_ON(pud_dirty(pud_wrprotect(pud_mkclean(pud)))); WARN_ON(!pud_dirty(pud_wrprotect(pud_mkdirty(pud)))); if (mm_pmd_folded(args->mm)) return; /* * A huge page does not point to next level page table * entry. Hence this must qualify as pud_bad(). */ WARN_ON(!pud_bad(pud_mkhuge(pud))); } static void __init pud_advanced_tests(struct pgtable_debug_args *args) { struct page *page; unsigned long vaddr = args->vaddr; pud_t pud; if (!has_transparent_hugepage()) return; page = (args->pud_pfn != ULONG_MAX) ? pfn_to_page(args->pud_pfn) : NULL; if (!page) return; /* * flush_dcache_page() is called after set_pud_at() to clear * PG_arch_1 for the page on ARM64. The page flag isn't cleared * when it's released and page allocation check will fail when * the page is allocated again. For architectures other than ARM64, * the unexpected overhead of cache flushing is acceptable. */ pr_debug("Validating PUD advanced\n"); /* Align the address wrt HPAGE_PUD_SIZE */ vaddr &= HPAGE_PUD_MASK; pud = pfn_pud(args->pud_pfn, args->page_prot); set_pud_at(args->mm, vaddr, args->pudp, pud); flush_dcache_page(page); pudp_set_wrprotect(args->mm, vaddr, args->pudp); pud = READ_ONCE(*args->pudp); WARN_ON(pud_write(pud)); #ifndef __PAGETABLE_PMD_FOLDED pudp_huge_get_and_clear(args->mm, vaddr, args->pudp); pud = READ_ONCE(*args->pudp); WARN_ON(!pud_none(pud)); #endif /* __PAGETABLE_PMD_FOLDED */ pud = pfn_pud(args->pud_pfn, args->page_prot); pud = pud_wrprotect(pud); pud = pud_mkclean(pud); set_pud_at(args->mm, vaddr, args->pudp, pud); flush_dcache_page(page); pud = pud_mkwrite(pud); pud = pud_mkdirty(pud); pudp_set_access_flags(args->vma, vaddr, args->pudp, pud, 1); pud = READ_ONCE(*args->pudp); WARN_ON(!(pud_write(pud) && pud_dirty(pud))); #ifndef __PAGETABLE_PMD_FOLDED pudp_huge_get_and_clear_full(args->mm, vaddr, args->pudp, 1); pud = READ_ONCE(*args->pudp); WARN_ON(!pud_none(pud)); #endif /* __PAGETABLE_PMD_FOLDED */ pud = pfn_pud(args->pud_pfn, args->page_prot); pud = pud_mkyoung(pud); set_pud_at(args->mm, vaddr, args->pudp, pud); flush_dcache_page(page); pudp_test_and_clear_young(args->vma, vaddr, args->pudp); pud = READ_ONCE(*args->pudp); WARN_ON(pud_young(pud)); pudp_huge_get_and_clear(args->mm, vaddr, args->pudp); } static void __init pud_leaf_tests(struct pgtable_debug_args *args) { pud_t pud; if (!has_transparent_hugepage()) return; pr_debug("Validating PUD leaf\n"); pud = pfn_pud(args->fixed_pud_pfn, args->page_prot); /* * PUD based THP is a leaf entry. */ pud = pud_mkhuge(pud); WARN_ON(!pud_leaf(pud)); } #else /* !CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */ static void __init pud_basic_tests(struct pgtable_debug_args *args, int idx) { } static void __init pud_advanced_tests(struct pgtable_debug_args *args) { } static void __init pud_leaf_tests(struct pgtable_debug_args *args) { } #endif /* CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */ #else /* !CONFIG_TRANSPARENT_HUGEPAGE */ static void __init pmd_basic_tests(struct pgtable_debug_args *args, int idx) { } static void __init pud_basic_tests(struct pgtable_debug_args *args, int idx) { } static void __init pmd_advanced_tests(struct pgtable_debug_args *args) { } static void __init pud_advanced_tests(struct pgtable_debug_args *args) { } static void __init pmd_leaf_tests(struct pgtable_debug_args *args) { } static void __init pud_leaf_tests(struct pgtable_debug_args *args) { } static void __init pmd_savedwrite_tests(struct pgtable_debug_args *args) { } #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ #ifdef CONFIG_HAVE_ARCH_HUGE_VMAP static void __init pmd_huge_tests(struct pgtable_debug_args *args) { pmd_t pmd; if (!arch_vmap_pmd_supported(args->page_prot)) return; pr_debug("Validating PMD huge\n"); /* * X86 defined pmd_set_huge() verifies that the given * PMD is not a populated non-leaf entry. */ WRITE_ONCE(*args->pmdp, __pmd(0)); WARN_ON(!pmd_set_huge(args->pmdp, __pfn_to_phys(args->fixed_pmd_pfn), args->page_prot)); WARN_ON(!pmd_clear_huge(args->pmdp)); pmd = READ_ONCE(*args->pmdp); WARN_ON(!pmd_none(pmd)); } static void __init pud_huge_tests(struct pgtable_debug_args *args) { pud_t pud; if (!arch_vmap_pud_supported(args->page_prot)) return; pr_debug("Validating PUD huge\n"); /* * X86 defined pud_set_huge() verifies that the given * PUD is not a populated non-leaf entry. */ WRITE_ONCE(*args->pudp, __pud(0)); WARN_ON(!pud_set_huge(args->pudp, __pfn_to_phys(args->fixed_pud_pfn), args->page_prot)); WARN_ON(!pud_clear_huge(args->pudp)); pud = READ_ONCE(*args->pudp); WARN_ON(!pud_none(pud)); } #else /* !CONFIG_HAVE_ARCH_HUGE_VMAP */ static void __init pmd_huge_tests(struct pgtable_debug_args *args) { }