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