maintainer updates for 10.2

- clean-up remaining 32 bit armhf bits in ci
   - rationalise build-environment.yml for Debian and Ubuntu
   - generate a Debian ppc64 package list
   - rationalise gitlab-runner.yml for Debian and Ubuntu
   - new TCG plugin feature to track discontinuities
   - add missing CFI annotation to plugin callbacks
   - drop SBSA_REF from minimal Arm build
   - format string fix for gdbstub syscall response
   - simplify the gdbstub flen handling for semihosting
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEZoWumedRZ7yvyN81+9DbCVqeKkQFAmkCInQACgkQ+9DbCVqe
 KkSZRwf/ReHIqQMxf8TqthskX8PLGUvsvWMkJptpu0Yc4HyU6DSjdPbU4L0tOmLU
 ss2sb+dZncp1iRxHpqOhPJ+a987RHHzFbz2GQ/nV37D7BTwtq0iID4SxmdfiYOAm
 VVm/WQ0HMjIYY84rzfE6U/3H+FgL+GaPbB0WYa5CtKpMOHMl4gJgoSsxljXQrmYe
 BCC+Z9loVUAnKVVM5BUMP/0Agfn0CUZlUHGEn6RvmNg81dJ5DWCfO9yW1EezLZmc
 PhS/txAWrpTqktPxN4h+um8ILvej5FF8nnNCsxodxD1XZImWsxawxcQAcgQQJGWu
 dFLBMre7QSM1ddIOgdyZt+zuDcpUiA==
 =QEqf
 -----END PGP SIGNATURE-----

Merge tag 'pull-10.2-maintainer-291025-1' of https://gitlab.com/stsquad/qemu into staging

maintainer updates for 10.2

  - clean-up remaining 32 bit armhf bits in ci
  - rationalise build-environment.yml for Debian and Ubuntu
  - generate a Debian ppc64 package list
  - rationalise gitlab-runner.yml for Debian and Ubuntu
  - new TCG plugin feature to track discontinuities
  - add missing CFI annotation to plugin callbacks
  - drop SBSA_REF from minimal Arm build
  - format string fix for gdbstub syscall response
  - simplify the gdbstub flen handling for semihosting

# -----BEGIN PGP SIGNATURE-----
#
# iQEzBAABCgAdFiEEZoWumedRZ7yvyN81+9DbCVqeKkQFAmkCInQACgkQ+9DbCVqe
# KkSZRwf/ReHIqQMxf8TqthskX8PLGUvsvWMkJptpu0Yc4HyU6DSjdPbU4L0tOmLU
# ss2sb+dZncp1iRxHpqOhPJ+a987RHHzFbz2GQ/nV37D7BTwtq0iID4SxmdfiYOAm
# VVm/WQ0HMjIYY84rzfE6U/3H+FgL+GaPbB0WYa5CtKpMOHMl4gJgoSsxljXQrmYe
# BCC+Z9loVUAnKVVM5BUMP/0Agfn0CUZlUHGEn6RvmNg81dJ5DWCfO9yW1EezLZmc
# PhS/txAWrpTqktPxN4h+um8ILvej5FF8nnNCsxodxD1XZImWsxawxcQAcgQQJGWu
# dFLBMre7QSM1ddIOgdyZt+zuDcpUiA==
# =QEqf
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 29 Oct 2025 03:19:32 PM CET
# 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-10.2-maintainer-291025-1' of https://gitlab.com/stsquad/qemu: (35 commits)
  semihosting: Fix GDB File-I/O FLEN
  gdbstub: Fix %s formatting
  configs: drop SBSA_REF from minimal specification
  plugins/core: add missing QEMU_DISABLE_CFI annotations
  tests: add test with interrupted memory accesses on rv64
  tests: add test for double-traps on rv64
  tests: add plugin asserting correctness of discon event's to_pc
  target/xtensa: call plugin trap callbacks
  target/tricore: call plugin trap callbacks
  target/sparc: call plugin trap callbacks
  target/sh4: call plugin trap callbacks
  target/s390x: call plugin trap callbacks
  target/rx: call plugin trap callbacks
  target/riscv: call plugin trap callbacks
  target/ppc: call plugin trap callbacks
  target/openrisc: call plugin trap callbacks
  target/mips: call plugin trap callbacks
  target/microblaze: call plugin trap callbacks
  target/m68k: call plugin trap callbacks
  target/loongarch: call plugin trap callbacks
  ...

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2025-10-30 09:25:14 +01:00
commit 4889d96660
47 changed files with 1031 additions and 37 deletions

View file

@ -107,7 +107,5 @@ ubuntu-24.04-aarch64-notcg:
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$AARCH64_RUNNER_AVAILABLE"
when: manual
allow_failure: true

View file

@ -6,4 +6,3 @@
#
CONFIG_ARM_VIRT=y
CONFIG_SBSA_REF=y

View file

@ -1,6 +1,6 @@
contrib_plugins = ['bbv', 'cache', 'cflow', 'drcov', 'execlog', 'hotblocks',
'hotpages', 'howvec', 'hwprofile', 'ips', 'stoptrigger',
'uftrace']
'traps', 'uftrace']
if host_os != 'windows'
# lockstep uses socket.h
contrib_plugins += 'lockstep'

83
contrib/plugins/traps.c Normal file
View file

@ -0,0 +1,83 @@
/*
* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright (C) 2025, Julian Ganz <neither@nut.email>
*
* Traps - count traps
*
* Count the number of interrupts (asyncronous events), exceptions (synchronous
* events) and host calls (e.g. semihosting) per cpu and report those counts on
* exit.
*/
#include <stdio.h>
#include <qemu-plugin.h>
QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
typedef struct {
uint64_t interrupts;
uint64_t exceptions;
uint64_t hostcalls;
} TrapCounters;
static struct qemu_plugin_scoreboard *traps;
static void vcpu_discon(qemu_plugin_id_t id, unsigned int vcpu_index,
enum qemu_plugin_discon_type type, uint64_t from_pc,
uint64_t to_pc)
{
TrapCounters *rec = qemu_plugin_scoreboard_find(traps, vcpu_index);
switch (type) {
case QEMU_PLUGIN_DISCON_INTERRUPT:
rec->interrupts++;
break;
case QEMU_PLUGIN_DISCON_EXCEPTION:
rec->exceptions++;
break;
case QEMU_PLUGIN_DISCON_HOSTCALL:
rec->hostcalls++;
break;
default:
g_assert_not_reached();
break;
}
}
static void plugin_exit(qemu_plugin_id_t id, void *p)
{
g_autoptr(GString) report;
report = g_string_new("VCPU, interrupts, exceptions, hostcalls\n");
int max_vcpus = qemu_plugin_num_vcpus();
int vcpu;
for (vcpu = 0; vcpu < max_vcpus; vcpu++) {
TrapCounters *rec = qemu_plugin_scoreboard_find(traps, vcpu);
g_string_append_printf(report,
"% 4d, % 10"PRId64", % 10"PRId64", % 10"PRId64
"\n", vcpu, rec->interrupts, rec->exceptions,
rec->hostcalls);
}
qemu_plugin_outs(report->str);
qemu_plugin_scoreboard_free(traps);
}
QEMU_PLUGIN_EXPORT
int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
int argc, char **argv)
{
if (!info->system_emulation) {
qemu_plugin_outs("Note: interrupts are only reported in system"
" emulation mode.");
}
traps = qemu_plugin_scoreboard_new(sizeof(TrapCounters));
qemu_plugin_register_vcpu_discon_cb(id, QEMU_PLUGIN_DISCON_ALL,
vcpu_discon);
qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
return 0;
}

View file

@ -1015,6 +1015,14 @@ interesting to generate them around a particular point of execution::
# generate trace around init execution (2 seconds):
$ uftrace dump --chrome --time-range=1753122320~1753122322 > init.json
Count traps
...........
``contrib/plugins/traps.c``
This plugin counts the number of interrupts (asyncronous events), exceptions
(synchronous events) and host calls (e.g. semihosting) per cpu.
Other emulation features
------------------------

View file

@ -168,13 +168,6 @@ If you've got access to an aarch64 host that can be used as a gitlab-CI
runner, you can set this variable to enable the tests that require this
kind of host. The runner should be tagged with "aarch64".
AARCH32_RUNNER_AVAILABLE
~~~~~~~~~~~~~~~~~~~~~~~~
If you've got access to an armhf host or an arch64 host that can run
aarch32 EL0 code to be used as a gitlab-CI runner, you can set this
variable to enable the tests that require this kind of host. The
runner should be tagged with "aarch32".
S390X_RUNNER_AVAILABLE
~~~~~~~~~~~~~~~~~~~~~~
If you've got access to an IBM Z host that can be used as a gitlab-CI

View file

@ -127,7 +127,7 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
case 's':
i64 = va_arg(va, uint64_t);
i32 = va_arg(va, uint32_t);
p += snprintf(p, p_end - p, "%" PRIx64 "/%x" PRIx32, i64, i32);
p += snprintf(p, p_end - p, "%" PRIx64 "/%" PRIx32, i64, i32);
break;
default:
bad_format:

View file

@ -20,6 +20,9 @@ enum qemu_plugin_event {
QEMU_PLUGIN_EV_VCPU_SYSCALL_RET,
QEMU_PLUGIN_EV_FLUSH,
QEMU_PLUGIN_EV_ATEXIT,
QEMU_PLUGIN_EV_VCPU_INTERRUPT,
QEMU_PLUGIN_EV_VCPU_EXCEPTION,
QEMU_PLUGIN_EV_VCPU_HOSTCALL,
QEMU_PLUGIN_EV_MAX, /* total number of plugin events we support */
};

View file

@ -59,6 +59,7 @@ union qemu_plugin_cb_sig {
qemu_plugin_udata_cb_t udata;
qemu_plugin_vcpu_simple_cb_t vcpu_simple;
qemu_plugin_vcpu_udata_cb_t vcpu_udata;
qemu_plugin_vcpu_discon_cb_t vcpu_discon;
qemu_plugin_vcpu_tb_trans_cb_t vcpu_tb_trans;
qemu_plugin_vcpu_mem_cb_t vcpu_mem;
qemu_plugin_vcpu_syscall_cb_t vcpu_syscall;
@ -160,6 +161,9 @@ void qemu_plugin_vcpu_exit_hook(CPUState *cpu);
void qemu_plugin_tb_trans_cb(CPUState *cpu, struct qemu_plugin_tb *tb);
void qemu_plugin_vcpu_idle_cb(CPUState *cpu);
void qemu_plugin_vcpu_resume_cb(CPUState *cpu);
void qemu_plugin_vcpu_interrupt_cb(CPUState *cpu, uint64_t from);
void qemu_plugin_vcpu_exception_cb(CPUState *cpu, uint64_t from);
void qemu_plugin_vcpu_hostcall_cb(CPUState *cpu, uint64_t from);
void
qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1,
uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5,
@ -257,6 +261,15 @@ static inline void qemu_plugin_vcpu_idle_cb(CPUState *cpu)
static inline void qemu_plugin_vcpu_resume_cb(CPUState *cpu)
{ }
static inline void qemu_plugin_vcpu_interrupt_cb(CPUState *cpu, uint64_t from)
{ }
static inline void qemu_plugin_vcpu_exception_cb(CPUState *cpu, uint64_t from)
{ }
static inline void qemu_plugin_vcpu_hostcall_cb(CPUState *cpu, uint64_t from)
{ }
static inline void
qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6,

View file

@ -161,6 +161,50 @@ typedef void (*qemu_plugin_vcpu_simple_cb_t)(qemu_plugin_id_t id,
typedef void (*qemu_plugin_vcpu_udata_cb_t)(unsigned int vcpu_index,
void *userdata);
/**
* enum qemu_plugin_discon_type - type of a (potential) PC discontinuity
*
* @QEMU_PLUGIN_DISCON_INTERRUPT: an interrupt, defined across all architectures
* as an asynchronous event, usually originating
* from outside the CPU
* @QEMU_PLUGIN_DISCON_EXCEPTION: an exception, defined across all architectures
* as a synchronous event in response to a
* specific instruction being executed
* @QEMU_PLUGIN_DISCON_HOSTCALL: a host call, functionally a special kind of
* exception that is not handled by code run by
* the vCPU but machinery outside the vCPU
* @QEMU_PLUGIN_DISCON_ALL: all types of disconinuity events currently covered
*/
enum qemu_plugin_discon_type {
QEMU_PLUGIN_DISCON_INTERRUPT = 1 << 0,
QEMU_PLUGIN_DISCON_EXCEPTION = 1 << 1,
QEMU_PLUGIN_DISCON_HOSTCALL = 1 << 2,
QEMU_PLUGIN_DISCON_ALL = -1
};
/**
* typedef qemu_plugin_vcpu_discon_cb_t - vcpu discontinuity callback
* @id: plugin ID
* @vcpu_index: the current vcpu context
* @type: the type of discontinuity
* @from_pc: the source of the discontinuity, e.g. the PC before the
* transition
* @to_pc: the PC pointing to the next instruction to be executed
*
* The exact semantics of @from_pc depends on the @type of discontinuity. For
* interrupts, @from_pc will point to the next instruction which would have
* been executed. For exceptions and host calls, @from_pc will point to the
* instruction that caused the exception or issued the host call. Note that
* in the case of exceptions, the instruction may not be retired and thus not
* observable via general instruction exec callbacks. The same may be the case
* for some host calls such as hypervisor call "exceptions".
*/
typedef void (*qemu_plugin_vcpu_discon_cb_t)(qemu_plugin_id_t id,
unsigned int vcpu_index,
enum qemu_plugin_discon_type type,
uint64_t from_pc, uint64_t to_pc);
/**
* qemu_plugin_uninstall() - Uninstall a plugin
* @id: this plugin's opaque ID
@ -237,6 +281,22 @@ QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id,
qemu_plugin_vcpu_simple_cb_t cb);
/**
* qemu_plugin_register_vcpu_discon_cb() - register a discontinuity callback
* @id: plugin ID
* @type: types of discontinuities for which to call the callback
* @cb: callback function
*
* The @cb function is called every time a vCPU receives a discontinuity event
* of the specified type(s), after the vCPU was prepared to handle the event.
* Preparation entails updating the PC, usually to some interrupt handler or
* trap vector entry.
*/
QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_discon_cb(qemu_plugin_id_t id,
enum qemu_plugin_discon_type type,
qemu_plugin_vcpu_discon_cb_t cb);
/** struct qemu_plugin_tb - Opaque handle for a translation block */
struct qemu_plugin_tb;
/** struct qemu_plugin_insn - Opaque handle for a translated instruction */

View file

@ -105,6 +105,30 @@ static void plugin_vcpu_cb__simple(CPUState *cpu, enum qemu_plugin_event ev)
}
}
/*
* Disable CFI checks.
* The callback function has been loaded from an external library so we do not
* have type information
*/
QEMU_DISABLE_CFI
static void plugin_vcpu_cb__discon(CPUState *cpu,
enum qemu_plugin_event ev,
enum qemu_plugin_discon_type type,
uint64_t from)
{
struct qemu_plugin_cb *cb, *next;
uint64_t to = cpu->cc->get_pc(cpu);
if (cpu->cpu_index < plugin.num_vcpus) {
/* iterate safely; plugins might uninstall themselves at any time */
QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
qemu_plugin_vcpu_discon_cb_t func = cb->f.vcpu_discon;
func(cb->ctx->id, cpu->cpu_index, type, from, to);
}
}
}
/*
* Disable CFI checks.
* The callback function has been loaded from an external library so we do not
@ -557,6 +581,24 @@ void qemu_plugin_vcpu_resume_cb(CPUState *cpu)
}
}
void qemu_plugin_vcpu_interrupt_cb(CPUState *cpu, uint64_t from)
{
plugin_vcpu_cb__discon(cpu, QEMU_PLUGIN_EV_VCPU_INTERRUPT,
QEMU_PLUGIN_DISCON_INTERRUPT, from);
}
void qemu_plugin_vcpu_exception_cb(CPUState *cpu, uint64_t from)
{
plugin_vcpu_cb__discon(cpu, QEMU_PLUGIN_EV_VCPU_EXCEPTION,
QEMU_PLUGIN_DISCON_EXCEPTION, from);
}
void qemu_plugin_vcpu_hostcall_cb(CPUState *cpu, uint64_t from)
{
plugin_vcpu_cb__discon(cpu, QEMU_PLUGIN_EV_VCPU_HOSTCALL,
QEMU_PLUGIN_DISCON_HOSTCALL, from);
}
void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id,
qemu_plugin_vcpu_simple_cb_t cb)
{
@ -569,6 +611,21 @@ void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id,
plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_RESUME, cb);
}
void qemu_plugin_register_vcpu_discon_cb(qemu_plugin_id_t id,
enum qemu_plugin_discon_type type,
qemu_plugin_vcpu_discon_cb_t cb)
{
if (type & QEMU_PLUGIN_DISCON_INTERRUPT) {
plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_INTERRUPT, cb);
}
if (type & QEMU_PLUGIN_DISCON_EXCEPTION) {
plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_EXCEPTION, cb);
}
if (type & QEMU_PLUGIN_DISCON_HOSTCALL) {
plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_HOSTCALL, cb);
}
}
void qemu_plugin_register_flush_cb(qemu_plugin_id_t id,
qemu_plugin_simple_cb_t cb)
{
@ -611,6 +668,7 @@ void exec_inline_op(enum plugin_dyn_cb_type type,
}
}
QEMU_DISABLE_CFI
void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
uint64_t value_low,
uint64_t value_high,

View file

@ -318,6 +318,7 @@ struct qemu_plugin_reset_data {
bool reset;
};
QEMU_DISABLE_CFI
static void plugin_reset_destroy__locked(struct qemu_plugin_reset_data *data)
{
struct qemu_plugin_ctx *ctx = data->ctx;

View file

@ -19,32 +19,38 @@
- '((ansible_version.major == 2) and (ansible_version.minor >= 8)) or (ansible_version.major >= 3)'
msg: "Unsuitable ansible version, please use version 2.8.0 or later"
- name: Add armhf foreign architecture to aarch64 hosts
command: dpkg --add-architecture armhf
when:
- ansible_facts['distribution'] == 'Ubuntu'
- ansible_facts['architecture'] == 'aarch64'
- name: Update apt cache / upgrade packages via apt
apt:
update_cache: yes
upgrade: yes
when:
- ansible_facts['distribution'] == 'Ubuntu'
- ansible_facts['distribution'] in ['Ubuntu', 'Debian']
# the package lists are updated by "make lcitool-refresh"
- name: Include package lists based on OS and architecture
include_vars:
file: "ubuntu-2404-{{ ansible_facts['architecture'] }}.yaml"
- name: Define package list file path for Ubuntu
set_fact:
package_file: "ubuntu/ubuntu-2404-{{ ansible_facts['architecture'] }}.yaml"
when:
- ansible_facts['distribution'] == 'Ubuntu'
- ansible_facts['distribution_version'] == '24.04'
- name: Install packages for QEMU on Ubuntu 24.04
- name: Define package list file path for Debian
set_fact:
package_file: "debian/debian-{{ ansible_facts['distribution_major_version'] }}-{{ ansible_facts['architecture'] }}.yaml"
when:
- ansible_facts['distribution'] == 'Debian'
- name: Include package lists based on OS and architecture
include_vars:
file: "{{ package_file }}"
when:
- package_file is exists
- name: Install packages for QEMU on Ubuntu/Debian
package:
name: "{{ packages }}"
when:
- ansible_facts['distribution'] == 'Ubuntu'
- ansible_facts['distribution_version'] == '24.04'
- package_file is exists
- packages is defined

View file

@ -0,0 +1,134 @@
# THIS FILE WAS AUTO-GENERATED
#
# $ lcitool variables --host-arch ppc64le debian-13 qemu
#
# https://gitlab.com/libvirt/libvirt-ci
packages:
- bash
- bc
- bindgen
- bison
- bsdextrautils
- bzip2
- ca-certificates
- ccache
- clang
- dbus
- debianutils
- diffutils
- exuberant-ctags
- findutils
- flex
- gcc
- gcovr
- gettext
- git
- hostname
- libaio-dev
- libasan8
- libasound2-dev
- libattr1-dev
- libbpf-dev
- libbrlapi-dev
- libbz2-dev
- libc6-dev
- libcacard-dev
- libcap-ng-dev
- libcapstone-dev
- libcbor-dev
- libclang-rt-dev
- libcmocka-dev
- libcurl4-gnutls-dev
- libdaxctl-dev
- libdrm-dev
- libepoxy-dev
- libfdt-dev
- libffi-dev
- libfuse3-dev
- libgbm-dev
- libgcrypt20-dev
- libglib2.0-dev
- libglusterfs-dev
- libgnutls28-dev
- libgtk-3-dev
- libgtk-vnc-2.0-dev
- libibverbs-dev
- libiscsi-dev
- libjemalloc-dev
- libjpeg62-turbo-dev
- libjson-c-dev
- liblttng-ust-dev
- liblzo2-dev
- libncursesw5-dev
- libnfs-dev
- libnuma-dev
- libpam0g-dev
- libpcre2-dev
- libpipewire-0.3-dev
- libpixman-1-dev
- libpng-dev
- libpulse-dev
- librbd-dev
- librdmacm-dev
- libsasl2-dev
- libsdl2-dev
- libsdl2-image-dev
- libseccomp-dev
- libselinux1-dev
- libslirp-dev
- libsnappy-dev
- libsndio-dev
- libspice-protocol-dev
- libspice-server-dev
- libssh-dev
- libstd-rust-dev
- libsystemd-dev
- libtasn1-6-dev
- libubsan1
- libudev-dev
- liburing-dev
- libusb-1.0-0-dev
- libusbredirhost-dev
- libvdeplug-dev
- libvirglrenderer-dev
- libvte-2.91-dev
- libxdp-dev
- libzstd-dev
- llvm
- locales
- make
- mtools
- multipath-tools
- ncat
- nettle-dev
- ninja-build
- openssh-client
- pkgconf
- python3
- python3-numpy
- python3-opencv
- python3-pillow
- python3-pip
- python3-setuptools
- python3-sphinx
- python3-sphinx-rtd-theme
- python3-tomli
- python3-venv
- python3-wheel
- python3-yaml
- rpm2cpio
- rustc
- sed
- socat
- sparse
- swtpm
- systemtap-sdt-dev
- tar
- tesseract-ocr
- tesseract-ocr-eng
- vulkan-tools
- xorriso
- zlib1g-dev
- zstd

View file

@ -56,12 +56,12 @@
url: "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh"
mode: 0755
when:
- ansible_facts['distribution'] == 'Ubuntu'
- ansible_facts['distribution'] in ['Ubuntu', 'Debian']
- name: Run gitlab-runner repo setup script (DEB)
shell: "/root/script.deb.sh"
when:
- ansible_facts['distribution'] == 'Ubuntu'
- ansible_facts['distribution'] in ['Ubuntu', 'Debian']
- name: Install gitlab-runner (DEB)
ansible.builtin.apt:
@ -69,7 +69,7 @@
update_cache: yes
state: present
when:
- ansible_facts['distribution'] == 'Ubuntu'
- ansible_facts['distribution'] in ['Ubuntu', 'Debian']
# RPM setup
- name: Get gitlab-runner repo setup script (RPM)

View file

@ -26,7 +26,7 @@ packages:
- git
- hostname
- libaio-dev
- libasan6
- libasan8
- libasound2-dev
- libattr1-dev
- libbpf-dev
@ -37,7 +37,7 @@ packages:
- libcap-ng-dev
- libcapstone-dev
- libcbor-dev
- libclang-dev
- libclang-rt-dev
- libcmocka-dev
- libcurl4-gnutls-dev
- libdaxctl-dev

View file

@ -26,7 +26,7 @@ packages:
- git
- hostname
- libaio-dev
- libasan6
- libasan8
- libasound2-dev
- libattr1-dev
- libbpf-dev
@ -37,7 +37,7 @@ packages:
- libcap-ng-dev
- libcapstone-dev
- libcbor-dev
- libclang-dev
- libclang-rt-dev
- libcmocka-dev
- libcurl4-gnutls-dev
- libdaxctl-dev

View file

@ -316,10 +316,7 @@ common_semi_flen_fstat_cb(CPUState *cs, uint64_t ret, int err)
&size, 8, 0)) {
ret = -1, err = EFAULT;
} else {
size = be64_to_cpu(size);
if (ret != size) {
ret = -1, err = EOVERFLOW;
}
ret = be64_to_cpu(size);
}
}
common_semi_cb(cs, ret, err);

View file

@ -27,6 +27,7 @@
#include "exec/helper-proto.h"
#include "qemu/qemu-print.h"
#include "system/memory.h"
#include "qemu/plugin.h"
#define CONVERT_BIT(X, SRC, DST) \
@ -328,6 +329,7 @@ void alpha_cpu_do_interrupt(CPUState *cs)
{
CPUAlphaState *env = cpu_env(cs);
int i = cs->exception_index;
uint64_t last_pc = env->pc;
if (qemu_loglevel_mask(CPU_LOG_INT)) {
static int count;
@ -431,6 +433,17 @@ void alpha_cpu_do_interrupt(CPUState *cs)
/* Switch to PALmode. */
env->flags |= ENV_FLAG_PAL_MODE;
switch (i) {
case EXCP_SMP_INTERRUPT:
case EXCP_CLK_INTERRUPT:
case EXCP_DEV_INTERRUPT:
qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
break;
default:
qemu_plugin_vcpu_exception_cb(cs, last_pc);
break;
}
}
bool alpha_cpu_exec_interrupt(CPUState *cs, int interrupt_request)

View file

@ -34,6 +34,7 @@
#endif
#include "cpregs.h"
#include "target/arm/gtimer.h"
#include "qemu/plugin.h"
#define HELPER_H "tcg/helper.h"
#include "exec/helper-proto.h.inc"
@ -8783,6 +8784,24 @@ static void take_aarch32_exception(CPUARMState *env, int new_mode,
}
}
void arm_do_plugin_vcpu_discon_cb(CPUState *cs, uint64_t from)
{
switch (cs->exception_index) {
case EXCP_IRQ:
case EXCP_VIRQ:
case EXCP_NMI:
case EXCP_VINMI:
case EXCP_FIQ:
case EXCP_VFIQ:
case EXCP_VFNMI:
case EXCP_VSERR:
qemu_plugin_vcpu_interrupt_cb(cs, from);
break;
default:
qemu_plugin_vcpu_exception_cb(cs, from);
}
}
static void arm_cpu_do_interrupt_aarch32_hyp(CPUState *cs)
{
/*
@ -9473,6 +9492,7 @@ void arm_cpu_do_interrupt(CPUState *cs)
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
unsigned int new_el = env->exception.target_el;
uint64_t last_pc = cs->cc->get_pc(cs);
assert(!arm_feature(env, ARM_FEATURE_M));
@ -9489,6 +9509,7 @@ void arm_cpu_do_interrupt(CPUState *cs)
if (tcg_enabled() && arm_is_psci_call(cpu, cs->exception_index)) {
arm_handle_psci_call(cpu);
qemu_log_mask(CPU_LOG_INT, "...handled as PSCI call\n");
qemu_plugin_vcpu_hostcall_cb(cs, last_pc);
return;
}
@ -9500,6 +9521,7 @@ void arm_cpu_do_interrupt(CPUState *cs)
#ifdef CONFIG_TCG
if (cs->exception_index == EXCP_SEMIHOST) {
tcg_handle_semihosting(cs);
qemu_plugin_vcpu_hostcall_cb(cs, last_pc);
return;
}
#endif
@ -9525,6 +9547,8 @@ void arm_cpu_do_interrupt(CPUState *cs)
if (!kvm_enabled()) {
cpu_set_interrupt(cs, CPU_INTERRUPT_EXITTB);
}
arm_do_plugin_vcpu_discon_cb(cs, last_pc);
}
#endif /* !CONFIG_USER_ONLY */

View file

@ -375,6 +375,7 @@ static inline int r14_bank_number(int mode)
void arm_cpu_register(const ARMCPUInfo *info);
void arm_do_plugin_vcpu_discon_cb(CPUState *cs, uint64_t from);
void register_cp_regs_for_features(ARMCPU *cpu);
void init_cpreg_list(ARMCPU *cpu);

View file

@ -23,6 +23,7 @@
#if !defined(CONFIG_USER_ONLY)
#include "hw/intc/armv7m_nvic.h"
#endif
#include "qemu/plugin.h"
static void v7m_msr_xpsr(CPUARMState *env, uint32_t mask,
uint32_t reg, uint32_t val)
@ -2194,6 +2195,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
CPUARMState *env = &cpu->env;
uint32_t lr;
bool ignore_stackfaults;
uint64_t last_pc = env->regs[15];
arm_log_exception(cs);
@ -2361,6 +2363,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
g_assert_not_reached();
#endif
env->regs[15] += env->thumb ? 2 : 4;
qemu_plugin_vcpu_hostcall_cb(cs, last_pc);
return;
case EXCP_BKPT:
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_DEBUG, false);
@ -2427,6 +2430,8 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
ignore_stackfaults = v7m_push_stack(cpu);
v7m_exception_taken(cpu, lr, false, ignore_stackfaults);
arm_do_plugin_vcpu_discon_cb(cs, last_pc);
}
uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg)

View file

@ -28,6 +28,7 @@
#include "exec/target_page.h"
#include "accel/tcg/cpu-ldst.h"
#include "exec/helper-proto.h"
#include "qemu/plugin.h"
bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
{
@ -102,6 +103,8 @@ void avr_cpu_do_interrupt(CPUState *cs)
env->sregI = 0; /* clear Global Interrupt Flag */
cs->exception_index = -1;
qemu_plugin_vcpu_interrupt_cb(cs, ret);
}
hwaddr avr_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)

View file

@ -24,6 +24,7 @@
#include "exec/helper-proto.h"
#include "hw/core/cpu.h"
#include "hw/hppa/hppa_hardware.h"
#include "qemu/plugin.h"
static void eval_interrupt(HPPACPU *cpu)
{
@ -95,6 +96,7 @@ void hppa_cpu_do_interrupt(CPUState *cs)
CPUHPPAState *env = &cpu->env;
int i = cs->exception_index;
uint64_t old_psw, old_gva_offset_mask;
uint64_t last_pc = cs->cc->get_pc(cs);
/* As documented in pa2.0 -- interruption handling. */
/* step 1 */
@ -212,6 +214,21 @@ void hppa_cpu_do_interrupt(CPUState *cs)
env->iasq_f = 0;
env->iasq_b = 0;
switch (i) {
case EXCP_HPMC:
case EXCP_POWER_FAIL:
case EXCP_RC:
case EXCP_EXT_INTERRUPT:
case EXCP_LPMC:
case EXCP_PER_INTERRUPT:
case EXCP_TOC:
qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
break;
default:
qemu_plugin_vcpu_exception_cb(cs, last_pc);
break;
}
if (qemu_loglevel_mask(CPU_LOG_INT)) {
static const char * const names[] = {
[EXCP_HPMC] = "high priority machine check",

View file

@ -23,6 +23,7 @@
#include "system/runstate.h"
#include "exec/helper-proto.h"
#include "helper-tcg.h"
#include "qemu/plugin.h"
G_NORETURN void helper_raise_interrupt(CPUX86State *env, int intno,
int next_eip_addend)
@ -93,6 +94,7 @@ void raise_interrupt2(CPUX86State *env, int intno,
uintptr_t retaddr)
{
CPUState *cs = env_cpu(env);
uint64_t last_pc = env->eip + env->segs[R_CS].base;
if (!is_int) {
cpu_svm_check_intercept_param(env, SVM_EXIT_EXCP_BASE + intno,
@ -106,6 +108,7 @@ void raise_interrupt2(CPUX86State *env, int intno,
env->error_code = error_code;
env->exception_is_int = is_int;
env->exception_next_eip = env->eip + next_eip_addend;
qemu_plugin_vcpu_exception_cb(cs, last_pc);
cpu_loop_exit_restore(cs, retaddr);
}

View file

@ -29,6 +29,7 @@
#include "seg_helper.h"
#include "access.h"
#include "tcg-cpu.h"
#include "qemu/plugin.h"
#ifdef TARGET_X86_64
#define SET_ESP(val, sp_mask) \
@ -1192,6 +1193,7 @@ void do_interrupt_all(X86CPU *cpu, int intno, int is_int,
int error_code, target_ulong next_eip, int is_hw)
{
CPUX86State *env = &cpu->env;
uint64_t last_pc = env->eip + env->segs[R_CS].base;
if (qemu_loglevel_mask(CPU_LOG_INT)) {
if ((env->cr[0] & CR0_PE_MASK)) {
@ -1263,6 +1265,8 @@ void do_interrupt_all(X86CPU *cpu, int intno, int is_int,
event_inj & ~SVM_EVTINJ_VALID);
}
#endif
qemu_plugin_vcpu_interrupt_cb(CPU(cpu), last_pc);
}
void do_interrupt_x86_hardirq(CPUX86State *env, int intno, int is_hw)

View file

@ -8,6 +8,7 @@
#include "qemu/accel.h"
#include "qemu/error-report.h"
#include "qemu/log.h"
#include "qemu/plugin.h"
#include "accel/accel-cpu-target.h"
#include "accel/tcg/cpu-ldst.h"
#include "accel/tcg/cpu-ops.h"
@ -80,6 +81,7 @@ static void loongarch_cpu_do_interrupt(CPUState *cs)
int cause = -1;
bool tlbfill = FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR);
uint32_t vec_size = FIELD_EX64(env->CSR_ECFG, CSR_ECFG, VS);
uint64_t last_pc = env->pc;
if (cs->exception_index != EXCCODE_INT) {
qemu_log_mask(CPU_LOG_INT,
@ -190,6 +192,7 @@ static void loongarch_cpu_do_interrupt(CPUState *cs)
__func__, env->pc, env->CSR_ERA,
cause, env->CSR_BADV, env->CSR_DERA, vector,
env->CSR_ECFG, env->CSR_ESTAT);
qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
} else {
if (tlbfill) {
set_pc(env, env->CSR_TLBRENTRY);
@ -208,6 +211,7 @@ static void loongarch_cpu_do_interrupt(CPUState *cs)
tlbfill ? env->CSR_TLBRBADV : env->CSR_BADV,
env->CSR_BADI, env->gpr[11], cs->cpu_index,
env->CSR_ASID);
qemu_plugin_vcpu_exception_cb(cs, last_pc);
}
cs->exception_index = -1;
}

View file

@ -22,6 +22,7 @@
#include "exec/helper-proto.h"
#include "accel/tcg/cpu-ldst.h"
#include "semihosting/semihost.h"
#include "qemu/plugin.h"
#if !defined(CONFIG_USER_ONLY)
@ -183,6 +184,21 @@ static const char *m68k_exception_name(int index)
return "Unassigned";
}
static void do_plugin_vcpu_interrupt_cb(CPUState *cs, uint64_t from)
{
switch (cs->exception_index) {
case EXCP_SPURIOUS ... EXCP_INT_LEVEL_7:
qemu_plugin_vcpu_interrupt_cb(cs, from);
break;
case EXCP_SEMIHOSTING:
qemu_plugin_vcpu_hostcall_cb(cs, from);
break;
default:
qemu_plugin_vcpu_exception_cb(cs, from);
break;
}
}
static void cf_interrupt_all(CPUM68KState *env, int is_hw)
{
CPUState *cs = env_cpu(env);
@ -203,6 +219,7 @@ static void cf_interrupt_all(CPUM68KState *env, int is_hw)
return;
case EXCP_SEMIHOSTING:
do_m68k_semihosting(env, env->dregs[0]);
qemu_plugin_vcpu_hostcall_cb(cs, retaddr);
return;
}
}
@ -239,6 +256,8 @@ static void cf_interrupt_all(CPUM68KState *env, int is_hw)
env->aregs[7] = sp;
/* Jump to vector. */
env->pc = cpu_ldl_mmuidx_ra(env, env->vbr + vector, MMU_KERNEL_IDX, 0);
do_plugin_vcpu_interrupt_cb(cs, retaddr);
}
static inline void do_stack_frame(CPUM68KState *env, uint32_t *sp,
@ -277,6 +296,7 @@ static void m68k_interrupt_all(CPUM68KState *env, int is_hw)
uint32_t sp;
uint32_t vector;
uint16_t sr, oldsr;
uint64_t last_pc = env->pc;
if (!is_hw) {
switch (cs->exception_index) {
@ -417,6 +437,8 @@ static void m68k_interrupt_all(CPUM68KState *env, int is_hw)
env->aregs[7] = sp;
/* Jump to vector. */
env->pc = cpu_ldl_mmuidx_ra(env, env->vbr + vector, MMU_KERNEL_IDX, 0);
do_plugin_vcpu_interrupt_cb(cs, last_pc);
}
static void do_interrupt_all(CPUM68KState *env, int is_hw)

View file

@ -27,6 +27,7 @@
#include "qemu/host-utils.h"
#include "exec/log.h"
#include "exec/helper-proto.h"
#include "qemu/plugin.h"
G_NORETURN
@ -35,6 +36,7 @@ static void mb_unaligned_access_internal(CPUState *cs, uint64_t addr,
{
CPUMBState *env = cpu_env(cs);
uint32_t esr, iflags;
uint64_t last_pc = env->pc;
/* Recover the pc and iflags from the corresponding insn_start. */
cpu_restore_state(cs, retaddr);
@ -54,6 +56,7 @@ static void mb_unaligned_access_internal(CPUState *cs, uint64_t addr,
env->ear = addr;
env->esr = esr;
cs->exception_index = EXCP_HW_EXCP;
qemu_plugin_vcpu_exception_cb(cs, last_pc);
cpu_loop_exit(cs);
}
@ -152,6 +155,7 @@ void mb_cpu_do_interrupt(CPUState *cs)
CPUMBState *env = &cpu->env;
uint32_t t, msr = mb_cpu_read_msr(env);
bool set_esr;
uint64_t last_pc = env->pc;
/* IMM flag cannot propagate across a branch and into the dslot. */
assert((env->iflags & (D_FLAG | IMM_FLAG)) != (D_FLAG | IMM_FLAG));
@ -256,6 +260,12 @@ void mb_cpu_do_interrupt(CPUState *cs)
env->res_addr = RES_ADDR_NONE;
env->iflags = 0;
if (cs->exception_index == EXCP_IRQ) {
qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
} else {
qemu_plugin_vcpu_exception_cb(cs, last_pc);
}
if (!set_esr) {
qemu_log_mask(CPU_LOG_INT,
" to pc=%08x msr=%08x\n", env->pc, msr);

View file

@ -18,6 +18,7 @@
*/
#include "qemu/osdep.h"
#include "qemu/bitops.h"
#include "qemu/plugin.h"
#include "cpu.h"
#include "internal.h"
@ -1034,6 +1035,7 @@ void mips_cpu_do_interrupt(CPUState *cs)
bool update_badinstr = 0;
target_ulong offset;
int cause = -1;
uint64_t last_pc = env->active_tc.PC;
if (qemu_loglevel_mask(CPU_LOG_INT)
&& cs->exception_index != EXCP_EXT_INTERRUPT) {
@ -1052,6 +1054,7 @@ void mips_cpu_do_interrupt(CPUState *cs)
cs->exception_index = EXCP_NONE;
mips_semihosting(env);
env->active_tc.PC += env->error_code;
qemu_plugin_vcpu_hostcall_cb(cs, last_pc);
return;
case EXCP_DSS:
env->CP0_Debug |= 1 << CP0DB_DSS;
@ -1336,6 +1339,14 @@ void mips_cpu_do_interrupt(CPUState *cs)
env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr,
env->CP0_DEPC);
}
switch (cs->exception_index) {
case EXCP_NMI:
case EXCP_EXT_INTERRUPT:
qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
break;
default:
qemu_plugin_vcpu_exception_cb(cs, last_pc);
}
cs->exception_index = EXCP_NONE;
}

View file

@ -25,11 +25,13 @@
#ifndef CONFIG_USER_ONLY
#include "hw/loader.h"
#endif
#include "qemu/plugin.h"
void openrisc_cpu_do_interrupt(CPUState *cs)
{
CPUOpenRISCState *env = cpu_env(cs);
int exception = cs->exception_index;
uint64_t last_pc = env->pc;
env->epcr = env->pc;
@ -98,6 +100,19 @@ void openrisc_cpu_do_interrupt(CPUState *cs)
cpu_abort(cs, "Unhandled exception 0x%x\n", exception);
}
switch (exception) {
case EXCP_RESET:
/* Resets are already exposed to plugins through a dedicated callback */
break;
case EXCP_TICK:
case EXCP_INT:
qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
break;
default:
qemu_plugin_vcpu_exception_cb(cs, last_pc);
break;
}
cs->exception_index = -1;
}

View file

@ -27,6 +27,7 @@
#include "internal.h"
#include "helper_regs.h"
#include "hw/ppc/ppc.h"
#include "qemu/plugin.h"
#include "trace.h"
@ -404,11 +405,31 @@ static void powerpc_mcheck_checkstop(CPUPPCState *env)
powerpc_checkstop(env, "machine check with MSR[ME]=0");
}
static void powerpc_do_plugin_vcpu_interrupt_cb(CPUState *cs, int excp,
uint64_t from)
{
switch (excp) {
case POWERPC_EXCP_NONE:
break;
case POWERPC_EXCP_FIT:
case POWERPC_EXCP_WDT:
case POWERPC_EXCP_PIT:
case POWERPC_EXCP_SMI:
case POWERPC_EXCP_PERFM:
case POWERPC_EXCP_THERM:
qemu_plugin_vcpu_interrupt_cb(cs, from);
break;
default:
qemu_plugin_vcpu_exception_cb(cs, from);
}
}
static void powerpc_excp_40x(PowerPCCPU *cpu, int excp)
{
CPUPPCState *env = &cpu->env;
target_ulong msr, new_msr, vector;
int srr0 = SPR_SRR0, srr1 = SPR_SRR1;
uint64_t last_pc = env->nip;
/* new srr1 value excluding must-be-zero bits */
msr = env->msr & ~0x783f0000ULL;
@ -456,6 +477,7 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp)
if (!FIELD_EX64_FE(env->msr) || !FIELD_EX64(env->msr, MSR, FP)) {
trace_ppc_excp_fp_ignore();
powerpc_reset_excp_state(cpu);
qemu_plugin_vcpu_exception_cb(env_cpu(env), last_pc);
return;
}
env->spr[SPR_40x_ESR] = ESR_FP;
@ -510,12 +532,14 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp)
env->spr[srr0] = env->nip;
env->spr[srr1] = msr;
powerpc_set_excp_state(cpu, vector, new_msr);
powerpc_do_plugin_vcpu_interrupt_cb(env_cpu(env), excp, last_pc);
}
static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp)
{
CPUPPCState *env = &cpu->env;
target_ulong msr, new_msr, vector;
uint64_t last_pc = env->nip;
/* new srr1 value excluding must-be-zero bits */
msr = env->msr & ~0x783f0000ULL;
@ -567,6 +591,7 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp)
if (!FIELD_EX64_FE(env->msr) || !FIELD_EX64(env->msr, MSR, FP)) {
trace_ppc_excp_fp_ignore();
powerpc_reset_excp_state(cpu);
qemu_plugin_vcpu_exception_cb(env_cpu(env), last_pc);
return;
}
/*
@ -653,12 +678,14 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp)
env->spr[SPR_SRR0] = env->nip;
env->spr[SPR_SRR1] = msr;
powerpc_set_excp_state(cpu, vector, new_msr);
powerpc_do_plugin_vcpu_interrupt_cb(env_cpu(env), excp, last_pc);
}
static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp)
{
CPUPPCState *env = &cpu->env;
target_ulong msr, new_msr, vector;
uint64_t last_pc = env->nip;
/* new srr1 value excluding must-be-zero bits */
msr = env->msr & ~0x783f0000ULL;
@ -708,6 +735,7 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp)
if (!FIELD_EX64_FE(env->msr) || !FIELD_EX64(env->msr, MSR, FP)) {
trace_ppc_excp_fp_ignore();
powerpc_reset_excp_state(cpu);
qemu_plugin_vcpu_exception_cb(env_cpu(env), last_pc);
return;
}
/*
@ -758,6 +786,7 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp)
if (lev == 1 && cpu->vhyp) {
cpu->vhyp_class->hypercall(cpu->vhyp, cpu);
powerpc_reset_excp_state(cpu);
qemu_plugin_vcpu_hostcall_cb(env_cpu(env), last_pc);
return;
}
@ -803,12 +832,14 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp)
env->spr[SPR_SRR0] = env->nip;
env->spr[SPR_SRR1] = msr;
powerpc_set_excp_state(cpu, vector, new_msr);
powerpc_do_plugin_vcpu_interrupt_cb(env_cpu(env), excp, last_pc);
}
static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp)
{
CPUPPCState *env = &cpu->env;
target_ulong msr, new_msr, vector;
uint64_t last_pc = env->nip;
/* new srr1 value excluding must-be-zero bits */
msr = env->msr & ~0x783f0000ULL;
@ -858,6 +889,7 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp)
if (!FIELD_EX64_FE(env->msr) || !FIELD_EX64(env->msr, MSR, FP)) {
trace_ppc_excp_fp_ignore();
powerpc_reset_excp_state(cpu);
qemu_plugin_vcpu_exception_cb(env_cpu(env), last_pc);
return;
}
/*
@ -908,6 +940,7 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp)
if (lev == 1 && cpu->vhyp) {
cpu->vhyp_class->hypercall(cpu->vhyp, cpu);
powerpc_reset_excp_state(cpu);
qemu_plugin_vcpu_hostcall_cb(env_cpu(env), last_pc);
return;
}
@ -947,6 +980,7 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp)
env->spr[SPR_SRR0] = env->nip;
env->spr[SPR_SRR1] = msr;
powerpc_set_excp_state(cpu, vector, new_msr);
powerpc_do_plugin_vcpu_interrupt_cb(env_cpu(env), excp, last_pc);
}
static void powerpc_excp_ppe42(PowerPCCPU *cpu, int excp)
@ -1073,6 +1107,7 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp)
CPUPPCState *env = &cpu->env;
target_ulong msr, new_msr, vector;
int srr0 = SPR_SRR0, srr1 = SPR_SRR1;
uint64_t last_pc = env->nip;
/*
* Book E does not play games with certain bits of xSRR1 being MSR save
@ -1144,6 +1179,7 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp)
if (!FIELD_EX64_FE(env->msr) || !FIELD_EX64(env->msr, MSR, FP)) {
trace_ppc_excp_fp_ignore();
powerpc_reset_excp_state(cpu);
qemu_plugin_vcpu_exception_cb(env_cpu(env), last_pc);
return;
}
/*
@ -1252,6 +1288,7 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp)
env->spr[srr0] = env->nip;
env->spr[srr1] = msr;
powerpc_set_excp_state(cpu, vector, new_msr);
powerpc_do_plugin_vcpu_interrupt_cb(env_cpu(env), excp, last_pc);
}
/*
@ -1373,6 +1410,7 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp)
CPUPPCState *env = &cpu->env;
target_ulong msr, new_msr, vector;
int srr0 = SPR_SRR0, srr1 = SPR_SRR1, lev = -1;
uint64_t last_pc = env->nip;
/* new srr1 value excluding must-be-zero bits */
msr = env->msr & ~0x783f0000ULL;
@ -1472,6 +1510,7 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp)
if (!FIELD_EX64_FE(env->msr) || !FIELD_EX64(env->msr, MSR, FP)) {
trace_ppc_excp_fp_ignore();
powerpc_reset_excp_state(cpu);
qemu_plugin_vcpu_exception_cb(env_cpu(env), last_pc);
return;
}
/*
@ -1516,6 +1555,7 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp)
if (lev == 1 && books_vhyp_handles_hcall(cpu)) {
cpu->vhyp_class->hypercall(cpu->vhyp, cpu);
powerpc_reset_excp_state(cpu);
qemu_plugin_vcpu_hostcall_cb(env_cpu(env), last_pc);
return;
}
if (env->insns_flags2 & PPC2_ISA310) {
@ -1662,6 +1702,7 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp)
ppc_excp_apply_ail(cpu, excp, msr, &new_msr, &vector);
powerpc_set_excp_state(cpu, vector, new_msr);
}
powerpc_do_plugin_vcpu_interrupt_cb(env_cpu(env), excp, last_pc);
}
#else
static inline void powerpc_excp_books(PowerPCCPU *cpu, int excp)

View file

@ -36,6 +36,7 @@
#include "cpu_bits.h"
#include "debug.h"
#include "pmp.h"
#include "qemu/plugin.h"
int riscv_env_mmu_index(CPURISCVState *env, bool ifetch)
{
@ -2175,6 +2176,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
uint64_t hdeleg = async ? env->hideleg : env->hedeleg;
const bool prev_virt = env->virt_enabled;
const target_ulong prev_priv = env->priv;
uint64_t last_pc = env->pc;
target_ulong tval = 0;
target_ulong tinst = 0;
target_ulong htval = 0;
@ -2197,6 +2199,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
case RISCV_EXCP_SEMIHOST:
do_common_semihosting(cs);
env->pc += 4;
qemu_plugin_vcpu_hostcall_cb(cs, last_pc);
return;
#endif
case RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT:
@ -2466,6 +2469,12 @@ void riscv_cpu_do_interrupt(CPUState *cs)
prev_priv, prev_virt);
}
if (async) {
qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
} else {
qemu_plugin_vcpu_exception_cb(cs, last_pc);
}
/*
* Interrupt/exception/trap delivery is asynchronous event and as per
* zicfilp spec CPU should clear up the ELP state. No harm in clearing

View file

@ -22,6 +22,7 @@
#include "exec/log.h"
#include "accel/tcg/cpu-ldst.h"
#include "hw/irq.h"
#include "qemu/plugin.h"
void rx_cpu_unpack_psw(CPURXState *env, uint32_t psw, int rte)
{
@ -46,6 +47,7 @@ void rx_cpu_do_interrupt(CPUState *cs)
CPURXState *env = cpu_env(cs);
int do_irq = cpu_test_interrupt(cs, INT_FLAGS);
uint32_t save_psw;
uint64_t last_pc = env->pc;
env->in_sleep = 0;
@ -65,6 +67,7 @@ void rx_cpu_do_interrupt(CPUState *cs)
env->psw_ipl = 15;
cpu_reset_interrupt(cs, CPU_INTERRUPT_FIR);
qemu_set_irq(env->ack, env->ack_irq);
qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
qemu_log_mask(CPU_LOG_INT, "fast interrupt raised\n");
} else if (do_irq & CPU_INTERRUPT_HARD) {
env->isp -= 4;
@ -75,6 +78,7 @@ void rx_cpu_do_interrupt(CPUState *cs)
env->psw_ipl = env->ack_ipl;
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
qemu_set_irq(env->ack, env->ack_irq);
qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
qemu_log_mask(CPU_LOG_INT,
"interrupt 0x%02x raised\n", env->ack_irq);
}
@ -92,6 +96,14 @@ void rx_cpu_do_interrupt(CPUState *cs)
} else {
env->pc = cpu_ldl_data(env, env->intb + (vec & 0xff) * 4);
}
if (vec == 30) {
/* Non-maskable interrupt */
qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
} else {
qemu_plugin_vcpu_exception_cb(cs, last_pc);
}
switch (vec) {
case 20:
expname = "privilege violation";

View file

@ -35,6 +35,7 @@
#include "hw/s390x/s390_flic.h"
#include "hw/boards.h"
#endif
#include "qemu/plugin.h"
G_NORETURN void tcg_s390_program_interrupt(CPUS390XState *env,
uint32_t code, uintptr_t ra)
@ -502,6 +503,7 @@ void s390_cpu_do_interrupt(CPUState *cs)
S390CPU *cpu = S390_CPU(cs);
CPUS390XState *env = &cpu->env;
bool stopped = false;
uint64_t last_pc = cpu->env.psw.addr;
qemu_log_mask(CPU_LOG_INT, "%s: %d at psw=%" PRIx64 ":%" PRIx64 "\n",
__func__, cs->exception_index, env->psw.mask, env->psw.addr);
@ -531,21 +533,27 @@ try_deliver:
switch (cs->exception_index) {
case EXCP_PGM:
do_program_interrupt(env);
qemu_plugin_vcpu_exception_cb(cs, last_pc);
break;
case EXCP_SVC:
do_svc_interrupt(env);
qemu_plugin_vcpu_exception_cb(cs, last_pc);
break;
case EXCP_EXT:
do_ext_interrupt(env);
qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
break;
case EXCP_IO:
do_io_interrupt(env);
qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
break;
case EXCP_MCHK:
do_mchk_interrupt(env);
qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
break;
case EXCP_RESTART:
do_restart_interrupt(env);
qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
break;
case EXCP_STOP:
do_stop_interrupt(env);

View file

@ -24,6 +24,7 @@
#include "exec/page-protection.h"
#include "exec/target_page.h"
#include "exec/log.h"
#include "qemu/plugin.h"
#if !defined(CONFIG_USER_ONLY)
#include "hw/sh4/sh_intc.h"
@ -60,6 +61,7 @@ void superh_cpu_do_interrupt(CPUState *cs)
CPUSH4State *env = cpu_env(cs);
int do_irq = cpu_test_interrupt(cs, CPU_INTERRUPT_HARD);
int do_exp, irq_vector = cs->exception_index;
uint64_t last_pc = env->pc;
/* prioritize exceptions over interrupts */
@ -176,12 +178,14 @@ void superh_cpu_do_interrupt(CPUState *cs)
env->pc = env->vbr + 0x100;
break;
}
qemu_plugin_vcpu_exception_cb(cs, last_pc);
return;
}
if (do_irq) {
env->intevt = irq_vector;
env->pc = env->vbr + 0x600;
qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
return;
}
}

View file

@ -24,6 +24,7 @@
#include "accel/tcg/cpu-ldst.h"
#include "exec/log.h"
#include "system/runstate.h"
#include "qemu/plugin.h"
static const char * const excp_names[0x80] = {
[TT_TFAULT] = "Instruction Access Fault",
@ -174,4 +175,10 @@ void sparc_cpu_do_interrupt(CPUState *cs)
env->qemu_irq_ack(env, intno);
}
#endif
if (intno == TT_EXTINT) {
qemu_plugin_vcpu_interrupt_cb(cs, env->regwptr[9]);
} else {
qemu_plugin_vcpu_exception_cb(cs, env->regwptr[9]);
}
}

View file

@ -24,6 +24,7 @@
#include "exec/helper-proto.h"
#include "exec/log.h"
#include "trace.h"
#include "qemu/plugin.h"
#define DEBUG_PCALL
@ -256,6 +257,15 @@ void sparc_cpu_do_interrupt(CPUState *cs)
}
env->npc = env->pc + 4;
cs->exception_index = -1;
switch (intno) {
case TT_EXTINT:
case TT_IVEC:
qemu_plugin_vcpu_interrupt_cb(cs, tsptr->tpc);
break;
default:
qemu_plugin_vcpu_exception_cb(cs, tsptr->tpc);
}
}
trap_state *cpu_tsptr(CPUSPARCState* env)

View file

@ -19,6 +19,7 @@
#include "qemu/host-utils.h"
#include "exec/helper-proto.h"
#include "accel/tcg/cpu-ldst.h"
#include "qemu/plugin.h"
#include <zlib.h> /* for crc32 */
@ -29,8 +30,11 @@ void raise_exception_sync_internal(CPUTriCoreState *env, uint32_t class, int tin
uintptr_t pc, uint32_t fcd_pc)
{
CPUState *cs = env_cpu(env);
uint64_t last_pc;
/* in case we come from a helper-call we need to restore the PC */
cpu_restore_state(cs, pc);
last_pc = env->PC;
/* Tin is loaded into d[15] */
env->gpr_d[15] = tin;
@ -90,6 +94,7 @@ void raise_exception_sync_internal(CPUTriCoreState *env, uint32_t class, int tin
/* Update PC using the trap vector table */
env->PC = env->BTV | (class << 5);
qemu_plugin_vcpu_exception_cb(cs, last_pc);
cpu_loop_exit(cs);
}

View file

@ -32,6 +32,7 @@
#include "exec/helper-proto.h"
#include "qemu/host-utils.h"
#include "qemu/atomic.h"
#include "qemu/plugin.h"
void HELPER(exception)(CPUXtensaState *env, uint32_t excp)
{
@ -207,6 +208,8 @@ void xtensa_cpu_do_interrupt(CPUState *cs)
CPUXtensaState *env = cpu_env(cs);
if (cs->exception_index == EXC_IRQ) {
uint64_t last_pc = env->pc;
qemu_log_mask(CPU_LOG_INT,
"%s(EXC_IRQ) level = %d, cintlevel = %d, "
"pc = %08x, a0 = %08x, ps = %08x, "
@ -218,6 +221,7 @@ void xtensa_cpu_do_interrupt(CPUState *cs)
env->sregs[INTSET], env->sregs[INTENABLE],
env->sregs[CCOUNT]);
handle_interrupt(env);
qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
}
switch (cs->exception_index) {
@ -238,9 +242,11 @@ void xtensa_cpu_do_interrupt(CPUState *cs)
env->sregs[CCOUNT]);
if (env->config->exception_vector[cs->exception_index]) {
uint32_t vector;
uint64_t last_pc = env->pc;
vector = env->config->exception_vector[cs->exception_index];
env->pc = relocated_vector(env, vector);
qemu_plugin_vcpu_exception_cb(cs, last_pc);
} else {
qemu_log_mask(CPU_LOG_INT,
"%s(pc = %08x) bad exception_index: %d\n",

View file

@ -35,6 +35,7 @@
#include "system/memory.h"
#include "qapi/error.h"
#include "qemu/log.h"
#include "qemu/plugin.h"
enum {
TARGET_SYS_exit = 1,
@ -197,6 +198,7 @@ void HELPER(simcall)(CPUXtensaState *env)
CPUState *cs = env_cpu(env);
AddressSpace *as = cs->as;
uint32_t *regs = env->regs;
uint64_t last_pc = env->pc;
switch (regs[2]) {
case TARGET_SYS_exit:
@ -433,4 +435,5 @@ void HELPER(simcall)(CPUXtensaState *env)
regs[3] = TARGET_ENOSYS;
break;
}
qemu_plugin_vcpu_hostcall_cb(cs, last_pc);
}

View file

@ -272,6 +272,7 @@ try:
#
generate_yaml("ubuntu", "ubuntu-2404", "aarch64")
generate_yaml("ubuntu", "ubuntu-2404", "s390x")
generate_yaml("debian", "debian-13", "ppc64le")
sys.exit(0)

221
tests/tcg/plugins/discons.c Normal file
View file

@ -0,0 +1,221 @@
/*
* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright (C) 2025, Julian Ganz <neither@nut.email>
*
* This plugin exercises the discontinuity plugin API and asserts some
* of its behaviour regarding reported program counters.
*/
#include <stdio.h>
#include <qemu-plugin.h>
QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
struct cpu_state {
uint64_t last_pc;
uint64_t from_pc;
uint64_t next_pc;
uint64_t has_from;
bool has_next;
enum qemu_plugin_discon_type next_type;
};
struct insn_data {
uint64_t addr;
uint64_t next_pc;
bool next_valid;
};
static struct qemu_plugin_scoreboard *states;
static qemu_plugin_u64 last_pc;
static qemu_plugin_u64 from_pc;
static qemu_plugin_u64 has_from;
static bool abort_on_mismatch;
static bool trace_all_insns;
static bool addr_eq(uint64_t a, uint64_t b)
{
if (a == b) {
return true;
}
uint64_t a_hw;
uint64_t b_hw;
if (!qemu_plugin_translate_vaddr(a, &a_hw) ||
!qemu_plugin_translate_vaddr(b, &b_hw))
{
return false;
}
return a_hw == b_hw;
}
static void report_mismatch(const char *pc_name, unsigned int vcpu_index,
enum qemu_plugin_discon_type type, uint64_t last,
uint64_t expected, uint64_t encountered)
{
gchar *report;
const char *discon_type_name = "unknown";
if (addr_eq(expected, encountered)) {
return;
}
switch (type) {
case QEMU_PLUGIN_DISCON_INTERRUPT:
discon_type_name = "interrupt";
break;
case QEMU_PLUGIN_DISCON_EXCEPTION:
discon_type_name = "exception";
break;
case QEMU_PLUGIN_DISCON_HOSTCALL:
discon_type_name = "hostcall";
break;
default:
break;
}
report = g_strdup_printf("Discon %s PC mismatch on VCPU %d\n"
"Expected: %"PRIx64"\nEncountered: %"
PRIx64"\nExecuted Last: %"PRIx64
"\nEvent type: %s\n",
pc_name, vcpu_index, expected, encountered, last,
discon_type_name);
if (abort_on_mismatch) {
/*
* The qemu log infrastructure may lose messages when aborting. Using
* fputs directly ensures the final report is visible to developers.
*/
fputs(report, stderr);
g_abort();
} else {
qemu_plugin_outs(report);
}
g_free(report);
}
static void vcpu_discon(qemu_plugin_id_t id, unsigned int vcpu_index,
enum qemu_plugin_discon_type type, uint64_t from_pc,
uint64_t to_pc)
{
struct cpu_state *state = qemu_plugin_scoreboard_find(states, vcpu_index);
if (type == QEMU_PLUGIN_DISCON_EXCEPTION &&
addr_eq(state->last_pc, from_pc))
{
/*
* For some types of exceptions, insn_exec will be called for the
* instruction that caused the exception. This is valid behaviour and
* does not need to be reported.
*/
} else if (state->has_next) {
/*
* We may encounter discontinuity chains without any instructions
* being executed in between.
*/
report_mismatch("source", vcpu_index, type, state->last_pc,
state->next_pc, from_pc);
} else if (state->has_from) {
report_mismatch("source", vcpu_index, type, state->last_pc,
state->from_pc, from_pc);
}
state->has_from = false;
state->next_pc = to_pc;
state->next_type = type;
state->has_next = true;
}
static void insn_exec(unsigned int vcpu_index, void *userdata)
{
struct cpu_state *state = qemu_plugin_scoreboard_find(states, vcpu_index);
if (state->has_next) {
report_mismatch("target", vcpu_index, state->next_type, state->last_pc,
state->next_pc, state->last_pc);
state->has_next = false;
}
if (trace_all_insns) {
g_autoptr(GString) report = g_string_new(NULL);
g_string_append_printf(report, "Exec insn at %"PRIx64" on VCPU %d\n",
state->last_pc, vcpu_index);
qemu_plugin_outs(report->str);
}
}
static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
{
size_t n_insns = qemu_plugin_tb_n_insns(tb);
for (size_t i = 0; i < n_insns; i++) {
struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
uint64_t pc = qemu_plugin_insn_vaddr(insn);
uint64_t next_pc = pc + qemu_plugin_insn_size(insn);
uint64_t has_next = (i + 1) < n_insns;
qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(insn,
QEMU_PLUGIN_INLINE_STORE_U64,
last_pc, pc);
qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(insn,
QEMU_PLUGIN_INLINE_STORE_U64,
from_pc, next_pc);
qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(insn,
QEMU_PLUGIN_INLINE_STORE_U64,
has_from, has_next);
qemu_plugin_register_vcpu_insn_exec_cb(insn, insn_exec,
QEMU_PLUGIN_CB_NO_REGS, NULL);
}
}
QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
const qemu_info_t *info,
int argc, char **argv)
{
if (!info->system_emulation) {
qemu_plugin_outs("Testing of the disontinuity plugin API is only"
" possible in system emulation mode.");
return 0;
}
/* Set defaults */
abort_on_mismatch = true;
trace_all_insns = false;
for (int i = 0; i < argc; i++) {
char *opt = argv[i];
g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
if (g_strcmp0(tokens[0], "abort") == 0) {
if (!qemu_plugin_bool_parse(tokens[0], tokens[1],
&abort_on_mismatch)) {
fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
return -1;
}
} else if (g_strcmp0(tokens[0], "trace-all") == 0) {
if (!qemu_plugin_bool_parse(tokens[0], tokens[1],
&trace_all_insns)) {
fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
return -1;
}
} else {
fprintf(stderr, "option parsing failed: %s\n", opt);
return -1;
}
}
states = qemu_plugin_scoreboard_new(sizeof(struct cpu_state));
last_pc = qemu_plugin_scoreboard_u64_in_struct(states, struct cpu_state,
last_pc);
from_pc = qemu_plugin_scoreboard_u64_in_struct(states, struct cpu_state,
from_pc);
has_from = qemu_plugin_scoreboard_u64_in_struct(states, struct cpu_state,
has_from);
qemu_plugin_register_vcpu_discon_cb(id, QEMU_PLUGIN_DISCON_ALL,
vcpu_discon);
qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
return 0;
}

View file

@ -1,6 +1,6 @@
t = []
if get_option('plugins')
foreach i : ['bb', 'empty', 'inline', 'insn', 'mem', 'reset', 'syscall', 'patch']
foreach i : ['bb', 'discons', 'empty', 'inline', 'insn', 'mem', 'reset', 'syscall', 'patch']
if host_os == 'windows'
t += shared_module(i, files(i + '.c') + '../../../contrib/plugins/win32_linker.c',
include_directories: '../../../include/qemu',

View file

@ -24,5 +24,17 @@ EXTRA_RUNS += run-test-mepc-masking
run-test-mepc-masking: test-mepc-masking
$(call run-test, $<, $(QEMU) $(QEMU_OPTS)$<)
EXTRA_RUNS += run-plugin-doubletrap
run-plugin-doubletrap: doubletrap
$(call run-test, $<, \
$(QEMU) -plugin ../plugins/libdiscons.so -d plugin -D $<.pout \
$(QEMU_OPTS)$<)
EXTRA_RUNS += run-plugin-interruptedmemory
run-plugin-interruptedmemory: interruptedmemory
$(call run-test, $<, \
$(QEMU) -plugin ../plugins/libdiscons.so -d plugin -D $<.pout \
$(QEMU_OPTS)$<)
# We don't currently support the multiarch system tests
undefine MULTIARCH_TESTS

View file

@ -0,0 +1,73 @@
.option norvc
.text
.global _start
_start:
# Set up vectored interrupts
lla t0, trap
add t0, t0, 1
csrw mtvec, t0
# Enable sw interrupts
csrrsi zero, mie, 0x8
csrrsi zero, mstatus, 0x8
# Engage the double trap: we trigger an machine-level software
# interrupt, which will trap to an illegal instruction
lui t1, 0x02000
li t0, 1
sw t0, 0(t1)
# If we still not went out via the software interrupt route after a
# short while, we failed the test.
lui t0, 0x1
0:
addi t0, t0, -1
bnez t0, 0b
j fail
trap:
j illegal_insn # Exceptions
j fail # Supervisor software interrupt
j fail
.insn i CUSTOM_0, 0, x0, x0, 0 # Machine software interrupt
j fail
j fail # Supervisor timer interrupt
j fail
j fail # Machine timer interrupt
j fail
j fail # Supervisor external interrupt
j fail
j fail # Machine external interrupt
j fail
j fail # Counter overflow interrupt
j fail
j fail
illegal_insn:
# Check whether we really got an illegal instruction
csrr t0, mcause
li t1, 2
bne t0, t1, fail
li a0, 0
j _exit
fail:
li a0, 1
_exit:
lla a1, semiargs
li t0, 0x20026 # ADP_Stopped_ApplicationExit
sd t0, 0(a1)
sd a0, 8(a1)
li a0, 0x20 # TARGET_SYS_EXIT_EXTENDED
# Semihosting call sequence
.balign 16
slli zero, zero, 0x1f
ebreak
srai zero, zero, 0x7
j .
.data
.balign 16
semiargs:
.space 16

View file

@ -0,0 +1,97 @@
.option norvc
.text
.global _start
_start:
# Set up trap vector
lla t0, trap
csrw mtvec, t0
# Set up timer
lui t1, 0x02004
sd zero, 0(t1) # MTIMECMP0
# Enable timer interrupts
li t0, 0x80
csrrs zero, mie, t0
csrrsi zero, mstatus, 0x8
# Set up UART
lui t1, 0x10000
li a0, 0x80 # DLAB=1
sb a0, 3(t1)
li a0, 1 # Full speed
sw a0, 0(t1)
li a0, 0x03 # 8N1, DLAB=0
sb a0, 3(t1)
# Run test for around 60s
call rtc_get
li t0, 30
slli t0, t0, 30 # Approx. 10e9 ns
add t0, t0, a0
0:
# Tight loop with memory accesses
li a1, 1000000
la a2, semiargs
1:
ld a0, 0(a2)
sd a0, 0(a2)
addi a1, a1, -1
bnez a1, 1b
li a0, '.'
call send_byte
call rtc_get
bltu a0, t0, 0b
li a0, '\n'
call send_byte
# Exit
li a0, 0
lla a1, semiargs
li t0, 0x20026 # ADP_Stopped_ApplicationExit
sd t0, 0(a1)
sd a0, 8(a1)
li a0, 0x20 # TARGET_SYS_EXIT_EXTENDED
# Semihosting call sequence
.balign 16
slli zero, zero, 0x1f
ebreak
srai zero, zero, 0x7
j .
rtc_get:
# Get current time from the goldfish RTC
lui t3, 0x0101
lw a0, 0(t3)
lw t3, 4(t3)
slli t3, t3, 32
add a0, a0, t3
ret
send_byte:
# Send a single byte over the serial
lui t3, 0x10000
lb a1, 5(t3)
andi a1, a1, 0x20
beqz a1, send_byte
sb a0, 0(t3)
ret
.balign 4
trap:
lui t5, 0x0200c
ld t6, -0x8(t5) # MTIME
addi t6, t6, 100
lui t5, 0x02004
sd t6, 0(t5) # MTIMECMP
mret
.data
.balign 16
semiargs:
.space 16