mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'pull-10.2-maintainer-260925-1' of https://gitlab.com/stsquad/qemu into staging
September maintainer updates (scripts, semihosting, plugins) - new gitlab-failure-analysis script - tweak checkpath to ignore license in removed lines - refactor semihosting to build once - add explicit assert to execlog for coverity - new uftrace plugin # -----BEGIN PGP SIGNATURE----- # # iQEzBAABCgAdFiEEZoWumedRZ7yvyN81+9DbCVqeKkQFAmjWWJYACgkQ+9DbCVqe # KkS1sgf+LsP0jsc1wKhzBhO4WarXXacWCDxK22riJ3aolm+gJ+b0WI4ds18A0e3R # z/J8VJVxBZ+6Hid+tOCQwfZ+Hb1p9IofzBdZryGUvwguviNdlpEChhXXnoZkicym # aGcC/jYRkhTx42dKRdZrSzPd3ccipqop9RvGx57bjCSBAEHYNz679p4z91kNR5a9 # UfcCzIQHbBUPZo0F9gQkNnBrjsJQhvF+gXPmmsmBI1pby6gNRQvFshrTQ1C32VpL # VgXNc9cZ6vaREWlgb6izNjsMP7cYTMH2Ppxty/FyEMg7GTfWRjI6Ec8fJKjPFtKr # ZbCNNAeJ9uLK6pJfTk2YxYabxx3JuQ== # =cR9e # -----END PGP SIGNATURE----- # gpg: Signature made Fri 26 Sep 2025 02:10:46 AM PDT # 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-260925-1' of https://gitlab.com/stsquad/qemu: (24 commits) contrib/plugins/uftrace: add documentation contrib/plugins/uftrace_symbols.py contrib/plugins/uftrace: implement x64 support contrib/plugins/uftrace: generate additional files for uftrace contrib/plugins/uftrace: implement privilege level tracing contrib/plugins/uftrace: implement tracing contrib/plugins/uftrace: track callstack contrib/plugins/uftrace: define cpu operations and implement aarch64 contrib/plugins/uftrace: skeleton file contrib/plugins/execlog: Explicitly check for qemu_plugin_read_register() failure semihosting/arm-compat-semi: compile once in system and per target for user mode semihosting/arm-compat-semi: remove dependency on cpu.h semihosting/arm-compat-semi: eradicate target_long semihosting/arm-compat-semi: replace target_ulong semihosting/arm-compat-semi: eradicate sizeof(target_ulong) include/semihosting/common-semi: extract common_semi API target/{arm, riscv}/common-semi-target: eradicate target_ulong target/riscv/common-semi-target: remove sizeof(target_ulong) semihosting/arm-compat-semi: change common_semi_sys_exit_extended semihosting/guestfd: compile once for system/user ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
@@ -95,6 +95,7 @@ static void insn_check_regs(CPU *cpu)
|
||||
|
||||
g_byte_array_set_size(reg->new, 0);
|
||||
sz = qemu_plugin_read_register(reg->handle, reg->new);
|
||||
g_assert(sz > 0);
|
||||
g_assert(sz == reg->last->len);
|
||||
|
||||
if (memcmp(reg->last->data, reg->new->data, sz)) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
contrib_plugins = ['bbv', 'cache', 'cflow', 'drcov', 'execlog', 'hotblocks',
|
||||
'hotpages', 'howvec', 'hwprofile', 'ips', 'stoptrigger']
|
||||
'hotpages', 'howvec', 'hwprofile', 'ips', 'stoptrigger',
|
||||
'uftrace']
|
||||
if host_os != 'windows'
|
||||
# lockstep uses socket.h
|
||||
contrib_plugins += 'lockstep'
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Executable
+152
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Create symbols and mapping files for uftrace.
|
||||
#
|
||||
# Copyright 2025 Linaro Ltd
|
||||
# Author: Pierrick Bouvier <pierrick.bouvier@linaro.org>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import argparse
|
||||
import elftools # pip install pyelftools
|
||||
import os
|
||||
|
||||
from elftools.elf.elffile import ELFFile
|
||||
from elftools.elf.sections import SymbolTableSection
|
||||
|
||||
def elf_func_symbols(elf):
|
||||
symbol_tables = [(idx, s) for idx, s in enumerate(elf.iter_sections())
|
||||
if isinstance(s, SymbolTableSection)]
|
||||
symbols = []
|
||||
for _, section in symbol_tables:
|
||||
for _, symbol in enumerate(section.iter_symbols()):
|
||||
if symbol_size(symbol) == 0:
|
||||
continue
|
||||
type = symbol['st_info']['type']
|
||||
if type == 'STT_FUNC' or type == 'STT_NOTYPE':
|
||||
symbols.append(symbol)
|
||||
symbols.sort(key = lambda x: symbol_addr(x))
|
||||
return symbols
|
||||
|
||||
def symbol_size(symbol):
|
||||
return symbol['st_size']
|
||||
|
||||
def symbol_addr(symbol):
|
||||
addr = symbol['st_value']
|
||||
# clamp addr to 48 bits, like uftrace entries
|
||||
return addr & 0xffffffffffff
|
||||
|
||||
def symbol_name(symbol):
|
||||
return symbol.name
|
||||
|
||||
class BinaryFile:
|
||||
def __init__(self, path, map_offset):
|
||||
self.fullpath = os.path.realpath(path)
|
||||
self.map_offset = map_offset
|
||||
with open(path, 'rb') as f:
|
||||
self.elf = ELFFile(f)
|
||||
self.symbols = elf_func_symbols(self.elf)
|
||||
|
||||
def path(self):
|
||||
return self.fullpath
|
||||
|
||||
def addr_start(self):
|
||||
return self.map_offset
|
||||
|
||||
def addr_end(self):
|
||||
last_sym = self.symbols[-1]
|
||||
return symbol_addr(last_sym) + symbol_size(last_sym) + self.map_offset
|
||||
|
||||
def generate_symbol_file(self, prefix_symbols):
|
||||
binary_name = os.path.basename(self.fullpath)
|
||||
sym_file_path = f'./uftrace.data/{binary_name}.sym'
|
||||
print(f'{sym_file_path} ({len(self.symbols)} symbols)')
|
||||
with open(sym_file_path, 'w') as sym_file:
|
||||
# print hexadecimal addresses on 48 bits
|
||||
addrx = "0>12x"
|
||||
for s in self.symbols:
|
||||
addr = symbol_addr(s)
|
||||
addr = f'{addr:{addrx}}'
|
||||
size = f'{symbol_size(s):{addrx}}'
|
||||
name = symbol_name(s)
|
||||
if prefix_symbols:
|
||||
name = f'{binary_name}:{name}'
|
||||
print(addr, size, 'T', name, file=sym_file)
|
||||
|
||||
def parse_parameter(p):
|
||||
s = p.split(":")
|
||||
path = s[0]
|
||||
if len(s) == 1:
|
||||
return path, 0
|
||||
if len(s) > 2:
|
||||
raise ValueError('only one offset can be set')
|
||||
offset = s[1]
|
||||
if not offset.startswith('0x'):
|
||||
err = f'offset "{offset}" is not an hexadecimal constant. '
|
||||
err += 'It should starts with "0x".'
|
||||
raise ValueError(err)
|
||||
offset = int(offset, 16)
|
||||
return path, offset
|
||||
|
||||
def is_from_user_mode(map_file_path):
|
||||
if os.path.exists(map_file_path):
|
||||
with open(map_file_path, 'r') as map_file:
|
||||
if not map_file.readline().startswith('# map stack on'):
|
||||
return True
|
||||
return False
|
||||
|
||||
def generate_map(binaries):
|
||||
map_file_path = './uftrace.data/sid-0.map'
|
||||
|
||||
if is_from_user_mode(map_file_path):
|
||||
print(f'do not overwrite {map_file_path} generated from qemu-user')
|
||||
return
|
||||
|
||||
mappings = []
|
||||
|
||||
# print hexadecimal addresses on 48 bits
|
||||
addrx = "0>12x"
|
||||
|
||||
mappings += ['# map stack on highest address possible, to prevent uftrace']
|
||||
mappings += ['# from considering any kernel address']
|
||||
mappings += ['ffffffffffff-ffffffffffff rw-p 00000000 00:00 0 [stack]']
|
||||
|
||||
for b in binaries:
|
||||
m = f'{b.addr_start():{addrx}}-{b.addr_end():{addrx}}'
|
||||
m += f' r--p 00000000 00:00 0 {b.path()}'
|
||||
mappings.append(m)
|
||||
|
||||
with open(map_file_path, 'w') as map_file:
|
||||
print('\n'.join(mappings), file=map_file)
|
||||
print(f'{map_file_path}')
|
||||
print('\n'.join(mappings))
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=
|
||||
'generate symbol files for uftrace')
|
||||
parser.add_argument('elf_file', nargs='+',
|
||||
help='path to an ELF file. '
|
||||
'Use /path/to/file:0xdeadbeef to add a mapping offset.')
|
||||
parser.add_argument('--prefix-symbols',
|
||||
help='prepend binary name to symbols',
|
||||
action=argparse.BooleanOptionalAction)
|
||||
args = parser.parse_args()
|
||||
|
||||
if not os.path.exists('./uftrace.data'):
|
||||
os.mkdir('./uftrace.data')
|
||||
|
||||
binaries = []
|
||||
for file in args.elf_file:
|
||||
path, offset = parse_parameter(file)
|
||||
b = BinaryFile(path, offset)
|
||||
binaries.append(b)
|
||||
binaries.sort(key = lambda b: b.addr_end());
|
||||
|
||||
for b in binaries:
|
||||
b.generate_symbol_file(args.prefix_symbols)
|
||||
|
||||
generate_map(binaries)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -816,6 +816,205 @@ This plugin can limit the number of Instructions Per Second that are executed::
|
||||
The lower the number the more accurate time will be, but the less efficient the plugin.
|
||||
Defaults to ips/10
|
||||
|
||||
Uftrace
|
||||
.......
|
||||
|
||||
``contrib/plugins/uftrace.c``
|
||||
|
||||
This plugin generates a binary trace compatible with
|
||||
`uftrace <https://github.com/namhyung/uftrace>`_.
|
||||
|
||||
Plugin supports aarch64 and x64, and works in user and system mode, allowing to
|
||||
trace a system boot, which is not something possible usually.
|
||||
|
||||
In user mode, the memory mapping is directly copied from ``/proc/self/maps`` at
|
||||
the end of execution. Uftrace should be able to retrieve symbols by itself,
|
||||
without any additional step.
|
||||
In system mode, the default memory mapping is empty, and you can generate
|
||||
one (and associated symbols) using ``contrib/plugins/uftrace_symbols.py``.
|
||||
Symbols must be present in ELF binaries.
|
||||
|
||||
It tracks the call stack (based on frame pointer analysis). Thus, your program
|
||||
and its dependencies must be compiled using ``-fno-omit-frame-pointer
|
||||
-mno-omit-leaf-frame-pointer``. In 2024, `Ubuntu and Fedora enabled it by
|
||||
default again on x64
|
||||
<https://www.brendangregg.com/blog/2024-03-17/the-return-of-the-frame-pointers.html>`_.
|
||||
On aarch64, this is less of a problem, as they are usually part of the ABI,
|
||||
except for leaf functions. That's true for user space applications, but not
|
||||
necessarily for bare metal code. You can read this `section
|
||||
<uftrace_build_system_example>` to easily build a system with frame pointers.
|
||||
|
||||
When tracing long scenarios (> 1 min), the generated trace can become very long,
|
||||
making it hard to extract data from it. In this case, a simple solution is to
|
||||
trace execution while generating a timestamped output log using
|
||||
``qemu-system-aarch64 ... | ts "%s"``. Then, ``uftrace --time-range=start~end``
|
||||
can be used to reduce trace for only this part of execution.
|
||||
|
||||
Performance wise, overhead compared to normal tcg execution is around x5-x15.
|
||||
|
||||
.. list-table:: Uftrace plugin arguments
|
||||
:widths: 20 80
|
||||
:header-rows: 1
|
||||
|
||||
* - Option
|
||||
- Description
|
||||
* - trace-privilege-level=[on|off]
|
||||
- Generate separate traces for each privilege level (Exception Level +
|
||||
Security State on aarch64, Rings on x64).
|
||||
|
||||
.. list-table:: uftrace_symbols.py arguments
|
||||
:widths: 20 80
|
||||
:header-rows: 1
|
||||
|
||||
* - Option
|
||||
- Description
|
||||
* - elf_file [elf_file ...]
|
||||
- path to an ELF file. Use /path/to/file:0xdeadbeef to add a mapping offset.
|
||||
* - --prefix-symbols
|
||||
- prepend binary name to symbols
|
||||
|
||||
Example user trace
|
||||
++++++++++++++++++
|
||||
|
||||
As an example, we can trace qemu itself running git::
|
||||
|
||||
$ ./build/qemu-aarch64 -plugin \
|
||||
build/contrib/plugins/libuftrace.so \
|
||||
./build/qemu-aarch64 /usr/bin/git --help
|
||||
|
||||
# and generate a chrome trace directly
|
||||
$ uftrace dump --chrome | gzip > ~/qemu_aarch64_git_help.json.gz
|
||||
|
||||
For convenience, you can download this trace `qemu_aarch64_git_help.json.gz
|
||||
<https://fileserver.linaro.org/s/N8X8fnZ5yGRZLsT/download/qemu_aarch64_git_help.json.gz>`_.
|
||||
Download it and open this trace on https://ui.perfetto.dev/. You can zoom in/out
|
||||
using :kbd:`W`, :kbd:`A`, :kbd:`S`, :kbd:`D` keys.
|
||||
Some sequences taken from this trace:
|
||||
|
||||
- Loading program and its interpreter
|
||||
|
||||
.. image:: https://fileserver.linaro.org/s/fie8JgX76yyL5cq/preview
|
||||
:height: 200px
|
||||
|
||||
- open syscall
|
||||
|
||||
.. image:: https://fileserver.linaro.org/s/rsXPTeZZPza4PcE/preview
|
||||
:height: 200px
|
||||
|
||||
- TB creation
|
||||
|
||||
.. image:: https://fileserver.linaro.org/s/GXY6NKMw5EeRCew/preview
|
||||
:height: 200px
|
||||
|
||||
It's usually better to use ``uftrace record`` directly. However, tracing
|
||||
binaries through qemu-user can be convenient when you don't want to recompile
|
||||
them (``uftrace record`` requires instrumentation), as long as symbols are
|
||||
present.
|
||||
|
||||
Example system trace
|
||||
++++++++++++++++++++
|
||||
|
||||
A full trace example (chrome trace, from instructions below) generated from a
|
||||
system boot can be found `here
|
||||
<https://fileserver.linaro.org/s/WsemLboPEzo24nw/download/aarch64_boot.json.gz>`_.
|
||||
Download it and open this trace on https://ui.perfetto.dev/. You can see code
|
||||
executed for all privilege levels, and zoom in/out using
|
||||
:kbd:`W`, :kbd:`A`, :kbd:`S`, :kbd:`D` keys. You can find below some sequences
|
||||
taken from this trace:
|
||||
|
||||
- Two first stages of boot sequence in Arm Trusted Firmware (EL3 and S-EL1)
|
||||
|
||||
.. image:: https://fileserver.linaro.org/s/kkxBS552W7nYESX/preview
|
||||
:height: 200px
|
||||
|
||||
- U-boot initialization (until code relocation, after which we can't track it)
|
||||
|
||||
.. image:: https://fileserver.linaro.org/s/LKTgsXNZFi5GFNC/preview
|
||||
:height: 200px
|
||||
|
||||
- Stat and open syscalls in kernel
|
||||
|
||||
.. image:: https://fileserver.linaro.org/s/dXe4MfraKg2F476/preview
|
||||
:height: 200px
|
||||
|
||||
- Timer interrupt
|
||||
|
||||
.. image:: https://fileserver.linaro.org/s/TM5yobYzJtP7P3C/preview
|
||||
:height: 200px
|
||||
|
||||
- Poweroff sequence (from kernel back to firmware, NS-EL2 to EL3)
|
||||
|
||||
.. image:: https://fileserver.linaro.org/s/oR2PtyGKJrqnfRf/preview
|
||||
:height: 200px
|
||||
|
||||
Build and run system example
|
||||
++++++++++++++++++++++++++++
|
||||
|
||||
.. _uftrace_build_system_example:
|
||||
|
||||
Building a full system image with frame pointers is not trivial.
|
||||
|
||||
We provide a `simple way <https://github.com/pbo-linaro/qemu-linux-stack>`_ to
|
||||
build an aarch64 system, combining Arm Trusted firmware, U-boot, Linux kernel
|
||||
and debian userland. It's based on containers (``podman`` only) and
|
||||
``qemu-user-static (binfmt)`` to make sure it's easily reproducible and does not depend
|
||||
on machine where you build it.
|
||||
|
||||
You can follow the exact same instructions for a x64 system, combining edk2,
|
||||
Linux, and Ubuntu, simply by switching to
|
||||
`x86_64 <https://github.com/pbo-linaro/qemu-linux-stack/tree/x86_64>`_ branch.
|
||||
|
||||
To build the system::
|
||||
|
||||
# Install dependencies
|
||||
$ sudo apt install -y podman qemu-user-static
|
||||
|
||||
$ git clone https://github.com/pbo-linaro/qemu-linux-stack
|
||||
$ cd qemu-linux-stack
|
||||
$ ./build.sh
|
||||
|
||||
# system can be started using:
|
||||
$ ./run.sh /path/to/qemu-system-aarch64
|
||||
|
||||
To generate a uftrace for a system boot from that::
|
||||
|
||||
# run true and poweroff the system
|
||||
$ env INIT=true ./run.sh path/to/qemu-system-aarch64 \
|
||||
-plugin path/to/contrib/plugins/libuftrace.so,trace-privilege-level=on
|
||||
|
||||
# generate symbols and memory mapping
|
||||
$ path/to/contrib/plugins/uftrace_symbols.py \
|
||||
--prefix-symbols \
|
||||
arm-trusted-firmware/build/qemu/debug/bl1/bl1.elf \
|
||||
arm-trusted-firmware/build/qemu/debug/bl2/bl2.elf \
|
||||
arm-trusted-firmware/build/qemu/debug/bl31/bl31.elf \
|
||||
u-boot/u-boot:0x60000000 \
|
||||
linux/vmlinux
|
||||
|
||||
# inspect trace with
|
||||
$ uftrace replay
|
||||
|
||||
Uftrace allows to filter the trace, and dump flamegraphs, or a chrome trace.
|
||||
This last one is very interesting to see visually the boot process::
|
||||
|
||||
$ uftrace dump --chrome > boot.json
|
||||
# Open your browser, and load boot.json on https://ui.perfetto.dev/.
|
||||
|
||||
Long visual chrome traces can't be easily opened, thus, it might be
|
||||
interesting to generate them around a particular point of execution::
|
||||
|
||||
# execute qemu and timestamp output log
|
||||
$ env INIT=true ./run.sh path/to/qemu-system-aarch64 \
|
||||
-plugin path/to/contrib/plugins/libuftrace.so,trace-privilege-level=on |&
|
||||
ts "%s" | tee exec.log
|
||||
|
||||
$ cat exec.log | grep 'Run /init'
|
||||
1753122320 [ 11.834391] Run /init as init process
|
||||
# init was launched at 1753122320
|
||||
|
||||
# generate trace around init execution (2 seconds):
|
||||
$ uftrace dump --chrome --time-range=1753122320~1753122322 > init.json
|
||||
|
||||
Other emulation features
|
||||
------------------------
|
||||
|
||||
|
||||
@@ -35,5 +35,11 @@
|
||||
#define COMMON_SEMI_H
|
||||
|
||||
void do_common_semihosting(CPUState *cs);
|
||||
uint64_t common_semi_arg(CPUState *cs, int argno);
|
||||
void common_semi_set_ret(CPUState *cs, uint64_t ret);
|
||||
bool is_64bit_semihosting(CPUArchState *env);
|
||||
bool common_semi_sys_exit_is_extended(CPUState *cs);
|
||||
uint64_t common_semi_stack_bottom(CPUState *cs);
|
||||
bool common_semi_has_synccache(CPUArchState *env);
|
||||
|
||||
#endif /* COMMON_SEMI_H */
|
||||
|
||||
@@ -35,13 +35,6 @@ typedef struct GuestFD {
|
||||
};
|
||||
} GuestFD;
|
||||
|
||||
/*
|
||||
* For ARM semihosting, we have a separate structure for routing
|
||||
* data for the console which is outside the guest fd address space.
|
||||
*/
|
||||
extern GuestFD console_in_gf;
|
||||
extern GuestFD console_out_gf;
|
||||
|
||||
/**
|
||||
* alloc_guestfd:
|
||||
*
|
||||
|
||||
@@ -33,6 +33,8 @@ typedef enum SemihostingTarget {
|
||||
* Return true if guest code is allowed to make semihosting calls.
|
||||
*/
|
||||
bool semihosting_enabled(bool is_user);
|
||||
bool semihosting_arm_compatible(void);
|
||||
void semihosting_arm_compatible_init(void);
|
||||
|
||||
SemihostingTarget semihosting_get_target(void);
|
||||
const char *semihosting_get_arg(int i);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#ifndef SEMIHOSTING_SYSCALLS_H
|
||||
#define SEMIHOSTING_SYSCALLS_H
|
||||
|
||||
#include "exec/cpu-defs.h"
|
||||
#include "exec/vaddr.h"
|
||||
#include "gdbstub/syscalls.h"
|
||||
|
||||
/*
|
||||
@@ -24,23 +24,23 @@
|
||||
typedef struct GuestFD GuestFD;
|
||||
|
||||
void semihost_sys_open(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong fname, target_ulong fname_len,
|
||||
vaddr fname, uint64_t fname_len,
|
||||
int gdb_flags, int mode);
|
||||
|
||||
void semihost_sys_close(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
int fd);
|
||||
|
||||
void semihost_sys_read(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
int fd, target_ulong buf, target_ulong len);
|
||||
int fd, vaddr buf, uint64_t len);
|
||||
|
||||
void semihost_sys_read_gf(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
GuestFD *gf, target_ulong buf, target_ulong len);
|
||||
GuestFD *gf, vaddr buf, uint64_t len);
|
||||
|
||||
void semihost_sys_write(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
int fd, target_ulong buf, target_ulong len);
|
||||
int fd, vaddr buf, uint64_t len);
|
||||
|
||||
void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
GuestFD *gf, target_ulong buf, target_ulong len);
|
||||
GuestFD *gf, vaddr buf, uint64_t len);
|
||||
|
||||
void semihost_sys_lseek(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
int fd, int64_t off, int gdb_whence);
|
||||
@@ -50,27 +50,27 @@ void semihost_sys_isatty(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
|
||||
void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb,
|
||||
gdb_syscall_complete_cb flen_cb,
|
||||
int fd, target_ulong fstat_addr);
|
||||
int fd, vaddr fstat_addr);
|
||||
|
||||
void semihost_sys_fstat(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
int fd, target_ulong addr);
|
||||
int fd, vaddr addr);
|
||||
|
||||
void semihost_sys_stat(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong fname, target_ulong fname_len,
|
||||
target_ulong addr);
|
||||
vaddr fname, uint64_t fname_len,
|
||||
vaddr addr);
|
||||
|
||||
void semihost_sys_remove(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong fname, target_ulong fname_len);
|
||||
vaddr fname, uint64_t fname_len);
|
||||
|
||||
void semihost_sys_rename(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong oname, target_ulong oname_len,
|
||||
target_ulong nname, target_ulong nname_len);
|
||||
vaddr oname, uint64_t oname_len,
|
||||
vaddr nname, uint64_t nname_len);
|
||||
|
||||
void semihost_sys_system(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong cmd, target_ulong cmd_len);
|
||||
vaddr cmd, uint64_t cmd_len);
|
||||
|
||||
void semihost_sys_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong tv_addr, target_ulong tz_addr);
|
||||
vaddr tv_addr, vaddr tz_addr);
|
||||
|
||||
void semihost_sys_poll_one(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
int fd, GIOCondition cond, int timeout);
|
||||
|
||||
@@ -1816,7 +1816,8 @@ sub process {
|
||||
}
|
||||
|
||||
# Check SPDX-License-Identifier references a permitted license
|
||||
if ($rawline =~ m,SPDX-License-Identifier: (.*?)(\*/)?\s*$,) {
|
||||
if (($rawline =~ m,SPDX-License-Identifier: (.*?)(\*/)?\s*$,) &&
|
||||
$rawline !~ /^-/) {
|
||||
$fileinfo->{facts}->{sawspdx} = 1;
|
||||
&checkspdx($realfile, $1);
|
||||
}
|
||||
|
||||
Executable
+117
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# A script to analyse failures in the gitlab pipelines. It requires an
|
||||
# API key from gitlab with the following permissions:
|
||||
# - api
|
||||
# - read_repository
|
||||
# - read_user
|
||||
#
|
||||
|
||||
import argparse
|
||||
import gitlab
|
||||
import os
|
||||
|
||||
#
|
||||
# Arguments
|
||||
#
|
||||
class NoneForEmptyStringAction(argparse.Action):
|
||||
def __call__(self, parser, namespace, value, option_string=None):
|
||||
if value == '':
|
||||
setattr(namespace, self.dest, None)
|
||||
else:
|
||||
setattr(namespace, self.dest, value)
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(description="Analyse failed GitLab CI runs.")
|
||||
|
||||
parser.add_argument("--gitlab",
|
||||
default="https://gitlab.com",
|
||||
help="GitLab instance URL (default: https://gitlab.com).")
|
||||
parser.add_argument("--id", default=11167699,
|
||||
type=int,
|
||||
help="GitLab project id (default: 11167699 for qemu-project/qemu)")
|
||||
parser.add_argument("--token",
|
||||
default=os.getenv("GITLAB_TOKEN"),
|
||||
help="Your personal access token with 'api' scope.")
|
||||
parser.add_argument("--branch",
|
||||
type=str,
|
||||
default="staging",
|
||||
action=NoneForEmptyStringAction,
|
||||
help="The name of the branch (default: 'staging')")
|
||||
parser.add_argument("--status",
|
||||
type=str,
|
||||
action=NoneForEmptyStringAction,
|
||||
default="failed",
|
||||
help="Filter by branch status (default: 'failed')")
|
||||
parser.add_argument("--count", type=int,
|
||||
default=3,
|
||||
help="The number of failed runs to fetch.")
|
||||
parser.add_argument("--skip-jobs",
|
||||
default=False,
|
||||
action='store_true',
|
||||
help="Skip dumping the job info")
|
||||
parser.add_argument("--pipeline", type=int,
|
||||
nargs="+",
|
||||
default=None,
|
||||
help="Explicit pipeline ID(s) to fetch.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parser.parse_args()
|
||||
|
||||
gl = gitlab.Gitlab(url=args.gitlab, private_token=args.token)
|
||||
project = gl.projects.get(args.id)
|
||||
|
||||
|
||||
pipelines_to_process = []
|
||||
|
||||
# Use explicit pipeline IDs if provided, otherwise fetch a list
|
||||
if args.pipeline:
|
||||
args.count = len(args.pipeline)
|
||||
for p_id in args.pipeline:
|
||||
pipelines_to_process.append(project.pipelines.get(p_id))
|
||||
else:
|
||||
# Use an iterator to fetch the pipelines
|
||||
pipe_iter = project.pipelines.list(iterator=True,
|
||||
status=args.status,
|
||||
ref=args.branch)
|
||||
# Check each failed pipeline
|
||||
pipelines_to_process = [next(pipe_iter) for _ in range(args.count)]
|
||||
|
||||
# Check each pipeline
|
||||
for p in pipelines_to_process:
|
||||
|
||||
jobs = p.jobs.list(get_all=True)
|
||||
failed_jobs = [j for j in jobs if j.status == "failed"]
|
||||
skipped_jobs = [j for j in jobs if j.status == "skipped"]
|
||||
manual_jobs = [j for j in jobs if j.status == "manual"]
|
||||
|
||||
trs = p.test_report_summary.get()
|
||||
total = trs.total["count"]
|
||||
skipped = trs.total["skipped"]
|
||||
failed = trs.total["failed"]
|
||||
|
||||
print(f"{p.status} pipeline {p.id}, total jobs {len(jobs)}, "
|
||||
f"skipped {len(skipped_jobs)}, "
|
||||
f"failed {len(failed_jobs)}, ",
|
||||
f"{total} tests, "
|
||||
f"{skipped} skipped tests, "
|
||||
f"{failed} failed tests")
|
||||
|
||||
if not args.skip_jobs:
|
||||
for j in failed_jobs:
|
||||
print(f" Failed job {j.id}, {j.name}, {j.web_url}")
|
||||
|
||||
# It seems we can only extract failing tests from the full
|
||||
# test report, maybe there is some way to filter it.
|
||||
|
||||
if failed > 0:
|
||||
ftr = p.test_report.get()
|
||||
failed_suites = [s for s in ftr.test_suites if
|
||||
s["failed_count"] > 0]
|
||||
for fs in failed_suites:
|
||||
name = fs["name"]
|
||||
tests = fs["test_cases"]
|
||||
failed_tests = [t for t in tests if t["status"] == 'failed']
|
||||
for t in failed_tests:
|
||||
print(f" Failed test {t["classname"]}, {name}, {t["name"]}")
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Stubs for platforms different from ARM
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "semihosting/semihost.h"
|
||||
#include <glib.h>
|
||||
|
||||
bool semihosting_arm_compatible(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void semihosting_arm_compatible_init(void)
|
||||
{
|
||||
g_assert_not_reached();
|
||||
}
|
||||
@@ -100,6 +100,13 @@ static int gdb_open_modeflags[12] = {
|
||||
GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
|
||||
};
|
||||
|
||||
/*
|
||||
* For ARM semihosting, we have a separate structure for routing
|
||||
* data for the console which is outside the guest fd address space.
|
||||
*/
|
||||
static GuestFD console_in_gf;
|
||||
static GuestFD console_out_gf;
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
|
||||
/**
|
||||
@@ -115,7 +122,7 @@ static int gdb_open_modeflags[12] = {
|
||||
*/
|
||||
|
||||
typedef struct LayoutInfo {
|
||||
target_ulong rambase;
|
||||
vaddr rambase;
|
||||
size_t ramsize;
|
||||
hwaddr heapbase;
|
||||
hwaddr heaplimit;
|
||||
@@ -166,8 +173,7 @@ static LayoutInfo common_semi_find_bases(CPUState *cs)
|
||||
|
||||
#endif
|
||||
|
||||
#include "cpu.h"
|
||||
#include "common-semi-target.h"
|
||||
#include "semihosting/common-semi.h"
|
||||
|
||||
/*
|
||||
* Read the input value from the argument block; fail the semihosting
|
||||
@@ -207,7 +213,7 @@ static LayoutInfo common_semi_find_bases(CPUState *cs)
|
||||
* global, and we assume that the guest takes care of avoiding any races.
|
||||
*/
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
static target_ulong syscall_err;
|
||||
static uint64_t syscall_err;
|
||||
|
||||
#include "semihosting/uaccess.h"
|
||||
#endif
|
||||
@@ -253,8 +259,8 @@ static void common_semi_rw_cb(CPUState *cs, uint64_t ret, int err)
|
||||
{
|
||||
/* Recover the original length from the third argument. */
|
||||
CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
|
||||
target_ulong args = common_semi_arg(cs, 1);
|
||||
target_ulong arg2;
|
||||
uint64_t args = common_semi_arg(cs, 1);
|
||||
uint64_t arg2;
|
||||
GET_ARG(2);
|
||||
|
||||
if (err) {
|
||||
@@ -293,9 +299,9 @@ static void common_semi_seek_cb(CPUState *cs, uint64_t ret, int err)
|
||||
* is defined by GDB's remote protocol and is not target-specific.)
|
||||
* We put this on the guest's stack just below SP.
|
||||
*/
|
||||
static target_ulong common_semi_flen_buf(CPUState *cs)
|
||||
static uint64_t common_semi_flen_buf(CPUState *cs)
|
||||
{
|
||||
target_ulong sp = common_semi_stack_bottom(cs);
|
||||
vaddr sp = common_semi_stack_bottom(cs);
|
||||
return sp - 64;
|
||||
}
|
||||
|
||||
@@ -352,6 +358,25 @@ static const uint8_t featurefile_data[] = {
|
||||
SH_EXT_EXIT_EXTENDED | SH_EXT_STDOUT_STDERR, /* Feature byte 0 */
|
||||
};
|
||||
|
||||
bool semihosting_arm_compatible(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void semihosting_arm_compatible_init(void)
|
||||
{
|
||||
/* For ARM-compat, the console is in a separate namespace. */
|
||||
if (use_gdb_syscalls()) {
|
||||
console_in_gf.type = GuestFDGDB;
|
||||
console_in_gf.hostfd = 0;
|
||||
console_out_gf.type = GuestFDGDB;
|
||||
console_out_gf.hostfd = 2;
|
||||
} else {
|
||||
console_in_gf.type = GuestFDConsole;
|
||||
console_out_gf.type = GuestFDConsole;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Do a semihosting call.
|
||||
*
|
||||
@@ -363,9 +388,9 @@ static const uint8_t featurefile_data[] = {
|
||||
void do_common_semihosting(CPUState *cs)
|
||||
{
|
||||
CPUArchState *env = cpu_env(cs);
|
||||
target_ulong args;
|
||||
target_ulong arg0, arg1, arg2, arg3;
|
||||
target_ulong ul_ret;
|
||||
uint64_t args;
|
||||
uint64_t arg0, arg1, arg2, arg3;
|
||||
uint64_t ul_ret;
|
||||
char * s;
|
||||
int nr;
|
||||
int64_t elapsed;
|
||||
@@ -436,7 +461,7 @@ void do_common_semihosting(CPUState *cs)
|
||||
|
||||
case TARGET_SYS_WRITEC:
|
||||
/*
|
||||
* FIXME: the byte to be written is in a target_ulong slot,
|
||||
* FIXME: the byte to be written is in a uint64_t slot,
|
||||
* which means this is wrong for a big-endian guest.
|
||||
*/
|
||||
semihost_sys_write_gf(cs, common_semi_dead_cb,
|
||||
@@ -475,10 +500,13 @@ void do_common_semihosting(CPUState *cs)
|
||||
break;
|
||||
|
||||
case TARGET_SYS_ISERROR:
|
||||
{
|
||||
GET_ARG(0);
|
||||
common_semi_set_ret(cs, (target_long)arg0 < 0);
|
||||
bool ret = is_64bit_semihosting(env) ?
|
||||
(int64_t)arg0 < 0 : (int32_t)arg0 < 0;
|
||||
common_semi_set_ret(cs, ret);
|
||||
break;
|
||||
|
||||
}
|
||||
case TARGET_SYS_ISTTY:
|
||||
GET_ARG(0);
|
||||
semihost_sys_isatty(cs, common_semi_istty_cb, arg0);
|
||||
@@ -662,7 +690,7 @@ void do_common_semihosting(CPUState *cs)
|
||||
|
||||
case TARGET_SYS_HEAPINFO:
|
||||
{
|
||||
target_ulong retvals[4];
|
||||
uint64_t retvals[4];
|
||||
int i;
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
TaskState *ts = get_task_state(cs);
|
||||
@@ -728,7 +756,8 @@ void do_common_semihosting(CPUState *cs)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
if (common_semi_sys_exit_extended(cs, nr)) {
|
||||
if (nr == TARGET_SYS_EXIT_EXTENDED ||
|
||||
common_semi_sys_exit_is_extended(cs)) {
|
||||
/*
|
||||
* The A64 version of SYS_EXIT takes a parameter block,
|
||||
* so the application-exit type can return a subcode which
|
||||
@@ -759,7 +788,7 @@ void do_common_semihosting(CPUState *cs)
|
||||
|
||||
case TARGET_SYS_ELAPSED:
|
||||
elapsed = get_clock() - clock_start;
|
||||
if (sizeof(target_ulong) == 8) {
|
||||
if (is_64bit_semihosting(env)) {
|
||||
if (SET_ARG(0, elapsed)) {
|
||||
goto do_fault;
|
||||
}
|
||||
|
||||
+5
-21
@@ -12,35 +12,20 @@
|
||||
#include "gdbstub/syscalls.h"
|
||||
#include "semihosting/semihost.h"
|
||||
#include "semihosting/guestfd.h"
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
#include CONFIG_DEVICES
|
||||
#endif
|
||||
|
||||
static GArray *guestfd_array;
|
||||
|
||||
#ifdef CONFIG_ARM_COMPATIBLE_SEMIHOSTING
|
||||
GuestFD console_in_gf;
|
||||
GuestFD console_out_gf;
|
||||
#endif
|
||||
|
||||
void qemu_semihosting_guestfd_init(void)
|
||||
{
|
||||
/* New entries zero-initialized, i.e. type GuestFDUnused */
|
||||
guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD));
|
||||
|
||||
#ifdef CONFIG_ARM_COMPATIBLE_SEMIHOSTING
|
||||
/* For ARM-compat, the console is in a separate namespace. */
|
||||
if (use_gdb_syscalls()) {
|
||||
console_in_gf.type = GuestFDGDB;
|
||||
console_in_gf.hostfd = 0;
|
||||
console_out_gf.type = GuestFDGDB;
|
||||
console_out_gf.hostfd = 2;
|
||||
} else {
|
||||
console_in_gf.type = GuestFDConsole;
|
||||
console_out_gf.type = GuestFDConsole;
|
||||
if (semihosting_arm_compatible()) {
|
||||
semihosting_arm_compatible_init();
|
||||
return;
|
||||
}
|
||||
#else
|
||||
/* Otherwise, the stdio file descriptors apply. */
|
||||
|
||||
/* Out of ARM, the stdio file descriptors apply. */
|
||||
guestfd_array = g_array_set_size(guestfd_array, 3);
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (!use_gdb_syscalls()) {
|
||||
@@ -54,7 +39,6 @@ void qemu_semihosting_guestfd_init(void)
|
||||
associate_guestfd(0, 0);
|
||||
associate_guestfd(1, 1);
|
||||
associate_guestfd(2, 2);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+11
-7
@@ -1,17 +1,21 @@
|
||||
specific_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files(
|
||||
'guestfd.c',
|
||||
'syscalls.c',
|
||||
))
|
||||
|
||||
common_ss.add(when: 'CONFIG_SEMIHOSTING', if_false: files('stubs-all.c'))
|
||||
user_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files('user.c'))
|
||||
user_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files(
|
||||
'user.c',
|
||||
'guestfd.c'))
|
||||
system_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files(
|
||||
'config.c',
|
||||
'console.c',
|
||||
'guestfd.c',
|
||||
'uaccess.c',
|
||||
'syscalls.c',
|
||||
), if_false: files(
|
||||
'stubs-system.c',
|
||||
))
|
||||
system_ss.add(when: 'CONFIG_ARM_COMPATIBLE_SEMIHOSTING',
|
||||
if_true: files('arm-compat-semi.c'),
|
||||
if_false: files('arm-compat-semi-stub.c'))
|
||||
|
||||
specific_ss.add(when: ['CONFIG_ARM_COMPATIBLE_SEMIHOSTING'],
|
||||
specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_USER_ONLY'],
|
||||
if_true: files('syscalls.c'))
|
||||
specific_ss.add(when: ['CONFIG_ARM_COMPATIBLE_SEMIHOSTING', 'CONFIG_USER_ONLY'],
|
||||
if_true: files('arm-compat-semi.c'))
|
||||
|
||||
+54
-55
@@ -8,7 +8,6 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "cpu.h"
|
||||
#include "gdbstub/syscalls.h"
|
||||
#include "semihosting/guestfd.h"
|
||||
#include "semihosting/syscalls.h"
|
||||
@@ -23,7 +22,7 @@
|
||||
/*
|
||||
* Validate or compute the length of the string (including terminator).
|
||||
*/
|
||||
static int validate_strlen(CPUState *cs, target_ulong str, target_ulong tlen)
|
||||
static int validate_strlen(CPUState *cs, vaddr str, uint64_t tlen)
|
||||
{
|
||||
CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
|
||||
char c;
|
||||
@@ -52,7 +51,7 @@ static int validate_strlen(CPUState *cs, target_ulong str, target_ulong tlen)
|
||||
}
|
||||
|
||||
static int validate_lock_user_string(char **pstr, CPUState *cs,
|
||||
target_ulong tstr, target_ulong tlen)
|
||||
vaddr tstr, uint64_t tlen)
|
||||
{
|
||||
int ret = validate_strlen(cs, tstr, tlen);
|
||||
CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
|
||||
@@ -72,7 +71,7 @@ static int validate_lock_user_string(char **pstr, CPUState *cs,
|
||||
* big-endian. Until we do something with gdb, also produce the
|
||||
* same big-endian result from the host.
|
||||
*/
|
||||
static int copy_stat_to_user(CPUState *cs, target_ulong addr,
|
||||
static int copy_stat_to_user(CPUState *cs, vaddr addr,
|
||||
const struct stat *s)
|
||||
{
|
||||
CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
|
||||
@@ -129,7 +128,7 @@ static void gdb_open_cb(CPUState *cs, uint64_t ret, int err)
|
||||
}
|
||||
|
||||
static void gdb_open(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong fname, target_ulong fname_len,
|
||||
vaddr fname, uint64_t fname_len,
|
||||
int gdb_flags, int mode)
|
||||
{
|
||||
int len = validate_strlen(cs, fname, fname_len);
|
||||
@@ -140,7 +139,7 @@ static void gdb_open(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
|
||||
gdb_open_complete = complete;
|
||||
gdb_do_syscall(gdb_open_cb, "open,%s,%x,%x",
|
||||
(uint64_t)fname, (uint32_t)len,
|
||||
(vaddr)fname, (uint32_t)len,
|
||||
(uint32_t)gdb_flags, (uint32_t)mode);
|
||||
}
|
||||
|
||||
@@ -151,17 +150,17 @@ static void gdb_close(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
static void gdb_read(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
GuestFD *gf, target_ulong buf, target_ulong len)
|
||||
GuestFD *gf, vaddr buf, uint64_t len)
|
||||
{
|
||||
gdb_do_syscall(complete, "read,%x,%lx,%lx",
|
||||
(uint32_t)gf->hostfd, (uint64_t)buf, (uint64_t)len);
|
||||
(uint32_t)gf->hostfd, (vaddr)buf, (uint64_t)len);
|
||||
}
|
||||
|
||||
static void gdb_write(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
GuestFD *gf, target_ulong buf, target_ulong len)
|
||||
GuestFD *gf, vaddr buf, uint64_t len)
|
||||
{
|
||||
gdb_do_syscall(complete, "write,%x,%lx,%lx",
|
||||
(uint32_t)gf->hostfd, (uint64_t)buf, (uint64_t)len);
|
||||
(uint32_t)gf->hostfd, (vaddr)buf, (uint64_t)len);
|
||||
}
|
||||
|
||||
static void gdb_lseek(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
@@ -178,15 +177,15 @@ static void gdb_isatty(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
static void gdb_fstat(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
GuestFD *gf, target_ulong addr)
|
||||
GuestFD *gf, vaddr addr)
|
||||
{
|
||||
gdb_do_syscall(complete, "fstat,%x,%lx",
|
||||
(uint32_t)gf->hostfd, (uint64_t)addr);
|
||||
(uint32_t)gf->hostfd, (vaddr)addr);
|
||||
}
|
||||
|
||||
static void gdb_stat(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong fname, target_ulong fname_len,
|
||||
target_ulong addr)
|
||||
vaddr fname, uint64_t fname_len,
|
||||
vaddr addr)
|
||||
{
|
||||
int len = validate_strlen(cs, fname, fname_len);
|
||||
if (len < 0) {
|
||||
@@ -195,11 +194,11 @@ static void gdb_stat(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
gdb_do_syscall(complete, "stat,%s,%lx",
|
||||
(uint64_t)fname, (uint32_t)len, (uint64_t)addr);
|
||||
(vaddr)fname, (uint32_t)len, (vaddr)addr);
|
||||
}
|
||||
|
||||
static void gdb_remove(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong fname, target_ulong fname_len)
|
||||
vaddr fname, uint64_t fname_len)
|
||||
{
|
||||
int len = validate_strlen(cs, fname, fname_len);
|
||||
if (len < 0) {
|
||||
@@ -207,12 +206,12 @@ static void gdb_remove(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
return;
|
||||
}
|
||||
|
||||
gdb_do_syscall(complete, "unlink,%s", (uint64_t)fname, (uint32_t)len);
|
||||
gdb_do_syscall(complete, "unlink,%s", (vaddr)fname, (uint32_t)len);
|
||||
}
|
||||
|
||||
static void gdb_rename(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong oname, target_ulong oname_len,
|
||||
target_ulong nname, target_ulong nname_len)
|
||||
vaddr oname, uint64_t oname_len,
|
||||
vaddr nname, uint64_t nname_len)
|
||||
{
|
||||
int olen, nlen;
|
||||
|
||||
@@ -228,12 +227,12 @@ static void gdb_rename(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
gdb_do_syscall(complete, "rename,%s,%s",
|
||||
(uint64_t)oname, (uint32_t)olen,
|
||||
(uint64_t)nname, (uint32_t)nlen);
|
||||
(vaddr)oname, (uint32_t)olen,
|
||||
(vaddr)nname, (uint32_t)nlen);
|
||||
}
|
||||
|
||||
static void gdb_system(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong cmd, target_ulong cmd_len)
|
||||
vaddr cmd, uint64_t cmd_len)
|
||||
{
|
||||
int len = validate_strlen(cs, cmd, cmd_len);
|
||||
if (len < 0) {
|
||||
@@ -241,14 +240,14 @@ static void gdb_system(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
return;
|
||||
}
|
||||
|
||||
gdb_do_syscall(complete, "system,%s", (uint64_t)cmd, (uint32_t)len);
|
||||
gdb_do_syscall(complete, "system,%s", (vaddr)cmd, (uint32_t)len);
|
||||
}
|
||||
|
||||
static void gdb_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong tv_addr, target_ulong tz_addr)
|
||||
vaddr tv_addr, vaddr tz_addr)
|
||||
{
|
||||
gdb_do_syscall(complete, "gettimeofday,%lx,%lx",
|
||||
(uint64_t)tv_addr, (uint64_t)tz_addr);
|
||||
(vaddr)tv_addr, (vaddr)tz_addr);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -256,7 +255,7 @@ static void gdb_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
*/
|
||||
|
||||
static void host_open(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong fname, target_ulong fname_len,
|
||||
vaddr fname, uint64_t fname_len,
|
||||
int gdb_flags, int mode)
|
||||
{
|
||||
CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
|
||||
@@ -316,7 +315,7 @@ static void host_close(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
static void host_read(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
GuestFD *gf, target_ulong buf, target_ulong len)
|
||||
GuestFD *gf, vaddr buf, uint64_t len)
|
||||
{
|
||||
CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
|
||||
void *ptr = lock_user(VERIFY_WRITE, buf, len, 0);
|
||||
@@ -337,7 +336,7 @@ static void host_read(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
static void host_write(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
GuestFD *gf, target_ulong buf, target_ulong len)
|
||||
GuestFD *gf, vaddr buf, uint64_t len)
|
||||
{
|
||||
CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
|
||||
void *ptr = lock_user(VERIFY_READ, buf, len, 1);
|
||||
@@ -395,7 +394,7 @@ static void host_flen(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
static void host_fstat(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
GuestFD *gf, target_ulong addr)
|
||||
GuestFD *gf, vaddr addr)
|
||||
{
|
||||
struct stat buf;
|
||||
int ret;
|
||||
@@ -410,8 +409,8 @@ static void host_fstat(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
static void host_stat(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong fname, target_ulong fname_len,
|
||||
target_ulong addr)
|
||||
vaddr fname, uint64_t fname_len,
|
||||
vaddr addr)
|
||||
{
|
||||
CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
|
||||
struct stat buf;
|
||||
@@ -440,7 +439,7 @@ static void host_stat(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
static void host_remove(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong fname, target_ulong fname_len)
|
||||
vaddr fname, uint64_t fname_len)
|
||||
{
|
||||
CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
|
||||
char *p;
|
||||
@@ -458,8 +457,8 @@ static void host_remove(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
static void host_rename(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong oname, target_ulong oname_len,
|
||||
target_ulong nname, target_ulong nname_len)
|
||||
vaddr oname, uint64_t oname_len,
|
||||
vaddr nname, uint64_t nname_len)
|
||||
{
|
||||
CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
|
||||
char *ostr, *nstr;
|
||||
@@ -484,7 +483,7 @@ static void host_rename(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
static void host_system(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong cmd, target_ulong cmd_len)
|
||||
vaddr cmd, uint64_t cmd_len)
|
||||
{
|
||||
CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
|
||||
char *p;
|
||||
@@ -502,7 +501,7 @@ static void host_system(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
static void host_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong tv_addr, target_ulong tz_addr)
|
||||
vaddr tv_addr, vaddr tz_addr)
|
||||
{
|
||||
CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
|
||||
struct gdb_timeval *p;
|
||||
@@ -547,10 +546,10 @@ static void host_poll_one(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
*/
|
||||
|
||||
static void staticfile_read(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
GuestFD *gf, target_ulong buf, target_ulong len)
|
||||
GuestFD *gf, vaddr buf, uint64_t len)
|
||||
{
|
||||
CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
|
||||
target_ulong rest = gf->staticfile.len - gf->staticfile.off;
|
||||
uint64_t rest = gf->staticfile.len - gf->staticfile.off;
|
||||
void *ptr;
|
||||
|
||||
if (len > rest) {
|
||||
@@ -605,7 +604,7 @@ static void staticfile_flen(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
*/
|
||||
|
||||
static void console_read(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
GuestFD *gf, target_ulong buf, target_ulong len)
|
||||
GuestFD *gf, vaddr buf, uint64_t len)
|
||||
{
|
||||
CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
|
||||
char *ptr;
|
||||
@@ -622,7 +621,7 @@ static void console_read(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
static void console_write(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
GuestFD *gf, target_ulong buf, target_ulong len)
|
||||
GuestFD *gf, vaddr buf, uint64_t len)
|
||||
{
|
||||
CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
|
||||
char *ptr = lock_user(VERIFY_READ, buf, len, 1);
|
||||
@@ -638,7 +637,7 @@ static void console_write(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
static void console_fstat(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
GuestFD *gf, target_ulong addr)
|
||||
GuestFD *gf, vaddr addr)
|
||||
{
|
||||
static const struct stat tty_buf = {
|
||||
.st_mode = 020666, /* S_IFCHR, ugo+rw */
|
||||
@@ -683,7 +682,7 @@ static void console_poll_one(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
*/
|
||||
|
||||
void semihost_sys_open(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong fname, target_ulong fname_len,
|
||||
vaddr fname, uint64_t fname_len,
|
||||
int gdb_flags, int mode)
|
||||
{
|
||||
if (use_gdb_syscalls()) {
|
||||
@@ -719,7 +718,7 @@ void semihost_sys_close(CPUState *cs, gdb_syscall_complete_cb complete, int fd)
|
||||
}
|
||||
|
||||
void semihost_sys_read_gf(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
GuestFD *gf, target_ulong buf, target_ulong len)
|
||||
GuestFD *gf, vaddr buf, uint64_t len)
|
||||
{
|
||||
/*
|
||||
* Bound length for 64-bit guests on 32-bit hosts, not overflowing ssize_t.
|
||||
@@ -748,7 +747,7 @@ void semihost_sys_read_gf(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
void semihost_sys_read(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
int fd, target_ulong buf, target_ulong len)
|
||||
int fd, vaddr buf, uint64_t len)
|
||||
{
|
||||
GuestFD *gf = get_guestfd(fd);
|
||||
|
||||
@@ -760,7 +759,7 @@ void semihost_sys_read(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
GuestFD *gf, target_ulong buf, target_ulong len)
|
||||
GuestFD *gf, vaddr buf, uint64_t len)
|
||||
{
|
||||
/*
|
||||
* Bound length for 64-bit guests on 32-bit hosts, not overflowing ssize_t.
|
||||
@@ -790,7 +789,7 @@ void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
void semihost_sys_write(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
int fd, target_ulong buf, target_ulong len)
|
||||
int fd, vaddr buf, uint64_t len)
|
||||
{
|
||||
GuestFD *gf = get_guestfd(fd);
|
||||
|
||||
@@ -856,7 +855,7 @@ void semihost_sys_isatty(CPUState *cs, gdb_syscall_complete_cb complete, int fd)
|
||||
|
||||
void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb,
|
||||
gdb_syscall_complete_cb flen_cb, int fd,
|
||||
target_ulong fstat_addr)
|
||||
vaddr fstat_addr)
|
||||
{
|
||||
GuestFD *gf = get_guestfd(fd);
|
||||
|
||||
@@ -881,7 +880,7 @@ void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb,
|
||||
}
|
||||
|
||||
void semihost_sys_fstat(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
int fd, target_ulong addr)
|
||||
int fd, vaddr addr)
|
||||
{
|
||||
GuestFD *gf = get_guestfd(fd);
|
||||
|
||||
@@ -906,8 +905,8 @@ void semihost_sys_fstat(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
void semihost_sys_stat(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong fname, target_ulong fname_len,
|
||||
target_ulong addr)
|
||||
vaddr fname, uint64_t fname_len,
|
||||
vaddr addr)
|
||||
{
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_stat(cs, complete, fname, fname_len, addr);
|
||||
@@ -917,7 +916,7 @@ void semihost_sys_stat(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
void semihost_sys_remove(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong fname, target_ulong fname_len)
|
||||
vaddr fname, uint64_t fname_len)
|
||||
{
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_remove(cs, complete, fname, fname_len);
|
||||
@@ -927,8 +926,8 @@ void semihost_sys_remove(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
void semihost_sys_rename(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong oname, target_ulong oname_len,
|
||||
target_ulong nname, target_ulong nname_len)
|
||||
vaddr oname, uint64_t oname_len,
|
||||
vaddr nname, uint64_t nname_len)
|
||||
{
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_rename(cs, complete, oname, oname_len, nname, nname_len);
|
||||
@@ -938,7 +937,7 @@ void semihost_sys_rename(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
void semihost_sys_system(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong cmd, target_ulong cmd_len)
|
||||
vaddr cmd, uint64_t cmd_len)
|
||||
{
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_system(cs, complete, cmd, cmd_len);
|
||||
@@ -948,7 +947,7 @@ void semihost_sys_system(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
}
|
||||
|
||||
void semihost_sys_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete,
|
||||
target_ulong tv_addr, target_ulong tz_addr)
|
||||
vaddr tv_addr, vaddr tz_addr)
|
||||
{
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_gettimeofday(cs, complete, tv_addr, tz_addr);
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef TARGET_ARM_COMMON_SEMI_TARGET_H
|
||||
#define TARGET_ARM_COMMON_SEMI_TARGET_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "semihosting/common-semi.h"
|
||||
#include "target/arm/cpu-qom.h"
|
||||
|
||||
static inline target_ulong common_semi_arg(CPUState *cs, int argno)
|
||||
uint64_t common_semi_arg(CPUState *cs, int argno)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
CPUARMState *env = &cpu->env;
|
||||
@@ -23,7 +23,7 @@ static inline target_ulong common_semi_arg(CPUState *cs, int argno)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void common_semi_set_ret(CPUState *cs, target_ulong ret)
|
||||
void common_semi_set_ret(CPUState *cs, uint64_t ret)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
CPUARMState *env = &cpu->env;
|
||||
@@ -34,27 +34,25 @@ static inline void common_semi_set_ret(CPUState *cs, target_ulong ret)
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool common_semi_sys_exit_extended(CPUState *cs, int nr)
|
||||
bool common_semi_sys_exit_is_extended(CPUState *cs)
|
||||
{
|
||||
return nr == TARGET_SYS_EXIT_EXTENDED || is_a64(cpu_env(cs));
|
||||
return is_a64(cpu_env(cs));
|
||||
}
|
||||
|
||||
static inline bool is_64bit_semihosting(CPUArchState *env)
|
||||
bool is_64bit_semihosting(CPUArchState *env)
|
||||
{
|
||||
return is_a64(env);
|
||||
}
|
||||
|
||||
static inline target_ulong common_semi_stack_bottom(CPUState *cs)
|
||||
uint64_t common_semi_stack_bottom(CPUState *cs)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
CPUARMState *env = &cpu->env;
|
||||
return is_a64(env) ? env->xregs[31] : env->regs[13];
|
||||
}
|
||||
|
||||
static inline bool common_semi_has_synccache(CPUArchState *env)
|
||||
bool common_semi_has_synccache(CPUArchState *env)
|
||||
{
|
||||
/* Ok for A64, invalid for A32/T32 */
|
||||
return is_a64(env);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -28,12 +28,16 @@ arm_user_ss.add(files(
|
||||
'vfp_fpscr.c',
|
||||
'el2-stubs.c',
|
||||
))
|
||||
arm_user_ss.add(when: 'CONFIG_ARM_COMPATIBLE_SEMIHOSTING',
|
||||
if_true: files('common-semi-target.c'))
|
||||
|
||||
arm_common_system_ss.add(files('cpu.c'))
|
||||
arm_common_system_ss.add(when: 'TARGET_AARCH64', if_false: files(
|
||||
'cpu32-stubs.c'))
|
||||
arm_common_system_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c'))
|
||||
arm_common_system_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c'))
|
||||
arm_common_system_ss.add(when: 'CONFIG_ARM_COMPATIBLE_SEMIHOSTING',
|
||||
if_true: files('common-semi-target.c'))
|
||||
arm_common_system_ss.add(files(
|
||||
'arch_dump.c',
|
||||
'arm-powerctl.c',
|
||||
|
||||
@@ -8,43 +8,42 @@
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef TARGET_RISCV_COMMON_SEMI_TARGET_H
|
||||
#define TARGET_RISCV_COMMON_SEMI_TARGET_H
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "semihosting/common-semi.h"
|
||||
|
||||
static inline target_ulong common_semi_arg(CPUState *cs, int argno)
|
||||
uint64_t common_semi_arg(CPUState *cs, int argno)
|
||||
{
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
return env->gpr[xA0 + argno];
|
||||
}
|
||||
|
||||
static inline void common_semi_set_ret(CPUState *cs, target_ulong ret)
|
||||
void common_semi_set_ret(CPUState *cs, uint64_t ret)
|
||||
{
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
env->gpr[xA0] = ret;
|
||||
}
|
||||
|
||||
static inline bool common_semi_sys_exit_extended(CPUState *cs, int nr)
|
||||
{
|
||||
return (nr == TARGET_SYS_EXIT_EXTENDED || sizeof(target_ulong) == 8);
|
||||
}
|
||||
|
||||
static inline bool is_64bit_semihosting(CPUArchState *env)
|
||||
bool is_64bit_semihosting(CPUArchState *env)
|
||||
{
|
||||
return riscv_cpu_mxl(env) != MXL_RV32;
|
||||
}
|
||||
|
||||
static inline target_ulong common_semi_stack_bottom(CPUState *cs)
|
||||
bool common_semi_sys_exit_is_extended(CPUState *cs)
|
||||
{
|
||||
return is_64bit_semihosting(cpu_env(cs));
|
||||
}
|
||||
|
||||
uint64_t common_semi_stack_bottom(CPUState *cs)
|
||||
{
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
return env->gpr[xSP];
|
||||
}
|
||||
|
||||
static inline bool common_semi_has_synccache(CPUArchState *env)
|
||||
bool common_semi_has_synccache(CPUArchState *env)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -8,6 +8,10 @@ gen = [
|
||||
|
||||
riscv_ss = ss.source_set()
|
||||
riscv_ss.add(gen)
|
||||
|
||||
riscv_ss.add(when: 'CONFIG_ARM_COMPATIBLE_SEMIHOSTING',
|
||||
if_true: files('common-semi-target.c'))
|
||||
|
||||
riscv_ss.add(files(
|
||||
'cpu.c',
|
||||
'cpu_helper.c',
|
||||
|
||||
Reference in New Issue
Block a user