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