aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.d/crossbuilds.yml4
-rw-r--r--.gitlab-ci.d/windows.yml1
-rw-r--r--.mailmap2
-rw-r--r--MAINTAINERS2
-rw-r--r--configs/targets/loongarch64-linux-user.mak1
-rwxr-xr-xconfigure13
-rw-r--r--contrib/gitdm/domain-map4
-rw-r--r--contrib/plugins/Makefile26
-rw-r--r--contrib/plugins/win32_linker.c34
-rw-r--r--cpu-target.c11
-rw-r--r--gdb-xml/arm-neon.xml2
-rw-r--r--gdbstub/gdbstub.c78
-rw-r--r--hw/core/cpu-common.c10
-rw-r--r--include/exec/gdbstub.h59
-rw-r--r--include/qemu/qemu-plugin.h50
-rw-r--r--meson.build5
-rw-r--r--plugins/meson.build19
-rw-r--r--scripts/feature_to_c.py46
-rw-r--r--target/arm/debug_helper.c10
-rw-r--r--target/arm/helper.c37
-rw-r--r--tests/avocado/tcg_plugins.py28
-rw-r--r--tests/plugin/meson.build14
-rw-r--r--tests/tcg/multiarch/Makefile.target11
-rw-r--r--tests/tcg/multiarch/gdbstub/registers.py197
-rw-r--r--tests/tcg/multiarch/system/Makefile.softmmu-target13
-rw-r--r--tests/tcg/nios2/Makefile.target11
-rw-r--r--tests/tcg/ppc64/Makefile.target7
-rw-r--r--tests/tcg/s390x/Makefile.target4
28 files changed, 639 insertions, 60 deletions
diff --git a/.gitlab-ci.d/crossbuilds.yml b/.gitlab-ci.d/crossbuilds.yml
index 84ff2f6d2b..ac71a2abd3 100644
--- a/.gitlab-ci.d/crossbuilds.yml
+++ b/.gitlab-ci.d/crossbuilds.yml
@@ -165,7 +165,7 @@ cross-win32-system:
job: win32-fedora-cross-container
variables:
IMAGE: fedora-win32-cross
- EXTRA_CONFIGURE_OPTS: --enable-fdt=internal
+ EXTRA_CONFIGURE_OPTS: --enable-fdt=internal --disable-plugins
CROSS_SKIP_TARGETS: alpha-softmmu avr-softmmu hppa-softmmu m68k-softmmu
microblazeel-softmmu mips64el-softmmu nios2-softmmu
artifacts:
@@ -179,7 +179,7 @@ cross-win64-system:
job: win64-fedora-cross-container
variables:
IMAGE: fedora-win64-cross
- EXTRA_CONFIGURE_OPTS: --enable-fdt=internal
+ EXTRA_CONFIGURE_OPTS: --enable-fdt=internal --disable-plugins
CROSS_SKIP_TARGETS: alpha-softmmu avr-softmmu hppa-softmmu
m68k-softmmu microblazeel-softmmu nios2-softmmu
or1k-softmmu rx-softmmu sh4eb-softmmu sparc64-softmmu
diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml
index 12a987cd71..f7645f72b7 100644
--- a/.gitlab-ci.d/windows.yml
+++ b/.gitlab-ci.d/windows.yml
@@ -72,6 +72,7 @@
- .\msys64\usr\bin\bash -lc "pacman -Sy --noconfirm --needed
bison diffutils flex
git grep make sed
+ $MINGW_TARGET-binutils
$MINGW_TARGET-capstone
$MINGW_TARGET-ccache
$MINGW_TARGET-curl
diff --git a/.mailmap b/.mailmap
index 94f19a0ac9..e12e19f691 100644
--- a/.mailmap
+++ b/.mailmap
@@ -30,10 +30,12 @@ malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>
# Corrupted Author fields
Aaron Larson <alarson@ddci.com> alarson@ddci.com
Andreas Färber <andreas.faerber@web.de> Andreas Färber <andreas.faerber>
+fanwenjie <fanwj@mail.ustc.edu.cn> fanwj@mail.ustc.edu.cn <fanwj@mail.ustc.edu.cn>
Jason Wang <jasowang@redhat.com> Jason Wang <jasowang>
Marek Dolata <mkdolata@us.ibm.com> mkdolata@us.ibm.com <mkdolata@us.ibm.com>
Michael Ellerman <mpe@ellerman.id.au> michael@ozlabs.org <michael@ozlabs.org>
Nick Hudson <hnick@vmware.com> hnick@vmware.com <hnick@vmware.com>
+Timothée Cocault <timothee.cocault@gmail.com> timothee.cocault@gmail.com <timothee.cocault@gmail.com>
# There is also a:
# (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162>
diff --git a/MAINTAINERS b/MAINTAINERS
index 33bb1ba0ea..e73a3ff544 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2942,7 +2942,7 @@ F: gdbstub/*
F: include/exec/gdbstub.h
F: include/gdbstub/*
F: gdb-xml/
-F: tests/tcg/multiarch/gdbstub/
+F: tests/tcg/multiarch/gdbstub/*
F: scripts/feature_to_c.py
F: scripts/probe-gdb-support.py
diff --git a/configs/targets/loongarch64-linux-user.mak b/configs/targets/loongarch64-linux-user.mak
index 7d1b964020..d878e5a113 100644
--- a/configs/targets/loongarch64-linux-user.mak
+++ b/configs/targets/loongarch64-linux-user.mak
@@ -1,3 +1,4 @@
# Default configuration for loongarch64-linux-user
TARGET_ARCH=loongarch64
TARGET_BASE_ARCH=loongarch
+TARGET_XML_FILES=gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml
diff --git a/configure b/configure
index f1456f6123..abcb199aa8 100755
--- a/configure
+++ b/configure
@@ -309,6 +309,7 @@ fi
ar="${AR-${cross_prefix}ar}"
as="${AS-${cross_prefix}as}"
ccas="${CCAS-$cc}"
+dlltool="${DLLTOOL-${cross_prefix}dlltool}"
objcopy="${OBJCOPY-${cross_prefix}objcopy}"
ld="${LD-${cross_prefix}ld}"
ranlib="${RANLIB-${cross_prefix}ranlib}"
@@ -1010,9 +1011,9 @@ if test "$targetos" = "bogus"; then
fi
# test for any invalid configuration combinations
-if test "$targetos" = "windows"; then
+if test "$targetos" = "windows" && ! has "$dlltool"; then
if test "$plugins" = "yes"; then
- error_exit "TCG plugins not currently supported on Windows platforms"
+ error_exit "TCG plugins requires dlltool to build on Windows platforms"
fi
plugins="no"
fi
@@ -1659,9 +1660,15 @@ echo "SRC_PATH=$source_path/contrib/plugins" >> contrib/plugins/$config_host_mak
echo "PKG_CONFIG=${pkg_config}" >> contrib/plugins/$config_host_mak
echo "CC=$cc $CPU_CFLAGS" >> contrib/plugins/$config_host_mak
echo "CFLAGS=${CFLAGS-$default_cflags} $EXTRA_CFLAGS" >> contrib/plugins/$config_host_mak
+if test "$targetos" = windows; then
+ echo "DLLTOOL=$dlltool" >> contrib/plugins/$config_host_mak
+fi
if test "$targetos" = darwin; then
echo "CONFIG_DARWIN=y" >> contrib/plugins/$config_host_mak
fi
+if test "$targetos" = windows; then
+ echo "CONFIG_WIN32=y" >> contrib/plugins/$config_host_mak
+fi
# tests/tcg configuration
(config_host_mak=tests/tcg/config-host.mak
@@ -1764,6 +1771,7 @@ if test "$skip_meson" = no; then
test -n "$cxx" && echo "cpp = [$(meson_quote $cxx $CPU_CFLAGS)]" >> $cross
test -n "$objcc" && echo "objc = [$(meson_quote $objcc $CPU_CFLAGS)]" >> $cross
echo "ar = [$(meson_quote $ar)]" >> $cross
+ echo "dlltool = [$(meson_quote $dlltool)]" >> $cross
echo "nm = [$(meson_quote $nm)]" >> $cross
echo "pkgconfig = [$(meson_quote $pkg_config)]" >> $cross
echo "pkg-config = [$(meson_quote $pkg_config)]" >> $cross
@@ -1869,6 +1877,7 @@ preserve_env CC
preserve_env CFLAGS
preserve_env CXX
preserve_env CXXFLAGS
+preserve_env DLLTOOL
preserve_env LD
preserve_env LDFLAGS
preserve_env LD_LIBRARY_PATH
diff --git a/contrib/gitdm/domain-map b/contrib/gitdm/domain-map
index 3e31a06245..bf1dce03fd 100644
--- a/contrib/gitdm/domain-map
+++ b/contrib/gitdm/domain-map
@@ -12,15 +12,18 @@ amd.com AMD
aspeedtech.com ASPEED Technology Inc.
baidu.com Baidu
bytedance.com ByteDance
+cestc.cn Cestc
cmss.chinamobile.com China Mobile
citrix.com Citrix
crudebyte.com Crudebyte
chinatelecom.cn China Telecom
+daynix.com Daynix
eldorado.org.br Instituto de Pesquisas Eldorado
fb.com Facebook
fujitsu.com Fujitsu
google.com Google
greensocs.com GreenSocs
+hisilicon.com Huawei
huawei.com Huawei
ibm.com IBM
igalia.com Igalia
@@ -38,6 +41,7 @@ proxmox.com Proxmox
quicinc.com Qualcomm Innovation Center
redhat.com Red Hat
rev.ng rev.ng Labs
+rivosinc.com Rivos Inc
rt-rk.com RT-RK
samsung.com Samsung
siemens.com Siemens
diff --git a/contrib/plugins/Makefile b/contrib/plugins/Makefile
index 8ba78c7a32..1783750cf6 100644
--- a/contrib/plugins/Makefile
+++ b/contrib/plugins/Makefile
@@ -17,12 +17,25 @@ NAMES += execlog
NAMES += hotblocks
NAMES += hotpages
NAMES += howvec
+
+# The lockstep example communicates using unix sockets,
+# and can't be easily made to work on windows.
+ifneq ($(CONFIG_WIN32),y)
NAMES += lockstep
+endif
+
NAMES += hwprofile
NAMES += cache
NAMES += drcov
-SONAMES := $(addsuffix .so,$(addprefix lib,$(NAMES)))
+ifeq ($(CONFIG_WIN32),y)
+SO_SUFFIX := .dll
+LDLIBS += $(shell $(PKG_CONFIG) --libs glib-2.0)
+else
+SO_SUFFIX := .so
+endif
+
+SONAMES := $(addsuffix $(SO_SUFFIX),$(addprefix lib,$(NAMES)))
# The main QEMU uses Glib extensively so it's perfectly fine to use it
# in plugins (which many example do).
@@ -35,15 +48,20 @@ all: $(SONAMES)
%.o: %.c
$(CC) $(CFLAGS) $(PLUGIN_CFLAGS) -c -o $@ $<
-lib%.so: %.o
-ifeq ($(CONFIG_DARWIN),y)
+ifeq ($(CONFIG_WIN32),y)
+lib%$(SO_SUFFIX): %.o win32_linker.o ../../plugins/qemu_plugin_api.lib
+ $(CC) -shared -o $@ $^ $(LDLIBS)
+else ifeq ($(CONFIG_DARWIN),y)
+lib%$(SO_SUFFIX): %.o
$(CC) -bundle -Wl,-undefined,dynamic_lookup -o $@ $^ $(LDLIBS)
else
+lib%$(SO_SUFFIX): %.o
$(CC) -shared -o $@ $^ $(LDLIBS)
endif
+
clean:
- rm -f *.o *.so *.d
+ rm -f *.o *$(SO_SUFFIX) *.d
rm -Rf .libs
.PHONY: all clean
diff --git a/contrib/plugins/win32_linker.c b/contrib/plugins/win32_linker.c
new file mode 100644
index 0000000000..7534b2b8bf
--- /dev/null
+++ b/contrib/plugins/win32_linker.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023, Greg Manning <gmanning@rapitasystems.com>
+ *
+ * This hook, __pfnDliFailureHook2, is documented in the microsoft documentation here:
+ * https://learn.microsoft.com/en-us/cpp/build/reference/error-handling-and-notification
+ * It gets called when a delay-loaded DLL encounters various errors.
+ * We handle the specific case of a DLL looking for a "qemu.exe",
+ * and give it the running executable (regardless of what it is named).
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include <windows.h>
+#include <delayimp.h>
+
+FARPROC WINAPI dll_failure_hook(unsigned dliNotify, PDelayLoadInfo pdli);
+
+
+PfnDliHook __pfnDliFailureHook2 = dll_failure_hook;
+
+FARPROC WINAPI dll_failure_hook(unsigned dliNotify, PDelayLoadInfo pdli) {
+ if (dliNotify == dliFailLoadLib) {
+ /* If the failing request was for qemu.exe, ... */
+ if (strcmp(pdli->szDll, "qemu.exe") == 0) {
+ /* Then pass back a pointer to the top level module. */
+ HMODULE top = GetModuleHandle(NULL);
+ return (FARPROC) top;
+ }
+ }
+ /* Otherwise we can't do anything special. */
+ return 0;
+}
+
diff --git a/cpu-target.c b/cpu-target.c
index f3e1ad8bcd..508013e23d 100644
--- a/cpu-target.c
+++ b/cpu-target.c
@@ -42,7 +42,6 @@
#include "hw/core/accel-cpu.h"
#include "trace/trace-root.h"
#include "qemu/accel.h"
-#include "qemu/plugin.h"
uintptr_t qemu_host_page_size;
intptr_t qemu_host_page_mask;
@@ -143,11 +142,6 @@ bool cpu_exec_realizefn(CPUState *cpu, Error **errp)
/* Wait until cpu initialization complete before exposing cpu. */
cpu_list_add(cpu);
- /* Plugin initialization must wait until cpu_index assigned. */
- if (tcg_enabled()) {
- qemu_plugin_vcpu_init_hook(cpu);
- }
-
#ifdef CONFIG_USER_ONLY
assert(qdev_get_vmsd(DEVICE(cpu)) == NULL ||
qdev_get_vmsd(DEVICE(cpu))->unmigratable);
@@ -176,11 +170,6 @@ void cpu_exec_unrealizefn(CPUState *cpu)
}
#endif
- /* Call the plugin hook before clearing cpu->cpu_index in cpu_list_remove */
- if (tcg_enabled()) {
- qemu_plugin_vcpu_exit_hook(cpu);
- }
-
cpu_list_remove(cpu);
/*
* Now that the vCPU has been removed from the RCU list, we can call
diff --git a/gdb-xml/arm-neon.xml b/gdb-xml/arm-neon.xml
index 9dce0a996f..d61f6b8549 100644
--- a/gdb-xml/arm-neon.xml
+++ b/gdb-xml/arm-neon.xml
@@ -76,7 +76,7 @@
<reg name="q8" bitsize="128" type="neon_q"/>
<reg name="q9" bitsize="128" type="neon_q"/>
<reg name="q10" bitsize="128" type="neon_q"/>
- <reg name="q10" bitsize="128" type="neon_q"/>
+ <reg name="q11" bitsize="128" type="neon_q"/>
<reg name="q12" bitsize="128" type="neon_q"/>
<reg name="q13" bitsize="128" type="neon_q"/>
<reg name="q14" bitsize="128" type="neon_q"/>
diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index 29540a0284..ebb912da1b 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -422,6 +422,84 @@ static const char *get_feature_xml(const char *p, const char **newp,
return NULL;
}
+void gdb_feature_builder_init(GDBFeatureBuilder *builder, GDBFeature *feature,
+ const char *name, const char *xmlname,
+ int base_reg)
+{
+ char *header = g_markup_printf_escaped(
+ "<?xml version=\"1.0\"?>"
+ "<!DOCTYPE feature SYSTEM \"gdb-target.dtd\">"
+ "<feature name=\"%s\">",
+ name);
+
+ builder->feature = feature;
+ builder->xml = g_ptr_array_new();
+ g_ptr_array_add(builder->xml, header);
+ builder->base_reg = base_reg;
+ feature->xmlname = xmlname;
+ feature->num_regs = 0;
+}
+
+void gdb_feature_builder_append_tag(const GDBFeatureBuilder *builder,
+ const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ g_ptr_array_add(builder->xml, g_markup_vprintf_escaped(format, ap));
+ va_end(ap);
+}
+
+void gdb_feature_builder_append_reg(const GDBFeatureBuilder *builder,
+ const char *name,
+ int bitsize,
+ int regnum,
+ const char *type,
+ const char *group)
+{
+ if (builder->feature->num_regs < regnum) {
+ builder->feature->num_regs = regnum;
+ }
+
+ if (group) {
+ gdb_feature_builder_append_tag(
+ builder,
+ "<reg name=\"%s\" bitsize=\"%d\" regnum=\"%d\" type=\"%s\" group=\"%s\"/>",
+ name, bitsize, builder->base_reg + regnum, type, group);
+ } else {
+ gdb_feature_builder_append_tag(
+ builder,
+ "<reg name=\"%s\" bitsize=\"%d\" regnum=\"%d\" type=\"%s\"/>",
+ name, bitsize, builder->base_reg + regnum, type);
+ }
+}
+
+void gdb_feature_builder_end(const GDBFeatureBuilder *builder)
+{
+ g_ptr_array_add(builder->xml, (void *)"</feature>");
+ g_ptr_array_add(builder->xml, NULL);
+
+ builder->feature->xml = g_strjoinv(NULL, (void *)builder->xml->pdata);
+
+ for (guint i = 0; i < builder->xml->len - 2; i++) {
+ g_free(g_ptr_array_index(builder->xml, i));
+ }
+
+ g_ptr_array_free(builder->xml, TRUE);
+}
+
+const GDBFeature *gdb_find_static_feature(const char *xmlname)
+{
+ const GDBFeature *feature;
+
+ for (feature = gdb_static_features; feature->xmlname; feature++) {
+ if (!strcmp(feature->xmlname, xmlname)) {
+ return feature;
+ }
+ }
+
+ g_assert_not_reached();
+}
+
static int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg)
{
CPUClass *cc = CPU_GET_CLASS(cpu);
diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c
index d4112b8919..82dae51a55 100644
--- a/hw/core/cpu-common.c
+++ b/hw/core/cpu-common.c
@@ -214,6 +214,11 @@ static void cpu_common_realizefn(DeviceState *dev, Error **errp)
cpu_resume(cpu);
}
+ /* Plugin initialization must wait until the cpu is fully realized. */
+ if (tcg_enabled()) {
+ qemu_plugin_vcpu_init_hook(cpu);
+ }
+
/* NOTE: latest generic point where the cpu is fully realized */
}
@@ -221,6 +226,11 @@ static void cpu_common_unrealizefn(DeviceState *dev)
{
CPUState *cpu = CPU(dev);
+ /* Call the plugin hook before clearing the cpu is fully unrealized */
+ if (tcg_enabled()) {
+ qemu_plugin_vcpu_exit_hook(cpu);
+ }
+
/* NOTE: latest generic point before the cpu is fully unrealized */
cpu_exec_unrealizefn(cpu);
}
diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
index 1a01c35f8e..d8a3c56fa2 100644
--- a/include/exec/gdbstub.h
+++ b/include/exec/gdbstub.h
@@ -13,8 +13,15 @@
typedef struct GDBFeature {
const char *xmlname;
const char *xml;
+ int num_regs;
} GDBFeature;
+typedef struct GDBFeatureBuilder {
+ GDBFeature *feature;
+ GPtrArray *xml;
+ int base_reg;
+} GDBFeatureBuilder;
+
/* Get or set a register. Returns the size of the register. */
typedef int (*gdb_get_reg_cb)(CPUArchState *env, GByteArray *buf, int reg);
@@ -43,6 +50,58 @@ void gdb_register_coprocessor(CPUState *cpu,
*/
int gdbserver_start(const char *port_or_device);
+/**
+ * gdb_feature_builder_init() - Initialize GDBFeatureBuilder.
+ * @builder: The builder to be initialized.
+ * @feature: The feature to be filled.
+ * @name: The name of the feature.
+ * @xmlname: The name of the XML.
+ * @base_reg: The base number of the register ID.
+ */
+void gdb_feature_builder_init(GDBFeatureBuilder *builder, GDBFeature *feature,
+ const char *name, const char *xmlname,
+ int base_reg);
+
+/**
+ * gdb_feature_builder_append_tag() - Append a tag.
+ * @builder: The builder.
+ * @format: The format of the tag.
+ * @...: The values to be formatted.
+ */
+void G_GNUC_PRINTF(2, 3)
+gdb_feature_builder_append_tag(const GDBFeatureBuilder *builder,
+ const char *format, ...);
+
+/**
+ * gdb_feature_builder_append_reg() - Append a register.
+ * @builder: The builder.
+ * @name: The register's name; it must be unique within a CPU.
+ * @bitsize: The register's size, in bits.
+ * @regnum: The offset of the register's number in the feature.
+ * @type: The type of the register.
+ * @group: The register group to which this register belongs; it can be NULL.
+ */
+void gdb_feature_builder_append_reg(const GDBFeatureBuilder *builder,
+ const char *name,
+ int bitsize,
+ int regnum,
+ const char *type,
+ const char *group);
+
+/**
+ * gdb_feature_builder_end() - End building GDBFeature.
+ * @builder: The builder.
+ */
+void gdb_feature_builder_end(const GDBFeatureBuilder *builder);
+
+/**
+ * gdb_find_static_feature() - Find a static feature.
+ * @xmlname: The name of the XML.
+ *
+ * Return: The static feature.
+ */
+const GDBFeature *gdb_find_static_feature(const char *xmlname);
+
void gdb_set_stop_cpu(CPUState *cpu);
/* in gdbstub-xml.c, generated by scripts/feature_to_c.py */
diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index 50a9957279..4daab6efd2 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -22,15 +22,18 @@
* https://gcc.gnu.org/wiki/Visibility
*/
#if defined _WIN32 || defined __CYGWIN__
- #ifdef BUILDING_DLL
- #define QEMU_PLUGIN_EXPORT __declspec(dllexport)
- #else
+ #ifdef CONFIG_PLUGIN
#define QEMU_PLUGIN_EXPORT __declspec(dllimport)
+ #define QEMU_PLUGIN_API __declspec(dllexport)
+ #else
+ #define QEMU_PLUGIN_EXPORT __declspec(dllexport)
+ #define QEMU_PLUGIN_API __declspec(dllimport)
#endif
#define QEMU_PLUGIN_LOCAL
#else
#define QEMU_PLUGIN_EXPORT __attribute__((visibility("default")))
#define QEMU_PLUGIN_LOCAL __attribute__((visibility("hidden")))
+ #define QEMU_PLUGIN_API
#endif
/**
@@ -147,6 +150,7 @@ typedef void (*qemu_plugin_vcpu_udata_cb_t)(unsigned int vcpu_index,
*
* Note: Calling this function from qemu_plugin_install() is a bug.
*/
+QEMU_PLUGIN_API
void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb);
/**
@@ -160,6 +164,7 @@ void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb);
* Plugins are reset asynchronously, and therefore the given plugin receives
* callbacks until @cb is called.
*/
+QEMU_PLUGIN_API
void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb);
/**
@@ -171,6 +176,7 @@ void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb);
*
* See also: qemu_plugin_register_vcpu_exit_cb()
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_init_cb(qemu_plugin_id_t id,
qemu_plugin_vcpu_simple_cb_t cb);
@@ -183,6 +189,7 @@ void qemu_plugin_register_vcpu_init_cb(qemu_plugin_id_t id,
*
* See also: qemu_plugin_register_vcpu_init_cb()
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id,
qemu_plugin_vcpu_simple_cb_t cb);
@@ -193,6 +200,7 @@ void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id,
*
* The @cb function is called every time a vCPU idles.
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id,
qemu_plugin_vcpu_simple_cb_t cb);
@@ -203,6 +211,7 @@ void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id,
*
* The @cb function is called every time a vCPU resumes execution.
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id,
qemu_plugin_vcpu_simple_cb_t cb);
@@ -253,6 +262,7 @@ typedef void (*qemu_plugin_vcpu_tb_trans_cb_t)(qemu_plugin_id_t id,
* callbacks to be triggered when the block or individual instruction
* executes.
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id,
qemu_plugin_vcpu_tb_trans_cb_t cb);
@@ -265,6 +275,7 @@ void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id,
*
* The @cb function is called every time a translated unit executes.
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb,
qemu_plugin_vcpu_udata_cb_t cb,
enum qemu_plugin_cb_flags flags,
@@ -296,6 +307,7 @@ enum qemu_plugin_op {
* Note: ops are not atomic so in multi-threaded/multi-smp situations
* you will get inexact results.
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb,
enum qemu_plugin_op op,
void *ptr, uint64_t imm);
@@ -309,6 +321,7 @@ void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb,
*
* The @cb function is called every time an instruction is executed
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn,
qemu_plugin_vcpu_udata_cb_t cb,
enum qemu_plugin_cb_flags flags,
@@ -324,6 +337,7 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn,
* Insert an inline op to every time an instruction executes. Useful
* if you just want to increment a single counter somewhere in memory.
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn,
enum qemu_plugin_op op,
void *ptr, uint64_t imm);
@@ -334,6 +348,7 @@ void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn,
*
* Returns: number of instructions in this block
*/
+QEMU_PLUGIN_API
size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb);
/**
@@ -342,6 +357,7 @@ size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb);
*
* Returns: virtual address of block start
*/
+QEMU_PLUGIN_API
uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb);
/**
@@ -355,6 +371,7 @@ uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb);
*
* Returns: opaque handle to instruction
*/
+QEMU_PLUGIN_API
struct qemu_plugin_insn *
qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx);
@@ -368,6 +385,7 @@ qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx);
* Returns: pointer to a stream of bytes containing the value of this
* instructions opcode.
*/
+QEMU_PLUGIN_API
const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn);
/**
@@ -376,6 +394,7 @@ const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn);
*
* Returns: size of instruction in bytes
*/
+QEMU_PLUGIN_API
size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn);
/**
@@ -384,6 +403,7 @@ size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn);
*
* Returns: virtual address of instruction
*/
+QEMU_PLUGIN_API
uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn);
/**
@@ -392,6 +412,7 @@ uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn);
*
* Returns: hardware (physical) target address of instruction
*/
+QEMU_PLUGIN_API
void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn);
/**
@@ -410,6 +431,7 @@ struct qemu_plugin_hwaddr;
*
* Returns: size of access in ^2 (0=byte, 1=16bit, 2=32bit etc...)
*/
+QEMU_PLUGIN_API
unsigned int qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info);
/**
* qemu_plugin_mem_is_sign_extended() - was the access sign extended
@@ -417,6 +439,7 @@ unsigned int qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info);
*
* Returns: true if it was, otherwise false
*/
+QEMU_PLUGIN_API
bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info);
/**
* qemu_plugin_mem_is_big_endian() - was the access big endian
@@ -424,6 +447,7 @@ bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info);
*
* Returns: true if it was, otherwise false
*/
+QEMU_PLUGIN_API
bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info);
/**
* qemu_plugin_mem_is_store() - was the access a store
@@ -431,6 +455,7 @@ bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info);
*
* Returns: true if it was, otherwise false
*/
+QEMU_PLUGIN_API
bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info);
/**
@@ -446,6 +471,7 @@ bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info);
* information about the handle should be recovered before the
* callback returns.
*/
+QEMU_PLUGIN_API
struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info,
uint64_t vaddr);
@@ -462,6 +488,7 @@ struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info,
* Returns true if the handle's memory operation is to memory-mapped IO, or
* false if it is to RAM
*/
+QEMU_PLUGIN_API
bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr);
/**
@@ -473,12 +500,14 @@ bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr);
* Note that the returned physical address may not be unique if you are dealing
* with multiple address spaces.
*/
+QEMU_PLUGIN_API
uint64_t qemu_plugin_hwaddr_phys_addr(const struct qemu_plugin_hwaddr *haddr);
/*
* Returns a string representing the device. The string is valid for
* the lifetime of the plugin.
*/
+QEMU_PLUGIN_API
const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h);
/**
@@ -513,6 +542,7 @@ typedef void (*qemu_plugin_vcpu_mem_cb_t) (unsigned int vcpu_index,
* callback so the plugin is responsible for ensuring it doesn't get
* confused by making appropriate use of locking if required.
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn,
qemu_plugin_vcpu_mem_cb_t cb,
enum qemu_plugin_cb_flags flags,
@@ -531,6 +561,7 @@ void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn,
* instruction. This provides for a lightweight but not thread-safe
* way of counting the number of operations done.
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_mem_inline(struct qemu_plugin_insn *insn,
enum qemu_plugin_mem_rw rw,
enum qemu_plugin_op op, void *ptr,
@@ -544,6 +575,7 @@ typedef void
uint64_t a3, uint64_t a4, uint64_t a5,
uint64_t a6, uint64_t a7, uint64_t a8);
+QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_syscall_cb(qemu_plugin_id_t id,
qemu_plugin_vcpu_syscall_cb_t cb);
@@ -551,6 +583,7 @@ typedef void
(*qemu_plugin_vcpu_syscall_ret_cb_t)(qemu_plugin_id_t id, unsigned int vcpu_idx,
int64_t num, int64_t ret);
+QEMU_PLUGIN_API
void
qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id,
qemu_plugin_vcpu_syscall_ret_cb_t cb);
@@ -563,6 +596,7 @@ qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id,
* Returns an allocated string containing the disassembly
*/
+QEMU_PLUGIN_API
char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn);
/**
@@ -572,6 +606,7 @@ char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn);
* Return a static string referring to the symbol. This is dependent
* on the binary QEMU is running having provided a symbol table.
*/
+QEMU_PLUGIN_API
const char *qemu_plugin_insn_symbol(const struct qemu_plugin_insn *insn);
/**
@@ -583,9 +618,11 @@ const char *qemu_plugin_insn_symbol(const struct qemu_plugin_insn *insn);
*
* See also: qemu_plugin_register_vcpu_init_cb()
*/
+QEMU_PLUGIN_API
void qemu_plugin_vcpu_for_each(qemu_plugin_id_t id,
qemu_plugin_vcpu_simple_cb_t cb);
+QEMU_PLUGIN_API
void qemu_plugin_register_flush_cb(qemu_plugin_id_t id,
qemu_plugin_simple_cb_t cb);
@@ -602,6 +639,7 @@ void qemu_plugin_register_flush_cb(qemu_plugin_id_t id,
* In user-mode it is possible a few un-instrumented instructions from
* child threads may run before the host kernel reaps the threads.
*/
+QEMU_PLUGIN_API
void qemu_plugin_register_atexit_cb(qemu_plugin_id_t id,
qemu_plugin_udata_cb_t cb, void *userdata);
@@ -615,6 +653,7 @@ int qemu_plugin_n_max_vcpus(void);
* qemu_plugin_outs() - output string via QEMU's logging system
* @string: a string
*/
+QEMU_PLUGIN_API
void qemu_plugin_outs(const char *string);
/**
@@ -628,6 +667,7 @@ void qemu_plugin_outs(const char *string);
* returns true if the combination @name=@val parses correctly to a boolean
* argument, and false otherwise
*/
+QEMU_PLUGIN_API
bool qemu_plugin_bool_parse(const char *name, const char *val, bool *ret);
/**
@@ -638,6 +678,7 @@ bool qemu_plugin_bool_parse(const char *name, const char *val, bool *ret);
* return NULL. The user should g_free() the string once no longer
* needed.
*/
+QEMU_PLUGIN_API
const char *qemu_plugin_path_to_binary(void);
/**
@@ -646,6 +687,7 @@ const char *qemu_plugin_path_to_binary(void);
* Returns the nominal start address of the main text segment in
* user-mode. Currently returns 0 for system emulation.
*/
+QEMU_PLUGIN_API
uint64_t qemu_plugin_start_code(void);
/**
@@ -654,6 +696,7 @@ uint64_t qemu_plugin_start_code(void);
* Returns the nominal end address of the main text segment in
* user-mode. Currently returns 0 for system emulation.
*/
+QEMU_PLUGIN_API
uint64_t qemu_plugin_end_code(void);
/**
@@ -662,6 +705,7 @@ uint64_t qemu_plugin_end_code(void);
* Returns the nominal entry address of the main text segment in
* user-mode. Currently returns 0 for system emulation.
*/
+QEMU_PLUGIN_API
uint64_t qemu_plugin_entry_code(void);
#endif /* QEMU_QEMU_PLUGIN_H */
diff --git a/meson.build b/meson.build
index 4848930680..d7d841e71e 100644
--- a/meson.build
+++ b/meson.build
@@ -3944,6 +3944,11 @@ endforeach
if get_option('plugins')
install_headers('include/qemu/qemu-plugin.h')
+ if targetos == 'windows'
+ # On windows, we want to deliver the qemu_plugin_api.lib file in the qemu installer,
+ # so that plugin authors can compile against it.
+ install_data(win32_qemu_plugin_api_lib, install_dir: 'lib')
+ endif
endif
subdir('qga')
diff --git a/plugins/meson.build b/plugins/meson.build
index 71ed996ed3..40d24529c0 100644
--- a/plugins/meson.build
+++ b/plugins/meson.build
@@ -14,6 +14,25 @@ if not enable_modules
endif
if get_option('plugins')
+ if targetos == 'windows'
+ dlltool = find_program('dlltool', required: true)
+
+ # Generate a .lib file for plugins to link against.
+ # First, create a .def file listing all the symbols a plugin should expect to have
+ # available in qemu
+ win32_plugin_def = configure_file(
+ input: files('qemu-plugins.symbols'),
+ output: 'qemu_plugin_api.def',
+ capture: true,
+ command: ['sed', '-e', '0,/^/s//EXPORTS/; s/[{};]//g', '@INPUT@'])
+ # then use dlltool to assemble a delaylib.
+ win32_qemu_plugin_api_lib = configure_file(
+ input: win32_plugin_def,
+ output: 'qemu_plugin_api.lib',
+ command: [dlltool, '--input-def', '@INPUT@',
+ '--output-delaylib', '@OUTPUT@', '--dllname', 'qemu.exe']
+ )
+ endif
specific_ss.add(files(
'loader.c',
'core.c',
diff --git a/scripts/feature_to_c.py b/scripts/feature_to_c.py
index bcbcb83beb..e04d6b2df7 100644
--- a/scripts/feature_to_c.py
+++ b/scripts/feature_to_c.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-or-later
-import os, sys
+import os, sys, xml.etree.ElementTree
def writeliteral(indent, bytes):
sys.stdout.write(' ' * indent)
@@ -39,10 +39,52 @@ for input in sys.argv[1:]:
with open(input, 'rb') as file:
read = file.read()
+ parser = xml.etree.ElementTree.XMLPullParser(['start', 'end'])
+ parser.feed(read)
+ events = parser.read_events()
+ event, element = next(events)
+ if event != 'start':
+ sys.stderr.write(f'unexpected event: {event}\n')
+ exit(1)
+ if element.tag != 'feature':
+ sys.stderr.write(f'unexpected start tag: {element.tag}\n')
+ exit(1)
+
+ regnum = 0
+ regnums = []
+ tags = ['feature']
+ for event, element in events:
+ if event == 'end':
+ if element.tag != tags[len(tags) - 1]:
+ sys.stderr.write(f'unexpected end tag: {element.tag}\n')
+ exit(1)
+
+ tags.pop()
+ if element.tag == 'feature':
+ break
+ elif event == 'start':
+ if len(tags) < 2 and element.tag == 'reg':
+ if 'regnum' in element.attrib:
+ regnum = int(element.attrib['regnum'])
+
+ regnums.append(regnum)
+ regnum += 1
+
+ tags.append(element.tag)
+ else:
+ raise Exception(f'unexpected event: {event}\n')
+
+ if len(tags):
+ sys.stderr.write('unterminated feature tag\n')
+ exit(1)
+
+ base_reg = min(regnums)
+ num_regs = max(regnums) - base_reg + 1 if len(regnums) else 0
+
sys.stdout.write(' {\n')
writeliteral(8, bytes(os.path.basename(input), 'utf-8'))
sys.stdout.write(',\n')
writeliteral(8, read)
- sys.stdout.write('\n },\n')
+ sys.stdout.write(f',\n {num_regs},\n }},\n')
sys.stdout.write(' { NULL }\n};\n')
diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c
index 79a3659c0c..cbfba532f5 100644
--- a/target/arm/debug_helper.c
+++ b/target/arm/debug_helper.c
@@ -937,14 +937,14 @@ static const ARMCPRegInfo debug_cp_reginfo[] = {
*/
{ .name = "DBGDRAR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0,
.access = PL0_R, .accessfn = access_tdra,
- .type = ARM_CP_CONST, .resetvalue = 0 },
+ .type = ARM_CP_CONST | ARM_CP_NO_GDB, .resetvalue = 0 },
{ .name = "MDRAR_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 0,
.access = PL1_R, .accessfn = access_tdra,
.type = ARM_CP_CONST, .resetvalue = 0 },
{ .name = "DBGDSAR", .cp = 14, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0,
.access = PL0_R, .accessfn = access_tdra,
- .type = ARM_CP_CONST, .resetvalue = 0 },
+ .type = ARM_CP_CONST | ARM_CP_NO_GDB, .resetvalue = 0 },
/* Monitor debug system control register; the 32-bit alias is DBGDSCRext. */
{ .name = "MDSCR_EL1", .state = ARM_CP_STATE_BOTH,
.cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2,
@@ -1065,9 +1065,11 @@ static const ARMCPRegInfo debug_cp_reginfo[] = {
static const ARMCPRegInfo debug_lpae_cp_reginfo[] = {
/* 64 bit access versions of the (dummy) debug registers */
{ .name = "DBGDRAR", .cp = 14, .crm = 1, .opc1 = 0,
- .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 },
+ .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_64BIT | ARM_CP_NO_GDB,
+ .resetvalue = 0 },
{ .name = "DBGDSAR", .cp = 14, .crm = 2, .opc1 = 0,
- .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 },
+ .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_64BIT | ARM_CP_NO_GDB,
+ .resetvalue = 0 },
};
static void dbgwvr_write(CPUARMState *env, const ARMCPRegInfo *ri,
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 5dc0d20a84..ff1970981e 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -3722,20 +3722,6 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri,
}
#endif
-static const ARMCPRegInfo vapa_cp_reginfo[] = {
- { .name = "PAR", .cp = 15, .crn = 7, .crm = 4, .opc1 = 0, .opc2 = 0,
- .access = PL1_RW, .resetvalue = 0,
- .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.par_s),
- offsetoflow32(CPUARMState, cp15.par_ns) },
- .writefn = par_write },
-#ifndef CONFIG_USER_ONLY
- /* This underdecoding is safe because the reginfo is NO_RAW. */
- { .name = "ATS", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = CP_ANY,
- .access = PL1_W, .accessfn = ats_access,
- .writefn = ats_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC },
-#endif
-};
-
/* Return basic MPU access permission bits. */
static uint32_t simple_mpu_ap_bits(uint32_t val)
{
@@ -8904,6 +8890,27 @@ void register_cp_regs_for_features(ARMCPU *cpu)
define_arm_cp_regs(cpu, generic_timer_cp_reginfo);
}
if (arm_feature(env, ARM_FEATURE_VAPA)) {
+ ARMCPRegInfo vapa_cp_reginfo[] = {
+ { .name = "PAR", .cp = 15, .crn = 7, .crm = 4, .opc1 = 0, .opc2 = 0,
+ .access = PL1_RW, .resetvalue = 0,
+ .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.par_s),
+ offsetoflow32(CPUARMState, cp15.par_ns) },
+ .writefn = par_write},
+#ifndef CONFIG_USER_ONLY
+ /* This underdecoding is safe because the reginfo is NO_RAW. */
+ { .name = "ATS", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = CP_ANY,
+ .access = PL1_W, .accessfn = ats_access,
+ .writefn = ats_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC },
+#endif
+ };
+
+ /*
+ * When LPAE exists this 32-bit PAR register is an alias of the
+ * 64-bit AArch32 PAR register defined in lpae_cp_reginfo[]
+ */
+ if (arm_feature(env, ARM_FEATURE_LPAE)) {
+ vapa_cp_reginfo[0].type = ARM_CP_ALIAS | ARM_CP_NO_GDB;
+ }
define_arm_cp_regs(cpu, vapa_cp_reginfo);
}
if (arm_feature(env, ARM_FEATURE_CACHE_TEST_CLEAN)) {
@@ -8993,7 +9000,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.type = ARM_CP_CONST, .resetvalue = cpu->revidr },
};
ARMCPRegInfo id_v8_midr_alias_cp_reginfo = {
- .name = "MIDR", .type = ARM_CP_ALIAS | ARM_CP_CONST,
+ .name = "MIDR", .type = ARM_CP_ALIAS | ARM_CP_CONST | ARM_CP_NO_GDB,
.cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 4,
.access = PL1_R, .resetvalue = cpu->midr
};
diff --git a/tests/avocado/tcg_plugins.py b/tests/avocado/tcg_plugins.py
index 642d2e49e3..15fd87b2c1 100644
--- a/tests/avocado/tcg_plugins.py
+++ b/tests/avocado/tcg_plugins.py
@@ -54,13 +54,11 @@ class PluginKernelBase(LinuxKernelTest):
class PluginKernelNormal(PluginKernelBase):
def _grab_aarch64_kernel(self):
- kernel_url = ('http://security.debian.org/'
- 'debian-security/pool/updates/main/l/linux-signed-arm64/'
- 'linux-image-4.19.0-12-arm64_4.19.152-1_arm64.deb')
- kernel_sha1 = '2036c2792f80ac9c4ccaae742b2e0a28385b6010'
- kernel_deb = self.fetch_asset(kernel_url, asset_hash=kernel_sha1)
- kernel_path = self.extract_from_deb(kernel_deb,
- "/boot/vmlinuz-4.19.0-12-arm64")
+ kernel_url = ('https://storage.tuxboot.com/20230331/arm64/Image')
+ kernel_sha256 = 'ce95a7101a5fecebe0fe630deee6bd97b32ba41bc8754090e9ad8961ea8674c7'
+ kernel_path = self.fetch_asset(kernel_url,
+ asset_hash=kernel_sha256,
+ algorithm = "sha256")
return kernel_path
def test_aarch64_virt_insn(self):
@@ -88,6 +86,10 @@ class PluginKernelNormal(PluginKernelBase):
m = re.search(br"insns: (?P<count>\d+)", s)
if "count" not in m.groupdict():
self.fail("Failed to find instruction count")
+ else:
+ count = int(m.group("count"))
+ self.log.info(f"Counted: {count} instructions")
+
def test_aarch64_virt_insn_icount(self):
"""
@@ -111,9 +113,13 @@ class PluginKernelNormal(PluginKernelBase):
with plugin_log as lf, \
mmap.mmap(lf.fileno(), 0, access=mmap.ACCESS_READ) as s:
- m = re.search(br"detected repeat execution @ (?P<addr>0x[0-9A-Fa-f]+)", s)
- if m is not None and "addr" in m.groupdict():
- self.fail("detected repeated instructions")
+
+ m = re.search(br"insns: (?P<count>\d+)", s)
+ if "count" not in m.groupdict():
+ self.fail("Failed to find instruction count")
+ else:
+ count = int(m.group("count"))
+ self.log.info(f"Counted: {count} instructions")
def test_aarch64_virt_mem_icount(self):
"""
@@ -145,3 +151,5 @@ class PluginKernelNormal(PluginKernelBase):
callback = int(m[1])
if inline != callback:
self.fail("mismatched access counts")
+ else:
+ self.log.info(f"Counted {inline} memory accesses")
diff --git a/tests/plugin/meson.build b/tests/plugin/meson.build
index 322cafcdf6..528bb9d86c 100644
--- a/tests/plugin/meson.build
+++ b/tests/plugin/meson.build
@@ -1,9 +1,17 @@
t = []
if get_option('plugins')
foreach i : ['bb', 'empty', 'insn', 'mem', 'syscall']
- t += shared_module(i, files(i + '.c'),
- include_directories: '../../include/qemu',
- dependencies: glib)
+ if targetos == 'windows'
+ t += shared_module(i, files(i + '.c') + '../../contrib/plugins/win32_linker.c',
+ include_directories: '../../include/qemu',
+ objects: [win32_qemu_plugin_api_lib],
+ dependencies: glib)
+
+ else
+ t += shared_module(i, files(i + '.c'),
+ include_directories: '../../include/qemu',
+ dependencies: glib)
+ endif
endforeach
endif
if t.length() > 0
diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target
index f3bfaf1a22..d31ba8d6ae 100644
--- a/tests/tcg/multiarch/Makefile.target
+++ b/tests/tcg/multiarch/Makefile.target
@@ -93,12 +93,21 @@ run-gdbstub-thread-breakpoint: testthread
--qemu $(QEMU) --qargs "$(QEMU_OPTS)" \
--bin $< --test $(MULTIARCH_SRC)/gdbstub/test-thread-breakpoint.py, \
hitting a breakpoint on non-main thread)
+
+run-gdbstub-registers: sha512
+ $(call run-test, $@, $(GDB_SCRIPT) \
+ --gdb $(GDB) \
+ --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \
+ --bin $< --test $(MULTIARCH_SRC)/gdbstub/registers.py, \
+ checking register enumeration)
+
else
run-gdbstub-%:
$(call skip-test, "gdbstub test $*", "need working gdb with $(patsubst -%,,$(TARGET_NAME)) support")
endif
EXTRA_RUNS += run-gdbstub-sha1 run-gdbstub-qxfer-auxv-read \
- run-gdbstub-proc-mappings run-gdbstub-thread-breakpoint
+ run-gdbstub-proc-mappings run-gdbstub-thread-breakpoint \
+ run-gdbstub-registers
# ARM Compatible Semi Hosting Tests
#
diff --git a/tests/tcg/multiarch/gdbstub/registers.py b/tests/tcg/multiarch/gdbstub/registers.py
new file mode 100644
index 0000000000..ff6076b09e
--- /dev/null
+++ b/tests/tcg/multiarch/gdbstub/registers.py
@@ -0,0 +1,197 @@
+# Exercise the register functionality by exhaustively iterating
+# through all supported registers on the system.
+#
+# This is launched via tests/guest-debug/run-test.py but you can also
+# call it directly if using it for debugging/introspection:
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import gdb
+import sys
+import xml.etree.ElementTree as ET
+
+initial_vlen = 0
+failcount = 0
+
+def report(cond, msg):
+ "Report success/fail of test."
+ if cond:
+ print("PASS: %s" % (msg))
+ else:
+ print("FAIL: %s" % (msg))
+ global failcount
+ failcount += 1
+
+
+def fetch_xml_regmap():
+ """
+ Iterate through the XML descriptions and validate.
+
+ We check for any duplicate registers and report them. Return a
+ reg_map hash containing the names, regnums and initial values of
+ all registers.
+ """
+
+ # First check the XML descriptions we have sent. Most arches
+ # support XML but a few of the ancient ones don't in which case we
+ # need to gracefully fail.
+
+ try:
+ xml = gdb.execute("maint print xml-tdesc", False, True)
+ except (gdb.error):
+ print("SKIP: target does not support XML")
+ return None
+
+ total_regs = 0
+ reg_map = {}
+ frame = gdb.selected_frame()
+
+ tree = ET.fromstring(xml)
+ for f in tree.findall("feature"):
+ name = f.attrib["name"]
+ regs = f.findall("reg")
+
+ total = len(regs)
+ total_regs += total
+ base = int(regs[0].attrib["regnum"])
+ top = int(regs[-1].attrib["regnum"])
+
+ print(f"feature: {name} has {total} registers from {base} to {top}")
+
+ for r in regs:
+ name = r.attrib["name"]
+ regnum = int(r.attrib["regnum"])
+ try:
+ value = frame.read_register(name)
+ except ValueError:
+ report(False, f"failed to read reg: {name}")
+
+ entry = { "name": name, "initial": value, "regnum": regnum }
+
+ if name in reg_map:
+ report(False, f"duplicate register {entry} vs {reg_map[name]}")
+ continue
+
+ reg_map[name] = entry
+
+ # Validate we match
+ report(total_regs == len(reg_map.keys()),
+ f"counted all {total_regs} registers in XML")
+
+ return reg_map
+
+def crosscheck_remote_xml(reg_map):
+ """
+ Cross-check the list of remote-registers with the XML info.
+ """
+
+ remote = gdb.execute("maint print remote-registers", False, True)
+ r_regs = remote.split("\n")
+
+ total_regs = len(reg_map.keys())
+ total_r_regs = 0
+
+ for r in r_regs:
+ fields = r.split()
+ # Some of the registers reported here are "pseudo" registers that
+ # gdb invents based on actual registers so we need to filter them
+ # out.
+ if len(fields) == 8:
+ r_name = fields[0]
+ r_regnum = int(fields[6])
+
+ # check in the XML
+ try:
+ x_reg = reg_map[r_name]
+ except KeyError:
+ report(False, f"{r_name} not in XML description")
+ continue
+
+ x_reg["seen"] = True
+ x_regnum = x_reg["regnum"]
+ if r_regnum != x_regnum:
+ report(False, f"{r_name} {r_regnum} == {x_regnum} (xml)")
+ else:
+ total_r_regs += 1
+
+ # Just print a mismatch in totals as gdb will filter out 64 bit
+ # registers on a 32 bit machine. Also print what is missing to
+ # help with debug.
+ if total_regs != total_r_regs:
+ print(f"xml-tdesc has ({total_regs}) registers")
+ print(f"remote-registers has ({total_r_regs}) registers")
+
+ for x_key in reg_map.keys():
+ x_reg = reg_map[x_key]
+ if "seen" not in x_reg:
+ print(f"{x_reg} wasn't seen in remote-registers")
+
+def complete_and_diff(reg_map):
+ """
+ Let the program run to (almost) completion and then iterate
+ through all the registers we know about and report which ones have
+ changed.
+ """
+ # Let the program get to the end and we can check what changed
+ b = gdb.Breakpoint("_exit")
+ if b.pending: # workaround Microblaze weirdness
+ b.delete()
+ gdb.Breakpoint("_Exit")
+
+ gdb.execute("continue")
+
+ frame = gdb.selected_frame()
+ changed = 0
+
+ for e in reg_map.values():
+ name = e["name"]
+ old_val = e["initial"]
+
+ try:
+ new_val = frame.read_register(name)
+ except:
+ report(False, f"failed to read {name} at end of run")
+ continue
+
+ if new_val != old_val:
+ print(f"{name} changes from {old_val} to {new_val}")
+ changed += 1
+
+ # as long as something changed we can be confident its working
+ report(changed > 0, f"{changed} registers were changed")
+
+
+def run_test():
+ "Run through the tests"
+
+ reg_map = fetch_xml_regmap()
+
+ if reg_map is not None:
+ crosscheck_remote_xml(reg_map)
+ complete_and_diff(reg_map)
+
+
+#
+# This runs as the script it sourced (via -x, via run-test.py)
+#
+try:
+ inferior = gdb.selected_inferior()
+ arch = inferior.architecture()
+ print("ATTACHED: %s" % arch.name())
+except (gdb.error, AttributeError):
+ print("SKIPPING (not connected)", file=sys.stderr)
+ exit(0)
+
+if gdb.parse_and_eval('$pc') == 0:
+ print("SKIP: PC not set")
+ exit(0)
+
+try:
+ run_test()
+except (gdb.error):
+ print ("GDB Exception: %s" % (sys.exc_info()[0]))
+ failcount += 1
+ pass
+
+print("All tests complete: %d failures" % failcount)
+exit(failcount)
diff --git a/tests/tcg/multiarch/system/Makefile.softmmu-target b/tests/tcg/multiarch/system/Makefile.softmmu-target
index dee4f58dea..32dc0f9830 100644
--- a/tests/tcg/multiarch/system/Makefile.softmmu-target
+++ b/tests/tcg/multiarch/system/Makefile.softmmu-target
@@ -48,9 +48,20 @@ run-gdbstub-untimely-packet: hello
$(call quiet-command, \
(! grep -Fq 'Packet instead of Ack, ignoring it' untimely-packet.gdb.err), \
"GREP", file untimely-packet.gdb.err)
+
+run-gdbstub-registers: memory
+ $(call run-test, $@, $(GDB_SCRIPT) \
+ --gdb $(GDB) \
+ --qemu $(QEMU) \
+ --output $<.registers.gdb.out \
+ --qargs \
+ "-monitor none -display none -chardev file$(COMMA)path=$<.out$(COMMA)id=output $(QEMU_OPTS)" \
+ --bin $< --test $(MULTIARCH_SRC)/gdbstub/registers.py, \
+ softmmu gdbstub support)
else
run-gdbstub-%:
$(call skip-test, "gdbstub test $*", "need working gdb with $(patsubst -%,,$(TARGET_NAME)) support")
endif
-MULTIARCH_RUNS += run-gdbstub-memory run-gdbstub-interrupt run-gdbstub-untimely-packet
+MULTIARCH_RUNS += run-gdbstub-memory run-gdbstub-interrupt \
+ run-gdbstub-untimely-packet run-gdbstub-registers
diff --git a/tests/tcg/nios2/Makefile.target b/tests/tcg/nios2/Makefile.target
new file mode 100644
index 0000000000..b38e2352b7
--- /dev/null
+++ b/tests/tcg/nios2/Makefile.target
@@ -0,0 +1,11 @@
+# nios2 specific test tweaks
+
+# Currently nios2 signal handling is broken
+run-signals: signals
+ $(call skip-test, $<, "BROKEN")
+run-plugin-signals-with-%:
+ $(call skip-test, $<, "BROKEN")
+run-linux-test: linux-test
+ $(call skip-test, $<, "BROKEN")
+run-plugin-linux-test-with-%:
+ $(call skip-test, $<, "BROKEN")
diff --git a/tests/tcg/ppc64/Makefile.target b/tests/tcg/ppc64/Makefile.target
index 5721c159f2..1d08076756 100644
--- a/tests/tcg/ppc64/Makefile.target
+++ b/tests/tcg/ppc64/Makefile.target
@@ -38,4 +38,11 @@ PPC64_TESTS += signal_save_restore_xer
PPC64_TESTS += xxspltw
PPC64_TESTS += test-aes
+ifneq ($(GDB),)
+# Skip for now until vsx registers sorted out
+run-gdbstub-registers:
+ $(call skip-test, $<, "BROKEN reading VSX registers")
+endif
+
+
TESTS += $(PPC64_TESTS)
diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target
index 0e670f3f8b..46544fecd4 100644
--- a/tests/tcg/s390x/Makefile.target
+++ b/tests/tcg/s390x/Makefile.target
@@ -103,6 +103,10 @@ run-gdbstub-svc: hello-s390x-asm
--bin $< --test $(S390X_SRC)/gdbstub/test-svc.py, \
single-stepping svc)
+# Skip for now until vx registers sorted out
+run-gdbstub-registers:
+ $(call skip-test, $<, "BROKEN reading VX registers")
+
EXTRA_RUNS += run-gdbstub-signals-s390x run-gdbstub-svc
endif