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