xref: /openbsd-src/regress/sys/arch/amd64/dump_tables/dump_tables.c (revision e9dcde5677852948d4d87ff9cb8593d3d445da15)
1*e9dcde56Sguenther /*	$OpenBSD: dump_tables.c,v 1.8 2023/05/11 22:28:38 guenther Exp $	*/
2ec49dea9Sguenther /*
3*e9dcde56Sguenther  * Copyright (c) 2019,2023 Philip Guenther <guenther@openbsd.org>
4ec49dea9Sguenther  *
5ec49dea9Sguenther  * Permission to use, copy, modify, and distribute this software for any
6ec49dea9Sguenther  * purpose with or without fee is hereby granted, provided that the above
7ec49dea9Sguenther  * copyright notice and this permission notice appear in all copies.
8ec49dea9Sguenther  *
9ec49dea9Sguenther  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10ec49dea9Sguenther  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11ec49dea9Sguenther  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12ec49dea9Sguenther  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13ec49dea9Sguenther  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14ec49dea9Sguenther  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15ec49dea9Sguenther  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16ec49dea9Sguenther  */
17ec49dea9Sguenther 
18ec49dea9Sguenther /*
19ec49dea9Sguenther  * Dump amd64 page tables to text for analysis
20ec49dea9Sguenther  * Requires "kern.allowkmem=1" sysctl
21ec49dea9Sguenther  */
22ec49dea9Sguenther 
2349a6e16fSderaadt #include <sys/param.h>	/* PAGE_SIZE and other things */
24098d1aeeSguenther #include <sys/sysctl.h>
25ec49dea9Sguenther #include <sys/time.h>
26ec49dea9Sguenther #include <uvm/uvm_extern.h>
27ec49dea9Sguenther #include <machine/pmap.h>
28ec49dea9Sguenther #include <machine/pcb.h>
29ec49dea9Sguenther 
30ec49dea9Sguenther /*
31ec49dea9Sguenther  * Getting struct pmap from <machine/pmap.h> is too hard right now.
32ec49dea9Sguenther  * Just extract it and go.
33ec49dea9Sguenther  */
34ec49dea9Sguenther #include "struct_pmap.h"
35ec49dea9Sguenther 
36ec49dea9Sguenther #define PG_1GFRAME	0x000fffffc0000000UL	/* should be in pmap.h */
37ec49dea9Sguenther 
38*e9dcde56Sguenther #define PG_PK_SHIFT	59
39*e9dcde56Sguenther 
40*e9dcde56Sguenther 
41ec49dea9Sguenther #include <err.h>
42ec49dea9Sguenther #include <fcntl.h>
43ec49dea9Sguenther #include <kvm.h>
44ec49dea9Sguenther #include <nlist.h>
45ec49dea9Sguenther #include <stdlib.h>
46ec49dea9Sguenther #include <unistd.h>
47ec49dea9Sguenther 
48ec49dea9Sguenther void
usage(int status)49c8edf930Sguenther usage(int status)
50ec49dea9Sguenther {
51ec49dea9Sguenther 	printf("\
52ec49dea9Sguenther Usage: dump_tables [-1234dlmpr]\n\
53ec49dea9Sguenther  -1234\tShow the specified levels in the page tables.\n\
54ec49dea9Sguenther  -d\tHide the entries in the direct-map.\n\
55c8edf930Sguenther  -h\tShow this usage message.\n\
56ec49dea9Sguenther  -l\tShow the leaf entries, whether 4kB, 2MB, or 1GB.\n\
57ec49dea9Sguenther  -m\tShow the Meltdown U-K page tables instead.\n\
58ec49dea9Sguenther  -p\tHide the entries through the recursive PTE mapping.\n\
59ec49dea9Sguenther  -r\tSuppress the 'U'sed and 'M'odified attributes to increase.\n\
60ec49dea9Sguenther \treproducibility.\n\
61098d1aeeSguenther  -u\tShow the page tables for PID 1 instead of those for kernel threads.\n\
62ec49dea9Sguenther \n\
63c8edf930Sguenther Dump the page tables, including intermediate levels, showing for\n\
64c8edf930Sguenther each valid entry the virtual-address (VA) it applies to, the level\n\
65c8edf930Sguenther of page table, the index of the entry within its page, the physical\n\
66c8edf930Sguenther address (PA) it points to, the size of leaf page it points to, the\n\
67c8edf930Sguenther attributes on the entry, the effective attributes for those affected\n\
68d077b070Sguenther by higher levels of page table, and the slot type for those which have\n\
69d077b070Sguenther a particular name.\n\n\
70c8edf930Sguenther If none of the options -1234l are used, then all levels will be shown.\n\
71c8edf930Sguenther ");
72c8edf930Sguenther 	exit(status);
73ec49dea9Sguenther }
74ec49dea9Sguenther 
75ec49dea9Sguenther 
76ec49dea9Sguenther kvm_t *k;
77ec49dea9Sguenther pd_entry_t *pt[5];
78ec49dea9Sguenther int meltdown, hide_direct, hide_pte, reproducible, show[5], show_leaves;
79098d1aeeSguenther int user_proc;
80ec49dea9Sguenther 
81ec49dea9Sguenther struct nlist proc0[] = { { "_proc0paddr" }, { NULL } };
82ec49dea9Sguenther 
83ec49dea9Sguenther #define KGET(addr, var)							\
84ec49dea9Sguenther 	KGETRET(addr, &var, sizeof var, #var)
85ec49dea9Sguenther #define KGETRET(addr, p, s, msg)	do {				\
86ec49dea9Sguenther 	if (kvm_read(k, addr, p, s) != s)				\
87ec49dea9Sguenther 		errx(1, "cannot read %s: %s", msg, kvm_geterr(k));	\
88ec49dea9Sguenther } while (0)
89ec49dea9Sguenther #define KGETPT_PA(addr, level)						\
90ec49dea9Sguenther 	KGETPT_VA(PMAP_DIRECT_MAP(addr), level)
91ec49dea9Sguenther #define KGETPT_VA(addr, level)						\
92098d1aeeSguenther 	KGETRET(addr, pt[level], PAGE_SIZE, ptname[level])
93ec49dea9Sguenther 
94ec49dea9Sguenther const int shift[] = {
95ec49dea9Sguenther     [3] = L3_SHIFT,
96ec49dea9Sguenther     [2] = L2_SHIFT,
97ec49dea9Sguenther     [1] = L1_SHIFT,
98ec49dea9Sguenther };
99ec49dea9Sguenther const char * const ptname[] = {
100ec49dea9Sguenther     [4] = "pml4",
101ec49dea9Sguenther     [3] = "pt3",
102ec49dea9Sguenther     [2] = "pt2",
103ec49dea9Sguenther     [1] = "pt1",
104ec49dea9Sguenther };
105ec49dea9Sguenther 
106ec49dea9Sguenther /* Not currently used */
107ec49dea9Sguenther const pd_entry_t ign_normal[] = {
108ec49dea9Sguenther     [4] =		0x0000000000000f40UL,
109ec49dea9Sguenther #define	IGN_1GFRAME	0x0000000000000e00UL
110ec49dea9Sguenther     [3] =		0x0000000000000f40UL,
111ec49dea9Sguenther #define	IGN_LGFRAME	0x0000000000000e00UL
112ec49dea9Sguenther     [2] =		0x0000000000000f40UL,
113ec49dea9Sguenther     [1] =		0x0000000000000e00UL,
114ec49dea9Sguenther };
115ec49dea9Sguenther 
116ec49dea9Sguenther const pd_entry_t mbz_normal[] = {
117ec49dea9Sguenther     [5] =		0x0000000000000fe7UL,
118ec49dea9Sguenther     [4] =		0x0000000000000080UL,
119ec49dea9Sguenther #define	MBZ_1GFRAME	0x000000003fffe000UL
120ec49dea9Sguenther     [3] =		0x0000000000000000UL,
121ec49dea9Sguenther #define	MBZ_LGFRAME	0x00000000001fe000UL
122ec49dea9Sguenther     [2] =		0x0000000000000000UL,
123ec49dea9Sguenther     [1] =		0x0000000000000000UL,
124ec49dea9Sguenther };
125ec49dea9Sguenther 
126ec49dea9Sguenther static inline void
check_mbz(pd_entry_t e,pd_entry_t mbz)127ec49dea9Sguenther check_mbz(pd_entry_t e, pd_entry_t mbz)
128ec49dea9Sguenther {
129ec49dea9Sguenther 	if ((e & mbz) != 0)
130ec49dea9Sguenther 		errx(1, "non-zero mbz: %016llx in %016llx", e & mbz, e);
131ec49dea9Sguenther }
132ec49dea9Sguenther 
133d077b070Sguenther enum l4_type { T_NORMAL = 0, T_DIRECT, T_PTE, T_KERNBASE, };
134d077b070Sguenther 
135d077b070Sguenther static inline enum l4_type
l4type(int i)136d077b070Sguenther l4type(int i)
137d077b070Sguenther {
138d077b070Sguenther 	if (i >= L4_SLOT_DIRECT && i < L4_SLOT_DIRECT + NUM_L4_SLOT_DIRECT)
139d077b070Sguenther 		return T_DIRECT;
140d077b070Sguenther 	if (i == L4_SLOT_PTE)
141d077b070Sguenther 		return T_PTE;
142d077b070Sguenther 	if (i == L4_SLOT_KERNBASE)
143d077b070Sguenther 		return T_KERNBASE;
144d077b070Sguenther 	return T_NORMAL;
145d077b070Sguenther }
146d077b070Sguenther 
147*e9dcde56Sguenther const char pk_name[16] = "R-23456789abcdef";
148ec49dea9Sguenther void
pflags(pd_entry_t e,pd_entry_t inherited)1499d9b0696Sguenther pflags(pd_entry_t e, pd_entry_t inherited)
150ec49dea9Sguenther {
151*e9dcde56Sguenther 	int pk = (e & PG_PKMASK) >> PG_PK_SHIFT;
152ec49dea9Sguenther 	if (reproducible)
153ec49dea9Sguenther 		e &= ~(PG_M|PG_U);
1549d9b0696Sguenther 	inherited &= e;
155*e9dcde56Sguenther 	printf("[%c%c%c%c%c""%c%c%c%c][%c%c%c%c]",
1569d9b0696Sguenther 	    e & PG_NX ? 'X' : '-',	/* reversed */
157*e9dcde56Sguenther 	    pk_name[pk],
158ec49dea9Sguenther 	    e & PG_G  ? 'G' : '-',
159ec49dea9Sguenther 	    e & PG_M  ? 'M' : '-',
160ec49dea9Sguenther 	    e & PG_U  ? 'U' : '-',
161ec49dea9Sguenther 	    e & PG_N  ? 'N' : '-',
162ec49dea9Sguenther 	    e & PG_WT ? 'w' : '-',
163ec49dea9Sguenther 	    e & PG_u  ? 'u' : '-',
164ec49dea9Sguenther 	    e & PG_RW ? 'W' : '-',
1659d9b0696Sguenther 	    inherited & PG_u  ? 'u' : '-',
166*e9dcde56Sguenther 	    pk_name[pk],
167*e9dcde56Sguenther 	    inherited & PG_RW ? 'W' : '-',
168*e9dcde56Sguenther 	    inherited & PG_NX ? 'X' : '-');	/* reversed */
169ec49dea9Sguenther }
170ec49dea9Sguenther 
171ec49dea9Sguenther const char * const prefix[] = {
172ec49dea9Sguenther     [4] = "4   ",
173ec49dea9Sguenther     [3] = " 3  ",
174ec49dea9Sguenther     [2] = "  2 ",
175ec49dea9Sguenther     [1] = "   1",
176ec49dea9Sguenther };
177ec49dea9Sguenther 
178ec49dea9Sguenther void
pent(int level,int idx,vaddr_t va,pd_entry_t e,pd_entry_t inherited,enum l4_type l4_type)179ec49dea9Sguenther pent(int level, int idx, vaddr_t va, pd_entry_t e, pd_entry_t inherited,
180d077b070Sguenther     enum l4_type l4_type)
181ec49dea9Sguenther {
182ec49dea9Sguenther 	if ((e & PG_V) == 0)
183ec49dea9Sguenther 		return;
184ec49dea9Sguenther 
185ec49dea9Sguenther 	/* have an actual mapping */
186ec49dea9Sguenther 	pd_entry_t pa, mbz;
187ec49dea9Sguenther 	char type;
188ec49dea9Sguenther 	if ((e & PG_PS) && level == 2) {
189ec49dea9Sguenther 		pa = e & PG_LGFRAME;
190ec49dea9Sguenther 		mbz = MBZ_LGFRAME;
191ec49dea9Sguenther 		type = 'M';
192ec49dea9Sguenther 	} else if ((e & PG_PS) && level == 3) {
193ec49dea9Sguenther 		pa = e & PG_1GFRAME;
194ec49dea9Sguenther 		mbz = MBZ_1GFRAME;
195ec49dea9Sguenther 		type = 'G';
196ec49dea9Sguenther 	} else {
197ec49dea9Sguenther 		pa = e & PG_FRAME;
198ec49dea9Sguenther 		mbz = mbz_normal[level];
199ec49dea9Sguenther 		type = level == 1 ? 'k' : ' ';
200ec49dea9Sguenther 	}
201ec49dea9Sguenther 	check_mbz(e, mbz);
202ec49dea9Sguenther 
203ec49dea9Sguenther 	e ^= PG_NX;
204ec49dea9Sguenther 	inherited &= e;
205ec49dea9Sguenther 	if (show[level] || (show_leaves && type != ' ')) {
206ec49dea9Sguenther 		printf("%016lx %s% 4d -> ", va, prefix[level], idx);
207ec49dea9Sguenther 
208ec49dea9Sguenther 		printf("%016llx %c ", pa, type);
2099d9b0696Sguenther 		pflags(e, inherited);
210d077b070Sguenther 		switch (l4_type) {
211d077b070Sguenther 		case T_NORMAL:		putchar('\n'); break;
212d077b070Sguenther 		case T_DIRECT:		puts(" direct"); break;
213d077b070Sguenther 		case T_PTE:		puts(" pte"); break;
214d077b070Sguenther 		case T_KERNBASE:	puts(" kernbase"); break;
215d077b070Sguenther 		}
216ec49dea9Sguenther 	}
217ec49dea9Sguenther 
218ec49dea9Sguenther 	if (type != ' ')
219ec49dea9Sguenther 		return;
220ec49dea9Sguenther 	level--;
221ec49dea9Sguenther 	KGETPT_PA(pa, level);
222098d1aeeSguenther 	for (u_long i = 0; i < PAGE_SIZE / 8; i++) {
223d077b070Sguenther 		pent(level, i, (i << shift[level]) + va, pt[level][i],
224d077b070Sguenther 		    inherited, l4_type == T_PTE ? l4type(i) : T_NORMAL);
225ec49dea9Sguenther 	}
226ec49dea9Sguenther }
227ec49dea9Sguenther 
228ec49dea9Sguenther 
229ec49dea9Sguenther int
main(int argc,char ** argv)230ec49dea9Sguenther main(int argc, char **argv)
231ec49dea9Sguenther {
232098d1aeeSguenther 	u_long paddr;
233098d1aeeSguenther 	struct pcb pcb;
234ec49dea9Sguenther 	pd_entry_t cr3;
235ec49dea9Sguenther 	u_long i;
236ec49dea9Sguenther 	int ch;
237ec49dea9Sguenther 
238c8edf930Sguenther 	while ((ch = getopt(argc, argv, "1234dhlmpru")) != -1) {
239ec49dea9Sguenther 		switch (ch) {
240ec49dea9Sguenther 		case '1': case '2': case '3': case '4':
241ec49dea9Sguenther 			show[ch - '0'] = 1;
242ec49dea9Sguenther 			break;
243ec49dea9Sguenther 		case 'd':
244ec49dea9Sguenther 			hide_direct = 1;
245ec49dea9Sguenther 			break;
246c8edf930Sguenther 		case 'h':
247c8edf930Sguenther 			usage(0);
248c8edf930Sguenther 			break;
249ec49dea9Sguenther 		case 'l':
250ec49dea9Sguenther 			show_leaves = 1;
251ec49dea9Sguenther 			break;
252ec49dea9Sguenther 		case 'm':
253ec49dea9Sguenther 			meltdown = 1;
254ec49dea9Sguenther 			break;
255ec49dea9Sguenther 		case 'p':
256ec49dea9Sguenther 			hide_pte = 1;
257ec49dea9Sguenther 			break;
258ec49dea9Sguenther 		case 'r':
259ec49dea9Sguenther 			reproducible = 1;
260ec49dea9Sguenther 			break;
261098d1aeeSguenther 		case 'u':
262098d1aeeSguenther 			user_proc = 1;
263098d1aeeSguenther 			break;
264ec49dea9Sguenther 		default:
265c8edf930Sguenther 			usage(1);
266ec49dea9Sguenther 		}
267ec49dea9Sguenther 	}
268ec49dea9Sguenther 	argc -= optind;
269ec49dea9Sguenther 	argv += optind;
270ec49dea9Sguenther 	if (argc != 0)
271c8edf930Sguenther 		usage(1);
272ec49dea9Sguenther 
273ec49dea9Sguenther 	if (!show[1] && !show[2] && !show[3] && !show[4] && !show_leaves)
274ec49dea9Sguenther 		show[1] = show[2] = show[3] = show[4] = 1;
275ec49dea9Sguenther 
276098d1aeeSguenther 	if ((pt[4] = malloc(PAGE_SIZE)) == NULL ||
277098d1aeeSguenther 	    (pt[3] = malloc(PAGE_SIZE)) == NULL ||
278098d1aeeSguenther 	    (pt[2] = malloc(PAGE_SIZE)) == NULL ||
279098d1aeeSguenther 	    (pt[1] = malloc(PAGE_SIZE)) == NULL)
280ec49dea9Sguenther 		err(1, "malloc");
281ec49dea9Sguenther 
282ec49dea9Sguenther 	k = kvm_open(NULL, NULL, NULL, O_RDONLY, "foo");
283ec49dea9Sguenther 	if (k == NULL)
284ec49dea9Sguenther 		return 1;
285ec49dea9Sguenther 
286098d1aeeSguenther 	if (user_proc) {
287098d1aeeSguenther 		int cnt;
288098d1aeeSguenther 		struct kinfo_proc *kp = kvm_getprocs(k, KERN_PROC_PID, 1,
289098d1aeeSguenther 			sizeof *kp, &cnt);
290098d1aeeSguenther 		paddr = kp->p_addr;
291098d1aeeSguenther 	} else {
292ec49dea9Sguenther 		if (kvm_nlist(k, proc0) != 0)
293ec49dea9Sguenther 			err(1, "nlist");
294098d1aeeSguenther 		KGET(proc0[0].n_value, paddr);
295098d1aeeSguenther 	}
296ec49dea9Sguenther 
297098d1aeeSguenther 	KGET(paddr, pcb);
298098d1aeeSguenther 
299098d1aeeSguenther 	cr3 = pcb.pcb_cr3 & ~0xfff;		/* mask off PCID */
300ec49dea9Sguenther 	if (meltdown) {
301ec49dea9Sguenther 		struct pmap pmap;
302098d1aeeSguenther 		KGET((u_long)pcb.pcb_pmap, pmap);
303ec49dea9Sguenther 		if (cr3 != pmap.pm_pdirpa)
304ec49dea9Sguenther 			errx(1, "cr3 != pm_pdir: %016llx != %016lx",
305ec49dea9Sguenther 			    cr3, pmap.pm_pdirpa);
306ec49dea9Sguenther 
307ec49dea9Sguenther 		cr3 = (u_long)pmap.pm_pdir_intel;	/* VA */
308ec49dea9Sguenther 		if (cr3 == 0)
309ec49dea9Sguenther 			errx(1, "meltdown mitigation not enabled");
310ec49dea9Sguenther 		KGETPT_VA(cr3, 4);
311ec49dea9Sguenther 	} else {
312ec49dea9Sguenther 		KGETPT_PA(cr3, 4);
31316cbbb6aSguenther 		/*printf("PML4 @ %016llx\n", cr3);*/
314ec49dea9Sguenther 		check_mbz(cr3, mbz_normal[5]);
315ec49dea9Sguenther 	}
31616cbbb6aSguenther 	printf("\
317c8edf930Sguenther VA               lvl  idx    PA              sz entry-attr  eff   L4-slot\
31816cbbb6aSguenther \n");
319098d1aeeSguenther 	for (i = 0; i < PAGE_SIZE / sizeof(pd_entry_t); i++) {
320d077b070Sguenther 		enum l4_type l4_type = l4type(i);
321d077b070Sguenther 		if ((l4_type == T_DIRECT && hide_direct) ||
322d077b070Sguenther 		    (l4_type == T_PTE && hide_pte))
323ec49dea9Sguenther 			continue;
324ec49dea9Sguenther 		u_long va = i << L4_SHIFT;
325ec49dea9Sguenther 		if (i > 255)
326ec49dea9Sguenther 			va |= VA_SIGN_MASK;
327d077b070Sguenther 		pent(4, i, va, pt[4][i], ~0UL, l4_type);
328ec49dea9Sguenther 	}
329ec49dea9Sguenther 	return 0;
330ec49dea9Sguenther }
331