git: 683947e13fb8 - stable/14 - bhyve: Add support for XML register definitions
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 29 Mar 2024 18:11:51 UTC
The branch stable/14 has been updated by markj:
URL: https://cgit.FreeBSD.org/src/commit/?id=683947e13fb8fdf5bee443cedd1b916238619998
commit 683947e13fb8fdf5bee443cedd1b916238619998
Author: Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2024-02-21 00:21:29 +0000
Commit: Mark Johnston <markj@FreeBSD.org>
CommitDate: 2024-03-29 13:53:05 +0000
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
(cherry picked from commit f81cdf24ba5436367377f7c8e8f51f6df2a75ca7)
---
etc/mtree/BSD.usr.dist | 2 +
usr.sbin/bhyve/Makefile | 1 +
usr.sbin/bhyve/gdb.c | 106 ++++++++++++++++++++++++++-
usr.sbin/bhyve/gdb/Makefile | 21 ++++++
usr.sbin/bhyve/gdb/amd64.xml | 165 +++++++++++++++++++++++++++++++++++++++++++
5 files changed, 294 insertions(+), 1 deletion(-)
diff --git a/etc/mtree/BSD.usr.dist b/etc/mtree/BSD.usr.dist
index a7738aaf6f78..0d43b657530b 100644
--- a/etc/mtree/BSD.usr.dist
+++ b/etc/mtree/BSD.usr.dist
@@ -184,6 +184,8 @@
atf tags=package=tests
..
bhyve
+ gdb
+ ..
kbdlayout
..
..
diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile
index b6cad38a6c39..c9d571daebbc 100644
--- a/usr.sbin/bhyve/Makefile
+++ b/usr.sbin/bhyve/Makefile
@@ -75,6 +75,7 @@ CFLAGS+= -DBHYVE_GDB
.ifdef GDB_LOG
CFLAGS+=-DGDB_LOG
.endif
+SUBDIR+= gdb
.endif
CFLAGS+=-I${.CURDIR} \
diff --git a/usr.sbin/bhyve/gdb.c b/usr.sbin/bhyve/gdb.c
index 6bdfbc5e8536..2a075cd10ca6 100644
--- a/usr.sbin/bhyve/gdb.c
+++ b/usr.sbin/bhyve/gdb.c
@@ -35,6 +35,8 @@
#include <sys/mman.h>
#include <sys/queue.h>
#include <sys/socket.h>
+#include <sys/stat.h>
+
#include <machine/atomic.h>
#include <machine/specialreg.h>
#include <machine/vmm.h>
@@ -64,6 +66,8 @@
#include "mem.h"
#include "mevent.h"
+#define _PATH_GDB_XML "/usr/share/bhyve/gdb"
+
/*
* GDB_SIGNAL_* numbers are part of the GDB remote protocol. Most stops
* use SIGTRAP.
@@ -86,6 +90,7 @@ static cpuset_t vcpus_active, vcpus_suspended, vcpus_waiting;
static pthread_mutex_t gdb_lock;
static pthread_cond_t idle_vcpus;
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
@@ -170,8 +175,25 @@ static const struct gdb_reg {
{ .id = VM_REG_GUEST_ES, .size = 4 },
{ .id = VM_REG_GUEST_FS, .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
#include <stdarg.h>
#include <stdio.h>
@@ -1030,9 +1052,13 @@ gdb_read_regs(void)
send_error(errno);
return;
}
+
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);
+ }
finish_packet();
}
@@ -1520,6 +1546,7 @@ check_features(const uint8_t *data, size_t len)
/* This is an arbitrary limit. */
append_string("PacketSize=4096");
append_string(";swbreak+");
+ append_string(";qXfer:features:read+");
finish_packet();
}
@@ -1591,6 +1618,71 @@ gdb_query(const uint8_t *data, size_t len)
start_packet();
append_asciihex(buf);
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
send_empty_response();
}
@@ -1918,6 +2010,9 @@ limit_gdb_socket(int s)
void
init_gdb(struct vmctx *_ctx)
{
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_t rights;
+#endif
int error, flags, optval, s;
struct addrinfo hints;
struct addrinfo *gdbaddr;
@@ -1998,4 +2093,13 @@ init_gdb(struct vmctx *_ctx)
gdb_active = true;
freeaddrinfo(gdbaddr);
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
}
diff --git a/usr.sbin/bhyve/gdb/Makefile b/usr.sbin/bhyve/gdb/Makefile
new file mode 100644
index 000000000000..a444b0cc4c69
--- /dev/null
+++ b/usr.sbin/bhyve/gdb/Makefile
@@ -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>
diff --git a/usr.sbin/bhyve/gdb/amd64.xml b/usr.sbin/bhyve/gdb/amd64.xml
new file mode 100644
index 000000000000..15bd270120d8
--- /dev/null
+++ b/usr.sbin/bhyve/gdb/amd64.xml
@@ -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>