Final test, gdbstub, plugin and gitdm updates for 8.2
- fix duplicate register in arm xml - hide various duplicate system registers from gdbstub - add new gdb register test to the CI (skipping s390x/ppc64 for now) - introduce GDBFeatureBuilder - move plugin initialisation to after vCPU init completes - enable building TCG plugins on Windows platform - various gitdm updates - some mailmap fixes - disable testing for nios2 signals which have regressed -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEZoWumedRZ7yvyN81+9DbCVqeKkQFAmVLpk4ACgkQ+9DbCVqe KkT7Zwf+LgNS2T8Gd6UBMk50Zwew3DSzK3HRRkAlxSV9vN9TCprnVDGJn7ObRpfq QCwiTmh20JRPFFBEsPGy/ozNPZsuWbt1/vyh3fnU4KD3aMySuyc/Hb9/mONPC9VE zh1mUxLCx10uwG5qF8jupIp22BQYD7B9i4YSF1gAUGsQNU7BPvcBDeDzyhCItJen 73oG9RQm7vDbjTOcGDkAMAG8iwLt07oMgFrDSgD8x7RWOxG8aiM3ninAW6S5GcO3 s49t0rTqJIu+pOncYYzmPvFxyZ/6W82tsJYtfxlVML02qj24HOmLWywRWgL5b10y TyXsDba3Ru8ez/kEaVVX6u9N1G/Ktg== =or8W -----END PGP SIGNATURE----- Merge tag 'pull-halloween-omnibus-081123-1' of https://gitlab.com/stsquad/qemu into staging Final test, gdbstub, plugin and gitdm updates for 8.2 - fix duplicate register in arm xml - hide various duplicate system registers from gdbstub - add new gdb register test to the CI (skipping s390x/ppc64 for now) - introduce GDBFeatureBuilder - move plugin initialisation to after vCPU init completes - enable building TCG plugins on Windows platform - various gitdm updates - some mailmap fixes - disable testing for nios2 signals which have regressed # -----BEGIN PGP SIGNATURE----- # # iQEzBAABCgAdFiEEZoWumedRZ7yvyN81+9DbCVqeKkQFAmVLpk4ACgkQ+9DbCVqe # KkT7Zwf+LgNS2T8Gd6UBMk50Zwew3DSzK3HRRkAlxSV9vN9TCprnVDGJn7ObRpfq # QCwiTmh20JRPFFBEsPGy/ozNPZsuWbt1/vyh3fnU4KD3aMySuyc/Hb9/mONPC9VE # zh1mUxLCx10uwG5qF8jupIp22BQYD7B9i4YSF1gAUGsQNU7BPvcBDeDzyhCItJen # 73oG9RQm7vDbjTOcGDkAMAG8iwLt07oMgFrDSgD8x7RWOxG8aiM3ninAW6S5GcO3 # s49t0rTqJIu+pOncYYzmPvFxyZ/6W82tsJYtfxlVML02qj24HOmLWywRWgL5b10y # TyXsDba3Ru8ez/kEaVVX6u9N1G/Ktg== # =or8W # -----END PGP SIGNATURE----- # gpg: Signature made Wed 08 Nov 2023 23:16:30 HKT # gpg: using RSA key 6685AE99E75167BCAFC8DF35FBD0DB095A9E2A44 # gpg: Good signature from "Alex Bennée (Master Work Key) <alex.bennee@linaro.org>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 6685 AE99 E751 67BC AFC8 DF35 FBD0 DB09 5A9E 2A44 * tag 'pull-halloween-omnibus-081123-1' of https://gitlab.com/stsquad/qemu: (23 commits) Revert "tests/tcg/nios2: Re-enable linux-user tests" mailmap: fixup some more corrupted author fields contrib/gitdm: add Daynix to domain-map contrib/gitdm: map HiSilicon to Huawei contrib/gitdm: add domain-map for Cestc contrib/gitdm: Add Rivos Inc to the domain map plugins: allow plugins to be enabled on windows gitlab: add dlltool to Windows CI plugins: disable lockstep plugin on windows plugins: make test/example plugins work on windows plugins: add dllexport and dllimport to api funcs configure: tell meson and contrib_plugins about DLLTOOL cpu: Call plugin hooks only when ready gdbstub: Introduce GDBFeatureBuilder gdbstub: Introduce gdb_find_static_feature() gdbstub: Add num_regs member to GDBFeature tests/avocado: update the tcg_plugins test tests/tcg: add an explicit gdbstub register tester target/arm: hide aliased MIDR from gdbstub target/arm: hide all versions of DBGD[RS]AR from gdbstub ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
9a4750143c
@ -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
|
||||
|
@ -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
|
||||
|
2
.mailmap
2
.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>
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
13
configure
vendored
13
configure
vendored
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
34
contrib/plugins/win32_linker.c
Normal file
34
contrib/plugins/win32_linker.c
Normal file
@ -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;
|
||||
}
|
||||
|
11
cpu-target.c
11
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
|
||||
|
@ -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"/>
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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')
|
||||
|
@ -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',
|
||||
|
@ -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')
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
#
|
||||
|
197
tests/tcg/multiarch/gdbstub/registers.py
Normal file
197
tests/tcg/multiarch/gdbstub/registers.py
Normal file
@ -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)
|
@ -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
|
||||
|
11
tests/tcg/nios2/Makefile.target
Normal file
11
tests/tcg/nios2/Makefile.target
Normal file
@ -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")
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user