2019-06-01 16:08:55 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2014-11-26 08:28:39 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
|
|
|
* Debug helper to dump the current kernel pagetables of the system
|
|
|
|
* so that we can see what the various memory ranges are set to.
|
|
|
|
*
|
|
|
|
* Derived from x86 and arm implementation:
|
|
|
|
* (C) Copyright 2008 Intel Corporation
|
|
|
|
*
|
|
|
|
* Author: Arjan van de Ven <arjan@linux.intel.com>
|
|
|
|
*/
|
|
|
|
#include <linux/debugfs.h>
|
2015-01-23 02:20:36 +08:00
|
|
|
#include <linux/errno.h>
|
2014-11-26 08:28:39 +08:00
|
|
|
#include <linux/fs.h>
|
2015-01-23 04:52:10 +08:00
|
|
|
#include <linux/io.h>
|
2015-01-23 02:20:36 +08:00
|
|
|
#include <linux/init.h>
|
2014-11-26 08:28:39 +08:00
|
|
|
#include <linux/mm.h>
|
2020-02-04 09:36:29 +08:00
|
|
|
#include <linux/ptdump.h>
|
2014-11-26 08:28:39 +08:00
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/seq_file.h>
|
|
|
|
|
|
|
|
#include <asm/fixmap.h>
|
2016-04-23 00:48:04 +08:00
|
|
|
#include <asm/kasan.h>
|
2015-01-23 02:20:36 +08:00
|
|
|
#include <asm/memory.h>
|
|
|
|
#include <asm/pgtable-hwdef.h>
|
2016-05-31 21:49:01 +08:00
|
|
|
#include <asm/ptdump.h>
|
2014-11-26 08:28:39 +08:00
|
|
|
|
2019-08-07 23:55:16 +08:00
|
|
|
|
|
|
|
enum address_markers_idx {
|
|
|
|
PAGE_OFFSET_NR = 0,
|
2019-08-14 21:28:48 +08:00
|
|
|
PAGE_END_NR,
|
2020-12-23 04:02:06 +08:00
|
|
|
#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
|
2019-08-07 23:55:16 +08:00
|
|
|
KASAN_START_NR,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct addr_marker address_markers[] = {
|
2019-08-07 23:55:14 +08:00
|
|
|
{ PAGE_OFFSET, "Linear Mapping start" },
|
2019-08-14 21:28:48 +08:00
|
|
|
{ 0 /* PAGE_END */, "Linear Mapping end" },
|
2020-12-23 04:02:06 +08:00
|
|
|
#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
|
2019-08-07 23:55:16 +08:00
|
|
|
{ 0 /* KASAN_SHADOW_START */, "Kasan shadow start" },
|
2016-04-23 00:48:04 +08:00
|
|
|
{ KASAN_SHADOW_END, "Kasan shadow end" },
|
|
|
|
#endif
|
2016-04-23 00:48:03 +08:00
|
|
|
{ MODULES_VADDR, "Modules start" },
|
|
|
|
{ MODULES_END, "Modules end" },
|
2018-09-05 22:12:27 +08:00
|
|
|
{ VMALLOC_START, "vmalloc() area" },
|
|
|
|
{ VMALLOC_END, "vmalloc() end" },
|
arm64: add FIXADDR_TOT_{START,SIZE}
Currently arm64's FIXADDR_{START,SIZE} definitions only cover the
runtime fixmap slots (and not the boot-time fixmap slots), but the code
for creating the fixmap assumes that these definitions cover the entire
fixmap range. This means that the ptdump boundaries are reported in a
misleading way, missing the VA region of the runtime slots. In theory
this could also cause the fixmap creation to go wrong if the boot-time
fixmap slots end up spilling into a separate PMD entry, though luckily
this is not currently the case in any configuration.
While it seems like we could extend FIXADDR_{START,SIZE} to cover the
entire fixmap area, core code relies upon these *only* covering the
runtime slots. For example, fix_to_virt() and virt_to_fix() try to
reject manipulation of the boot-time slots based upon
FIXADDR_{START,SIZE}, while __fix_to_virt() and __virt_to_fix() can
handle any fixmap slot.
This patch follows the lead of x86 in commit:
55f49fcb879fbeeb ("x86/mm: Fix overlap of i386 CPU_ENTRY_AREA with FIX_BTMAP")
... and add new FIXADDR_TOT_{START,SIZE} definitions which cover the
entire fixmap area, using these for the fixmap creation and ptdump code.
As the boot-time fixmap slots are now rejected by fix_to_virt(),
the early_fixmap_init() code is changed to consistently use
__fix_to_virt(), as it already does in a few cases.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Anshuman Khandual <anshuman.khandual@arm.com>
Cc: Ard Biesheuvel <ardb@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Ryan Roberts <ryan.roberts@arm.com>
Link: https://lore.kernel.org/r/20230406152759.4164229-2-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2023-04-06 23:27:57 +08:00
|
|
|
{ FIXADDR_TOT_START, "Fixmap start" },
|
2016-04-23 00:48:03 +08:00
|
|
|
{ FIXADDR_TOP, "Fixmap end" },
|
|
|
|
{ PCI_IO_START, "PCI I/O start" },
|
|
|
|
{ PCI_IO_END, "PCI I/O end" },
|
|
|
|
{ VMEMMAP_START, "vmemmap start" },
|
|
|
|
{ VMEMMAP_START + VMEMMAP_SIZE, "vmemmap end" },
|
|
|
|
{ -1, NULL },
|
2014-11-26 08:28:39 +08:00
|
|
|
};
|
|
|
|
|
2016-10-28 00:27:32 +08:00
|
|
|
#define pt_dump_seq_printf(m, fmt, args...) \
|
|
|
|
({ \
|
|
|
|
if (m) \
|
|
|
|
seq_printf(m, fmt, ##args); \
|
|
|
|
})
|
|
|
|
|
|
|
|
#define pt_dump_seq_puts(m, fmt) \
|
|
|
|
({ \
|
|
|
|
if (m) \
|
|
|
|
seq_printf(m, fmt); \
|
|
|
|
})
|
|
|
|
|
2015-10-08 01:00:23 +08:00
|
|
|
/*
|
|
|
|
* The page dumper groups page table entries of the same type into a single
|
|
|
|
* description. It uses pg_state to track the range information while
|
|
|
|
* iterating over the pte entries. When the continuity is broken it then
|
|
|
|
* dumps out a description of the range.
|
|
|
|
*/
|
2014-11-26 08:28:39 +08:00
|
|
|
struct pg_state {
|
2020-02-04 09:36:29 +08:00
|
|
|
struct ptdump_state ptdump;
|
2014-11-26 08:28:39 +08:00
|
|
|
struct seq_file *seq;
|
|
|
|
const struct addr_marker *marker;
|
|
|
|
unsigned long start_address;
|
2020-02-04 09:36:29 +08:00
|
|
|
int level;
|
2014-11-26 08:28:39 +08:00
|
|
|
u64 current_prot;
|
2016-10-28 00:27:34 +08:00
|
|
|
bool check_wx;
|
|
|
|
unsigned long wx_pages;
|
|
|
|
unsigned long uxn_pages;
|
2014-11-26 08:28:39 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct prot_bits {
|
|
|
|
u64 mask;
|
|
|
|
u64 val;
|
|
|
|
const char *set;
|
|
|
|
const char *clear;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct prot_bits pte_bits[] = {
|
|
|
|
{
|
2016-02-06 08:24:48 +08:00
|
|
|
.mask = PTE_VALID,
|
|
|
|
.val = PTE_VALID,
|
|
|
|
.set = " ",
|
|
|
|
.clear = "F",
|
|
|
|
}, {
|
2014-11-26 08:28:39 +08:00
|
|
|
.mask = PTE_USER,
|
|
|
|
.val = PTE_USER,
|
|
|
|
.set = "USR",
|
|
|
|
.clear = " ",
|
|
|
|
}, {
|
|
|
|
.mask = PTE_RDONLY,
|
|
|
|
.val = PTE_RDONLY,
|
|
|
|
.set = "ro",
|
|
|
|
.clear = "RW",
|
|
|
|
}, {
|
|
|
|
.mask = PTE_PXN,
|
|
|
|
.val = PTE_PXN,
|
|
|
|
.set = "NX",
|
|
|
|
.clear = "x ",
|
|
|
|
}, {
|
|
|
|
.mask = PTE_SHARED,
|
|
|
|
.val = PTE_SHARED,
|
|
|
|
.set = "SHD",
|
|
|
|
.clear = " ",
|
|
|
|
}, {
|
|
|
|
.mask = PTE_AF,
|
|
|
|
.val = PTE_AF,
|
|
|
|
.set = "AF",
|
|
|
|
.clear = " ",
|
|
|
|
}, {
|
|
|
|
.mask = PTE_NG,
|
|
|
|
.val = PTE_NG,
|
|
|
|
.set = "NG",
|
|
|
|
.clear = " ",
|
2015-10-08 01:00:23 +08:00
|
|
|
}, {
|
|
|
|
.mask = PTE_CONT,
|
|
|
|
.val = PTE_CONT,
|
|
|
|
.set = "CON",
|
|
|
|
.clear = " ",
|
|
|
|
}, {
|
|
|
|
.mask = PTE_TABLE_BIT,
|
|
|
|
.val = PTE_TABLE_BIT,
|
|
|
|
.set = " ",
|
|
|
|
.clear = "BLK",
|
2014-11-26 08:28:39 +08:00
|
|
|
}, {
|
|
|
|
.mask = PTE_UXN,
|
|
|
|
.val = PTE_UXN,
|
|
|
|
.set = "UXN",
|
2019-11-21 21:51:32 +08:00
|
|
|
.clear = " ",
|
2020-03-17 00:50:53 +08:00
|
|
|
}, {
|
|
|
|
.mask = PTE_GP,
|
|
|
|
.val = PTE_GP,
|
|
|
|
.set = "GP",
|
|
|
|
.clear = " ",
|
2014-11-26 08:28:39 +08:00
|
|
|
}, {
|
|
|
|
.mask = PTE_ATTRINDX_MASK,
|
|
|
|
.val = PTE_ATTRINDX(MT_DEVICE_nGnRnE),
|
|
|
|
.set = "DEVICE/nGnRnE",
|
|
|
|
}, {
|
|
|
|
.mask = PTE_ATTRINDX_MASK,
|
|
|
|
.val = PTE_ATTRINDX(MT_DEVICE_nGnRE),
|
|
|
|
.set = "DEVICE/nGnRE",
|
|
|
|
}, {
|
|
|
|
.mask = PTE_ATTRINDX_MASK,
|
|
|
|
.val = PTE_ATTRINDX(MT_NORMAL_NC),
|
|
|
|
.set = "MEM/NORMAL-NC",
|
|
|
|
}, {
|
|
|
|
.mask = PTE_ATTRINDX_MASK,
|
|
|
|
.val = PTE_ATTRINDX(MT_NORMAL),
|
|
|
|
.set = "MEM/NORMAL",
|
2019-11-27 17:51:13 +08:00
|
|
|
}, {
|
|
|
|
.mask = PTE_ATTRINDX_MASK,
|
|
|
|
.val = PTE_ATTRINDX(MT_NORMAL_TAGGED),
|
|
|
|
.set = "MEM/NORMAL-TAGGED",
|
2014-11-26 08:28:39 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct pg_level {
|
|
|
|
const struct prot_bits *bits;
|
arm64: mm: dump: log span level
The page table dump code logs spans of entries at the same level
(pgd/pud/pmd/pte) which have the same attributes. While we log the
(decoded) attributes, we don't log the level, which leaves the output
ambiguous and/or confusing in some cases.
For example:
0xffff800800000000-0xffff800980000000 6G RW NX SHD AF BLK UXN MEM/NORMAL
If using 4K pages, this may describe a span of 6 1G block entries at the
PGD/PUD level, or 3072 2M block entries at the PMD level.
This patch adds the page table level to each output line, removing this
ambiguity. For the example above, this will produce:
0xffffffc800000000-0xffffffc980000000 6G PUD RW NX SHD AF BLK UXN MEM/NORMAL
When 3 level tables are in use, and we use the asm-generic/nopud.h
definitions, the dump code treats each entry in the PGD as a 1 element
table at the PUD level, and logs spans as being PUDs, which can be
confusing. To counteract this, the "PUD" mnemonic is replaced with "PGD"
when CONFIG_PGTABLE_LEVELS <= 3. Likewise for "PMD" when
CONFIG_PGTABLE_LEVELS <= 2.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Huang Shijie <shijie.huang@arm.com>
Cc: Laura Abbott <labbott@fedoraproject.org>
Cc: Steve Capper <steve.capper@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2016-05-31 21:49:02 +08:00
|
|
|
const char *name;
|
2014-11-26 08:28:39 +08:00
|
|
|
size_t num;
|
|
|
|
u64 mask;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct pg_level pg_level[] = {
|
2020-02-04 09:36:38 +08:00
|
|
|
{ /* pgd */
|
arm64: mm: dump: log span level
The page table dump code logs spans of entries at the same level
(pgd/pud/pmd/pte) which have the same attributes. While we log the
(decoded) attributes, we don't log the level, which leaves the output
ambiguous and/or confusing in some cases.
For example:
0xffff800800000000-0xffff800980000000 6G RW NX SHD AF BLK UXN MEM/NORMAL
If using 4K pages, this may describe a span of 6 1G block entries at the
PGD/PUD level, or 3072 2M block entries at the PMD level.
This patch adds the page table level to each output line, removing this
ambiguity. For the example above, this will produce:
0xffffffc800000000-0xffffffc980000000 6G PUD RW NX SHD AF BLK UXN MEM/NORMAL
When 3 level tables are in use, and we use the asm-generic/nopud.h
definitions, the dump code treats each entry in the PGD as a 1 element
table at the PUD level, and logs spans as being PUDs, which can be
confusing. To counteract this, the "PUD" mnemonic is replaced with "PGD"
when CONFIG_PGTABLE_LEVELS <= 3. Likewise for "PMD" when
CONFIG_PGTABLE_LEVELS <= 2.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Huang Shijie <shijie.huang@arm.com>
Cc: Laura Abbott <labbott@fedoraproject.org>
Cc: Steve Capper <steve.capper@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2016-05-31 21:49:02 +08:00
|
|
|
.name = "PGD",
|
2014-11-26 08:28:39 +08:00
|
|
|
.bits = pte_bits,
|
|
|
|
.num = ARRAY_SIZE(pte_bits),
|
2020-02-04 09:36:29 +08:00
|
|
|
}, { /* p4d */
|
|
|
|
.name = "P4D",
|
|
|
|
.bits = pte_bits,
|
|
|
|
.num = ARRAY_SIZE(pte_bits),
|
2014-11-26 08:28:39 +08:00
|
|
|
}, { /* pud */
|
arm64: mm: dump: log span level
The page table dump code logs spans of entries at the same level
(pgd/pud/pmd/pte) which have the same attributes. While we log the
(decoded) attributes, we don't log the level, which leaves the output
ambiguous and/or confusing in some cases.
For example:
0xffff800800000000-0xffff800980000000 6G RW NX SHD AF BLK UXN MEM/NORMAL
If using 4K pages, this may describe a span of 6 1G block entries at the
PGD/PUD level, or 3072 2M block entries at the PMD level.
This patch adds the page table level to each output line, removing this
ambiguity. For the example above, this will produce:
0xffffffc800000000-0xffffffc980000000 6G PUD RW NX SHD AF BLK UXN MEM/NORMAL
When 3 level tables are in use, and we use the asm-generic/nopud.h
definitions, the dump code treats each entry in the PGD as a 1 element
table at the PUD level, and logs spans as being PUDs, which can be
confusing. To counteract this, the "PUD" mnemonic is replaced with "PGD"
when CONFIG_PGTABLE_LEVELS <= 3. Likewise for "PMD" when
CONFIG_PGTABLE_LEVELS <= 2.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Huang Shijie <shijie.huang@arm.com>
Cc: Laura Abbott <labbott@fedoraproject.org>
Cc: Steve Capper <steve.capper@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2016-05-31 21:49:02 +08:00
|
|
|
.name = (CONFIG_PGTABLE_LEVELS > 3) ? "PUD" : "PGD",
|
2014-11-26 08:28:39 +08:00
|
|
|
.bits = pte_bits,
|
|
|
|
.num = ARRAY_SIZE(pte_bits),
|
|
|
|
}, { /* pmd */
|
arm64: mm: dump: log span level
The page table dump code logs spans of entries at the same level
(pgd/pud/pmd/pte) which have the same attributes. While we log the
(decoded) attributes, we don't log the level, which leaves the output
ambiguous and/or confusing in some cases.
For example:
0xffff800800000000-0xffff800980000000 6G RW NX SHD AF BLK UXN MEM/NORMAL
If using 4K pages, this may describe a span of 6 1G block entries at the
PGD/PUD level, or 3072 2M block entries at the PMD level.
This patch adds the page table level to each output line, removing this
ambiguity. For the example above, this will produce:
0xffffffc800000000-0xffffffc980000000 6G PUD RW NX SHD AF BLK UXN MEM/NORMAL
When 3 level tables are in use, and we use the asm-generic/nopud.h
definitions, the dump code treats each entry in the PGD as a 1 element
table at the PUD level, and logs spans as being PUDs, which can be
confusing. To counteract this, the "PUD" mnemonic is replaced with "PGD"
when CONFIG_PGTABLE_LEVELS <= 3. Likewise for "PMD" when
CONFIG_PGTABLE_LEVELS <= 2.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Huang Shijie <shijie.huang@arm.com>
Cc: Laura Abbott <labbott@fedoraproject.org>
Cc: Steve Capper <steve.capper@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2016-05-31 21:49:02 +08:00
|
|
|
.name = (CONFIG_PGTABLE_LEVELS > 2) ? "PMD" : "PGD",
|
2014-11-26 08:28:39 +08:00
|
|
|
.bits = pte_bits,
|
|
|
|
.num = ARRAY_SIZE(pte_bits),
|
|
|
|
}, { /* pte */
|
arm64: mm: dump: log span level
The page table dump code logs spans of entries at the same level
(pgd/pud/pmd/pte) which have the same attributes. While we log the
(decoded) attributes, we don't log the level, which leaves the output
ambiguous and/or confusing in some cases.
For example:
0xffff800800000000-0xffff800980000000 6G RW NX SHD AF BLK UXN MEM/NORMAL
If using 4K pages, this may describe a span of 6 1G block entries at the
PGD/PUD level, or 3072 2M block entries at the PMD level.
This patch adds the page table level to each output line, removing this
ambiguity. For the example above, this will produce:
0xffffffc800000000-0xffffffc980000000 6G PUD RW NX SHD AF BLK UXN MEM/NORMAL
When 3 level tables are in use, and we use the asm-generic/nopud.h
definitions, the dump code treats each entry in the PGD as a 1 element
table at the PUD level, and logs spans as being PUDs, which can be
confusing. To counteract this, the "PUD" mnemonic is replaced with "PGD"
when CONFIG_PGTABLE_LEVELS <= 3. Likewise for "PMD" when
CONFIG_PGTABLE_LEVELS <= 2.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Huang Shijie <shijie.huang@arm.com>
Cc: Laura Abbott <labbott@fedoraproject.org>
Cc: Steve Capper <steve.capper@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2016-05-31 21:49:02 +08:00
|
|
|
.name = "PTE",
|
2014-11-26 08:28:39 +08:00
|
|
|
.bits = pte_bits,
|
|
|
|
.num = ARRAY_SIZE(pte_bits),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static void dump_prot(struct pg_state *st, const struct prot_bits *bits,
|
|
|
|
size_t num)
|
|
|
|
{
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
for (i = 0; i < num; i++, bits++) {
|
|
|
|
const char *s;
|
|
|
|
|
|
|
|
if ((st->current_prot & bits->mask) == bits->val)
|
|
|
|
s = bits->set;
|
|
|
|
else
|
|
|
|
s = bits->clear;
|
|
|
|
|
|
|
|
if (s)
|
2016-10-28 00:27:32 +08:00
|
|
|
pt_dump_seq_printf(st->seq, " %s", s);
|
2014-11-26 08:28:39 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-28 00:27:34 +08:00
|
|
|
static void note_prot_uxn(struct pg_state *st, unsigned long addr)
|
|
|
|
{
|
|
|
|
if (!st->check_wx)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ((st->current_prot & PTE_UXN) == PTE_UXN)
|
|
|
|
return;
|
|
|
|
|
|
|
|
WARN_ONCE(1, "arm64/mm: Found non-UXN mapping at address %p/%pS\n",
|
|
|
|
(void *)st->start_address, (void *)st->start_address);
|
|
|
|
|
|
|
|
st->uxn_pages += (addr - st->start_address) / PAGE_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void note_prot_wx(struct pg_state *st, unsigned long addr)
|
|
|
|
{
|
|
|
|
if (!st->check_wx)
|
|
|
|
return;
|
|
|
|
if ((st->current_prot & PTE_RDONLY) == PTE_RDONLY)
|
|
|
|
return;
|
|
|
|
if ((st->current_prot & PTE_PXN) == PTE_PXN)
|
|
|
|
return;
|
|
|
|
|
|
|
|
WARN_ONCE(1, "arm64/mm: Found insecure W+X mapping at address %p/%pS\n",
|
|
|
|
(void *)st->start_address, (void *)st->start_address);
|
|
|
|
|
|
|
|
st->wx_pages += (addr - st->start_address) / PAGE_SIZE;
|
|
|
|
}
|
|
|
|
|
2020-02-04 09:36:29 +08:00
|
|
|
static void note_page(struct ptdump_state *pt_st, unsigned long addr, int level,
|
2020-06-02 12:50:01 +08:00
|
|
|
u64 val)
|
2014-11-26 08:28:39 +08:00
|
|
|
{
|
2020-02-04 09:36:29 +08:00
|
|
|
struct pg_state *st = container_of(pt_st, struct pg_state, ptdump);
|
2014-11-26 08:28:39 +08:00
|
|
|
static const char units[] = "KMGTPE";
|
2020-02-04 09:36:29 +08:00
|
|
|
u64 prot = 0;
|
|
|
|
|
|
|
|
if (level >= 0)
|
|
|
|
prot = val & pg_level[level].mask;
|
2014-11-26 08:28:39 +08:00
|
|
|
|
2020-02-04 09:36:38 +08:00
|
|
|
if (st->level == -1) {
|
2014-11-26 08:28:39 +08:00
|
|
|
st->level = level;
|
|
|
|
st->current_prot = prot;
|
|
|
|
st->start_address = addr;
|
2016-10-28 00:27:32 +08:00
|
|
|
pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
|
2014-11-26 08:28:39 +08:00
|
|
|
} else if (prot != st->current_prot || level != st->level ||
|
|
|
|
addr >= st->marker[1].start_address) {
|
|
|
|
const char *unit = units;
|
|
|
|
unsigned long delta;
|
|
|
|
|
|
|
|
if (st->current_prot) {
|
2016-10-28 00:27:34 +08:00
|
|
|
note_prot_uxn(st, addr);
|
|
|
|
note_prot_wx(st, addr);
|
2020-02-04 09:36:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pt_dump_seq_printf(st->seq, "0x%016lx-0x%016lx ",
|
2014-11-26 08:28:39 +08:00
|
|
|
st->start_address, addr);
|
|
|
|
|
2020-02-04 09:36:34 +08:00
|
|
|
delta = (addr - st->start_address) >> 10;
|
|
|
|
while (!(delta & 1023) && unit[1]) {
|
|
|
|
delta >>= 10;
|
|
|
|
unit++;
|
2014-11-26 08:28:39 +08:00
|
|
|
}
|
2020-02-04 09:36:34 +08:00
|
|
|
pt_dump_seq_printf(st->seq, "%9lu%c %s", delta, *unit,
|
|
|
|
pg_level[st->level].name);
|
|
|
|
if (st->current_prot && pg_level[st->level].bits)
|
|
|
|
dump_prot(st, pg_level[st->level].bits,
|
|
|
|
pg_level[st->level].num);
|
|
|
|
pt_dump_seq_puts(st->seq, "\n");
|
2014-11-26 08:28:39 +08:00
|
|
|
|
|
|
|
if (addr >= st->marker[1].start_address) {
|
|
|
|
st->marker++;
|
2016-10-28 00:27:32 +08:00
|
|
|
pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
|
2014-11-26 08:28:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
st->start_address = addr;
|
|
|
|
st->current_prot = prot;
|
|
|
|
st->level = level;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (addr >= st->marker[1].start_address) {
|
|
|
|
st->marker++;
|
2016-10-28 00:27:32 +08:00
|
|
|
pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
|
2014-11-26 08:28:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-02-04 09:36:29 +08:00
|
|
|
void ptdump_walk(struct seq_file *s, struct ptdump_info *info)
|
2014-11-26 08:28:39 +08:00
|
|
|
{
|
2020-02-04 09:36:29 +08:00
|
|
|
unsigned long end = ~0UL;
|
|
|
|
struct pg_state st;
|
2014-11-26 08:28:39 +08:00
|
|
|
|
2020-02-04 09:36:29 +08:00
|
|
|
if (info->base_addr < TASK_SIZE_64)
|
|
|
|
end = TASK_SIZE_64;
|
2014-11-26 08:28:39 +08:00
|
|
|
|
2020-02-04 09:36:29 +08:00
|
|
|
st = (struct pg_state){
|
|
|
|
.seq = s,
|
2016-05-31 21:49:01 +08:00
|
|
|
.marker = info->markers,
|
2021-02-02 23:07:49 +08:00
|
|
|
.level = -1,
|
2020-02-04 09:36:29 +08:00
|
|
|
.ptdump = {
|
|
|
|
.note_page = note_page,
|
|
|
|
.range = (struct ptdump_range[]){
|
|
|
|
{info->base_addr, end},
|
|
|
|
{0, 0}
|
|
|
|
}
|
|
|
|
}
|
2014-11-26 08:28:39 +08:00
|
|
|
};
|
|
|
|
|
2020-02-04 09:36:42 +08:00
|
|
|
ptdump_walk_pgd(&st.ptdump, info->mm, NULL);
|
2014-11-26 08:28:39 +08:00
|
|
|
}
|
|
|
|
|
2021-03-30 13:54:49 +08:00
|
|
|
static void __init ptdump_initialize(void)
|
2014-11-26 08:28:39 +08:00
|
|
|
{
|
|
|
|
unsigned i, j;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(pg_level); i++)
|
|
|
|
if (pg_level[i].bits)
|
|
|
|
for (j = 0; j < pg_level[i].num; j++)
|
|
|
|
pg_level[i].mask |= pg_level[i].bits[j].mask;
|
|
|
|
}
|
2016-05-31 21:49:01 +08:00
|
|
|
|
|
|
|
static struct ptdump_info kernel_ptdump_info = {
|
|
|
|
.mm = &init_mm,
|
|
|
|
.markers = address_markers,
|
2019-08-07 23:55:14 +08:00
|
|
|
.base_addr = PAGE_OFFSET,
|
2016-05-31 21:49:01 +08:00
|
|
|
};
|
|
|
|
|
2016-10-28 00:27:34 +08:00
|
|
|
void ptdump_check_wx(void)
|
|
|
|
{
|
|
|
|
struct pg_state st = {
|
|
|
|
.seq = NULL,
|
|
|
|
.marker = (struct addr_marker[]) {
|
|
|
|
{ 0, NULL},
|
|
|
|
{ -1, NULL},
|
|
|
|
},
|
2020-02-04 09:36:38 +08:00
|
|
|
.level = -1,
|
2016-10-28 00:27:34 +08:00
|
|
|
.check_wx = true,
|
2020-02-04 09:36:29 +08:00
|
|
|
.ptdump = {
|
|
|
|
.note_page = note_page,
|
|
|
|
.range = (struct ptdump_range[]) {
|
|
|
|
{PAGE_OFFSET, ~0UL},
|
|
|
|
{0, 0}
|
|
|
|
}
|
|
|
|
}
|
2016-10-28 00:27:34 +08:00
|
|
|
};
|
|
|
|
|
2020-02-04 09:36:42 +08:00
|
|
|
ptdump_walk_pgd(&st.ptdump, &init_mm, NULL);
|
2020-02-04 09:36:29 +08:00
|
|
|
|
2016-10-28 00:27:34 +08:00
|
|
|
if (st.wx_pages || st.uxn_pages)
|
|
|
|
pr_warn("Checked W+X mappings: FAILED, %lu W+X pages found, %lu non-UXN pages found\n",
|
|
|
|
st.wx_pages, st.uxn_pages);
|
|
|
|
else
|
|
|
|
pr_info("Checked W+X mappings: passed, no W+X pages found\n");
|
|
|
|
}
|
|
|
|
|
2021-03-30 13:54:49 +08:00
|
|
|
static int __init ptdump_init(void)
|
2016-05-31 21:49:01 +08:00
|
|
|
{
|
2019-08-14 21:28:48 +08:00
|
|
|
address_markers[PAGE_END_NR].start_address = PAGE_END;
|
2020-12-23 04:02:06 +08:00
|
|
|
#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
|
2019-08-07 23:55:16 +08:00
|
|
|
address_markers[KASAN_START_NR].start_address = KASAN_SHADOW_START;
|
|
|
|
#endif
|
2016-10-28 00:27:31 +08:00
|
|
|
ptdump_initialize();
|
2019-01-22 22:41:11 +08:00
|
|
|
ptdump_debugfs_register(&kernel_ptdump_info, "kernel_page_tables");
|
|
|
|
return 0;
|
2016-05-31 21:49:01 +08:00
|
|
|
}
|
2014-11-26 08:28:39 +08:00
|
|
|
device_initcall(ptdump_init);
|