mirror of
https://git.FreeBSD.org/src.git
synced 2024-11-30 08:19:09 +00:00
bhyve: Add support for XML register definitions
This is useful for exposing additional registers to debuggers. For instance, control registers are now available on amd64 when using gdb to debug a guest. The stub indicates support by including the string "qXfer:features:read+" in its feature list. The debugger queries for target descriptions by sending the query "qXfer:features:read:" followed by a file path. The XML definitions are copied from QEMU and installed to /usr/share/bhyve/gdb. Note that we currently don't handle the SIMD registers at all, since that's of somewhat limited utility (for me at least) and since that requires new ioctls to fetch the register values. Reviewed by: jhb MFC after: 2 weeks Sponsored by: Innovate UK Differential Revision: https://reviews.freebsd.org/D43666
This commit is contained in:
parent
3668e1fa0a
commit
f81cdf24ba
@ -184,6 +184,8 @@
|
|||||||
atf tags=package=tests
|
atf tags=package=tests
|
||||||
..
|
..
|
||||||
bhyve
|
bhyve
|
||||||
|
gdb
|
||||||
|
..
|
||||||
kbdlayout
|
kbdlayout
|
||||||
..
|
..
|
||||||
..
|
..
|
||||||
|
@ -75,6 +75,7 @@ CFLAGS+= -DBHYVE_GDB
|
|||||||
.ifdef GDB_LOG
|
.ifdef GDB_LOG
|
||||||
CFLAGS+=-DGDB_LOG
|
CFLAGS+=-DGDB_LOG
|
||||||
.endif
|
.endif
|
||||||
|
SUBDIR+= gdb
|
||||||
.endif
|
.endif
|
||||||
|
|
||||||
CFLAGS+=-I${.CURDIR} \
|
CFLAGS+=-I${.CURDIR} \
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/queue.h>
|
#include <sys/queue.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <machine/atomic.h>
|
#include <machine/atomic.h>
|
||||||
#include <machine/specialreg.h>
|
#include <machine/specialreg.h>
|
||||||
#include <machine/vmm.h>
|
#include <machine/vmm.h>
|
||||||
@ -63,6 +65,8 @@
|
|||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
#include "mevent.h"
|
#include "mevent.h"
|
||||||
|
|
||||||
|
#define _PATH_GDB_XML "/usr/share/bhyve/gdb"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GDB_SIGNAL_* numbers are part of the GDB remote protocol. Most stops
|
* GDB_SIGNAL_* numbers are part of the GDB remote protocol. Most stops
|
||||||
* use SIGTRAP.
|
* use SIGTRAP.
|
||||||
@ -85,6 +89,7 @@ static cpuset_t vcpus_active, vcpus_suspended, vcpus_waiting;
|
|||||||
static pthread_mutex_t gdb_lock;
|
static pthread_mutex_t gdb_lock;
|
||||||
static pthread_cond_t idle_vcpus;
|
static pthread_cond_t idle_vcpus;
|
||||||
static bool first_stop, report_next_stop, swbreak_enabled;
|
static bool first_stop, report_next_stop, swbreak_enabled;
|
||||||
|
static int xml_dfd = -1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* An I/O buffer contains 'capacity' bytes of room at 'data'. For a
|
* An I/O buffer contains 'capacity' bytes of room at 'data'. For a
|
||||||
@ -169,8 +174,25 @@ static const struct gdb_reg {
|
|||||||
{ .id = VM_REG_GUEST_ES, .size = 4 },
|
{ .id = VM_REG_GUEST_ES, .size = 4 },
|
||||||
{ .id = VM_REG_GUEST_FS, .size = 4 },
|
{ .id = VM_REG_GUEST_FS, .size = 4 },
|
||||||
{ .id = VM_REG_GUEST_GS, .size = 4 },
|
{ .id = VM_REG_GUEST_GS, .size = 4 },
|
||||||
|
/*
|
||||||
|
* Registers past this point are not included in a reply to a 'g' query,
|
||||||
|
* to provide compatibility with debuggers that do not fetch a target
|
||||||
|
* description. The debugger can query them individually with 'p' if it
|
||||||
|
* knows about them.
|
||||||
|
*/
|
||||||
|
#define GDB_REG_FIRST_EXT VM_REG_GUEST_FS_BASE
|
||||||
|
{ .id = VM_REG_GUEST_FS_BASE, .size = 8 },
|
||||||
|
{ .id = VM_REG_GUEST_GS_BASE, .size = 8 },
|
||||||
|
{ .id = VM_REG_GUEST_KGS_BASE, .size = 8 },
|
||||||
|
{ .id = VM_REG_GUEST_CR0, .size = 8 },
|
||||||
|
{ .id = VM_REG_GUEST_CR2, .size = 8 },
|
||||||
|
{ .id = VM_REG_GUEST_CR3, .size = 8 },
|
||||||
|
{ .id = VM_REG_GUEST_CR4, .size = 8 },
|
||||||
|
{ .id = VM_REG_GUEST_TPR, .size = 8 },
|
||||||
|
{ .id = VM_REG_GUEST_EFER, .size = 8 },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define GDB_LOG
|
||||||
#ifdef GDB_LOG
|
#ifdef GDB_LOG
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -1029,9 +1051,13 @@ gdb_read_regs(void)
|
|||||||
send_error(errno);
|
send_error(errno);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
start_packet();
|
start_packet();
|
||||||
for (size_t i = 0; i < nitems(gdb_regset); i++)
|
for (size_t i = 0; i < nitems(gdb_regset); i++) {
|
||||||
|
if (gdb_regset[i].id == GDB_REG_FIRST_EXT)
|
||||||
|
break;
|
||||||
append_unsigned_native(regvals[i], gdb_regset[i].size);
|
append_unsigned_native(regvals[i], gdb_regset[i].size);
|
||||||
|
}
|
||||||
finish_packet();
|
finish_packet();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1519,6 +1545,7 @@ check_features(const uint8_t *data, size_t len)
|
|||||||
/* This is an arbitrary limit. */
|
/* This is an arbitrary limit. */
|
||||||
append_string("PacketSize=4096");
|
append_string("PacketSize=4096");
|
||||||
append_string(";swbreak+");
|
append_string(";swbreak+");
|
||||||
|
append_string(";qXfer:features:read+");
|
||||||
finish_packet();
|
finish_packet();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1590,6 +1617,71 @@ gdb_query(const uint8_t *data, size_t len)
|
|||||||
start_packet();
|
start_packet();
|
||||||
append_asciihex(buf);
|
append_asciihex(buf);
|
||||||
finish_packet();
|
finish_packet();
|
||||||
|
} else if (command_equals(data, len, "qXfer:features:read:")) {
|
||||||
|
struct stat sb;
|
||||||
|
const char *xml;
|
||||||
|
const uint8_t *pathend;
|
||||||
|
char buf[64], path[PATH_MAX];
|
||||||
|
size_t xmllen;
|
||||||
|
unsigned int doff, dlen;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
data += strlen("qXfer:features:read:");
|
||||||
|
len -= strlen("qXfer:features:read:");
|
||||||
|
|
||||||
|
pathend = memchr(data, ':', len);
|
||||||
|
if (pathend == NULL ||
|
||||||
|
(size_t)(pathend - data) >= sizeof(path) - 1) {
|
||||||
|
send_error(EINVAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(path, data, pathend - data);
|
||||||
|
path[pathend - data] = '\0';
|
||||||
|
data += (pathend - data) + 1;
|
||||||
|
len -= (pathend - data) + 1;
|
||||||
|
|
||||||
|
if (len > sizeof(buf) - 1) {
|
||||||
|
send_error(EINVAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(buf, data, len);
|
||||||
|
buf[len] = '\0';
|
||||||
|
if (sscanf(buf, "%x,%x", &doff, &dlen) != 2) {
|
||||||
|
send_error(EINVAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = openat(xml_dfd, path, O_RDONLY | O_RESOLVE_BENEATH);
|
||||||
|
if (fd < 0) {
|
||||||
|
send_error(errno);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fstat(fd, &sb) < 0) {
|
||||||
|
send_error(errno);
|
||||||
|
close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
xml = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||||
|
if (xml == MAP_FAILED) {
|
||||||
|
send_error(errno);
|
||||||
|
close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
xmllen = sb.st_size;
|
||||||
|
|
||||||
|
start_packet();
|
||||||
|
if (doff >= xmllen) {
|
||||||
|
append_char('l');
|
||||||
|
} else if (doff + dlen >= xmllen) {
|
||||||
|
append_char('l');
|
||||||
|
append_packet_data(xml + doff, xmllen - doff);
|
||||||
|
} else {
|
||||||
|
append_char('m');
|
||||||
|
append_packet_data(xml + doff, dlen);
|
||||||
|
}
|
||||||
|
finish_packet();
|
||||||
|
(void)munmap(__DECONST(void *, xml), xmllen);
|
||||||
} else
|
} else
|
||||||
send_empty_response();
|
send_empty_response();
|
||||||
}
|
}
|
||||||
@ -1917,6 +2009,9 @@ limit_gdb_socket(int s)
|
|||||||
void
|
void
|
||||||
init_gdb(struct vmctx *_ctx)
|
init_gdb(struct vmctx *_ctx)
|
||||||
{
|
{
|
||||||
|
#ifndef WITHOUT_CAPSICUM
|
||||||
|
cap_rights_t rights;
|
||||||
|
#endif
|
||||||
int error, flags, optval, s;
|
int error, flags, optval, s;
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
struct addrinfo *gdbaddr;
|
struct addrinfo *gdbaddr;
|
||||||
@ -1997,4 +2092,13 @@ init_gdb(struct vmctx *_ctx)
|
|||||||
gdb_active = true;
|
gdb_active = true;
|
||||||
freeaddrinfo(gdbaddr);
|
freeaddrinfo(gdbaddr);
|
||||||
free(sport);
|
free(sport);
|
||||||
|
|
||||||
|
xml_dfd = open(_PATH_GDB_XML, O_DIRECTORY);
|
||||||
|
if (xml_dfd == -1)
|
||||||
|
err(1, "Failed to open gdb xml directory");
|
||||||
|
#ifndef WITHOUT_CAPSICUM
|
||||||
|
cap_rights_init(&rights, CAP_FSTAT, CAP_LOOKUP, CAP_MMAP_R, CAP_PREAD);
|
||||||
|
if (caph_rights_limit(xml_dfd, &rights) == -1)
|
||||||
|
err(1, "cap_rights_init");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
21
usr.sbin/bhyve/gdb/Makefile
Normal file
21
usr.sbin/bhyve/gdb/Makefile
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
PACKAGE= bhyve
|
||||||
|
FILESDIR= ${SHAREDIR}/bhyve/gdb
|
||||||
|
|
||||||
|
FILES+= target.xml
|
||||||
|
|
||||||
|
.if ${MACHINE_ARCH} == "amd64"
|
||||||
|
XMLARCH= i386:x86-64
|
||||||
|
FILES+= amd64.xml
|
||||||
|
.endif
|
||||||
|
|
||||||
|
target.xml: .PHONY
|
||||||
|
@echo "<?xml version=\"1.0\"?>" > ${.TARGET}
|
||||||
|
@echo "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">" >> ${.TARGET}
|
||||||
|
@echo "<target>" >> ${.TARGET}
|
||||||
|
@echo " <architecture>${XMLARCH}</architecture>" >> ${.TARGET}
|
||||||
|
.for file in ${FILES:Ntarget.xml}
|
||||||
|
@echo " <xi:include href=\"${file}\"/>" >> ${.TARGET}
|
||||||
|
.endfor
|
||||||
|
@echo "</target>" >> ${.TARGET}
|
||||||
|
|
||||||
|
.include <bsd.prog.mk>
|
165
usr.sbin/bhyve/gdb/amd64.xml
Normal file
165
usr.sbin/bhyve/gdb/amd64.xml
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- Copyright (C) 2010-2017 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Copying and distribution of this file, with or without modification,
|
||||||
|
are permitted in any medium without royalty provided the copyright
|
||||||
|
notice and this notice are preserved. -->
|
||||||
|
|
||||||
|
<!-- x86_64 64bit -->
|
||||||
|
|
||||||
|
<!DOCTYPE target SYSTEM "gdb-target.dtd">
|
||||||
|
|
||||||
|
<feature name="org.gnu.gdb.i386.core">
|
||||||
|
<flags id="x64_eflags" size="4">
|
||||||
|
<field name="" start="22" end="31"/>
|
||||||
|
<field name="ID" start="21" end="21"/>
|
||||||
|
<field name="VIP" start="20" end="20"/>
|
||||||
|
<field name="VIF" start="19" end="19"/>
|
||||||
|
<field name="AC" start="18" end="18"/>
|
||||||
|
<field name="VM" start="17" end="17"/>
|
||||||
|
<field name="RF" start="16" end="16"/>
|
||||||
|
<field name="" start="15" end="15"/>
|
||||||
|
<field name="NT" start="14" end="14"/>
|
||||||
|
<field name="IOPL" start="12" end="13"/>
|
||||||
|
<field name="OF" start="11" end="11"/>
|
||||||
|
<field name="DF" start="10" end="10"/>
|
||||||
|
<field name="IF" start="9" end="9"/>
|
||||||
|
<field name="TF" start="8" end="8"/>
|
||||||
|
<field name="SF" start="7" end="7"/>
|
||||||
|
<field name="ZF" start="6" end="6"/>
|
||||||
|
<field name="" start="5" end="5"/>
|
||||||
|
<field name="AF" start="4" end="4"/>
|
||||||
|
<field name="" start="3" end="3"/>
|
||||||
|
<field name="PF" start="2" end="2"/>
|
||||||
|
<field name="" start="1" end="1"/>
|
||||||
|
<field name="CF" start="0" end="0"/>
|
||||||
|
</flags>
|
||||||
|
|
||||||
|
<!-- General registers -->
|
||||||
|
|
||||||
|
<reg name="rax" bitsize="64" type="int64" regnum="0"/>
|
||||||
|
<reg name="rbx" bitsize="64" type="int64"/>
|
||||||
|
<reg name="rcx" bitsize="64" type="int64"/>
|
||||||
|
<reg name="rdx" bitsize="64" type="int64"/>
|
||||||
|
<reg name="rsi" bitsize="64" type="int64"/>
|
||||||
|
<reg name="rdi" bitsize="64" type="int64"/>
|
||||||
|
<reg name="rbp" bitsize="64" type="data_ptr"/>
|
||||||
|
<reg name="rsp" bitsize="64" type="data_ptr"/>
|
||||||
|
<reg name="r8" bitsize="64" type="int64"/>
|
||||||
|
<reg name="r9" bitsize="64" type="int64"/>
|
||||||
|
<reg name="r10" bitsize="64" type="int64"/>
|
||||||
|
<reg name="r11" bitsize="64" type="int64"/>
|
||||||
|
<reg name="r12" bitsize="64" type="int64"/>
|
||||||
|
<reg name="r13" bitsize="64" type="int64"/>
|
||||||
|
<reg name="r14" bitsize="64" type="int64"/>
|
||||||
|
<reg name="r15" bitsize="64" type="int64"/>
|
||||||
|
|
||||||
|
<reg name="rip" bitsize="64" type="code_ptr"/>
|
||||||
|
<reg name="eflags" bitsize="32" type="x64_eflags"/>
|
||||||
|
|
||||||
|
<!-- Segment registers -->
|
||||||
|
|
||||||
|
<reg name="cs" bitsize="32" type="int32"/>
|
||||||
|
<reg name="ss" bitsize="32" type="int32"/>
|
||||||
|
<reg name="ds" bitsize="32" type="int32"/>
|
||||||
|
<reg name="es" bitsize="32" type="int32"/>
|
||||||
|
<reg name="fs" bitsize="32" type="int32"/>
|
||||||
|
<reg name="gs" bitsize="32" type="int32"/>
|
||||||
|
|
||||||
|
<!-- Segment descriptor caches and TLS base MSRs -->
|
||||||
|
|
||||||
|
<!--reg name="cs_base" bitsize="64" type="int64"/>
|
||||||
|
<reg name="ss_base" bitsize="64" type="int64"/>
|
||||||
|
<reg name="ds_base" bitsize="64" type="int64"/>
|
||||||
|
<reg name="es_base" bitsize="64" type="int64"/-->
|
||||||
|
<reg name="fs_base" bitsize="64" type="int64"/>
|
||||||
|
<reg name="gs_base" bitsize="64" type="int64"/>
|
||||||
|
<reg name="k_gs_base" bitsize="64" type="int64"/>
|
||||||
|
|
||||||
|
<!-- Control registers -->
|
||||||
|
|
||||||
|
<flags id="x64_cr0" size="8">
|
||||||
|
<field name="PG" start="31" end="31"/>
|
||||||
|
<field name="CD" start="30" end="30"/>
|
||||||
|
<field name="NW" start="29" end="29"/>
|
||||||
|
<field name="AM" start="18" end="18"/>
|
||||||
|
<field name="WP" start="16" end="16"/>
|
||||||
|
<field name="NE" start="5" end="5"/>
|
||||||
|
<field name="ET" start="4" end="4"/>
|
||||||
|
<field name="TS" start="3" end="3"/>
|
||||||
|
<field name="EM" start="2" end="2"/>
|
||||||
|
<field name="MP" start="1" end="1"/>
|
||||||
|
<field name="PE" start="0" end="0"/>
|
||||||
|
</flags>
|
||||||
|
|
||||||
|
<flags id="x64_cr3" size="8">
|
||||||
|
<field name="PDBR" start="12" end="63"/>
|
||||||
|
<!--field name="" start="3" end="11"/>
|
||||||
|
<field name="WT" start="2" end="2"/>
|
||||||
|
<field name="CD" start="1" end="1"/>
|
||||||
|
<field name="" start="0" end="0"/-->
|
||||||
|
<field name="PCID" start="0" end="11"/>
|
||||||
|
</flags>
|
||||||
|
|
||||||
|
<flags id="x64_cr4" size="8">
|
||||||
|
<field name="PKE" start="22" end="22"/>
|
||||||
|
<field name="SMAP" start="21" end="21"/>
|
||||||
|
<field name="SMEP" start="20" end="20"/>
|
||||||
|
<field name="OSXSAVE" start="18" end="18"/>
|
||||||
|
<field name="PCIDE" start="17" end="17"/>
|
||||||
|
<field name="FSGSBASE" start="16" end="16"/>
|
||||||
|
<field name="SMXE" start="14" end="14"/>
|
||||||
|
<field name="VMXE" start="13" end="13"/>
|
||||||
|
<field name="LA57" start="12" end="12"/>
|
||||||
|
<field name="UMIP" start="11" end="11"/>
|
||||||
|
<field name="OSXMMEXCPT" start="10" end="10"/>
|
||||||
|
<field name="OSFXSR" start="9" end="9"/>
|
||||||
|
<field name="PCE" start="8" end="8"/>
|
||||||
|
<field name="PGE" start="7" end="7"/>
|
||||||
|
<field name="MCE" start="6" end="6"/>
|
||||||
|
<field name="PAE" start="5" end="5"/>
|
||||||
|
<field name="PSE" start="4" end="4"/>
|
||||||
|
<field name="DE" start="3" end="3"/>
|
||||||
|
<field name="TSD" start="2" end="2"/>
|
||||||
|
<field name="PVI" start="1" end="1"/>
|
||||||
|
<field name="VME" start="0" end="0"/>
|
||||||
|
</flags>
|
||||||
|
|
||||||
|
<flags id="x64_efer" size="8">
|
||||||
|
<field name="TCE" start="15" end="15"/>
|
||||||
|
<field name="FFXSR" start="14" end="14"/>
|
||||||
|
<field name="LMSLE" start="13" end="13"/>
|
||||||
|
<field name="SVME" start="12" end="12"/>
|
||||||
|
<field name="NXE" start="11" end="11"/>
|
||||||
|
<field name="LMA" start="10" end="10"/>
|
||||||
|
<field name="LME" start="8" end="8"/>
|
||||||
|
<field name="SCE" start="0" end="0"/>
|
||||||
|
</flags>
|
||||||
|
|
||||||
|
<reg name="cr0" bitsize="64" type="x64_cr0"/>
|
||||||
|
<reg name="cr2" bitsize="64" type="int64"/>
|
||||||
|
<reg name="cr3" bitsize="64" type="x64_cr3"/>
|
||||||
|
<reg name="cr4" bitsize="64" type="x64_cr4"/>
|
||||||
|
<reg name="cr8" bitsize="64" type="int64"/>
|
||||||
|
<reg name="efer" bitsize="64" type="x64_efer"/>
|
||||||
|
|
||||||
|
<!-- x87 FPU -->
|
||||||
|
|
||||||
|
<reg name="st0" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st1" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st2" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st3" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st4" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st5" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st6" bitsize="80" type="i387_ext"/>
|
||||||
|
<reg name="st7" bitsize="80" type="i387_ext"/>
|
||||||
|
|
||||||
|
<reg name="fctrl" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="fstat" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="ftag" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="fiseg" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="fioff" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="foseg" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="fooff" bitsize="32" type="int" group="float"/>
|
||||||
|
<reg name="fop" bitsize="32" type="int" group="float"/>
|
||||||
|
</feature>
|
Loading…
Reference in New Issue
Block a user