Merge tag 'mips_6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux
[linux-2.6-microblaze.git] / arch / arm64 / mm / ptdump.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2014, The Linux Foundation. All rights reserved.
4  * Debug helper to dump the current kernel pagetables of the system
5  * so that we can see what the various memory ranges are set to.
6  *
7  * Derived from x86 and arm implementation:
8  * (C) Copyright 2008 Intel Corporation
9  *
10  * Author: Arjan van de Ven <arjan@linux.intel.com>
11  */
12 #include <linux/debugfs.h>
13 #include <linux/errno.h>
14 #include <linux/fs.h>
15 #include <linux/io.h>
16 #include <linux/init.h>
17 #include <linux/mm.h>
18 #include <linux/ptdump.h>
19 #include <linux/sched.h>
20 #include <linux/seq_file.h>
21
22 #include <asm/fixmap.h>
23 #include <asm/kasan.h>
24 #include <asm/memory.h>
25 #include <asm/pgtable-hwdef.h>
26 #include <asm/ptdump.h>
27
28
29 #define pt_dump_seq_printf(m, fmt, args...)     \
30 ({                                              \
31         if (m)                                  \
32                 seq_printf(m, fmt, ##args);     \
33 })
34
35 #define pt_dump_seq_puts(m, fmt)        \
36 ({                                      \
37         if (m)                          \
38                 seq_printf(m, fmt);     \
39 })
40
41 /*
42  * The page dumper groups page table entries of the same type into a single
43  * description. It uses pg_state to track the range information while
44  * iterating over the pte entries. When the continuity is broken it then
45  * dumps out a description of the range.
46  */
47 struct pg_state {
48         struct ptdump_state ptdump;
49         struct seq_file *seq;
50         const struct addr_marker *marker;
51         const struct mm_struct *mm;
52         unsigned long start_address;
53         int level;
54         u64 current_prot;
55         bool check_wx;
56         unsigned long wx_pages;
57         unsigned long uxn_pages;
58 };
59
60 struct prot_bits {
61         u64             mask;
62         u64             val;
63         const char      *set;
64         const char      *clear;
65 };
66
67 static const struct prot_bits pte_bits[] = {
68         {
69                 .mask   = PTE_VALID,
70                 .val    = PTE_VALID,
71                 .set    = " ",
72                 .clear  = "F",
73         }, {
74                 .mask   = PTE_USER,
75                 .val    = PTE_USER,
76                 .set    = "USR",
77                 .clear  = "   ",
78         }, {
79                 .mask   = PTE_RDONLY,
80                 .val    = PTE_RDONLY,
81                 .set    = "ro",
82                 .clear  = "RW",
83         }, {
84                 .mask   = PTE_PXN,
85                 .val    = PTE_PXN,
86                 .set    = "NX",
87                 .clear  = "x ",
88         }, {
89                 .mask   = PTE_SHARED,
90                 .val    = PTE_SHARED,
91                 .set    = "SHD",
92                 .clear  = "   ",
93         }, {
94                 .mask   = PTE_AF,
95                 .val    = PTE_AF,
96                 .set    = "AF",
97                 .clear  = "  ",
98         }, {
99                 .mask   = PTE_NG,
100                 .val    = PTE_NG,
101                 .set    = "NG",
102                 .clear  = "  ",
103         }, {
104                 .mask   = PTE_CONT,
105                 .val    = PTE_CONT,
106                 .set    = "CON",
107                 .clear  = "   ",
108         }, {
109                 .mask   = PTE_TABLE_BIT,
110                 .val    = PTE_TABLE_BIT,
111                 .set    = "   ",
112                 .clear  = "BLK",
113         }, {
114                 .mask   = PTE_UXN,
115                 .val    = PTE_UXN,
116                 .set    = "UXN",
117                 .clear  = "   ",
118         }, {
119                 .mask   = PTE_GP,
120                 .val    = PTE_GP,
121                 .set    = "GP",
122                 .clear  = "  ",
123         }, {
124                 .mask   = PTE_ATTRINDX_MASK,
125                 .val    = PTE_ATTRINDX(MT_DEVICE_nGnRnE),
126                 .set    = "DEVICE/nGnRnE",
127         }, {
128                 .mask   = PTE_ATTRINDX_MASK,
129                 .val    = PTE_ATTRINDX(MT_DEVICE_nGnRE),
130                 .set    = "DEVICE/nGnRE",
131         }, {
132                 .mask   = PTE_ATTRINDX_MASK,
133                 .val    = PTE_ATTRINDX(MT_NORMAL_NC),
134                 .set    = "MEM/NORMAL-NC",
135         }, {
136                 .mask   = PTE_ATTRINDX_MASK,
137                 .val    = PTE_ATTRINDX(MT_NORMAL),
138                 .set    = "MEM/NORMAL",
139         }, {
140                 .mask   = PTE_ATTRINDX_MASK,
141                 .val    = PTE_ATTRINDX(MT_NORMAL_TAGGED),
142                 .set    = "MEM/NORMAL-TAGGED",
143         }
144 };
145
146 struct pg_level {
147         const struct prot_bits *bits;
148         char name[4];
149         int num;
150         u64 mask;
151 };
152
153 static struct pg_level pg_level[] __ro_after_init = {
154         { /* pgd */
155                 .name   = "PGD",
156                 .bits   = pte_bits,
157                 .num    = ARRAY_SIZE(pte_bits),
158         }, { /* p4d */
159                 .name   = "P4D",
160                 .bits   = pte_bits,
161                 .num    = ARRAY_SIZE(pte_bits),
162         }, { /* pud */
163                 .name   = "PUD",
164                 .bits   = pte_bits,
165                 .num    = ARRAY_SIZE(pte_bits),
166         }, { /* pmd */
167                 .name   = "PMD",
168                 .bits   = pte_bits,
169                 .num    = ARRAY_SIZE(pte_bits),
170         }, { /* pte */
171                 .name   = "PTE",
172                 .bits   = pte_bits,
173                 .num    = ARRAY_SIZE(pte_bits),
174         },
175 };
176
177 static void dump_prot(struct pg_state *st, const struct prot_bits *bits,
178                         size_t num)
179 {
180         unsigned i;
181
182         for (i = 0; i < num; i++, bits++) {
183                 const char *s;
184
185                 if ((st->current_prot & bits->mask) == bits->val)
186                         s = bits->set;
187                 else
188                         s = bits->clear;
189
190                 if (s)
191                         pt_dump_seq_printf(st->seq, " %s", s);
192         }
193 }
194
195 static void note_prot_uxn(struct pg_state *st, unsigned long addr)
196 {
197         if (!st->check_wx)
198                 return;
199
200         if ((st->current_prot & PTE_UXN) == PTE_UXN)
201                 return;
202
203         WARN_ONCE(1, "arm64/mm: Found non-UXN mapping at address %p/%pS\n",
204                   (void *)st->start_address, (void *)st->start_address);
205
206         st->uxn_pages += (addr - st->start_address) / PAGE_SIZE;
207 }
208
209 static void note_prot_wx(struct pg_state *st, unsigned long addr)
210 {
211         if (!st->check_wx)
212                 return;
213         if ((st->current_prot & PTE_RDONLY) == PTE_RDONLY)
214                 return;
215         if ((st->current_prot & PTE_PXN) == PTE_PXN)
216                 return;
217
218         WARN_ONCE(1, "arm64/mm: Found insecure W+X mapping at address %p/%pS\n",
219                   (void *)st->start_address, (void *)st->start_address);
220
221         st->wx_pages += (addr - st->start_address) / PAGE_SIZE;
222 }
223
224 static void note_page(struct ptdump_state *pt_st, unsigned long addr, int level,
225                       u64 val)
226 {
227         struct pg_state *st = container_of(pt_st, struct pg_state, ptdump);
228         static const char units[] = "KMGTPE";
229         u64 prot = 0;
230
231         /* check if the current level has been folded dynamically */
232         if ((level == 1 && mm_p4d_folded(st->mm)) ||
233             (level == 2 && mm_pud_folded(st->mm)))
234                 level = 0;
235
236         if (level >= 0)
237                 prot = val & pg_level[level].mask;
238
239         if (st->level == -1) {
240                 st->level = level;
241                 st->current_prot = prot;
242                 st->start_address = addr;
243                 pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
244         } else if (prot != st->current_prot || level != st->level ||
245                    addr >= st->marker[1].start_address) {
246                 const char *unit = units;
247                 unsigned long delta;
248
249                 if (st->current_prot) {
250                         note_prot_uxn(st, addr);
251                         note_prot_wx(st, addr);
252                 }
253
254                 pt_dump_seq_printf(st->seq, "0x%016lx-0x%016lx   ",
255                                    st->start_address, addr);
256
257                 delta = (addr - st->start_address) >> 10;
258                 while (!(delta & 1023) && unit[1]) {
259                         delta >>= 10;
260                         unit++;
261                 }
262                 pt_dump_seq_printf(st->seq, "%9lu%c %s", delta, *unit,
263                                    pg_level[st->level].name);
264                 if (st->current_prot && pg_level[st->level].bits)
265                         dump_prot(st, pg_level[st->level].bits,
266                                   pg_level[st->level].num);
267                 pt_dump_seq_puts(st->seq, "\n");
268
269                 if (addr >= st->marker[1].start_address) {
270                         st->marker++;
271                         pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
272                 }
273
274                 st->start_address = addr;
275                 st->current_prot = prot;
276                 st->level = level;
277         }
278
279         if (addr >= st->marker[1].start_address) {
280                 st->marker++;
281                 pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
282         }
283
284 }
285
286 void ptdump_walk(struct seq_file *s, struct ptdump_info *info)
287 {
288         unsigned long end = ~0UL;
289         struct pg_state st;
290
291         if (info->base_addr < TASK_SIZE_64)
292                 end = TASK_SIZE_64;
293
294         st = (struct pg_state){
295                 .seq = s,
296                 .marker = info->markers,
297                 .mm = info->mm,
298                 .level = -1,
299                 .ptdump = {
300                         .note_page = note_page,
301                         .range = (struct ptdump_range[]){
302                                 {info->base_addr, end},
303                                 {0, 0}
304                         }
305                 }
306         };
307
308         ptdump_walk_pgd(&st.ptdump, info->mm, NULL);
309 }
310
311 static void __init ptdump_initialize(void)
312 {
313         unsigned i, j;
314
315         for (i = 0; i < ARRAY_SIZE(pg_level); i++)
316                 if (pg_level[i].bits)
317                         for (j = 0; j < pg_level[i].num; j++)
318                                 pg_level[i].mask |= pg_level[i].bits[j].mask;
319 }
320
321 static struct ptdump_info kernel_ptdump_info __ro_after_init = {
322         .mm             = &init_mm,
323 };
324
325 bool ptdump_check_wx(void)
326 {
327         struct pg_state st = {
328                 .seq = NULL,
329                 .marker = (struct addr_marker[]) {
330                         { 0, NULL},
331                         { -1, NULL},
332                 },
333                 .level = -1,
334                 .check_wx = true,
335                 .ptdump = {
336                         .note_page = note_page,
337                         .range = (struct ptdump_range[]) {
338                                 {_PAGE_OFFSET(vabits_actual), ~0UL},
339                                 {0, 0}
340                         }
341                 }
342         };
343
344         ptdump_walk_pgd(&st.ptdump, &init_mm, NULL);
345
346         if (st.wx_pages || st.uxn_pages) {
347                 pr_warn("Checked W+X mappings: FAILED, %lu W+X pages found, %lu non-UXN pages found\n",
348                         st.wx_pages, st.uxn_pages);
349
350                 return false;
351         } else {
352                 pr_info("Checked W+X mappings: passed, no W+X pages found\n");
353
354                 return true;
355         }
356 }
357
358 static int __init ptdump_init(void)
359 {
360         u64 page_offset = _PAGE_OFFSET(vabits_actual);
361         u64 vmemmap_start = (u64)virt_to_page((void *)page_offset);
362         struct addr_marker m[] = {
363                 { PAGE_OFFSET,          "Linear Mapping start" },
364                 { PAGE_END,             "Linear Mapping end" },
365 #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
366                 { KASAN_SHADOW_START,   "Kasan shadow start" },
367                 { KASAN_SHADOW_END,     "Kasan shadow end" },
368 #endif
369                 { MODULES_VADDR,        "Modules start" },
370                 { MODULES_END,          "Modules end" },
371                 { VMALLOC_START,        "vmalloc() area" },
372                 { VMALLOC_END,          "vmalloc() end" },
373                 { vmemmap_start,        "vmemmap start" },
374                 { VMEMMAP_END,          "vmemmap end" },
375                 { PCI_IO_START,         "PCI I/O start" },
376                 { PCI_IO_END,           "PCI I/O end" },
377                 { FIXADDR_TOT_START,    "Fixmap start" },
378                 { FIXADDR_TOP,          "Fixmap end" },
379                 { -1,                   NULL },
380         };
381         static struct addr_marker address_markers[ARRAY_SIZE(m)] __ro_after_init;
382
383         kernel_ptdump_info.markers = memcpy(address_markers, m, sizeof(m));
384         kernel_ptdump_info.base_addr = page_offset;
385
386         ptdump_initialize();
387         ptdump_debugfs_register(&kernel_ptdump_info, "kernel_page_tables");
388         return 0;
389 }
390 device_initcall(ptdump_init);