xref: /illumos-gate/usr/src/cmd/mdb/intel/mdb/mdb_bhyve.c (revision 2776faf7c78dc27c9bdb459af6ed4db21b891b3b)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2019 Joyent, Inc.
14  * Copyright 2025 Oxide Computer Company
15  */
16 
17 /*
18  * bhyve target
19  *
20  * The bhyve target is used to examine and manipulate a bhyve VM. Access to
21  * a bhyve VM is provided by libvmm, which itself uses libvmmapi, which uses
22  * the vmm driver's ioctl interface to carry out requests.
23  *
24  * The bhyve target does not know about threads or processes, but it handles
25  * multiple vCPUs and can switch between them. Execution control is currently
26  * limited to completely stopping or resuming all vCPUs of a VM, or single-
27  * stepping a particular vCPU while all other vCPUs remain stopped. Breakpoints
28  * are not implemented yet, and as such step-out and step-over don't work yet.
29  * All known x86 instruction sets are support, legacy IA-16, IA-32 and AMD64.
30  * The current CPU instruction set is automatically determined by parsing the
31  * code segment (CS) attributes in the current vCPU.
32  *
33  * All of the VMs physical memory and device memory segments are mapped R/W
34  * into mdb's address space by libvmm. All accesses to those memory are
35  * facilitated through libvmm calls, which may include virtual address
36  * translation according to the current vCPU mode. Both real-mode and protected-
37  * mode segmentation are understood and used for translating virtual addresses
38  * into linear addresses, which may further be translated using 2-level, 3-level
39  * or 4-level paging.
40  *
41  * To handle disassembly and stack tracing properly when segmentation is used by
42  * a vCPU (always in real mode, sometimes in protected mode) the bhyve target
43  * has a notion of three virtual address spaces used for reading/writing memory:
44  *   - MDB_TGT_AS_VIRT, the default virtual address space uses the DS segment
45  *     by default, but this default can be changed with the ::defseg dcmd.
46  *   - MDB_TGT_AS_VIRT_I, the virtual address space for instructions always
47  *     uses the code segment (CS) for translation
48  *   - MDB_TGT_AS_VIRT_S, the virtual address space for the stack always uses
49  *     the stack segment (SS) for translation
50  *
51  * Register printing and stack tracing is using the common x86 ISA-specific code
52  * in IA-32 and AMD64 modes. There is no stack tracing for IA-16 mode yet.
53  *
54  * Todo:
55  *   - support for breakpoint, step-out, and step-over
56  *   - support for x86 stack tracing
57  */
58 #include <mdb/mdb_conf.h>
59 #include <mdb/mdb_err.h>
60 #include <mdb/mdb_signal.h>
61 #include <mdb/mdb_stack.h>
62 #include <mdb/mdb_modapi.h>
63 #include <mdb/mdb_io_impl.h>
64 #include <mdb/mdb_kreg_impl.h>
65 #include <mdb/mdb_target_impl.h>
66 #include <mdb/mdb_isautil.h>
67 #include <mdb/mdb_amd64util.h>
68 #include <mdb/mdb_ia32util.h>
69 #include <mdb/mdb_x86util.h>
70 #include <mdb/mdb.h>
71 
72 #include <sys/controlregs.h>
73 #include <sys/debugreg.h>
74 #include <sys/sysmacros.h>
75 #include <sys/note.h>
76 #include <unistd.h>
77 #include <inttypes.h>
78 
79 #include <libvmm.h>
80 
81 #define	MDB_DEF_PROMPT	"[%<_cpuid>]> "
82 
83 typedef struct bhyve_data {
84 	vmm_t *bd_vmm;
85 	uint_t bd_curcpu;
86 	int bd_defseg;
87 
88 	/* must be last */
89 	char bd_name[];
90 } bhyve_data_t;
91 
92 
93 const mdb_tgt_regdesc_t bhyve_kregs[] = {
94 	{ "rdi",	KREG_RDI,	MDB_TGT_R_EXPORT },
95 	{ "edi",	KREG_RDI,	MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
96 	{ "di",		KREG_RDI,	MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
97 	{ "dil",	KREG_RDI,	MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
98 	{ "rsi",	KREG_RSI,	MDB_TGT_R_EXPORT },
99 	{ "esi",	KREG_RSI,	MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
100 	{ "si",		KREG_RSI,	MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
101 	{ "sil",	KREG_RSI,	MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
102 	{ "rdx",	KREG_RDX,	MDB_TGT_R_EXPORT },
103 	{ "edx",	KREG_RDX,	MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
104 	{ "dx",		KREG_RDX,	MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
105 	{ "dh",		KREG_RDX,	MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
106 	{ "dl",		KREG_RDX,	MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
107 	{ "rcx",	KREG_RCX,	MDB_TGT_R_EXPORT },
108 	{ "ecx",	KREG_RCX,	MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
109 	{ "cx",		KREG_RCX,	MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
110 	{ "ch",		KREG_RCX,	MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
111 	{ "cl",		KREG_RCX,	MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
112 	{ "r8",		KREG_R8,	MDB_TGT_R_EXPORT },
113 	{ "r8d",	KREG_R8,	MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
114 	{ "r8w",	KREG_R8,	MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
115 	{ "r8l",	KREG_R8,	MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
116 	{ "r9",		KREG_R9,	MDB_TGT_R_EXPORT },
117 	{ "r9d",	KREG_R8,	MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
118 	{ "r9w",	KREG_R8,	MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
119 	{ "r9l",	KREG_R8,	MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
120 	{ "rax",	KREG_RAX,	MDB_TGT_R_EXPORT },
121 	{ "eax",	KREG_RAX,	MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
122 	{ "ax",		KREG_RAX,	MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
123 	{ "ah",		KREG_RAX,	MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
124 	{ "al",		KREG_RAX,	MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
125 	{ "rbx",	KREG_RBX,	MDB_TGT_R_EXPORT },
126 	{ "ebx",	KREG_RBX,	MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
127 	{ "bx",		KREG_RBX,	MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
128 	{ "bh",		KREG_RBX,	MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
129 	{ "bl",		KREG_RBX,	MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
130 	{ "rbp",	KREG_RBP,	MDB_TGT_R_EXPORT },
131 	{ "ebp",	KREG_RBP,	MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
132 	{ "bp",		KREG_RBP,	MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
133 	{ "bpl",	KREG_RBP,	MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
134 	{ "r10",	KREG_R10,	MDB_TGT_R_EXPORT },
135 	{ "r10d",	KREG_R10,	MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
136 	{ "r10w",	KREG_R10,	MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
137 	{ "r10l",	KREG_R10,	MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
138 	{ "r11",	KREG_R11,	MDB_TGT_R_EXPORT },
139 	{ "r11d",	KREG_R11,	MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
140 	{ "r11w",	KREG_R11,	MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
141 	{ "r11l",	KREG_R11,	MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
142 	{ "r12",	KREG_R12,	MDB_TGT_R_EXPORT },
143 	{ "r12d",	KREG_R12,	MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
144 	{ "r12w",	KREG_R12,	MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
145 	{ "r12l",	KREG_R12,	MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
146 	{ "r13",	KREG_R13,	MDB_TGT_R_EXPORT },
147 	{ "r13d",	KREG_R13,	MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
148 	{ "r13w",	KREG_R13,	MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
149 	{ "r13l",	KREG_R13,	MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
150 	{ "r14",	KREG_R14,	MDB_TGT_R_EXPORT },
151 	{ "r14d",	KREG_R14,	MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
152 	{ "r14w",	KREG_R14,	MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
153 	{ "r14l",	KREG_R14,	MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
154 	{ "r15",	KREG_R15,	MDB_TGT_R_EXPORT },
155 	{ "r15d",	KREG_R15,	MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
156 	{ "r15w",	KREG_R15,	MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
157 	{ "r15l",	KREG_R15,	MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
158 	{ "ds",		KREG_DS,	MDB_TGT_R_EXPORT },
159 	{ "es",		KREG_ES,	MDB_TGT_R_EXPORT },
160 	{ "fs",		KREG_FS,	MDB_TGT_R_EXPORT },
161 	{ "gs",		KREG_GS,	MDB_TGT_R_EXPORT },
162 	{ "rip",	KREG_RIP,	MDB_TGT_R_EXPORT },
163 	{ "cs",		KREG_CS,	MDB_TGT_R_EXPORT },
164 	{ "rflags",	KREG_RFLAGS,	MDB_TGT_R_EXPORT },
165 	{ "eflags",	KREG_RFLAGS,	MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
166 	{ "rsp",	KREG_RSP,	MDB_TGT_R_EXPORT },
167 	{ "esp",	KREG_RSP,	MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
168 	{ "sp",		KREG_RSP,	MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
169 	{ "spl",	KREG_RSP,	MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
170 	{ "ss",		KREG_SS,	MDB_TGT_R_EXPORT },
171 	{ "cr2",	KREG_CR2,	MDB_TGT_R_EXPORT },
172 	{ "cr3",	KREG_CR3,	MDB_TGT_R_EXPORT },
173 	{ NULL, 0, 0 }
174 };
175 
176 static const char *segments[] = { "CS", "DS", "ES", "FS", "GS", "SS" };
177 
178 
179 /*ARGSUSED*/
180 static uintmax_t
bhyve_cpuid_get(const mdb_var_t * v)181 bhyve_cpuid_get(const mdb_var_t *v)
182 {
183 	bhyve_data_t *bd = mdb.m_target->t_data;
184 
185 	return (bd->bd_curcpu);
186 }
187 
188 static const mdb_nv_disc_t bhyve_cpuid_disc = {
189 	.disc_get = bhyve_cpuid_get
190 };
191 
192 
193 static uintmax_t
bhyve_reg_get(const mdb_var_t * v)194 bhyve_reg_get(const mdb_var_t *v)
195 {
196 	mdb_tgt_reg_t r = 0;
197 
198 	if (mdb_tgt_getareg(MDB_NV_COOKIE(v), 0, mdb_nv_get_name(v), &r) == -1)
199 		mdb_warn("failed to get %%%s register", mdb_nv_get_name(v));
200 
201 	return (r);
202 }
203 
204 static void
bhyve_reg_set(mdb_var_t * v,uintmax_t r)205 bhyve_reg_set(mdb_var_t *v, uintmax_t r)
206 {
207 	if (mdb_tgt_putareg(MDB_NV_COOKIE(v), 0, mdb_nv_get_name(v), r) == -1)
208 		mdb_warn("failed to modify %%%s register", mdb_nv_get_name(v));
209 }
210 
211 static const mdb_nv_disc_t bhyve_reg_disc = {
212 	.disc_set = bhyve_reg_set,
213 	.disc_get = bhyve_reg_get
214 };
215 
216 static int
bhyve_get_gregset(bhyve_data_t * bd,int cpu,mdb_tgt_gregset_t * gregs)217 bhyve_get_gregset(bhyve_data_t *bd, int cpu, mdb_tgt_gregset_t *gregs)
218 {
219 	vmm_desc_t fs, gs;
220 
221 	/*
222 	 * Register numbers to get, the order must match the definitions of
223 	 * KREG_* in mdb_kreg.h so that we get a proper mdb_tgt_gregset_t
224 	 * that the register printing functions will understand.
225 	 *
226 	 * There are a few fields in mdb_tgt_gregset_t that can't be accessed
227 	 * with vmm_get_regset(), either because they don't exist in bhyve or
228 	 * or because they need to be accessed with vmm_get_desc(). For these
229 	 * cases we ask for RAX instead and fill it with 0 or the real value,
230 	 * respectively.
231 	 */
232 	static const int regnums[] = {
233 		KREG_RAX, /* dummy for SAVFP */
234 		KREG_RAX, /* dummy for SAVFP */
235 		KREG_RDI,
236 		KREG_RSI,
237 		KREG_RDX,
238 		KREG_RCX,
239 		KREG_R8,
240 		KREG_R9,
241 		KREG_RAX,
242 		KREG_RBX,
243 		KREG_RBP,
244 		KREG_R10,
245 		KREG_R11,
246 		KREG_R12,
247 		KREG_R13,
248 		KREG_R14,
249 		KREG_R15,
250 		KREG_RAX, /* dummy for FSBASE */
251 		KREG_RAX, /* dummy for GSBASE */
252 		KREG_RAX, /* dummy for KGSBASE */
253 		KREG_CR2,
254 		KREG_CR3,
255 		KREG_DS,
256 		KREG_ES,
257 		KREG_FS,
258 		KREG_GS,
259 		KREG_RAX, /* dummy for TRAPNO */
260 		KREG_RAX, /* dummy for ERR */
261 		KREG_RIP,
262 		KREG_CS,
263 		KREG_RFLAGS,
264 		KREG_RSP,
265 		KREG_SS
266 	};
267 
268 	if (vmm_get_regset(bd->bd_vmm, cpu, KREG_NGREG, regnums,
269 	    &gregs->kregs[0]) != 0) {
270 		mdb_warn("failed to get general-purpose registers for CPU %d",
271 		    cpu);
272 		return (-1);
273 	}
274 
275 	if (vmm_get_desc(bd->bd_vmm, cpu, VMM_DESC_FS, &fs) != 0 ||
276 	    vmm_get_desc(bd->bd_vmm, cpu, VMM_DESC_GS, &gs) != 0) {
277 		mdb_warn("failed to get FS/GS descriptors for CPU %d", cpu);
278 		return (-1);
279 	}
280 
281 	gregs->kregs[KREG_SAVFP] = 0;
282 	gregs->kregs[KREG_SAVPC] = 0;
283 	gregs->kregs[KREG_KGSBASE] = 0;
284 	gregs->kregs[KREG_TRAPNO] = 0;
285 	gregs->kregs[KREG_ERR] = 0;
286 
287 	gregs->kregs[KREG_FSBASE] = fs.vd_base;
288 	gregs->kregs[KREG_GSBASE] = gs.vd_base;
289 
290 	return (0);
291 }
292 
293 static int
bhyve_cpuregs_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)294 bhyve_cpuregs_dcmd(uintptr_t addr, uint_t flags, int argc,
295     const mdb_arg_t *argv)
296 {
297 	bhyve_data_t *bd = mdb.m_target->t_data;
298 	uint64_t cpu = bd->bd_curcpu;
299 	mdb_tgt_gregset_t gregs;
300 	int i;
301 
302 
303 	if (flags & DCMD_ADDRSPEC) {
304 		if (argc != 0)
305 			return (DCMD_USAGE);
306 
307 		cpu = (uint64_t)addr;
308 	}
309 
310 	i = mdb_getopts(argc, argv, 'c', MDB_OPT_UINT64, &cpu, NULL);
311 
312 	argc -= i;
313 	argv += i;
314 
315 	if (argc != 0)
316 		return (DCMD_USAGE);
317 
318 	if (cpu >= vmm_ncpu(bd->bd_vmm)) {
319 		mdb_warn("no such CPU\n");
320 		return (DCMD_ERR);
321 	}
322 
323 	if (bhyve_get_gregset(bd, cpu, &gregs) != 0)
324 		return (DCMD_ERR);
325 
326 
327 	switch (vmm_vcpu_isa(bd->bd_vmm, cpu)) {
328 	case VMM_ISA_64:
329 		mdb_amd64_printregs(&gregs);
330 		break;
331 	case VMM_ISA_32:
332 	case VMM_ISA_16:
333 		mdb_ia32_printregs(&gregs);
334 		break;
335 	default:
336 		mdb_warn("CPU %d mode unknown", cpu);
337 		return (DCMD_ERR);
338 	}
339 
340 	return (0);
341 }
342 
343 static int
bhyve_regs_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)344 bhyve_regs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
345 {
346 	if ((flags & DCMD_ADDRSPEC) || argc != 0)
347 		return (DCMD_USAGE);
348 
349 	return (bhyve_cpuregs_dcmd(addr, flags, argc, argv));
350 }
351 
352 static int
bhyve_stack_common(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv,int vcpu,mdb_stack_frame_flags_t sflags)353 bhyve_stack_common(uintptr_t addr, uint_t flags, int argc,
354     const mdb_arg_t *argv, int vcpu, mdb_stack_frame_flags_t sflags)
355 {
356 	bhyve_data_t *bd = mdb.m_target->t_data;
357 	mdb_stack_frame_hdl_t *hdl;
358 	mdb_tgt_t *t = mdb.m_target;
359 	uint_t arglim = mdb.m_nargs;
360 	int ret = DCMD_OK;
361 	int i;
362 
363 	mdb_tgt_gregset_t gregs;
364 
365 	if (vcpu == -1) {
366 		vcpu = bd->bd_curcpu;
367 	} else if (vcpu >= vmm_ncpu(bd->bd_vmm)) {
368 		mdb_warn("no such CPU\n");
369 		return (DCMD_ERR);
370 	}
371 
372 	if (flags & DCMD_ADDRSPEC) {
373 		bzero(&gregs, sizeof (gregs));
374 		gregs.kregs[KREG_RBP] = addr;
375 	} else if (bhyve_get_gregset(bd, vcpu, &gregs) != 0)
376 		return (DCMD_ERR);
377 
378 	i = mdb_getopts(argc, argv,
379 	    'n', MDB_OPT_SETBITS, MSF_ADDR, &sflags,
380 	    's', MDB_OPT_SETBITS, MSF_SIZES, &sflags,
381 	    't', MDB_OPT_SETBITS, MSF_TYPES, &sflags,
382 	    'v', MDB_OPT_SETBITS, MSF_VERBOSE, &sflags,
383 	    NULL);
384 
385 	argc -= i;
386 	argv += i;
387 
388 	if ((hdl = mdb_stack_frame_init(t, arglim, sflags)) == NULL) {
389 		mdb_warn("failed to init stack frame\n");
390 		return (DCMD_ERR);
391 	}
392 
393 	switch (vmm_vcpu_isa(bd->bd_vmm, vcpu)) {
394 	case VMM_ISA_32:
395 	case VMM_ISA_64:
396 		(void) mdb_isa_kvm_stack_iter(mdb.m_target, &gregs,
397 		    mdb_isa_kvm_frame, (void *)hdl);
398 		break;
399 	case VMM_ISA_16:
400 		mdb_warn("IA16 stack tracing not implemented\n");
401 		ret = DCMD_ERR;
402 		break;
403 	default:
404 		mdb_warn("CPU %d mode unknown", vcpu);
405 		ret = DCMD_ERR;
406 		break;
407 	}
408 
409 	return (ret);
410 }
411 
412 static int
bhyve_cpustack_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)413 bhyve_cpustack_dcmd(uintptr_t addr, uint_t flags, int argc,
414     const mdb_arg_t *argv)
415 {
416 	bhyve_data_t *bd = mdb.m_target->t_data;
417 	uint64_t cpu = bd->bd_curcpu;
418 	boolean_t verbose;
419 	int i;
420 
421 	if (flags & DCMD_ADDRSPEC) {
422 		if (argc != 0)
423 			return (DCMD_USAGE);
424 
425 		if (addr < vmm_ncpu(bd->bd_vmm)) {
426 			cpu = (uint64_t)addr;
427 			flags &= ~DCMD_ADDRSPEC;
428 		}
429 	}
430 
431 	i = mdb_getopts(argc, argv,
432 	    'c', MDB_OPT_UINT64, &cpu,
433 	    'v', MDB_OPT_SETBITS, 1, &verbose,
434 	    NULL);
435 
436 	argc -= i;
437 	argv += i;
438 
439 	if (argc != 0)
440 		return (DCMD_USAGE);
441 
442 	return (bhyve_stack_common(addr, flags, argc, argv, cpu,
443 	    verbose ? MSF_VERBOSE : 0));
444 }
445 
446 static int
bhyve_stack_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)447 bhyve_stack_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
448 {
449 	return (bhyve_stack_common(addr, flags, argc, argv, -1, 0));
450 }
451 
452 static int
bhyve_stackv_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)453 bhyve_stackv_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
454 {
455 	return (bhyve_stack_common(addr, flags, argc, argv, -1, MSF_VERBOSE));
456 }
457 
458 static int
bhyve_stackr_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)459 bhyve_stackr_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
460 {
461 	return (bhyve_stack_common(addr, flags, argc, argv, -1, MSF_VERBOSE));
462 }
463 
464 static int
bhyve_status_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)465 bhyve_status_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
466 {
467 	bhyve_data_t *bd = mdb.m_target->t_data;
468 	vmm_mode_t mode;
469 	vmm_isa_t isa;
470 
471 	static const char *modes[] = {
472 		"unknown mode",
473 		"real mode",
474 		"protected mode, no PAE",
475 		"protected mode, PAE",
476 		"long mode"
477 	};
478 	static const char *isas[] = {
479 		"unknown ISA",
480 		"IA16",
481 		"IA32",
482 		"AMD64"
483 	};
484 
485 	if ((flags & DCMD_ADDRSPEC) || argc != 0)
486 		return (DCMD_USAGE);
487 
488 	mode = vmm_vcpu_mode(bd->bd_vmm, bd->bd_curcpu);
489 	isa = vmm_vcpu_isa(bd->bd_vmm, bd->bd_curcpu);
490 
491 	mdb_printf("debugging live VM '%s'\n", bd->bd_name);
492 	mdb_printf("VM memory size: %d MB\n",
493 	    vmm_memsize(bd->bd_vmm) / 1024 / 1024);
494 	mdb_printf("vCPUs: %d\n", vmm_ncpu(bd->bd_vmm));
495 	mdb_printf("current CPU: %d (%s, %s)\n", bd->bd_curcpu, modes[mode],
496 	    isas[isa]);
497 	mdb_printf("default segment: %s",
498 	    segments[bd->bd_defseg - VMM_DESC_CS]);
499 
500 	return (DCMD_OK);
501 }
502 
503 
504 static int
bhyve_sysregs_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)505 bhyve_sysregs_dcmd(uintptr_t addr, uint_t flags, int argc,
506     const mdb_arg_t *argv)
507 {
508 	bhyve_data_t *bd = mdb.m_target->t_data;
509 	uint64_t cpu = bd->bd_curcpu;
510 	int ret = DCMD_ERR;
511 	struct sysregs sregs;
512 	int i;
513 
514 	/*
515 	 * This array must use the order of the elements of struct sysregs.
516 	 */
517 	static const int regnums[] = {
518 		VMM_REG_CR0,
519 		VMM_REG_CR2,
520 		VMM_REG_CR3,
521 		VMM_REG_CR4,
522 		VMM_REG_DR0,
523 		VMM_REG_DR1,
524 		VMM_REG_DR2,
525 		VMM_REG_DR3,
526 		VMM_REG_DR6,
527 		VMM_REG_DR7,
528 		VMM_REG_EFER,
529 		VMM_REG_PDPTE0,
530 		VMM_REG_PDPTE1,
531 		VMM_REG_PDPTE2,
532 		VMM_REG_PDPTE3,
533 		VMM_REG_INTR_SHADOW
534 	};
535 
536 	if (flags & DCMD_ADDRSPEC) {
537 		if (argc != 0)
538 			return (DCMD_USAGE);
539 
540 		cpu = (uint64_t)addr;
541 	}
542 
543 	i = mdb_getopts(argc, argv, 'c', MDB_OPT_UINT64, &cpu, NULL);
544 
545 	argc -= i;
546 	argv += i;
547 
548 	if (argc != 0)
549 		return (DCMD_USAGE);
550 
551 	if (cpu >= vmm_ncpu(bd->bd_vmm)) {
552 		mdb_warn("no such CPU\n");
553 		return (DCMD_ERR);
554 	}
555 
556 	if (vmm_get_regset(bd->bd_vmm, cpu, ARRAY_SIZE(regnums), regnums,
557 	    (uint64_t *)&sregs) != 0)
558 		goto fail;
559 
560 	if (vmm_get_desc(bd->bd_vmm, cpu, VMM_DESC_GDTR,
561 	    (vmm_desc_t *)&sregs.sr_gdtr) != 0 ||
562 	    vmm_get_desc(bd->bd_vmm, cpu, VMM_DESC_IDTR,
563 	    (vmm_desc_t *)&sregs.sr_idtr) != 0 ||
564 	    vmm_get_desc(bd->bd_vmm, cpu, VMM_DESC_LDTR,
565 	    (vmm_desc_t *)&sregs.sr_ldtr) != 0 ||
566 	    vmm_get_desc(bd->bd_vmm, cpu, VMM_DESC_TR,
567 	    (vmm_desc_t *)&sregs.sr_tr) != 0 ||
568 	    vmm_get_desc(bd->bd_vmm, cpu, VMM_DESC_CS,
569 	    (vmm_desc_t *)&sregs.sr_cs) != 0 ||
570 	    vmm_get_desc(bd->bd_vmm, cpu, VMM_DESC_DS,
571 	    (vmm_desc_t *)&sregs.sr_ds) != 0 ||
572 	    vmm_get_desc(bd->bd_vmm, cpu, VMM_DESC_ES,
573 	    (vmm_desc_t *)&sregs.sr_es) != 0 ||
574 	    vmm_get_desc(bd->bd_vmm, cpu, VMM_DESC_FS,
575 	    (vmm_desc_t *)&sregs.sr_fs) != 0 ||
576 	    vmm_get_desc(bd->bd_vmm, cpu, VMM_DESC_GS,
577 	    (vmm_desc_t *)&sregs.sr_gs) != 0 ||
578 	    vmm_get_desc(bd->bd_vmm, cpu, VMM_DESC_SS,
579 	    (vmm_desc_t *)&sregs.sr_ss) != 0)
580 		goto fail;
581 
582 	mdb_x86_print_sysregs(&sregs, vmm_vcpu_mode(bd->bd_vmm, cpu) ==
583 	    VMM_MODE_LONG);
584 
585 	ret = DCMD_OK;
586 
587 fail:
588 	if (ret != DCMD_OK)
589 		mdb_warn("failed to get system registers for CPU %d\n", cpu);
590 	return (ret);
591 }
592 
593 static int
bhyve_dbgregs_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)594 bhyve_dbgregs_dcmd(uintptr_t addr, uint_t flags, int argc,
595     const mdb_arg_t *argv)
596 {
597 	bhyve_data_t *bd = mdb.m_target->t_data;
598 	uint64_t cpu = bd->bd_curcpu;
599 	int ret = DCMD_ERR;
600 	vmm_desc_t gdtr, ldtr, idtr, tr, cs, ds, es, fs, gs, ss;
601 	uint64_t *regvals;
602 	int i;
603 
604 	/*
605 	 * This array must use the order of definitions set in libvmm.h
606 	 * to make GETREG() work.
607 	 */
608 #define	GETREG(r)	(regvals[r - VMM_REG_DR0])
609 	static const int regnums[] = {
610 		VMM_REG_DR0,
611 		VMM_REG_DR1,
612 		VMM_REG_DR2,
613 		VMM_REG_DR3,
614 		VMM_REG_DR6,
615 		VMM_REG_DR7,
616 	};
617 
618 	static const mdb_bitmask_t dr6_flag_bits[] = {
619 		{ "DR0",	DR_TRAP0,	DR_TRAP0 },
620 		{ "DR1",	DR_TRAP1,	DR_TRAP1 },
621 		{ "DR2",	DR_TRAP2,	DR_TRAP2 },
622 		{ "DR3",	DR_TRAP3,	DR_TRAP3 },
623 		{ "debug reg",	DR_ICEALSO,	DR_ICEALSO },
624 		{ "single step", DR_SINGLESTEP,	DR_SINGLESTEP },
625 		{ "task switch", DR_TASKSWITCH,	DR_TASKSWITCH },
626 		{ NULL,		0,		0 }
627 	};
628 
629 #define	DR_RW(x, m)	\
630 	((DR_RW_MASK & (m)) << (DR_CONTROL_SHIFT + (x) * DR_CONTROL_SIZE))
631 #define	DR_LEN(x, m)	\
632 	((DR_LEN_MASK & (m)) << (DR_CONTROL_SHIFT + (x) * DR_CONTROL_SIZE))
633 
634 	static const mdb_bitmask_t dr7_flag_bits[] = {
635 		{ "L0",   DR_ENABLE0,	DR_LOCAL_ENABLE_MASK & DR_ENABLE0 },
636 		{ "G0",   DR_ENABLE0,	DR_GLOBAL_ENABLE_MASK & DR_ENABLE0 },
637 		{ "L1",   DR_ENABLE1,	DR_LOCAL_ENABLE_MASK & DR_ENABLE1 },
638 		{ "G1",   DR_ENABLE1,	DR_GLOBAL_ENABLE_MASK & DR_ENABLE1 },
639 		{ "L2",   DR_ENABLE2,	DR_LOCAL_ENABLE_MASK & DR_ENABLE2 },
640 		{ "G2",   DR_ENABLE2,	DR_GLOBAL_ENABLE_MASK & DR_ENABLE2 },
641 		{ "L3",   DR_ENABLE3,	DR_LOCAL_ENABLE_MASK & DR_ENABLE3 },
642 		{ "G3",   DR_ENABLE3,	DR_GLOBAL_ENABLE_MASK & DR_ENABLE3 },
643 		{ "LE",   DR_LOCAL_SLOWDOWN,	DR_LOCAL_SLOWDOWN },
644 		{ "GE",   DR_GLOBAL_SLOWDOWN,	DR_GLOBAL_SLOWDOWN },
645 		{ "RTM",  DR_RTM,		DR_RTM },
646 		{ "GD",   DR_GENERAL_DETECT,	DR_GENERAL_DETECT },
647 		{ "0:X",  DR_RW(0, DR_RW_MASK),   DR_RW(0, DR_RW_EXECUTE) },
648 		{ "0:W",  DR_RW(0, DR_RW_MASK),   DR_RW(0, DR_RW_WRITE) },
649 		{ "0:IO", DR_RW(0, DR_RW_MASK),   DR_RW(0, DR_RW_IO_RW) },
650 		{ "0:RW", DR_RW(0, DR_RW_MASK),   DR_RW(0, DR_RW_READ) },
651 		{ "1:X",  DR_RW(1, DR_RW_MASK),   DR_RW(1, DR_RW_EXECUTE) },
652 		{ "1:W",  DR_RW(1, DR_RW_MASK),   DR_RW(1, DR_RW_WRITE) },
653 		{ "1:IO", DR_RW(1, DR_RW_MASK),   DR_RW(1, DR_RW_IO_RW) },
654 		{ "1:RW", DR_RW(1, DR_RW_MASK),   DR_RW(1, DR_RW_READ) },
655 		{ "2:X",  DR_RW(2, DR_RW_MASK),   DR_RW(2, DR_RW_EXECUTE) },
656 		{ "2:W",  DR_RW(2, DR_RW_MASK),   DR_RW(2, DR_RW_WRITE) },
657 		{ "2:IO", DR_RW(2, DR_RW_MASK),   DR_RW(2, DR_RW_IO_RW) },
658 		{ "2:RW", DR_RW(2, DR_RW_MASK),   DR_RW(2, DR_RW_READ) },
659 		{ "3:X",  DR_RW(3, DR_RW_MASK),   DR_RW(3, DR_RW_EXECUTE) },
660 		{ "3:W",  DR_RW(3, DR_RW_MASK),   DR_RW(3, DR_RW_WRITE) },
661 		{ "3:IO", DR_RW(3, DR_RW_MASK),   DR_RW(3, DR_RW_IO_RW) },
662 		{ "3:RW", DR_RW(3, DR_RW_MASK),   DR_RW(3, DR_RW_READ) },
663 		{ "0:1",  DR_LEN(0, DR_LEN_MASK), DR_LEN(0, DR_LEN_1) },
664 		{ "0:2",  DR_LEN(0, DR_LEN_MASK), DR_LEN(0, DR_LEN_2) },
665 		{ "0:4",  DR_LEN(0, DR_LEN_MASK), DR_LEN(0, DR_LEN_4) },
666 		{ "0:8",  DR_LEN(0, DR_LEN_MASK), DR_LEN(0, DR_LEN_8) },
667 		{ "1:1",  DR_LEN(1, DR_LEN_MASK), DR_LEN(1, DR_LEN_1) },
668 		{ "1:2",  DR_LEN(1, DR_LEN_MASK), DR_LEN(1, DR_LEN_2) },
669 		{ "1:4",  DR_LEN(1, DR_LEN_MASK), DR_LEN(1, DR_LEN_4) },
670 		{ "1:8",  DR_LEN(1, DR_LEN_MASK), DR_LEN(1, DR_LEN_8) },
671 		{ "2:1",  DR_LEN(2, DR_LEN_MASK), DR_LEN(2, DR_LEN_1) },
672 		{ "2:2",  DR_LEN(2, DR_LEN_MASK), DR_LEN(2, DR_LEN_2) },
673 		{ "2:4",  DR_LEN(2, DR_LEN_MASK), DR_LEN(2, DR_LEN_4) },
674 		{ "2:8",  DR_LEN(2, DR_LEN_MASK), DR_LEN(2, DR_LEN_8) },
675 		{ "3:1",  DR_LEN(3, DR_LEN_MASK), DR_LEN(3, DR_LEN_1) },
676 		{ "3:2",  DR_LEN(3, DR_LEN_MASK), DR_LEN(3, DR_LEN_2) },
677 		{ "3:4",  DR_LEN(3, DR_LEN_MASK), DR_LEN(3, DR_LEN_4) },
678 		{ "3:8",  DR_LEN(3, DR_LEN_MASK), DR_LEN(3, DR_LEN_8) },
679 		{ NULL, 0, 0 },
680 	};
681 
682 
683 	if (flags & DCMD_ADDRSPEC) {
684 		if (argc != 0)
685 			return (DCMD_USAGE);
686 
687 		cpu = (uint64_t)addr;
688 	}
689 
690 	i = mdb_getopts(argc, argv, 'c', MDB_OPT_UINT64, &cpu, NULL);
691 
692 	argc -= i;
693 	argv += i;
694 
695 	if (argc != 0)
696 		return (DCMD_USAGE);
697 
698 	if (cpu >= vmm_ncpu(bd->bd_vmm)) {
699 		mdb_warn("no such CPU\n");
700 		return (DCMD_ERR);
701 	}
702 
703 	regvals = mdb_zalloc(ARRAY_SIZE(regnums) * sizeof (uint64_t), UM_SLEEP);
704 
705 	if (vmm_get_regset(bd->bd_vmm, cpu, ARRAY_SIZE(regnums), regnums,
706 	    regvals) != 0)
707 		goto fail;
708 
709 	mdb_printf("%%dr0 = 0x%0?p %A\n",
710 	    GETREG(VMM_REG_DR0), GETREG(VMM_REG_DR0));
711 	mdb_printf("%%dr1 = 0x%0?p %A\n",
712 	    GETREG(VMM_REG_DR1), GETREG(VMM_REG_DR1));
713 	mdb_printf("%%dr2 = 0x%0?p %A\n",
714 	    GETREG(VMM_REG_DR2), GETREG(VMM_REG_DR2));
715 	mdb_printf("%%dr3 = 0x%0?p %A\n",
716 	    GETREG(VMM_REG_DR3), GETREG(VMM_REG_DR3));
717 	mdb_printf("%%dr6 = 0x%0lx <%b>\n",
718 	    GETREG(VMM_REG_DR6), GETREG(VMM_REG_DR6), dr6_flag_bits);
719 	mdb_printf("%%dr7 = 0x%0lx <%b>\n",
720 	    GETREG(VMM_REG_DR7), GETREG(VMM_REG_DR7), dr7_flag_bits);
721 #undef GETREG
722 
723 	ret = DCMD_OK;
724 
725 fail:
726 	if (ret != DCMD_OK)
727 		mdb_warn("failed to get debug registers for CPU %d\n", cpu);
728 	mdb_free(regvals, ARRAY_SIZE(regnums) * sizeof (uint64_t));
729 	return (ret);
730 }
731 
732 static int
bhyve_switch_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)733 bhyve_switch_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
734 {
735 	bhyve_data_t *bd = mdb.m_target->t_data;
736 	size_t cpu = (int)addr;
737 
738 	if (!(flags & DCMD_ADDRSPEC) || argc != 0)
739 		return (DCMD_USAGE);
740 
741 	if (cpu >= vmm_ncpu(bd->bd_vmm)) {
742 		mdb_warn("no such CPU\n");
743 		return (DCMD_ERR);
744 	}
745 
746 	bd->bd_curcpu = cpu;
747 	return (DCMD_OK);
748 
749 }
750 
751 static int
bhyve_seg2reg(const char * seg)752 bhyve_seg2reg(const char *seg)
753 {
754 	if (strcasecmp(seg, "cs") == 0)
755 		return (VMM_DESC_CS);
756 	else if (strcasecmp(seg, "ds") == 0)
757 		return (VMM_DESC_DS);
758 	else if (strcasecmp(seg, "es") == 0)
759 		return (VMM_DESC_ES);
760 	else if (strcasecmp(seg, "fs") == 0)
761 		return (VMM_DESC_FS);
762 	else if (strcasecmp(seg, "gs") == 0)
763 		return (VMM_DESC_GS);
764 	else if (strcasecmp(seg, "ss") == 0)
765 		return (VMM_DESC_SS);
766 	else
767 		return (-1);
768 }
769 
770 static int
bhyve_vtol_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)771 bhyve_vtol_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
772 {
773 	bhyve_data_t *bd = mdb.m_target->t_data;
774 	int segreg = bd->bd_defseg;
775 	char *seg = "";
776 	uint64_t laddr;
777 	int i;
778 
779 	if (!(flags & DCMD_ADDRSPEC))
780 		return (DCMD_USAGE);
781 
782 	i = mdb_getopts(argc, argv, 's', MDB_OPT_STR, &seg, NULL);
783 
784 	argc -= i;
785 	argv += i;
786 
787 	if (i != 0) {
788 		if (argc != 0)
789 			return (DCMD_USAGE);
790 
791 		segreg = bhyve_seg2reg(seg);
792 		if (segreg == -1)
793 			return (DCMD_USAGE);
794 	}
795 
796 	if (vmm_vtol(bd->bd_vmm, bd->bd_curcpu, segreg, addr, &laddr) != 0) {
797 		if (errno == EFAULT)
798 			(void) set_errno(EMDB_NOMAP);
799 		return (DCMD_ERR);
800 	}
801 
802 	if (flags & DCMD_PIPE_OUT)
803 		mdb_printf("%llr\n", laddr);
804 	else
805 		mdb_printf("virtual %lr mapped to linear %llr\n", addr, laddr);
806 
807 	return (DCMD_OK);
808 }
809 
810 static int
bhyve_vtop_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)811 bhyve_vtop_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
812 {
813 	bhyve_data_t *bd = mdb.m_target->t_data;
814 	int segreg = bd->bd_defseg;
815 	char *seg = "";
816 	physaddr_t pa;
817 	int i;
818 
819 	if (!(flags & DCMD_ADDRSPEC))
820 		return (DCMD_USAGE);
821 
822 	i = mdb_getopts(argc, argv, 's', MDB_OPT_STR, &seg, NULL);
823 
824 	argc -= i;
825 	argv += i;
826 
827 	if (i != 0) {
828 		segreg = bhyve_seg2reg(seg);
829 		if (segreg == -1)
830 			return (DCMD_USAGE);
831 	}
832 
833 	if (vmm_vtop(bd->bd_vmm, bd->bd_curcpu, segreg, addr, &pa) == -1) {
834 		mdb_warn("failed to get physical mapping");
835 		return (DCMD_ERR);
836 	}
837 
838 	if (flags & DCMD_PIPE_OUT)
839 		mdb_printf("%llr\n", pa);
840 	else
841 		mdb_printf("virtual %lr mapped to physical %llr\n", addr, pa);
842 	return (DCMD_OK);
843 }
844 
845 static int
bhyve_defseg_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)846 bhyve_defseg_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
847 {
848 	bhyve_data_t *bd = mdb.m_target->t_data;
849 	int segreg = bd->bd_defseg;
850 	char *seg = "";
851 	int i;
852 
853 	if (flags & DCMD_ADDRSPEC)
854 		return (DCMD_USAGE);
855 
856 	i = mdb_getopts(argc, argv, 's', MDB_OPT_STR, &seg, NULL);
857 
858 	argc -= i;
859 	argv += i;
860 
861 	if (i != 0) {
862 		if (argc != 0)
863 			return (DCMD_USAGE);
864 
865 		segreg = bhyve_seg2reg(seg);
866 		if (segreg == -1)
867 			return (DCMD_USAGE);
868 
869 		bd->bd_defseg = segreg;
870 	}
871 
872 	mdb_printf("using segment %s for virtual to linear address translation",
873 	    segments[bd->bd_defseg - VMM_DESC_CS]);
874 
875 	return (DCMD_OK);
876 }
877 
878 static void
bhyve_stack_help(void)879 bhyve_stack_help(void)
880 {
881 	mdb_printf(
882 	    "Options:\n"
883 	    "  -n   do not resolve addresses to names\n"
884 	    "  -s   show the size of each stack frame to the left\n"
885 	    "  -t   where CTF is present, show types for functions and "
886 	    "arguments\n"
887 	    "  -v   include frame pointer information (this is the default "
888 	    "with %<b>$C%</b>)\n"
889 	    "\n"
890 	    "If the optional %<u>cnt%</u> is given, no more than %<u>cnt%</u> "
891 	    "arguments are shown\nfor each stack frame.\n");
892 }
893 
894 static const mdb_dcmd_t bhyve_dcmds[] = {
895 	{ "$c", "[-nstv]", "print stack backtrace", bhyve_stack_dcmd,
896 	    bhyve_stack_help },
897 	{ "$C", "[-nstv]", "print stack backtrace", bhyve_stackv_dcmd,
898 	    bhyve_stack_help },
899 	{ "$r", NULL, "print general-purpose registers", bhyve_regs_dcmd },
900 	{ "$?", NULL, "print status and registers", bhyve_regs_dcmd },
901 	{ ":x", ":", "change the active CPU", bhyve_switch_dcmd },
902 	{ "cpustack", "?[-v] [-c cpuid] [cnt]", "print stack backtrace for a "
903 	    "specific CPU", bhyve_cpustack_dcmd },
904 	{ "cpuregs", "?[-c cpuid]", "print general-purpose registers for a "
905 	    "specific CPU", bhyve_cpuregs_dcmd },
906 	{ "dbgregs", "?[-c cpuid]", "print debug registers for a specific CPU",
907 	    bhyve_dbgregs_dcmd },
908 	{ "defseg", "?[-s segment]", "change the default segment used to "
909 	    "translate addresses", bhyve_defseg_dcmd },
910 	{ "regs", NULL, "print general-purpose registers", bhyve_regs_dcmd },
911 	{ "stack", "[-nstv]", "print stack backtrace", bhyve_stack_dcmd,
912 	    bhyve_stack_help },
913 	{ "stackregs", "[-nstv]", "print stack backtrace and registers",
914 	    bhyve_stackr_dcmd, bhyve_stack_help },
915 	{ "status", NULL, "print summary of current target",
916 	    bhyve_status_dcmd },
917 	{ "sysregs", "?[-c cpuid]", "print system registers for a specific CPU",
918 	    bhyve_sysregs_dcmd },
919 	{ "switch", ":", "change the active CPU", bhyve_switch_dcmd },
920 	{ "vtol", ":[-s segment]", "print linear mapping of virtual address",
921 	    bhyve_vtol_dcmd },
922 	{ "vtop", ":[-s segment]", "print physical mapping of virtual "
923 	    "address", bhyve_vtop_dcmd },
924 	{ NULL }
925 };
926 
927 /*
928  * t_setflags: change target flags
929  */
930 static int
bhyve_setflags(mdb_tgt_t * tgt,int flags)931 bhyve_setflags(mdb_tgt_t *tgt, int flags)
932 {
933 	bhyve_data_t *bd = tgt->t_data;
934 
935 	if (((tgt->t_flags ^ flags) & MDB_TGT_F_RDWR) != 0) {
936 		boolean_t writable = (flags & MDB_TGT_F_RDWR) != 0;
937 
938 		vmm_unmap(bd->bd_vmm);
939 		if (vmm_map(bd->bd_vmm, writable) != 0) {
940 			mdb_warn("failed to map guest memory");
941 			return (set_errno(EMDB_TGT));
942 		}
943 	}
944 
945 	tgt->t_flags = flags;
946 
947 	return (0);
948 }
949 
950 /*
951  * t_activate: activate target
952  */
953 static void
bhyve_activate(mdb_tgt_t * tgt)954 bhyve_activate(mdb_tgt_t *tgt)
955 {
956 	mdb_tgt_status_t *tsp = &tgt->t_status;
957 	bhyve_data_t *bd = tgt->t_data;
958 	const char *format;
959 	char buf[BUFSIZ];
960 
961 	(void) mdb_set_prompt(MDB_DEF_PROMPT);
962 
963 	(void) mdb_tgt_register_dcmds(tgt, bhyve_dcmds, MDB_MOD_FORCE);
964 	mdb_tgt_register_regvars(tgt, bhyve_kregs, &bhyve_reg_disc, 0);
965 
966 	(void) vmm_stop(bd->bd_vmm);
967 
968 	if (mdb_tgt_status(tgt, tsp) != 0)
969 		return;
970 
971 	if (tsp->st_pc != 0) {
972 		if (mdb_dis_ins2str(mdb.m_disasm, mdb.m_target,
973 		    MDB_TGT_AS_VIRT_I, buf, sizeof (buf), tsp->st_pc) !=
974 		    tsp->st_pc)
975 			format = "target stopped at:\n%-#16a%8T%s\n";
976 		else
977 			format = "target stopped at %a:\n";
978 		mdb_warn(format, tsp->st_pc, buf);
979 	}
980 }
981 
982 /*
983  * t_deactivate: deactivate target
984  */
985 static void
bhyve_deactivate(mdb_tgt_t * tgt)986 bhyve_deactivate(mdb_tgt_t *tgt)
987 {
988 	bhyve_data_t *bd = tgt->t_data;
989 	const mdb_tgt_regdesc_t *rd;
990 	const mdb_dcmd_t *dc;
991 
992 	for (rd = bhyve_kregs; rd->rd_name != NULL; rd++) {
993 		mdb_var_t *var;
994 
995 		if (!(rd->rd_flags & MDB_TGT_R_EXPORT))
996 			continue; /* didn't export register as variable */
997 
998 		if ((var = mdb_nv_lookup(&mdb.m_nv, rd->rd_name)) != NULL) {
999 			var->v_flags &= ~MDB_NV_PERSIST;
1000 			mdb_nv_remove(&mdb.m_nv, var);
1001 		}
1002 	}
1003 
1004 	for (dc = bhyve_dcmds; dc->dc_name != NULL; dc++)
1005 		if (mdb_module_remove_dcmd(tgt->t_module, dc->dc_name) == -1)
1006 			mdb_warn("failed to remove dcmd %s", dc->dc_name);
1007 
1008 	(void) vmm_cont(bd->bd_vmm);
1009 }
1010 
1011 /*
1012  * t_name: return name of target
1013  */
1014 static const char *
bhyve_name(mdb_tgt_t * tgt)1015 bhyve_name(mdb_tgt_t *tgt)
1016 {
1017 	_NOTE(ARGUNUSED(tgt));
1018 
1019 	return ("bhyve");
1020 }
1021 
1022 /*
1023  * t_destroy: cleanup target private resources
1024  */
1025 static void
bhyve_destroy(mdb_tgt_t * tgt)1026 bhyve_destroy(mdb_tgt_t *tgt)
1027 {
1028 	bhyve_data_t *bd = tgt->t_data;
1029 
1030 	(void) vmm_cont(bd->bd_vmm);
1031 	vmm_unmap(bd->bd_vmm);
1032 	vmm_close_vm(bd->bd_vmm);
1033 	mdb_free(bd, sizeof (bhyve_data_t));
1034 	tgt->t_data = NULL;
1035 }
1036 
1037 /*
1038  * t_isa: return name of target ISA
1039  */
1040 const char *
bhyve_isa(mdb_tgt_t * tgt)1041 bhyve_isa(mdb_tgt_t *tgt)
1042 {
1043 	_NOTE(ARGUNUSED(tgt));
1044 
1045 	return ("amd64");
1046 }
1047 
1048 /*
1049  * t_dmodel: return target data model
1050  */
1051 static int
bhyve_dmodel(mdb_tgt_t * tgt)1052 bhyve_dmodel(mdb_tgt_t *tgt)
1053 {
1054 	_NOTE(ARGUNUSED(tgt));
1055 
1056 	return (MDB_TGT_MODEL_LP64);
1057 }
1058 
1059 /*ARGSUSED*/
1060 static ssize_t
bhyve_aread(mdb_tgt_t * tgt,mdb_tgt_as_t as,void * buf,size_t nbytes,mdb_tgt_addr_t addr)1061 bhyve_aread(mdb_tgt_t *tgt, mdb_tgt_as_t as, void *buf, size_t nbytes,
1062     mdb_tgt_addr_t addr)
1063 {
1064 	bhyve_data_t *bd = tgt->t_data;
1065 	ssize_t cnt = 0;
1066 
1067 	switch ((uintptr_t)as) {
1068 	case (uintptr_t)MDB_TGT_AS_VIRT:
1069 		cnt = vmm_vread(bd->bd_vmm, bd->bd_curcpu, bd->bd_defseg, buf,
1070 		    nbytes, addr);
1071 		break;
1072 
1073 	case (uintptr_t)MDB_TGT_AS_VIRT_I:
1074 		cnt = vmm_vread(bd->bd_vmm, bd->bd_curcpu, VMM_DESC_CS, buf,
1075 		    nbytes, addr);
1076 		break;
1077 
1078 	case (uintptr_t)MDB_TGT_AS_VIRT_S:
1079 		cnt = vmm_vread(bd->bd_vmm, bd->bd_curcpu, VMM_DESC_SS, buf,
1080 		    nbytes, addr);
1081 		break;
1082 
1083 	case (uintptr_t)MDB_TGT_AS_PHYS:
1084 		cnt = vmm_pread(bd->bd_vmm, buf, nbytes, addr);
1085 		break;
1086 
1087 	case (uintptr_t)MDB_TGT_AS_FILE:
1088 	case (uintptr_t)MDB_TGT_AS_IO:
1089 		return (set_errno(EMDB_TGTNOTSUP));
1090 	}
1091 
1092 	if (errno == EFAULT)
1093 		return (set_errno(EMDB_NOMAP));
1094 
1095 	return (cnt);
1096 }
1097 
1098 /*ARGSUSED*/
1099 static ssize_t
bhyve_awrite(mdb_tgt_t * tgt,mdb_tgt_as_t as,const void * buf,size_t nbytes,mdb_tgt_addr_t addr)1100 bhyve_awrite(mdb_tgt_t *tgt, mdb_tgt_as_t as, const void *buf, size_t nbytes,
1101     mdb_tgt_addr_t addr)
1102 {
1103 	bhyve_data_t *bd = tgt->t_data;
1104 	ssize_t cnt = 0;
1105 
1106 	switch ((uintptr_t)as) {
1107 	case (uintptr_t)MDB_TGT_AS_VIRT:
1108 		cnt = vmm_vwrite(bd->bd_vmm, bd->bd_curcpu, bd->bd_defseg, buf,
1109 		    nbytes, addr);
1110 		break;
1111 
1112 	case (uintptr_t)MDB_TGT_AS_VIRT_I:
1113 		cnt = vmm_vwrite(bd->bd_vmm, bd->bd_curcpu, VMM_DESC_CS, buf,
1114 		    nbytes, addr);
1115 		break;
1116 
1117 	case (uintptr_t)MDB_TGT_AS_VIRT_S:
1118 		cnt = vmm_vwrite(bd->bd_vmm, bd->bd_curcpu, VMM_DESC_SS, buf,
1119 		    nbytes, addr);
1120 		break;
1121 
1122 	case (uintptr_t)MDB_TGT_AS_PHYS:
1123 		cnt = vmm_pwrite(bd->bd_vmm, buf, nbytes, addr);
1124 		break;
1125 
1126 	case (uintptr_t)MDB_TGT_AS_FILE:
1127 	case (uintptr_t)MDB_TGT_AS_IO:
1128 		return (set_errno(EMDB_TGTNOTSUP));
1129 	}
1130 
1131 	if (errno == EFAULT)
1132 		return (set_errno(EMDB_NOMAP));
1133 
1134 	return (cnt);
1135 }
1136 
1137 /*
1138  * t_vread: read from virtual memory
1139  */
1140 /*ARGSUSED*/
1141 static ssize_t
bhyve_vread(mdb_tgt_t * tgt,void * buf,size_t nbytes,uintptr_t addr)1142 bhyve_vread(mdb_tgt_t *tgt, void *buf, size_t nbytes, uintptr_t addr)
1143 {
1144 	return (bhyve_aread(tgt, MDB_TGT_AS_VIRT, buf, nbytes, addr));
1145 }
1146 
1147 /*
1148  * t_vwrite: write to virtual memory
1149  */
1150 /*ARGSUSED*/
1151 static ssize_t
bhyve_vwrite(mdb_tgt_t * tgt,const void * buf,size_t nbytes,uintptr_t addr)1152 bhyve_vwrite(mdb_tgt_t *tgt, const void *buf, size_t nbytes, uintptr_t addr)
1153 {
1154 	return (bhyve_awrite(tgt, MDB_TGT_AS_VIRT, buf, nbytes, addr));
1155 }
1156 
1157 /*
1158  * t_pread: read from physical memory
1159  */
1160 /*ARGSUSED*/
1161 static ssize_t
bhyve_pread(mdb_tgt_t * tgt,void * buf,size_t nbytes,physaddr_t addr)1162 bhyve_pread(mdb_tgt_t *tgt, void *buf, size_t nbytes, physaddr_t addr)
1163 {
1164 	return (bhyve_aread(tgt, MDB_TGT_AS_PHYS, buf, nbytes, addr));
1165 }
1166 
1167 /*
1168  * t_pwrite: write to physical memory
1169  */
1170 /*ARGSUSED*/
1171 static ssize_t
bhyve_pwrite(mdb_tgt_t * tgt,const void * buf,size_t nbytes,physaddr_t addr)1172 bhyve_pwrite(mdb_tgt_t *tgt, const void *buf, size_t nbytes, physaddr_t addr)
1173 {
1174 	return (bhyve_awrite(tgt, MDB_TGT_AS_PHYS, buf, nbytes, addr));
1175 }
1176 
1177 /*
1178  * t_fread: read from core/object file
1179  */
1180 /*ARGSUSED*/
1181 static ssize_t
bhyve_fread(mdb_tgt_t * tgt,void * buf,size_t nbytes,uintptr_t addr)1182 bhyve_fread(mdb_tgt_t *tgt, void *buf, size_t nbytes, uintptr_t addr)
1183 {
1184 	return (bhyve_aread(tgt, MDB_TGT_AS_FILE, buf, nbytes, addr));
1185 }
1186 
1187 /*
1188  * t_fwrite: write to core/object file
1189  */
1190 /*ARGSUSED*/
1191 static ssize_t
bhyve_fwrite(mdb_tgt_t * tgt,const void * buf,size_t nbytes,uintptr_t addr)1192 bhyve_fwrite(mdb_tgt_t *tgt, const void *buf, size_t nbytes, uintptr_t addr)
1193 {
1194 	return (bhyve_awrite(tgt, MDB_TGT_AS_FILE, buf, nbytes, addr));
1195 }
1196 
1197 /*
1198  * t_ioread: read from I/O space
1199  */
1200 /*ARGSUSED*/
1201 static ssize_t
bhyve_ioread(mdb_tgt_t * tgt,void * buf,size_t nbytes,uintptr_t addr)1202 bhyve_ioread(mdb_tgt_t *tgt, void *buf, size_t nbytes, uintptr_t addr)
1203 {
1204 	return (bhyve_aread(tgt, MDB_TGT_AS_IO, buf, nbytes, addr));
1205 }
1206 
1207 /*
1208  * t_iowrite: write to I/O space
1209  */
1210 /*ARGSUSED*/
1211 static ssize_t
bhyve_iowrite(mdb_tgt_t * tgt,const void * buf,size_t nbytes,uintptr_t addr)1212 bhyve_iowrite(mdb_tgt_t *tgt, const void *buf, size_t nbytes, uintptr_t addr)
1213 {
1214 	return (bhyve_awrite(tgt, MDB_TGT_AS_IO, buf, nbytes, addr));
1215 }
1216 
1217 /*
1218  * t_vtop: translate virtual to physical address
1219  */
1220 static int
bhyve_vtop(mdb_tgt_t * tgt,mdb_tgt_as_t as,uintptr_t va,physaddr_t * pa)1221 bhyve_vtop(mdb_tgt_t *tgt, mdb_tgt_as_t as, uintptr_t va, physaddr_t *pa)
1222 {
1223 	bhyve_data_t *bd = tgt->t_data;
1224 	int seg;
1225 
1226 	switch ((uintptr_t)as) {
1227 	case (uintptr_t)MDB_TGT_AS_VIRT:
1228 		seg = bd->bd_defseg;
1229 		break;
1230 
1231 	case (uintptr_t)MDB_TGT_AS_VIRT_I:
1232 		seg = VMM_DESC_CS;
1233 		break;
1234 
1235 	case (uintptr_t)MDB_TGT_AS_VIRT_S:
1236 		seg = VMM_DESC_SS;
1237 		break;
1238 
1239 	default:
1240 		return (set_errno(EINVAL));
1241 	}
1242 
1243 	if (vmm_vtop(bd->bd_vmm, bd->bd_curcpu, seg, va, pa) != 0) {
1244 		if (errno == EFAULT)
1245 			return (set_errno(EMDB_NOMAP));
1246 		else
1247 			return (-1);
1248 	}
1249 
1250 	return (0);
1251 }
1252 
1253 /*
1254  * t_lookup_by_addr: find symbol information for a given name
1255  */
1256 static int
bhyve_lookup_by_name(mdb_tgt_t * t,const char * object,const char * name,GElf_Sym * symp,mdb_syminfo_t * sip)1257 bhyve_lookup_by_name(mdb_tgt_t *t, const char *object, const char *name,
1258     GElf_Sym *symp, mdb_syminfo_t *sip)
1259 {
1260 	int err;
1261 
1262 	/*
1263 	 * Search only the private symbols, as nothing else will be populated.
1264 	 */
1265 	err = mdb_gelf_symtab_lookup_by_name(mdb.m_prsym, name, symp,
1266 	    &sip->sym_id);
1267 	sip->sym_table = MDB_TGT_PRVSYM;
1268 	return (err);
1269 }
1270 
1271 /*
1272  * t_lookup_by_addr: find symbol information for a given address
1273  */
1274 static int
bhyve_lookup_by_addr(mdb_tgt_t * tgt,uintptr_t addr,uint_t flags,char * buf,size_t nbytes,GElf_Sym * symp,mdb_syminfo_t * sip)1275 bhyve_lookup_by_addr(mdb_tgt_t *tgt, uintptr_t addr, uint_t flags, char *buf,
1276     size_t nbytes, GElf_Sym *symp, mdb_syminfo_t *sip)
1277 {
1278 	int err;
1279 
1280 	/*
1281 	 * Only the private symbols (created with ::nmadd) will be populated, so
1282 	 * search against those.
1283 	 */
1284 	err = mdb_gelf_symtab_lookup_by_addr(mdb.m_prsym, addr, flags, buf,
1285 	    nbytes, symp, &sip->sym_id);
1286 	sip->sym_table = MDB_TGT_PRVSYM;
1287 	return (err);
1288 }
1289 
1290 /*
1291  * t_status: get target status
1292  */
1293 static int
bhyve_status(mdb_tgt_t * tgt,mdb_tgt_status_t * tsp)1294 bhyve_status(mdb_tgt_t *tgt, mdb_tgt_status_t *tsp)
1295 {
1296 	bhyve_data_t *bd = tgt->t_data;
1297 	mdb_tgt_reg_t rip;
1298 	vmm_desc_t cs;
1299 	int ret;
1300 
1301 	bzero(tsp, sizeof (mdb_tgt_status_t));
1302 
1303 	ret = vmm_getreg(bd->bd_vmm, bd->bd_curcpu, KREG_RIP, &rip);
1304 	if (ret != 0) {
1305 		tsp->st_state = MDB_TGT_UNDEAD;
1306 	} else {
1307 		tsp->st_state = MDB_TGT_STOPPED;
1308 		tsp->st_pc = rip;
1309 	}
1310 
1311 	switch (vmm_vcpu_isa(bd->bd_vmm, bd->bd_curcpu)) {
1312 	case VMM_ISA_16:
1313 		(void) mdb_dis_select("ia16");
1314 		break;
1315 	case VMM_ISA_32:
1316 		(void) mdb_dis_select("ia32");
1317 		break;
1318 	case VMM_ISA_64:
1319 		(void) mdb_dis_select("amd64");
1320 		break;
1321 	default:
1322 		break;
1323 	}
1324 
1325 	return (0);
1326 }
1327 
1328 static void
bhyve_sighdl(int sig,siginfo_t * sip,ucontext_t * ucp,mdb_tgt_t * tgt)1329 bhyve_sighdl(int sig, siginfo_t *sip, ucontext_t *ucp, mdb_tgt_t *tgt)
1330 {
1331 	mdb_tgt_status_t *tsp = &tgt->t_status;
1332 	bhyve_data_t *bd = tgt->t_data;
1333 
1334 	switch (sig) {
1335 	case SIGINT:
1336 		/*
1337 		 * vmm_stop() may fail if the VM was destroyed while we were
1338 		 * waiting. This will be handled by mdb_tgt_status().
1339 		 */
1340 		(void) vmm_stop(bd->bd_vmm);
1341 		(void) mdb_tgt_status(tgt, tsp);
1342 		break;
1343 	}
1344 }
1345 
1346 /*
1347  * t_step: single-step target
1348  */
1349 static int
bhyve_step(mdb_tgt_t * tgt,mdb_tgt_status_t * tsp)1350 bhyve_step(mdb_tgt_t *tgt, mdb_tgt_status_t *tsp)
1351 {
1352 	bhyve_data_t *bd = tgt->t_data;
1353 	int ret;
1354 
1355 	ret = vmm_step(bd->bd_vmm, bd->bd_curcpu);
1356 	(void) mdb_tgt_status(tgt, tsp);
1357 
1358 	return (ret);
1359 }
1360 
1361 /*
1362  * t_cont: continue target execution
1363  *
1364  * Catch SIGINT so that the target can be stopped with Ctrl-C.
1365  */
1366 static int
bhyve_cont(mdb_tgt_t * tgt,mdb_tgt_status_t * tsp)1367 bhyve_cont(mdb_tgt_t *tgt, mdb_tgt_status_t *tsp)
1368 {
1369 	bhyve_data_t *bd = tgt->t_data;
1370 	mdb_signal_f *intf;
1371 	void *intd;
1372 	int ret;
1373 
1374 	intf = mdb_signal_gethandler(SIGINT, &intd);
1375 	(void) mdb_signal_sethandler(SIGINT, (mdb_signal_f *)bhyve_sighdl, tgt);
1376 
1377 	if (ret = vmm_cont(bd->bd_vmm) != 0) {
1378 		mdb_warn("failed to continue target execution: %d", ret);
1379 		return (set_errno(EMDB_TGT));
1380 	}
1381 
1382 	tsp->st_state = MDB_TGT_RUNNING;
1383 	(void) pause();
1384 
1385 	(void) mdb_signal_sethandler(SIGINT, intf, intd);
1386 	(void) mdb_tgt_status(tgt, tsp);
1387 
1388 	return (ret);
1389 }
1390 
1391 static int
bhyve_lookup_reg(mdb_tgt_t * tgt,const char * rname)1392 bhyve_lookup_reg(mdb_tgt_t *tgt, const char *rname)
1393 {
1394 	bhyve_data_t *bd = tgt->t_data;
1395 	const mdb_tgt_regdesc_t *rd;
1396 
1397 	for (rd = bhyve_kregs; rd->rd_name != NULL; rd++)
1398 		if (strcmp(rd->rd_name, rname) == 0)
1399 			return (rd->rd_num);
1400 
1401 	return (-1);
1402 }
1403 
1404 /*
1405  * t_getareg: get the value of a single register
1406  */
1407 static int
bhyve_getareg(mdb_tgt_t * tgt,mdb_tgt_tid_t tid,const char * rname,mdb_tgt_reg_t * rp)1408 bhyve_getareg(mdb_tgt_t *tgt, mdb_tgt_tid_t tid, const char *rname,
1409     mdb_tgt_reg_t *rp)
1410 {
1411 	bhyve_data_t *bd = tgt->t_data;
1412 	int reg = bhyve_lookup_reg(tgt, rname);
1413 	int ret;
1414 
1415 	if (reg == -1)
1416 		return (set_errno(EMDB_BADREG));
1417 
1418 	ret = vmm_getreg(bd->bd_vmm, bd->bd_curcpu, reg, rp);
1419 	if (ret == -1)
1420 		return (set_errno(EMDB_BADREG));
1421 
1422 	return (0);
1423 }
1424 
1425 /*
1426  * t_putareg: set the value of a single register
1427  */
1428 static int
bhyve_putareg(mdb_tgt_t * tgt,mdb_tgt_tid_t tid,const char * rname,mdb_tgt_reg_t r)1429 bhyve_putareg(mdb_tgt_t *tgt, mdb_tgt_tid_t tid, const char *rname,
1430     mdb_tgt_reg_t r)
1431 {
1432 	bhyve_data_t *bd = tgt->t_data;
1433 	int reg = bhyve_lookup_reg(tgt, rname);
1434 	int ret;
1435 
1436 	if ((tgt->t_flags & MDB_TGT_F_RDWR) == 0)
1437 		return (set_errno(EMDB_TGTRDONLY));
1438 
1439 	if (reg == -1)
1440 		return (set_errno(EMDB_BADREG));
1441 
1442 	ret = vmm_setreg(bd->bd_vmm, bd->bd_curcpu, reg, r);
1443 	if (ret == -1)
1444 		return (set_errno(EMDB_BADREG));
1445 
1446 	return (0);
1447 }
1448 
1449 static const mdb_tgt_ops_t bhyve_ops = {
1450 	.t_setflags =		bhyve_setflags,
1451 	.t_setcontext =		(int (*)())(uintptr_t)mdb_tgt_notsup,
1452 	.t_activate =		bhyve_activate,
1453 	.t_deactivate =		bhyve_deactivate,
1454 	.t_periodic =		(void (*)())(uintptr_t)mdb_tgt_nop,
1455 	.t_destroy =		bhyve_destroy,
1456 	.t_name =		bhyve_name,
1457 	.t_isa =		bhyve_isa,
1458 	.t_platform =		(const char *(*)())mdb_conf_platform,
1459 	.t_uname =		(int (*)())(uintptr_t)mdb_tgt_notsup,
1460 	.t_dmodel =		bhyve_dmodel,
1461 	.t_aread =		bhyve_aread,
1462 	.t_awrite =		bhyve_awrite,
1463 	.t_vread =		bhyve_vread,
1464 	.t_vwrite =		bhyve_vwrite,
1465 	.t_pread =		bhyve_pread,
1466 	.t_pwrite =		bhyve_pwrite,
1467 	.t_fread =		bhyve_fread,
1468 	.t_fwrite =		bhyve_fwrite,
1469 	.t_ioread =		bhyve_ioread,
1470 	.t_iowrite =		bhyve_iowrite,
1471 	.t_vtop =		bhyve_vtop,
1472 	.t_lookup_by_name =	bhyve_lookup_by_name,
1473 	.t_lookup_by_addr =	bhyve_lookup_by_addr,
1474 	.t_symbol_iter =	(int (*)())(uintptr_t)mdb_tgt_notsup,
1475 	.t_mapping_iter =	(int (*)())(uintptr_t)mdb_tgt_notsup,
1476 	.t_object_iter =	(int (*)())(uintptr_t)mdb_tgt_notsup,
1477 	.t_addr_to_map =	(const mdb_map_t *(*)())mdb_tgt_null,
1478 	.t_name_to_map =	(const mdb_map_t *(*)())mdb_tgt_null,
1479 	.t_addr_to_ctf =	(struct ctf_file *(*)())mdb_tgt_null,
1480 	.t_name_to_ctf =	(struct ctf_file *(*)())mdb_tgt_null,
1481 	.t_status =		bhyve_status,
1482 	.t_run =		(int (*)())(uintptr_t)mdb_tgt_notsup,
1483 	.t_step =		bhyve_step,
1484 	.t_step_out =		(int (*)())(uintptr_t)mdb_tgt_notsup,
1485 	.t_next =		(int (*)())(uintptr_t)mdb_tgt_notsup,
1486 	.t_cont =		bhyve_cont,
1487 	.t_signal =		(int (*)())(uintptr_t)mdb_tgt_notsup,
1488 	.t_add_vbrkpt =		(int (*)())(uintptr_t)mdb_tgt_null,
1489 	.t_add_sbrkpt =		(int (*)())(uintptr_t)mdb_tgt_null,
1490 	.t_add_pwapt =		(int (*)())(uintptr_t)mdb_tgt_null,
1491 	.t_add_vwapt =		(int (*)())(uintptr_t)mdb_tgt_null,
1492 	.t_add_iowapt =		(int (*)())(uintptr_t)mdb_tgt_null,
1493 	.t_add_sysenter =	(int (*)())(uintptr_t)mdb_tgt_null,
1494 	.t_add_sysexit =	(int (*)())(uintptr_t)mdb_tgt_null,
1495 	.t_add_signal =		(int (*)())(uintptr_t)mdb_tgt_null,
1496 	.t_add_fault =		(int (*)())(uintptr_t)mdb_tgt_null,
1497 	.t_getareg =		bhyve_getareg,
1498 	.t_putareg =		bhyve_putareg,
1499 	.t_stack_iter =		(int (*)())(uintptr_t)mdb_tgt_notsup,
1500 	.t_auxv =		(int (*)())(uintptr_t)mdb_tgt_notsup,
1501 	.t_thread_name =	(int (*)())(uintptr_t)mdb_tgt_notsup
1502 };
1503 
1504 int
mdb_bhyve_tgt_create(mdb_tgt_t * tgt,int argc,const char * argv[])1505 mdb_bhyve_tgt_create(mdb_tgt_t *tgt, int argc, const char *argv[])
1506 {
1507 	bhyve_data_t *bd;
1508 	vmm_t *vmm = NULL;
1509 	boolean_t writable = (tgt->t_flags & MDB_TGT_F_RDWR) != 0;
1510 
1511 	if (argc != 1)
1512 		return (set_errno(EINVAL));
1513 
1514 	vmm = vmm_open_vm(argv[0]);
1515 	if (vmm == NULL) {
1516 		mdb_warn("failed to open %s", argv[0]);
1517 		return (set_errno(EMDB_TGT));
1518 	}
1519 
1520 	if (vmm_map(vmm, writable) != 0) {
1521 		mdb_warn("failed to map %s", argv[0]);
1522 		vmm_close_vm(vmm);
1523 		return (set_errno(EMDB_TGT));
1524 	}
1525 
1526 	bd = mdb_zalloc(sizeof (bhyve_data_t) + strlen(argv[0]) + 1, UM_SLEEP);
1527 	(void) strcpy(bd->bd_name, argv[0]);
1528 	bd->bd_vmm = vmm;
1529 	bd->bd_curcpu = 0;
1530 	bd->bd_defseg = VMM_DESC_DS;
1531 
1532 	tgt->t_ops = &bhyve_ops;
1533 	tgt->t_data = bd;
1534 	tgt->t_flags |= MDB_TGT_F_ASIO;
1535 
1536 	(void) mdb_nv_insert(&mdb.m_nv, "cpuid", &bhyve_cpuid_disc, 0,
1537 	    MDB_NV_PERSIST | MDB_NV_RDONLY);
1538 
1539 	return (0);
1540 }
1541