forked from torvalds/linux
-
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/[email protected] Signed-off-by: Steven Price <[email protected]> Cc: Albert Ou <[email protected]> Cc: Alexandre Ghiti <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: Ard Biesheuvel <[email protected]> Cc: Arnd Bergmann <[email protected]> Cc: Benjamin Herrenschmidt <[email protected]> Cc: Borislav Petkov <[email protected]> Cc: Catalin Marinas <[email protected]> Cc: Christian Borntraeger <[email protected]> Cc: Dave Hansen <[email protected]> Cc: David S. Miller <[email protected]> Cc: Heiko Carstens <[email protected]> Cc: "H. Peter Anvin" <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: James Hogan <[email protected]> Cc: James Morse <[email protected]> Cc: Jerome Glisse <[email protected]> Cc: "Liang, Kan" <[email protected]> Cc: Mark Rutland <[email protected]> Cc: Michael Ellerman <[email protected]> Cc: Paul Burton <[email protected]> Cc: Paul Mackerras <[email protected]> Cc: Paul Walmsley <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Ralf Baechle <[email protected]> Cc: Russell King <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Vasily Gorbik <[email protected]> Cc: Vineet Gupta <[email protected]> Cc: Will Deacon <[email protected]> Cc: Zong Li <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
- Loading branch information
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); | ||
} |