-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a generic version of page table dumping that architectures can opt-in to. Link: http://lkml.kernel.org/r/20191218162402.45610-20-steven.price@arm.com Signed-off-by: Steven Price <steven.price@arm.com> Cc: Albert Ou <aou@eecs.berkeley.edu> Cc: Alexandre Ghiti <alex@ghiti.fr> Cc: Andy Lutomirski <luto@kernel.org> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Borislav Petkov <bp@alien8.de> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Christian Borntraeger <borntraeger@de.ibm.com> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: David S. Miller <davem@davemloft.net> Cc: Heiko Carstens <heiko.carstens@de.ibm.com> Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Ingo Molnar <mingo@redhat.com> Cc: James Hogan <jhogan@kernel.org> Cc: James Morse <james.morse@arm.com> Cc: Jerome Glisse <jglisse@redhat.com> Cc: "Liang, Kan" <kan.liang@linux.intel.com> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Paul Burton <paul.burton@mips.com> Cc: Paul Mackerras <paulus@samba.org> Cc: Paul Walmsley <paul.walmsley@sifive.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Ralf Baechle <ralf@linux-mips.org> Cc: Russell King <linux@armlinux.org.uk> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Vasily Gorbik <gor@linux.ibm.com> Cc: Vineet Gupta <vgupta@synopsys.com> Cc: Will Deacon <will@kernel.org> Cc: Zong Li <zong.li@sifive.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
- Loading branch information
Steven Price
authored and
Linus Torvalds
committed
Feb 4, 2020
1 parent
c5cfae1
commit 30d621f
Showing
4 changed files
with
182 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
|
||
#ifndef _LINUX_PTDUMP_H | ||
#define _LINUX_PTDUMP_H | ||
|
||
#include <linux/mm_types.h> | ||
|
||
struct ptdump_range { | ||
unsigned long start; | ||
unsigned long end; | ||
}; | ||
|
||
struct ptdump_state { | ||
void (*note_page)(struct ptdump_state *st, unsigned long addr, | ||
int level, unsigned long val); | ||
const struct ptdump_range *range; | ||
}; | ||
|
||
void ptdump_walk_pgd(struct ptdump_state *st, struct mm_struct *mm); | ||
|
||
#endif /* _LINUX_PTDUMP_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
|
||
#include <linux/pagewalk.h> | ||
#include <linux/ptdump.h> | ||
#include <linux/kasan.h> | ||
|
||
#ifdef CONFIG_KASAN | ||
/* | ||
* This is an optimization for KASAN=y case. Since all kasan page tables | ||
* eventually point to the kasan_early_shadow_page we could call note_page() | ||
* right away without walking through lower level page tables. This saves | ||
* us dozens of seconds (minutes for 5-level config) while checking for | ||
* W+X mapping or reading kernel_page_tables debugfs file. | ||
*/ | ||
static inline int note_kasan_page_table(struct mm_walk *walk, | ||
unsigned long addr) | ||
{ | ||
struct ptdump_state *st = walk->private; | ||
|
||
st->note_page(st, addr, 5, pte_val(kasan_early_shadow_pte[0])); | ||
|
||
walk->action = ACTION_CONTINUE; | ||
|
||
return 0; | ||
} | ||
#endif | ||
|
||
static int ptdump_pgd_entry(pgd_t *pgd, unsigned long addr, | ||
unsigned long next, struct mm_walk *walk) | ||
{ | ||
struct ptdump_state *st = walk->private; | ||
pgd_t val = READ_ONCE(*pgd); | ||
|
||
#if CONFIG_PGTABLE_LEVELS > 4 && defined(CONFIG_KASAN) | ||
if (pgd_page(val) == virt_to_page(lm_alias(kasan_early_shadow_p4d))) | ||
return note_kasan_page_table(walk, addr); | ||
#endif | ||
|
||
if (pgd_leaf(val)) | ||
st->note_page(st, addr, 1, pgd_val(val)); | ||
|
||
return 0; | ||
} | ||
|
||
static int ptdump_p4d_entry(p4d_t *p4d, unsigned long addr, | ||
unsigned long next, struct mm_walk *walk) | ||
{ | ||
struct ptdump_state *st = walk->private; | ||
p4d_t val = READ_ONCE(*p4d); | ||
|
||
#if CONFIG_PGTABLE_LEVELS > 3 && defined(CONFIG_KASAN) | ||
if (p4d_page(val) == virt_to_page(lm_alias(kasan_early_shadow_pud))) | ||
return note_kasan_page_table(walk, addr); | ||
#endif | ||
|
||
if (p4d_leaf(val)) | ||
st->note_page(st, addr, 2, p4d_val(val)); | ||
|
||
return 0; | ||
} | ||
|
||
static int ptdump_pud_entry(pud_t *pud, unsigned long addr, | ||
unsigned long next, struct mm_walk *walk) | ||
{ | ||
struct ptdump_state *st = walk->private; | ||
pud_t val = READ_ONCE(*pud); | ||
|
||
#if CONFIG_PGTABLE_LEVELS > 2 && defined(CONFIG_KASAN) | ||
if (pud_page(val) == virt_to_page(lm_alias(kasan_early_shadow_pmd))) | ||
return note_kasan_page_table(walk, addr); | ||
#endif | ||
|
||
if (pud_leaf(val)) | ||
st->note_page(st, addr, 3, pud_val(val)); | ||
|
||
return 0; | ||
} | ||
|
||
static int ptdump_pmd_entry(pmd_t *pmd, unsigned long addr, | ||
unsigned long next, struct mm_walk *walk) | ||
{ | ||
struct ptdump_state *st = walk->private; | ||
pmd_t val = READ_ONCE(*pmd); | ||
|
||
#if defined(CONFIG_KASAN) | ||
if (pmd_page(val) == virt_to_page(lm_alias(kasan_early_shadow_pte))) | ||
return note_kasan_page_table(walk, addr); | ||
#endif | ||
|
||
if (pmd_leaf(val)) | ||
st->note_page(st, addr, 4, pmd_val(val)); | ||
|
||
return 0; | ||
} | ||
|
||
static int ptdump_pte_entry(pte_t *pte, unsigned long addr, | ||
unsigned long next, struct mm_walk *walk) | ||
{ | ||
struct ptdump_state *st = walk->private; | ||
|
||
st->note_page(st, addr, 5, pte_val(READ_ONCE(*pte))); | ||
|
||
return 0; | ||
} | ||
|
||
static int ptdump_hole(unsigned long addr, unsigned long next, | ||
int depth, struct mm_walk *walk) | ||
{ | ||
struct ptdump_state *st = walk->private; | ||
|
||
st->note_page(st, addr, depth + 1, 0); | ||
|
||
return 0; | ||
} | ||
|
||
static const struct mm_walk_ops ptdump_ops = { | ||
.pgd_entry = ptdump_pgd_entry, | ||
.p4d_entry = ptdump_p4d_entry, | ||
.pud_entry = ptdump_pud_entry, | ||
.pmd_entry = ptdump_pmd_entry, | ||
.pte_entry = ptdump_pte_entry, | ||
.pte_hole = ptdump_hole, | ||
}; | ||
|
||
void ptdump_walk_pgd(struct ptdump_state *st, struct mm_struct *mm) | ||
{ | ||
const struct ptdump_range *range = st->range; | ||
|
||
down_read(&mm->mmap_sem); | ||
while (range->start != range->end) { | ||
walk_page_range_novma(mm, range->start, range->end, | ||
&ptdump_ops, st); | ||
range++; | ||
} | ||
up_read(&mm->mmap_sem); | ||
|
||
/* Flush out the last page */ | ||
st->note_page(st, 0, 0, 0); | ||
} |