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