aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/mm
diff options
context:
space:
mode:
authorBorislav Petkov <bp@suse.de>2014-01-18 06:48:14 -0500
committerMatt Fleming <matt.fleming@intel.com>2014-03-04 11:16:17 -0500
commitef6bea6ddf0e76077d0798e57b374015b23a837e (patch)
tree262c82db081fe5ea6b3c1da63e6c9b930694a033 /arch/x86/mm
parent9b7d204982cf6e4e5b9b0f8bff151e48250cce58 (diff)
x86, ptdump: Add the functionality to dump an arbitrary pagetable
With reusing the ->trampoline_pgd page table for mapping EFI regions in order to use them after having switched to EFI virtual mode, it is very useful to be able to dump aforementioned page table in dmesg. This adds that functionality through the walk_pgd_level() interface which can be called from somewhere else. The original functionality of dumping to debugfs remains untouched. Cc: Arjan van de Ven <arjan@linux.intel.com> Signed-off-by: Borislav Petkov <bp@suse.de> Tested-by: Toshi Kani <toshi.kani@hp.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Diffstat (limited to 'arch/x86/mm')
-rw-r--r--arch/x86/mm/dump_pagetables.c84
1 files changed, 54 insertions, 30 deletions
diff --git a/arch/x86/mm/dump_pagetables.c b/arch/x86/mm/dump_pagetables.c
index 0002a3a33081..20621d753d5f 100644
--- a/arch/x86/mm/dump_pagetables.c
+++ b/arch/x86/mm/dump_pagetables.c
@@ -30,6 +30,7 @@ struct pg_state {
30 unsigned long start_address; 30 unsigned long start_address;
31 unsigned long current_address; 31 unsigned long current_address;
32 const struct addr_marker *marker; 32 const struct addr_marker *marker;
33 bool to_dmesg;
33}; 34};
34 35
35struct addr_marker { 36struct addr_marker {
@@ -88,10 +89,28 @@ static struct addr_marker address_markers[] = {
88#define PUD_LEVEL_MULT (PTRS_PER_PMD * PMD_LEVEL_MULT) 89#define PUD_LEVEL_MULT (PTRS_PER_PMD * PMD_LEVEL_MULT)
89#define PGD_LEVEL_MULT (PTRS_PER_PUD * PUD_LEVEL_MULT) 90#define PGD_LEVEL_MULT (PTRS_PER_PUD * PUD_LEVEL_MULT)
90 91
92#define pt_dump_seq_printf(m, to_dmesg, fmt, args...) \
93({ \
94 if (to_dmesg) \
95 printk(KERN_INFO fmt, ##args); \
96 else \
97 if (m) \
98 seq_printf(m, fmt, ##args); \
99})
100
101#define pt_dump_cont_printf(m, to_dmesg, fmt, args...) \
102({ \
103 if (to_dmesg) \
104 printk(KERN_CONT fmt, ##args); \
105 else \
106 if (m) \
107 seq_printf(m, fmt, ##args); \
108})
109
91/* 110/*
92 * Print a readable form of a pgprot_t to the seq_file 111 * Print a readable form of a pgprot_t to the seq_file
93 */ 112 */
94static void printk_prot(struct seq_file *m, pgprot_t prot, int level) 113static void printk_prot(struct seq_file *m, pgprot_t prot, int level, bool dmsg)
95{ 114{
96 pgprotval_t pr = pgprot_val(prot); 115 pgprotval_t pr = pgprot_val(prot);
97 static const char * const level_name[] = 116 static const char * const level_name[] =
@@ -99,47 +118,47 @@ static void printk_prot(struct seq_file *m, pgprot_t prot, int level)
99 118
100 if (!pgprot_val(prot)) { 119 if (!pgprot_val(prot)) {
101 /* Not present */ 120 /* Not present */
102 seq_printf(m, " "); 121 pt_dump_cont_printf(m, dmsg, " ");
103 } else { 122 } else {
104 if (pr & _PAGE_USER) 123 if (pr & _PAGE_USER)
105 seq_printf(m, "USR "); 124 pt_dump_cont_printf(m, dmsg, "USR ");
106 else 125 else
107 seq_printf(m, " "); 126 pt_dump_cont_printf(m, dmsg, " ");
108 if (pr & _PAGE_RW) 127 if (pr & _PAGE_RW)
109 seq_printf(m, "RW "); 128 pt_dump_cont_printf(m, dmsg, "RW ");
110 else 129 else
111 seq_printf(m, "ro "); 130 pt_dump_cont_printf(m, dmsg, "ro ");
112 if (pr & _PAGE_PWT) 131 if (pr & _PAGE_PWT)
113 seq_printf(m, "PWT "); 132 pt_dump_cont_printf(m, dmsg, "PWT ");
114 else 133 else
115 seq_printf(m, " "); 134 pt_dump_cont_printf(m, dmsg, " ");
116 if (pr & _PAGE_PCD) 135 if (pr & _PAGE_PCD)
117 seq_printf(m, "PCD "); 136 pt_dump_cont_printf(m, dmsg, "PCD ");
118 else 137 else
119 seq_printf(m, " "); 138 pt_dump_cont_printf(m, dmsg, " ");
120 139
121 /* Bit 9 has a different meaning on level 3 vs 4 */ 140 /* Bit 9 has a different meaning on level 3 vs 4 */
122 if (level <= 3) { 141 if (level <= 3) {
123 if (pr & _PAGE_PSE) 142 if (pr & _PAGE_PSE)
124 seq_printf(m, "PSE "); 143 pt_dump_cont_printf(m, dmsg, "PSE ");
125 else 144 else
126 seq_printf(m, " "); 145 pt_dump_cont_printf(m, dmsg, " ");
127 } else { 146 } else {
128 if (pr & _PAGE_PAT) 147 if (pr & _PAGE_PAT)
129 seq_printf(m, "pat "); 148 pt_dump_cont_printf(m, dmsg, "pat ");
130 else 149 else
131 seq_printf(m, " "); 150 pt_dump_cont_printf(m, dmsg, " ");
132 } 151 }
133 if (pr & _PAGE_GLOBAL) 152 if (pr & _PAGE_GLOBAL)
134 seq_printf(m, "GLB "); 153 pt_dump_cont_printf(m, dmsg, "GLB ");
135 else 154 else
136 seq_printf(m, " "); 155 pt_dump_cont_printf(m, dmsg, " ");
137 if (pr & _PAGE_NX) 156 if (pr & _PAGE_NX)
138 seq_printf(m, "NX "); 157 pt_dump_cont_printf(m, dmsg, "NX ");
139 else 158 else
140 seq_printf(m, "x "); 159 pt_dump_cont_printf(m, dmsg, "x ");
141 } 160 }
142 seq_printf(m, "%s\n", level_name[level]); 161 pt_dump_cont_printf(m, dmsg, "%s\n", level_name[level]);
143} 162}
144 163
145/* 164/*
@@ -178,7 +197,8 @@ static void note_page(struct seq_file *m, struct pg_state *st,
178 st->current_prot = new_prot; 197 st->current_prot = new_prot;
179 st->level = level; 198 st->level = level;
180 st->marker = address_markers; 199 st->marker = address_markers;
181 seq_printf(m, "---[ %s ]---\n", st->marker->name); 200 pt_dump_seq_printf(m, st->to_dmesg, "---[ %s ]---\n",
201 st->marker->name);
182 } else if (prot != cur || level != st->level || 202 } else if (prot != cur || level != st->level ||
183 st->current_address >= st->marker[1].start_address) { 203 st->current_address >= st->marker[1].start_address) {
184 const char *unit = units; 204 const char *unit = units;
@@ -188,17 +208,17 @@ static void note_page(struct seq_file *m, struct pg_state *st,
188 /* 208 /*
189 * Now print the actual finished series 209 * Now print the actual finished series
190 */ 210 */
191 seq_printf(m, "0x%0*lx-0x%0*lx ", 211 pt_dump_seq_printf(m, st->to_dmesg, "0x%0*lx-0x%0*lx ",
192 width, st->start_address, 212 width, st->start_address,
193 width, st->current_address); 213 width, st->current_address);
194 214
195 delta = (st->current_address - st->start_address) >> 10; 215 delta = (st->current_address - st->start_address) >> 10;
196 while (!(delta & 1023) && unit[1]) { 216 while (!(delta & 1023) && unit[1]) {
197 delta >>= 10; 217 delta >>= 10;
198 unit++; 218 unit++;
199 } 219 }
200 seq_printf(m, "%9lu%c ", delta, *unit); 220 pt_dump_cont_printf(m, st->to_dmesg, "%9lu%c ", delta, *unit);
201 printk_prot(m, st->current_prot, st->level); 221 printk_prot(m, st->current_prot, st->level, st->to_dmesg);
202 222
203 /* 223 /*
204 * We print markers for special areas of address space, 224 * We print markers for special areas of address space,
@@ -207,7 +227,8 @@ static void note_page(struct seq_file *m, struct pg_state *st,
207 */ 227 */
208 if (st->current_address >= st->marker[1].start_address) { 228 if (st->current_address >= st->marker[1].start_address) {
209 st->marker++; 229 st->marker++;
210 seq_printf(m, "---[ %s ]---\n", st->marker->name); 230 pt_dump_seq_printf(m, st->to_dmesg, "---[ %s ]---\n",
231 st->marker->name);
211 } 232 }
212 233
213 st->start_address = st->current_address; 234 st->start_address = st->current_address;
@@ -296,7 +317,7 @@ static void walk_pud_level(struct seq_file *m, struct pg_state *st, pgd_t addr,
296#define pgd_none(a) pud_none(__pud(pgd_val(a))) 317#define pgd_none(a) pud_none(__pud(pgd_val(a)))
297#endif 318#endif
298 319
299static void walk_pgd_level(struct seq_file *m) 320void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd)
300{ 321{
301#ifdef CONFIG_X86_64 322#ifdef CONFIG_X86_64
302 pgd_t *start = (pgd_t *) &init_level4_pgt; 323 pgd_t *start = (pgd_t *) &init_level4_pgt;
@@ -304,9 +325,12 @@ static void walk_pgd_level(struct seq_file *m)
304 pgd_t *start = swapper_pg_dir; 325 pgd_t *start = swapper_pg_dir;
305#endif 326#endif
306 int i; 327 int i;
307 struct pg_state st; 328 struct pg_state st = {};
308 329
309 memset(&st, 0, sizeof(st)); 330 if (pgd) {
331 start = pgd;
332 st.to_dmesg = true;
333 }
310 334
311 for (i = 0; i < PTRS_PER_PGD; i++) { 335 for (i = 0; i < PTRS_PER_PGD; i++) {
312 st.current_address = normalize_addr(i * PGD_LEVEL_MULT); 336 st.current_address = normalize_addr(i * PGD_LEVEL_MULT);
@@ -331,7 +355,7 @@ static void walk_pgd_level(struct seq_file *m)
331 355
332static int ptdump_show(struct seq_file *m, void *v) 356static int ptdump_show(struct seq_file *m, void *v)
333{ 357{
334 walk_pgd_level(m); 358 ptdump_walk_pgd_level(m, NULL);
335 return 0; 359 return 0;
336} 360}
337 361