Maintainer updates for testing, gdbstub, semihosting, plugins
- bump python in *BSD images via libvirt-ci - remove old unused Leon3 Avocado test - re-factor gdb command extension - add stoptrigger plugin to contrib - ensure plugin mem callbacks properly sized - reduce check-tcg noise of inline plugin test - fix register dumping in execlog plugin - restrict semihosting to TCG builds - fix regex in MTE test -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEZoWumedRZ7yvyN81+9DbCVqeKkQFAmae5OcACgkQ+9DbCVqe KkR8cgf/eM2Sm7EG7zIQ8SbY53DS07ls6uT7Mfn4374GEmj4Cy1I+WNoLGM5vq1r qWAC9q2LgJVMQoWJA6Fi3SCKiylBp3/jIdJ7CWN5qj/NmePHSV3EisQXf2qOWWL9 qOX2hJI7IIYNI2v3IvCzN/fB8F8U60iXERFHRypBH2p6Mz+EGMC3CEhesOEUta6o 2IMkRW8MoDv9x4B+FnNYav6CfqZjhRenu1CGgVGvWYRds2QDVNB/14kOunmBuwSs gPb7AhhnpobDYVxMarlJNPMbOdFjtDkYCajCNW7ffLcl+OjhoVR6cJcFpbOMv4kZ 8Nok8aDjUDWwUbmU0rBynca+1k8OTg== =TjRc -----END PGP SIGNATURE----- Merge tag 'pull-maintainer-9.1-rc0-230724-1' of https://gitlab.com/stsquad/qemu into staging Maintainer updates for testing, gdbstub, semihosting, plugins - bump python in *BSD images via libvirt-ci - remove old unused Leon3 Avocado test - re-factor gdb command extension - add stoptrigger plugin to contrib - ensure plugin mem callbacks properly sized - reduce check-tcg noise of inline plugin test - fix register dumping in execlog plugin - restrict semihosting to TCG builds - fix regex in MTE test # -----BEGIN PGP SIGNATURE----- # # iQEzBAABCgAdFiEEZoWumedRZ7yvyN81+9DbCVqeKkQFAmae5OcACgkQ+9DbCVqe # KkR8cgf/eM2Sm7EG7zIQ8SbY53DS07ls6uT7Mfn4374GEmj4Cy1I+WNoLGM5vq1r # qWAC9q2LgJVMQoWJA6Fi3SCKiylBp3/jIdJ7CWN5qj/NmePHSV3EisQXf2qOWWL9 # qOX2hJI7IIYNI2v3IvCzN/fB8F8U60iXERFHRypBH2p6Mz+EGMC3CEhesOEUta6o # 2IMkRW8MoDv9x4B+FnNYav6CfqZjhRenu1CGgVGvWYRds2QDVNB/14kOunmBuwSs # gPb7AhhnpobDYVxMarlJNPMbOdFjtDkYCajCNW7ffLcl+OjhoVR6cJcFpbOMv4kZ # 8Nok8aDjUDWwUbmU0rBynca+1k8OTg== # =TjRc # -----END PGP SIGNATURE----- # gpg: Signature made Tue 23 Jul 2024 09:01:59 AM AEST # gpg: using RSA key 6685AE99E75167BCAFC8DF35FBD0DB095A9E2A44 # gpg: Good signature from "Alex Bennée (Master Work Key) <alex.bennee@linaro.org>" [full] * tag 'pull-maintainer-9.1-rc0-230724-1' of https://gitlab.com/stsquad/qemu: tests/tcg/aarch64: Fix test-mte.py semihosting: Restrict to TCG target/xtensa: Restrict semihosting to TCG target/riscv: Restrict semihosting to TCG target/mips: Restrict semihosting to TCG target/m68k: Restrict semihosting to TCG target/mips: Add semihosting stub target/m68k: Add semihosting stub semihosting: Include missing 'gdbstub/syscalls.h' header plugins/execlog.c: correct dump of registers values tests/plugins: use qemu_plugin_outs for inline stats plugins: fix mem callback array size plugins/stoptrigger: TCG plugin to stop execution under conditions gdbstub: Re-factor gdb command extensions tests/avocado: Remove non-working sparc leon3 test testing: bump to latest libvirt-ci Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
commit
26b09663a9
@ -11,6 +11,6 @@ MAKE='/usr/local/bin/gmake'
|
||||
NINJA='/usr/local/bin/ninja'
|
||||
PACKAGING_COMMAND='pkg'
|
||||
PIP3='/usr/local/bin/pip-3.8'
|
||||
PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson mtools ncurses nettle ninja opencv pixman pkgconf png py39-numpy py39-pillow py39-pip py39-sphinx py39-sphinx_rtd_theme py39-tomli py39-yaml python3 rpm2cpio sdl2 sdl2_image snappy sndio socat spice-protocol tesseract usbredir virglrenderer vte3 xorriso zstd'
|
||||
PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson mtools ncurses nettle ninja opencv pixman pkgconf png py311-numpy py311-pillow py311-pip py311-sphinx py311-sphinx_rtd_theme py311-tomli py311-yaml python3 rpm2cpio sdl2 sdl2_image snappy sndio socat spice-protocol tesseract usbredir virglrenderer vte3 xorriso zstd'
|
||||
PYPI_PKGS=''
|
||||
PYTHON='/usr/local/bin/python3'
|
||||
|
@ -1727,7 +1727,6 @@ S: Maintained
|
||||
F: hw/sparc/leon3.c
|
||||
F: hw/*/grlib*
|
||||
F: include/hw/*/grlib*
|
||||
F: tests/avocado/machine_sparc_leon3.py
|
||||
|
||||
S390 Machines
|
||||
-------------
|
||||
|
@ -85,8 +85,7 @@ static void gen_enable_mem_helper(struct qemu_plugin_tb *ptb,
|
||||
len = insn->mem_cbs->len;
|
||||
arr = g_array_sized_new(false, false,
|
||||
sizeof(struct qemu_plugin_dyn_cb), len);
|
||||
memcpy(arr->data, insn->mem_cbs->data,
|
||||
len * sizeof(struct qemu_plugin_dyn_cb));
|
||||
g_array_append_vals(arr, insn->mem_cbs->data, len);
|
||||
qemu_plugin_add_dyn_cb_arr(arr);
|
||||
|
||||
tcg_gen_st_ptr(tcg_constant_ptr((intptr_t)arr), tcg_env,
|
||||
|
@ -28,6 +28,7 @@ NAMES += hwprofile
|
||||
NAMES += cache
|
||||
NAMES += drcov
|
||||
NAMES += ips
|
||||
NAMES += stoptrigger
|
||||
|
||||
ifeq ($(CONFIG_WIN32),y)
|
||||
SO_SUFFIX := .dll
|
||||
|
@ -101,7 +101,7 @@ static void insn_check_regs(CPU *cpu)
|
||||
GByteArray *temp = reg->last;
|
||||
g_string_append_printf(cpu->last_exec, ", %s -> 0x", reg->name);
|
||||
/* TODO: handle BE properly */
|
||||
for (int i = sz; i >= 0; i--) {
|
||||
for (int i = sz - 1; i >= 0; i--) {
|
||||
g_string_append_printf(cpu->last_exec, "%02x",
|
||||
reg->new->data[i]);
|
||||
}
|
||||
|
151
contrib/plugins/stoptrigger.c
Normal file
151
contrib/plugins/stoptrigger.c
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2024, Simon Hamelin <simon.hamelin@grenoble-inp.org>
|
||||
*
|
||||
* Stop execution once a given address is reached or if the
|
||||
* count of executed instructions reached a specified limit
|
||||
*
|
||||
* License: GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <glib.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <qemu-plugin.h>
|
||||
|
||||
QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
|
||||
|
||||
/* Scoreboard to track executed instructions count */
|
||||
typedef struct {
|
||||
uint64_t insn_count;
|
||||
} InstructionsCount;
|
||||
static struct qemu_plugin_scoreboard *insn_count_sb;
|
||||
static qemu_plugin_u64 insn_count;
|
||||
|
||||
static uint64_t icount;
|
||||
static int icount_exit_code;
|
||||
|
||||
static bool exit_on_icount;
|
||||
static bool exit_on_address;
|
||||
|
||||
/* Map trigger addresses to exit code */
|
||||
static GHashTable *addrs_ht;
|
||||
|
||||
static void exit_emulation(int return_code, char *message)
|
||||
{
|
||||
qemu_plugin_outs(message);
|
||||
g_free(message);
|
||||
exit(return_code);
|
||||
}
|
||||
|
||||
static void exit_icount_reached(unsigned int cpu_index, void *udata)
|
||||
{
|
||||
uint64_t insn_vaddr = GPOINTER_TO_UINT(udata);
|
||||
char *msg = g_strdup_printf("icount reached at 0x%" PRIx64 ", exiting\n",
|
||||
insn_vaddr);
|
||||
|
||||
exit_emulation(icount_exit_code, msg);
|
||||
}
|
||||
|
||||
static void exit_address_reached(unsigned int cpu_index, void *udata)
|
||||
{
|
||||
uint64_t insn_vaddr = GPOINTER_TO_UINT(udata);
|
||||
char *msg = g_strdup_printf("0x%" PRIx64 " reached, exiting\n", insn_vaddr);
|
||||
int exit_code;
|
||||
|
||||
exit_code = GPOINTER_TO_INT(
|
||||
g_hash_table_lookup(addrs_ht, GUINT_TO_POINTER(insn_vaddr)));
|
||||
|
||||
exit_emulation(exit_code, msg);
|
||||
}
|
||||
|
||||
static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
|
||||
{
|
||||
size_t tb_n = qemu_plugin_tb_n_insns(tb);
|
||||
for (size_t i = 0; i < tb_n; i++) {
|
||||
struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
|
||||
gpointer insn_vaddr = GUINT_TO_POINTER(qemu_plugin_insn_vaddr(insn));
|
||||
|
||||
if (exit_on_icount) {
|
||||
/* Increment and check scoreboard for each instruction */
|
||||
qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
|
||||
insn, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, 1);
|
||||
qemu_plugin_register_vcpu_insn_exec_cond_cb(
|
||||
insn, exit_icount_reached, QEMU_PLUGIN_CB_NO_REGS,
|
||||
QEMU_PLUGIN_COND_EQ, insn_count, icount + 1, insn_vaddr);
|
||||
}
|
||||
|
||||
if (exit_on_address) {
|
||||
if (g_hash_table_contains(addrs_ht, insn_vaddr)) {
|
||||
/* Exit triggered by address */
|
||||
qemu_plugin_register_vcpu_insn_exec_cb(
|
||||
insn, exit_address_reached, QEMU_PLUGIN_CB_NO_REGS,
|
||||
insn_vaddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
{
|
||||
g_hash_table_destroy(addrs_ht);
|
||||
qemu_plugin_scoreboard_free(insn_count_sb);
|
||||
}
|
||||
|
||||
QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
|
||||
const qemu_info_t *info, int argc,
|
||||
char **argv)
|
||||
{
|
||||
addrs_ht = g_hash_table_new(NULL, g_direct_equal);
|
||||
|
||||
insn_count_sb = qemu_plugin_scoreboard_new(sizeof(InstructionsCount));
|
||||
insn_count = qemu_plugin_scoreboard_u64_in_struct(
|
||||
insn_count_sb, InstructionsCount, insn_count);
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
char *opt = argv[i];
|
||||
g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
|
||||
if (g_strcmp0(tokens[0], "icount") == 0) {
|
||||
g_auto(GStrv) icount_tokens = g_strsplit(tokens[1], ":", 2);
|
||||
icount = g_ascii_strtoull(icount_tokens[0], NULL, 0);
|
||||
if (icount < 1 || g_strrstr(icount_tokens[0], "-") != NULL) {
|
||||
fprintf(stderr,
|
||||
"icount parsing failed: '%s' must be a positive "
|
||||
"integer\n",
|
||||
icount_tokens[0]);
|
||||
return -1;
|
||||
}
|
||||
if (icount_tokens[1]) {
|
||||
icount_exit_code = g_ascii_strtoull(icount_tokens[1], NULL, 0);
|
||||
}
|
||||
exit_on_icount = true;
|
||||
} else if (g_strcmp0(tokens[0], "addr") == 0) {
|
||||
g_auto(GStrv) addr_tokens = g_strsplit(tokens[1], ":", 2);
|
||||
uint64_t exit_addr = g_ascii_strtoull(addr_tokens[0], NULL, 0);
|
||||
int exit_code = 0;
|
||||
if (addr_tokens[1]) {
|
||||
exit_code = g_ascii_strtoull(addr_tokens[1], NULL, 0);
|
||||
}
|
||||
g_hash_table_insert(addrs_ht, GUINT_TO_POINTER(exit_addr),
|
||||
GINT_TO_POINTER(exit_code));
|
||||
exit_on_address = true;
|
||||
} else {
|
||||
fprintf(stderr, "option parsing failed: %s\n", opt);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!exit_on_icount && !exit_on_address) {
|
||||
fprintf(stderr, "'icount' or 'addr' argument missing\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Register translation block and exit callbacks */
|
||||
qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
|
||||
qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
@ -642,6 +642,28 @@ The plugin has a number of arguments, all of them are optional:
|
||||
configuration arguments implies ``l2=on``.
|
||||
(default: N = 2097152 (2MB), B = 64, A = 16)
|
||||
|
||||
- contrib/plugins/stoptrigger.c
|
||||
|
||||
The stoptrigger plugin allows to setup triggers to stop emulation.
|
||||
It can be used for research purposes to launch some code and precisely stop it
|
||||
and understand where its execution flow went.
|
||||
|
||||
Two types of triggers can be configured: a count of instructions to stop at,
|
||||
or an address to stop at. Multiple triggers can be set at once.
|
||||
|
||||
By default, QEMU will exit with return code 0. A custom return code can be
|
||||
configured for each trigger using ``:CODE`` syntax.
|
||||
|
||||
For example, to stop at the 20-th instruction with return code 41, at address
|
||||
0xd4 with return code 0 or at address 0xd8 with return code 42::
|
||||
|
||||
$ qemu-system-aarch64 $(QEMU_ARGS) \
|
||||
-plugin ./contrib/plugins/libstoptrigger.so,icount=20:41,addr=0xd4,addr=0xd8:42 -d plugin
|
||||
|
||||
The plugin will log the reason of exit, for example::
|
||||
|
||||
0xd4 reached, exiting
|
||||
|
||||
Plugin API
|
||||
==========
|
||||
|
||||
|
@ -1614,18 +1614,21 @@ static void handle_query_thread_extra(GArray *params, void *user_ctx)
|
||||
gdb_put_strbuf();
|
||||
}
|
||||
|
||||
static char *extended_qsupported_features;
|
||||
void gdb_extend_qsupported_features(char *qsupported_features)
|
||||
{
|
||||
/*
|
||||
* We don't support different sets of CPU gdb features on different CPUs yet
|
||||
* so assert the feature strings are the same on all CPUs, or is set only
|
||||
* once (1 CPU).
|
||||
*/
|
||||
g_assert(extended_qsupported_features == NULL ||
|
||||
g_strcmp0(extended_qsupported_features, qsupported_features) == 0);
|
||||
|
||||
extended_qsupported_features = qsupported_features;
|
||||
static char **extra_query_flags;
|
||||
|
||||
void gdb_extend_qsupported_features(char *qflags)
|
||||
{
|
||||
if (!extra_query_flags) {
|
||||
extra_query_flags = g_new0(char *, 2);
|
||||
extra_query_flags[0] = g_strdup(qflags);
|
||||
} else if (!g_strv_contains((const gchar * const *) extra_query_flags,
|
||||
qflags)) {
|
||||
int len = g_strv_length(extra_query_flags);
|
||||
extra_query_flags = g_realloc_n(extra_query_flags, len + 2,
|
||||
sizeof(char *));
|
||||
extra_query_flags[len] = g_strdup(qflags);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_query_supported(GArray *params, void *user_ctx)
|
||||
@ -1668,8 +1671,11 @@ static void handle_query_supported(GArray *params, void *user_ctx)
|
||||
|
||||
g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+");
|
||||
|
||||
if (extended_qsupported_features) {
|
||||
g_string_append(gdbserver_state.str_buf, extended_qsupported_features);
|
||||
if (extra_query_flags) {
|
||||
int extras = g_strv_length(extra_query_flags);
|
||||
for (int i = 0; i < extras; i++) {
|
||||
g_string_append(gdbserver_state.str_buf, extra_query_flags[i]);
|
||||
}
|
||||
}
|
||||
|
||||
gdb_put_strbuf();
|
||||
@ -1753,39 +1759,58 @@ static const GdbCmdParseEntry gdb_gen_query_set_common_table[] = {
|
||||
},
|
||||
};
|
||||
|
||||
/* Compares if a set of command parsers is equal to another set of parsers. */
|
||||
static bool cmp_cmds(GdbCmdParseEntry *c, GdbCmdParseEntry *d, int size)
|
||||
/**
|
||||
* extend_table() - extend one of the command tables
|
||||
* @table: the command table to extend (or NULL)
|
||||
* @extensions: a list of GdbCmdParseEntry pointers
|
||||
*
|
||||
* The entries themselves should be pointers to static const
|
||||
* GdbCmdParseEntry entries. If the entry is already in the table we
|
||||
* skip adding it again.
|
||||
*
|
||||
* Returns (a potentially freshly allocated) GPtrArray of GdbCmdParseEntry
|
||||
*/
|
||||
static GPtrArray *extend_table(GPtrArray *table, GPtrArray *extensions)
|
||||
{
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (!(c[i].handler == d[i].handler &&
|
||||
g_strcmp0(c[i].cmd, d[i].cmd) == 0 &&
|
||||
c[i].cmd_startswith == d[i].cmd_startswith &&
|
||||
g_strcmp0(c[i].schema, d[i].schema) == 0)) {
|
||||
if (!table) {
|
||||
table = g_ptr_array_new();
|
||||
}
|
||||
|
||||
/* Sets are different. */
|
||||
return false;
|
||||
for (int i = 0; i < extensions->len; i++) {
|
||||
gpointer entry = g_ptr_array_index(extensions, i);
|
||||
if (!g_ptr_array_find(table, entry, NULL)) {
|
||||
g_ptr_array_add(table, entry);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sets are equal, i.e. contain the same command parsers. */
|
||||
return true;
|
||||
return table;
|
||||
}
|
||||
|
||||
static GdbCmdParseEntry *extended_query_table;
|
||||
static int extended_query_table_size;
|
||||
void gdb_extend_query_table(GdbCmdParseEntry *table, int size)
|
||||
/**
|
||||
* process_extended_table() - run through an extended command table
|
||||
* @table: the command table to check
|
||||
* @data: parameters
|
||||
*
|
||||
* returns true if the command was found and executed
|
||||
*/
|
||||
static bool process_extended_table(GPtrArray *table, const char *data)
|
||||
{
|
||||
/*
|
||||
* We don't support different sets of CPU gdb features on different CPUs yet
|
||||
* so assert query table is the same on all CPUs, or is set only once
|
||||
* (1 CPU).
|
||||
*/
|
||||
g_assert(extended_query_table == NULL ||
|
||||
(extended_query_table_size == size &&
|
||||
cmp_cmds(extended_query_table, table, size)));
|
||||
for (int i = 0; i < table->len; i++) {
|
||||
const GdbCmdParseEntry *entry = g_ptr_array_index(table, i);
|
||||
if (process_string_cmd(data, entry, 1)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
extended_query_table = table;
|
||||
extended_query_table_size = size;
|
||||
|
||||
/* Ptr to GdbCmdParseEntry */
|
||||
static GPtrArray *extended_query_table;
|
||||
|
||||
void gdb_extend_query_table(GPtrArray *new_queries)
|
||||
{
|
||||
extended_query_table = extend_table(extended_query_table, new_queries);
|
||||
}
|
||||
|
||||
static const GdbCmdParseEntry gdb_gen_query_table[] = {
|
||||
@ -1880,20 +1905,12 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = {
|
||||
#endif
|
||||
};
|
||||
|
||||
static GdbCmdParseEntry *extended_set_table;
|
||||
static int extended_set_table_size;
|
||||
void gdb_extend_set_table(GdbCmdParseEntry *table, int size)
|
||||
{
|
||||
/*
|
||||
* We don't support different sets of CPU gdb features on different CPUs yet
|
||||
* so assert set table is the same on all CPUs, or is set only once (1 CPU).
|
||||
*/
|
||||
g_assert(extended_set_table == NULL ||
|
||||
(extended_set_table_size == size &&
|
||||
cmp_cmds(extended_set_table, table, size)));
|
||||
/* Ptr to GdbCmdParseEntry */
|
||||
static GPtrArray *extended_set_table;
|
||||
|
||||
extended_set_table = table;
|
||||
extended_set_table_size = size;
|
||||
void gdb_extend_set_table(GPtrArray *new_set)
|
||||
{
|
||||
extended_set_table = extend_table(extended_set_table, new_set);
|
||||
}
|
||||
|
||||
static const GdbCmdParseEntry gdb_gen_set_table[] = {
|
||||
@ -1924,26 +1941,28 @@ static const GdbCmdParseEntry gdb_gen_set_table[] = {
|
||||
|
||||
static void handle_gen_query(GArray *params, void *user_ctx)
|
||||
{
|
||||
const char *data;
|
||||
|
||||
if (!params->len) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (process_string_cmd(gdb_get_cmd_param(params, 0)->data,
|
||||
data = gdb_get_cmd_param(params, 0)->data;
|
||||
|
||||
if (process_string_cmd(data,
|
||||
gdb_gen_query_set_common_table,
|
||||
ARRAY_SIZE(gdb_gen_query_set_common_table))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (process_string_cmd(gdb_get_cmd_param(params, 0)->data,
|
||||
if (process_string_cmd(data,
|
||||
gdb_gen_query_table,
|
||||
ARRAY_SIZE(gdb_gen_query_table))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (extended_query_table &&
|
||||
process_string_cmd(gdb_get_cmd_param(params, 0)->data,
|
||||
extended_query_table,
|
||||
extended_query_table_size)) {
|
||||
process_extended_table(extended_query_table, data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1953,26 +1972,28 @@ static void handle_gen_query(GArray *params, void *user_ctx)
|
||||
|
||||
static void handle_gen_set(GArray *params, void *user_ctx)
|
||||
{
|
||||
const char *data;
|
||||
|
||||
if (!params->len) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (process_string_cmd(gdb_get_cmd_param(params, 0)->data,
|
||||
data = gdb_get_cmd_param(params, 0)->data;
|
||||
|
||||
if (process_string_cmd(data,
|
||||
gdb_gen_query_set_common_table,
|
||||
ARRAY_SIZE(gdb_gen_query_set_common_table))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (process_string_cmd(gdb_get_cmd_param(params, 0)->data,
|
||||
if (process_string_cmd(data,
|
||||
gdb_gen_set_table,
|
||||
ARRAY_SIZE(gdb_gen_set_table))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (extended_set_table &&
|
||||
process_string_cmd(gdb_get_cmd_param(params, 0)->data,
|
||||
extended_set_table,
|
||||
extended_set_table_size)) {
|
||||
process_extended_table(extended_set_table, data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -74,23 +74,28 @@ int gdb_put_packet(const char *buf);
|
||||
|
||||
/**
|
||||
* gdb_extend_query_table() - Extend query table.
|
||||
* @table: The table with the additional query packet handlers.
|
||||
* @size: The number of handlers to be added.
|
||||
* @table: GPtrArray of GdbCmdParseEntry entries.
|
||||
*
|
||||
* The caller should free @table afterwards
|
||||
*/
|
||||
void gdb_extend_query_table(GdbCmdParseEntry *table, int size);
|
||||
void gdb_extend_query_table(GPtrArray *table);
|
||||
|
||||
/**
|
||||
* gdb_extend_set_table() - Extend set table.
|
||||
* @table: The table with the additional set packet handlers.
|
||||
* @size: The number of handlers to be added.
|
||||
* @table: GPtrArray of GdbCmdParseEntry entries.
|
||||
*
|
||||
* The caller should free @table afterwards
|
||||
*/
|
||||
void gdb_extend_set_table(GdbCmdParseEntry *table, int size);
|
||||
void gdb_extend_set_table(GPtrArray *table);
|
||||
|
||||
/**
|
||||
* gdb_extend_qsupported_features() - Extend the qSupported features string.
|
||||
* @qsupported_features: The additional qSupported feature(s) string. The string
|
||||
* should start with a semicolon and, if there are more than one feature, the
|
||||
* features should be separate by a semiocolon.
|
||||
* features should be separate by a semicolon.
|
||||
*
|
||||
* The caller should free @qsupported_features afterwards if
|
||||
* dynamically allocated.
|
||||
*/
|
||||
void gdb_extend_qsupported_features(char *qsupported_features);
|
||||
|
||||
|
@ -9,6 +9,8 @@
|
||||
#ifndef SEMIHOSTING_SYSCALLS_H
|
||||
#define SEMIHOSTING_SYSCALLS_H
|
||||
|
||||
#include "gdbstub/syscalls.h"
|
||||
|
||||
/*
|
||||
* Argument loading from the guest is performed by the caller;
|
||||
* results are returned via the 'complete' callback.
|
||||
|
@ -1,6 +1,7 @@
|
||||
|
||||
config SEMIHOSTING
|
||||
bool
|
||||
depends on TCG
|
||||
|
||||
config ARM_COMPATIBLE_SEMIHOSTING
|
||||
bool
|
||||
|
@ -477,11 +477,9 @@ static GDBFeature *arm_gen_dynamic_m_secextreg_feature(CPUState *cs,
|
||||
|
||||
void arm_cpu_register_gdb_commands(ARMCPU *cpu)
|
||||
{
|
||||
GArray *query_table =
|
||||
g_array_new(FALSE, FALSE, sizeof(GdbCmdParseEntry));
|
||||
GArray *set_table =
|
||||
g_array_new(FALSE, FALSE, sizeof(GdbCmdParseEntry));
|
||||
GString *qsupported_features = g_string_new(NULL);
|
||||
g_autoptr(GPtrArray) query_table = g_ptr_array_new();
|
||||
g_autoptr(GPtrArray) set_table = g_ptr_array_new();
|
||||
g_autoptr(GString) qsupported_features = g_string_new(NULL);
|
||||
|
||||
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
|
||||
#ifdef TARGET_AARCH64
|
||||
@ -492,16 +490,12 @@ void arm_cpu_register_gdb_commands(ARMCPU *cpu)
|
||||
|
||||
/* Set arch-specific handlers for 'q' commands. */
|
||||
if (query_table->len) {
|
||||
gdb_extend_query_table(&g_array_index(query_table,
|
||||
GdbCmdParseEntry, 0),
|
||||
query_table->len);
|
||||
gdb_extend_query_table(query_table);
|
||||
}
|
||||
|
||||
/* Set arch-specific handlers for 'Q' commands. */
|
||||
if (set_table->len) {
|
||||
gdb_extend_set_table(&g_array_index(set_table,
|
||||
GdbCmdParseEntry, 0),
|
||||
set_table->len);
|
||||
gdb_extend_set_table(set_table);
|
||||
}
|
||||
|
||||
/* Set arch-specific qSupported feature. */
|
||||
|
@ -564,7 +564,7 @@ enum Command {
|
||||
NUM_CMDS
|
||||
};
|
||||
|
||||
static GdbCmdParseEntry cmd_handler_table[NUM_CMDS] = {
|
||||
static const GdbCmdParseEntry cmd_handler_table[NUM_CMDS] = {
|
||||
[qMemTags] = {
|
||||
.handler = handle_q_memtag,
|
||||
.cmd_startswith = true,
|
||||
@ -590,17 +590,16 @@ static GdbCmdParseEntry cmd_handler_table[NUM_CMDS] = {
|
||||
#endif /* CONFIG_USER_ONLY */
|
||||
|
||||
void aarch64_cpu_register_gdb_commands(ARMCPU *cpu, GString *qsupported,
|
||||
GArray *qtable, GArray *stable)
|
||||
GPtrArray *qtable, GPtrArray *stable)
|
||||
{
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/* MTE */
|
||||
if (cpu_isar_feature(aa64_mte, cpu)) {
|
||||
g_string_append(qsupported, ";memory-tagging+");
|
||||
|
||||
g_array_append_val(qtable, cmd_handler_table[qMemTags]);
|
||||
g_array_append_val(qtable, cmd_handler_table[qIsAddressTagged]);
|
||||
|
||||
g_array_append_val(stable, cmd_handler_table[QMemTags]);
|
||||
g_ptr_array_add(qtable, (gpointer) &cmd_handler_table[qMemTags]);
|
||||
g_ptr_array_add(qtable, (gpointer) &cmd_handler_table[qIsAddressTagged]);
|
||||
g_ptr_array_add(stable, (gpointer) &cmd_handler_table[QMemTags]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -359,8 +359,8 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu);
|
||||
void arm_translate_init(void);
|
||||
|
||||
void arm_cpu_register_gdb_commands(ARMCPU *cpu);
|
||||
void aarch64_cpu_register_gdb_commands(ARMCPU *cpu, GString *, GArray *,
|
||||
GArray *);
|
||||
void aarch64_cpu_register_gdb_commands(ARMCPU *cpu, GString *,
|
||||
GPtrArray *, GPtrArray *);
|
||||
|
||||
void arm_restore_state_to_opc(CPUState *cs,
|
||||
const TranslationBlock *tb,
|
||||
|
@ -1,3 +1,3 @@
|
||||
config M68K
|
||||
bool
|
||||
select SEMIHOSTING
|
||||
imply SEMIHOSTING if TCG
|
||||
|
@ -11,9 +11,12 @@ m68k_ss.add(files(
|
||||
|
||||
m68k_system_ss = ss.source_set()
|
||||
m68k_system_ss.add(files(
|
||||
'm68k-semi.c',
|
||||
'monitor.c'
|
||||
))
|
||||
m68k_system_ss.add(when: ['CONFIG_SEMIHOSTING'],
|
||||
if_true: files('m68k-semi.c'),
|
||||
if_false: files('semihosting-stub.c')
|
||||
)
|
||||
|
||||
target_arch += {'m68k': m68k_ss}
|
||||
target_system_arch += {'m68k': m68k_system_ss}
|
||||
|
15
target/m68k/semihosting-stub.c
Normal file
15
target/m68k/semihosting-stub.c
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* m68k/ColdFire semihosting stub
|
||||
*
|
||||
* SPDX-FileContributor: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
* SPDX-FileCopyrightText: 2024 Linaro Ltd.
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
|
||||
void do_m68k_semihosting(CPUM68KState *env, int nr)
|
||||
{
|
||||
g_assert_not_reached();
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
config MIPS
|
||||
bool
|
||||
select SEMIHOSTING
|
||||
imply SEMIHOSTING if TCG
|
||||
|
||||
config MIPS64
|
||||
bool
|
||||
|
@ -1,10 +1,12 @@
|
||||
mips_system_ss.add(files(
|
||||
'cp0_helper.c',
|
||||
'mips-semi.c',
|
||||
'special_helper.c',
|
||||
'tlb_helper.c',
|
||||
))
|
||||
|
||||
mips_system_ss.add(when: ['CONFIG_SEMIHOSTING'],
|
||||
if_true: files('mips-semi.c'),
|
||||
if_false: files('semihosting-stub.c')
|
||||
)
|
||||
mips_system_ss.add(when: 'TARGET_MIPS64', if_true: files(
|
||||
'lcsr_helper.c',
|
||||
))
|
||||
|
15
target/mips/tcg/sysemu/semihosting-stub.c
Normal file
15
target/mips/tcg/sysemu/semihosting-stub.c
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* MIPS semihosting stub
|
||||
*
|
||||
* SPDX-FileContributor: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
* SPDX-FileCopyrightText: 2024 Linaro Ltd.
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "internal.h"
|
||||
|
||||
void mips_semihosting(CPUMIPSState *env)
|
||||
{
|
||||
g_assert_not_reached();
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
config RISCV32
|
||||
bool
|
||||
select ARM_COMPATIBLE_SEMIHOSTING # for do_common_semihosting()
|
||||
imply ARM_COMPATIBLE_SEMIHOSTING if TCG
|
||||
select DEVICE_TREE # needed by boot.c
|
||||
|
||||
config RISCV64
|
||||
bool
|
||||
select ARM_COMPATIBLE_SEMIHOSTING # for do_common_semihosting()
|
||||
imply ARM_COMPATIBLE_SEMIHOSTING if TCG
|
||||
select DEVICE_TREE # needed by boot.c
|
||||
|
@ -1,3 +1,3 @@
|
||||
config XTENSA
|
||||
bool
|
||||
select SEMIHOSTING
|
||||
imply SEMIHOSTING if TCG
|
||||
|
@ -1,37 +0,0 @@
|
||||
# Functional test that boots a Leon3 machine and checks its serial console.
|
||||
#
|
||||
# Copyright (c) Philippe Mathieu-Daudé <f4bug@amsat.org>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
# later. See the COPYING file in the top-level directory.
|
||||
|
||||
from avocado_qemu import QemuSystemTest
|
||||
from avocado_qemu import wait_for_console_pattern
|
||||
from avocado import skip
|
||||
|
||||
|
||||
class Leon3Machine(QemuSystemTest):
|
||||
|
||||
timeout = 60
|
||||
|
||||
@skip("Test currently broken")
|
||||
# A Window Underflow exception occurs before booting the kernel,
|
||||
# and QEMU exit calling cpu_abort(), which makes this test to fail.
|
||||
def test_leon3_helenos_uimage(self):
|
||||
"""
|
||||
:avocado: tags=arch:sparc
|
||||
:avocado: tags=machine:leon3_generic
|
||||
:avocado: tags=binfmt:uimage
|
||||
"""
|
||||
kernel_url = ('http://www.helenos.org/releases/'
|
||||
'HelenOS-0.6.0-sparc32-leon3.bin')
|
||||
kernel_hash = 'a88c9cfdb8430c66650e5290a08765f9bf049a30'
|
||||
kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
|
||||
|
||||
self.vm.set_console()
|
||||
self.vm.add_args('-kernel', kernel_path)
|
||||
|
||||
self.vm.launch()
|
||||
|
||||
wait_for_console_pattern(self, 'Copyright (c) 2001-2014 HelenOS project')
|
||||
wait_for_console_pattern(self, 'Booting the kernel ...')
|
@ -1 +1 @@
|
||||
Subproject commit 0e9490cebc726ef772b6c9e27dac32e7ae99f9b2
|
||||
Subproject commit 789b4601bce4e01f43fdb6ad4ce5ab4e46674440
|
@ -71,10 +71,12 @@ static void stats_insn(void)
|
||||
const uint64_t cond_track_left = qemu_plugin_u64_sum(insn_cond_track_count);
|
||||
const uint64_t conditional =
|
||||
cond_num_trigger * cond_trigger_limit + cond_track_left;
|
||||
printf("insn: %" PRIu64 "\n", expected);
|
||||
printf("insn: %" PRIu64 " (per vcpu)\n", per_vcpu);
|
||||
printf("insn: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu);
|
||||
printf("insn: %" PRIu64 " (cond cb)\n", conditional);
|
||||
g_autoptr(GString) stats = g_string_new("");
|
||||
g_string_append_printf(stats, "insn: %" PRIu64 "\n", expected);
|
||||
g_string_append_printf(stats, "insn: %" PRIu64 " (per vcpu)\n", per_vcpu);
|
||||
g_string_append_printf(stats, "insn: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu);
|
||||
g_string_append_printf(stats, "insn: %" PRIu64 " (cond cb)\n", conditional);
|
||||
qemu_plugin_outs(stats->str);
|
||||
g_assert(expected > 0);
|
||||
g_assert(per_vcpu == expected);
|
||||
g_assert(inl_per_vcpu == expected);
|
||||
@ -91,10 +93,12 @@ static void stats_tb(void)
|
||||
const uint64_t cond_track_left = qemu_plugin_u64_sum(tb_cond_track_count);
|
||||
const uint64_t conditional =
|
||||
cond_num_trigger * cond_trigger_limit + cond_track_left;
|
||||
printf("tb: %" PRIu64 "\n", expected);
|
||||
printf("tb: %" PRIu64 " (per vcpu)\n", per_vcpu);
|
||||
printf("tb: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu);
|
||||
printf("tb: %" PRIu64 " (conditional cb)\n", conditional);
|
||||
g_autoptr(GString) stats = g_string_new("");
|
||||
g_string_append_printf(stats, "tb: %" PRIu64 "\n", expected);
|
||||
g_string_append_printf(stats, "tb: %" PRIu64 " (per vcpu)\n", per_vcpu);
|
||||
g_string_append_printf(stats, "tb: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu);
|
||||
g_string_append_printf(stats, "tb: %" PRIu64 " (conditional cb)\n", conditional);
|
||||
qemu_plugin_outs(stats->str);
|
||||
g_assert(expected > 0);
|
||||
g_assert(per_vcpu == expected);
|
||||
g_assert(inl_per_vcpu == expected);
|
||||
@ -107,9 +111,11 @@ static void stats_mem(void)
|
||||
const uint64_t per_vcpu = qemu_plugin_u64_sum(count_mem);
|
||||
const uint64_t inl_per_vcpu =
|
||||
qemu_plugin_u64_sum(count_mem_inline);
|
||||
printf("mem: %" PRIu64 "\n", expected);
|
||||
printf("mem: %" PRIu64 " (per vcpu)\n", per_vcpu);
|
||||
printf("mem: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu);
|
||||
g_autoptr(GString) stats = g_string_new("");
|
||||
g_string_append_printf(stats, "mem: %" PRIu64 "\n", expected);
|
||||
g_string_append_printf(stats, "mem: %" PRIu64 " (per vcpu)\n", per_vcpu);
|
||||
g_string_append_printf(stats, "mem: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu);
|
||||
qemu_plugin_outs(stats->str);
|
||||
g_assert(expected > 0);
|
||||
g_assert(per_vcpu == expected);
|
||||
g_assert(inl_per_vcpu == expected);
|
||||
@ -118,6 +124,7 @@ static void stats_mem(void)
|
||||
static void plugin_exit(qemu_plugin_id_t id, void *udata)
|
||||
{
|
||||
const unsigned int num_cpus = qemu_plugin_num_vcpus();
|
||||
g_autoptr(GString) stats = g_string_new("");
|
||||
g_assert(num_cpus == max_cpu_index + 1);
|
||||
|
||||
for (int i = 0; i < num_cpus ; ++i) {
|
||||
@ -135,20 +142,21 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata)
|
||||
qemu_plugin_u64_get(insn_cond_num_trigger, i);
|
||||
const uint64_t insn_cond_left =
|
||||
qemu_plugin_u64_get(insn_cond_track_count, i);
|
||||
printf("cpu %d: tb (%" PRIu64 ", %" PRIu64
|
||||
", %" PRIu64 " * %" PRIu64 " + %" PRIu64
|
||||
") | "
|
||||
"insn (%" PRIu64 ", %" PRIu64
|
||||
", %" PRIu64 " * %" PRIu64 " + %" PRIu64
|
||||
") | "
|
||||
"mem (%" PRIu64 ", %" PRIu64 ")"
|
||||
"\n",
|
||||
i,
|
||||
tb, tb_inline,
|
||||
tb_cond_trigger, cond_trigger_limit, tb_cond_left,
|
||||
insn, insn_inline,
|
||||
insn_cond_trigger, cond_trigger_limit, insn_cond_left,
|
||||
mem, mem_inline);
|
||||
g_string_printf(stats, "cpu %d: tb (%" PRIu64 ", %" PRIu64
|
||||
", %" PRIu64 " * %" PRIu64 " + %" PRIu64
|
||||
") | "
|
||||
"insn (%" PRIu64 ", %" PRIu64
|
||||
", %" PRIu64 " * %" PRIu64 " + %" PRIu64
|
||||
") | "
|
||||
"mem (%" PRIu64 ", %" PRIu64 ")"
|
||||
"\n",
|
||||
i,
|
||||
tb, tb_inline,
|
||||
tb_cond_trigger, cond_trigger_limit, tb_cond_left,
|
||||
insn, insn_inline,
|
||||
insn_cond_trigger, cond_trigger_limit, insn_cond_left,
|
||||
mem, mem_inline);
|
||||
qemu_plugin_outs(stats->str);
|
||||
g_assert(tb == tb_inline);
|
||||
g_assert(insn == insn_inline);
|
||||
g_assert(mem == mem_inline);
|
||||
|
@ -18,7 +18,7 @@ import re
|
||||
from test_gdbstub import main, report
|
||||
|
||||
|
||||
PATTERN_0 = "Memory tags for address 0x[0-9a-f]+ match \(0x[0-9a-f]+\)."
|
||||
PATTERN_0 = "Memory tags for address 0x[0-9a-f]+ match \\(0x[0-9a-f]+\\)."
|
||||
PATTERN_1 = ".*(0x[0-9a-f]+)"
|
||||
|
||||
|
||||
|
@ -51,13 +51,13 @@
|
||||
"pixman",
|
||||
"pkgconf",
|
||||
"png",
|
||||
"py39-numpy",
|
||||
"py39-pillow",
|
||||
"py39-pip",
|
||||
"py39-sphinx",
|
||||
"py39-sphinx_rtd_theme",
|
||||
"py39-tomli",
|
||||
"py39-yaml",
|
||||
"py311-numpy",
|
||||
"py311-pillow",
|
||||
"py311-pip",
|
||||
"py311-sphinx",
|
||||
"py311-sphinx_rtd_theme",
|
||||
"py311-tomli",
|
||||
"py311-yaml",
|
||||
"python3",
|
||||
"rpm2cpio",
|
||||
"sdl2",
|
||||
|
Loading…
Reference in New Issue
Block a user