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