// 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 = vm_get_page_prot(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 vm_get_page_prot(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(!