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 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Copyright 2018 Joyent, Inc. 26 * Copyright 2025 Oxide Computer Company 27 */ 28 29 /* 30 * isa-dependent portions of the kmdb target 31 */ 32 33 #include <kmdb/kvm.h> 34 #include <kmdb/kmdb_kdi.h> 35 #include <kmdb/kmdb_asmutil.h> 36 #include <mdb/mdb_debug.h> 37 #include <mdb/mdb_err.h> 38 #include <mdb/mdb_list.h> 39 #include <mdb/mdb_target_impl.h> 40 #include <mdb/mdb_isautil.h> 41 #include <mdb/mdb_kreg_impl.h> 42 #include <mdb/mdb.h> 43 44 #include <sys/types.h> 45 #include <sys/frame.h> 46 #include <sys/trap.h> 47 #include <sys/bitmap.h> 48 #include <sys/pci_impl.h> 49 50 /* Higher than the highest trap number for which we have a defined specifier */ 51 #define KMT_MAXTRAPNO 0x20 52 53 #define IOPORTLIMIT 0xffff /* XXX find a new home for this */ 54 55 const char * 56 kmt_def_dismode(void) 57 { 58 #ifdef __amd64 59 return ("amd64"); 60 #else 61 return ("ia32"); 62 #endif 63 } 64 65 int 66 kmt_step_out_validate(mdb_tgt_t *t, uintptr_t pc) 67 { 68 kmt_data_t *kmt = t->t_data; 69 int i; 70 71 for (i = 0; i < sizeof (kmt->kmt_intrsyms) / sizeof (GElf_Sym); i++) { 72 GElf_Sym *sym = (GElf_Sym *)&kmt->kmt_intrsyms + i; 73 74 if (pc >= sym->st_value && pc < sym->st_value + sym->st_size) 75 return (0); 76 } 77 78 return (1); 79 } 80 81 /* 82 * Determine the return address for the current frame. 83 */ 84 int 85 kmt_step_out(mdb_tgt_t *t, uintptr_t *p) 86 { 87 mdb_instr_t instr; 88 kreg_t pc, sp, fp; 89 90 (void) kmdb_dpi_get_register("pc", &pc); 91 (void) kmdb_dpi_get_register("sp", &sp); 92 (void) kmdb_dpi_get_register("fp", &fp); 93 94 if (mdb_tgt_vread(t, &instr, sizeof (mdb_instr_t), pc) != 95 sizeof (mdb_instr_t)) 96 return (-1); /* errno is set for us */ 97 98 if (!kmt_step_out_validate(t, pc)) 99 return (set_errno(EMDB_TGTNOTSUP)); 100 101 return (mdb_isa_step_out(t, p, pc, fp, sp, instr)); 102 } 103 104 /* 105 * Return the address of the next instruction following a call, or return -1 106 * and set errno to EAGAIN if the target should just single-step. 107 */ 108 int 109 kmt_next(mdb_tgt_t *t, uintptr_t *p) 110 { 111 kreg_t pc; 112 mdb_instr_t instr; 113 114 (void) kmdb_dpi_get_register("pc", &pc); 115 116 if (mdb_tgt_vread(t, &instr, sizeof (mdb_instr_t), pc) != 117 sizeof (mdb_instr_t)) 118 return (-1); /* errno is set for us */ 119 120 return (mdb_isa_next(t, p, pc, instr)); 121 } 122 123 /*ARGSUSED*/ 124 static int 125 kmt_stack_common(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv, 126 int cpuid, mdb_tgt_stack_f *func) 127 { 128 const mdb_tgt_gregset_t *grp = NULL; 129 mdb_tgt_gregset_t gregs; 130 void *arg = (void *)(uintptr_t)mdb.m_nargs; 131 132 if (flags & DCMD_ADDRSPEC) { 133 bzero(&gregs, sizeof (gregs)); 134 gregs.kregs[KREG_FP] = addr; 135 grp = &gregs; 136 } else 137 grp = kmdb_dpi_get_gregs(cpuid); 138 139 if (grp == NULL) { 140 warn("failed to retrieve registers for cpu %d", cpuid); 141 return (DCMD_ERR); 142 } 143 144 if (argc != 0) { 145 if (argv->a_type == MDB_TYPE_CHAR || argc > 1) 146 return (DCMD_USAGE); 147 148 arg = (void *)(uintptr_t)mdb_argtoull(argv); 149 } 150 151 (void) mdb_isa_kvm_stack_iter(mdb.m_target, grp, func, arg); 152 153 return (DCMD_OK); 154 } 155 156 int 157 kmt_cpustack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv, 158 int cpuid, int verbose) 159 { 160 return (kmt_stack_common(addr, flags, argc, argv, cpuid, 161 (verbose ? mdb_isa_kvm_framev : mdb_isa_kvm_frame))); 162 } 163 164 int 165 kmt_stack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 166 { 167 return (kmt_stack_common(addr, flags, argc, argv, DPI_MASTER_CPUID, 168 mdb_isa_kvm_frame)); 169 } 170 171 int 172 kmt_stackv(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 173 { 174 return (kmt_stack_common(addr, flags, argc, argv, DPI_MASTER_CPUID, 175 mdb_isa_kvm_framev)); 176 } 177 178 int 179 kmt_stackr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 180 { 181 return (kmt_stack_common(addr, flags, argc, argv, DPI_MASTER_CPUID, 182 mdb_isa_kvm_framev)); 183 } 184 185 /*ARGSUSED*/ 186 void 187 kmt_printregs(const mdb_tgt_gregset_t *gregs) 188 { 189 mdb_isa_printregs(gregs); 190 } 191 192 #define IOCHECK_NOWARN 0 193 #define IOCHECK_WARN 1 194 195 static int 196 kmt_io_check(uint64_t nbytes, uintptr_t addr, int dowarn) 197 { 198 if (addr > IOPORTLIMIT) { 199 if (dowarn) 200 warn("port address must be 0-%#x\n", IOPORTLIMIT); 201 return (set_errno(EINVAL)); 202 } 203 204 if (nbytes != 1 && nbytes != 2 && nbytes != 4) { 205 if (dowarn) 206 warn("port access must be 1, 2, or 4 bytes\n"); 207 return (set_errno(EINVAL)); 208 } 209 210 if ((addr & (nbytes - 1)) != 0) { 211 if (dowarn) { 212 warn("address for %llu-byte access must be %llu-byte " 213 "aligned\n", (u_longlong_t)nbytes, 214 (u_longlong_t)nbytes); 215 } 216 return (set_errno(EINVAL)); 217 } 218 219 return (0); 220 } 221 222 /*ARGSUSED1*/ 223 int 224 kmt_in_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 225 { 226 uint64_t len = 0; 227 uint32_t buf; 228 229 if (mdb_getopts(argc, argv, 230 'L', MDB_OPT_UINT64, &len, 231 NULL) != argc) 232 return (DCMD_USAGE); 233 234 if (len == 0) 235 len = mdb.m_dcount; 236 237 if (kmt_io_check(len, addr, IOCHECK_WARN) < 0) 238 return (DCMD_ERR); 239 240 if (mdb_tgt_ioread(mdb.m_target, &buf, len, addr) < 0) { 241 warn("failed to read from port 0x%llx", (u_longlong_t)addr); 242 return (DCMD_ERR); 243 } 244 245 mdb_printf("%x\n", buf); 246 247 return (DCMD_OK); 248 } 249 250 /*ARGSUSED1*/ 251 int 252 kmt_out_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 253 { 254 uint64_t len = 0; 255 uint64_t val; 256 257 if (mdb_getopts(argc, argv, 258 'L', MDB_OPT_UINT64, &len, 259 NULL) != argc - 1) 260 return (DCMD_USAGE); 261 262 if (len == 0) 263 len = mdb.m_dcount; 264 265 argv += argc - 1; 266 val = mdb_argtoull(argv); 267 268 if (kmt_io_check(len, addr, IOCHECK_WARN) < 0) 269 return (DCMD_ERR); 270 271 if (val > (1ULL << (len * NBBY)) - 1) { 272 warn("value is out of range for port size\n"); 273 return (DCMD_ERR); 274 } 275 276 if (mdb_tgt_iowrite(mdb.m_target, &val, len, addr) < 0) { 277 warn("failed to write to port %llx", (u_longlong_t)addr); 278 return (DCMD_ERR); 279 } 280 281 return (DCMD_OK); 282 } 283 284 static int 285 kmt_rwmsr(uint32_t addr, uint64_t *valp, void (*rw)(uint32_t, uint64_t *)) 286 { 287 jmp_buf pcb, *oldpcb = NULL; 288 289 if (setjmp(pcb) != 0) { 290 kmdb_dpi_restore_fault_hdlr(oldpcb); 291 return (-1); /* errno is set for us */ 292 } 293 294 oldpcb = kmdb_dpi_set_fault_hdlr(&pcb); 295 rw(addr, valp); 296 kmdb_dpi_restore_fault_hdlr(oldpcb); 297 298 return (0); 299 } 300 301 /*ARGSUSED*/ 302 int 303 kmt_rdmsr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 304 { 305 uint64_t val; 306 307 if (!(flags & DCMD_ADDRSPEC)) 308 return (DCMD_USAGE); 309 310 if (kmt_rwmsr(addr, &val, rdmsr) < 0) { 311 warn("rdmsr failed"); 312 return (DCMD_ERR); 313 } 314 315 mdb_printf("%llx\n", (u_longlong_t)val); 316 317 return (DCMD_OK); 318 } 319 320 /*ARGSUSED*/ 321 int 322 kmt_wrmsr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 323 { 324 uint64_t val; 325 326 if (!(flags & DCMD_ADDRSPEC) || argc != 1) 327 return (DCMD_USAGE); 328 329 val = mdb_argtoull(argv); 330 331 if (kmt_rwmsr(addr, &val, wrmsr)) { 332 warn("wrmsr failed"); 333 return (DCMD_ERR); 334 } 335 336 return (DCMD_OK); 337 } 338 339 /*ARGSUSED*/ 340 ssize_t 341 kmt_write(mdb_tgt_t *t, const void *buf, size_t nbytes, uintptr_t addr) 342 { 343 if (!(t->t_flags & MDB_TGT_F_ALLOWIO) && 344 (nbytes = kmdb_kdi_range_is_nontoxic(addr, nbytes, 1)) == 0) 345 return (set_errno(EMDB_NOMAP)); 346 347 /* 348 * No writes to user space are allowed. If we were to allow it, we'd 349 * be in the unfortunate situation where kmdb could place a breakpoint 350 * on a userspace executable page; this dirty page would end up being 351 * flushed back to disk, incurring sadness when it's next executed. 352 * Besides, we can't allow trapping in from userspace anyway. 353 */ 354 if (addr < kmdb_kdi_get_userlimit()) 355 return (set_errno(EMDB_TGTNOTSUP)); 356 357 return (kmt_rw(t, (void *)buf, nbytes, addr, kmt_writer)); 358 } 359 360 /*ARGSUSED*/ 361 static ssize_t 362 kmt_iorw(mdb_tgt_t *t, void *buf, size_t nbytes, uint64_t addr, 363 void (*iorw)(void *, size_t, uintptr_t)) 364 { 365 jmp_buf pcb, *oldpcb = NULL; 366 367 if (kmt_io_check(nbytes, addr, IOCHECK_NOWARN) < 0) 368 return (-1); /* errno is set for us */ 369 370 if (setjmp(pcb) != 0) { 371 kmdb_dpi_restore_fault_hdlr(oldpcb); 372 return (-1); /* errno is set for us */ 373 } 374 375 oldpcb = kmdb_dpi_set_fault_hdlr(&pcb); 376 iorw(buf, nbytes, addr); 377 kmdb_dpi_restore_fault_hdlr(oldpcb); 378 379 return (nbytes); 380 } 381 382 /*ARGSUSED*/ 383 ssize_t 384 kmt_ioread(mdb_tgt_t *t, void *buf, size_t nbytes, uintptr_t addr) 385 { 386 return (kmt_iorw(t, buf, nbytes, addr, kmt_in)); 387 } 388 389 /*ARGSUSED*/ 390 ssize_t 391 kmt_iowrite(mdb_tgt_t *t, const void *buf, size_t nbytes, uintptr_t addr) 392 { 393 return (kmt_iorw(t, (void *)buf, nbytes, addr, kmt_out)); 394 } 395 396 static int 397 kmt_pcicfg_common(uintptr_t off, uint32_t *valp, const mdb_arg_t *argv, 398 void (*rw)(void *, size_t, uintptr_t)) 399 { 400 uint32_t bus, dev, func; 401 uint32_t addr; 402 403 bus = (uint32_t)mdb_argtoull(&argv[0]); 404 dev = (uint32_t)mdb_argtoull(&argv[1]); 405 func = (uint32_t)mdb_argtoull(&argv[2]); 406 407 if ((bus & 0xffff) != bus) { 408 warn("invalid bus number (must be 0-0xffff)\n"); 409 return (DCMD_ERR); 410 } 411 412 if ((dev & 0x1f) != dev) { 413 warn("invalid device number (must be 0-0x1f)\n"); 414 return (DCMD_ERR); 415 } 416 417 if ((func & 0x7) != func) { 418 warn("invalid function number (must be 0-7)\n"); 419 return (DCMD_ERR); 420 } 421 422 if ((off & 0xfc) != off) { 423 warn("invalid register number (must be 0-0xff, and 4-byte " 424 "aligned\n"); 425 return (DCMD_ERR); 426 } 427 428 addr = PCI_CADDR1(bus, dev, func, off); 429 430 if (kmt_iowrite(mdb.m_target, &addr, sizeof (addr), PCI_CONFADD) != 431 sizeof (addr)) { 432 warn("write of PCI_CONFADD failed"); 433 return (DCMD_ERR); 434 } 435 436 if (kmt_iorw(mdb.m_target, valp, sizeof (*valp), PCI_CONFDATA, rw) != 437 sizeof (*valp)) { 438 warn("access to PCI_CONFDATA failed"); 439 return (DCMD_ERR); 440 } 441 442 return (DCMD_OK); 443 } 444 445 /*ARGSUSED*/ 446 int 447 kmt_rdpcicfg(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 448 { 449 uint32_t val; 450 451 if (argc != 3 || !(flags & DCMD_ADDRSPEC)) 452 return (DCMD_USAGE); 453 454 if (kmt_pcicfg_common(addr, &val, argv, kmt_in) != DCMD_OK) 455 return (DCMD_ERR); 456 457 mdb_printf("%llx\n", (u_longlong_t)val); 458 459 return (DCMD_OK); 460 } 461 462 /*ARGSUSED*/ 463 int 464 kmt_wrpcicfg(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 465 { 466 uint32_t val; 467 468 if (argc != 4 || !(flags & DCMD_ADDRSPEC)) 469 return (DCMD_USAGE); 470 471 val = (uint32_t)mdb_argtoull(&argv[3]); 472 473 if (kmt_pcicfg_common(addr, &val, argv, kmt_out) != DCMD_OK) 474 return (DCMD_ERR); 475 476 return (DCMD_OK); 477 } 478 479 const char * 480 kmt_trapname(int trapnum) 481 { 482 static char trapname[11]; 483 484 switch (trapnum) { 485 case T_ZERODIV: 486 return ("division by zero (#de) trap"); 487 case T_SGLSTP: 488 return ("single-step (#db) trap"); 489 case T_NMIFLT: 490 return ("NMI"); 491 case T_BPTFLT: 492 return ("breakpoint (#bp) trap"); 493 case T_ILLINST: 494 return ("illegal instruction (#ud) trap"); 495 case T_SEGFLT: 496 return ("segment not present (#np) trap"); 497 case T_STKFLT: 498 return ("stack (#ss) trap"); 499 case T_GPFLT: 500 return ("general protection (#gp) trap"); 501 case T_PGFLT: 502 return ("page fault (#pf) trap"); 503 case T_ALIGNMENT: 504 return ("alignment check (#ac) trap"); 505 case T_MCE: 506 return ("machine check (#mc) trap"); 507 case T_SIMDFPE: 508 return ("SSE/SSE2 (#xm) trap"); 509 case T_DBGENTR: 510 return ("debugger entry trap"); 511 default: 512 (void) mdb_snprintf(trapname, sizeof (trapname), "trap %#x", 513 trapnum); 514 return (trapname); 515 } 516 } 517 518 void 519 kmt_init_isadep(mdb_tgt_t *t) 520 { 521 kmt_data_t *kmt = t->t_data; 522 523 kmt->kmt_rds = mdb_isa_kregs; 524 525 kmt->kmt_trapmax = KMT_MAXTRAPNO; 526 kmt->kmt_trapmap = mdb_zalloc(BT_SIZEOFMAP(kmt->kmt_trapmax), UM_SLEEP); 527 528 /* Traps for which we want to provide an explicit message */ 529 (void) mdb_tgt_add_fault(t, T_ZERODIV, MDB_TGT_SPEC_INTERNAL, 530 no_se_f, NULL); 531 (void) mdb_tgt_add_fault(t, T_ILLINST, MDB_TGT_SPEC_INTERNAL, 532 no_se_f, NULL); 533 (void) mdb_tgt_add_fault(t, T_SEGFLT, MDB_TGT_SPEC_INTERNAL, 534 no_se_f, NULL); 535 (void) mdb_tgt_add_fault(t, T_STKFLT, MDB_TGT_SPEC_INTERNAL, 536 no_se_f, NULL); 537 (void) mdb_tgt_add_fault(t, T_GPFLT, MDB_TGT_SPEC_INTERNAL, 538 no_se_f, NULL); 539 (void) mdb_tgt_add_fault(t, T_PGFLT, MDB_TGT_SPEC_INTERNAL, 540 no_se_f, NULL); 541 (void) mdb_tgt_add_fault(t, T_ALIGNMENT, MDB_TGT_SPEC_INTERNAL, 542 no_se_f, NULL); 543 (void) mdb_tgt_add_fault(t, T_MCE, MDB_TGT_SPEC_INTERNAL, 544 no_se_f, NULL); 545 (void) mdb_tgt_add_fault(t, T_SIMDFPE, MDB_TGT_SPEC_INTERNAL, 546 no_se_f, NULL); 547 548 /* 549 * Traps which will be handled elsewhere, and which therefore don't 550 * need the trap-based message. 551 */ 552 BT_SET(kmt->kmt_trapmap, T_SGLSTP); 553 BT_SET(kmt->kmt_trapmap, T_BPTFLT); 554 BT_SET(kmt->kmt_trapmap, T_DBGENTR); 555 556 /* Catch-all for traps not explicitly listed here */ 557 (void) mdb_tgt_add_fault(t, KMT_TRAP_NOTENUM, MDB_TGT_SPEC_INTERNAL, 558 no_se_f, NULL); 559 } 560 561 void 562 kmt_startup_isadep(mdb_tgt_t *t) 563 { 564 kmt_data_t *kmt = t->t_data; 565 566 /* 567 * The stack trace and ::step out code need to detect "interrupt" 568 * frames. The heuristic they use to detect said frames requires the 569 * addresses of routines that can generate them. 570 */ 571 (void) mdb_tgt_lookup_by_name(t, MDB_TGT_OBJ_EXEC, 572 "cmnint", &kmt->kmt_intrsyms._kmt_cmnint, NULL); 573 (void) mdb_tgt_lookup_by_name(t, MDB_TGT_OBJ_EXEC, 574 "cmntrap", &kmt->kmt_intrsyms._kmt_cmntrap, NULL); 575 (void) mdb_tgt_lookup_by_name(t, MDB_TGT_OBJ_EXEC, 576 "sys_sysenter", &kmt->kmt_intrsyms._kmt_sysenter, NULL); 577 (void) mdb_tgt_lookup_by_name(t, MDB_TGT_OBJ_EXEC, 578 "brand_sys_sysenter", &kmt->kmt_intrsyms._kmt_brand_sysenter, NULL); 579 #if defined(__amd64) 580 (void) mdb_tgt_lookup_by_name(t, MDB_TGT_OBJ_EXEC, 581 "sys_syscall", &kmt->kmt_intrsyms._kmt_syscall, NULL); 582 (void) mdb_tgt_lookup_by_name(t, MDB_TGT_OBJ_EXEC, 583 "brand_sys_syscall", &kmt->kmt_intrsyms._kmt_brand_syscall, NULL); 584 #endif 585 } 586