11584 ::xcall would be useful
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Robert Mustacchi <rm@joyent.com>
1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2018 Joyent, Inc.
24 */
25
26 #include <mdb/mdb_modapi.h>
27 #include <mdb/mdb_ctf.h>
28 #include <sys/cpuvar.h>
29 #include <sys/systm.h>
30 #include <sys/traptrace.h>
31 #include <sys/x_call.h>
32 #include <sys/xc_levels.h>
33 #include <sys/avintr.h>
34 #include <sys/systm.h>
35 #include <sys/trap.h>
36 #include <sys/mutex.h>
37 #include <sys/mutex_impl.h>
38 #include "i86mmu.h"
39 #include "unix_sup.h"
40 #include <sys/apix.h>
41 #include <sys/x86_archext.h>
42 #include <sys/bitmap.h>
43 #include <sys/controlregs.h>
44
45 #define TT_HDLR_WIDTH 17
46
47
48 /* apix only */
49 static apix_impl_t *d_apixs[NCPU];
50 static int use_apix = 0;
51
52 static int
53 ttrace_ttr_size_check(void)
54 {
55 mdb_ctf_id_t ttrtid;
56 ssize_t ttr_size;
57
58 if (mdb_ctf_lookup_by_name("trap_trace_rec_t", &ttrtid) != 0 ||
59 mdb_ctf_type_resolve(ttrtid, &ttrtid) != 0) {
60 mdb_warn("failed to determine size of trap_trace_rec_t; "
61 "non-TRAPTRACE kernel?\n");
62 return (0);
63 }
64
65 if ((ttr_size = mdb_ctf_type_size(ttrtid)) !=
66 sizeof (trap_trace_rec_t)) {
67 /*
68 * On Intel machines, this will happen when TTR_STACK_DEPTH
69 * is changed. This code could be smarter, and could
70 * dynamically adapt to different depths, but not until a
71 * need for such adaptation is demonstrated.
72 */
73 mdb_warn("size of trap_trace_rec_t (%d bytes) doesn't "
74 "match expected %d\n", ttr_size, sizeof (trap_trace_rec_t));
75 return (0);
76 }
77
78 return (1);
79 }
80
81 int
82 ttrace_walk_init(mdb_walk_state_t *wsp)
83 {
84 trap_trace_ctl_t *ttcp;
85 size_t ttc_size = sizeof (trap_trace_ctl_t) * NCPU;
86 int i;
87
88 if (!ttrace_ttr_size_check())
89 return (WALK_ERR);
90
91 ttcp = mdb_zalloc(ttc_size, UM_SLEEP);
92
93 if (wsp->walk_addr != 0) {
94 mdb_warn("ttrace only supports global walks\n");
95 return (WALK_ERR);
96 }
97
98 if (mdb_readsym(ttcp, ttc_size, "trap_trace_ctl") == -1) {
99 mdb_warn("symbol 'trap_trace_ctl' not found; "
100 "non-TRAPTRACE kernel?\n");
101 mdb_free(ttcp, ttc_size);
102 return (WALK_ERR);
103 }
104
105 /*
106 * We'll poach the ttc_current pointer (which isn't used for
107 * anything) to store a pointer to our current TRAPTRACE record.
108 * This allows us to only keep the array of trap_trace_ctl structures
109 * as our walker state (ttc_current may be the only kernel data
110 * structure member added exclusively to make writing the mdb walker
111 * a little easier).
112 */
113 for (i = 0; i < NCPU; i++) {
114 trap_trace_ctl_t *ttc = &ttcp[i];
115
116 if (ttc->ttc_first == 0)
117 continue;
118
119 /*
120 * Assign ttc_current to be the last completed record.
121 * Note that the error checking (i.e. in the ttc_next ==
122 * ttc_first case) is performed in the step function.
123 */
124 ttc->ttc_current = ttc->ttc_next - sizeof (trap_trace_rec_t);
125 }
126
127 wsp->walk_data = ttcp;
128 return (WALK_NEXT);
129 }
130
131 int
132 ttrace_walk_step(mdb_walk_state_t *wsp)
133 {
134 trap_trace_ctl_t *ttcp = wsp->walk_data, *ttc, *latest_ttc;
135 trap_trace_rec_t rec;
136 int rval, i, recsize = sizeof (trap_trace_rec_t);
137 hrtime_t latest = 0;
138
139 /*
140 * Loop through the CPUs, looking for the latest trap trace record
141 * (we want to walk through the trap trace records in reverse
142 * chronological order).
143 */
144 for (i = 0; i < NCPU; i++) {
145 ttc = &ttcp[i];
146
147 if (ttc->ttc_current == 0)
148 continue;
149
150 if (ttc->ttc_current < ttc->ttc_first)
151 ttc->ttc_current = ttc->ttc_limit - recsize;
152
153 if (mdb_vread(&rec, sizeof (rec), ttc->ttc_current) == -1) {
154 mdb_warn("couldn't read rec at %p", ttc->ttc_current);
155 return (WALK_ERR);
156 }
157
158 if (rec.ttr_stamp > latest) {
159 latest = rec.ttr_stamp;
160 latest_ttc = ttc;
161 }
162 }
163
164 if (latest == 0)
165 return (WALK_DONE);
166
167 ttc = latest_ttc;
168
169 if (mdb_vread(&rec, sizeof (rec), ttc->ttc_current) == -1) {
170 mdb_warn("couldn't read rec at %p", ttc->ttc_current);
171 return (WALK_ERR);
172 }
173
174 rval = wsp->walk_callback(ttc->ttc_current, &rec, wsp->walk_cbdata);
175
176 if (ttc->ttc_current == ttc->ttc_next)
177 ttc->ttc_current = 0;
178 else
179 ttc->ttc_current -= sizeof (trap_trace_rec_t);
180
181 return (rval);
182 }
183
184 void
185 ttrace_walk_fini(mdb_walk_state_t *wsp)
186 {
187 mdb_free(wsp->walk_data, sizeof (trap_trace_ctl_t) * NCPU);
188 }
189
190 static int
191 ttrace_syscall(trap_trace_rec_t *rec)
192 {
193 GElf_Sym sym;
194 int sysnum = rec->ttr_sysnum;
195 uintptr_t addr;
196 struct sysent sys;
197
198 mdb_printf("%-3x", sysnum);
199
200 if (rec->ttr_sysnum > NSYSCALL) {
201 mdb_printf(" %-*d", TT_HDLR_WIDTH, rec->ttr_sysnum);
202 return (0);
203 }
204
205 if (mdb_lookup_by_name("sysent", &sym) == -1) {
206 mdb_warn("\ncouldn't find 'sysent'");
207 return (-1);
208 }
209
210 addr = (uintptr_t)sym.st_value + sysnum * sizeof (struct sysent);
211
212 if (addr >= (uintptr_t)sym.st_value + sym.st_size) {
213 mdb_warn("\nsysnum %d out-of-range\n", sysnum);
214 return (-1);
215 }
216
217 if (mdb_vread(&sys, sizeof (sys), addr) == -1) {
218 mdb_warn("\nfailed to read sysent at %p", addr);
219 return (-1);
220 }
221
222 mdb_printf(" %-*a", TT_HDLR_WIDTH, sys.sy_callc);
223
224 return (0);
225 }
226
227 static int
228 ttrace_interrupt(trap_trace_rec_t *rec)
229 {
230 GElf_Sym sym;
231 uintptr_t addr;
232 struct av_head hd;
233 struct autovec av;
234
235 switch (rec->ttr_regs.r_trapno) {
236 case T_SOFTINT:
237 mdb_printf("%-3s %-*s", "-", TT_HDLR_WIDTH, "(fakesoftint)");
238 return (0);
239 default:
240 break;
241 }
242
243 mdb_printf("%-3x ", rec->ttr_vector);
244
245 if (mdb_lookup_by_name("autovect", &sym) == -1) {
246 mdb_warn("\ncouldn't find 'autovect'");
247 return (-1);
248 }
249
250 addr = (uintptr_t)sym.st_value +
251 rec->ttr_vector * sizeof (struct av_head);
252
253 if (addr >= (uintptr_t)sym.st_value + sym.st_size) {
254 mdb_warn("\nav_head for vec %x is corrupt\n", rec->ttr_vector);
255 return (-1);
256 }
257
258 if (mdb_vread(&hd, sizeof (hd), addr) == -1) {
259 mdb_warn("\ncouldn't read av_head for vec %x", rec->ttr_vector);
260 return (-1);
261 }
262
263 if (hd.avh_link == NULL) {
264 if (rec->ttr_ipl == XC_CPUPOKE_PIL)
265 mdb_printf("%-*s", TT_HDLR_WIDTH, "(cpupoke)");
266 else
267 mdb_printf("%-*s", TT_HDLR_WIDTH, "(spurious)");
268 } else {
269 if (mdb_vread(&av, sizeof (av), (uintptr_t)hd.avh_link) == -1) {
270 mdb_warn("couldn't read autovec at %p",
271 (uintptr_t)hd.avh_link);
272 }
273
274 mdb_printf("%-*a", TT_HDLR_WIDTH, av.av_vector);
275 }
276
277 return (0);
278 }
279
280 static int
281 ttrace_apix_interrupt(trap_trace_rec_t *rec)
282 {
283 struct autovec av;
284 apix_impl_t apix;
285 apix_vector_t apix_vector;
286
287 switch (rec->ttr_regs.r_trapno) {
288 case T_SOFTINT:
289 mdb_printf("%-3s %-*s", "-", TT_HDLR_WIDTH, "(fakesoftint)");
290 return (0);
291 default:
292 break;
293 }
294
295 mdb_printf("%-3x ", rec->ttr_vector);
296
297 /* Read the per CPU apix entry */
298 if (mdb_vread(&apix, sizeof (apix_impl_t),
299 (uintptr_t)d_apixs[rec->ttr_cpuid]) == -1) {
300 mdb_warn("\ncouldn't read apix[%d]", rec->ttr_cpuid);
301 return (-1);
302 }
303 if (mdb_vread(&apix_vector, sizeof (apix_vector_t),
304 (uintptr_t)apix.x_vectbl[rec->ttr_vector]) == -1) {
305 mdb_warn("\ncouldn't read apix_vector_t[%d]", rec->ttr_vector);
306 return (-1);
307 }
308 if (apix_vector.v_share == 0) {
309 if (rec->ttr_ipl == XC_CPUPOKE_PIL)
310 mdb_printf("%-*s", TT_HDLR_WIDTH, "(cpupoke)");
311 else
312 mdb_printf("%-*s", TT_HDLR_WIDTH, "(spurious)");
313 } else {
314 if (mdb_vread(&av, sizeof (struct autovec),
315 (uintptr_t)(apix_vector.v_autovect)) == -1) {
316 mdb_warn("couldn't read autovec at %p",
317 (uintptr_t)apix_vector.v_autovect);
318 }
319
320 mdb_printf("%-*a", TT_HDLR_WIDTH, av.av_vector);
321 }
322
323 return (0);
324 }
325
326
327 static struct {
328 int tt_trapno;
329 char *tt_name;
330 } ttrace_traps[] = {
331 { T_ZERODIV, "divide-error" },
332 { T_SGLSTP, "debug-exception" },
333 { T_NMIFLT, "nmi-interrupt" },
334 { T_BPTFLT, "breakpoint" },
335 { T_OVFLW, "into-overflow" },
336 { T_BOUNDFLT, "bound-exceeded" },
337 { T_ILLINST, "invalid-opcode" },
338 { T_NOEXTFLT, "device-not-avail" },
339 { T_DBLFLT, "double-fault" },
340 { T_EXTOVRFLT, "segment-overrun" },
341 { T_TSSFLT, "invalid-tss" },
342 { T_SEGFLT, "segment-not-pres" },
343 { T_STKFLT, "stack-fault" },
344 { T_GPFLT, "general-protectn" },
345 { T_PGFLT, "page-fault" },
346 { T_EXTERRFLT, "error-fault" },
347 { T_ALIGNMENT, "alignment-check" },
348 { T_MCE, "machine-check" },
349 { T_SIMDFPE, "sse-exception" },
350
351 { T_DBGENTR, "debug-enter" },
352 { T_FASTTRAP, "fasttrap-0xd2" },
353 { T_SYSCALLINT, "syscall-0x91" },
354 { T_DTRACE_RET, "dtrace-ret" },
355 { T_SOFTINT, "softint" },
356 { T_INTERRUPT, "interrupt" },
357 { T_FAULT, "fault" },
358 { T_AST, "ast" },
359 { T_SYSCALL, "syscall" },
360
361 { 0, NULL }
362 };
363
364 static int
365 ttrace_trap(trap_trace_rec_t *rec)
366 {
367 int i;
368
369 if (rec->ttr_regs.r_trapno == T_AST)
370 mdb_printf("%-3s ", "-");
371 else
372 mdb_printf("%-3x ", rec->ttr_regs.r_trapno);
373
374 for (i = 0; ttrace_traps[i].tt_name != NULL; i++) {
375 if (rec->ttr_regs.r_trapno == ttrace_traps[i].tt_trapno)
376 break;
377 }
378
379 if (ttrace_traps[i].tt_name == NULL)
380 mdb_printf("%-*s", TT_HDLR_WIDTH, "(unknown)");
381 else
382 mdb_printf("%-*s", TT_HDLR_WIDTH, ttrace_traps[i].tt_name);
383
384 return (0);
385 }
386
387 static void
388 ttrace_intr_detail(trap_trace_rec_t *rec)
389 {
390 mdb_printf("\tirq %x ipl %d oldpri %d basepri %d\n", rec->ttr_vector,
391 rec->ttr_ipl, rec->ttr_pri, rec->ttr_spl);
392 }
393
394 static struct {
395 uchar_t t_marker;
396 char *t_name;
397 int (*t_hdlr)(trap_trace_rec_t *);
398 } ttrace_hdlr[] = {
399 { TT_SYSCALL, "sysc", ttrace_syscall },
400 { TT_SYSENTER, "syse", ttrace_syscall },
401 { TT_SYSC, "asys", ttrace_syscall },
402 { TT_SYSC64, "sc64", ttrace_syscall },
403 { TT_INTERRUPT, "intr", ttrace_interrupt },
404 { TT_TRAP, "trap", ttrace_trap },
405 { TT_EVENT, "evnt", ttrace_trap },
406 { 0, NULL, NULL }
407 };
408
409 typedef struct ttrace_dcmd {
410 processorid_t ttd_cpu;
411 uint_t ttd_extended;
412 uintptr_t ttd_kthread;
413 trap_trace_ctl_t ttd_ttc[NCPU];
414 } ttrace_dcmd_t;
415
416 #if defined(__amd64)
417
418 #define DUMP(reg) #reg, regs->r_##reg
419 #define THREEREGS " %3s: %16lx %3s: %16lx %3s: %16lx\n"
420
421 static void
422 ttrace_dumpregs(trap_trace_rec_t *rec)
423 {
424 struct regs *regs = &rec->ttr_regs;
425
426 mdb_printf(THREEREGS, DUMP(rdi), DUMP(rsi), DUMP(rdx));
427 mdb_printf(THREEREGS, DUMP(rcx), DUMP(r8), DUMP(r9));
428 mdb_printf(THREEREGS, DUMP(rax), DUMP(rbx), DUMP(rbp));
429 mdb_printf(THREEREGS, DUMP(r10), DUMP(r11), DUMP(r12));
430 mdb_printf(THREEREGS, DUMP(r13), DUMP(r14), DUMP(r15));
431 mdb_printf(THREEREGS, DUMP(ds), DUMP(es), DUMP(fs));
432 mdb_printf(THREEREGS, DUMP(gs), "trp", regs->r_trapno, DUMP(err));
433 mdb_printf(THREEREGS, DUMP(rip), DUMP(cs), DUMP(rfl));
434 mdb_printf(THREEREGS, DUMP(rsp), DUMP(ss), "cr2", rec->ttr_cr2);
435 mdb_printf(" %3s: %16lx %3s: %16lx\n",
436 "fsb", regs->__r_fsbase,
437 "gsb", regs->__r_gsbase);
438 mdb_printf("\n");
439 }
440
441 #else
442
443 #define DUMP(reg) #reg, regs->r_##reg
444 #define FOURREGS " %3s: %08x %3s: %08x %3s: %08x %3s: %08x\n"
445
446 static void
447 ttrace_dumpregs(trap_trace_rec_t *rec)
448 {
449 struct regs *regs = &rec->ttr_regs;
450
451 mdb_printf(FOURREGS, DUMP(gs), DUMP(fs), DUMP(es), DUMP(ds));
452 mdb_printf(FOURREGS, DUMP(edi), DUMP(esi), DUMP(ebp), DUMP(esp));
453 mdb_printf(FOURREGS, DUMP(ebx), DUMP(edx), DUMP(ecx), DUMP(eax));
454 mdb_printf(FOURREGS, "trp", regs->r_trapno, DUMP(err),
455 DUMP(pc), DUMP(cs));
456 mdb_printf(FOURREGS, DUMP(efl), "usp", regs->r_uesp, DUMP(ss),
457 "cr2", rec->ttr_cr2);
458 mdb_printf("\n");
459 }
460
461 #endif /* __amd64 */
462
463 int
464 ttrace_walk(uintptr_t addr, trap_trace_rec_t *rec, ttrace_dcmd_t *dcmd)
465 {
466 struct regs *regs = &rec->ttr_regs;
467 processorid_t cpu = -1, i;
468
469 for (i = 0; i < NCPU; i++) {
470 if (addr >= dcmd->ttd_ttc[i].ttc_first &&
471 addr < dcmd->ttd_ttc[i].ttc_limit) {
472 cpu = i;
473 break;
474 }
475 }
476
477 if (cpu == -1) {
478 mdb_warn("couldn't find %p in any trap trace ctl\n", addr);
479 return (WALK_ERR);
480 }
481
482 if (dcmd->ttd_cpu != -1 && cpu != dcmd->ttd_cpu)
483 return (WALK_NEXT);
484
485 if (dcmd->ttd_kthread != 0 &&
486 dcmd->ttd_kthread != rec->ttr_curthread)
487 return (WALK_NEXT);
488
489 mdb_printf("%3d %15llx ", cpu, rec->ttr_stamp);
490
491 for (i = 0; ttrace_hdlr[i].t_hdlr != NULL; i++) {
492 if (rec->ttr_marker != ttrace_hdlr[i].t_marker)
493 continue;
494 mdb_printf("%4s ", ttrace_hdlr[i].t_name);
495 if (ttrace_hdlr[i].t_hdlr(rec) == -1)
496 return (WALK_ERR);
497 }
498
499 mdb_printf(" %a\n", regs->r_pc);
500
501 if (dcmd->ttd_extended == FALSE)
502 return (WALK_NEXT);
503
504 if (rec->ttr_marker == TT_INTERRUPT)
505 ttrace_intr_detail(rec);
506 else
507 ttrace_dumpregs(rec);
508
509 if (rec->ttr_sdepth > 0) {
510 for (i = 0; i < rec->ttr_sdepth; i++) {
511 if (i >= TTR_STACK_DEPTH) {
512 mdb_printf("%17s*** invalid ttr_sdepth (is %d, "
513 "should be <= %d)\n", " ", rec->ttr_sdepth,
514 TTR_STACK_DEPTH);
515 break;
516 }
517
518 mdb_printf("%17s %a()\n", " ", rec->ttr_stack[i]);
519 }
520 mdb_printf("\n");
521 }
522
523 return (WALK_NEXT);
524 }
525
526 int
527 ttrace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
528 {
529 ttrace_dcmd_t dcmd;
530 trap_trace_ctl_t *ttc = dcmd.ttd_ttc;
531 trap_trace_rec_t rec;
532 size_t ttc_size = sizeof (trap_trace_ctl_t) * NCPU;
533
534 if (!ttrace_ttr_size_check())
535 return (WALK_ERR);
536
537 bzero(&dcmd, sizeof (dcmd));
538 dcmd.ttd_cpu = -1;
539 dcmd.ttd_extended = FALSE;
540
541 if (mdb_readsym(ttc, ttc_size, "trap_trace_ctl") == -1) {
542 mdb_warn("symbol 'trap_trace_ctl' not found; "
543 "non-TRAPTRACE kernel?\n");
544 return (DCMD_ERR);
545 }
546
547 if (mdb_getopts(argc, argv,
548 'x', MDB_OPT_SETBITS, TRUE, &dcmd.ttd_extended,
549 't', MDB_OPT_UINTPTR, &dcmd.ttd_kthread, NULL) != argc)
550 return (DCMD_USAGE);
551
552 if (DCMD_HDRSPEC(flags)) {
553 mdb_printf("%3s %15s %4s %2s %-*s%s\n", "CPU",
554 "TIMESTAMP", "TYPE", "Vec", TT_HDLR_WIDTH, "HANDLER",
555 " EIP");
556 }
557
558 if (flags & DCMD_ADDRSPEC) {
559 if (addr >= NCPU) {
560 if (mdb_vread(&rec, sizeof (rec), addr) == -1) {
561 mdb_warn("couldn't read trap trace record "
562 "at %p", addr);
563 return (DCMD_ERR);
564 }
565
566 if (ttrace_walk(addr, &rec, &dcmd) == WALK_ERR)
567 return (DCMD_ERR);
568
569 return (DCMD_OK);
570 }
571 dcmd.ttd_cpu = addr;
572 }
573
574 if (mdb_readvar(&use_apix, "apix_enable") == -1) {
575 mdb_warn("failed to read apix_enable");
576 use_apix = 0;
577 }
578
579 if (use_apix) {
580 if (mdb_readvar(&d_apixs, "apixs") == -1) {
581 mdb_warn("\nfailed to read apixs.");
582 return (DCMD_ERR);
583 }
584 /* change to apix ttrace interrupt handler */
585 ttrace_hdlr[4].t_hdlr = ttrace_apix_interrupt;
586 }
587
588 if (mdb_walk("ttrace", (mdb_walk_cb_t)ttrace_walk, &dcmd) == -1) {
589 mdb_warn("couldn't walk 'ttrace'");
590 return (DCMD_ERR);
591 }
592
593 return (DCMD_OK);
594 }
595
596 /*ARGSUSED*/
597 int
598 mutex_owner_init(mdb_walk_state_t *wsp)
599 {
600 return (WALK_NEXT);
601 }
602
603 int
604 mutex_owner_step(mdb_walk_state_t *wsp)
605 {
606 uintptr_t addr = wsp->walk_addr;
607 mutex_impl_t mtx;
608 uintptr_t owner;
609 kthread_t thr;
610
611 if (mdb_vread(&mtx, sizeof (mtx), addr) == -1)
612 return (WALK_ERR);
613
614 if (!MUTEX_TYPE_ADAPTIVE(&mtx))
615 return (WALK_DONE);
616
617 if ((owner = (uintptr_t)MUTEX_OWNER(&mtx)) == 0)
618 return (WALK_DONE);
619
620 if (mdb_vread(&thr, sizeof (thr), owner) != -1)
621 (void) wsp->walk_callback(owner, &thr, wsp->walk_cbdata);
622
623 return (WALK_DONE);
624 }
625
626 static void
627 gate_desc_dump(gate_desc_t *gate, const char *label, int header)
628 {
629 const char *lastnm;
630 uint_t lastval;
631 char type[4];
632
633 switch (gate->sgd_type) {
634 case SDT_SYSIGT:
635 strcpy(type, "int");
636 break;
637 case SDT_SYSTGT:
638 strcpy(type, "trp");
639 break;
640 case SDT_SYSTASKGT:
641 strcpy(type, "tsk");
642 break;
643 default:
644 (void) mdb_snprintf(type, sizeof (type), "%3x", gate->sgd_type);
645 }
646
647 #if defined(__amd64)
648 lastnm = "IST";
649 lastval = gate->sgd_ist;
650 #else
651 lastnm = "STK";
652 lastval = gate->sgd_stkcpy;
653 #endif
654
655 if (header) {
656 mdb_printf("%*s%<u>%-30s%</u> %<u>%-4s%</u> %<u>%3s%</u> "
657 "%<u>%1s%</u> %<u>%3s%</u> %<u>%3s%</u>\n", strlen(label),
658 "", "HANDLER", "SEL", "DPL", "P", "TYP", lastnm);
659 }
660
661 mdb_printf("%s", label);
662
663 if (gate->sgd_type == SDT_SYSTASKGT)
664 mdb_printf("%-30s ", "-");
665 else
666 mdb_printf("%-30a ", GATESEG_GETOFFSET(gate));
667
668 mdb_printf("%4x %d %c %3s %2x\n", gate->sgd_selector,
669 gate->sgd_dpl, (gate->sgd_p ? '+' : ' '), type, lastval);
670 }
671
672 /*ARGSUSED*/
673 static int
674 gate_desc(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
675 {
676 gate_desc_t gate;
677
678 if (argc != 0 || !(flags & DCMD_ADDRSPEC))
679 return (DCMD_USAGE);
680
681 if (mdb_vread(&gate, sizeof (gate_desc_t), addr) !=
682 sizeof (gate_desc_t)) {
683 mdb_warn("failed to read gate descriptor at %p\n", addr);
684 return (DCMD_ERR);
685 }
686
687 gate_desc_dump(&gate, "", DCMD_HDRSPEC(flags));
688
689 return (DCMD_OK);
690 }
691
692 /*ARGSUSED*/
693 static int
694 idt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
695 {
696 int i;
697
698 if (!(flags & DCMD_ADDRSPEC)) {
699 GElf_Sym idt0_va;
700 gate_desc_t *idt0;
701
702 if (mdb_lookup_by_name("idt0", &idt0_va) < 0) {
703 mdb_warn("failed to find VA of idt0");
704 return (DCMD_ERR);
705 }
706
707 addr = idt0_va.st_value;
708 if (mdb_vread(&idt0, sizeof (idt0), addr) != sizeof (idt0)) {
709 mdb_warn("failed to read idt0 at %p\n", addr);
710 return (DCMD_ERR);
711 }
712
713 addr = (uintptr_t)idt0;
714 }
715
716 for (i = 0; i < NIDT; i++, addr += sizeof (gate_desc_t)) {
717 gate_desc_t gate;
718 char label[6];
719
720 if (mdb_vread(&gate, sizeof (gate_desc_t), addr) !=
721 sizeof (gate_desc_t)) {
722 mdb_warn("failed to read gate descriptor at %p\n",
723 addr);
724 return (DCMD_ERR);
725 }
726
727 (void) mdb_snprintf(label, sizeof (label), "%3d: ", i);
728 gate_desc_dump(&gate, label, i == 0);
729 }
730
731 return (DCMD_OK);
732 }
733
734 static void
735 htables_help(void)
736 {
737 mdb_printf(
738 "Given a (hat_t *), generates the list of all (htable_t *)s\n"
739 "that correspond to that address space\n");
740 }
741
742 static void
743 report_maps_help(void)
744 {
745 mdb_printf(
746 "Given a PFN, report HAT structures that map the page, or use\n"
747 "the page as a pagetable.\n"
748 "\n"
749 "-m Interpret the PFN as an MFN (machine frame number)\n");
750 }
751
752 static void
753 ptable_help(void)
754 {
755 mdb_printf(
756 "Given a PFN holding a page table, print its contents, and\n"
757 "the address of the corresponding htable structure.\n"
758 "\n"
759 "-m Interpret the PFN as an MFN (machine frame number)\n"
760 "-l force page table level (3 is top)\n");
761 }
762
763 static void
764 ptmap_help(void)
765 {
766 mdb_printf(
767 "Report all mappings represented by the page table hierarchy\n"
768 "rooted at the given cr3 value / physical address.\n"
769 "\n"
770 "-w run ::whatis on mapping start addresses\n");
771 }
772
773 static const char *const scalehrtime_desc =
774 "Scales a timestamp from ticks to nanoseconds. Unscaled timestamps\n"
775 "are used as both a quick way of accumulating relative time (as for\n"
776 "usage) and as a quick way of getting the absolute current time.\n"
777 "These uses require slightly different scaling algorithms. By\n"
778 "default, if a specified time is greater than half of the unscaled\n"
779 "time at the last tick (that is, if the unscaled time represents\n"
780 "more than half the time since boot), the timestamp is assumed to\n"
781 "be absolute, and the scaling algorithm used mimics that which the\n"
782 "kernel uses in gethrtime(). Otherwise, the timestamp is assumed to\n"
783 "be relative, and the algorithm mimics scalehrtime(). This behavior\n"
784 "can be overridden by forcing the unscaled time to be interpreted\n"
785 "as relative (via -r) or absolute (via -a).\n";
786
787 static void
788 scalehrtime_help(void)
789 {
790 mdb_printf("%s", scalehrtime_desc);
791 }
792
793 /*
794 * NSEC_SHIFT is replicated here (it is not defined in a header file),
795 * but for amusement, the reader is directed to the comment that explains
796 * the rationale for this particular value on x86. Spoiler: the value is
797 * selected to accommodate 60 MHz Pentiums! (And a confession: if the voice
798 * in that comment sounds too familiar, it's because your author also wrote
799 * that code -- some fifteen years prior to this writing in 2011...)
800 */
801 #define NSEC_SHIFT 5
802
803 /*ARGSUSED*/
804 static int
805 scalehrtime_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
806 {
807 uint32_t nsec_scale;
808 hrtime_t tsc = addr, hrt, tsc_last, base, mult = 1;
809 unsigned int *tscp = (unsigned int *)&tsc;
810 uintptr_t scalehrtimef;
811 uint64_t scale;
812 GElf_Sym sym;
813 int expected = !(flags & DCMD_ADDRSPEC);
814 uint_t absolute = FALSE, relative = FALSE;
815
816 if (mdb_getopts(argc, argv,
817 'a', MDB_OPT_SETBITS, TRUE, &absolute,
818 'r', MDB_OPT_SETBITS, TRUE, &relative, NULL) != argc - expected)
819 return (DCMD_USAGE);
820
821 if (absolute && relative) {
822 mdb_warn("can't specify both -a and -r\n");
823 return (DCMD_USAGE);
824 }
825
826 if (expected == 1) {
827 switch (argv[argc - 1].a_type) {
828 case MDB_TYPE_STRING:
829 tsc = mdb_strtoull(argv[argc - 1].a_un.a_str);
830 break;
831 case MDB_TYPE_IMMEDIATE:
832 tsc = argv[argc - 1].a_un.a_val;
833 break;
834 default:
835 return (DCMD_USAGE);
836 }
837 }
838
839 if (mdb_readsym(&scalehrtimef,
840 sizeof (scalehrtimef), "scalehrtimef") == -1) {
841 mdb_warn("couldn't read 'scalehrtimef'");
842 return (DCMD_ERR);
843 }
844
845 if (mdb_lookup_by_name("tsc_scalehrtime", &sym) == -1) {
846 mdb_warn("couldn't find 'tsc_scalehrtime'");
847 return (DCMD_ERR);
848 }
849
850 if (sym.st_value != scalehrtimef) {
851 mdb_warn("::scalehrtime requires that scalehrtimef "
852 "be set to tsc_scalehrtime\n");
853 return (DCMD_ERR);
854 }
855
856 if (mdb_readsym(&nsec_scale, sizeof (nsec_scale), "nsec_scale") == -1) {
857 mdb_warn("couldn't read 'nsec_scale'");
858 return (DCMD_ERR);
859 }
860
861 if (mdb_readsym(&tsc_last, sizeof (tsc_last), "tsc_last") == -1) {
862 mdb_warn("couldn't read 'tsc_last'");
863 return (DCMD_ERR);
864 }
865
866 if (mdb_readsym(&base, sizeof (base), "tsc_hrtime_base") == -1) {
867 mdb_warn("couldn't read 'tsc_hrtime_base'");
868 return (DCMD_ERR);
869 }
870
871 /*
872 * If our time is greater than half of tsc_last, we will take our
873 * delta against tsc_last, convert it, and add that to (or subtract it
874 * from) tsc_hrtime_base. This mimics what the kernel actually does
875 * in gethrtime() (modulo the tsc_sync_tick_delta) and gets us a much
876 * higher precision result than trying to convert a large tsc value.
877 */
878 if (absolute || (tsc > (tsc_last >> 1) && !relative)) {
879 if (tsc > tsc_last) {
880 tsc = tsc - tsc_last;
881 } else {
882 tsc = tsc_last - tsc;
883 mult = -1;
884 }
885 } else {
886 base = 0;
887 }
888
889 scale = (uint64_t)nsec_scale;
890
891 hrt = ((uint64_t)tscp[1] * scale) << NSEC_SHIFT;
892 hrt += ((uint64_t)tscp[0] * scale) >> (32 - NSEC_SHIFT);
893
894 mdb_printf("0x%llx\n", base + (hrt * mult));
895
896 return (DCMD_OK);
897 }
898
899 /*
900 * The x86 feature set is implemented as a bitmap array. That bitmap array is
901 * stored across a number of uchars based on the BT_SIZEOFMAP(NUM_X86_FEATURES)
902 * macro. We have the names for each of these features in unix's text segment
903 * so we do not have to duplicate them and instead just look them up.
904 */
905 /*ARGSUSED*/
906 static int
907 x86_featureset_cmd(uintptr_t addr, uint_t flags, int argc,
908 const mdb_arg_t *argv)
909 {
910 void *fset;
911 GElf_Sym sym;
912 uintptr_t nptr;
913 char name[128];
914 int ii;
915
916 size_t sz = sizeof (uchar_t) * BT_SIZEOFMAP(NUM_X86_FEATURES);
917
918 if (argc != 0)
919 return (DCMD_USAGE);
920
921 if (mdb_lookup_by_name("x86_feature_names", &sym) == -1) {
922 mdb_warn("couldn't find x86_feature_names");
923 return (DCMD_ERR);
924 }
925
926 fset = mdb_zalloc(sz, UM_NOSLEEP);
927 if (fset == NULL) {
928 mdb_warn("failed to allocate memory for x86_featureset");
929 return (DCMD_ERR);
930 }
931
932 if (mdb_readvar(fset, "x86_featureset") != sz) {
933 mdb_warn("failed to read x86_featureset");
934 mdb_free(fset, sz);
935 return (DCMD_ERR);
936 }
937
938 for (ii = 0; ii < NUM_X86_FEATURES; ii++) {
939 if (!BT_TEST((ulong_t *)fset, ii))
940 continue;
941
942 if (mdb_vread(&nptr, sizeof (char *), sym.st_value +
943 sizeof (void *) * ii) != sizeof (char *)) {
944 mdb_warn("failed to read feature array %d", ii);
945 mdb_free(fset, sz);
946 return (DCMD_ERR);
947 }
948
949 if (mdb_readstr(name, sizeof (name), nptr) == -1) {
950 mdb_warn("failed to read feature %d", ii);
951 mdb_free(fset, sz);
952 return (DCMD_ERR);
953 }
954 mdb_printf("%s\n", name);
955 }
956
957 mdb_free(fset, sz);
958 return (DCMD_OK);
959 }
960
961 #ifdef _KMDB
962 /* ARGSUSED */
963 static int
964 sysregs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
965 {
966 ulong_t cr0, cr2, cr3, cr4;
967 desctbr_t gdtr;
968
969 static const mdb_bitmask_t cr0_flag_bits[] = {
970 { "PE", CR0_PE, CR0_PE },
971 { "MP", CR0_MP, CR0_MP },
972 { "EM", CR0_EM, CR0_EM },
973 { "TS", CR0_TS, CR0_TS },
974 { "ET", CR0_ET, CR0_ET },
975 { "NE", CR0_NE, CR0_NE },
976 { "WP", CR0_WP, CR0_WP },
977 { "AM", CR0_AM, CR0_AM },
978 { "NW", CR0_NW, CR0_NW },
979 { "CD", CR0_CD, CR0_CD },
980 { "PG", CR0_PG, CR0_PG },
981 { NULL, 0, 0 }
982 };
983
984 static const mdb_bitmask_t cr3_flag_bits[] = {
985 { "PCD", CR3_PCD, CR3_PCD },
986 { "PWT", CR3_PWT, CR3_PWT },
987 { NULL, 0, 0, }
988 };
989
990 static const mdb_bitmask_t cr4_flag_bits[] = {
991 { "VME", CR4_VME, CR4_VME },
992 { "PVI", CR4_PVI, CR4_PVI },
993 { "TSD", CR4_TSD, CR4_TSD },
994 { "DE", CR4_DE, CR4_DE },
995 { "PSE", CR4_PSE, CR4_PSE },
996 { "PAE", CR4_PAE, CR4_PAE },
997 { "MCE", CR4_MCE, CR4_MCE },
998 { "PGE", CR4_PGE, CR4_PGE },
999 { "PCE", CR4_PCE, CR4_PCE },
1000 { "OSFXSR", CR4_OSFXSR, CR4_OSFXSR },
1001 { "OSXMMEXCPT", CR4_OSXMMEXCPT, CR4_OSXMMEXCPT },
1002 { "VMXE", CR4_VMXE, CR4_VMXE },
1003 { "SMXE", CR4_SMXE, CR4_SMXE },
1004 { "PCIDE", CR4_PCIDE, CR4_PCIDE },
1005 { "OSXSAVE", CR4_OSXSAVE, CR4_OSXSAVE },
1006 { "SMEP", CR4_SMEP, CR4_SMEP },
1007 { "SMAP", CR4_SMAP, CR4_SMAP },
1008 { NULL, 0, 0 }
1009 };
1010
1011 cr0 = kmdb_unix_getcr0();
1012 cr2 = kmdb_unix_getcr2();
1013 cr3 = kmdb_unix_getcr3();
1014 cr4 = kmdb_unix_getcr4();
1015
1016 kmdb_unix_getgdtr(&gdtr);
1017
1018 mdb_printf("%%cr0 = 0x%lx <%b>\n", cr0, cr0, cr0_flag_bits);
1019 mdb_printf("%%cr2 = 0x%lx <%a>\n", cr2, cr2);
1020
1021 if ((cr4 & CR4_PCIDE)) {
1022 mdb_printf("%%cr3 = 0x%lx <pfn:0x%lx pcid:%lu>\n", cr3,
1023 cr3 >> MMU_PAGESHIFT, cr3 & MMU_PAGEOFFSET);
1024 } else {
1025 mdb_printf("%%cr3 = 0x%lx <pfn:0x%lx flags:%b>\n", cr3,
1026 cr3 >> MMU_PAGESHIFT, cr3, cr3_flag_bits);
1027 }
1028
1029 mdb_printf("%%cr4 = 0x%lx <%b>\n", cr4, cr4, cr4_flag_bits);
1030
1031 mdb_printf("%%gdtr.base = 0x%lx, %%gdtr.limit = 0x%hx\n",
1032 gdtr.dtr_base, gdtr.dtr_limit);
1033
1034 return (DCMD_OK);
1035 }
1036 #endif
1037
1038 static const mdb_dcmd_t dcmds[] = {
1039 { "gate_desc", ":", "dump a gate descriptor", gate_desc },
1040 { "idt", ":[-v]", "dump an IDT", idt },
1041 { "ttrace", "[-x] [-t kthread]", "dump trap trace buffers", ttrace },
1042 { "vatopfn", ":[-a as]", "translate address to physical page",
1043 va2pfn_dcmd },
1044 { "report_maps", ":[-m]",
1045 "Given PFN, report mappings / page table usage",
1046 report_maps_dcmd, report_maps_help },
1047 { "htables", "", "Given hat_t *, lists all its htable_t * values",
1048 htables_dcmd, htables_help },
1049 { "ptable", ":[-lm]", "Given PFN, dump contents of a page table",
1050 ptable_dcmd, ptable_help },
1051 { "ptmap", ":", "Given a cr3 value, dump all mappings",
1052 ptmap_dcmd, ptmap_help },
1053 { "pte", ":[-l N]", "print human readable page table entry",
1054 pte_dcmd },
1055 { "pfntomfn", ":", "convert physical page to hypervisor machine page",
1056 pfntomfn_dcmd },
1057 { "mfntopfn", ":", "convert hypervisor machine page to physical page",
1058 mfntopfn_dcmd },
1059 { "memseg_list", ":", "show memseg list", memseg_list },
1060 { "scalehrtime", ":[-a|-r]", "scale an unscaled high-res time",
1061 scalehrtime_cmd, scalehrtime_help },
1062 { "x86_featureset", NULL, "dump the x86_featureset vector",
1063 x86_featureset_cmd },
1064 #ifdef _KMDB
1065 { "sysregs", NULL, "dump system registers", sysregs_dcmd },
1066 #endif
1067 { NULL }
1068 };
1069
1070 static const mdb_walker_t walkers[] = {
1071 { "ttrace", "walks trap trace buffers in reverse chronological order",
1072 ttrace_walk_init, ttrace_walk_step, ttrace_walk_fini },
1073 { "mutex_owner", "walks the owner of a mutex",
1074 mutex_owner_init, mutex_owner_step },
1075 { "memseg", "walk the memseg structures",
1076 memseg_walk_init, memseg_walk_step, memseg_walk_fini },
1077 { NULL }
1078 };
1079
1080 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
1081
1082 const mdb_modinfo_t *
1083 _mdb_init(void)
1084 {
1085 return (&modinfo);
1086 }
1087
1088 void
1089 _mdb_fini(void)
1090 {
1091 free_mmu();
1092 }
--- EOF ---