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

ptdump.c (8650B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2019 SiFive
      4 */
      5
      6#include <linux/efi.h>
      7#include <linux/init.h>
      8#include <linux/debugfs.h>
      9#include <linux/seq_file.h>
     10#include <linux/ptdump.h>
     11
     12#include <asm/ptdump.h>
     13#include <linux/pgtable.h>
     14#include <asm/kasan.h>
     15
     16#define pt_dump_seq_printf(m, fmt, args...)	\
     17({						\
     18	if (m)					\
     19		seq_printf(m, fmt, ##args);	\
     20})
     21
     22#define pt_dump_seq_puts(m, fmt)	\
     23({					\
     24	if (m)				\
     25		seq_printf(m, fmt);	\
     26})
     27
     28/*
     29 * The page dumper groups page table entries of the same type into a single
     30 * description. It uses pg_state to track the range information while
     31 * iterating over the pte entries. When the continuity is broken it then
     32 * dumps out a description of the range.
     33 */
     34struct pg_state {
     35	struct ptdump_state ptdump;
     36	struct seq_file *seq;
     37	const struct addr_marker *marker;
     38	unsigned long start_address;
     39	unsigned long start_pa;
     40	unsigned long last_pa;
     41	int level;
     42	u64 current_prot;
     43	bool check_wx;
     44	unsigned long wx_pages;
     45};
     46
     47/* Address marker */
     48struct addr_marker {
     49	unsigned long start_address;
     50	const char *name;
     51};
     52
     53/* Private information for debugfs */
     54struct ptd_mm_info {
     55	struct mm_struct		*mm;
     56	const struct addr_marker	*markers;
     57	unsigned long base_addr;
     58	unsigned long end;
     59};
     60
     61enum address_markers_idx {
     62#ifdef CONFIG_KASAN
     63	KASAN_SHADOW_START_NR,
     64	KASAN_SHADOW_END_NR,
     65#endif
     66	FIXMAP_START_NR,
     67	FIXMAP_END_NR,
     68	PCI_IO_START_NR,
     69	PCI_IO_END_NR,
     70#ifdef CONFIG_SPARSEMEM_VMEMMAP
     71	VMEMMAP_START_NR,
     72	VMEMMAP_END_NR,
     73#endif
     74	VMALLOC_START_NR,
     75	VMALLOC_END_NR,
     76	PAGE_OFFSET_NR,
     77#ifdef CONFIG_64BIT
     78	MODULES_MAPPING_NR,
     79	KERNEL_MAPPING_NR,
     80#endif
     81	END_OF_SPACE_NR
     82};
     83
     84static struct addr_marker address_markers[] = {
     85#ifdef CONFIG_KASAN
     86	{0, "Kasan shadow start"},
     87	{0, "Kasan shadow end"},
     88#endif
     89	{0, "Fixmap start"},
     90	{0, "Fixmap end"},
     91	{0, "PCI I/O start"},
     92	{0, "PCI I/O end"},
     93#ifdef CONFIG_SPARSEMEM_VMEMMAP
     94	{0, "vmemmap start"},
     95	{0, "vmemmap end"},
     96#endif
     97	{0, "vmalloc() area"},
     98	{0, "vmalloc() end"},
     99	{0, "Linear mapping"},
    100#ifdef CONFIG_64BIT
    101	{0, "Modules/BPF mapping"},
    102	{0, "Kernel mapping"},
    103#endif
    104	{-1, NULL},
    105};
    106
    107static struct ptd_mm_info kernel_ptd_info = {
    108	.mm		= &init_mm,
    109	.markers	= address_markers,
    110	.base_addr	= 0,
    111	.end		= ULONG_MAX,
    112};
    113
    114#ifdef CONFIG_EFI
    115static struct addr_marker efi_addr_markers[] = {
    116		{ 0,		"UEFI runtime start" },
    117		{ SZ_1G,	"UEFI runtime end" },
    118		{ -1,		NULL }
    119};
    120
    121static struct ptd_mm_info efi_ptd_info = {
    122	.mm		= &efi_mm,
    123	.markers	= efi_addr_markers,
    124	.base_addr	= 0,
    125	.end		= SZ_2G,
    126};
    127#endif
    128
    129/* Page Table Entry */
    130struct prot_bits {
    131	u64 mask;
    132	u64 val;
    133	const char *set;
    134	const char *clear;
    135};
    136
    137static const struct prot_bits pte_bits[] = {
    138	{
    139		.mask = _PAGE_SOFT,
    140		.val = _PAGE_SOFT,
    141		.set = "RSW",
    142		.clear = "   ",
    143	}, {
    144		.mask = _PAGE_DIRTY,
    145		.val = _PAGE_DIRTY,
    146		.set = "D",
    147		.clear = ".",
    148	}, {
    149		.mask = _PAGE_ACCESSED,
    150		.val = _PAGE_ACCESSED,
    151		.set = "A",
    152		.clear = ".",
    153	}, {
    154		.mask = _PAGE_GLOBAL,
    155		.val = _PAGE_GLOBAL,
    156		.set = "G",
    157		.clear = ".",
    158	}, {
    159		.mask = _PAGE_USER,
    160		.val = _PAGE_USER,
    161		.set = "U",
    162		.clear = ".",
    163	}, {
    164		.mask = _PAGE_EXEC,
    165		.val = _PAGE_EXEC,
    166		.set = "X",
    167		.clear = ".",
    168	}, {
    169		.mask = _PAGE_WRITE,
    170		.val = _PAGE_WRITE,
    171		.set = "W",
    172		.clear = ".",
    173	}, {
    174		.mask = _PAGE_READ,
    175		.val = _PAGE_READ,
    176		.set = "R",
    177		.clear = ".",
    178	}, {
    179		.mask = _PAGE_PRESENT,
    180		.val = _PAGE_PRESENT,
    181		.set = "V",
    182		.clear = ".",
    183	}
    184};
    185
    186/* Page Level */
    187struct pg_level {
    188	const char *name;
    189	u64 mask;
    190};
    191
    192static struct pg_level pg_level[] = {
    193	{ /* pgd */
    194		.name = "PGD",
    195	}, { /* p4d */
    196		.name = (CONFIG_PGTABLE_LEVELS > 4) ? "P4D" : "PGD",
    197	}, { /* pud */
    198		.name = (CONFIG_PGTABLE_LEVELS > 3) ? "PUD" : "PGD",
    199	}, { /* pmd */
    200		.name = (CONFIG_PGTABLE_LEVELS > 2) ? "PMD" : "PGD",
    201	}, { /* pte */
    202		.name = "PTE",
    203	},
    204};
    205
    206static void dump_prot(struct pg_state *st)
    207{
    208	unsigned int i;
    209
    210	for (i = 0; i < ARRAY_SIZE(pte_bits); i++) {
    211		const char *s;
    212
    213		if ((st->current_prot & pte_bits[i].mask) == pte_bits[i].val)
    214			s = pte_bits[i].set;
    215		else
    216			s = pte_bits[i].clear;
    217
    218		if (s)
    219			pt_dump_seq_printf(st->seq, " %s", s);
    220	}
    221}
    222
    223#ifdef CONFIG_64BIT
    224#define ADDR_FORMAT	"0x%016lx"
    225#else
    226#define ADDR_FORMAT	"0x%08lx"
    227#endif
    228static void dump_addr(struct pg_state *st, unsigned long addr)
    229{
    230	static const char units[] = "KMGTPE";
    231	const char *unit = units;
    232	unsigned long delta;
    233
    234	pt_dump_seq_printf(st->seq, ADDR_FORMAT "-" ADDR_FORMAT "   ",
    235			   st->start_address, addr);
    236
    237	pt_dump_seq_printf(st->seq, " " ADDR_FORMAT " ", st->start_pa);
    238	delta = (addr - st->start_address) >> 10;
    239
    240	while (!(delta & 1023) && unit[1]) {
    241		delta >>= 10;
    242		unit++;
    243	}
    244
    245	pt_dump_seq_printf(st->seq, "%9lu%c %s", delta, *unit,
    246			   pg_level[st->level].name);
    247}
    248
    249static void note_prot_wx(struct pg_state *st, unsigned long addr)
    250{
    251	if (!st->check_wx)
    252		return;
    253
    254	if ((st->current_prot & (_PAGE_WRITE | _PAGE_EXEC)) !=
    255	    (_PAGE_WRITE | _PAGE_EXEC))
    256		return;
    257
    258	WARN_ONCE(1, "riscv/mm: Found insecure W+X mapping at address %p/%pS\n",
    259		  (void *)st->start_address, (void *)st->start_address);
    260
    261	st->wx_pages += (addr - st->start_address) / PAGE_SIZE;
    262}
    263
    264static void note_page(struct ptdump_state *pt_st, unsigned long addr,
    265		      int level, u64 val)
    266{
    267	struct pg_state *st = container_of(pt_st, struct pg_state, ptdump);
    268	u64 pa = PFN_PHYS(pte_pfn(__pte(val)));
    269	u64 prot = 0;
    270
    271	if (level >= 0)
    272		prot = val & pg_level[level].mask;
    273
    274	if (st->level == -1) {
    275		st->level = level;
    276		st->current_prot = prot;
    277		st->start_address = addr;
    278		st->start_pa = pa;
    279		st->last_pa = pa;
    280		pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
    281	} else if (prot != st->current_prot ||
    282		   level != st->level || addr >= st->marker[1].start_address) {
    283		if (st->current_prot) {
    284			note_prot_wx(st, addr);
    285			dump_addr(st, addr);
    286			dump_prot(st);
    287			pt_dump_seq_puts(st->seq, "\n");
    288		}
    289
    290		while (addr >= st->marker[1].start_address) {
    291			st->marker++;
    292			pt_dump_seq_printf(st->seq, "---[ %s ]---\n",
    293					   st->marker->name);
    294		}
    295
    296		st->start_address = addr;
    297		st->start_pa = pa;
    298		st->last_pa = pa;
    299		st->current_prot = prot;
    300		st->level = level;
    301	} else {
    302		st->last_pa = pa;
    303	}
    304}
    305
    306static void ptdump_walk(struct seq_file *s, struct ptd_mm_info *pinfo)
    307{
    308	struct pg_state st = {
    309		.seq = s,
    310		.marker = pinfo->markers,
    311		.level = -1,
    312		.ptdump = {
    313			.note_page = note_page,
    314			.range = (struct ptdump_range[]) {
    315				{pinfo->base_addr, pinfo->end},
    316				{0, 0}
    317			}
    318		}
    319	};
    320
    321	ptdump_walk_pgd(&st.ptdump, pinfo->mm, NULL);
    322}
    323
    324void ptdump_check_wx(void)
    325{
    326	struct pg_state st = {
    327		.seq = NULL,
    328		.marker = (struct addr_marker[]) {
    329			{0, NULL},
    330			{-1, NULL},
    331		},
    332		.level = -1,
    333		.check_wx = true,
    334		.ptdump = {
    335			.note_page = note_page,
    336			.range = (struct ptdump_range[]) {
    337				{KERN_VIRT_START, ULONG_MAX},
    338				{0, 0}
    339			}
    340		}
    341	};
    342
    343	ptdump_walk_pgd(&st.ptdump, &init_mm, NULL);
    344
    345	if (st.wx_pages)
    346		pr_warn("Checked W+X mappings: failed, %lu W+X pages found\n",
    347			st.wx_pages);
    348	else
    349		pr_info("Checked W+X mappings: passed, no W+X pages found\n");
    350}
    351
    352static int ptdump_show(struct seq_file *m, void *v)
    353{
    354	ptdump_walk(m, m->private);
    355
    356	return 0;
    357}
    358
    359DEFINE_SHOW_ATTRIBUTE(ptdump);
    360
    361static int __init ptdump_init(void)
    362{
    363	unsigned int i, j;
    364
    365#ifdef CONFIG_KASAN
    366	address_markers[KASAN_SHADOW_START_NR].start_address = KASAN_SHADOW_START;
    367	address_markers[KASAN_SHADOW_END_NR].start_address = KASAN_SHADOW_END;
    368#endif
    369	address_markers[FIXMAP_START_NR].start_address = FIXADDR_START;
    370	address_markers[FIXMAP_END_NR].start_address = FIXADDR_TOP;
    371	address_markers[PCI_IO_START_NR].start_address = PCI_IO_START;
    372	address_markers[PCI_IO_END_NR].start_address = PCI_IO_END;
    373#ifdef CONFIG_SPARSEMEM_VMEMMAP
    374	address_markers[VMEMMAP_START_NR].start_address = VMEMMAP_START;
    375	address_markers[VMEMMAP_END_NR].start_address = VMEMMAP_END;
    376#endif
    377	address_markers[VMALLOC_START_NR].start_address = VMALLOC_START;
    378	address_markers[VMALLOC_END_NR].start_address = VMALLOC_END;
    379	address_markers[PAGE_OFFSET_NR].start_address = PAGE_OFFSET;
    380#ifdef CONFIG_64BIT
    381	address_markers[MODULES_MAPPING_NR].start_address = MODULES_VADDR;
    382	address_markers[KERNEL_MAPPING_NR].start_address = kernel_map.virt_addr;
    383#endif
    384
    385	kernel_ptd_info.base_addr = KERN_VIRT_START;
    386
    387	for (i = 0; i < ARRAY_SIZE(pg_level); i++)
    388		for (j = 0; j < ARRAY_SIZE(pte_bits); j++)
    389			pg_level[i].mask |= pte_bits[j].mask;
    390
    391	debugfs_create_file("kernel_page_tables", 0400, NULL, &kernel_ptd_info,
    392			    &ptdump_fops);
    393#ifdef CONFIG_EFI
    394	if (efi_enabled(EFI_RUNTIME_SERVICES))
    395		debugfs_create_file("efi_page_tables", 0400, NULL, &efi_ptd_info,
    396				    &ptdump_fops);
    397#endif
    398
    399	return 0;
    400}
    401
    402device_initcall(ptdump_init);