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
81typedef 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
91const 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
174static const char *segments[] = { "CS", "DS", "ES", "FS", "GS", "SS" };
175
176
177/*ARGSUSED*/
178static uintmax_t
179bhyve_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
186static const mdb_nv_disc_t bhyve_cpuid_disc = {
187	.disc_get = bhyve_cpuid_get
188};
189
190
191static uintmax_t
192bhyve_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
202static void
203bhyve_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
209static const mdb_nv_disc_t bhyve_reg_disc = {
210	.disc_set = bhyve_reg_set,
211	.disc_get = bhyve_reg_get
212};
213
214static int
215bhyve_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
291static int
292bhyve_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
341static int
342bhyve_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
350static int
351bhyve_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
390static int
391bhyve_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
423static int
424bhyve_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
429static int
430bhyve_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
435static int
436bhyve_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
441static int
442bhyve_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
481static int
482bhyve_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
564fail:
565	if (ret != DCMD_OK)
566		mdb_warn("failed to get system registers for CPU %d\n", cpu);
567	return (ret);
568}
569
570static int
571bhyve_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
702fail:
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
709static int
710bhyve_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
728static int
729bhyve_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
747static int
748bhyve_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
787static int
788bhyve_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
822static int
823bhyve_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
855static 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 */
889static int
890bhyve_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 */
912static void
913bhyve_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 */
944static void
945bhyve_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 */
973static const char *
974bhyve_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 */
984static void
985bhyve_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 */
999const char *
1000bhyve_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 */
1010static int
1011bhyve_dmodel(mdb_tgt_t *tgt)
1012{
1013	_NOTE(ARGUNUSED(tgt));
1014
1015	return (MDB_TGT_MODEL_LP64);
1016}
1017
1018/*ARGSUSED*/
1019static ssize_t
1020bhyve_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*/
1058static ssize_t
1059bhyve_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*/
1100static ssize_t
1101bhyve_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*/
1110static ssize_t
1111bhyve_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*/
1120static ssize_t
1121bhyve_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*/
1130static ssize_t
1131bhyve_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*/
1140static ssize_t
1141bhyve_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*/
1150static ssize_t
1151bhyve_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*/
1160static ssize_t
1161bhyve_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*/
1170static ssize_t
1171bhyve_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 */
1179static int
1180bhyve_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 */
1215static int
1216bhyve_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
1250static void
1251bhyve_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 */
1271static int
1272bhyve_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 */
1288static int
1289bhyve_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
1313static int
1314bhyve_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 */
1329static int
1330bhyve_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 */
1350static int
1351bhyve_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
1371static 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
1425int
1426mdb_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