17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5c7bf3205Sjohnlev * Common Development and Distribution License (the "License"). 6c7bf3205Sjohnlev * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*ae115bc7Smrj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate /* 297c478bd9Sstevel@tonic-gate * The debugger/"PROM" interface layer 307c478bd9Sstevel@tonic-gate * 31*ae115bc7Smrj * It makes more sense on SPARC. In reality, these interfaces deal with three 32*ae115bc7Smrj * things: setting break/watchpoints, stepping, and interfacing with the KDI to 33*ae115bc7Smrj * set up kmdb's IDT handlers. 347c478bd9Sstevel@tonic-gate */ 357c478bd9Sstevel@tonic-gate 367c478bd9Sstevel@tonic-gate #include <kmdb/kmdb_dpi_impl.h> 377c478bd9Sstevel@tonic-gate #include <kmdb/kmdb_kdi.h> 387c478bd9Sstevel@tonic-gate #include <kmdb/kmdb_umemglue.h> 397c478bd9Sstevel@tonic-gate #include <kmdb/kaif.h> 40c7bf3205Sjohnlev #include <kmdb/kmdb_io.h> 41*ae115bc7Smrj #include <kmdb/kaif_start.h> 427c478bd9Sstevel@tonic-gate #include <mdb/mdb_err.h> 437c478bd9Sstevel@tonic-gate #include <mdb/mdb_debug.h> 447c478bd9Sstevel@tonic-gate #include <mdb/mdb_isautil.h> 457c478bd9Sstevel@tonic-gate #include <mdb/mdb_io_impl.h> 46*ae115bc7Smrj #include <mdb/mdb_kreg_impl.h> 477c478bd9Sstevel@tonic-gate #include <mdb/mdb.h> 487c478bd9Sstevel@tonic-gate 497c478bd9Sstevel@tonic-gate #include <sys/types.h> 507c478bd9Sstevel@tonic-gate #include <sys/bitmap.h> 51c7bf3205Sjohnlev #include <sys/termios.h> 52*ae115bc7Smrj #include <sys/kdi_impl.h> 537c478bd9Sstevel@tonic-gate 54*ae115bc7Smrj /* 55*ae115bc7Smrj * This is the area containing the saved state when we enter 56*ae115bc7Smrj * via kmdb's IDT entries. 57*ae115bc7Smrj */ 58*ae115bc7Smrj kdi_cpusave_t *kaif_cpusave; 597c478bd9Sstevel@tonic-gate int kaif_ncpusave; 60*ae115bc7Smrj kdi_drreg_t kaif_drreg; 617c478bd9Sstevel@tonic-gate 627c478bd9Sstevel@tonic-gate uint32_t kaif_waptmap; 637c478bd9Sstevel@tonic-gate 647c478bd9Sstevel@tonic-gate int kaif_trap_switch; 657c478bd9Sstevel@tonic-gate 667c478bd9Sstevel@tonic-gate void (*kaif_modchg_cb)(struct modctl *, int); 677c478bd9Sstevel@tonic-gate 687c478bd9Sstevel@tonic-gate enum { 697c478bd9Sstevel@tonic-gate M_SYSRET = 0x07, /* after M_ESC */ 707c478bd9Sstevel@tonic-gate M_ESC = 0x0f, 717c478bd9Sstevel@tonic-gate M_SYSEXIT = 0x35, /* after M_ESC */ 727c478bd9Sstevel@tonic-gate M_REX_LO = 0x40, /* first REX prefix */ 737c478bd9Sstevel@tonic-gate M_REX_HI = 0x4f, /* last REX prefix */ 747c478bd9Sstevel@tonic-gate M_PUSHF = 0x9c, /* pushfl and pushfq */ 757c478bd9Sstevel@tonic-gate M_POPF = 0x9d, /* popfl and popfq */ 767c478bd9Sstevel@tonic-gate M_INT3 = 0xcc, 777c478bd9Sstevel@tonic-gate M_INTX = 0xcd, 787c478bd9Sstevel@tonic-gate M_INTO = 0xce, 797c478bd9Sstevel@tonic-gate M_IRET = 0xcf, 807c478bd9Sstevel@tonic-gate M_CLI = 0xfa, 817c478bd9Sstevel@tonic-gate M_STI = 0xfb 827c478bd9Sstevel@tonic-gate }; 837c478bd9Sstevel@tonic-gate 847c478bd9Sstevel@tonic-gate #define KAIF_BREAKPOINT_INSTR M_INT3 857c478bd9Sstevel@tonic-gate 867c478bd9Sstevel@tonic-gate #define KAIF_WPPRIV2ID(wp) (int)(uintptr_t)((wp)->wp_priv) 877c478bd9Sstevel@tonic-gate 887c478bd9Sstevel@tonic-gate #ifdef __amd64 897c478bd9Sstevel@tonic-gate #define FLAGS_REG_NAME "rflags" 907c478bd9Sstevel@tonic-gate #else 917c478bd9Sstevel@tonic-gate #define FLAGS_REG_NAME "eflags" 927c478bd9Sstevel@tonic-gate #endif 937c478bd9Sstevel@tonic-gate 947c478bd9Sstevel@tonic-gate /* 957c478bd9Sstevel@tonic-gate * Called during normal debugger operation and during debugger faults. 967c478bd9Sstevel@tonic-gate */ 977c478bd9Sstevel@tonic-gate static void 987c478bd9Sstevel@tonic-gate kaif_enter_mon(void) 997c478bd9Sstevel@tonic-gate { 1007c478bd9Sstevel@tonic-gate char c; 1017c478bd9Sstevel@tonic-gate 1027c478bd9Sstevel@tonic-gate for (;;) { 1037c478bd9Sstevel@tonic-gate mdb_iob_printf(mdb.m_out, 1047c478bd9Sstevel@tonic-gate "%s: Do you really want to reboot? (y/n) ", 1057c478bd9Sstevel@tonic-gate mdb.m_pname); 1067c478bd9Sstevel@tonic-gate mdb_iob_flush(mdb.m_out); 107c7bf3205Sjohnlev mdb_iob_clearlines(mdb.m_out); 1087c478bd9Sstevel@tonic-gate 109c7bf3205Sjohnlev c = kmdb_getchar(); 1107c478bd9Sstevel@tonic-gate 111c7bf3205Sjohnlev if (c == 'n' || c == 'N' || c == CTRL('c')) 1127c478bd9Sstevel@tonic-gate return; 1137c478bd9Sstevel@tonic-gate else if (c == 'y' || c == 'Y') { 1147c478bd9Sstevel@tonic-gate mdb_iob_printf(mdb.m_out, "Rebooting...\n"); 1157c478bd9Sstevel@tonic-gate 1167c478bd9Sstevel@tonic-gate kmdb_dpi_reboot(); 1177c478bd9Sstevel@tonic-gate } 1187c478bd9Sstevel@tonic-gate } 1197c478bd9Sstevel@tonic-gate } 1207c478bd9Sstevel@tonic-gate 121acbc304dSjohnlev static kaif_cpusave_t * 122acbc304dSjohnlev kaif_cpuid2save(int cpuid) 123acbc304dSjohnlev { 124acbc304dSjohnlev kaif_cpusave_t *save; 125acbc304dSjohnlev 126acbc304dSjohnlev if (cpuid == DPI_MASTER_CPUID) 127acbc304dSjohnlev return (&kaif_cpusave[kaif_master_cpuid]); 128acbc304dSjohnlev 129acbc304dSjohnlev if (cpuid < 0 || cpuid >= kaif_ncpusave) { 130acbc304dSjohnlev (void) set_errno(EINVAL); 131acbc304dSjohnlev return (NULL); 132acbc304dSjohnlev } 133acbc304dSjohnlev 134acbc304dSjohnlev save = &kaif_cpusave[cpuid]; 135acbc304dSjohnlev 136acbc304dSjohnlev if (save->krs_cpu_state != KAIF_CPU_STATE_MASTER && 137acbc304dSjohnlev save->krs_cpu_state != KAIF_CPU_STATE_SLAVE) { 138acbc304dSjohnlev (void) set_errno(EINVAL); 139acbc304dSjohnlev return (NULL); 140acbc304dSjohnlev } 141acbc304dSjohnlev 142acbc304dSjohnlev return (save); 143acbc304dSjohnlev } 144acbc304dSjohnlev 1457c478bd9Sstevel@tonic-gate static int 1467c478bd9Sstevel@tonic-gate kaif_get_cpu_state(int cpuid) 1477c478bd9Sstevel@tonic-gate { 148acbc304dSjohnlev kaif_cpusave_t *save; 1497c478bd9Sstevel@tonic-gate 150acbc304dSjohnlev if ((save = kaif_cpuid2save(cpuid)) == NULL) 151acbc304dSjohnlev return (-1); /* errno is set for us */ 1527c478bd9Sstevel@tonic-gate 153acbc304dSjohnlev switch (save->krs_cpu_state) { 1547c478bd9Sstevel@tonic-gate case KAIF_CPU_STATE_MASTER: 1557c478bd9Sstevel@tonic-gate return (DPI_CPU_STATE_MASTER); 1567c478bd9Sstevel@tonic-gate case KAIF_CPU_STATE_SLAVE: 1577c478bd9Sstevel@tonic-gate return (DPI_CPU_STATE_SLAVE); 1587c478bd9Sstevel@tonic-gate default: 1597c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 1607c478bd9Sstevel@tonic-gate } 1617c478bd9Sstevel@tonic-gate } 1627c478bd9Sstevel@tonic-gate 1637c478bd9Sstevel@tonic-gate static int 1647c478bd9Sstevel@tonic-gate kaif_get_master_cpuid(void) 1657c478bd9Sstevel@tonic-gate { 1667c478bd9Sstevel@tonic-gate return (kaif_master_cpuid); 1677c478bd9Sstevel@tonic-gate } 1687c478bd9Sstevel@tonic-gate 169*ae115bc7Smrj static mdb_tgt_gregset_t * 170*ae115bc7Smrj kaif_kdi_to_gregs(int cpuid) 1717c478bd9Sstevel@tonic-gate { 172acbc304dSjohnlev kaif_cpusave_t *save; 1737c478bd9Sstevel@tonic-gate 174acbc304dSjohnlev if ((save = kaif_cpuid2save(cpuid)) == NULL) 175acbc304dSjohnlev return (NULL); /* errno is set for us */ 1767c478bd9Sstevel@tonic-gate 177*ae115bc7Smrj /* 178*ae115bc7Smrj * The saved registers are actually identical to an mdb_tgt_gregset, 179*ae115bc7Smrj * so we can directly cast here. 180*ae115bc7Smrj */ 181*ae115bc7Smrj return ((mdb_tgt_gregset_t *)save->krs_gregs); 182*ae115bc7Smrj } 183*ae115bc7Smrj 184*ae115bc7Smrj static const mdb_tgt_gregset_t * 185*ae115bc7Smrj kaif_get_gregs(int cpuid) 186*ae115bc7Smrj { 187*ae115bc7Smrj return (kaif_kdi_to_gregs(cpuid)); 1887c478bd9Sstevel@tonic-gate } 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate typedef struct kaif_reg_synonyms { 1917c478bd9Sstevel@tonic-gate const char *rs_syn; 1927c478bd9Sstevel@tonic-gate const char *rs_name; 1937c478bd9Sstevel@tonic-gate } kaif_reg_synonyms_t; 1947c478bd9Sstevel@tonic-gate 1957c478bd9Sstevel@tonic-gate static kreg_t * 196acbc304dSjohnlev kaif_find_regp(const char *regname) 1977c478bd9Sstevel@tonic-gate { 1987c478bd9Sstevel@tonic-gate static const kaif_reg_synonyms_t synonyms[] = { 1997c478bd9Sstevel@tonic-gate #ifdef __amd64 2007c478bd9Sstevel@tonic-gate { "pc", "rip" }, 2017c478bd9Sstevel@tonic-gate { "sp", "rsp" }, 2027c478bd9Sstevel@tonic-gate { "fp", "rbp" }, 2037c478bd9Sstevel@tonic-gate #else 2047c478bd9Sstevel@tonic-gate { "pc", "eip" }, 2057c478bd9Sstevel@tonic-gate { "sp", "esp" }, 2067c478bd9Sstevel@tonic-gate { "fp", "ebp" }, 2077c478bd9Sstevel@tonic-gate #endif 2087c478bd9Sstevel@tonic-gate { "tt", "trapno" } 2097c478bd9Sstevel@tonic-gate }; 210*ae115bc7Smrj mdb_tgt_gregset_t *regs; 2117c478bd9Sstevel@tonic-gate int i; 2127c478bd9Sstevel@tonic-gate 213*ae115bc7Smrj if ((regs = kaif_kdi_to_gregs(DPI_MASTER_CPUID)) == NULL) 214*ae115bc7Smrj return (NULL); 2157c478bd9Sstevel@tonic-gate 2167c478bd9Sstevel@tonic-gate for (i = 0; i < sizeof (synonyms) / sizeof (synonyms[0]); i++) { 2177c478bd9Sstevel@tonic-gate if (strcmp(synonyms[i].rs_syn, regname) == 0) 2187c478bd9Sstevel@tonic-gate regname = synonyms[i].rs_name; 2197c478bd9Sstevel@tonic-gate } 2207c478bd9Sstevel@tonic-gate 2217c478bd9Sstevel@tonic-gate for (i = 0; mdb_isa_kregs[i].rd_name != NULL; i++) { 2227c478bd9Sstevel@tonic-gate const mdb_tgt_regdesc_t *rd = &mdb_isa_kregs[i]; 2237c478bd9Sstevel@tonic-gate 2247c478bd9Sstevel@tonic-gate if (strcmp(rd->rd_name, regname) == 0) 225*ae115bc7Smrj return (®s->kregs[rd->rd_num]); 2267c478bd9Sstevel@tonic-gate } 2277c478bd9Sstevel@tonic-gate 2287c478bd9Sstevel@tonic-gate (void) set_errno(ENOENT); 2297c478bd9Sstevel@tonic-gate return (NULL); 2307c478bd9Sstevel@tonic-gate } 2317c478bd9Sstevel@tonic-gate 2327c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 2337c478bd9Sstevel@tonic-gate static int 234acbc304dSjohnlev kaif_get_register(const char *regname, kreg_t *valp) 2357c478bd9Sstevel@tonic-gate { 2367c478bd9Sstevel@tonic-gate kreg_t *regp; 2377c478bd9Sstevel@tonic-gate 238acbc304dSjohnlev if ((regp = kaif_find_regp(regname)) == NULL) 2397c478bd9Sstevel@tonic-gate return (-1); 2407c478bd9Sstevel@tonic-gate 2417c478bd9Sstevel@tonic-gate *valp = *regp; 2427c478bd9Sstevel@tonic-gate 2437c478bd9Sstevel@tonic-gate return (0); 2447c478bd9Sstevel@tonic-gate } 2457c478bd9Sstevel@tonic-gate 2467c478bd9Sstevel@tonic-gate static int 247acbc304dSjohnlev kaif_set_register(const char *regname, kreg_t val) 2487c478bd9Sstevel@tonic-gate { 2497c478bd9Sstevel@tonic-gate kreg_t *regp; 2507c478bd9Sstevel@tonic-gate 251acbc304dSjohnlev if ((regp = kaif_find_regp(regname)) == NULL) 2527c478bd9Sstevel@tonic-gate return (-1); 2537c478bd9Sstevel@tonic-gate 2547c478bd9Sstevel@tonic-gate *regp = val; 2557c478bd9Sstevel@tonic-gate 2567c478bd9Sstevel@tonic-gate return (0); 2577c478bd9Sstevel@tonic-gate } 2587c478bd9Sstevel@tonic-gate 2597c478bd9Sstevel@tonic-gate static int 2607c478bd9Sstevel@tonic-gate kaif_brkpt_arm(uintptr_t addr, mdb_instr_t *instrp) 2617c478bd9Sstevel@tonic-gate { 2627c478bd9Sstevel@tonic-gate mdb_instr_t bkpt = KAIF_BREAKPOINT_INSTR; 2637c478bd9Sstevel@tonic-gate 2647c478bd9Sstevel@tonic-gate if (mdb_tgt_vread(mdb.m_target, instrp, sizeof (mdb_instr_t), addr) != 2657c478bd9Sstevel@tonic-gate sizeof (mdb_instr_t)) 2667c478bd9Sstevel@tonic-gate return (-1); /* errno is set for us */ 2677c478bd9Sstevel@tonic-gate 2687c478bd9Sstevel@tonic-gate if (mdb_tgt_vwrite(mdb.m_target, &bkpt, sizeof (mdb_instr_t), addr) != 2697c478bd9Sstevel@tonic-gate sizeof (mdb_instr_t)) 2707c478bd9Sstevel@tonic-gate return (-1); /* errno is set for us */ 2717c478bd9Sstevel@tonic-gate 2727c478bd9Sstevel@tonic-gate return (0); 2737c478bd9Sstevel@tonic-gate } 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate static int 2767c478bd9Sstevel@tonic-gate kaif_brkpt_disarm(uintptr_t addr, mdb_instr_t instrp) 2777c478bd9Sstevel@tonic-gate { 2787c478bd9Sstevel@tonic-gate if (mdb_tgt_vwrite(mdb.m_target, &instrp, sizeof (mdb_instr_t), addr) != 2797c478bd9Sstevel@tonic-gate sizeof (mdb_instr_t)) 2807c478bd9Sstevel@tonic-gate return (-1); /* errno is set for us */ 2817c478bd9Sstevel@tonic-gate 2827c478bd9Sstevel@tonic-gate return (0); 2837c478bd9Sstevel@tonic-gate } 2847c478bd9Sstevel@tonic-gate 2857c478bd9Sstevel@tonic-gate /* 2867c478bd9Sstevel@tonic-gate * Intel watchpoints are even more fun than SPARC ones. The Intel architecture 2877c478bd9Sstevel@tonic-gate * manuals refer to watchpoints as breakpoints. For consistency with the 2887c478bd9Sstevel@tonic-gate * terminology used in other portions of kmdb, we will, however, refer to them 2897c478bd9Sstevel@tonic-gate * as watchpoints. 2907c478bd9Sstevel@tonic-gate * 2917c478bd9Sstevel@tonic-gate * Execute, data write, I/O read/write, and data read/write watchpoints are 2927c478bd9Sstevel@tonic-gate * supported by the hardware. Execute watchpoints must be one byte in length, 2937c478bd9Sstevel@tonic-gate * and must be placed on the first byte of the instruction to be watched. 2947c478bd9Sstevel@tonic-gate * Lengths of other watchpoints are more varied. 2957c478bd9Sstevel@tonic-gate * 2967c478bd9Sstevel@tonic-gate * Given that we already have a breakpoint facility, and given the restrictions 2977c478bd9Sstevel@tonic-gate * placed on execute watchpoints, we're going to disallow the creation of 2987c478bd9Sstevel@tonic-gate * execute watchpoints. The others will be fully supported. See the Debugging 2997c478bd9Sstevel@tonic-gate * chapter in both the IA32 and AMD64 System Programming books for more details. 3007c478bd9Sstevel@tonic-gate */ 3017c478bd9Sstevel@tonic-gate 3027c478bd9Sstevel@tonic-gate #ifdef __amd64 3037c478bd9Sstevel@tonic-gate #define WAPT_DATA_MAX_SIZE 8 3047c478bd9Sstevel@tonic-gate #define WAPT_DATA_SIZES_MSG "1, 2, 4, or 8" 3057c478bd9Sstevel@tonic-gate #else 3067c478bd9Sstevel@tonic-gate #define WAPT_DATA_MAX_SIZE 4 3077c478bd9Sstevel@tonic-gate #define WAPT_DATA_SIZES_MSG "1, 2, or 4" 3087c478bd9Sstevel@tonic-gate #endif 3097c478bd9Sstevel@tonic-gate 3107c478bd9Sstevel@tonic-gate static int 3117c478bd9Sstevel@tonic-gate kaif_wapt_validate(kmdb_wapt_t *wp) 3127c478bd9Sstevel@tonic-gate { 3137c478bd9Sstevel@tonic-gate if (wp->wp_type == DPI_WAPT_TYPE_IO) { 3147c478bd9Sstevel@tonic-gate if (wp->wp_wflags != (MDB_TGT_WA_R | MDB_TGT_WA_W)) { 3157c478bd9Sstevel@tonic-gate warn("I/O port watchpoints must be read/write\n"); 3167c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 3177c478bd9Sstevel@tonic-gate } 3187c478bd9Sstevel@tonic-gate 3197c478bd9Sstevel@tonic-gate if (wp->wp_size != 1 && wp->wp_size != 2 && wp->wp_size != 4) { 3207c478bd9Sstevel@tonic-gate warn("I/O watchpoint size must be 1, 2, or 4 bytes\n"); 3217c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 3227c478bd9Sstevel@tonic-gate } 3237c478bd9Sstevel@tonic-gate 3247c478bd9Sstevel@tonic-gate } else if (wp->wp_type == DPI_WAPT_TYPE_PHYS) { 3257c478bd9Sstevel@tonic-gate warn("physical address watchpoints are not supported on this " 3267c478bd9Sstevel@tonic-gate "platform\n"); 3277c478bd9Sstevel@tonic-gate return (set_errno(EMDB_TGTHWNOTSUP)); 3287c478bd9Sstevel@tonic-gate 3297c478bd9Sstevel@tonic-gate } else { 3307c478bd9Sstevel@tonic-gate if (wp->wp_wflags != (MDB_TGT_WA_R | MDB_TGT_WA_W) && 3317c478bd9Sstevel@tonic-gate wp->wp_wflags != MDB_TGT_WA_W) { 3327c478bd9Sstevel@tonic-gate warn("watchpoints must be read/write or write-only\n"); 3337c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 3347c478bd9Sstevel@tonic-gate } 3357c478bd9Sstevel@tonic-gate 3367c478bd9Sstevel@tonic-gate if ((wp->wp_size & -(wp->wp_size)) != wp->wp_size || 3377c478bd9Sstevel@tonic-gate wp->wp_size > WAPT_DATA_MAX_SIZE) { 3387c478bd9Sstevel@tonic-gate warn("data watchpoint size must be " WAPT_DATA_SIZES_MSG 3397c478bd9Sstevel@tonic-gate " bytes\n"); 3407c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 3417c478bd9Sstevel@tonic-gate } 3427c478bd9Sstevel@tonic-gate 3437c478bd9Sstevel@tonic-gate } 3447c478bd9Sstevel@tonic-gate 3457c478bd9Sstevel@tonic-gate if (wp->wp_addr & (wp->wp_size - 1)) { 3467c478bd9Sstevel@tonic-gate warn("%lu-byte watchpoints must be %lu-byte aligned\n", 3477c478bd9Sstevel@tonic-gate (ulong_t)wp->wp_size, (ulong_t)wp->wp_size); 3487c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 3497c478bd9Sstevel@tonic-gate } 3507c478bd9Sstevel@tonic-gate 3517c478bd9Sstevel@tonic-gate return (0); 3527c478bd9Sstevel@tonic-gate } 3537c478bd9Sstevel@tonic-gate 3547c478bd9Sstevel@tonic-gate static int 3557c478bd9Sstevel@tonic-gate kaif_wapt_reserve(kmdb_wapt_t *wp) 3567c478bd9Sstevel@tonic-gate { 3577c478bd9Sstevel@tonic-gate int id; 3587c478bd9Sstevel@tonic-gate 359*ae115bc7Smrj for (id = 0; id <= KDI_MAXWPIDX; id++) { 3607c478bd9Sstevel@tonic-gate if (!BT_TEST(&kaif_waptmap, id)) { 3617c478bd9Sstevel@tonic-gate /* found one */ 3627c478bd9Sstevel@tonic-gate BT_SET(&kaif_waptmap, id); 3637c478bd9Sstevel@tonic-gate wp->wp_priv = (void *)(uintptr_t)id; 3647c478bd9Sstevel@tonic-gate return (0); 3657c478bd9Sstevel@tonic-gate } 3667c478bd9Sstevel@tonic-gate } 3677c478bd9Sstevel@tonic-gate 3687c478bd9Sstevel@tonic-gate return (set_errno(EMDB_WPTOOMANY)); 3697c478bd9Sstevel@tonic-gate } 3707c478bd9Sstevel@tonic-gate 3717c478bd9Sstevel@tonic-gate static void 3727c478bd9Sstevel@tonic-gate kaif_wapt_release(kmdb_wapt_t *wp) 3737c478bd9Sstevel@tonic-gate { 3747c478bd9Sstevel@tonic-gate int id = KAIF_WPPRIV2ID(wp); 3757c478bd9Sstevel@tonic-gate 3767c478bd9Sstevel@tonic-gate ASSERT(BT_TEST(&kaif_waptmap, id)); 3777c478bd9Sstevel@tonic-gate BT_CLEAR(&kaif_waptmap, id); 3787c478bd9Sstevel@tonic-gate } 3797c478bd9Sstevel@tonic-gate 3807c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 3817c478bd9Sstevel@tonic-gate static void 3827c478bd9Sstevel@tonic-gate kaif_wapt_arm(kmdb_wapt_t *wp) 3837c478bd9Sstevel@tonic-gate { 3847c478bd9Sstevel@tonic-gate uint_t rw; 3857c478bd9Sstevel@tonic-gate int hwid = KAIF_WPPRIV2ID(wp); 3867c478bd9Sstevel@tonic-gate 3877c478bd9Sstevel@tonic-gate ASSERT(BT_TEST(&kaif_waptmap, hwid)); 3887c478bd9Sstevel@tonic-gate 3897c478bd9Sstevel@tonic-gate if (wp->wp_type == DPI_WAPT_TYPE_IO) 3907c478bd9Sstevel@tonic-gate rw = KREG_DRCTL_WP_IORW; 3917c478bd9Sstevel@tonic-gate else if (wp->wp_wflags & MDB_TGT_WA_R) 3927c478bd9Sstevel@tonic-gate rw = KREG_DRCTL_WP_RW; 3937c478bd9Sstevel@tonic-gate else if (wp->wp_wflags & MDB_TGT_WA_X) 3947c478bd9Sstevel@tonic-gate rw = KREG_DRCTL_WP_EXEC; 3957c478bd9Sstevel@tonic-gate else 3967c478bd9Sstevel@tonic-gate rw = KREG_DRCTL_WP_WONLY; 3977c478bd9Sstevel@tonic-gate 3987c478bd9Sstevel@tonic-gate kaif_drreg.dr_addr[hwid] = wp->wp_addr; 3997c478bd9Sstevel@tonic-gate 4007c478bd9Sstevel@tonic-gate kaif_drreg.dr_ctl &= ~KREG_DRCTL_WP_MASK(hwid); 4017c478bd9Sstevel@tonic-gate kaif_drreg.dr_ctl |= KREG_DRCTL_WP_LENRW(hwid, wp->wp_size - 1, rw); 4027c478bd9Sstevel@tonic-gate kaif_drreg.dr_ctl |= KREG_DRCTL_WPEN(hwid); 403*ae115bc7Smrj kmdb_kdi_update_drreg(&kaif_drreg); 4047c478bd9Sstevel@tonic-gate } 4057c478bd9Sstevel@tonic-gate 4067c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 4077c478bd9Sstevel@tonic-gate static void 4087c478bd9Sstevel@tonic-gate kaif_wapt_disarm(kmdb_wapt_t *wp) 4097c478bd9Sstevel@tonic-gate { 4107c478bd9Sstevel@tonic-gate int hwid = KAIF_WPPRIV2ID(wp); 4117c478bd9Sstevel@tonic-gate 4127c478bd9Sstevel@tonic-gate ASSERT(BT_TEST(&kaif_waptmap, hwid)); 4137c478bd9Sstevel@tonic-gate 4147c478bd9Sstevel@tonic-gate kaif_drreg.dr_addr[hwid] = 0; 4157c478bd9Sstevel@tonic-gate kaif_drreg.dr_ctl &= ~(KREG_DRCTL_WP_MASK(hwid) | 4167c478bd9Sstevel@tonic-gate KREG_DRCTL_WPEN_MASK(hwid)); 417*ae115bc7Smrj kmdb_kdi_update_drreg(&kaif_drreg); 4187c478bd9Sstevel@tonic-gate } 4197c478bd9Sstevel@tonic-gate 4207c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 4217c478bd9Sstevel@tonic-gate static int 4227c478bd9Sstevel@tonic-gate kaif_wapt_match(kmdb_wapt_t *wp) 4237c478bd9Sstevel@tonic-gate { 4247c478bd9Sstevel@tonic-gate int hwid = KAIF_WPPRIV2ID(wp); 4257c478bd9Sstevel@tonic-gate uint32_t mask = KREG_DRSTAT_WP_MASK(hwid); 4267c478bd9Sstevel@tonic-gate int n = 0; 4277c478bd9Sstevel@tonic-gate int i; 4287c478bd9Sstevel@tonic-gate 4297c478bd9Sstevel@tonic-gate ASSERT(BT_TEST(&kaif_waptmap, hwid)); 4307c478bd9Sstevel@tonic-gate 4317c478bd9Sstevel@tonic-gate for (i = 0; i < kaif_ncpusave; i++) 4327c478bd9Sstevel@tonic-gate n += (kaif_cpusave[i].krs_dr.dr_stat & mask) != 0; 4337c478bd9Sstevel@tonic-gate 4347c478bd9Sstevel@tonic-gate return (n); 4357c478bd9Sstevel@tonic-gate } 4367c478bd9Sstevel@tonic-gate 4377c478bd9Sstevel@tonic-gate static int 4387c478bd9Sstevel@tonic-gate kaif_step(void) 4397c478bd9Sstevel@tonic-gate { 4407c478bd9Sstevel@tonic-gate kreg_t pc, fl, oldfl, newfl, sp; 4417c478bd9Sstevel@tonic-gate mdb_tgt_addr_t npc; 4427c478bd9Sstevel@tonic-gate mdb_instr_t instr; 4437c478bd9Sstevel@tonic-gate int emulated = 0, rchk = 0; 4447c478bd9Sstevel@tonic-gate size_t pcoff = 0; 4457c478bd9Sstevel@tonic-gate 4467c478bd9Sstevel@tonic-gate (void) kmdb_dpi_get_register("pc", &pc); 4477c478bd9Sstevel@tonic-gate 4487c478bd9Sstevel@tonic-gate if ((npc = mdb_dis_nextins(mdb.m_disasm, mdb.m_target, 4497c478bd9Sstevel@tonic-gate MDB_TGT_AS_VIRT, pc)) == pc) { 4507c478bd9Sstevel@tonic-gate warn("failed to decode instruction at %a for step\n", pc); 4517c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 4527c478bd9Sstevel@tonic-gate } 4537c478bd9Sstevel@tonic-gate 4547c478bd9Sstevel@tonic-gate /* 4557c478bd9Sstevel@tonic-gate * Stepping behavior depends on the type of instruction. It does not 4567c478bd9Sstevel@tonic-gate * depend on the presence of a REX prefix, as the action we take for a 4577c478bd9Sstevel@tonic-gate * given instruction doesn't currently vary for 32-bit instructions 4587c478bd9Sstevel@tonic-gate * versus their 64-bit counterparts. 4597c478bd9Sstevel@tonic-gate */ 4607c478bd9Sstevel@tonic-gate do { 4617c478bd9Sstevel@tonic-gate if (mdb_tgt_vread(mdb.m_target, &instr, sizeof (mdb_instr_t), 4627c478bd9Sstevel@tonic-gate pc + pcoff) != sizeof (mdb_instr_t)) { 4637c478bd9Sstevel@tonic-gate warn("failed to read at %p for step", 4647c478bd9Sstevel@tonic-gate (void *)(pc + pcoff)); 4657c478bd9Sstevel@tonic-gate return (-1); 4667c478bd9Sstevel@tonic-gate } 4677c478bd9Sstevel@tonic-gate } while (pcoff++, (instr >= M_REX_LO && instr <= M_REX_HI && !rchk++)); 4687c478bd9Sstevel@tonic-gate 4697c478bd9Sstevel@tonic-gate switch (instr) { 4707c478bd9Sstevel@tonic-gate case M_IRET: 4717c478bd9Sstevel@tonic-gate warn("iret cannot be stepped\n"); 4727c478bd9Sstevel@tonic-gate return (set_errno(EMDB_TGTNOTSUP)); 4737c478bd9Sstevel@tonic-gate 4747c478bd9Sstevel@tonic-gate case M_INT3: 4757c478bd9Sstevel@tonic-gate case M_INTX: 4767c478bd9Sstevel@tonic-gate case M_INTO: 4777c478bd9Sstevel@tonic-gate warn("int cannot be stepped\n"); 4787c478bd9Sstevel@tonic-gate return (set_errno(EMDB_TGTNOTSUP)); 4797c478bd9Sstevel@tonic-gate 4807c478bd9Sstevel@tonic-gate case M_ESC: 4817c478bd9Sstevel@tonic-gate if (mdb_tgt_vread(mdb.m_target, &instr, sizeof (mdb_instr_t), 4827c478bd9Sstevel@tonic-gate pc + pcoff) != sizeof (mdb_instr_t)) { 4837c478bd9Sstevel@tonic-gate warn("failed to read at %p for step", 4847c478bd9Sstevel@tonic-gate (void *)(pc + pcoff)); 4857c478bd9Sstevel@tonic-gate return (-1); 4867c478bd9Sstevel@tonic-gate } 4877c478bd9Sstevel@tonic-gate 4887c478bd9Sstevel@tonic-gate switch (instr) { 4897c478bd9Sstevel@tonic-gate case M_SYSRET: 4907c478bd9Sstevel@tonic-gate warn("sysret cannot be stepped\n"); 4917c478bd9Sstevel@tonic-gate return (set_errno(EMDB_TGTNOTSUP)); 4927c478bd9Sstevel@tonic-gate case M_SYSEXIT: 4937c478bd9Sstevel@tonic-gate warn("sysexit cannot be stepped\n"); 4947c478bd9Sstevel@tonic-gate return (set_errno(EMDB_TGTNOTSUP)); 4957c478bd9Sstevel@tonic-gate } 4967c478bd9Sstevel@tonic-gate break; 4977c478bd9Sstevel@tonic-gate 4987c478bd9Sstevel@tonic-gate /* 4997c478bd9Sstevel@tonic-gate * Some instructions need to be emulated. We need to prevent direct 5007c478bd9Sstevel@tonic-gate * manipulations of EFLAGS, so we'll emulate cli, sti. pushfl and 5017c478bd9Sstevel@tonic-gate * popfl also receive special handling, as they manipulate both EFLAGS 5027c478bd9Sstevel@tonic-gate * and %esp. 5037c478bd9Sstevel@tonic-gate */ 5047c478bd9Sstevel@tonic-gate case M_CLI: 5057c478bd9Sstevel@tonic-gate (void) kmdb_dpi_get_register(FLAGS_REG_NAME, &fl); 5067c478bd9Sstevel@tonic-gate fl &= ~KREG_EFLAGS_IF_MASK; 5077c478bd9Sstevel@tonic-gate (void) kmdb_dpi_set_register(FLAGS_REG_NAME, fl); 5087c478bd9Sstevel@tonic-gate 5097c478bd9Sstevel@tonic-gate emulated = 1; 5107c478bd9Sstevel@tonic-gate break; 5117c478bd9Sstevel@tonic-gate 5127c478bd9Sstevel@tonic-gate case M_STI: 5137c478bd9Sstevel@tonic-gate (void) kmdb_dpi_get_register(FLAGS_REG_NAME, &fl); 5147c478bd9Sstevel@tonic-gate fl |= (1 << KREG_EFLAGS_IF_SHIFT); 5157c478bd9Sstevel@tonic-gate (void) kmdb_dpi_set_register(FLAGS_REG_NAME, fl); 5167c478bd9Sstevel@tonic-gate 5177c478bd9Sstevel@tonic-gate emulated = 1; 5187c478bd9Sstevel@tonic-gate break; 5197c478bd9Sstevel@tonic-gate 5207c478bd9Sstevel@tonic-gate case M_POPF: 5217c478bd9Sstevel@tonic-gate /* 5227c478bd9Sstevel@tonic-gate * popfl will restore a pushed EFLAGS from the stack, and could 523*ae115bc7Smrj * in so doing cause IF to be turned on, if only for a brief 5247c478bd9Sstevel@tonic-gate * period. To avoid this, we'll secretly replace the stack's 5257c478bd9Sstevel@tonic-gate * EFLAGS with our decaffeinated brand. We'll then manually 5267c478bd9Sstevel@tonic-gate * load our EFLAGS copy with the real verion after the step. 5277c478bd9Sstevel@tonic-gate */ 5287c478bd9Sstevel@tonic-gate (void) kmdb_dpi_get_register("sp", &sp); 5297c478bd9Sstevel@tonic-gate (void) kmdb_dpi_get_register(FLAGS_REG_NAME, &fl); 5307c478bd9Sstevel@tonic-gate 5317c478bd9Sstevel@tonic-gate if (mdb_tgt_vread(mdb.m_target, &newfl, sizeof (kreg_t), 5327c478bd9Sstevel@tonic-gate sp) != sizeof (kreg_t)) { 5337c478bd9Sstevel@tonic-gate warn("failed to read " FLAGS_REG_NAME 5347c478bd9Sstevel@tonic-gate " at %p for popfl step\n", (void *)sp); 5357c478bd9Sstevel@tonic-gate return (set_errno(EMDB_TGTNOTSUP)); /* XXX ? */ 5367c478bd9Sstevel@tonic-gate } 5377c478bd9Sstevel@tonic-gate 5387c478bd9Sstevel@tonic-gate fl = (fl & ~KREG_EFLAGS_IF_MASK) | KREG_EFLAGS_TF_MASK; 5397c478bd9Sstevel@tonic-gate 5407c478bd9Sstevel@tonic-gate if (mdb_tgt_vwrite(mdb.m_target, &fl, sizeof (kreg_t), 5417c478bd9Sstevel@tonic-gate sp) != sizeof (kreg_t)) { 5427c478bd9Sstevel@tonic-gate warn("failed to update " FLAGS_REG_NAME 5437c478bd9Sstevel@tonic-gate " at %p for popfl step\n", (void *)sp); 5447c478bd9Sstevel@tonic-gate return (set_errno(EMDB_TGTNOTSUP)); /* XXX ? */ 5457c478bd9Sstevel@tonic-gate } 5467c478bd9Sstevel@tonic-gate break; 5477c478bd9Sstevel@tonic-gate } 5487c478bd9Sstevel@tonic-gate 5497c478bd9Sstevel@tonic-gate if (emulated) { 5507c478bd9Sstevel@tonic-gate (void) kmdb_dpi_set_register("pc", npc); 5517c478bd9Sstevel@tonic-gate return (0); 5527c478bd9Sstevel@tonic-gate } 5537c478bd9Sstevel@tonic-gate 5547c478bd9Sstevel@tonic-gate /* Do the step with IF off, and TF (step) on */ 5557c478bd9Sstevel@tonic-gate (void) kmdb_dpi_get_register(FLAGS_REG_NAME, &oldfl); 5567c478bd9Sstevel@tonic-gate (void) kmdb_dpi_set_register(FLAGS_REG_NAME, 5577c478bd9Sstevel@tonic-gate ((oldfl | (1 << KREG_EFLAGS_TF_SHIFT)) & ~KREG_EFLAGS_IF_MASK)); 5587c478bd9Sstevel@tonic-gate 5597c478bd9Sstevel@tonic-gate kmdb_dpi_resume_master(); /* ... there and back again ... */ 5607c478bd9Sstevel@tonic-gate 5617c478bd9Sstevel@tonic-gate /* EFLAGS has now changed, and may require tuning */ 5627c478bd9Sstevel@tonic-gate 5637c478bd9Sstevel@tonic-gate switch (instr) { 5647c478bd9Sstevel@tonic-gate case M_POPF: 5657c478bd9Sstevel@tonic-gate /* 5667c478bd9Sstevel@tonic-gate * Use the EFLAGS we grabbed before the pop - see the pre-step 5677c478bd9Sstevel@tonic-gate * M_POPFL comment. 5687c478bd9Sstevel@tonic-gate */ 5697c478bd9Sstevel@tonic-gate (void) kmdb_dpi_set_register(FLAGS_REG_NAME, newfl); 5707c478bd9Sstevel@tonic-gate return (0); 5717c478bd9Sstevel@tonic-gate 5727c478bd9Sstevel@tonic-gate case M_PUSHF: 5737c478bd9Sstevel@tonic-gate /* 5747c478bd9Sstevel@tonic-gate * We pushed our modified EFLAGS (with IF and TF turned off) 5757c478bd9Sstevel@tonic-gate * onto the stack. Replace the pushed version with our 5767c478bd9Sstevel@tonic-gate * unmodified one. 5777c478bd9Sstevel@tonic-gate */ 5787c478bd9Sstevel@tonic-gate (void) kmdb_dpi_get_register("sp", &sp); 5797c478bd9Sstevel@tonic-gate 5807c478bd9Sstevel@tonic-gate if (mdb_tgt_vwrite(mdb.m_target, &oldfl, sizeof (kreg_t), 5817c478bd9Sstevel@tonic-gate sp) != sizeof (kreg_t)) { 5827c478bd9Sstevel@tonic-gate warn("failed to update pushed " FLAGS_REG_NAME 5837c478bd9Sstevel@tonic-gate " at %p after pushfl step\n", (void *)sp); 5847c478bd9Sstevel@tonic-gate return (set_errno(EMDB_TGTNOTSUP)); /* XXX ? */ 5857c478bd9Sstevel@tonic-gate } 5867c478bd9Sstevel@tonic-gate 5877c478bd9Sstevel@tonic-gate /* Go back to using the EFLAGS we were using before the step */ 5887c478bd9Sstevel@tonic-gate (void) kmdb_dpi_set_register(FLAGS_REG_NAME, oldfl); 5897c478bd9Sstevel@tonic-gate return (0); 5907c478bd9Sstevel@tonic-gate 5917c478bd9Sstevel@tonic-gate default: 5927c478bd9Sstevel@tonic-gate /* 5937c478bd9Sstevel@tonic-gate * The stepped instruction may have altered EFLAGS. We only 5947c478bd9Sstevel@tonic-gate * really care about the value of IF, and we know the stepped 5957c478bd9Sstevel@tonic-gate * instruction didn't alter it, so we can simply copy the 5967c478bd9Sstevel@tonic-gate * pre-step value. We'll also need to turn TF back off. 5977c478bd9Sstevel@tonic-gate */ 5987c478bd9Sstevel@tonic-gate (void) kmdb_dpi_get_register(FLAGS_REG_NAME, &fl); 5997c478bd9Sstevel@tonic-gate (void) kmdb_dpi_set_register(FLAGS_REG_NAME, 6007c478bd9Sstevel@tonic-gate ((fl & ~(KREG_EFLAGS_TF_MASK|KREG_EFLAGS_IF_MASK)) | 6017c478bd9Sstevel@tonic-gate (oldfl & KREG_EFLAGS_IF_MASK))); 6027c478bd9Sstevel@tonic-gate return (0); 6037c478bd9Sstevel@tonic-gate } 6047c478bd9Sstevel@tonic-gate } 6057c478bd9Sstevel@tonic-gate 6067c478bd9Sstevel@tonic-gate /* 6077c478bd9Sstevel@tonic-gate * The target has already configured the chip for branch step, leaving us to 6087c478bd9Sstevel@tonic-gate * actually make the machine go. Due to a number of issues involving 6097c478bd9Sstevel@tonic-gate * the potential alteration of system state via instructions like sti, cli, 6107c478bd9Sstevel@tonic-gate * pushfl, and popfl, we're going to treat this like a normal system resume. 6117c478bd9Sstevel@tonic-gate * All CPUs will be released, on the kernel's IDT. Our primary concern is 6127c478bd9Sstevel@tonic-gate * the alteration/storage of our TF'd EFLAGS via pushfl and popfl. There's no 6137c478bd9Sstevel@tonic-gate * real workaround - we don't have opcode breakpoints - so the best we can do is 6147c478bd9Sstevel@tonic-gate * to ensure that the world won't end if someone does bad things to EFLAGS. 6157c478bd9Sstevel@tonic-gate * 6167c478bd9Sstevel@tonic-gate * Two things can happen: 6177c478bd9Sstevel@tonic-gate * 1. EFLAGS.TF may be cleared, either maliciously or via a popfl from saved 6187c478bd9Sstevel@tonic-gate * state. The CPU will continue execution beyond the branch, and will not 6197c478bd9Sstevel@tonic-gate * reenter the debugger unless brought/sent in by other means. 6207c478bd9Sstevel@tonic-gate * 2. Someone may pushlf the TF'd EFLAGS, and may stash a copy of it somewhere. 6217c478bd9Sstevel@tonic-gate * When the saved version is popfl'd back into place, the debugger will be 6227c478bd9Sstevel@tonic-gate * re-entered on a single-step trap. 6237c478bd9Sstevel@tonic-gate */ 6247c478bd9Sstevel@tonic-gate static void 6257c478bd9Sstevel@tonic-gate kaif_step_branch(void) 6267c478bd9Sstevel@tonic-gate { 6277c478bd9Sstevel@tonic-gate kreg_t fl; 6287c478bd9Sstevel@tonic-gate 6297c478bd9Sstevel@tonic-gate (void) kmdb_dpi_get_register(FLAGS_REG_NAME, &fl); 6307c478bd9Sstevel@tonic-gate (void) kmdb_dpi_set_register(FLAGS_REG_NAME, 6317c478bd9Sstevel@tonic-gate (fl | (1 << KREG_EFLAGS_TF_SHIFT))); 6327c478bd9Sstevel@tonic-gate 6337c478bd9Sstevel@tonic-gate kmdb_dpi_resume_master(); 6347c478bd9Sstevel@tonic-gate 6357c478bd9Sstevel@tonic-gate (void) kmdb_dpi_set_register(FLAGS_REG_NAME, fl); 6367c478bd9Sstevel@tonic-gate } 6377c478bd9Sstevel@tonic-gate 6387c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 6397c478bd9Sstevel@tonic-gate static uintptr_t 6407c478bd9Sstevel@tonic-gate kaif_call(uintptr_t funcva, uint_t argc, const uintptr_t argv[]) 6417c478bd9Sstevel@tonic-gate { 6427c478bd9Sstevel@tonic-gate return (kaif_invoke(funcva, argc, argv)); 6437c478bd9Sstevel@tonic-gate } 6447c478bd9Sstevel@tonic-gate 6457c478bd9Sstevel@tonic-gate static void 646*ae115bc7Smrj dump_crumb(kdi_crumb_t *krmp) 6477c478bd9Sstevel@tonic-gate { 648*ae115bc7Smrj kdi_crumb_t krm; 649acbc304dSjohnlev 650*ae115bc7Smrj if (mdb_vread(&krm, sizeof (kdi_crumb_t), (uintptr_t)krmp) != 651*ae115bc7Smrj sizeof (kdi_crumb_t)) { 652acbc304dSjohnlev warn("failed to read crumb at %p", krmp); 653acbc304dSjohnlev return; 654acbc304dSjohnlev } 655acbc304dSjohnlev 6567c478bd9Sstevel@tonic-gate mdb_printf("state: "); 657acbc304dSjohnlev switch (krm.krm_cpu_state) { 6587c478bd9Sstevel@tonic-gate case KAIF_CPU_STATE_MASTER: 6597c478bd9Sstevel@tonic-gate mdb_printf("M"); 6607c478bd9Sstevel@tonic-gate break; 6617c478bd9Sstevel@tonic-gate case KAIF_CPU_STATE_SLAVE: 6627c478bd9Sstevel@tonic-gate mdb_printf("S"); 6637c478bd9Sstevel@tonic-gate break; 6647c478bd9Sstevel@tonic-gate default: 665acbc304dSjohnlev mdb_printf("%d", krm.krm_cpu_state); 6667c478bd9Sstevel@tonic-gate } 6677c478bd9Sstevel@tonic-gate 6687c478bd9Sstevel@tonic-gate mdb_printf(" trapno %3d sp %08x flag %d pc %p %A\n", 669acbc304dSjohnlev krm.krm_trapno, krm.krm_sp, krm.krm_flag, krm.krm_pc, krm.krm_pc); 6707c478bd9Sstevel@tonic-gate } 6717c478bd9Sstevel@tonic-gate 6727c478bd9Sstevel@tonic-gate static void 6737c478bd9Sstevel@tonic-gate dump_crumbs(kaif_cpusave_t *save) 6747c478bd9Sstevel@tonic-gate { 6757c478bd9Sstevel@tonic-gate int i; 6767c478bd9Sstevel@tonic-gate 677*ae115bc7Smrj for (i = KDI_NCRUMBS; i > 0; i--) { 678*ae115bc7Smrj uint_t idx = (save->krs_curcrumbidx + i) % KDI_NCRUMBS; 6797c478bd9Sstevel@tonic-gate dump_crumb(&save->krs_crumbs[idx]); 6807c478bd9Sstevel@tonic-gate } 6817c478bd9Sstevel@tonic-gate } 6827c478bd9Sstevel@tonic-gate 6837c478bd9Sstevel@tonic-gate static void 6847c478bd9Sstevel@tonic-gate kaif_dump_crumbs(uintptr_t addr, int cpuid) 6857c478bd9Sstevel@tonic-gate { 6867c478bd9Sstevel@tonic-gate int i; 6877c478bd9Sstevel@tonic-gate 6887c478bd9Sstevel@tonic-gate if (addr != NULL) { 689acbc304dSjohnlev /* dump_crumb will protect us against bogus addresses */ 690*ae115bc7Smrj dump_crumb((kdi_crumb_t *)addr); 6917c478bd9Sstevel@tonic-gate 6927c478bd9Sstevel@tonic-gate } else if (cpuid != -1) { 693acbc304dSjohnlev if (cpuid < 0 || cpuid >= kaif_ncpusave) 6947c478bd9Sstevel@tonic-gate return; 6957c478bd9Sstevel@tonic-gate 6967c478bd9Sstevel@tonic-gate dump_crumbs(&kaif_cpusave[cpuid]); 6977c478bd9Sstevel@tonic-gate 6987c478bd9Sstevel@tonic-gate } else { 6997c478bd9Sstevel@tonic-gate for (i = 0; i < kaif_ncpusave; i++) { 7007c478bd9Sstevel@tonic-gate kaif_cpusave_t *save = &kaif_cpusave[i]; 7017c478bd9Sstevel@tonic-gate 7027c478bd9Sstevel@tonic-gate if (save->krs_cpu_state == KAIF_CPU_STATE_NONE) 7037c478bd9Sstevel@tonic-gate continue; 7047c478bd9Sstevel@tonic-gate 7057c478bd9Sstevel@tonic-gate mdb_printf("%sCPU %d crumbs: (curidx %d)\n", 7067c478bd9Sstevel@tonic-gate (i == 0 ? "" : "\n"), i, save->krs_curcrumbidx); 7077c478bd9Sstevel@tonic-gate 7087c478bd9Sstevel@tonic-gate dump_crumbs(save); 7097c478bd9Sstevel@tonic-gate } 7107c478bd9Sstevel@tonic-gate } 7117c478bd9Sstevel@tonic-gate } 7127c478bd9Sstevel@tonic-gate 7137c478bd9Sstevel@tonic-gate static void 7147c478bd9Sstevel@tonic-gate kaif_modchg_register(void (*func)(struct modctl *, int)) 7157c478bd9Sstevel@tonic-gate { 7167c478bd9Sstevel@tonic-gate kaif_modchg_cb = func; 7177c478bd9Sstevel@tonic-gate } 7187c478bd9Sstevel@tonic-gate 7197c478bd9Sstevel@tonic-gate static void 7207c478bd9Sstevel@tonic-gate kaif_modchg_cancel(void) 7217c478bd9Sstevel@tonic-gate { 7227c478bd9Sstevel@tonic-gate ASSERT(kaif_modchg_cb != NULL); 7237c478bd9Sstevel@tonic-gate 7247c478bd9Sstevel@tonic-gate kaif_modchg_cb = NULL; 7257c478bd9Sstevel@tonic-gate } 7267c478bd9Sstevel@tonic-gate 727*ae115bc7Smrj static void 728*ae115bc7Smrj kaif_msr_add(const kdi_msr_t *msrs) 729*ae115bc7Smrj { 730*ae115bc7Smrj kdi_msr_t *save; 731*ae115bc7Smrj size_t nr_msrs = 0; 732*ae115bc7Smrj size_t i; 733*ae115bc7Smrj 734*ae115bc7Smrj while (msrs[nr_msrs].msr_num != 0) 735*ae115bc7Smrj nr_msrs++; 736*ae115bc7Smrj /* we want to copy the terminating kdi_msr_t too */ 737*ae115bc7Smrj nr_msrs++; 738*ae115bc7Smrj 739*ae115bc7Smrj save = mdb_zalloc(sizeof (kdi_msr_t) * nr_msrs * kaif_ncpusave, 740*ae115bc7Smrj UM_SLEEP); 741*ae115bc7Smrj 742*ae115bc7Smrj for (i = 0; i < kaif_ncpusave; i++) 743*ae115bc7Smrj bcopy(msrs, &save[nr_msrs * i], sizeof (kdi_msr_t) * nr_msrs); 744*ae115bc7Smrj 745*ae115bc7Smrj kmdb_kdi_set_debug_msrs(save); 746*ae115bc7Smrj } 747*ae115bc7Smrj 748*ae115bc7Smrj static uint64_t 749*ae115bc7Smrj kaif_msr_get(int cpuid, uint_t num) 750*ae115bc7Smrj { 751*ae115bc7Smrj kdi_cpusave_t *save; 752*ae115bc7Smrj kdi_msr_t *msr; 753*ae115bc7Smrj int i; 754*ae115bc7Smrj 755*ae115bc7Smrj if ((save = kaif_cpuid2save(cpuid)) == NULL) 756*ae115bc7Smrj return (-1); /* errno is set for us */ 757*ae115bc7Smrj 758*ae115bc7Smrj msr = save->krs_msr; 759*ae115bc7Smrj 760*ae115bc7Smrj for (i = 0; msr[i].msr_num != 0; i++) { 761*ae115bc7Smrj if (msr[i].msr_num == num && (msr[i].msr_type & KDI_MSR_READ)) 762*ae115bc7Smrj return (msr[i].kdi_msr_val); 763*ae115bc7Smrj } 764*ae115bc7Smrj 765*ae115bc7Smrj return (0); 766*ae115bc7Smrj } 767*ae115bc7Smrj 768*ae115bc7Smrj void 769*ae115bc7Smrj kaif_trap_set_debugger(void) 770*ae115bc7Smrj { 771*ae115bc7Smrj kmdb_kdi_idt_switch(NULL); 772*ae115bc7Smrj } 773*ae115bc7Smrj 774*ae115bc7Smrj void 775*ae115bc7Smrj kaif_trap_set_saved(kaif_cpusave_t *cpusave) 776*ae115bc7Smrj { 777*ae115bc7Smrj kmdb_kdi_idt_switch(cpusave); 778*ae115bc7Smrj } 779*ae115bc7Smrj 780*ae115bc7Smrj static void 781*ae115bc7Smrj kaif_vmready(void) 782*ae115bc7Smrj { 783*ae115bc7Smrj } 784*ae115bc7Smrj 785*ae115bc7Smrj void 786*ae115bc7Smrj kaif_memavail(caddr_t base, size_t len) 787*ae115bc7Smrj { 788*ae115bc7Smrj int ret; 789*ae115bc7Smrj /* 790*ae115bc7Smrj * In the unlikely event that someone is stepping through this routine, 791*ae115bc7Smrj * we need to make sure that the KDI knows about the new range before 792*ae115bc7Smrj * umem gets it. That way the entry code can recognize stacks 793*ae115bc7Smrj * allocated from the new region. 794*ae115bc7Smrj */ 795*ae115bc7Smrj kmdb_kdi_memrange_add(base, len); 796*ae115bc7Smrj ret = mdb_umem_add(base, len); 797*ae115bc7Smrj ASSERT(ret == 0); 798*ae115bc7Smrj } 799*ae115bc7Smrj 8007c478bd9Sstevel@tonic-gate void 8017c478bd9Sstevel@tonic-gate kaif_mod_loaded(struct modctl *modp) 8027c478bd9Sstevel@tonic-gate { 8037c478bd9Sstevel@tonic-gate if (kaif_modchg_cb != NULL) 8047c478bd9Sstevel@tonic-gate kaif_modchg_cb(modp, 1); 8057c478bd9Sstevel@tonic-gate } 8067c478bd9Sstevel@tonic-gate 8077c478bd9Sstevel@tonic-gate void 8087c478bd9Sstevel@tonic-gate kaif_mod_unloading(struct modctl *modp) 8097c478bd9Sstevel@tonic-gate { 8107c478bd9Sstevel@tonic-gate if (kaif_modchg_cb != NULL) 8117c478bd9Sstevel@tonic-gate kaif_modchg_cb(modp, 0); 8127c478bd9Sstevel@tonic-gate } 8137c478bd9Sstevel@tonic-gate 814*ae115bc7Smrj void 815*ae115bc7Smrj kaif_handle_fault(greg_t trapno, greg_t pc, greg_t sp, int cpuid) 8167c478bd9Sstevel@tonic-gate { 817*ae115bc7Smrj kmdb_dpi_handle_fault((kreg_t)trapno, (kreg_t)pc, 818*ae115bc7Smrj (kreg_t)sp, cpuid); 819*ae115bc7Smrj } 820*ae115bc7Smrj 821*ae115bc7Smrj static kdi_debugvec_t kaif_dvec = { 822*ae115bc7Smrj NULL, /* dv_kctl_vmready */ 823*ae115bc7Smrj NULL, /* dv_kctl_memavail */ 824*ae115bc7Smrj NULL, /* dv_kctl_modavail */ 825*ae115bc7Smrj NULL, /* dv_kctl_thravail */ 826*ae115bc7Smrj kaif_vmready, 827*ae115bc7Smrj kaif_memavail, 828*ae115bc7Smrj kaif_mod_loaded, 829*ae115bc7Smrj kaif_mod_unloading, 830*ae115bc7Smrj kaif_handle_fault 8317c478bd9Sstevel@tonic-gate }; 8327c478bd9Sstevel@tonic-gate 8337c478bd9Sstevel@tonic-gate void 834*ae115bc7Smrj kaif_kdi_entry(kdi_cpusave_t *cpusave) 8357c478bd9Sstevel@tonic-gate { 836*ae115bc7Smrj int ret = kaif_main_loop(cpusave); 837*ae115bc7Smrj ASSERT(ret == KAIF_CPU_CMD_RESUME || 838*ae115bc7Smrj ret == KAIF_CPU_CMD_RESUME_MASTER); 8397c478bd9Sstevel@tonic-gate } 8407c478bd9Sstevel@tonic-gate 841*ae115bc7Smrj /*ARGSUSED*/ 8427c478bd9Sstevel@tonic-gate void 843*ae115bc7Smrj kaif_activate(kdi_debugvec_t **dvecp, uint_t flags) 8447c478bd9Sstevel@tonic-gate { 845*ae115bc7Smrj kmdb_kdi_activate(kaif_kdi_entry, kaif_cpusave, kaif_ncpusave); 846*ae115bc7Smrj *dvecp = &kaif_dvec; 8477c478bd9Sstevel@tonic-gate } 8487c478bd9Sstevel@tonic-gate 8497c478bd9Sstevel@tonic-gate static int 8507c478bd9Sstevel@tonic-gate kaif_init(kmdb_auxv_t *kav) 8517c478bd9Sstevel@tonic-gate { 8527c478bd9Sstevel@tonic-gate /* Allocate the per-CPU save areas */ 8537c478bd9Sstevel@tonic-gate kaif_cpusave = mdb_zalloc(sizeof (kaif_cpusave_t) * kav->kav_ncpu, 8547c478bd9Sstevel@tonic-gate UM_SLEEP); 8557c478bd9Sstevel@tonic-gate kaif_ncpusave = kav->kav_ncpu; 8567c478bd9Sstevel@tonic-gate 8577c478bd9Sstevel@tonic-gate kaif_modchg_cb = NULL; 8587c478bd9Sstevel@tonic-gate 8597c478bd9Sstevel@tonic-gate kaif_waptmap = 0; 8607c478bd9Sstevel@tonic-gate 8617c478bd9Sstevel@tonic-gate kaif_trap_switch = (kav->kav_flags & KMDB_AUXV_FL_NOTRPSWTCH) == 0; 8627c478bd9Sstevel@tonic-gate 8637c478bd9Sstevel@tonic-gate return (0); 8647c478bd9Sstevel@tonic-gate } 8657c478bd9Sstevel@tonic-gate 8667c478bd9Sstevel@tonic-gate dpi_ops_t kmdb_dpi_ops = { 8677c478bd9Sstevel@tonic-gate kaif_init, 8687c478bd9Sstevel@tonic-gate kaif_activate, 869*ae115bc7Smrj kmdb_kdi_deactivate, 8707c478bd9Sstevel@tonic-gate kaif_enter_mon, 8717c478bd9Sstevel@tonic-gate kaif_modchg_register, 8727c478bd9Sstevel@tonic-gate kaif_modchg_cancel, 8737c478bd9Sstevel@tonic-gate kaif_get_cpu_state, 8747c478bd9Sstevel@tonic-gate kaif_get_master_cpuid, 8757c478bd9Sstevel@tonic-gate kaif_get_gregs, 876acbc304dSjohnlev kaif_get_register, 877acbc304dSjohnlev kaif_set_register, 8787c478bd9Sstevel@tonic-gate kaif_brkpt_arm, 8797c478bd9Sstevel@tonic-gate kaif_brkpt_disarm, 8807c478bd9Sstevel@tonic-gate kaif_wapt_validate, 8817c478bd9Sstevel@tonic-gate kaif_wapt_reserve, 8827c478bd9Sstevel@tonic-gate kaif_wapt_release, 8837c478bd9Sstevel@tonic-gate kaif_wapt_arm, 8847c478bd9Sstevel@tonic-gate kaif_wapt_disarm, 8857c478bd9Sstevel@tonic-gate kaif_wapt_match, 8867c478bd9Sstevel@tonic-gate kaif_step, 8877c478bd9Sstevel@tonic-gate kaif_step_branch, 8887c478bd9Sstevel@tonic-gate kaif_call, 8897c478bd9Sstevel@tonic-gate kaif_dump_crumbs, 8907c478bd9Sstevel@tonic-gate kaif_msr_add, 8917c478bd9Sstevel@tonic-gate kaif_msr_get, 8927c478bd9Sstevel@tonic-gate }; 893