cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dump.c (10710B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Debug helper to dump the current kernel pagetables of the system
      4 * so that we can see what the various memory ranges are set to.
      5 *
      6 * Derived from x86 implementation:
      7 * (C) Copyright 2008 Intel Corporation
      8 *
      9 * Author: Arjan van de Ven <arjan@linux.intel.com>
     10 */
     11#include <linux/debugfs.h>
     12#include <linux/fs.h>
     13#include <linux/mm.h>
     14#include <linux/seq_file.h>
     15
     16#include <asm/domain.h>
     17#include <asm/fixmap.h>
     18#include <asm/memory.h>
     19#include <asm/ptdump.h>
     20
     21static struct addr_marker address_markers[] = {
     22#ifdef CONFIG_KASAN
     23	{ KASAN_SHADOW_START,	"Kasan shadow start"},
     24	{ KASAN_SHADOW_END,	"Kasan shadow end"},
     25#endif
     26	{ MODULES_VADDR,	"Modules" },
     27	{ PAGE_OFFSET,		"Kernel Mapping" },
     28	{ 0,			"vmalloc() Area" },
     29	{ VMALLOC_END,		"vmalloc() End" },
     30	{ FIXADDR_START,	"Fixmap Area" },
     31	{ VECTORS_BASE,	"Vectors" },
     32	{ VECTORS_BASE + PAGE_SIZE * 2, "Vectors End" },
     33	{ -1,			NULL },
     34};
     35
     36#define pt_dump_seq_printf(m, fmt, args...) \
     37({                      \
     38	if (m)					\
     39		seq_printf(m, fmt, ##args);	\
     40})
     41
     42#define pt_dump_seq_puts(m, fmt)    \
     43({						\
     44	if (m)					\
     45		seq_printf(m, fmt);	\
     46})
     47
     48struct pg_state {
     49	struct seq_file *seq;
     50	const struct addr_marker *marker;
     51	unsigned long start_address;
     52	unsigned level;
     53	u64 current_prot;
     54	bool check_wx;
     55	unsigned long wx_pages;
     56	const char *current_domain;
     57};
     58
     59struct prot_bits {
     60	u64		mask;
     61	u64		val;
     62	const char	*set;
     63	const char	*clear;
     64	bool		ro_bit;
     65	bool		nx_bit;
     66};
     67
     68static const struct prot_bits pte_bits[] = {
     69	{
     70		.mask	= L_PTE_USER,
     71		.val	= L_PTE_USER,
     72		.set	= "USR",
     73		.clear	= "   ",
     74	}, {
     75		.mask	= L_PTE_RDONLY,
     76		.val	= L_PTE_RDONLY,
     77		.set	= "ro",
     78		.clear	= "RW",
     79		.ro_bit	= true,
     80	}, {
     81		.mask	= L_PTE_XN,
     82		.val	= L_PTE_XN,
     83		.set	= "NX",
     84		.clear	= "x ",
     85		.nx_bit	= true,
     86	}, {
     87		.mask	= L_PTE_SHARED,
     88		.val	= L_PTE_SHARED,
     89		.set	= "SHD",
     90		.clear	= "   ",
     91	}, {
     92		.mask	= L_PTE_MT_MASK,
     93		.val	= L_PTE_MT_UNCACHED,
     94		.set	= "SO/UNCACHED",
     95	}, {
     96		.mask	= L_PTE_MT_MASK,
     97		.val	= L_PTE_MT_BUFFERABLE,
     98		.set	= "MEM/BUFFERABLE/WC",
     99	}, {
    100		.mask	= L_PTE_MT_MASK,
    101		.val	= L_PTE_MT_WRITETHROUGH,
    102		.set	= "MEM/CACHED/WT",
    103	}, {
    104		.mask	= L_PTE_MT_MASK,
    105		.val	= L_PTE_MT_WRITEBACK,
    106		.set	= "MEM/CACHED/WBRA",
    107#ifndef CONFIG_ARM_LPAE
    108	}, {
    109		.mask	= L_PTE_MT_MASK,
    110		.val	= L_PTE_MT_MINICACHE,
    111		.set	= "MEM/MINICACHE",
    112#endif
    113	}, {
    114		.mask	= L_PTE_MT_MASK,
    115		.val	= L_PTE_MT_WRITEALLOC,
    116		.set	= "MEM/CACHED/WBWA",
    117	}, {
    118		.mask	= L_PTE_MT_MASK,
    119		.val	= L_PTE_MT_DEV_SHARED,
    120		.set	= "DEV/SHARED",
    121#ifndef CONFIG_ARM_LPAE
    122	}, {
    123		.mask	= L_PTE_MT_MASK,
    124		.val	= L_PTE_MT_DEV_NONSHARED,
    125		.set	= "DEV/NONSHARED",
    126#endif
    127	}, {
    128		.mask	= L_PTE_MT_MASK,
    129		.val	= L_PTE_MT_DEV_WC,
    130		.set	= "DEV/WC",
    131	}, {
    132		.mask	= L_PTE_MT_MASK,
    133		.val	= L_PTE_MT_DEV_CACHED,
    134		.set	= "DEV/CACHED",
    135	},
    136};
    137
    138static const struct prot_bits section_bits[] = {
    139#ifdef CONFIG_ARM_LPAE
    140	{
    141		.mask	= PMD_SECT_USER,
    142		.val	= PMD_SECT_USER,
    143		.set	= "USR",
    144	}, {
    145		.mask	= L_PMD_SECT_RDONLY | PMD_SECT_AP2,
    146		.val	= L_PMD_SECT_RDONLY | PMD_SECT_AP2,
    147		.set	= "ro",
    148		.clear	= "RW",
    149		.ro_bit	= true,
    150#elif __LINUX_ARM_ARCH__ >= 6
    151	{
    152		.mask	= PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
    153		.val	= PMD_SECT_APX | PMD_SECT_AP_WRITE,
    154		.set	= "    ro",
    155		.ro_bit	= true,
    156	}, {
    157		.mask	= PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
    158		.val	= PMD_SECT_AP_WRITE,
    159		.set	= "    RW",
    160	}, {
    161		.mask	= PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
    162		.val	= PMD_SECT_AP_READ,
    163		.set	= "USR ro",
    164	}, {
    165		.mask	= PMD_SECT_APX | PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
    166		.val	= PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
    167		.set	= "USR RW",
    168#else /* ARMv4/ARMv5  */
    169	/* These are approximate */
    170	{
    171		.mask   = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
    172		.val    = 0,
    173		.set    = "    ro",
    174		.ro_bit	= true,
    175	}, {
    176		.mask   = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
    177		.val    = PMD_SECT_AP_WRITE,
    178		.set    = "    RW",
    179	}, {
    180		.mask   = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
    181		.val    = PMD_SECT_AP_READ,
    182		.set    = "USR ro",
    183	}, {
    184		.mask   = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
    185		.val    = PMD_SECT_AP_READ | PMD_SECT_AP_WRITE,
    186		.set    = "USR RW",
    187#endif
    188	}, {
    189		.mask	= PMD_SECT_XN,
    190		.val	= PMD_SECT_XN,
    191		.set	= "NX",
    192		.clear	= "x ",
    193		.nx_bit	= true,
    194	}, {
    195		.mask	= PMD_SECT_S,
    196		.val	= PMD_SECT_S,
    197		.set	= "SHD",
    198		.clear	= "   ",
    199	},
    200};
    201
    202struct pg_level {
    203	const struct prot_bits *bits;
    204	size_t num;
    205	u64 mask;
    206	const struct prot_bits *ro_bit;
    207	const struct prot_bits *nx_bit;
    208};
    209
    210static struct pg_level pg_level[] = {
    211	{
    212	}, { /* pgd */
    213	}, { /* p4d */
    214	}, { /* pud */
    215	}, { /* pmd */
    216		.bits	= section_bits,
    217		.num	= ARRAY_SIZE(section_bits),
    218	}, { /* pte */
    219		.bits	= pte_bits,
    220		.num	= ARRAY_SIZE(pte_bits),
    221	},
    222};
    223
    224static void dump_prot(struct pg_state *st, const struct prot_bits *bits, size_t num)
    225{
    226	unsigned i;
    227
    228	for (i = 0; i < num; i++, bits++) {
    229		const char *s;
    230
    231		if ((st->current_prot & bits->mask) == bits->val)
    232			s = bits->set;
    233		else
    234			s = bits->clear;
    235
    236		if (s)
    237			pt_dump_seq_printf(st->seq, " %s", s);
    238	}
    239}
    240
    241static void note_prot_wx(struct pg_state *st, unsigned long addr)
    242{
    243	if (!st->check_wx)
    244		return;
    245	if ((st->current_prot & pg_level[st->level].ro_bit->mask) ==
    246				pg_level[st->level].ro_bit->val)
    247		return;
    248	if ((st->current_prot & pg_level[st->level].nx_bit->mask) ==
    249				pg_level[st->level].nx_bit->val)
    250		return;
    251
    252	WARN_ONCE(1, "arm/mm: Found insecure W+X mapping at address %pS\n",
    253			(void *)st->start_address);
    254
    255	st->wx_pages += (addr - st->start_address) / PAGE_SIZE;
    256}
    257
    258static void note_page(struct pg_state *st, unsigned long addr,
    259		      unsigned int level, u64 val, const char *domain)
    260{
    261	static const char units[] = "KMGTPE";
    262	u64 prot = val & pg_level[level].mask;
    263
    264	if (!st->level) {
    265		st->level = level;
    266		st->current_prot = prot;
    267		st->current_domain = domain;
    268		pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
    269	} else if (prot != st->current_prot || level != st->level ||
    270		   domain != st->current_domain ||
    271		   addr >= st->marker[1].start_address) {
    272		const char *unit = units;
    273		unsigned long delta;
    274
    275		if (st->current_prot) {
    276			note_prot_wx(st, addr);
    277			pt_dump_seq_printf(st->seq, "0x%08lx-0x%08lx   ",
    278				   st->start_address, addr);
    279
    280			delta = (addr - st->start_address) >> 10;
    281			while (!(delta & 1023) && unit[1]) {
    282				delta >>= 10;
    283				unit++;
    284			}
    285			pt_dump_seq_printf(st->seq, "%9lu%c", delta, *unit);
    286			if (st->current_domain)
    287				pt_dump_seq_printf(st->seq, " %s",
    288							st->current_domain);
    289			if (pg_level[st->level].bits)
    290				dump_prot(st, pg_level[st->level].bits, pg_level[st->level].num);
    291			pt_dump_seq_printf(st->seq, "\n");
    292		}
    293
    294		if (addr >= st->marker[1].start_address) {
    295			st->marker++;
    296			pt_dump_seq_printf(st->seq, "---[ %s ]---\n",
    297							st->marker->name);
    298		}
    299		st->start_address = addr;
    300		st->current_prot = prot;
    301		st->current_domain = domain;
    302		st->level = level;
    303	}
    304}
    305
    306static void walk_pte(struct pg_state *st, pmd_t *pmd, unsigned long start,
    307		     const char *domain)
    308{
    309	pte_t *pte = pte_offset_kernel(pmd, 0);
    310	unsigned long addr;
    311	unsigned i;
    312
    313	for (i = 0; i < PTRS_PER_PTE; i++, pte++) {
    314		addr = start + i * PAGE_SIZE;
    315		note_page(st, addr, 5, pte_val(*pte), domain);
    316	}
    317}
    318
    319static const char *get_domain_name(pmd_t *pmd)
    320{
    321#ifndef CONFIG_ARM_LPAE
    322	switch (pmd_val(*pmd) & PMD_DOMAIN_MASK) {
    323	case PMD_DOMAIN(DOMAIN_KERNEL):
    324		return "KERNEL ";
    325	case PMD_DOMAIN(DOMAIN_USER):
    326		return "USER   ";
    327	case PMD_DOMAIN(DOMAIN_IO):
    328		return "IO     ";
    329	case PMD_DOMAIN(DOMAIN_VECTORS):
    330		return "VECTORS";
    331	default:
    332		return "unknown";
    333	}
    334#endif
    335	return NULL;
    336}
    337
    338static void walk_pmd(struct pg_state *st, pud_t *pud, unsigned long start)
    339{
    340	pmd_t *pmd = pmd_offset(pud, 0);
    341	unsigned long addr;
    342	unsigned i;
    343	const char *domain;
    344
    345	for (i = 0; i < PTRS_PER_PMD; i++, pmd++) {
    346		addr = start + i * PMD_SIZE;
    347		domain = get_domain_name(pmd);
    348		if (pmd_none(*pmd) || pmd_large(*pmd) || !pmd_present(*pmd))
    349			note_page(st, addr, 3, pmd_val(*pmd), domain);
    350		else
    351			walk_pte(st, pmd, addr, domain);
    352
    353		if (SECTION_SIZE < PMD_SIZE && pmd_large(pmd[1])) {
    354			addr += SECTION_SIZE;
    355			pmd++;
    356			domain = get_domain_name(pmd);
    357			note_page(st, addr, 4, pmd_val(*pmd), domain);
    358		}
    359	}
    360}
    361
    362static void walk_pud(struct pg_state *st, p4d_t *p4d, unsigned long start)
    363{
    364	pud_t *pud = pud_offset(p4d, 0);
    365	unsigned long addr;
    366	unsigned i;
    367
    368	for (i = 0; i < PTRS_PER_PUD; i++, pud++) {
    369		addr = start + i * PUD_SIZE;
    370		if (!pud_none(*pud)) {
    371			walk_pmd(st, pud, addr);
    372		} else {
    373			note_page(st, addr, 3, pud_val(*pud), NULL);
    374		}
    375	}
    376}
    377
    378static void walk_p4d(struct pg_state *st, pgd_t *pgd, unsigned long start)
    379{
    380	p4d_t *p4d = p4d_offset(pgd, 0);
    381	unsigned long addr;
    382	unsigned i;
    383
    384	for (i = 0; i < PTRS_PER_P4D; i++, p4d++) {
    385		addr = start + i * P4D_SIZE;
    386		if (!p4d_none(*p4d)) {
    387			walk_pud(st, p4d, addr);
    388		} else {
    389			note_page(st, addr, 2, p4d_val(*p4d), NULL);
    390		}
    391	}
    392}
    393
    394static void walk_pgd(struct pg_state *st, struct mm_struct *mm,
    395			unsigned long start)
    396{
    397	pgd_t *pgd = pgd_offset(mm, 0UL);
    398	unsigned i;
    399	unsigned long addr;
    400
    401	for (i = 0; i < PTRS_PER_PGD; i++, pgd++) {
    402		addr = start + i * PGDIR_SIZE;
    403		if (!pgd_none(*pgd)) {
    404			walk_p4d(st, pgd, addr);
    405		} else {
    406			note_page(st, addr, 1, pgd_val(*pgd), NULL);
    407		}
    408	}
    409}
    410
    411void ptdump_walk_pgd(struct seq_file *m, struct ptdump_info *info)
    412{
    413	struct pg_state st = {
    414		.seq = m,
    415		.marker = info->markers,
    416		.check_wx = false,
    417	};
    418
    419	walk_pgd(&st, info->mm, info->base_addr);
    420	note_page(&st, 0, 0, 0, NULL);
    421}
    422
    423static void __init ptdump_initialize(void)
    424{
    425	unsigned i, j;
    426
    427	for (i = 0; i < ARRAY_SIZE(pg_level); i++)
    428		if (pg_level[i].bits)
    429			for (j = 0; j < pg_level[i].num; j++) {
    430				pg_level[i].mask |= pg_level[i].bits[j].mask;
    431				if (pg_level[i].bits[j].ro_bit)
    432					pg_level[i].ro_bit = &pg_level[i].bits[j];
    433				if (pg_level[i].bits[j].nx_bit)
    434					pg_level[i].nx_bit = &pg_level[i].bits[j];
    435			}
    436#ifdef CONFIG_KASAN
    437	address_markers[4].start_address = VMALLOC_START;
    438#else
    439	address_markers[2].start_address = VMALLOC_START;
    440#endif
    441}
    442
    443static struct ptdump_info kernel_ptdump_info = {
    444	.mm = &init_mm,
    445	.markers = address_markers,
    446	.base_addr = 0,
    447};
    448
    449void ptdump_check_wx(void)
    450{
    451	struct pg_state st = {
    452		.seq = NULL,
    453		.marker = (struct addr_marker[]) {
    454			{ 0, NULL},
    455			{ -1, NULL},
    456		},
    457		.check_wx = true,
    458	};
    459
    460	walk_pgd(&st, &init_mm, 0);
    461	note_page(&st, 0, 0, 0, NULL);
    462	if (st.wx_pages)
    463		pr_warn("Checked W+X mappings: FAILED, %lu W+X pages found\n",
    464			st.wx_pages);
    465	else
    466		pr_info("Checked W+X mappings: passed, no W+X pages found\n");
    467}
    468
    469static int __init ptdump_init(void)
    470{
    471	ptdump_initialize();
    472	ptdump_debugfs_register(&kernel_ptdump_info, "kernel_page_tables");
    473	return 0;
    474}
    475__initcall(ptdump_init);