xref: /illumos-gate/usr/src/cmd/mdb/intel/kmdb/kaif.c (revision 0c1b95be)
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 /*
22ae115bc7Smrj  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
24399ca3a7SJohn Levon  *
25399ca3a7SJohn Levon  * Copyright 2018 Joyent, Inc.
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate  * The debugger/"PROM" interface layer
307c478bd9Sstevel@tonic-gate  *
31ae115bc7Smrj  * It makes more sense on SPARC. In reality, these interfaces deal with three
32ae115bc7Smrj  * things: setting break/watchpoints, stepping, and interfacing with the KDI to
33ae115bc7Smrj  * 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>
41ae115bc7Smrj #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>
46ae115bc7Smrj #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>
52ae115bc7Smrj #include <sys/kdi_impl.h>
5374ecdb51SJohn Levon #include <sys/sysmacros.h>
547c478bd9Sstevel@tonic-gate 
55ae115bc7Smrj /*
56ae115bc7Smrj  * This is the area containing the saved state when we enter
57ae115bc7Smrj  * via kmdb's IDT entries.
58ae115bc7Smrj  */
59ae115bc7Smrj kdi_cpusave_t	*kaif_cpusave;
607c478bd9Sstevel@tonic-gate int		kaif_ncpusave;
61ae115bc7Smrj kdi_drreg_t	kaif_drreg;
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate uint32_t	kaif_waptmap;
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate int		kaif_trap_switch;
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate void (*kaif_modchg_cb)(struct modctl *, int);
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate enum {
707c478bd9Sstevel@tonic-gate 	M_SYSRET	= 0x07, /* after M_ESC */
717c478bd9Sstevel@tonic-gate 	M_ESC		= 0x0f,
727c478bd9Sstevel@tonic-gate 	M_SYSEXIT	= 0x35, /* after M_ESC */
737c478bd9Sstevel@tonic-gate 	M_REX_LO	= 0x40, /* first REX prefix */
747c478bd9Sstevel@tonic-gate 	M_REX_HI	= 0x4f, /* last REX prefix */
757c478bd9Sstevel@tonic-gate 	M_PUSHF		= 0x9c,	/* pushfl and pushfq */
767c478bd9Sstevel@tonic-gate 	M_POPF		= 0x9d,	/* popfl and popfq */
777c478bd9Sstevel@tonic-gate 	M_INT3		= 0xcc,
787c478bd9Sstevel@tonic-gate 	M_INTX		= 0xcd,
797c478bd9Sstevel@tonic-gate 	M_INTO		= 0xce,
807c478bd9Sstevel@tonic-gate 	M_IRET		= 0xcf,
817c478bd9Sstevel@tonic-gate 	M_CLI		= 0xfa,
827c478bd9Sstevel@tonic-gate 	M_STI		= 0xfb
837c478bd9Sstevel@tonic-gate };
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate #define	KAIF_BREAKPOINT_INSTR	M_INT3
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate #define	KAIF_WPPRIV2ID(wp)	(int)(uintptr_t)((wp)->wp_priv)
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate #ifdef __amd64
907c478bd9Sstevel@tonic-gate #define	FLAGS_REG_NAME		"rflags"
917c478bd9Sstevel@tonic-gate #else
927c478bd9Sstevel@tonic-gate #define	FLAGS_REG_NAME		"eflags"
937c478bd9Sstevel@tonic-gate #endif
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate /*
967c478bd9Sstevel@tonic-gate  * Called during normal debugger operation and during debugger faults.
977c478bd9Sstevel@tonic-gate  */
987c478bd9Sstevel@tonic-gate static void
kaif_enter_mon(void)997c478bd9Sstevel@tonic-gate kaif_enter_mon(void)
1007c478bd9Sstevel@tonic-gate {
1017c478bd9Sstevel@tonic-gate 	char c;
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate 	for (;;) {
1047c478bd9Sstevel@tonic-gate 		mdb_iob_printf(mdb.m_out,
1057c478bd9Sstevel@tonic-gate 		    "%s: Do you really want to reboot? (y/n) ",
1067c478bd9Sstevel@tonic-gate 		    mdb.m_pname);
1077c478bd9Sstevel@tonic-gate 		mdb_iob_flush(mdb.m_out);
108c7bf3205Sjohnlev 		mdb_iob_clearlines(mdb.m_out);
1097c478bd9Sstevel@tonic-gate 
110c7bf3205Sjohnlev 		c = kmdb_getchar();
1117c478bd9Sstevel@tonic-gate 
112c7bf3205Sjohnlev 		if (c == 'n' || c == 'N' || c == CTRL('c'))
1137c478bd9Sstevel@tonic-gate 			return;
1147c478bd9Sstevel@tonic-gate 		else if (c == 'y' || c == 'Y') {
1157c478bd9Sstevel@tonic-gate 			mdb_iob_printf(mdb.m_out, "Rebooting...\n");
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate 			kmdb_dpi_reboot();
1187c478bd9Sstevel@tonic-gate 		}
1197c478bd9Sstevel@tonic-gate 	}
1207c478bd9Sstevel@tonic-gate }
1217c478bd9Sstevel@tonic-gate 
122acbc304dSjohnlev static kaif_cpusave_t *
kaif_cpuid2save(int cpuid)123acbc304dSjohnlev kaif_cpuid2save(int cpuid)
124acbc304dSjohnlev {
125acbc304dSjohnlev 	kaif_cpusave_t *save;
126acbc304dSjohnlev 
127acbc304dSjohnlev 	if (cpuid == DPI_MASTER_CPUID)
128acbc304dSjohnlev 		return (&kaif_cpusave[kaif_master_cpuid]);
129acbc304dSjohnlev 
130acbc304dSjohnlev 	if (cpuid < 0 || cpuid >= kaif_ncpusave) {
131acbc304dSjohnlev 		(void) set_errno(EINVAL);
132acbc304dSjohnlev 		return (NULL);
133acbc304dSjohnlev 	}
134acbc304dSjohnlev 
135acbc304dSjohnlev 	save = &kaif_cpusave[cpuid];
136acbc304dSjohnlev 
137acbc304dSjohnlev 	if (save->krs_cpu_state != KAIF_CPU_STATE_MASTER &&
138acbc304dSjohnlev 	    save->krs_cpu_state != KAIF_CPU_STATE_SLAVE) {
139acbc304dSjohnlev 		(void) set_errno(EINVAL);
140acbc304dSjohnlev 		return (NULL);
141acbc304dSjohnlev 	}
142acbc304dSjohnlev 
143acbc304dSjohnlev 	return (save);
144acbc304dSjohnlev }
145acbc304dSjohnlev 
1467c478bd9Sstevel@tonic-gate static int
kaif_get_cpu_state(int cpuid)1477c478bd9Sstevel@tonic-gate kaif_get_cpu_state(int cpuid)
1487c478bd9Sstevel@tonic-gate {
149acbc304dSjohnlev 	kaif_cpusave_t *save;
1507c478bd9Sstevel@tonic-gate 
151acbc304dSjohnlev 	if ((save = kaif_cpuid2save(cpuid)) == NULL)
152acbc304dSjohnlev 		return (-1); /* errno is set for us */
1537c478bd9Sstevel@tonic-gate 
154acbc304dSjohnlev 	switch (save->krs_cpu_state) {
1557c478bd9Sstevel@tonic-gate 	case KAIF_CPU_STATE_MASTER:
1567c478bd9Sstevel@tonic-gate 		return (DPI_CPU_STATE_MASTER);
1577c478bd9Sstevel@tonic-gate 	case KAIF_CPU_STATE_SLAVE:
1587c478bd9Sstevel@tonic-gate 		return (DPI_CPU_STATE_SLAVE);
1597c478bd9Sstevel@tonic-gate 	default:
1607c478bd9Sstevel@tonic-gate 		return (set_errno(EINVAL));
1617c478bd9Sstevel@tonic-gate 	}
1627c478bd9Sstevel@tonic-gate }
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate static int
kaif_get_master_cpuid(void)1657c478bd9Sstevel@tonic-gate kaif_get_master_cpuid(void)
1667c478bd9Sstevel@tonic-gate {
1677c478bd9Sstevel@tonic-gate 	return (kaif_master_cpuid);
1687c478bd9Sstevel@tonic-gate }
1697c478bd9Sstevel@tonic-gate 
170ae115bc7Smrj static mdb_tgt_gregset_t *
kaif_kdi_to_gregs(int cpuid)171ae115bc7Smrj kaif_kdi_to_gregs(int cpuid)
1727c478bd9Sstevel@tonic-gate {
173acbc304dSjohnlev 	kaif_cpusave_t *save;
1747c478bd9Sstevel@tonic-gate 
175acbc304dSjohnlev 	if ((save = kaif_cpuid2save(cpuid)) == NULL)
176acbc304dSjohnlev 		return (NULL); /* errno is set for us */
1777c478bd9Sstevel@tonic-gate 
178ae115bc7Smrj 	/*
179ae115bc7Smrj 	 * The saved registers are actually identical to an mdb_tgt_gregset,
180ae115bc7Smrj 	 * so we can directly cast here.
181ae115bc7Smrj 	 */
182ae115bc7Smrj 	return ((mdb_tgt_gregset_t *)save->krs_gregs);
183ae115bc7Smrj }
184ae115bc7Smrj 
185ae115bc7Smrj static const mdb_tgt_gregset_t *
kaif_get_gregs(int cpuid)186ae115bc7Smrj kaif_get_gregs(int cpuid)
187ae115bc7Smrj {
188ae115bc7Smrj 	return (kaif_kdi_to_gregs(cpuid));
1897c478bd9Sstevel@tonic-gate }
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate typedef struct kaif_reg_synonyms {
1927c478bd9Sstevel@tonic-gate 	const char *rs_syn;
1937c478bd9Sstevel@tonic-gate 	const char *rs_name;
1947c478bd9Sstevel@tonic-gate } kaif_reg_synonyms_t;
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate static kreg_t *
kaif_find_regp(const char * regname)197acbc304dSjohnlev kaif_find_regp(const char *regname)
1987c478bd9Sstevel@tonic-gate {
1997c478bd9Sstevel@tonic-gate 	static const kaif_reg_synonyms_t synonyms[] = {
2007c478bd9Sstevel@tonic-gate #ifdef __amd64
2017c478bd9Sstevel@tonic-gate 	    { "pc", "rip" },
2027c478bd9Sstevel@tonic-gate 	    { "sp", "rsp" },
2037c478bd9Sstevel@tonic-gate 	    { "fp", "rbp" },
2047c478bd9Sstevel@tonic-gate #else
2057c478bd9Sstevel@tonic-gate 	    { "pc", "eip" },
2067c478bd9Sstevel@tonic-gate 	    { "sp", "esp" },
2077c478bd9Sstevel@tonic-gate 	    { "fp", "ebp" },
2087c478bd9Sstevel@tonic-gate #endif
2097c478bd9Sstevel@tonic-gate 	    { "tt", "trapno" }
2107c478bd9Sstevel@tonic-gate 	};
211ae115bc7Smrj 	mdb_tgt_gregset_t *regs;
2127c478bd9Sstevel@tonic-gate 	int i;
2137c478bd9Sstevel@tonic-gate 
214ae115bc7Smrj 	if ((regs = kaif_kdi_to_gregs(DPI_MASTER_CPUID)) == NULL)
215ae115bc7Smrj 		return (NULL);
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate 	for (i = 0; i < sizeof (synonyms) / sizeof (synonyms[0]); i++) {
2187c478bd9Sstevel@tonic-gate 		if (strcmp(synonyms[i].rs_syn, regname) == 0)
2197c478bd9Sstevel@tonic-gate 			regname = synonyms[i].rs_name;
2207c478bd9Sstevel@tonic-gate 	}
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 	for (i = 0; mdb_isa_kregs[i].rd_name != NULL; i++) {
2237c478bd9Sstevel@tonic-gate 		const mdb_tgt_regdesc_t *rd = &mdb_isa_kregs[i];
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate 		if (strcmp(rd->rd_name, regname) == 0)
226ae115bc7Smrj 			return (&regs->kregs[rd->rd_num]);
2277c478bd9Sstevel@tonic-gate 	}
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate 	(void) set_errno(ENOENT);
2307c478bd9Sstevel@tonic-gate 	return (NULL);
2317c478bd9Sstevel@tonic-gate }
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2347c478bd9Sstevel@tonic-gate static int
kaif_get_register(const char * regname,kreg_t * valp)235acbc304dSjohnlev kaif_get_register(const char *regname, kreg_t *valp)
2367c478bd9Sstevel@tonic-gate {
2377c478bd9Sstevel@tonic-gate 	kreg_t *regp;
2387c478bd9Sstevel@tonic-gate 
239acbc304dSjohnlev 	if ((regp = kaif_find_regp(regname)) == NULL)
2407c478bd9Sstevel@tonic-gate 		return (-1);
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 	*valp = *regp;
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate 	return (0);
2457c478bd9Sstevel@tonic-gate }
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate static int
kaif_set_register(const char * regname,kreg_t val)248acbc304dSjohnlev kaif_set_register(const char *regname, kreg_t val)
2497c478bd9Sstevel@tonic-gate {
2507c478bd9Sstevel@tonic-gate 	kreg_t *regp;
2517c478bd9Sstevel@tonic-gate 
252acbc304dSjohnlev 	if ((regp = kaif_find_regp(regname)) == NULL)
2537c478bd9Sstevel@tonic-gate 		return (-1);
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 	*regp = val;
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 	return (0);
2587c478bd9Sstevel@tonic-gate }
2597c478bd9Sstevel@tonic-gate 
26074ecdb51SJohn Levon /*
26174ecdb51SJohn Levon  * Refuse to single-step or break within any stub that loads a user %cr3 value.
26274ecdb51SJohn Levon  * As the KDI traps are not careful to restore such a %cr3, this can all go
26374ecdb51SJohn Levon  * wrong, both spectacularly and subtly.
26474ecdb51SJohn Levon  */
26574ecdb51SJohn Levon static boolean_t
kaif_toxic_text(uintptr_t addr)26674ecdb51SJohn Levon kaif_toxic_text(uintptr_t addr)
26774ecdb51SJohn Levon {
26874ecdb51SJohn Levon 	static GElf_Sym toxic_syms[2] = { 0, };
26974ecdb51SJohn Levon 	size_t i;
27074ecdb51SJohn Levon 
271892ad162SToomas Soome 	if (toxic_syms[0].st_name == 0) {
27274ecdb51SJohn Levon 		if (mdb_tgt_lookup_by_name(mdb.m_target, MDB_TGT_OBJ_EXEC,
27374ecdb51SJohn Levon 		    "tr_iret_user", &toxic_syms[0], NULL) != 0)
27474ecdb51SJohn Levon 			warn("couldn't find tr_iret_user\n");
27574ecdb51SJohn Levon 		if (mdb_tgt_lookup_by_name(mdb.m_target, MDB_TGT_OBJ_EXEC,
27674ecdb51SJohn Levon 		    "tr_mmu_flush_user_range", &toxic_syms[1], NULL) != 0)
27774ecdb51SJohn Levon 			warn("couldn't find tr_mmu_flush_user_range\n");
27874ecdb51SJohn Levon 	}
27974ecdb51SJohn Levon 
28074ecdb51SJohn Levon 	for (i = 0; i < ARRAY_SIZE(toxic_syms); i++) {
28174ecdb51SJohn Levon 		if (addr >= toxic_syms[i].st_value &&
28274ecdb51SJohn Levon 		    addr - toxic_syms[i].st_value < toxic_syms[i].st_size)
28374ecdb51SJohn Levon 			return (B_TRUE);
28474ecdb51SJohn Levon 	}
28574ecdb51SJohn Levon 
28674ecdb51SJohn Levon 	return (B_FALSE);
28774ecdb51SJohn Levon }
28874ecdb51SJohn Levon 
2897c478bd9Sstevel@tonic-gate static int
kaif_brkpt_arm(uintptr_t addr,mdb_instr_t * instrp)2907c478bd9Sstevel@tonic-gate kaif_brkpt_arm(uintptr_t addr, mdb_instr_t *instrp)
2917c478bd9Sstevel@tonic-gate {
2927c478bd9Sstevel@tonic-gate 	mdb_instr_t bkpt = KAIF_BREAKPOINT_INSTR;
2937c478bd9Sstevel@tonic-gate 
29474ecdb51SJohn Levon 	if (kaif_toxic_text(addr)) {
29574ecdb51SJohn Levon 		warn("%a cannot be a breakpoint target\n", addr);
29674ecdb51SJohn Levon 		return (set_errno(EMDB_TGTNOTSUP));
29774ecdb51SJohn Levon 	}
29874ecdb51SJohn Levon 
2999c3024a3SHans Rosenfeld 	if (mdb_tgt_aread(mdb.m_target, MDB_TGT_AS_VIRT_I, instrp,
3009c3024a3SHans Rosenfeld 	    sizeof (mdb_instr_t), addr) != sizeof (mdb_instr_t))
3017c478bd9Sstevel@tonic-gate 		return (-1); /* errno is set for us */
3027c478bd9Sstevel@tonic-gate 
3039c3024a3SHans Rosenfeld 	if (mdb_tgt_awrite(mdb.m_target, MDB_TGT_AS_VIRT_I, &bkpt,
3049c3024a3SHans Rosenfeld 	    sizeof (mdb_instr_t), addr) != sizeof (mdb_instr_t))
3057c478bd9Sstevel@tonic-gate 		return (-1); /* errno is set for us */
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate 	return (0);
3087c478bd9Sstevel@tonic-gate }
3097c478bd9Sstevel@tonic-gate 
3107c478bd9Sstevel@tonic-gate static int
kaif_brkpt_disarm(uintptr_t addr,mdb_instr_t instrp)3117c478bd9Sstevel@tonic-gate kaif_brkpt_disarm(uintptr_t addr, mdb_instr_t instrp)
3127c478bd9Sstevel@tonic-gate {
3139c3024a3SHans Rosenfeld 	if (mdb_tgt_awrite(mdb.m_target, MDB_TGT_AS_VIRT_I, &instrp,
3149c3024a3SHans Rosenfeld 	    sizeof (mdb_instr_t), addr) != sizeof (mdb_instr_t))
3157c478bd9Sstevel@tonic-gate 		return (-1); /* errno is set for us */
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 	return (0);
3187c478bd9Sstevel@tonic-gate }
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate /*
3217c478bd9Sstevel@tonic-gate  * Intel watchpoints are even more fun than SPARC ones.  The Intel architecture
3227c478bd9Sstevel@tonic-gate  * manuals refer to watchpoints as breakpoints.  For consistency  with the
3237c478bd9Sstevel@tonic-gate  * terminology used in other portions of kmdb, we will, however, refer to them
3247c478bd9Sstevel@tonic-gate  * as watchpoints.
3257c478bd9Sstevel@tonic-gate  *
3267c478bd9Sstevel@tonic-gate  * Execute, data write, I/O read/write, and data read/write watchpoints are
3277c478bd9Sstevel@tonic-gate  * supported by the hardware.  Execute watchpoints must be one byte in length,
3287c478bd9Sstevel@tonic-gate  * and must be placed on the first byte of the instruction to be watched.
3297c478bd9Sstevel@tonic-gate  * Lengths of other watchpoints are more varied.
3307c478bd9Sstevel@tonic-gate  *
3317c478bd9Sstevel@tonic-gate  * Given that we already have a breakpoint facility, and given the restrictions
3327c478bd9Sstevel@tonic-gate  * placed on execute watchpoints, we're going to disallow the creation of
3337c478bd9Sstevel@tonic-gate  * execute watchpoints.  The others will be fully supported.  See the Debugging
3347c478bd9Sstevel@tonic-gate  * chapter in both the IA32 and AMD64 System Programming books for more details.
3357c478bd9Sstevel@tonic-gate  */
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate #ifdef __amd64
3387c478bd9Sstevel@tonic-gate #define	WAPT_DATA_MAX_SIZE	8
3397c478bd9Sstevel@tonic-gate #define	WAPT_DATA_SIZES_MSG	"1, 2, 4, or 8"
3407c478bd9Sstevel@tonic-gate #else
3417c478bd9Sstevel@tonic-gate #define	WAPT_DATA_MAX_SIZE	4
3427c478bd9Sstevel@tonic-gate #define	WAPT_DATA_SIZES_MSG	"1, 2, or 4"
3437c478bd9Sstevel@tonic-gate #endif
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate static int
kaif_wapt_validate(kmdb_wapt_t * wp)3467c478bd9Sstevel@tonic-gate kaif_wapt_validate(kmdb_wapt_t *wp)
3477c478bd9Sstevel@tonic-gate {
3487c478bd9Sstevel@tonic-gate 	if (wp->wp_type == DPI_WAPT_TYPE_IO) {
3497c478bd9Sstevel@tonic-gate 		if (wp->wp_wflags != (MDB_TGT_WA_R | MDB_TGT_WA_W)) {
3507c478bd9Sstevel@tonic-gate 			warn("I/O port watchpoints must be read/write\n");
3517c478bd9Sstevel@tonic-gate 			return (set_errno(EINVAL));
3527c478bd9Sstevel@tonic-gate 		}
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate 		if (wp->wp_size != 1 && wp->wp_size != 2 && wp->wp_size != 4) {
3557c478bd9Sstevel@tonic-gate 			warn("I/O watchpoint size must be 1, 2, or 4 bytes\n");
3567c478bd9Sstevel@tonic-gate 			return (set_errno(EINVAL));
3577c478bd9Sstevel@tonic-gate 		}
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate 	} else if (wp->wp_type == DPI_WAPT_TYPE_PHYS) {
3607c478bd9Sstevel@tonic-gate 		warn("physical address watchpoints are not supported on this "
3617c478bd9Sstevel@tonic-gate 		    "platform\n");
3627c478bd9Sstevel@tonic-gate 		return (set_errno(EMDB_TGTHWNOTSUP));
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 	} else {
3657c478bd9Sstevel@tonic-gate 		if (wp->wp_wflags != (MDB_TGT_WA_R | MDB_TGT_WA_W) &&
3667c478bd9Sstevel@tonic-gate 		    wp->wp_wflags != MDB_TGT_WA_W) {
3677c478bd9Sstevel@tonic-gate 			warn("watchpoints must be read/write or write-only\n");
3687c478bd9Sstevel@tonic-gate 			return (set_errno(EINVAL));
3697c478bd9Sstevel@tonic-gate 		}
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 		if ((wp->wp_size & -(wp->wp_size)) != wp->wp_size ||
3727c478bd9Sstevel@tonic-gate 		    wp->wp_size > WAPT_DATA_MAX_SIZE) {
3737c478bd9Sstevel@tonic-gate 			warn("data watchpoint size must be " WAPT_DATA_SIZES_MSG
3747c478bd9Sstevel@tonic-gate 			    " bytes\n");
3757c478bd9Sstevel@tonic-gate 			return (set_errno(EINVAL));
3767c478bd9Sstevel@tonic-gate 		}
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate 	}
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 	if (wp->wp_addr & (wp->wp_size - 1)) {
3817c478bd9Sstevel@tonic-gate 		warn("%lu-byte watchpoints must be %lu-byte aligned\n",
3827c478bd9Sstevel@tonic-gate 		    (ulong_t)wp->wp_size, (ulong_t)wp->wp_size);
3837c478bd9Sstevel@tonic-gate 		return (set_errno(EINVAL));
3847c478bd9Sstevel@tonic-gate 	}
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 	return (0);
3877c478bd9Sstevel@tonic-gate }
3887c478bd9Sstevel@tonic-gate 
3897c478bd9Sstevel@tonic-gate static int
kaif_wapt_reserve(kmdb_wapt_t * wp)3907c478bd9Sstevel@tonic-gate kaif_wapt_reserve(kmdb_wapt_t *wp)
3917c478bd9Sstevel@tonic-gate {
3927c478bd9Sstevel@tonic-gate 	int id;
3937c478bd9Sstevel@tonic-gate 
394ae115bc7Smrj 	for (id = 0; id <= KDI_MAXWPIDX; id++) {
3957c478bd9Sstevel@tonic-gate 		if (!BT_TEST(&kaif_waptmap, id)) {
3967c478bd9Sstevel@tonic-gate 			/* found one */
3977c478bd9Sstevel@tonic-gate 			BT_SET(&kaif_waptmap, id);
3987c478bd9Sstevel@tonic-gate 			wp->wp_priv = (void *)(uintptr_t)id;
3997c478bd9Sstevel@tonic-gate 			return (0);
4007c478bd9Sstevel@tonic-gate 		}
4017c478bd9Sstevel@tonic-gate 	}
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate 	return (set_errno(EMDB_WPTOOMANY));
4047c478bd9Sstevel@tonic-gate }
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate static void
kaif_wapt_release(kmdb_wapt_t * wp)4077c478bd9Sstevel@tonic-gate kaif_wapt_release(kmdb_wapt_t *wp)
4087c478bd9Sstevel@tonic-gate {
4097c478bd9Sstevel@tonic-gate 	int id = KAIF_WPPRIV2ID(wp);
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 	ASSERT(BT_TEST(&kaif_waptmap, id));
4127c478bd9Sstevel@tonic-gate 	BT_CLEAR(&kaif_waptmap, id);
4137c478bd9Sstevel@tonic-gate }
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate /*ARGSUSED*/
4167c478bd9Sstevel@tonic-gate static void
kaif_wapt_arm(kmdb_wapt_t * wp)4177c478bd9Sstevel@tonic-gate kaif_wapt_arm(kmdb_wapt_t *wp)
4187c478bd9Sstevel@tonic-gate {
4197c478bd9Sstevel@tonic-gate 	uint_t rw;
4207c478bd9Sstevel@tonic-gate 	int hwid = KAIF_WPPRIV2ID(wp);
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate 	ASSERT(BT_TEST(&kaif_waptmap, hwid));
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate 	if (wp->wp_type == DPI_WAPT_TYPE_IO)
4257c478bd9Sstevel@tonic-gate 		rw = KREG_DRCTL_WP_IORW;
4267c478bd9Sstevel@tonic-gate 	else if (wp->wp_wflags & MDB_TGT_WA_R)
4277c478bd9Sstevel@tonic-gate 		rw = KREG_DRCTL_WP_RW;
4287c478bd9Sstevel@tonic-gate 	else if (wp->wp_wflags & MDB_TGT_WA_X)
4297c478bd9Sstevel@tonic-gate 		rw = KREG_DRCTL_WP_EXEC;
4307c478bd9Sstevel@tonic-gate 	else
4317c478bd9Sstevel@tonic-gate 		rw = KREG_DRCTL_WP_WONLY;
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate 	kaif_drreg.dr_addr[hwid] = wp->wp_addr;
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 	kaif_drreg.dr_ctl &= ~KREG_DRCTL_WP_MASK(hwid);
4367c478bd9Sstevel@tonic-gate 	kaif_drreg.dr_ctl |= KREG_DRCTL_WP_LENRW(hwid, wp->wp_size - 1, rw);
4377c478bd9Sstevel@tonic-gate 	kaif_drreg.dr_ctl |= KREG_DRCTL_WPEN(hwid);
438ae115bc7Smrj 	kmdb_kdi_update_drreg(&kaif_drreg);
4397c478bd9Sstevel@tonic-gate }
4407c478bd9Sstevel@tonic-gate 
4417c478bd9Sstevel@tonic-gate /*ARGSUSED*/
4427c478bd9Sstevel@tonic-gate static void
kaif_wapt_disarm(kmdb_wapt_t * wp)4437c478bd9Sstevel@tonic-gate kaif_wapt_disarm(kmdb_wapt_t *wp)
4447c478bd9Sstevel@tonic-gate {
4457c478bd9Sstevel@tonic-gate 	int hwid = KAIF_WPPRIV2ID(wp);
4467c478bd9Sstevel@tonic-gate 
4477c478bd9Sstevel@tonic-gate 	ASSERT(BT_TEST(&kaif_waptmap, hwid));
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate 	kaif_drreg.dr_addr[hwid] = 0;
4507c478bd9Sstevel@tonic-gate 	kaif_drreg.dr_ctl &= ~(KREG_DRCTL_WP_MASK(hwid) |
4517c478bd9Sstevel@tonic-gate 	    KREG_DRCTL_WPEN_MASK(hwid));
452ae115bc7Smrj 	kmdb_kdi_update_drreg(&kaif_drreg);
4537c478bd9Sstevel@tonic-gate }
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate /*ARGSUSED*/
4567c478bd9Sstevel@tonic-gate static int
kaif_wapt_match(kmdb_wapt_t * wp)4577c478bd9Sstevel@tonic-gate kaif_wapt_match(kmdb_wapt_t *wp)
4587c478bd9Sstevel@tonic-gate {
4597c478bd9Sstevel@tonic-gate 	int hwid = KAIF_WPPRIV2ID(wp);
4607c478bd9Sstevel@tonic-gate 	uint32_t mask = KREG_DRSTAT_WP_MASK(hwid);
4617c478bd9Sstevel@tonic-gate 	int n = 0;
4627c478bd9Sstevel@tonic-gate 	int i;
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate 	ASSERT(BT_TEST(&kaif_waptmap, hwid));
4657c478bd9Sstevel@tonic-gate 
4667c478bd9Sstevel@tonic-gate 	for (i = 0; i < kaif_ncpusave; i++)
4677c478bd9Sstevel@tonic-gate 		n += (kaif_cpusave[i].krs_dr.dr_stat & mask) != 0;
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 	return (n);
4707c478bd9Sstevel@tonic-gate }
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate static int
kaif_step(void)4737c478bd9Sstevel@tonic-gate kaif_step(void)
4747c478bd9Sstevel@tonic-gate {
4757c478bd9Sstevel@tonic-gate 	kreg_t pc, fl, oldfl, newfl, sp;
4767c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t npc;
4777c478bd9Sstevel@tonic-gate 	mdb_instr_t instr;
4787c478bd9Sstevel@tonic-gate 	int emulated = 0, rchk = 0;
4797c478bd9Sstevel@tonic-gate 	size_t pcoff = 0;
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate 	(void) kmdb_dpi_get_register("pc", &pc);
4827c478bd9Sstevel@tonic-gate 
48374ecdb51SJohn Levon 	if (kaif_toxic_text(pc)) {
48474ecdb51SJohn Levon 		warn("%a cannot be stepped\n", pc);
48574ecdb51SJohn Levon 		return (set_errno(EMDB_TGTNOTSUP));
48674ecdb51SJohn Levon 	}
48774ecdb51SJohn Levon 
4887c478bd9Sstevel@tonic-gate 	if ((npc = mdb_dis_nextins(mdb.m_disasm, mdb.m_target,
4899c3024a3SHans Rosenfeld 	    MDB_TGT_AS_VIRT_I, pc)) == pc) {
4907c478bd9Sstevel@tonic-gate 		warn("failed to decode instruction at %a for step\n", pc);
4917c478bd9Sstevel@tonic-gate 		return (set_errno(EINVAL));
4927c478bd9Sstevel@tonic-gate 	}
4937c478bd9Sstevel@tonic-gate 
4947c478bd9Sstevel@tonic-gate 	/*
4957c478bd9Sstevel@tonic-gate 	 * Stepping behavior depends on the type of instruction.  It does not
4967c478bd9Sstevel@tonic-gate 	 * depend on the presence of a REX prefix, as the action we take for a
4977c478bd9Sstevel@tonic-gate 	 * given instruction doesn't currently vary for 32-bit instructions
4987c478bd9Sstevel@tonic-gate 	 * versus their 64-bit counterparts.
4997c478bd9Sstevel@tonic-gate 	 */
5007c478bd9Sstevel@tonic-gate 	do {
5019c3024a3SHans Rosenfeld 		if (mdb_tgt_aread(mdb.m_target, MDB_TGT_AS_VIRT_I, &instr,
5029c3024a3SHans Rosenfeld 		    sizeof (mdb_instr_t), pc + pcoff) != sizeof (mdb_instr_t)) {
5037c478bd9Sstevel@tonic-gate 			warn("failed to read at %p for step",
5047c478bd9Sstevel@tonic-gate 			    (void *)(pc + pcoff));
5057c478bd9Sstevel@tonic-gate 			return (-1);
5067c478bd9Sstevel@tonic-gate 		}
5077c478bd9Sstevel@tonic-gate 	} while (pcoff++, (instr >= M_REX_LO && instr <= M_REX_HI && !rchk++));
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate 	switch (instr) {
5107c478bd9Sstevel@tonic-gate 	case M_IRET:
5117c478bd9Sstevel@tonic-gate 		warn("iret cannot be stepped\n");
5127c478bd9Sstevel@tonic-gate 		return (set_errno(EMDB_TGTNOTSUP));
5137c478bd9Sstevel@tonic-gate 
5147c478bd9Sstevel@tonic-gate 	case M_INT3:
5157c478bd9Sstevel@tonic-gate 	case M_INTX:
5167c478bd9Sstevel@tonic-gate 	case M_INTO:
5177c478bd9Sstevel@tonic-gate 		warn("int cannot be stepped\n");
5187c478bd9Sstevel@tonic-gate 		return (set_errno(EMDB_TGTNOTSUP));
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate 	case M_ESC:
5219c3024a3SHans Rosenfeld 		if (mdb_tgt_aread(mdb.m_target, MDB_TGT_AS_VIRT_I, &instr,
5229c3024a3SHans Rosenfeld 		    sizeof (mdb_instr_t), pc + pcoff) != sizeof (mdb_instr_t)) {
5237c478bd9Sstevel@tonic-gate 			warn("failed to read at %p for step",
5247c478bd9Sstevel@tonic-gate 			    (void *)(pc + pcoff));
5257c478bd9Sstevel@tonic-gate 			return (-1);
5267c478bd9Sstevel@tonic-gate 		}
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate 		switch (instr) {
5297c478bd9Sstevel@tonic-gate 		case M_SYSRET:
5307c478bd9Sstevel@tonic-gate 			warn("sysret cannot be stepped\n");
5317c478bd9Sstevel@tonic-gate 			return (set_errno(EMDB_TGTNOTSUP));
5327c478bd9Sstevel@tonic-gate 		case M_SYSEXIT:
5337c478bd9Sstevel@tonic-gate 			warn("sysexit cannot be stepped\n");
5347c478bd9Sstevel@tonic-gate 			return (set_errno(EMDB_TGTNOTSUP));
5357c478bd9Sstevel@tonic-gate 		}
5367c478bd9Sstevel@tonic-gate 		break;
5377c478bd9Sstevel@tonic-gate 
5387c478bd9Sstevel@tonic-gate 	/*
5397c478bd9Sstevel@tonic-gate 	 * Some instructions need to be emulated.  We need to prevent direct
5407c478bd9Sstevel@tonic-gate 	 * manipulations of EFLAGS, so we'll emulate cli, sti.  pushfl and
5417c478bd9Sstevel@tonic-gate 	 * popfl also receive special handling, as they manipulate both EFLAGS
5427c478bd9Sstevel@tonic-gate 	 * and %esp.
5437c478bd9Sstevel@tonic-gate 	 */
5447c478bd9Sstevel@tonic-gate 	case M_CLI:
5457c478bd9Sstevel@tonic-gate 		(void) kmdb_dpi_get_register(FLAGS_REG_NAME, &fl);
5467c478bd9Sstevel@tonic-gate 		fl &= ~KREG_EFLAGS_IF_MASK;
5477c478bd9Sstevel@tonic-gate 		(void) kmdb_dpi_set_register(FLAGS_REG_NAME, fl);
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 		emulated = 1;
5507c478bd9Sstevel@tonic-gate 		break;
5517c478bd9Sstevel@tonic-gate 
5527c478bd9Sstevel@tonic-gate 	case M_STI:
5537c478bd9Sstevel@tonic-gate 		(void) kmdb_dpi_get_register(FLAGS_REG_NAME, &fl);
5547c478bd9Sstevel@tonic-gate 		fl |= (1 << KREG_EFLAGS_IF_SHIFT);
5557c478bd9Sstevel@tonic-gate 		(void) kmdb_dpi_set_register(FLAGS_REG_NAME, fl);
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate 		emulated = 1;
5587c478bd9Sstevel@tonic-gate 		break;
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate 	case M_POPF:
5617c478bd9Sstevel@tonic-gate 		/*
5627c478bd9Sstevel@tonic-gate 		 * popfl will restore a pushed EFLAGS from the stack, and could
563ae115bc7Smrj 		 * in so doing cause IF to be turned on, if only for a brief
5647c478bd9Sstevel@tonic-gate 		 * period.  To avoid this, we'll secretly replace the stack's
5657c478bd9Sstevel@tonic-gate 		 * EFLAGS with our decaffeinated brand.  We'll then manually
5667c478bd9Sstevel@tonic-gate 		 * load our EFLAGS copy with the real verion after the step.
5677c478bd9Sstevel@tonic-gate 		 */
5687c478bd9Sstevel@tonic-gate 		(void) kmdb_dpi_get_register("sp", &sp);
5697c478bd9Sstevel@tonic-gate 		(void) kmdb_dpi_get_register(FLAGS_REG_NAME, &fl);
5707c478bd9Sstevel@tonic-gate 
5719c3024a3SHans Rosenfeld 		if (mdb_tgt_aread(mdb.m_target, MDB_TGT_AS_VIRT_S, &newfl,
5729c3024a3SHans Rosenfeld 		    sizeof (kreg_t), sp) != sizeof (kreg_t)) {
5737c478bd9Sstevel@tonic-gate 			warn("failed to read " FLAGS_REG_NAME
5747c478bd9Sstevel@tonic-gate 			    " at %p for popfl step\n", (void *)sp);
5757c478bd9Sstevel@tonic-gate 			return (set_errno(EMDB_TGTNOTSUP)); /* XXX ? */
5767c478bd9Sstevel@tonic-gate 		}
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 		fl = (fl & ~KREG_EFLAGS_IF_MASK) | KREG_EFLAGS_TF_MASK;
5797c478bd9Sstevel@tonic-gate 
5809c3024a3SHans Rosenfeld 		if (mdb_tgt_awrite(mdb.m_target, MDB_TGT_AS_VIRT_S, &fl,
5819c3024a3SHans Rosenfeld 		    sizeof (kreg_t), sp) != sizeof (kreg_t)) {
5827c478bd9Sstevel@tonic-gate 			warn("failed to update " FLAGS_REG_NAME
5837c478bd9Sstevel@tonic-gate 			    " at %p for popfl step\n", (void *)sp);
5847c478bd9Sstevel@tonic-gate 			return (set_errno(EMDB_TGTNOTSUP)); /* XXX ? */
5857c478bd9Sstevel@tonic-gate 		}
5867c478bd9Sstevel@tonic-gate 		break;
5877c478bd9Sstevel@tonic-gate 	}
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 	if (emulated) {
5907c478bd9Sstevel@tonic-gate 		(void) kmdb_dpi_set_register("pc", npc);
5917c478bd9Sstevel@tonic-gate 		return (0);
5927c478bd9Sstevel@tonic-gate 	}
5937c478bd9Sstevel@tonic-gate 
5947c478bd9Sstevel@tonic-gate 	/* Do the step with IF off, and TF (step) on */
5957c478bd9Sstevel@tonic-gate 	(void) kmdb_dpi_get_register(FLAGS_REG_NAME, &oldfl);
5967c478bd9Sstevel@tonic-gate 	(void) kmdb_dpi_set_register(FLAGS_REG_NAME,
5977c478bd9Sstevel@tonic-gate 	    ((oldfl | (1 << KREG_EFLAGS_TF_SHIFT)) & ~KREG_EFLAGS_IF_MASK));
5987c478bd9Sstevel@tonic-gate 
5997c478bd9Sstevel@tonic-gate 	kmdb_dpi_resume_master(); /* ... there and back again ... */
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 	/* EFLAGS has now changed, and may require tuning */
6027c478bd9Sstevel@tonic-gate 
6037c478bd9Sstevel@tonic-gate 	switch (instr) {
6047c478bd9Sstevel@tonic-gate 	case M_POPF:
6057c478bd9Sstevel@tonic-gate 		/*
6067c478bd9Sstevel@tonic-gate 		 * Use the EFLAGS we grabbed before the pop - see the pre-step
6077c478bd9Sstevel@tonic-gate 		 * M_POPFL comment.
6087c478bd9Sstevel@tonic-gate 		 */
6097c478bd9Sstevel@tonic-gate 		(void) kmdb_dpi_set_register(FLAGS_REG_NAME, newfl);
6107c478bd9Sstevel@tonic-gate 		return (0);
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate 	case M_PUSHF:
6137c478bd9Sstevel@tonic-gate 		/*
6147c478bd9Sstevel@tonic-gate 		 * We pushed our modified EFLAGS (with IF and TF turned off)
6157c478bd9Sstevel@tonic-gate 		 * onto the stack.  Replace the pushed version with our
6167c478bd9Sstevel@tonic-gate 		 * unmodified one.
6177c478bd9Sstevel@tonic-gate 		 */
6187c478bd9Sstevel@tonic-gate 		(void) kmdb_dpi_get_register("sp", &sp);
6197c478bd9Sstevel@tonic-gate 
6209c3024a3SHans Rosenfeld 		if (mdb_tgt_awrite(mdb.m_target, MDB_TGT_AS_VIRT_S, &oldfl,
6219c3024a3SHans Rosenfeld 		    sizeof (kreg_t), sp) != sizeof (kreg_t)) {
6227c478bd9Sstevel@tonic-gate 			warn("failed to update pushed " FLAGS_REG_NAME
6237c478bd9Sstevel@tonic-gate 			    " at %p after pushfl step\n", (void *)sp);
6247c478bd9Sstevel@tonic-gate 			return (set_errno(EMDB_TGTNOTSUP)); /* XXX ? */
6257c478bd9Sstevel@tonic-gate 		}
6267c478bd9Sstevel@tonic-gate 
6277c478bd9Sstevel@tonic-gate 		/* Go back to using the EFLAGS we were using before the step */
6287c478bd9Sstevel@tonic-gate 		(void) kmdb_dpi_set_register(FLAGS_REG_NAME, oldfl);
6297c478bd9Sstevel@tonic-gate 		return (0);
6307c478bd9Sstevel@tonic-gate 
6317c478bd9Sstevel@tonic-gate 	default:
6327c478bd9Sstevel@tonic-gate 		/*
6337c478bd9Sstevel@tonic-gate 		 * The stepped instruction may have altered EFLAGS.  We only
6347c478bd9Sstevel@tonic-gate 		 * really care about the value of IF, and we know the stepped
6357c478bd9Sstevel@tonic-gate 		 * instruction didn't alter it, so we can simply copy the
6367c478bd9Sstevel@tonic-gate 		 * pre-step value.  We'll also need to turn TF back off.
6377c478bd9Sstevel@tonic-gate 		 */
6387c478bd9Sstevel@tonic-gate 		(void) kmdb_dpi_get_register(FLAGS_REG_NAME, &fl);
6397c478bd9Sstevel@tonic-gate 		(void) kmdb_dpi_set_register(FLAGS_REG_NAME,
6407c478bd9Sstevel@tonic-gate 		    ((fl & ~(KREG_EFLAGS_TF_MASK|KREG_EFLAGS_IF_MASK)) |
6417c478bd9Sstevel@tonic-gate 		    (oldfl & KREG_EFLAGS_IF_MASK)));
6427c478bd9Sstevel@tonic-gate 		return (0);
6437c478bd9Sstevel@tonic-gate 	}
6447c478bd9Sstevel@tonic-gate }
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate /*ARGSUSED*/
6477c478bd9Sstevel@tonic-gate static uintptr_t
kaif_call(uintptr_t funcva,uint_t argc,const uintptr_t argv[])6487c478bd9Sstevel@tonic-gate kaif_call(uintptr_t funcva, uint_t argc, const uintptr_t argv[])
6497c478bd9Sstevel@tonic-gate {
6507c478bd9Sstevel@tonic-gate 	return (kaif_invoke(funcva, argc, argv));
6517c478bd9Sstevel@tonic-gate }
6527c478bd9Sstevel@tonic-gate 
6537c478bd9Sstevel@tonic-gate static void
dump_crumb(kdi_crumb_t * krmp)654ae115bc7Smrj dump_crumb(kdi_crumb_t *krmp)
6557c478bd9Sstevel@tonic-gate {
656ae115bc7Smrj 	kdi_crumb_t krm;
657acbc304dSjohnlev 
658ae115bc7Smrj 	if (mdb_vread(&krm, sizeof (kdi_crumb_t), (uintptr_t)krmp) !=
659ae115bc7Smrj 	    sizeof (kdi_crumb_t)) {
660acbc304dSjohnlev 		warn("failed to read crumb at %p", krmp);
661acbc304dSjohnlev 		return;
662acbc304dSjohnlev 	}
663acbc304dSjohnlev 
6647c478bd9Sstevel@tonic-gate 	mdb_printf("state: ");
665acbc304dSjohnlev 	switch (krm.krm_cpu_state) {
6667c478bd9Sstevel@tonic-gate 	case KAIF_CPU_STATE_MASTER:
6677c478bd9Sstevel@tonic-gate 		mdb_printf("M");
6687c478bd9Sstevel@tonic-gate 		break;
6697c478bd9Sstevel@tonic-gate 	case KAIF_CPU_STATE_SLAVE:
6707c478bd9Sstevel@tonic-gate 		mdb_printf("S");
6717c478bd9Sstevel@tonic-gate 		break;
6727c478bd9Sstevel@tonic-gate 	default:
673acbc304dSjohnlev 		mdb_printf("%d", krm.krm_cpu_state);
6747c478bd9Sstevel@tonic-gate 	}
6757c478bd9Sstevel@tonic-gate 
6767c478bd9Sstevel@tonic-gate 	mdb_printf(" trapno %3d sp %08x flag %d pc %p %A\n",
677acbc304dSjohnlev 	    krm.krm_trapno, krm.krm_sp, krm.krm_flag, krm.krm_pc, krm.krm_pc);
6787c478bd9Sstevel@tonic-gate }
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate static void
dump_crumbs(kaif_cpusave_t * save)6817c478bd9Sstevel@tonic-gate dump_crumbs(kaif_cpusave_t *save)
6827c478bd9Sstevel@tonic-gate {
6837c478bd9Sstevel@tonic-gate 	int i;
6847c478bd9Sstevel@tonic-gate 
685ae115bc7Smrj 	for (i = KDI_NCRUMBS; i > 0; i--) {
686ae115bc7Smrj 		uint_t idx = (save->krs_curcrumbidx + i) % KDI_NCRUMBS;
6877c478bd9Sstevel@tonic-gate 		dump_crumb(&save->krs_crumbs[idx]);
6887c478bd9Sstevel@tonic-gate 	}
6897c478bd9Sstevel@tonic-gate }
6907c478bd9Sstevel@tonic-gate 
6917c478bd9Sstevel@tonic-gate static void
kaif_dump_crumbs(uintptr_t addr,int cpuid)6927c478bd9Sstevel@tonic-gate kaif_dump_crumbs(uintptr_t addr, int cpuid)
6937c478bd9Sstevel@tonic-gate {
6947c478bd9Sstevel@tonic-gate 	int i;
6957c478bd9Sstevel@tonic-gate 
696892ad162SToomas Soome 	if (addr != 0) {
697acbc304dSjohnlev 		/* dump_crumb will protect us against bogus addresses */
698ae115bc7Smrj 		dump_crumb((kdi_crumb_t *)addr);
6997c478bd9Sstevel@tonic-gate 
7007c478bd9Sstevel@tonic-gate 	} else if (cpuid != -1) {
701acbc304dSjohnlev 		if (cpuid < 0 || cpuid >= kaif_ncpusave)
7027c478bd9Sstevel@tonic-gate 			return;
7037c478bd9Sstevel@tonic-gate 
7047c478bd9Sstevel@tonic-gate 		dump_crumbs(&kaif_cpusave[cpuid]);
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate 	} else {
7077c478bd9Sstevel@tonic-gate 		for (i = 0; i < kaif_ncpusave; i++) {
7087c478bd9Sstevel@tonic-gate 			kaif_cpusave_t *save = &kaif_cpusave[i];
7097c478bd9Sstevel@tonic-gate 
7107c478bd9Sstevel@tonic-gate 			if (save->krs_cpu_state == KAIF_CPU_STATE_NONE)
7117c478bd9Sstevel@tonic-gate 				continue;
7127c478bd9Sstevel@tonic-gate 
7137c478bd9Sstevel@tonic-gate 			mdb_printf("%sCPU %d crumbs: (curidx %d)\n",
7147c478bd9Sstevel@tonic-gate 			    (i == 0 ? "" : "\n"), i, save->krs_curcrumbidx);
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 			dump_crumbs(save);
7177c478bd9Sstevel@tonic-gate 		}
7187c478bd9Sstevel@tonic-gate 	}
7197c478bd9Sstevel@tonic-gate }
7207c478bd9Sstevel@tonic-gate 
7217c478bd9Sstevel@tonic-gate static void
kaif_modchg_register(void (* func)(struct modctl *,int))7227c478bd9Sstevel@tonic-gate kaif_modchg_register(void (*func)(struct modctl *, int))
7237c478bd9Sstevel@tonic-gate {
7247c478bd9Sstevel@tonic-gate 	kaif_modchg_cb = func;
7257c478bd9Sstevel@tonic-gate }
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate static void
kaif_modchg_cancel(void)7287c478bd9Sstevel@tonic-gate kaif_modchg_cancel(void)
7297c478bd9Sstevel@tonic-gate {
7307c478bd9Sstevel@tonic-gate 	ASSERT(kaif_modchg_cb != NULL);
7317c478bd9Sstevel@tonic-gate 
7327c478bd9Sstevel@tonic-gate 	kaif_modchg_cb = NULL;
7337c478bd9Sstevel@tonic-gate }
7347c478bd9Sstevel@tonic-gate 
735ae115bc7Smrj void
kaif_trap_set_debugger(void)736ae115bc7Smrj kaif_trap_set_debugger(void)
7377c478bd9Sstevel@tonic-gate {
738ae115bc7Smrj 	kmdb_kdi_idt_switch(NULL);
739ae115bc7Smrj }
7407c478bd9Sstevel@tonic-gate 
741ae115bc7Smrj void
kaif_trap_set_saved(kaif_cpusave_t * cpusave)742ae115bc7Smrj kaif_trap_set_saved(kaif_cpusave_t *cpusave)
743ae115bc7Smrj {
744ae115bc7Smrj 	kmdb_kdi_idt_switch(cpusave);
745ae115bc7Smrj }
7467c478bd9Sstevel@tonic-gate 
747ae115bc7Smrj static void
kaif_vmready(void)748ae115bc7Smrj kaif_vmready(void)
749ae115bc7Smrj {
750ae115bc7Smrj }
751ae115bc7Smrj 
752ae115bc7Smrj void
kaif_memavail(caddr_t base,size_t len)753ae115bc7Smrj kaif_memavail(caddr_t base, size_t len)
754ae115bc7Smrj {
755ae115bc7Smrj 	int ret;
7567c478bd9Sstevel@tonic-gate 	/*
7577c478bd9Sstevel@tonic-gate 	 * In the unlikely event that someone is stepping through this routine,
758ae115bc7Smrj 	 * we need to make sure that the KDI knows about the new range before
759ae115bc7Smrj 	 * umem gets it.  That way the entry code can recognize stacks
7607c478bd9Sstevel@tonic-gate 	 * allocated from the new region.
7617c478bd9Sstevel@tonic-gate 	 */
762ae115bc7Smrj 	kmdb_kdi_memrange_add(base, len);
763ae115bc7Smrj 	ret = mdb_umem_add(base, len);
764ae115bc7Smrj 	ASSERT(ret == 0);
765ae115bc7Smrj }
7667c478bd9Sstevel@tonic-gate 
767ae115bc7Smrj void
kaif_mod_loaded(struct modctl * modp)768ae115bc7Smrj kaif_mod_loaded(struct modctl *modp)
769ae115bc7Smrj {
770ae115bc7Smrj 	if (kaif_modchg_cb != NULL)
771ae115bc7Smrj 		kaif_modchg_cb(modp, 1);
772ae115bc7Smrj }
7737c478bd9Sstevel@tonic-gate 
774ae115bc7Smrj void
kaif_mod_unloading(struct modctl * modp)775ae115bc7Smrj kaif_mod_unloading(struct modctl *modp)
776ae115bc7Smrj {
777ae115bc7Smrj 	if (kaif_modchg_cb != NULL)
778ae115bc7Smrj 		kaif_modchg_cb(modp, 0);
7797c478bd9Sstevel@tonic-gate }
7807c478bd9Sstevel@tonic-gate 
7817c478bd9Sstevel@tonic-gate void
kaif_handle_fault(greg_t trapno,greg_t pc,greg_t sp,int cpuid)782ae115bc7Smrj kaif_handle_fault(greg_t trapno, greg_t pc, greg_t sp, int cpuid)
783ae115bc7Smrj {
784ae115bc7Smrj 	kmdb_dpi_handle_fault((kreg_t)trapno, (kreg_t)pc,
785ae115bc7Smrj 	    (kreg_t)sp, cpuid);
786ae115bc7Smrj }
787ae115bc7Smrj 
788ae115bc7Smrj static kdi_debugvec_t kaif_dvec = {
789ae115bc7Smrj 	NULL,			/* dv_kctl_vmready */
790ae115bc7Smrj 	NULL,			/* dv_kctl_memavail */
791ae115bc7Smrj 	NULL,			/* dv_kctl_modavail */
792ae115bc7Smrj 	NULL,			/* dv_kctl_thravail */
793ae115bc7Smrj 	kaif_vmready,
794ae115bc7Smrj 	kaif_memavail,
795ae115bc7Smrj 	kaif_mod_loaded,
796ae115bc7Smrj 	kaif_mod_unloading,
797ae115bc7Smrj 	kaif_handle_fault
798ae115bc7Smrj };
799ae115bc7Smrj 
800ae115bc7Smrj void
kaif_kdi_entry(kdi_cpusave_t * cpusave)801ae115bc7Smrj kaif_kdi_entry(kdi_cpusave_t *cpusave)
8027c478bd9Sstevel@tonic-gate {
803ae115bc7Smrj 	int ret = kaif_main_loop(cpusave);
804ae115bc7Smrj 	ASSERT(ret == KAIF_CPU_CMD_RESUME ||
805ae115bc7Smrj 	    ret == KAIF_CPU_CMD_RESUME_MASTER);
8067c478bd9Sstevel@tonic-gate }
8077c478bd9Sstevel@tonic-gate 
808ae115bc7Smrj /*ARGSUSED*/
8097c478bd9Sstevel@tonic-gate void
kaif_activate(kdi_debugvec_t ** dvecp,uint_t flags)810ae115bc7Smrj kaif_activate(kdi_debugvec_t **dvecp, uint_t flags)
8117c478bd9Sstevel@tonic-gate {
812ae115bc7Smrj 	kmdb_kdi_activate(kaif_kdi_entry, kaif_cpusave, kaif_ncpusave);
813ae115bc7Smrj 	*dvecp = &kaif_dvec;
8147c478bd9Sstevel@tonic-gate }
8157c478bd9Sstevel@tonic-gate 
8167c478bd9Sstevel@tonic-gate static int
kaif_init(kmdb_auxv_t * kav)8177c478bd9Sstevel@tonic-gate kaif_init(kmdb_auxv_t *kav)
8187c478bd9Sstevel@tonic-gate {
8197c478bd9Sstevel@tonic-gate 	/* Allocate the per-CPU save areas */
8207c478bd9Sstevel@tonic-gate 	kaif_cpusave = mdb_zalloc(sizeof (kaif_cpusave_t) * kav->kav_ncpu,
8217c478bd9Sstevel@tonic-gate 	    UM_SLEEP);
8227c478bd9Sstevel@tonic-gate 	kaif_ncpusave = kav->kav_ncpu;
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate 	kaif_modchg_cb = NULL;
8257c478bd9Sstevel@tonic-gate 
8267c478bd9Sstevel@tonic-gate 	kaif_waptmap = 0;
8277c478bd9Sstevel@tonic-gate 
8287c478bd9Sstevel@tonic-gate 	kaif_trap_switch = (kav->kav_flags & KMDB_AUXV_FL_NOTRPSWTCH) == 0;
8297c478bd9Sstevel@tonic-gate 
8307c478bd9Sstevel@tonic-gate 	return (0);
8317c478bd9Sstevel@tonic-gate }
8327c478bd9Sstevel@tonic-gate 
8337c478bd9Sstevel@tonic-gate dpi_ops_t kmdb_dpi_ops = {
834*0c1b95beSRichard Lowe 	.dpo_init = kaif_init,
835*0c1b95beSRichard Lowe 	.dpo_debugger_activate = kaif_activate,
836*0c1b95beSRichard Lowe 	.dpo_debugger_deactivate = kmdb_kdi_deactivate,
837*0c1b95beSRichard Lowe 	.dpo_enter_mon = kaif_enter_mon,
838*0c1b95beSRichard Lowe 	.dpo_modchg_register = kaif_modchg_register,
839*0c1b95beSRichard Lowe 	.dpo_modchg_cancel = kaif_modchg_cancel,
840*0c1b95beSRichard Lowe 	.dpo_get_cpu_state = kaif_get_cpu_state,
841*0c1b95beSRichard Lowe 	.dpo_get_master_cpuid = kaif_get_master_cpuid,
842*0c1b95beSRichard Lowe 	.dpo_get_gregs = kaif_get_gregs,
843*0c1b95beSRichard Lowe 	.dpo_get_register = kaif_get_register,
844*0c1b95beSRichard Lowe 	.dpo_set_register = kaif_set_register,
845*0c1b95beSRichard Lowe 	.dpo_brkpt_arm = kaif_brkpt_arm,
846*0c1b95beSRichard Lowe 	.dpo_brkpt_disarm = kaif_brkpt_disarm,
847*0c1b95beSRichard Lowe 	.dpo_wapt_validate = kaif_wapt_validate,
848*0c1b95beSRichard Lowe 	.dpo_wapt_reserve = kaif_wapt_reserve,
849*0c1b95beSRichard Lowe 	.dpo_wapt_release = kaif_wapt_release,
850*0c1b95beSRichard Lowe 	.dpo_wapt_arm = kaif_wapt_arm,
851*0c1b95beSRichard Lowe 	.dpo_wapt_disarm = kaif_wapt_disarm,
852*0c1b95beSRichard Lowe 	.dpo_wapt_match = kaif_wapt_match,
853*0c1b95beSRichard Lowe 	.dpo_step = kaif_step,
854*0c1b95beSRichard Lowe 	.dpo_call = kaif_call,
855*0c1b95beSRichard Lowe 	.dpo_dump_crumbs = kaif_dump_crumbs,
8567c478bd9Sstevel@tonic-gate };
857