Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support page table check PowerPC #724

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 9 additions & 9 deletions arch/arm64/include/asm/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ static inline void set_ptes(struct mm_struct *mm,
unsigned long __always_unused addr,
pte_t *ptep, pte_t pte, unsigned int nr)
{
page_table_check_ptes_set(mm, ptep, pte, nr);
page_table_check_ptes_set(mm, addr, ptep, pte, nr);
__sync_cache_and_tags(pte, nr);

for (;;) {
Expand Down Expand Up @@ -540,15 +540,15 @@ static inline void __set_pte_at(struct mm_struct *mm,
static inline void set_pmd_at(struct mm_struct *mm, unsigned long addr,
pmd_t *pmdp, pmd_t pmd)
{
page_table_check_pmd_set(mm, pmdp, pmd);
page_table_check_pmd_set(mm, addr, pmdp, pmd);
return __set_pte_at(mm, addr, (pte_t *)pmdp, pmd_pte(pmd),
PMD_SIZE >> PAGE_SHIFT);
}

static inline void set_pud_at(struct mm_struct *mm, unsigned long addr,
pud_t *pudp, pud_t pud)
{
page_table_check_pud_set(mm, pudp, pud);
page_table_check_pud_set(mm, addr, pudp, pud);
return __set_pte_at(mm, addr, (pte_t *)pudp, pud_pte(pud),
PUD_SIZE >> PAGE_SHIFT);
}
Expand Down Expand Up @@ -874,17 +874,17 @@ static inline int pgd_devmap(pgd_t pgd)
#endif

#ifdef CONFIG_PAGE_TABLE_CHECK
static inline bool pte_user_accessible_page(pte_t pte)
static inline bool pte_user_accessible_page(pte_t pte, unsigned long addr)
{
return pte_present(pte) && (pte_user(pte) || pte_user_exec(pte));
}

static inline bool pmd_user_accessible_page(pmd_t pmd)
static inline bool pmd_user_accessible_page(pmd_t pmd, unsigned long addr)
{
return pmd_leaf(pmd) && !pmd_present_invalid(pmd) && (pmd_user(pmd) || pmd_user_exec(pmd));
}

static inline bool pud_user_accessible_page(pud_t pud)
static inline bool pud_user_accessible_page(pud_t pud, unsigned long addr)
{
return pud_leaf(pud) && (pud_user(pud) || pud_user_exec(pud));
}
Expand Down Expand Up @@ -953,7 +953,7 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
{
pte_t pte = __pte(xchg_relaxed(&pte_val(*ptep), 0));

page_table_check_pte_clear(mm, pte);
page_table_check_pte_clear(mm, address, pte);

return pte;
}
Expand All @@ -965,7 +965,7 @@ static inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm,
{
pmd_t pmd = __pmd(xchg_relaxed(&pmd_val(*pmdp), 0));

page_table_check_pmd_clear(mm, pmd);
page_table_check_pmd_clear(mm, address, pmd);

return pmd;
}
Expand Down Expand Up @@ -1001,7 +1001,7 @@ static inline void pmdp_set_wrprotect(struct mm_struct *mm,
static inline pmd_t pmdp_establish(struct vm_area_struct *vma,
unsigned long address, pmd_t *pmdp, pmd_t pmd)
{
page_table_check_pmd_set(vma->vm_mm, pmdp, pmd);
page_table_check_pmd_set(vma->vm_mm, address, pmdp, pmd);
return __pmd(xchg_relaxed(&pmd_val(*pmdp), pmd_val(pmd)));
}
#endif
Expand Down
1 change: 1 addition & 0 deletions arch/powerpc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ config PPC
select ARCH_STACKWALK
select ARCH_SUPPORTS_ATOMIC_RMW
select ARCH_SUPPORTS_DEBUG_PAGEALLOC if PPC_BOOK3S || PPC_8xx || 40x
select ARCH_SUPPORTS_PAGE_TABLE_CHECK
select ARCH_USE_BUILTIN_BSWAP
select ARCH_USE_CMPXCHG_LOCKREF if PPC64
select ARCH_USE_MEMTEST
Expand Down
7 changes: 6 additions & 1 deletion arch/powerpc/include/asm/book3s/32/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ void unmap_kernel_page(unsigned long va);
#ifndef __ASSEMBLY__
#include <linux/sched.h>
#include <linux/threads.h>
#include <linux/page_table_check.h>

/* Bits to mask out from a PGD to get to the PUD page */
#define PGD_MASKED_BITS 0
Expand Down Expand Up @@ -314,7 +315,11 @@ static inline int __ptep_test_and_clear_young(struct mm_struct *mm,
static inline pte_t ptep_get_and_clear(struct mm_struct *mm, unsigned long addr,
pte_t *ptep)
{
return __pte(pte_update(mm, addr, ptep, ~_PAGE_HASHPTE, 0, 0));
pte_t old_pte = __pte(pte_update(mm, addr, ptep, ~_PAGE_HASHPTE, 0, 0));

page_table_check_pte_clear(mm, addr, old_pte);

return old_pte;
}

#define __HAVE_ARCH_PTEP_SET_WRPROTECT
Expand Down
74 changes: 58 additions & 16 deletions arch/powerpc/include/asm/book3s/64/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@
#define PAGE_KERNEL_ROX __pgprot(_PAGE_BASE | _PAGE_KERNEL_ROX)

#ifndef __ASSEMBLY__
#include <linux/page_table_check.h>

/*
* page table defines
*/
Expand Down Expand Up @@ -415,8 +417,11 @@ static inline void huge_ptep_set_wrprotect(struct mm_struct *mm,
static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
unsigned long addr, pte_t *ptep)
{
unsigned long old = pte_update(mm, addr, ptep, ~0UL, 0, 0);
return __pte(old);
pte_t old_pte = __pte(pte_update(mm, addr, ptep, ~0UL, 0, 0));

page_table_check_pte_clear(mm, addr, old_pte);

return old_pte;
}

#define __HAVE_ARCH_PTEP_GET_AND_CLEAR_FULL
Expand All @@ -425,11 +430,16 @@ static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm,
pte_t *ptep, int full)
{
if (full && radix_enabled()) {
pte_t old_pte;

/*
* We know that this is a full mm pte clear and
* hence can be sure there is no parallel set_pte.
*/
return radix__ptep_get_and_clear_full(mm, addr, ptep, full);
old_pte = radix__ptep_get_and_clear_full(mm, addr, ptep, full);
page_table_check_pte_clear(mm, addr, old_pte);

return old_pte;
}
return ptep_get_and_clear(mm, addr, ptep);
}
Expand Down Expand Up @@ -538,6 +548,12 @@ static inline bool pte_access_permitted(pte_t pte, bool write)
return arch_pte_access_permitted(pte_val(pte), write, 0);
}

#define pte_user_accessible_page pte_user_accessible_page
static inline bool pte_user_accessible_page(pte_t pte, unsigned long addr)
{
return pte_present(pte) && pte_user(pte);
}

/*
* Conversion functions: convert a page and protection to a page entry,
* and a page entry and page directory to the page they refer to.
Expand Down Expand Up @@ -881,6 +897,7 @@ static inline int pud_present(pud_t pud)

extern struct page *pud_page(pud_t pud);
extern struct page *pmd_page(pmd_t pmd);

static inline pte_t pud_pte(pud_t pud)
{
return __pte_raw(pud_raw(pud));
Expand Down Expand Up @@ -926,6 +943,12 @@ static inline bool pud_access_permitted(pud_t pud, bool write)
return pte_access_permitted(pud_pte(pud), write);
}

#define pud_user_accessible_page pud_user_accessible_page
static inline bool pud_user_accessible_page(pud_t pud, unsigned long addr)
{
return pte_user_accessible_page(pud_pte(pud), addr);
}

#define __p4d_raw(x) ((p4d_t) { __pgd_raw(x) })
static inline __be64 p4d_raw(p4d_t x)
{
Expand Down Expand Up @@ -1101,6 +1124,12 @@ static inline bool pmd_access_permitted(pmd_t pmd, bool write)
return pte_access_permitted(pmd_pte(pmd), write);
}

#define pmd_user_accessible_page pmd_user_accessible_page
static inline bool pmd_user_accessible_page(pmd_t pmd, unsigned long addr)
{
return pte_user_accessible_page(pmd_pte(pmd), addr);
}

#ifdef CONFIG_TRANSPARENT_HUGEPAGE
extern pmd_t pfn_pmd(unsigned long pfn, pgprot_t pgprot);
extern pud_t pfn_pud(unsigned long pfn, pgprot_t pgprot);
Expand Down Expand Up @@ -1325,19 +1354,34 @@ extern int pudp_test_and_clear_young(struct vm_area_struct *vma,
static inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm,
unsigned long addr, pmd_t *pmdp)
{
if (radix_enabled())
return radix__pmdp_huge_get_and_clear(mm, addr, pmdp);
return hash__pmdp_huge_get_and_clear(mm, addr, pmdp);
pmd_t old_pmd;

if (radix_enabled()) {
old_pmd = radix__pmdp_huge_get_and_clear(mm, addr, pmdp);
} else {
old_pmd = hash__pmdp_huge_get_and_clear(mm, addr, pmdp);
}

page_table_check_pmd_clear(mm, addr, old_pmd);

return old_pmd;
}

#define __HAVE_ARCH_PUDP_HUGE_GET_AND_CLEAR
static inline pud_t pudp_huge_get_and_clear(struct mm_struct *mm,
unsigned long addr, pud_t *pudp)
{
if (radix_enabled())
return radix__pudp_huge_get_and_clear(mm, addr, pudp);
BUG();
return *pudp;
pud_t old_pud;

if (radix_enabled()) {
old_pud = radix__pudp_huge_get_and_clear(mm, addr, pudp);
} else {
BUG();
}

page_table_check_pud_clear(mm, addr, old_pud);

return old_pud;
}

static inline pmd_t pmdp_collapse_flush(struct vm_area_struct *vma,
Expand Down Expand Up @@ -1453,16 +1497,14 @@ static inline bool is_pte_rw_upgrade(unsigned long old_val, unsigned long new_va
/*
* Like pmd_huge() and pmd_large(), but works regardless of config options
*/
#define pmd_is_leaf pmd_is_leaf
#define pmd_leaf pmd_is_leaf
static inline bool pmd_is_leaf(pmd_t pmd)
#define pmd_leaf pmd_leaf
static inline bool pmd_leaf(pmd_t pmd)
{
return !!(pmd_raw(pmd) & cpu_to_be64(_PAGE_PTE));
}

#define pud_is_leaf pud_is_leaf
#define pud_leaf pud_is_leaf
static inline bool pud_is_leaf(pud_t pud)
#define pud_leaf pud_leaf
static inline bool pud_leaf(pud_t pud)
{
return !!(pud_raw(pud) & cpu_to_be64(_PAGE_PTE));
}
Expand Down
66 changes: 42 additions & 24 deletions arch/powerpc/include/asm/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ struct mm_struct;
void set_ptes(struct mm_struct *mm, unsigned long addr, pte_t *ptep,
pte_t pte, unsigned int nr);
#define set_ptes set_ptes
void set_pte_at_unchecked(struct mm_struct *mm, unsigned long addr,
pte_t *ptep, pte_t pte);
#define update_mmu_cache(vma, addr, ptep) \
update_mmu_cache_range(NULL, vma, addr, ptep, 1)

Expand Down Expand Up @@ -180,30 +182,6 @@ static inline void pte_frag_set(mm_context_t *ctx, void *p)
}
#endif

#ifndef pmd_is_leaf
#define pmd_is_leaf pmd_is_leaf
static inline bool pmd_is_leaf(pmd_t pmd)
{
return false;
}
#endif

#ifndef pud_is_leaf
#define pud_is_leaf pud_is_leaf
static inline bool pud_is_leaf(pud_t pud)
{
return false;
}
#endif

#ifndef p4d_is_leaf
#define p4d_is_leaf p4d_is_leaf
static inline bool p4d_is_leaf(p4d_t p4d)
{
return false;
}
#endif

#define pmd_pgtable pmd_pgtable
static inline pgtable_t pmd_pgtable(pmd_t pmd)
{
Expand Down Expand Up @@ -237,6 +215,46 @@ static inline bool arch_supports_memmap_on_memory(unsigned long vmemmap_size)

#endif /* CONFIG_PPC64 */

/*
* Currently only consumed by page_table_check_pud_{set,clear}. Since clears
* and sets to page table entries at any level are done through
* page_table_check_pte_{set,clear}, provide stub implementation.
*/
#ifndef pud_pfn
#define pud_pfn pud_pfn
static inline int pud_pfn(pud_t pud)
{
WARN_ONCE(1, "pud: platform does not use pud entries directly");
return 0;
}
#endif

#ifndef pte_user_accessible_page
#define pte_user_accessible_page pte_user_accessible_page
static inline bool pte_user_accessible_page(pte_t pte, unsigned long addr)
{
return pte_present(pte) && !is_kernel_addr(addr);
}
#endif

#ifndef pmd_user_accessible_page
#define pmd_user_accessible_page pmd_user_accessible_page
static inline bool pmd_user_accessible_page(pmd_t pmd, unsigned long addr)
{
WARN_ONCE(1, "pmd: platform does not use pmd entries directly");
return false;
}
#endif

#ifndef pud_user_accessible_page
#define pud_user_accessible_page pud_user_accessible_page
static inline bool pud_user_accessible_page(pud_t pud, unsigned long addr)
{
WARN_ONCE(1, "pud: platform does not use pud entries directly");
return false;
}
#endif

#endif /* __ASSEMBLY__ */

#endif /* _ASM_POWERPC_PGTABLE_H */
12 changes: 6 additions & 6 deletions arch/powerpc/kvm/book3s_64_mmu_radix.c
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ static void kvmppc_unmap_free_pmd(struct kvm *kvm, pmd_t *pmd, bool full,
for (im = 0; im < PTRS_PER_PMD; ++im, ++p) {
if (!pmd_present(*p))
continue;
if (pmd_is_leaf(*p)) {
if (pmd_leaf(*p)) {
if (full) {
pmd_clear(p);
} else {
Expand Down Expand Up @@ -532,7 +532,7 @@ static void kvmppc_unmap_free_pud(struct kvm *kvm, pud_t *pud,
for (iu = 0; iu < PTRS_PER_PUD; ++iu, ++p) {
if (!pud_present(*p))
continue;
if (pud_is_leaf(*p)) {
if (pud_leaf(*p)) {
pud_clear(p);
} else {
pmd_t *pmd;
Expand Down Expand Up @@ -635,12 +635,12 @@ int kvmppc_create_pte(struct kvm *kvm, pgd_t *pgtable, pte_t pte,
new_pud = pud_alloc_one(kvm->mm, gpa);

pmd = NULL;
if (pud && pud_present(*pud) && !pud_is_leaf(*pud))
if (pud && pud_present(*pud) && !pud_leaf(*pud))
pmd = pmd_offset(pud, gpa);
else if (level <= 1)
new_pmd = kvmppc_pmd_alloc();

if (level == 0 && !(pmd && pmd_present(*pmd) && !pmd_is_leaf(*pmd)))
if (level == 0 && !(pmd && pmd_present(*pmd) && !pmd_leaf(*pmd)))
new_ptep = kvmppc_pte_alloc();

/* Check if we might have been invalidated; let the guest retry if so */
Expand All @@ -658,7 +658,7 @@ int kvmppc_create_pte(struct kvm *kvm, pgd_t *pgtable, pte_t pte,
new_pud = NULL;
}
pud = pud_offset(p4d, gpa);
if (pud_is_leaf(*pud)) {
if (pud_leaf(*pud)) {
unsigned long hgpa = gpa & PUD_MASK;

/* Check if we raced and someone else has set the same thing */
Expand Down Expand Up @@ -709,7 +709,7 @@ int kvmppc_create_pte(struct kvm *kvm, pgd_t *pgtable, pte_t pte,
new_pmd = NULL;
}
pmd = pmd_offset(pud, gpa);
if (pmd_is_leaf(*pmd)) {
if (pmd_leaf(*pmd)) {
unsigned long lgpa = gpa & PMD_MASK;

/* Check if we raced and someone else has set the same thing */
Expand Down