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:
Stefan Hajnoczi 2023-11-09 08:25:12 +08:00
commit 9a4750143c
28 changed files with 639 additions and 60 deletions

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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
View File

@ -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

View File

@ -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

View File

@ -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

View 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;
}

View File

@ -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

View File

@ -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"/>

View File

@ -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);

View File

@ -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);
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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')

View File

@ -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',

View File

@ -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')

View File

@ -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,

View File

@ -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
};

View File

@ -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")

View File

@ -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

View File

@ -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
#

View 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)

View File

@ -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

View 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")

View File

@ -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)

View File

@ -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