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