maintainer updates: testing, fuzz, plugins, docs, gdbstub

- clean up gitlab artefact handling
  - ensure gitlab publishes artefacts with coverage data
  - reduce testing scope for coverage job
  - mention CI pipeline in developer docs
  - add ability to add plugin args to check-tcg
  - fix some memory leaks and UB in tests
  - suppress xcb leaks from fuzzing output
  - add a test-fuzz to mirror the CI run
  - allow lci-refresh to be run in $SRC
  - update lcitool to latest version
  - add qemu-minimal package set with gcc-native
  - convert riscv64-cross to lcitool
  - update sbsa-ref tests
  - don't include arm_casq_ptw emulation unless TCG
  - convert plugins to use g_memdup2
  - ensure plugins instrument SVE helper mem access
  - improve documentation of QOM/QDEV
  - make gdbstub send stop responses when it should
  - report user-mode pid in gdbstub
  - add support for info proc mappings in gdbstub
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEZoWumedRZ7yvyN81+9DbCVqeKkQFAmSiuH4ACgkQ+9DbCVqe
 KkRt0Qf+N0oD/VuEcRSxK1bWlLtf5nxQpPKKzkRItPc5jqJnLWa/gh21sfQgs5Uq
 BczAT+JfgTnMozbq0mjvQ+uAGI4MHzBs+UAn60+ZcXfk2inyk77XKBEoHOFuK1ry
 rgQ4+p21/hcZedDiDLnLSfbGfUU0KkM/pbAegOz7HO0EQDV0CSXqeAW3WAuM1lne
 +YmXkKwoFI1V8HvslzCT12GFiaUfmSSBtASqWcf67Ief97K24+rpkAVM7JChLm5X
 fC1MOFNuNYV+jO+9U3KIs15P1WH12oMcpNUY+KqQ5ZWovBg83yOLtKY1o3f6Z2Y+
 iQgFJr6F8ZVBdKNJtqVi8DkbiFfbsA==
 =Ho/h
 -----END PGP SIGNATURE-----

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

maintainer updates: testing, fuzz, plugins, docs, gdbstub

 - clean up gitlab artefact handling
 - ensure gitlab publishes artefacts with coverage data
 - reduce testing scope for coverage job
 - mention CI pipeline in developer docs
 - add ability to add plugin args to check-tcg
 - fix some memory leaks and UB in tests
 - suppress xcb leaks from fuzzing output
 - add a test-fuzz to mirror the CI run
 - allow lci-refresh to be run in $SRC
 - update lcitool to latest version
 - add qemu-minimal package set with gcc-native
 - convert riscv64-cross to lcitool
 - update sbsa-ref tests
 - don't include arm_casq_ptw emulation unless TCG
 - convert plugins to use g_memdup2
 - ensure plugins instrument SVE helper mem access
 - improve documentation of QOM/QDEV
 - make gdbstub send stop responses when it should
 - report user-mode pid in gdbstub
 - add support for info proc mappings in gdbstub

# -----BEGIN PGP SIGNATURE-----
#
# iQEzBAABCgAdFiEEZoWumedRZ7yvyN81+9DbCVqeKkQFAmSiuH4ACgkQ+9DbCVqe
# KkRt0Qf+N0oD/VuEcRSxK1bWlLtf5nxQpPKKzkRItPc5jqJnLWa/gh21sfQgs5Uq
# BczAT+JfgTnMozbq0mjvQ+uAGI4MHzBs+UAn60+ZcXfk2inyk77XKBEoHOFuK1ry
# rgQ4+p21/hcZedDiDLnLSfbGfUU0KkM/pbAegOz7HO0EQDV0CSXqeAW3WAuM1lne
# +YmXkKwoFI1V8HvslzCT12GFiaUfmSSBtASqWcf67Ief97K24+rpkAVM7JChLm5X
# fC1MOFNuNYV+jO+9U3KIs15P1WH12oMcpNUY+KqQ5ZWovBg83yOLtKY1o3f6Z2Y+
# iQgFJr6F8ZVBdKNJtqVi8DkbiFfbsA==
# =Ho/h
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon 03 Jul 2023 02:01:02 PM CEST
# gpg:                using RSA key 6685AE99E75167BCAFC8DF35FBD0DB095A9E2A44
# gpg: Good signature from "Alex Bennée (Master Work Key) <alex.bennee@linaro.org>" [undefined]
# 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-maintainer-ominbus-030723-1' of https://gitlab.com/stsquad/qemu: (38 commits)
  tests/tcg: Add a test for info proc mappings
  docs: Document security implications of debugging
  gdbstub: Add support for info proc mappings
  gdbstub: Report the actual qemu-user pid
  gdbstub: Expose gdb_get_process() and gdb_get_first_cpu_in_process()
  linux-user: Emulate /proc/self/smaps
  linux-user: Add "safe" parameter to do_guest_openat()
  linux-user: Expose do_guest_openat() and do_guest_readlink()
  gdbstub: clean-up vcont handling to avoid goto
  gdbstub: Permit reverse step/break to provide stop response
  gdbstub: lightly refactor connection to avoid snprintf
  docs/devel: introduce some key concepts for QOM development
  docs/devel: split qom-api reference into new file
  docs/devel/qom.rst: Correct code style
  include/hw/qdev-core: fixup kerneldoc annotations
  include/migration: mark vmstate_register() as a legacy function
  docs/devel: add some front matter to the devel index
  plugins: update lockstep to use g_memdup2
  plugins: fix memory leak while parsing options
  plugins: force slow path when plugins instrument memory ops
  ...

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2023-07-04 08:36:44 +02:00
commit 2a6ae69154
71 changed files with 1125 additions and 331 deletions

View File

@ -25,6 +25,7 @@
# rebuilding all the object files we skip in the artifacts
.native_build_artifact_template:
artifacts:
when: on_success
expire_in: 2 days
paths:
- build
@ -53,6 +54,7 @@
extends: .common_test_job_template
artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
when: always
expire_in: 7 days
paths:
- build/meson-logs/testlog.txt
@ -68,7 +70,7 @@
policy: pull-push
artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
when: on_failure
when: always
expire_in: 7 days
paths:
- build/tests/results/latest/results.xml

View File

@ -454,7 +454,7 @@ gcov:
IMAGE: ubuntu2204
CONFIGURE_ARGS: --enable-gcov
TARGETS: aarch64-softmmu ppc64-softmmu s390x-softmmu x86_64-softmmu
MAKE_CHECK_ARGS: check
MAKE_CHECK_ARGS: check-unit check-softfloat
after_script:
- cd build
- gcovr --xml-pretty --exclude-unreachable-branches --print-summary
@ -462,8 +462,12 @@ gcov:
coverage: /^\s*lines:\s*\d+.\d+\%/
artifacts:
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
when: always
expire_in: 2 days
paths:
- build/meson-logs/testlog.txt
reports:
junit: build/meson-logs/testlog.junit.xml
coverage_report:
coverage_format: cobertura
path: build/coverage.xml
@ -587,6 +591,7 @@ pages:
- make -C build install DESTDIR=$(pwd)/temp-install
- mv temp-install/usr/local/share/doc/qemu/* public/
artifacts:
when: on_success
paths:
- public
variables:

View File

@ -55,6 +55,7 @@
.cross_test_artifacts:
artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
when: always
expire_in: 7 days
paths:
- build/meson-logs/testlog.txt

View File

@ -169,6 +169,7 @@ cross-win32-system:
CROSS_SKIP_TARGETS: alpha-softmmu avr-softmmu hppa-softmmu m68k-softmmu
microblazeel-softmmu mips64el-softmmu nios2-softmmu
artifacts:
when: on_success
paths:
- build/qemu-setup*.exe
@ -184,6 +185,7 @@ cross-win64-system:
or1k-softmmu rx-softmmu sh4eb-softmmu sparc64-softmmu
tricore-softmmu xtensaeb-softmmu
artifacts:
when: on_success
paths:
- build/qemu-setup*.exe

View File

@ -63,6 +63,7 @@ build-opensbi:
stage: build
needs: ['docker-opensbi']
artifacts:
when: on_success
paths: # 'artifacts.zip' will contains the following files:
- pc-bios/opensbi-riscv32-generic-fw_dynamic.bin
- pc-bios/opensbi-riscv64-generic-fw_dynamic.bin

View File

@ -3106,6 +3106,7 @@ R: Qiuhao Li <Qiuhao.Li@outlook.com>
S: Maintained
F: tests/qtest/fuzz/
F: tests/qtest/fuzz-*test.c
F: tests/docker/test-fuzz
F: scripts/oss-fuzz/
F: hw/mem/sparse-mem.c
F: docs/devel/fuzzing.rst

View File

@ -28,7 +28,7 @@ quiet-command = $(quiet-@)$(call quiet-command-run,$1,$2,$3)
UNCHECKED_GOALS := TAGS gtags cscope ctags dist \
help check-help print-% \
docker docker-% vm-help vm-test vm-build-%
docker docker-% lcitool-refresh vm-help vm-test vm-build-%
all:
.PHONY: all clean distclean recurse-all dist msi FORCE

View File

@ -1513,13 +1513,14 @@ static int probe_access_internal(CPUArchState *env, vaddr addr,
int fault_size, MMUAccessType access_type,
int mmu_idx, bool nonfault,
void **phost, CPUTLBEntryFull **pfull,
uintptr_t retaddr)
uintptr_t retaddr, bool check_mem_cbs)
{
uintptr_t index = tlb_index(env, mmu_idx, addr);
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
uint64_t tlb_addr = tlb_read_idx(entry, access_type);
vaddr page_addr = addr & TARGET_PAGE_MASK;
int flags = TLB_FLAGS_MASK & ~TLB_FORCE_SLOW;
bool force_mmio = check_mem_cbs && cpu_plugin_mem_cbs_enabled(env_cpu(env));
CPUTLBEntryFull *full;
if (!tlb_hit_page(tlb_addr, page_addr)) {
@ -1553,7 +1554,9 @@ static int probe_access_internal(CPUArchState *env, vaddr addr,
flags |= full->slow_flags[access_type];
/* Fold all "mmio-like" bits into TLB_MMIO. This is not RAM. */
if (unlikely(flags & ~(TLB_WATCHPOINT | TLB_NOTDIRTY))) {
if (unlikely(flags & ~(TLB_WATCHPOINT | TLB_NOTDIRTY))
||
(access_type != MMU_INST_FETCH && force_mmio)) {
*phost = NULL;
return TLB_MMIO;
}
@ -1569,7 +1572,7 @@ int probe_access_full(CPUArchState *env, vaddr addr, int size,
uintptr_t retaddr)
{
int flags = probe_access_internal(env, addr, size, access_type, mmu_idx,
nonfault, phost, pfull, retaddr);
nonfault, phost, pfull, retaddr, true);
/* Handle clean RAM pages. */
if (unlikely(flags & TLB_NOTDIRTY)) {
@ -1580,6 +1583,29 @@ int probe_access_full(CPUArchState *env, vaddr addr, int size,
return flags;
}
int probe_access_full_mmu(CPUArchState *env, vaddr addr, int size,
MMUAccessType access_type, int mmu_idx,
void **phost, CPUTLBEntryFull **pfull)
{
void *discard_phost;
CPUTLBEntryFull *discard_tlb;
/* privately handle users that don't need full results */
phost = phost ? phost : &discard_phost;
pfull = pfull ? pfull : &discard_tlb;
int flags = probe_access_internal(env, addr, size, access_type, mmu_idx,
true, phost, pfull, 0, false);
/* Handle clean RAM pages. */
if (unlikely(flags & TLB_NOTDIRTY)) {
notdirty_write(env_cpu(env), addr, 1, *pfull, 0);
flags &= ~TLB_NOTDIRTY;
}
return flags;
}
int probe_access_flags(CPUArchState *env, vaddr addr, int size,
MMUAccessType access_type, int mmu_idx,
bool nonfault, void **phost, uintptr_t retaddr)
@ -1590,7 +1616,7 @@ int probe_access_flags(CPUArchState *env, vaddr addr, int size,
g_assert(-(addr | TARGET_PAGE_MASK) >= size);
flags = probe_access_internal(env, addr, size, access_type, mmu_idx,
nonfault, phost, &full, retaddr);
nonfault, phost, &full, retaddr, true);
/* Handle clean RAM pages. */
if (unlikely(flags & TLB_NOTDIRTY)) {
@ -1611,7 +1637,7 @@ void *probe_access(CPUArchState *env, vaddr addr, int size,
g_assert(-(addr | TARGET_PAGE_MASK) >= size);
flags = probe_access_internal(env, addr, size, access_type, mmu_idx,
false, &host, &full, retaddr);
false, &host, &full, retaddr, true);
/* Per the interface, size == 0 merely faults the access. */
if (size == 0) {
@ -1644,7 +1670,7 @@ void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr,
int flags;
flags = probe_access_internal(env, addr, 0, access_type,
mmu_idx, true, &host, &full, 0);
mmu_idx, true, &host, &full, 0, false);
/* No combination of flags are expected by the caller. */
return flags ? NULL : host;
@ -1667,7 +1693,8 @@ tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr,
void *p;
(void)probe_access_internal(env, addr, 1, MMU_INST_FETCH,
cpu_mmu_index(env, true), false, &p, &full, 0);
cpu_mmu_index(env, true), false,
&p, &full, 0, false);
if (p == NULL) {
return -1;
}

View File

@ -745,6 +745,10 @@ static int probe_access_internal(CPUArchState *env, vaddr addr,
if (guest_addr_valid_untagged(addr)) {
int page_flags = page_get_flags(addr);
if (page_flags & acc_flag) {
if ((acc_flag == PAGE_READ || acc_flag == PAGE_WRITE)
&& cpu_plugin_mem_cbs_enabled(env_cpu(env))) {
return TLB_MMIO;
}
return 0; /* success */
}
maperr = !(page_flags & PAGE_VALID);
@ -767,7 +771,7 @@ int probe_access_flags(CPUArchState *env, vaddr addr, int size,
g_assert(-(addr | TARGET_PAGE_MASK) >= size);
flags = probe_access_internal(env, addr, size, access_type, nonfault, ra);
*phost = flags ? NULL : g2h(env_cpu(env), addr);
*phost = (flags & TLB_INVALID_MASK) ? NULL : g2h(env_cpu(env), addr);
return flags;
}
@ -778,7 +782,7 @@ void *probe_access(CPUArchState *env, vaddr addr, int size,
g_assert(-(addr | TARGET_PAGE_MASK) >= size);
flags = probe_access_internal(env, addr, size, access_type, false, ra);
g_assert(flags == 0);
g_assert((flags & ~TLB_MMIO) == 0);
return size ? g2h(env_cpu(env), addr) : NULL;
}

View File

@ -772,7 +772,7 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
for (i = 0; i < argc; i++) {
char *opt = argv[i];
g_autofree char **tokens = g_strsplit(opt, "=", 2);
g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
if (g_strcmp0(tokens[0], "iblksize") == 0) {
l1_iblksize = STRTOLL(tokens[1]);

View File

@ -148,7 +148,7 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
int argc, char **argv)
{
for (int i = 0; i < argc; i++) {
g_autofree char **tokens = g_strsplit(argv[i], "=", 2);
g_auto(GStrv) tokens = g_strsplit(argv[i], "=", 2);
if (g_strcmp0(tokens[0], "filename") == 0) {
file_name = g_strdup(tokens[1]);
}

View File

@ -227,7 +227,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
for (int i = 0; i < argc; i++) {
char *opt = argv[i];
g_autofree char **tokens = g_strsplit(opt, "=", 2);
g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
if (g_strcmp0(tokens[0], "ifilter") == 0) {
parse_insn_match(tokens[1]);
} else if (g_strcmp0(tokens[0], "afilter") == 0) {

View File

@ -135,7 +135,7 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
{
for (int i = 0; i < argc; i++) {
char *opt = argv[i];
g_autofree char **tokens = g_strsplit(opt, "=", 2);
g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
if (g_strcmp0(tokens[0], "inline") == 0) {
if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) {
fprintf(stderr, "boolean argument parsing failed: %s\n", opt);

View File

@ -169,7 +169,7 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
for (i = 0; i < argc; i++) {
char *opt = argv[i];
g_autofree char **tokens = g_strsplit(opt, "=", -1);
g_auto(GStrv) tokens = g_strsplit(opt, "=", -1);
if (g_strcmp0(tokens[0], "sortby") == 0) {
if (g_strcmp0(tokens[1], "reads") == 0) {

View File

@ -333,7 +333,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
for (i = 0; i < argc; i++) {
char *p = argv[i];
g_autofree char **tokens = g_strsplit(p, "=", -1);
g_auto(GStrv) tokens = g_strsplit(p, "=", -1);
if (g_strcmp0(tokens[0], "inline") == 0) {
if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) {
fprintf(stderr, "boolean argument parsing failed: %s\n", p);

View File

@ -263,7 +263,7 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
for (i = 0; i < argc; i++) {
char *opt = argv[i];
g_autofree char **tokens = g_strsplit(opt, "=", 2);
g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
if (g_strcmp0(tokens[0], "track") == 0) {
if (g_strcmp0(tokens[1], "read") == 0) {

View File

@ -130,7 +130,7 @@ static void report_divergance(ExecState *us, ExecState *them)
}
}
divergence_log = g_slist_prepend(divergence_log,
g_memdup(&divrec, sizeof(divrec)));
g_memdup2(&divrec, sizeof(divrec)));
/* Output short log entry of going out of sync... */
if (verbose || divrec.distance == 1 || diverged) {
@ -323,7 +323,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
for (i = 0; i < argc; i++) {
char *p = argv[i];
g_autofree char **tokens = g_strsplit(p, "=", 2);
g_auto(GStrv) tokens = g_strsplit(p, "=", 2);
if (g_strcmp0(tokens[0], "verbose") == 0) {
if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &verbose)) {

View File

@ -11,5 +11,7 @@ generated from in-code annotations to function prototypes.
loads-stores
memory
modules
qom-api
qdev-api
ui
zoned-storage

View File

@ -1,3 +1,5 @@
.. _development_process:
QEMU Community Processes
------------------------

View File

@ -1,3 +1,5 @@
.. _tcg:
TCG Emulation
-------------

View File

@ -2,10 +2,30 @@
Developer Information
---------------------
This section of the manual documents various parts of the internals of QEMU.
You only need to read it if you are interested in reading or
This section of the manual documents various parts of the internals of
QEMU. You only need to read it if you are interested in reading or
modifying QEMU's source code.
QEMU is a large and mature project with a number of complex subsystems
that can be overwhelming to understand. The development documentation
is not comprehensive but hopefully presents enough to get you started.
If there are areas that are unclear please reach out either via the
IRC channel or mailing list and hopefully we can improve the
documentation for future developers.
All developers will want to familiarise themselves with
:ref:`development_process` and how the community interacts. Please pay
particular attention to the :ref:`coding-style` and
:ref:`submitting-a-patch` sections to avoid common pitfalls.
If you wish to implement a new hardware model you will want to read
through the :ref:`qom` documentation to understand how QEMU's object
model works.
Those wishing to enhance or add new CPU emulation capabilities will
want to read our :ref:`tcg` documentation, especially the overview of
the :ref:`tcg_internals`.
.. toctree::
:maxdepth: 1

7
docs/devel/qdev-api.rst Normal file
View File

@ -0,0 +1,7 @@
.. _qdev-api:
================================
QEMU Device (qdev) API Reference
================================
.. kernel-doc:: include/hw/qdev-core.h

9
docs/devel/qom-api.rst Normal file
View File

@ -0,0 +1,9 @@
.. _qom-api:
=====================================
QEMU Object Model (QOM) API Reference
=====================================
This is the complete API documentation for :ref:`qom`.
.. kernel-doc:: include/qom/object.h

View File

@ -13,6 +13,24 @@ features:
- System for dynamically registering types
- Support for single-inheritance of types
- Multiple inheritance of stateless interfaces
- Mapping internal members to publicly exposed properties
The root object class is TYPE_OBJECT which provides for the basic
object methods.
The QOM tree
============
The QOM tree is a composition tree which represents all of the objects
that make up a QEMU "machine". You can view this tree by running
``info qom-tree`` in the :ref:`QEMU monitor`. It will contain both
objects created by the machine itself as well those created due to
user configuration.
Creating a QOM class
====================
A simple minimal device implementation may look something like bellow:
.. code-block:: c
:caption: Creating a minimal type
@ -26,7 +44,7 @@ features:
typedef DeviceClass MyDeviceClass;
typedef struct MyDevice
{
DeviceState parent;
DeviceState parent_obj;
int reg0, reg1, reg2;
} MyDevice;
@ -48,6 +66,12 @@ In the above example, we create a simple type that is described by #TypeInfo.
#TypeInfo describes information about the type including what it inherits
from, the instance and class size, and constructor/destructor hooks.
The TYPE_DEVICE class is the parent class for all modern devices
implemented in QEMU and adds some specific methods to handle QEMU
device model. This includes managing the lifetime of devices from
creation through to when they become visible to the guest and
eventually unrealized.
Alternatively several static types could be registered using helper macro
DEFINE_TYPES()
@ -98,7 +122,7 @@ when the object is needed.
module_obj(TYPE_MY_DEVICE);
Class Initialization
====================
--------------------
Before an object is initialized, the class for the object must be
initialized. There is only one class object for all instance objects
@ -147,7 +171,7 @@ will also have a wrapper function to call it easily:
typedef struct MyDeviceClass
{
DeviceClass parent;
DeviceClass parent_class;
void (*frobnicate) (MyDevice *obj);
} MyDeviceClass;
@ -168,7 +192,7 @@ will also have a wrapper function to call it easily:
}
Interfaces
==========
----------
Interfaces allow a limited form of multiple inheritance. Instances are
similar to normal types except for the fact that are only defined by
@ -182,7 +206,7 @@ an argument to a method on its corresponding SomethingIfClass, or to
dynamically cast it to an object that implements the interface.
Methods
=======
-------
A *method* is a function within the namespace scope of
a class. It usually operates on the object instance by passing it as a
@ -275,8 +299,8 @@ Alternatively, object_class_by_name() can be used to obtain the class and
its non-overridden methods for a specific type. This would correspond to
``MyClass::method(...)`` in C++.
The first example of such a QOM method was #CPUClass.reset,
another example is #DeviceClass.realize.
One example of such methods is ``DeviceClass.reset``. More examples
can be found at :ref:`device-life-cycle`.
Standard type declaration and definition macros
===============================================
@ -382,9 +406,32 @@ OBJECT_DEFINE_ABSTRACT_TYPE() macro can be used instead:
OBJECT_DEFINE_ABSTRACT_TYPE(MyDevice, my_device,
MY_DEVICE, DEVICE)
.. _device-life-cycle:
Device Life-cycle
=================
As class initialisation cannot fail devices have an two additional
methods to handle the creation of dynamic devices. The ``realize``
function is called with ``Error **`` pointer which should be set if
the device cannot complete its setup. Otherwise on successful
completion of the ``realize`` method the device object is added to the
QOM tree and made visible to the guest.
The reverse function is ``unrealize`` and should be were clean-up
code lives to tidy up after the system is done with the device.
All devices can be instantiated by C code, however only some can
created dynamically via the command line or monitor.
Likewise only some can be unplugged after creation and need an
explicit ``unrealize`` implementation. This is determined by the
``user_creatable`` variable in the root ``DeviceClass`` structure.
Devices can only be unplugged if their ``parent_bus`` has a registered
``HotplugHandler``.
API Reference
-------------
=============
.. kernel-doc:: include/qom/object.h
See the :ref:`QOM API<qom-api>` and :ref:`QDEV API<qdev-api>`
documents for the complete API description.

View File

@ -1,3 +1,5 @@
.. _tcg_internals:
====================
Translator Internals
====================

View File

@ -485,6 +485,12 @@ first to contribute the mapping to the ``libvirt-ci`` project:
`CI <https://www.qemu.org/docs/master/devel/ci.html>`__ documentation
page on how to trigger gitlab CI pipelines on your change.
* Please also trigger gitlab container generation pipelines on your change
for as many OS distros as practical to make sure that there are no
obvious breakages when adding the new pre-requisite. Please see
`CI <https://www.qemu.org/docs/master/devel/ci.html>`__ documentation
page on how to trigger gitlab CI pipelines on your change.
For enterprise distros that default to old, end-of-life versions of the
Python runtime, QEMU uses a separate set of mappings that work with more
recent versions. These can be found in ``tests/lcitool/mappings.yml``.

View File

@ -214,3 +214,18 @@ The memory mode can be checked by sending the following command:
``maintenance packet Qqemu.PhyMemMode:0``
This will change it back to normal memory mode.
Security considerations
=======================
Connecting to the GDB socket allows running arbitrary code inside the guest;
in case of the TCG emulation, which is not considered a security boundary, this
also means running arbitrary code on the host. Additionally, when debugging
qemu-user, it allows directly downloading any file readable by QEMU from the
host.
The GDB socket is not protected by authentication, authorization or encryption.
It is therefore a responsibility of the user to make sure that only authorized
clients can connect to it, e.g., by using a unix socket with proper
permissions, or by opening a TCP socket only on interfaces that are not
reachable by potential attackers.

View File

@ -202,16 +202,19 @@ void gdb_memtox(GString *buf, const char *mem, int len)
static uint32_t gdb_get_cpu_pid(CPUState *cpu)
{
/* TODO: In user mode, we should use the task state PID */
#ifdef CONFIG_USER_ONLY
return getpid();
#else
if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) {
/* Return the default process' PID */
int index = gdbserver_state.process_num - 1;
return gdbserver_state.processes[index].pid;
}
return cpu->cluster_index + 1;
#endif
}
static GDBProcess *gdb_get_process(uint32_t pid)
GDBProcess *gdb_get_process(uint32_t pid)
{
int i;
@ -247,7 +250,7 @@ static CPUState *find_cpu(uint32_t thread_id)
return NULL;
}
static CPUState *get_first_cpu_in_process(GDBProcess *process)
CPUState *gdb_get_first_cpu_in_process(GDBProcess *process)
{
CPUState *cpu;
@ -325,7 +328,7 @@ static CPUState *gdb_get_cpu(uint32_t pid, uint32_t tid)
return NULL;
}
return get_first_cpu_in_process(process);
return gdb_get_first_cpu_in_process(process);
} else {
/* a specific thread */
cpu = find_cpu(tid);
@ -354,7 +357,7 @@ static const char *get_feature_xml(const char *p, const char **newp,
size_t len;
int i;
const char *name;
CPUState *cpu = get_first_cpu_in_process(process);
CPUState *cpu = gdb_get_first_cpu_in_process(process);
CPUClass *cc = CPU_GET_CLASS(cpu);
len = 0;
@ -490,7 +493,7 @@ void gdb_register_coprocessor(CPUState *cpu,
static void gdb_process_breakpoint_remove_all(GDBProcess *p)
{
CPUState *cpu = get_first_cpu_in_process(p);
CPUState *cpu = gdb_get_first_cpu_in_process(p);
while (cpu) {
gdb_breakpoint_remove_all(cpu);
@ -573,7 +576,6 @@ static int gdb_handle_vcont(const char *p)
{
int res, signal = 0;
char cur_action;
char *newstates;
unsigned long tmp;
uint32_t pid, tid;
GDBProcess *process;
@ -581,7 +583,7 @@ static int gdb_handle_vcont(const char *p)
GDBThreadIdKind kind;
unsigned int max_cpus = gdb_get_max_cpus();
/* uninitialised CPUs stay 0 */
newstates = g_new0(char, max_cpus);
g_autofree char *newstates = g_new0(char, max_cpus);
/* mark valid CPUs with 1 */
CPU_FOREACH(cpu) {
@ -597,8 +599,7 @@ static int gdb_handle_vcont(const char *p)
res = 0;
while (*p) {
if (*p++ != ';') {
res = -ENOTSUP;
goto out;
return -ENOTSUP;
}
cur_action = *p++;
@ -606,13 +607,12 @@ static int gdb_handle_vcont(const char *p)
cur_action = qemu_tolower(cur_action);
res = qemu_strtoul(p, &p, 16, &tmp);
if (res) {
goto out;
return res;
}
signal = gdb_signal_to_target(tmp);
} else if (cur_action != 'c' && cur_action != 's') {
/* unknown/invalid/unsupported command */
res = -ENOTSUP;
goto out;
return -ENOTSUP;
}
if (*p == '\0' || *p == ';') {
@ -625,14 +625,12 @@ static int gdb_handle_vcont(const char *p)
} else if (*p++ == ':') {
kind = read_thread_id(p, &p, &pid, &tid);
} else {
res = -ENOTSUP;
goto out;
return -ENOTSUP;
}
switch (kind) {
case GDB_READ_THREAD_ERR:
res = -EINVAL;
goto out;
return -EINVAL;
case GDB_ALL_PROCESSES:
cpu = gdb_first_attached_cpu();
@ -649,11 +647,10 @@ static int gdb_handle_vcont(const char *p)
process = gdb_get_process(pid);
if (!process->attached) {
res = -EINVAL;
goto out;
return -EINVAL;
}
cpu = get_first_cpu_in_process(process);
cpu = gdb_get_first_cpu_in_process(process);
while (cpu) {
if (newstates[cpu->cpu_index] == 1) {
newstates[cpu->cpu_index] = cur_action;
@ -668,8 +665,7 @@ static int gdb_handle_vcont(const char *p)
/* invalid CPU/thread specified */
if (!cpu) {
res = -EINVAL;
goto out;
return -EINVAL;
}
/* only use if no previous match occourred */
@ -679,12 +675,9 @@ static int gdb_handle_vcont(const char *p)
break;
}
}
gdbserver_state.signal = signal;
gdb_continue_partial(newstates);
out:
g_free(newstates);
return res;
}
@ -1280,7 +1273,7 @@ static void handle_v_attach(GArray *params, void *user_ctx)
goto cleanup;
}
cpu = get_first_cpu_in_process(process);
cpu = gdb_get_first_cpu_in_process(process);
if (!cpu) {
goto cleanup;
}
@ -1334,6 +1327,36 @@ static const GdbCmdParseEntry gdb_v_commands_table[] = {
.cmd = "Kill;",
.cmd_startswith = 1
},
#ifdef CONFIG_USER_ONLY
/*
* Host I/O Packets. See [1] for details.
* [1] https://sourceware.org/gdb/onlinedocs/gdb/Host-I_002fO-Packets.html
*/
{
.handler = gdb_handle_v_file_open,
.cmd = "File:open:",
.cmd_startswith = 1,
.schema = "s,L,L0"
},
{
.handler = gdb_handle_v_file_close,
.cmd = "File:close:",
.cmd_startswith = 1,
.schema = "l0"
},
{
.handler = gdb_handle_v_file_pread,
.cmd = "File:pread:",
.cmd_startswith = 1,
.schema = "l,L,L0"
},
{
.handler = gdb_handle_v_file_readlink,
.cmd = "File:readlink:",
.cmd_startswith = 1,
.schema = "s0"
},
#endif
};
static void handle_v_commands(GArray *params, void *user_ctx)
@ -1403,7 +1426,7 @@ static void handle_query_curr_tid(GArray *params, void *user_ctx)
* first thread).
*/
process = gdb_get_cpu_process(gdbserver_state.g_cpu);
cpu = get_first_cpu_in_process(process);
cpu = gdb_get_first_cpu_in_process(process);
g_string_assign(gdbserver_state.str_buf, "QC");
gdb_append_thread_id(cpu, gdbserver_state.str_buf);
gdb_put_strbuf();
@ -1479,11 +1502,14 @@ static void handle_query_supported(GArray *params, void *user_ctx)
";ReverseStep+;ReverseContinue+");
}
#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX)
#if defined(CONFIG_USER_ONLY)
#if defined(CONFIG_LINUX)
if (gdbserver_state.c_cpu->opaque) {
g_string_append(gdbserver_state.str_buf, ";qXfer:auxv:read+");
}
#endif
g_string_append(gdbserver_state.str_buf, ";qXfer:exec-file:read+");
#endif
if (params->len &&
strstr(get_param(params, 0)->data, "multiprocess+")) {
@ -1622,13 +1648,21 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = {
.cmd_startswith = 1,
.schema = "s:l,l0"
},
#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX)
#if defined(CONFIG_USER_ONLY)
#if defined(CONFIG_LINUX)
{
.handler = gdb_handle_query_xfer_auxv,
.cmd = "Xfer:auxv:read::",
.cmd_startswith = 1,
.schema = "l,l0"
},
#endif
{
.handler = gdb_handle_query_xfer_exec_file,
.cmd = "Xfer:exec-file:read:",
.cmd_startswith = 1,
.schema = "l:l,l0"
},
#endif
{
.handler = gdb_handle_query_attached,
@ -1814,6 +1848,7 @@ static int gdb_handle_packet(const char *line_buf)
.handler = handle_backward,
.cmd = "b",
.cmd_startswith = 1,
.allow_stop_reply = true,
.schema = "o0"
};
cmd_parser = &backward_cmd_desc;
@ -2146,19 +2181,25 @@ void gdb_read_byte(uint8_t ch)
void gdb_create_default_process(GDBState *s)
{
GDBProcess *process;
int max_pid = 0;
int pid;
#ifdef CONFIG_USER_ONLY
assert(gdbserver_state.process_num == 0);
pid = getpid();
#else
if (gdbserver_state.process_num) {
max_pid = s->processes[s->process_num - 1].pid;
pid = s->processes[s->process_num - 1].pid;
} else {
pid = 0;
}
/* We need an available PID slot for this process */
assert(pid < UINT32_MAX);
pid++;
#endif
s->processes = g_renew(GDBProcess, s->processes, ++s->process_num);
process = &s->processes[s->process_num - 1];
/* We need an available PID slot for this process */
assert(max_pid < UINT32_MAX);
process->pid = max_pid + 1;
process->pid = pid;
process->attached = false;
process->target_xml[0] = '\0';
}

View File

@ -129,6 +129,8 @@ void gdb_read_byte(uint8_t ch);
*/
bool gdb_got_immediate_ack(void);
/* utility helpers */
GDBProcess *gdb_get_process(uint32_t pid);
CPUState *gdb_get_first_cpu_in_process(GDBProcess *process);
CPUState *gdb_first_attached_cpu(void);
void gdb_append_thread_id(CPUState *cpu, GString *buf);
int gdb_get_cpu_index(CPUState *cpu);
@ -187,6 +189,11 @@ typedef union GdbCmdVariant {
void gdb_handle_query_rcmd(GArray *params, void *user_ctx); /* softmmu */
void gdb_handle_query_offsets(GArray *params, void *user_ctx); /* user */
void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx); /*user */
void gdb_handle_v_file_open(GArray *params, void *user_ctx); /* user */
void gdb_handle_v_file_close(GArray *params, void *user_ctx); /* user */
void gdb_handle_v_file_pread(GArray *params, void *user_ctx); /* user */
void gdb_handle_v_file_readlink(GArray *params, void *user_ctx); /* user */
void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx); /* user */
void gdb_handle_query_attached(GArray *params, void *user_ctx); /* both */

View File

@ -332,11 +332,9 @@ static void create_processes(GDBState *s)
int gdbserver_start(const char *device)
{
trace_gdbstub_op_start(device);
char gdbstub_device_name[128];
Chardev *chr = NULL;
Chardev *mon_chr;
g_autoptr(GString) cs = g_string_new(device);
if (!first_cpu) {
error_report("gdbstub: meaningless to attach gdb to a "
@ -350,15 +348,16 @@ int gdbserver_start(const char *device)
return -1;
}
if (!device) {
if (cs->len == 0) {
return -1;
}
if (strcmp(device, "none") != 0) {
if (strstart(device, "tcp:", NULL)) {
trace_gdbstub_op_start(cs->str);
if (g_strcmp0(cs->str, "none") != 0) {
if (g_str_has_prefix(cs->str, "tcp:")) {
/* enforce required TCP attributes */
snprintf(gdbstub_device_name, sizeof(gdbstub_device_name),
"%s,wait=off,nodelay=on,server=on", device);
device = gdbstub_device_name;
g_string_append_printf(cs, ",wait=off,nodelay=on,server=on");
}
#ifndef _WIN32
else if (strcmp(device, "stdio") == 0) {
@ -373,7 +372,7 @@ int gdbserver_start(const char *device)
* FIXME: it's a bit weird to allow using a mux chardev here
* and implicitly setup a monitor. We may want to break this.
*/
chr = qemu_chr_new_noreplay("gdb", device, true, NULL);
chr = qemu_chr_new_noreplay("gdb", cs->str, true, NULL);
if (!chr) {
return -1;
}

View File

@ -11,6 +11,10 @@
#include "exec/gdbstub.h"
#include "qemu.h"
#include "internals.h"
#ifdef CONFIG_LINUX
#include "linux-user/loader.h"
#include "linux-user/qemu.h"
#endif
/*
* Map target signal numbers to GDB protocol signal numbers and vice
@ -281,3 +285,136 @@ void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx)
gdbserver_state.str_buf->len, true);
}
#endif
static const char *get_filename_param(GArray *params, int i)
{
const char *hex_filename = get_param(params, i)->data;
gdb_hextomem(gdbserver_state.mem_buf, hex_filename,
strlen(hex_filename) / 2);
g_byte_array_append(gdbserver_state.mem_buf, (const guint8 *)"", 1);
return (const char *)gdbserver_state.mem_buf->data;
}
static void hostio_reply_with_data(const void *buf, size_t n)
{
g_string_printf(gdbserver_state.str_buf, "F%zx;", n);
gdb_memtox(gdbserver_state.str_buf, buf, n);
gdb_put_packet_binary(gdbserver_state.str_buf->str,
gdbserver_state.str_buf->len, true);
}
void gdb_handle_v_file_open(GArray *params, void *user_ctx)
{
const char *filename = get_filename_param(params, 0);
uint64_t flags = get_param(params, 1)->val_ull;
uint64_t mode = get_param(params, 2)->val_ull;
#ifdef CONFIG_LINUX
int fd = do_guest_openat(gdbserver_state.g_cpu->env_ptr, 0, filename,
flags, mode, false);
#else
int fd = open(filename, flags, mode);
#endif
if (fd < 0) {
g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno);
} else {
g_string_printf(gdbserver_state.str_buf, "F%d", fd);
}
gdb_put_strbuf();
}
void gdb_handle_v_file_close(GArray *params, void *user_ctx)
{
int fd = get_param(params, 0)->val_ul;
if (close(fd) == -1) {
g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno);
gdb_put_strbuf();
return;
}
gdb_put_packet("F00");
}
void gdb_handle_v_file_pread(GArray *params, void *user_ctx)
{
int fd = get_param(params, 0)->val_ul;
size_t count = get_param(params, 1)->val_ull;
off_t offset = get_param(params, 2)->val_ull;
size_t bufsiz = MIN(count, BUFSIZ);
g_autofree char *buf = g_try_malloc(bufsiz);
if (buf == NULL) {
gdb_put_packet("E12");
return;
}
ssize_t n = pread(fd, buf, bufsiz, offset);
if (n < 0) {
g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno);
gdb_put_strbuf();
return;
}
hostio_reply_with_data(buf, n);
}
void gdb_handle_v_file_readlink(GArray *params, void *user_ctx)
{
const char *filename = get_filename_param(params, 0);
g_autofree char *buf = g_try_malloc(BUFSIZ);
if (buf == NULL) {
gdb_put_packet("E12");
return;
}
#ifdef CONFIG_LINUX
ssize_t n = do_guest_readlink(filename, buf, BUFSIZ);
#else
ssize_t n = readlink(filename, buf, BUFSIZ);
#endif
if (n < 0) {
g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno);
gdb_put_strbuf();
return;
}
hostio_reply_with_data(buf, n);
}
void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx)
{
uint32_t pid = get_param(params, 0)->val_ul;
uint32_t offset = get_param(params, 1)->val_ul;
uint32_t length = get_param(params, 2)->val_ul;
GDBProcess *process = gdb_get_process(pid);
if (!process) {
gdb_put_packet("E00");
return;
}
CPUState *cpu = gdb_get_first_cpu_in_process(process);
if (!cpu) {
gdb_put_packet("E00");
return;
}
TaskState *ts = cpu->opaque;
if (!ts || !ts->bprm || !ts->bprm->filename) {
gdb_put_packet("E00");
return;
}
size_t total_length = strlen(ts->bprm->filename);
if (offset > total_length) {
gdb_put_packet("E00");
return;
}
if (offset + length > total_length) {
length = total_length - offset;
}
g_string_printf(gdbserver_state.str_buf, "l%.*s", length,
ts->bprm->filename + offset);
gdb_put_strbuf();
}

View File

@ -301,7 +301,7 @@ CPUArchState *cpu_copy(CPUArchState *env);
* be signaled by probe_access_flags().
*/
#define TLB_INVALID_MASK (1 << (TARGET_PAGE_BITS_MIN - 1))
#define TLB_MMIO 0
#define TLB_MMIO (1 << (TARGET_PAGE_BITS_MIN - 2))
#define TLB_WATCHPOINT 0
#else

View File

@ -464,6 +464,29 @@ int probe_access_full(CPUArchState *env, vaddr addr, int size,
MMUAccessType access_type, int mmu_idx,
bool nonfault, void **phost,
CPUTLBEntryFull **pfull, uintptr_t retaddr);
/**
* probe_access_mmu() - Like probe_access_full except cannot fault and
* doesn't trigger instrumentation.
*
* @env: CPUArchState
* @vaddr: virtual address to probe
* @size: size of the probe
* @access_type: read, write or execute permission
* @mmu_idx: softmmu index
* @phost: ptr to return value host address or NULL
* @pfull: ptr to return value CPUTLBEntryFull structure or NULL
*
* The CPUTLBEntryFull structure returned via @pfull is transient
* and must be consumed or copied immediately, before any further
* access or changes to TLB @mmu_idx.
*
* Returns: TLB flags as per probe_access_flags()
*/
int probe_access_full_mmu(CPUArchState *env, vaddr addr, int size,
MMUAccessType access_type, int mmu_idx,
void **phost, CPUTLBEntryFull **pfull);
#endif
/* Hide the qatomic_read to make code a little easier on the eyes */

View File

@ -976,6 +976,23 @@ void cpu_watchpoint_remove_by_ref(CPUState *cpu, CPUWatchpoint *watchpoint);
void cpu_watchpoint_remove_all(CPUState *cpu, int mask);
#endif
/**
* cpu_plugin_mem_cbs_enabled() - are plugin memory callbacks enabled?
* @cs: CPUState pointer
*
* The memory callbacks are installed if a plugin has instrumented an
* instruction for memory. This can be useful to know if you want to
* force a slow path for a series of memory accesses.
*/
static inline bool cpu_plugin_mem_cbs_enabled(const CPUState *cpu)
{
#ifdef CONFIG_PLUGIN
return !!cpu->plugin_mem_cbs;
#else
return false;
#endif
}
/**
* cpu_get_address_space:
* @cpu: CPU to get address space from

View File

@ -10,6 +10,65 @@
#include "hw/hotplug.h"
#include "hw/resettable.h"
/**
* DOC: The QEMU Device API
*
* All modern devices should represented as a derived QOM class of
* TYPE_DEVICE. The device API introduces the additional methods of
* @realize and @unrealize to represent additional stages in a device
* objects life cycle.
*
* Realization
* -----------
*
* Devices are constructed in two stages:
*
* 1) object instantiation via object_initialize() and
* 2) device realization via the #DeviceState.realized property
*
* The former may not fail (and must not abort or exit, since it is called
* during device introspection already), and the latter may return error
* information to the caller and must be re-entrant.
* Trivial field initializations should go into #TypeInfo.instance_init.
* Operations depending on @props static properties should go into @realize.
* After successful realization, setting static properties will fail.
*
* As an interim step, the #DeviceState.realized property can also be
* set with qdev_realize(). In the future, devices will propagate this
* state change to their children and along busses they expose. The
* point in time will be deferred to machine creation, so that values
* set in @realize will not be introspectable beforehand. Therefore
* devices must not create children during @realize; they should
* initialize them via object_initialize() in their own
* #TypeInfo.instance_init and forward the realization events
* appropriately.
*
* Any type may override the @realize and/or @unrealize callbacks but needs
* to call the parent type's implementation if keeping their functionality
* is desired. Refer to QOM documentation for further discussion and examples.
*
* .. note::
* Since TYPE_DEVICE doesn't implement @realize and @unrealize, types
* derived directly from it need not call their parent's @realize and
* @unrealize. For other types consult the documentation and
* implementation of the respective parent types.
*
* Hiding a device
* ---------------
*
* To hide a device, a DeviceListener function hide_device() needs to
* be registered. It can be used to defer adding a device and
* therefore hide it from the guest. The handler registering to this
* DeviceListener can save the QOpts passed to it for re-using it
* later. It must return if it wants the device to be hidden or
* visible. When the handler function decides the device shall be
* visible it will be added with qdev_device_add() and realized as any
* other device. Otherwise qdev_device_add() will return early without
* adding the device. The guest will not see a "hidden" device until
* it was marked visible and qdev_device_add called again.
*
*/
enum {
DEV_NVECTORS_UNSPECIFIED = -1,
};
@ -38,7 +97,7 @@ typedef void (*BusRealize)(BusState *bus, Error **errp);
typedef void (*BusUnrealize)(BusState *bus);
/**
* DeviceClass:
* struct DeviceClass - The base class for all devices.
* @props: Properties accessing state fields.
* @realize: Callback function invoked when the #DeviceState:realized
* property is changed to %true.
@ -47,72 +106,37 @@ typedef void (*BusUnrealize)(BusState *bus);
* @hotpluggable: indicates if #DeviceClass is hotpluggable, available
* as readonly "hotpluggable" property of #DeviceState instance
*
* # Realization #
* Devices are constructed in two stages,
* 1) object instantiation via object_initialize() and
* 2) device realization via #DeviceState:realized property.
* The former may not fail (and must not abort or exit, since it is called
* during device introspection already), and the latter may return error
* information to the caller and must be re-entrant.
* Trivial field initializations should go into #TypeInfo.instance_init.
* Operations depending on @props static properties should go into @realize.
* After successful realization, setting static properties will fail.
*
* As an interim step, the #DeviceState:realized property can also be
* set with qdev_realize().
* In the future, devices will propagate this state change to their children
* and along busses they expose.
* The point in time will be deferred to machine creation, so that values
* set in @realize will not be introspectable beforehand. Therefore devices
* must not create children during @realize; they should initialize them via
* object_initialize() in their own #TypeInfo.instance_init and forward the
* realization events appropriately.
*
* Any type may override the @realize and/or @unrealize callbacks but needs
* to call the parent type's implementation if keeping their functionality
* is desired. Refer to QOM documentation for further discussion and examples.
*
* <note>
* <para>
* Since TYPE_DEVICE doesn't implement @realize and @unrealize, types
* derived directly from it need not call their parent's @realize and
* @unrealize.
* For other types consult the documentation and implementation of the
* respective parent types.
* </para>
* </note>
*
* # Hiding a device #
* To hide a device, a DeviceListener function hide_device() needs to
* be registered.
* It can be used to defer adding a device and therefore hide it from
* the guest. The handler registering to this DeviceListener can save
* the QOpts passed to it for re-using it later. It must return if it
* wants the device to be hidden or visible. When the handler function
* decides the device shall be visible it will be added with
* qdev_device_add() and realized as any other device. Otherwise
* qdev_device_add() will return early without adding the device. The
* guest will not see a "hidden" device until it was marked visible
* and qdev_device_add called again.
*
*/
struct DeviceClass {
/*< private >*/
/* private: */
ObjectClass parent_class;
/*< public >*/
/* public: */
/**
* @categories: device categories device belongs to
*/
DECLARE_BITMAP(categories, DEVICE_CATEGORY_MAX);
/**
* @fw_name: name used to identify device to firmware interfaces
*/
const char *fw_name;
/**
* @desc: human readable description of device
*/
const char *desc;
/*
* The underscore at the end ensures a compile-time error if someone
* assigns to dc->props instead of using device_class_set_props.
/**
* @props_: properties associated with device, should only be
* assigned by using device_class_set_props(). The underscore
* ensures a compile-time error if someone attempts to assign
* dc->props directly.
*/
Property *props_;
/*
* Can this device be instantiated with -device / device_add?
/**
* @user_creatable: Can user instantiate with -device / device_add?
*
* All devices should support instantiation with device_add, and
* this flag should not exist. But we're not there, yet. Some
* devices fail to instantiate with cryptic error messages.
@ -120,25 +144,35 @@ struct DeviceClass {
* behavior would be cruel; clearing this flag will protect them.
* It should never be cleared without a comment explaining why it
* is cleared.
*
* TODO remove once we're there
*/
bool user_creatable;
bool hotpluggable;
/* callbacks */
/*
* Reset method here is deprecated and replaced by methods in the
* resettable class interface to implement a multi-phase reset.
/**
* @reset: deprecated device reset method pointer
*
* Modern code should use the ResettableClass interface to
* implement a multi-phase reset.
*
* TODO: remove once every reset callback is unused
*/
DeviceReset reset;
DeviceRealize realize;
DeviceUnrealize unrealize;
/* device state */
/**
* @vmsd: device state serialisation description for
* migration/save/restore
*/
const VMStateDescription *vmsd;
/* Private to qdev / bus. */
/**
* @bus_type: bus type
* private: to qdev / bus.
*/
const char *bus_type;
};
@ -167,37 +201,96 @@ typedef struct {
bool engaged_in_io;
} MemReentrancyGuard;
typedef QLIST_HEAD(, NamedGPIOList) NamedGPIOListHead;
typedef QLIST_HEAD(, NamedClockList) NamedClockListHead;
typedef QLIST_HEAD(, BusState) BusStateHead;
/**
* DeviceState:
* @reset: ResettableState for the device; handled by Resettable interface.
* struct DeviceState - common device state, accessed with qdev helpers
*
* This structure should not be accessed directly. We declare it here
* so that it can be embedded in individual device state structures.
*/
struct DeviceState {
/*< private >*/
/* private: */
Object parent_obj;
/*< public >*/
/* public: */
/**
* @id: global device id
*/
char *id;
/**
* @canonical_path: canonical path of realized device in the QOM tree
*/
char *canonical_path;
/**
* @realized: has device been realized?
*/
bool realized;
/**
* @pending_deleted_event: track pending deletion events during unplug
*/
bool pending_deleted_event;
/**
* @pending_deleted_expires_ms: optional timeout for deletion events
*/
int64_t pending_deleted_expires_ms;
/**
* @opts: QDict of options for the device
*/
QDict *opts;
/**
* @hotplugged: was device added after PHASE_MACHINE_READY?
*/
int hotplugged;
/**
* @allow_unplug_during_migration: can device be unplugged during migration
*/
bool allow_unplug_during_migration;
/**
* @parent_bus: bus this device belongs to
*/
BusState *parent_bus;
QLIST_HEAD(, NamedGPIOList) gpios;
QLIST_HEAD(, NamedClockList) clocks;
QLIST_HEAD(, BusState) child_bus;
/**
* @gpios: QLIST of named GPIOs the device provides.
*/
NamedGPIOListHead gpios;
/**
* @clocks: QLIST of named clocks the device provides.
*/
NamedClockListHead clocks;
/**
* @child_bus: QLIST of child buses
*/
BusStateHead child_bus;
/**
* @num_child_bus: number of @child_bus entries
*/
int num_child_bus;
/**
* @instance_id_alias: device alias for handling legacy migration setups
*/
int instance_id_alias;
/**
* @alias_required_for_version: indicates @instance_id_alias is
* needed for migration
*/
int alias_required_for_version;
/**
* @reset: ResettableState for the device; handled by Resettable interface.
*/
ResettableState reset;
/**
* @unplug_blockers: list of reasons to block unplugging of device
*/
GSList *unplug_blockers;
/* Is the device currently in mmio/pio/dma? Used to prevent re-entrancy */
/**
* @mem_reentrancy_guard: Is the device currently in mmio/pio/dma?
*
* Used to prevent re-entrancy confusing things.
*/
MemReentrancyGuard mem_reentrancy_guard;
};
@ -264,13 +357,24 @@ typedef struct BusChild {
#define QDEV_HOTPLUG_HANDLER_PROPERTY "hotplug-handler"
typedef QTAILQ_HEAD(, BusChild) BusChildHead;
typedef QLIST_ENTRY(BusState) BusStateEntry;
/**
* BusState:
* struct BusState:
* @obj: parent object
* @parent: parent Device
* @name: name of bus
* @hotplug_handler: link to a hotplug handler associated with bus.
* @reset: ResettableState for the bus; handled by Resettable interface.
* @max_index: max number of child buses
* @realized: is the bus itself realized?
* @full: is the bus full?
* @num_children: current number of child buses
*/
struct BusState {
/* private: */
Object obj;
/* public: */
DeviceState *parent;
char *name;
HotplugHandler *hotplug_handler;
@ -279,18 +383,24 @@ struct BusState {
bool full;
int num_children;
/*
* children is a RCU QTAILQ, thus readers must use RCU to access it,
* and writers must hold the big qemu lock
/**
* @children: an RCU protected QTAILQ, thus readers must use RCU
* to access it, and writers must hold the big qemu lock
*/
BusChildHead children;
/**
* @sibling: next bus
*/
BusStateEntry sibling;
/**
* @reset: ResettableState for the bus; handled by Resettable interface.
*/
QTAILQ_HEAD(, BusChild) children;
QLIST_ENTRY(BusState) sibling;
ResettableState reset;
};
/**
* GlobalProperty:
* typedef GlobalProperty - a global property type
*
* @used: Set to true if property was used when initializing a device.
* @optional: If set to true, GlobalProperty will be skipped without errors
* if the property doesn't exist.
@ -324,7 +434,8 @@ compat_props_add(GPtrArray *arr,
* This only allocates the memory and initializes the device state
* structure, ready for the caller to set properties if they wish.
* The device still needs to be realized.
* The returned object has a reference count of 1.
*
* Return: a derived DeviceState object with a reference count of 1.
*/
DeviceState *qdev_new(const char *name);
@ -334,16 +445,18 @@ DeviceState *qdev_new(const char *name);
*
* This is like qdev_new(), except it returns %NULL when type @name
* does not exist, rather than asserting.
*
* Return: a derived DeviceState object with a reference count of 1 or
* NULL if type @name does not exist.
*/
DeviceState *qdev_try_new(const char *name);
/**
* qdev_is_realized:
* qdev_is_realized() - check if device is realized
* @dev: The device to check.
*
* May be called outside big qemu lock.
*
* Returns: %true% if the device has been fully constructed, %false% otherwise.
* Context: May be called outside big qemu lock.
* Return: true if the device has been fully constructed, false otherwise.
*/
static inline bool qdev_is_realized(DeviceState *dev)
{
@ -361,11 +474,11 @@ static inline bool qdev_is_realized(DeviceState *dev)
* @dev must not be plugged into a bus already.
* If @bus, plug @dev into @bus. This takes a reference to @dev.
* If @dev has no QOM parent, make one up, taking another reference.
* On success, return true.
* On failure, store an error through @errp and return false.
*
* If you created @dev using qdev_new(), you probably want to use
* qdev_realize_and_unref() instead.
*
* Return: true on success, else false setting @errp with error
*/
bool qdev_realize(DeviceState *dev, BusState *bus, Error **errp);
@ -392,6 +505,8 @@ bool qdev_realize(DeviceState *dev, BusState *bus, Error **errp);
* for the only reference to the child device to be held by the parent
* via the child<> property, and so the reference-count-drop done here
* would be incorrect. For that use case you want qdev_realize().
*
* Return: true on success, else false setting @errp with error
*/
bool qdev_realize_and_unref(DeviceState *dev, BusState *bus, Error **errp);
@ -420,16 +535,16 @@ void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id,
HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev);
HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev);
bool qdev_hotplug_allowed(DeviceState *dev, Error **errp);
/**
* qdev_get_hotplug_handler: Get handler responsible for device wiring
*
* Find HOTPLUG_HANDLER for @dev that provides [pre|un]plug callbacks for it.
* qdev_get_hotplug_handler() - Get handler responsible for device wiring
* @dev: the device we want the HOTPLUG_HANDLER for.
*
* Note: in case @dev has a parent bus, it will be returned as handler unless
* machine handler overrides it.
*
* Returns: pointer to object that implements TYPE_HOTPLUG_HANDLER interface
* or NULL if there aren't any.
* Return: pointer to object that implements TYPE_HOTPLUG_HANDLER interface
* or NULL if there aren't any.
*/
HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev);
void qdev_unplug(DeviceState *dev, Error **errp);
@ -459,15 +574,15 @@ void qdev_del_unplug_blocker(DeviceState *dev, Error *reason);
* qdev_unplug_blocked: Confirm if a device is blocked from unplug
*
* @dev: Device to be tested
* @reason: Returns one of the reasons why the device is blocked,
* if any
* @errp: The reasons why the device is blocked, if any
*
* Returns: true if device is blocked from unplug, false otherwise
* Returns: true (also setting @errp) if device is blocked from unplug,
* false otherwise
*/
bool qdev_unplug_blocked(DeviceState *dev, Error **errp);
/**
* GpioPolarity: Polarity of a GPIO line
* typedef GpioPolarity - Polarity of a GPIO line
*
* GPIO lines use either positive (active-high) logic,
* or negative (active-low) logic.
@ -499,6 +614,8 @@ typedef enum {
* connect another device's output GPIO line to this input.
*
* For named input GPIO lines, use qdev_get_gpio_in_named().
*
* Return: qemu_irq corresponding to anonymous input GPIO line
*/
qemu_irq qdev_get_gpio_in(DeviceState *dev, int n);
@ -516,6 +633,8 @@ qemu_irq qdev_get_gpio_in(DeviceState *dev, int n);
* array); this function will assert() if passed an invalid name or index.
*
* For anonymous input GPIO lines, use qdev_get_gpio_in().
*
* Return: qemu_irq corresponding to named input GPIO line
*/
qemu_irq qdev_get_gpio_in_named(DeviceState *dev, const char *name, int n);
@ -523,7 +642,7 @@ qemu_irq qdev_get_gpio_in_named(DeviceState *dev, const char *name, int n);
* qdev_connect_gpio_out: Connect one of a device's anonymous output GPIO lines
* @dev: Device whose GPIO to connect
* @n: Number of the anonymous output GPIO line (which must be in range)
* @input_pin: qemu_irq to connect the output line to
* @pin: qemu_irq to connect the output line to
*
* This function connects an anonymous output GPIO line on a device
* up to an arbitrary qemu_irq, so that when the device asserts that
@ -594,6 +713,8 @@ void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n,
*
* You probably don't need to use this function -- it is used only
* by the platform-bus subsystem.
*
* Return: qemu_irq associated with GPIO or NULL if un-wired.
*/
qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n);
@ -604,14 +725,17 @@ qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n);
* @name: Name of the output GPIO array
* @n: Number of the GPIO line in the array
*
* This function is provided only for use by the qtest testing framework
* and is not suitable for use in non-testing parts of QEMU.
* .. note::
* This function is provided only for use by the qtest testing framework
* and is not suitable for use in non-testing parts of QEMU.
*
* This function breaks an existing connection of an outbound GPIO
* line from @dev, and replaces it with the new qemu_irq @icpt, as if
* ``qdev_connect_gpio_out_named(dev, icpt, name, n)`` had been called.
* The previously connected qemu_irq is returned, so it can be restored
* by a second call to qdev_intercept_gpio_out() if desired.
*
* Return: old disconnected qemu_irq if one existed
*/
qemu_irq qdev_intercept_gpio_out(DeviceState *dev, qemu_irq icpt,
const char *name, int n);
@ -683,9 +807,7 @@ void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins,
const char *name, int n);
/**
* qdev_init_gpio_in_named_with_opaque: create an array of input GPIO lines
* for the specified device
*
* qdev_init_gpio_in_named_with_opaque() - create an array of input GPIO lines
* @dev: Device to create input GPIOs for
* @handler: Function to call when GPIO line value is set
* @opaque: Opaque data pointer to pass to @handler
@ -698,8 +820,11 @@ void qdev_init_gpio_in_named_with_opaque(DeviceState *dev,
const char *name, int n);
/**
* qdev_init_gpio_in_named: create an array of input GPIO lines
* for the specified device
* qdev_init_gpio_in_named() - create an array of input GPIO lines
* @dev: device to add array to
* @handler: a &typedef qemu_irq_handler function to call when GPIO is set
* @name: Name of the GPIO input (must be unique for this device)
* @n: Number of GPIO lines in this input set
*
* Like qdev_init_gpio_in_named_with_opaque(), but the opaque pointer
* passed to the handler is @dev (which is the most commonly desired behaviour).
@ -762,14 +887,17 @@ int qdev_walk_children(DeviceState *dev,
void *opaque);
/**
* device_cold_reset:
* device_cold_reset() - perform a recursive cold reset on a device
* @dev: device to reset.
*
* Reset device @dev and perform a recursive processing using the resettable
* interface. It triggers a RESET_TYPE_COLD.
*/
void device_cold_reset(DeviceState *dev);
/**
* bus_cold_reset:
* bus_cold_reset() - perform a recursive cold reset on a bus
* @bus: bus to reset
*
* Reset bus @bus and perform a recursive processing using the resettable
* interface. It triggers a RESET_TYPE_COLD.
@ -777,14 +905,18 @@ void device_cold_reset(DeviceState *dev);
void bus_cold_reset(BusState *bus);
/**
* device_is_in_reset:
* Return true if the device @dev is currently being reset.
* device_is_in_reset() - check device reset state
* @dev: device to check
*
* Return: true if the device @dev is currently being reset.
*/
bool device_is_in_reset(DeviceState *dev);
/**
* bus_is_in_reset:
* Return true if the bus @bus is currently being reset.
* bus_is_in_reset() - check bus reset state
* @bus: bus to check
*
* Return: true if the bus @bus is currently being reset.
*/
bool bus_is_in_reset(BusState *bus);
@ -797,7 +929,14 @@ char *qdev_get_own_fw_dev_path_from_handler(BusState *bus, DeviceState *dev);
void device_class_set_props(DeviceClass *dc, Property *props);
/**
* device_class_set_parent_reset:
* device_class_set_parent_reset() - legacy set device reset handlers
* @dc: device class
* @dev_reset: function pointer to reset handler
* @parent_reset: function pointer to parents reset handler
*
* Modern code should use the ResettableClass interface to
* implement a multi-phase reset instead.
*
* TODO: remove the function when DeviceClass's reset method
* is not used anymore.
*/
@ -871,14 +1010,15 @@ void device_listener_register(DeviceListener *listener);
void device_listener_unregister(DeviceListener *listener);
/**
* @qdev_should_hide_device:
* qdev_should_hide_device() - check if device should be hidden
*
* @opts: options QDict
* @from_json: true if @opts entries are typed, false for all strings
* @errp: pointer to error object
*
* Check if a device should be added.
* When a device is added via qdev_device_add() this will be called,
* and return if the device should be added now or not.
* When a device is added via qdev_device_add() this will be called.
*
* Return: if the device should be added now or not.
*/
bool qdev_should_hide_device(const QDict *opts, bool from_json, Error **errp);

View File

@ -1209,7 +1209,15 @@ int vmstate_register_with_alias_id(VMStateIf *obj, uint32_t instance_id,
int required_for_version,
Error **errp);
/* Returns: 0 on success, -1 on failure */
/**
* vmstate_register() - legacy function to register state
* serialisation description
*
* New code shouldn't be using this function as QOM-ified devices have
* dc->vmsd to store the serialisation description.
*
* Returns: 0 on success, -1 on failure
*/
static inline int vmstate_register(VMStateIf *obj, int instance_id,
const VMStateDescription *vmsd,
void *opaque)

View File

@ -165,6 +165,9 @@ typedef struct TaskState {
} TaskState;
abi_long do_brk(abi_ulong new_brk);
int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *pathname,
int flags, mode_t mode, bool safe);
ssize_t do_guest_readlink(const char *pathname, char *buf, size_t bufsiz);
/* user access */

View File

@ -8042,7 +8042,36 @@ static int open_self_cmdline(CPUArchState *cpu_env, int fd)
return 0;
}
static int open_self_maps(CPUArchState *cpu_env, int fd)
static void show_smaps(int fd, unsigned long size)
{
unsigned long page_size_kb = TARGET_PAGE_SIZE >> 10;
unsigned long size_kb = size >> 10;
dprintf(fd, "Size: %lu kB\n"
"KernelPageSize: %lu kB\n"
"MMUPageSize: %lu kB\n"
"Rss: 0 kB\n"
"Pss: 0 kB\n"
"Pss_Dirty: 0 kB\n"
"Shared_Clean: 0 kB\n"
"Shared_Dirty: 0 kB\n"
"Private_Clean: 0 kB\n"
"Private_Dirty: 0 kB\n"
"Referenced: 0 kB\n"
"Anonymous: 0 kB\n"
"LazyFree: 0 kB\n"
"AnonHugePages: 0 kB\n"
"ShmemPmdMapped: 0 kB\n"
"FilePmdMapped: 0 kB\n"
"Shared_Hugetlb: 0 kB\n"
"Private_Hugetlb: 0 kB\n"
"Swap: 0 kB\n"
"SwapPss: 0 kB\n"
"Locked: 0 kB\n"
"THPeligible: 0\n", size_kb, page_size_kb, page_size_kb);
}
static int open_self_maps_1(CPUArchState *cpu_env, int fd, bool smaps)
{
CPUState *cpu = env_cpu(cpu_env);
TaskState *ts = cpu->opaque;
@ -8089,6 +8118,18 @@ static int open_self_maps(CPUArchState *cpu_env, int fd)
} else {
dprintf(fd, "\n");
}
if (smaps) {
show_smaps(fd, max - min);
dprintf(fd, "VmFlags:%s%s%s%s%s%s%s%s\n",
(flags & PAGE_READ) ? " rd" : "",
(flags & PAGE_WRITE_ORG) ? " wr" : "",
(flags & PAGE_EXEC) ? " ex" : "",
e->is_priv ? "" : " sh",
(flags & PAGE_READ) ? " mr" : "",
(flags & PAGE_WRITE_ORG) ? " mw" : "",
(flags & PAGE_EXEC) ? " me" : "",
e->is_priv ? "" : " ms");
}
}
}
@ -8103,11 +8144,25 @@ static int open_self_maps(CPUArchState *cpu_env, int fd)
" --xp 00000000 00:00 0",
TARGET_VSYSCALL_PAGE, TARGET_VSYSCALL_PAGE + TARGET_PAGE_SIZE);
dprintf(fd, "%*s%s\n", 73 - count, "", "[vsyscall]");
if (smaps) {
show_smaps(fd, TARGET_PAGE_SIZE);
dprintf(fd, "VmFlags: ex\n");
}
#endif
return 0;
}
static int open_self_maps(CPUArchState *cpu_env, int fd)
{
return open_self_maps_1(cpu_env, fd, false);
}
static int open_self_smaps(CPUArchState *cpu_env, int fd)
{
return open_self_maps_1(cpu_env, fd, true);
}
static int open_self_stat(CPUArchState *cpu_env, int fd)
{
CPUState *cpu = env_cpu(cpu_env);
@ -8448,7 +8503,8 @@ static int open_hardware(CPUArchState *cpu_env, int fd)
}
#endif
static int do_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, int flags, mode_t mode)
int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *pathname,
int flags, mode_t mode, bool safe)
{
struct fake_open {
const char *filename;
@ -8458,6 +8514,7 @@ static int do_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, int
const struct fake_open *fake_open;
static const struct fake_open fakes[] = {
{ "maps", open_self_maps, is_proc_myself },
{ "smaps", open_self_smaps, is_proc_myself },
{ "stat", open_self_stat, is_proc_myself },
{ "auxv", open_self_auxv, is_proc_myself },
{ "cmdline", open_self_cmdline, is_proc_myself },
@ -8475,7 +8532,11 @@ static int do_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, int
};
if (is_proc_myself(pathname, "exe")) {
return safe_openat(dirfd, exec_path, flags, mode);
if (safe) {
return safe_openat(dirfd, exec_path, flags, mode);
} else {
return openat(dirfd, exec_path, flags, mode);
}
}
for (fake_open = fakes; fake_open->filename; fake_open++) {
@ -8517,7 +8578,41 @@ static int do_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, int
return fd;
}
return safe_openat(dirfd, path(pathname), flags, mode);
if (safe) {
return safe_openat(dirfd, path(pathname), flags, mode);
} else {
return openat(dirfd, path(pathname), flags, mode);
}
}
ssize_t do_guest_readlink(const char *pathname, char *buf, size_t bufsiz)
{
ssize_t ret;
if (!pathname || !buf) {
errno = EFAULT;
return -1;
}
if (!bufsiz) {
/* Short circuit this for the magic exe check. */
errno = EINVAL;
return -1;
}
if (is_proc_myself((const char *)pathname, "exe")) {
/*
* Don't worry about sign mismatch as earlier mapping
* logic would have thrown a bad address error.
*/
ret = MIN(strlen(exec_path), bufsiz);
/* We cannot NUL terminate the string. */
memcpy(buf, exec_path, ret);
} else {
ret = readlink(path(pathname), buf, bufsiz);
}
return ret;
}
static int do_execveat(CPUArchState *cpu_env, int dirfd,
@ -8994,9 +9089,9 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
case TARGET_NR_open:
if (!(p = lock_user_string(arg1)))
return -TARGET_EFAULT;
ret = get_errno(do_openat(cpu_env, AT_FDCWD, p,
ret = get_errno(do_guest_openat(cpu_env, AT_FDCWD, p,
target_to_host_bitmask(arg2, fcntl_flags_tbl),
arg3));
arg3, true));
fd_trans_unregister(ret);
unlock_user(p, arg1, 0);
return ret;
@ -9004,9 +9099,9 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
case TARGET_NR_openat:
if (!(p = lock_user_string(arg2)))
return -TARGET_EFAULT;
ret = get_errno(do_openat(cpu_env, arg1, p,
ret = get_errno(do_guest_openat(cpu_env, arg1, p,
target_to_host_bitmask(arg3, fcntl_flags_tbl),
arg4));
arg4, true));
fd_trans_unregister(ret);
unlock_user(p, arg2, 0);
return ret;
@ -10229,22 +10324,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
void *p2;
p = lock_user_string(arg1);
p2 = lock_user(VERIFY_WRITE, arg2, arg3, 0);
if (!p || !p2) {
ret = -TARGET_EFAULT;
} else if (!arg3) {
/* Short circuit this for the magic exe check. */
ret = -TARGET_EINVAL;
} else if (is_proc_myself((const char *)p, "exe")) {
/*
* Don't worry about sign mismatch as earlier mapping
* logic would have thrown a bad address error.
*/
ret = MIN(strlen(exec_path), arg3);
/* We cannot NUL terminate the string. */
memcpy(p2, exec_path, ret);
} else {
ret = get_errno(readlink(path(p), p2, arg3));
}
ret = get_errno(do_guest_readlink(p, p2, arg3));
unlock_user(p2, arg2, ret);
unlock_user(p, arg1, 0);
}

View File

@ -140,6 +140,18 @@ static void usage(FILE *out)
names.options ?: "-");
}
static xkb_mod_mask_t get_mod(struct xkb_keymap *map, const char *name)
{
xkb_mod_index_t mod;
xkb_mod_mask_t mask = 0;
mod = xkb_keymap_mod_get_index(map, name);
if (mod != XKB_MOD_INVALID) {
mask = (1 << mod);
}
return mask;
}
int main(int argc, char *argv[])
{
struct xkb_context *ctx;
@ -215,14 +227,10 @@ int main(int argc, char *argv[])
mod, xkb_keymap_mod_get_name(map, mod));
}
mod = xkb_keymap_mod_get_index(map, "Shift");
shift = (1 << mod);
mod = xkb_keymap_mod_get_index(map, "Control");
ctrl = (1 << mod);
mod = xkb_keymap_mod_get_index(map, "AltGr");
altgr = (1 << mod);
mod = xkb_keymap_mod_get_index(map, "NumLock");
numlock = (1 << mod);
shift = get_mod(map, "Shift");
ctrl = get_mod(map, "Control");
altgr = get_mod(map, "AltGr");
numlock = get_mod(map, "NumLock");
state = xkb_state_new(map);
xkb_keymap_key_for_each(map, walk_map, state);

View File

@ -1,2 +1,5 @@
# The tcmalloc on Fedora37 confuses things
leak:/lib64/libtcmalloc_minimal.so.4
# libxkbcommon also leaks in qemu-keymap
leak:/lib64/libxkbcommon.so.0

View File

@ -489,9 +489,9 @@ static bool S1_ptw_translate(CPUARMState *env, S1Translate *ptw,
int flags;
env->tlb_fi = fi;
flags = probe_access_full(env, addr, 0, MMU_DATA_LOAD,
arm_to_core_mmu_idx(s2_mmu_idx),
true, &ptw->out_host, &full, 0);
flags = probe_access_full_mmu(env, addr, 0, MMU_DATA_LOAD,
arm_to_core_mmu_idx(s2_mmu_idx),
&ptw->out_host, &full);
env->tlb_fi = NULL;
if (unlikely(flags & TLB_INVALID_MASK)) {
@ -628,7 +628,7 @@ static uint64_t arm_casq_ptw(CPUARMState *env, uint64_t old_val,
uint64_t new_val, S1Translate *ptw,
ARMMMUFaultInfo *fi)
{
#ifdef TARGET_AARCH64
#if defined(TARGET_AARCH64) && defined(CONFIG_TCG)
uint64_t cur_val;
void *host = ptw->out_host;
@ -644,12 +644,12 @@ static uint64_t arm_casq_ptw(CPUARMState *env, uint64_t old_val,
*/
if (unlikely(!ptw->out_rw)) {
int flags;
void *discard;
env->tlb_fi = fi;
flags = probe_access_flags(env, ptw->out_virt, 0, MMU_DATA_STORE,
arm_to_core_mmu_idx(ptw->in_ptw_idx),
true, &discard, 0);
flags = probe_access_full_mmu(env, ptw->out_virt, 0,
MMU_DATA_STORE,
arm_to_core_mmu_idx(ptw->in_ptw_idx),
NULL, NULL);
env->tlb_fi = NULL;
if (unlikely(flags & TLB_INVALID_MASK)) {
@ -709,7 +709,7 @@ static uint64_t arm_casq_ptw(CPUARMState *env, uint64_t old_val,
return cur_val;
#else
/* AArch32 does not have FEAT_HADFS. */
/* AArch32 does not have FEAT_HADFS; non-TCG guests only use debug-mode. */
g_assert_not_reached();
#endif
}

View File

@ -5688,9 +5688,6 @@ void sve_ldN_r(CPUARMState *env, uint64_t *vg, const target_ulong addr,
flags = info.page[0].flags | info.page[1].flags;
if (unlikely(flags != 0)) {
#ifdef CONFIG_USER_ONLY
g_assert_not_reached();
#else
/*
* At least one page includes MMIO.
* Any bus operation can fail with cpu_transaction_failed,
@ -5727,7 +5724,6 @@ void sve_ldN_r(CPUARMState *env, uint64_t *vg, const target_ulong addr,
memcpy(&env->vfp.zregs[(rd + i) & 31], &scratch[i], reg_max);
}
return;
#endif
}
/* The entire operation is in RAM, on valid pages. */

View File

@ -869,7 +869,7 @@ class BootLinuxConsole(LinuxKernelTest):
self.vm.set_console()
kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
'console=ttyS0,115200 '
'root=/dev/mmcblk0 rootwait rw '
'root=b300 rootwait rw '
'panic=-1 noreboot')
self.vm.add_args('-kernel', kernel_path,
'-dtb', dtb_path,
@ -885,7 +885,7 @@ class BootLinuxConsole(LinuxKernelTest):
exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
'Allwinner sun8i Family')
exec_command_and_wait_for_pattern(self, 'cat /proc/partitions',
'mmcblk0')
'mmcblk')
exec_command_and_wait_for_pattern(self, 'ifconfig eth0 up',
'eth0: Link is Up')
exec_command_and_wait_for_pattern(self, 'udhcpc eth0',

View File

@ -29,23 +29,23 @@ class Aarch64SbsarefMachine(QemuSystemTest):
"""
Flash volumes generated using:
- Fedora GNU Toolchain version 12.2.1 20220819 (Red Hat Cross 12.2.1-2)
- Fedora GNU Toolchain version 13.1.1 20230511 (Red Hat 13.1.1-2)
- Trusted Firmware-A
https://github.com/ARM-software/arm-trusted-firmware/tree/5fdb2e54
https://github.com/ARM-software/arm-trusted-firmware/tree/c0d8ee38
- Tianocore EDK II
https://github.com/tianocore/edk2/tree/494127613b
https://github.com/tianocore/edk2-non-osi/tree/41876073
https://github.com/tianocore/edk2-platforms/tree/8efa4f42
https://github.com/tianocore/edk2/tree/0f9283429dd4
https://github.com/tianocore/edk2-non-osi/tree/f0bb00937ad6
https://github.com/tianocore/edk2-platforms/tree/7880b92e2a04
"""
# Secure BootRom (TF-A code)
fs0_xz_url = (
"https://fileserver.linaro.org/s/ATnSmq6k8SoXgbH/"
"https://fileserver.linaro.org/s/HrYMCjP7MEccjRP/"
"download/SBSA_FLASH0.fd.xz"
)
fs0_xz_hash = "a210a09692bcbe0a3743ffd0df44e80e0c7ad8ab"
fs0_xz_hash = "447eff64a90b84ce47703c6ec41fbfc25befaaea"
tar_xz_path = self.fetch_asset(fs0_xz_url, asset_hash=fs0_xz_hash)
archive.extract(tar_xz_path, self.workdir)
fs0_path = os.path.join(self.workdir, "SBSA_FLASH0.fd")
@ -93,15 +93,15 @@ class Aarch64SbsarefMachine(QemuSystemTest):
# AP Trusted ROM
wait_for_console_pattern(self, "Booting Trusted Firmware")
wait_for_console_pattern(self, "BL1: v2.8(release):v2.8")
wait_for_console_pattern(self, "BL1: v2.9(release):v2.9")
wait_for_console_pattern(self, "BL1: Booting BL2")
# Trusted Boot Firmware
wait_for_console_pattern(self, "BL2: v2.8(release)")
wait_for_console_pattern(self, "BL2: v2.9(release)")
wait_for_console_pattern(self, "Booting BL31")
# EL3 Runtime Software
wait_for_console_pattern(self, "BL31: v2.8(release)")
wait_for_console_pattern(self, "BL31: v2.9(release)")
# Non-trusted Firmware
wait_for_console_pattern(self, "UEFI firmware (version 1.0")
@ -136,21 +136,18 @@ class Aarch64SbsarefMachine(QemuSystemTest):
self.vm.launch()
wait_for_console_pattern(self, "Welcome to Alpine Linux 3.17")
@skipUnless(os.getenv("AVOCADO_TIMEOUT_EXPECTED"), "Test might timeout")
def test_sbsaref_alpine_linux_cortex_a57(self):
"""
:avocado: tags=cpu:cortex-a57
"""
self.boot_alpine_linux("cortex-a57")
@skipUnless(os.getenv("AVOCADO_TIMEOUT_EXPECTED"), "Test might timeout")
def test_sbsaref_alpine_linux_neoverse_n1(self):
"""
:avocado: tags=cpu:max
"""
self.boot_alpine_linux("neoverse-n1")
@skip("requires TF-A update to handle FEAT_FGT")
def test_sbsaref_alpine_linux_max(self):
"""
:avocado: tags=cpu:max

View File

@ -1,10 +1,10 @@
# THIS FILE WAS AUTO-GENERATED
#
# $ lcitool dockerfile --layers all alpine-316 qemu
# $ lcitool dockerfile --layers all alpine-318 qemu
#
# https://gitlab.com/libvirt/libvirt-ci
FROM docker.io/library/alpine:3.16
FROM docker.io/library/alpine:3.18
RUN apk update && \
apk upgrade && \

View File

@ -24,6 +24,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
exuberant-ctags \
findutils \
flex \
gcc \
gcovr \
gettext \
git \

View File

@ -24,6 +24,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
exuberant-ctags \
findutils \
flex \
gcc \
gcovr \
gettext \
git \

View File

@ -24,6 +24,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
exuberant-ctags \
findutils \
flex \
gcc \
gcovr \
gettext \
git \

View File

@ -24,6 +24,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
exuberant-ctags \
findutils \
flex \
gcc \
gcovr \
gettext \
git \

View File

@ -24,6 +24,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
exuberant-ctags \
findutils \
flex \
gcc \
gcovr \
gettext \
git \

View File

@ -24,6 +24,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
exuberant-ctags \
findutils \
flex \
gcc \
gcovr \
gettext \
git \

View File

@ -24,6 +24,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
exuberant-ctags \
findutils \
flex \
gcc \
gcovr \
gettext \
git \

View File

@ -1,54 +1,85 @@
# THIS FILE WAS AUTO-GENERATED
#
# Docker cross-compiler target for riscv64
#
# Currently the only distro that gets close to cross compiling riscv64
# images is Debian Sid (with unofficial ports). As this is a moving
# target we keep the library list minimal and are aiming to migrate
# from this hack as soon as we are able.
# $ lcitool dockerfile --layers all --cross riscv64 debian-sid qemu-minimal
#
# https://gitlab.com/libvirt/libvirt-ci
FROM docker.io/library/debian:sid-slim
# Add ports
RUN apt update && \
DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \
DEBIAN_FRONTEND=noninteractive eatmydata apt update -yy && \
DEBIAN_FRONTEND=noninteractive eatmydata apt upgrade -yy
RUN export DEBIAN_FRONTEND=noninteractive && \
apt-get update && \
apt-get install -y eatmydata && \
eatmydata apt-get dist-upgrade -y && \
eatmydata apt-get install --no-install-recommends -y \
bash \
bc \
bison \
ca-certificates \
ccache \
findutils \
flex \
gcc \
git \
libglib2.0-dev \
locales \
make \
meson \
ninja-build \
pkgconf \
python3 \
python3-venv \
sed \
tar && \
eatmydata apt-get autoremove -y && \
eatmydata apt-get autoclean -y && \
sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \
dpkg-reconfigure locales
# Install common build utilities
RUN DEBIAN_FRONTEND=noninteractive eatmydata apt install -yy \
bison \
bc \
build-essential \
ca-certificates \
debian-ports-archive-keyring \
dpkg-dev \
flex \
gettext \
git \
libglib2.0-dev \
ninja-build \
pkg-config \
python3 \
python3-venv
ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers"
ENV LANG "en_US.UTF-8"
ENV MAKE "/usr/bin/make"
ENV NINJA "/usr/bin/ninja"
ENV PYTHON "/usr/bin/python3"
# Add ports and riscv64 architecture
RUN echo "deb http://ftp.ports.debian.org/debian-ports/ sid main" >> /etc/apt/sources.list
RUN dpkg --add-architecture riscv64
RUN export DEBIAN_FRONTEND=noninteractive && \
dpkg --add-architecture riscv64 && \
eatmydata apt-get install debian-ports-archive-keyring && \
eatmydata echo 'deb http://ftp.ports.debian.org/debian-ports/ sid main' > /etc/apt/sources.list.d/ports.list && \
eatmydata echo 'deb http://ftp.ports.debian.org/debian-ports/ unreleased main' >> /etc/apt/sources.list.d/ports.list && \
eatmydata apt-get update && \
eatmydata apt-get dist-upgrade -y && \
eatmydata apt-get install --no-install-recommends -y dpkg-dev && \
eatmydata apt-get install --no-install-recommends -y \
g++-riscv64-linux-gnu \
gcc-riscv64-linux-gnu \
libc6-dev:riscv64 \
libfdt-dev:riscv64 \
libffi-dev:riscv64 \
libglib2.0-dev:riscv64 \
libpixman-1-dev:riscv64 && \
eatmydata apt-get autoremove -y && \
eatmydata apt-get autoclean -y && \
mkdir -p /usr/local/share/meson/cross && \
printf "[binaries]\n\
c = '/usr/bin/riscv64-linux-gnu-gcc'\n\
ar = '/usr/bin/riscv64-linux-gnu-gcc-ar'\n\
strip = '/usr/bin/riscv64-linux-gnu-strip'\n\
pkgconfig = '/usr/bin/riscv64-linux-gnu-pkg-config'\n\
\n\
[host_machine]\n\
system = 'linux'\n\
cpu_family = 'riscv64'\n\
cpu = 'riscv64'\n\
endian = 'little'\n" > /usr/local/share/meson/cross/riscv64-linux-gnu && \
dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \
mkdir -p /usr/libexec/ccache-wrappers && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-c++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-cc && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-g++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-gcc
# Duplicate deb line as deb-src
RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list
RUN apt update && \
DEBIAN_FRONTEND=noninteractive eatmydata \
apt install -y --no-install-recommends \
gcc-riscv64-linux-gnu \
libc6-dev-riscv64-cross \
libfdt-dev:riscv64 \
libffi-dev:riscv64 \
libglib2.0-dev:riscv64 \
libpixman-1-dev:riscv64
# Specify the cross prefix for this image (see tests/docker/common.rc)
ENV ABI "riscv64-linux-gnu"
ENV MESON_OPTS "--cross-file=riscv64-linux-gnu"
ENV QEMU_CONFIGURE_OPTS --cross-prefix=riscv64-linux-gnu-
ENV DEF_TARGET_LIST riscv64-softmmu,riscv64-linux-user
# As a final step configure the user (if env is defined)

View File

@ -24,6 +24,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
exuberant-ctags \
findutils \
flex \
gcc \
gcovr \
gettext \
git \

View File

@ -1,10 +1,10 @@
# THIS FILE WAS AUTO-GENERATED
#
# $ lcitool dockerfile --layers all --cross mingw32 fedora-37 qemu
# $ lcitool dockerfile --layers all --cross mingw32 fedora-38 qemu
#
# https://gitlab.com/libvirt/libvirt-ci
FROM registry.fedoraproject.org/fedora:37
FROM registry.fedoraproject.org/fedora:38
RUN dnf install -y nosync && \
printf '#!/bin/sh\n\
@ -29,6 +29,7 @@ exec "$@"\n' > /usr/bin/nosync && \
diffutils \
findutils \
flex \
gcc \
gcovr \
git \
glib2-devel \

View File

@ -1,10 +1,10 @@
# THIS FILE WAS AUTO-GENERATED
#
# $ lcitool dockerfile --layers all --cross mingw64 fedora-37 qemu
# $ lcitool dockerfile --layers all --cross mingw64 fedora-38 qemu
#
# https://gitlab.com/libvirt/libvirt-ci
FROM registry.fedoraproject.org/fedora:37
FROM registry.fedoraproject.org/fedora:38
RUN dnf install -y nosync && \
printf '#!/bin/sh\n\
@ -29,6 +29,7 @@ exec "$@"\n' > /usr/bin/nosync && \
diffutils \
findutils \
flex \
gcc \
gcovr \
git \
glib2-devel \

View File

@ -1,10 +1,10 @@
# THIS FILE WAS AUTO-GENERATED
#
# $ lcitool dockerfile --layers all fedora-37 qemu
# $ lcitool dockerfile --layers all fedora-38 qemu
#
# https://gitlab.com/libvirt/libvirt-ci
FROM registry.fedoraproject.org/fedora:37
FROM registry.fedoraproject.org/fedora:38
RUN dnf install -y nosync && \
printf '#!/bin/sh\n\

28
tests/docker/test-fuzz Executable file
View File

@ -0,0 +1,28 @@
#!/bin/bash -e
#
# Compile and check with oss-fuzz.
#
# Copyright (c) 2023 Linaro Ltd.
#
# Authors:
# Alex Bennée <alex.bennee@linaro.org>
#
# SPDX-License-Identifier: GPL-2.0-or-later
. common.rc
requires_binary clang
# the build script runs out of $src so we need to copy across
cd "$BUILD_DIR"
cp -a $QEMU_SRC .
cd src
mkdir build-oss-fuzz
export LSAN_OPTIONS=suppressions=scripts/oss-fuzz/lsan_suppressions.txt
env CC="clang" CXX="clang++" CFLAGS="-fsanitize=address" ./scripts/oss-fuzz/build.sh
export ASAN_OPTIONS="fast_unwind_on_malloc=0"
for fuzzer in $(find ./build-oss-fuzz/DEST_DIR/ -executable -type f | grep -v slirp); do
grep "LLVMFuzzerTestOneInput" ${fuzzer} > /dev/null 2>&1 || continue ;
echo Testing ${fuzzer} ... ;
"${fuzzer}" -runs=1 -seed=1 || exit 1 ;
done

@ -1 +1 @@
Subproject commit c8971e90ac169ee2b539c747f74d96c876debdf9
Subproject commit b0f44f929a81c0a604fb7fbf8afc34d37ab0eae9

View File

@ -0,0 +1,27 @@
# Very minimal set of qemu packages, used for minimal cross-compile sanity checks
---
packages:
- bash
- bc
- bison
- ccache
- findutils
- flex
- g++
- gcc
- gcc-native
- glib2
- glib2-native
- glib2-static
- libc-static
- libfdt
- libffi
- make
- meson
- ninja
- pixman
- pkg-config
- python3
- python3-venv
- sed
- tar

View File

@ -24,6 +24,7 @@ packages:
- fuse3
- g++
- gcc
- gcc-native
- gcovr
- gettext
- glib2

View File

@ -63,12 +63,12 @@ add_user_mapping = [
" id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi\n"
]
def generate_dockerfile(host, target, cross=None, trailer=None):
def generate_dockerfile(host, target, project="qemu", cross=None, trailer=None):
filename = Path(src_dir, "tests", "docker", "dockerfiles", host + ".docker")
cmd = lcitool_cmd + ["dockerfile"]
if cross is not None:
cmd.extend(["--cross", cross])
cmd.extend([target, "qemu"])
cmd.extend([target, project])
if trailer is not None:
trailer += "\n".join(add_user_mapping)
@ -115,11 +115,11 @@ try:
#
# Standard native builds
#
generate_dockerfile("alpine", "alpine-316")
generate_dockerfile("alpine", "alpine-318")
generate_dockerfile("centos8", "centos-stream-8")
generate_dockerfile("debian-amd64", "debian-11",
trailer="".join(debian11_extras))
generate_dockerfile("fedora", "fedora-37")
generate_dockerfile("fedora", "fedora-38")
generate_dockerfile("opensuse-leap", "opensuse-leap-15")
generate_dockerfile("ubuntu2004", "ubuntu-2004")
generate_dockerfile("ubuntu2204", "ubuntu-2204")
@ -164,17 +164,23 @@ try:
trailer=cross_build("powerpc64le-linux-gnu-",
"ppc64-softmmu,ppc64-linux-user"))
generate_dockerfile("debian-riscv64-cross", "debian-sid",
project="qemu-minimal",
cross="riscv64",
trailer=cross_build("riscv64-linux-gnu-",
"riscv64-softmmu,riscv64-linux-user"))
generate_dockerfile("debian-s390x-cross", "debian-11",
cross="s390x",
trailer=cross_build("s390x-linux-gnu-",
"s390x-softmmu,s390x-linux-user"))
generate_dockerfile("fedora-win32-cross", "fedora-37",
generate_dockerfile("fedora-win32-cross", "fedora-38",
cross="mingw32",
trailer=cross_build("i686-w64-mingw32-",
"i386-softmmu"))
generate_dockerfile("fedora-win64-cross", "fedora-37",
generate_dockerfile("fedora-win64-cross", "fedora-38",
cross="mingw64",
trailer=cross_build("x86_64-w64-mingw32-",
"x86_64-softmmu"))

View File

@ -104,7 +104,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
for (i = 0; i < argc; i++) {
char *opt = argv[i];
g_autofree char **tokens = g_strsplit(opt, "=", 2);
g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
if (g_strcmp0(tokens[0], "inline") == 0) {
if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) {
fprintf(stderr, "boolean argument parsing failed: %s\n", opt);

View File

@ -189,7 +189,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
{
for (int i = 0; i < argc; i++) {
char *opt = argv[i];
g_autofree char **tokens = g_strsplit(opt, "=", 2);
g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
if (g_strcmp0(tokens[0], "inline") == 0) {
if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) {
fprintf(stderr, "boolean argument parsing failed: %s\n", opt);

View File

@ -83,7 +83,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
for (int i = 0; i < argc; i++) {
char *opt = argv[i];
g_autofree char **tokens = g_strsplit(opt, "=", 2);
g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
if (g_strcmp0(tokens[0], "haddr") == 0) {
if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_haddr)) {

View File

@ -121,7 +121,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
for (int i = 0; i < argc; i++) {
char *opt = argv[i];
g_autofree char **tokens = g_strsplit(opt, "=", 2);
g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
if (g_strcmp0(tokens[0], "print") == 0) {
if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_print)) {

View File

@ -954,17 +954,10 @@ static void register_generic_fuzz_targets(void)
.crossover = generic_fuzz_crossover
});
GString *name;
const generic_fuzz_config *config;
for (int i = 0;
i < sizeof(predefined_configs) / sizeof(generic_fuzz_config);
i++) {
config = predefined_configs + i;
name = g_string_new("generic-fuzz");
g_string_append_printf(name, "-%s", config->name);
for (int i = 0; i < ARRAY_SIZE(predefined_configs); i++) {
const generic_fuzz_config *config = predefined_configs + i;
fuzz_add_target(&(FuzzTarget){
.name = name->str,
.name = g_strconcat("generic-fuzz-", config->name, NULL),
.description = "Predefined generic-fuzz config.",
.get_init_cmdline = generic_fuzz_predefined_config_cmdline,
.pre_fuzz = generic_pre_fuzz,

View File

@ -169,13 +169,17 @@ extract-plugin = $(wordlist 2, 2, $(subst -with-, ,$1))
RUN_TESTS+=$(EXTRA_RUNS)
# Some plugins need additional arguments above the default to fully
# exercise things. We can define them on a per-test basis here.
run-plugin-%-with-libmem.so: PLUGIN_ARGS=$(COMMA)inline=true$(COMMA)callback=true
ifeq ($(filter %-softmmu, $(TARGET)),)
run-%: %
$(call run-test, $<, $(QEMU) $(QEMU_OPTS) $<)
run-plugin-%:
$(call run-test, $@, $(QEMU) $(QEMU_OPTS) \
-plugin $(PLUGIN_LIB)/$(call extract-plugin,$@) \
-plugin $(PLUGIN_LIB)/$(call extract-plugin,$@)$(PLUGIN_ARGS) \
-d plugin -D $*.pout \
$(call strip-plugin,$<))
else
@ -189,7 +193,7 @@ run-plugin-%:
$(call run-test, $@, \
$(QEMU) -monitor none -display none \
-chardev file$(COMMA)path=$@.out$(COMMA)id=output \
-plugin $(PLUGIN_LIB)/$(call extract-plugin,$@) \
-plugin $(PLUGIN_LIB)/$(call extract-plugin,$@)$(PLUGIN_ARGS) \
-d plugin -D $*.pout \
$(QEMU_OPTS) $(call strip-plugin,$<))
endif

View File

@ -91,6 +91,14 @@ sha512-vector: sha512.c
TESTS += sha512-vector
ifneq ($(CROSS_CC_HAS_SVE),)
sha512-sve: CFLAGS=-O3 -march=armv8.1-a+sve
sha512-sve: sha512.c
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS)
TESTS += sha512-sve
endif
ifeq ($(HOST_GDB_SUPPORTS_ARCH),y)
GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py

View File

@ -81,6 +81,13 @@ run-gdbstub-qxfer-auxv-read: sha1
--bin $< --test $(MULTIARCH_SRC)/gdbstub/test-qxfer-auxv-read.py, \
basic gdbstub qXfer:auxv:read support)
run-gdbstub-proc-mappings: sha1
$(call run-test, $@, $(GDB_SCRIPT) \
--gdb $(HAVE_GDB_BIN) \
--qemu $(QEMU) --qargs "$(QEMU_OPTS)" \
--bin $< --test $(MULTIARCH_SRC)/gdbstub/test-proc-mappings.py, \
proc mappings support)
run-gdbstub-thread-breakpoint: testthread
$(call run-test, $@, $(GDB_SCRIPT) \
--gdb $(HAVE_GDB_BIN) \
@ -97,7 +104,7 @@ run-gdbstub-%:
$(call skip-test, "gdbstub test $*", "need working gdb")
endif
EXTRA_RUNS += run-gdbstub-sha1 run-gdbstub-qxfer-auxv-read \
run-gdbstub-thread-breakpoint
run-gdbstub-proc-mappings run-gdbstub-thread-breakpoint
# ARM Compatible Semi Hosting Tests
#

View File

@ -0,0 +1,65 @@
"""Test that gdbstub has access to proc mappings.
This runs as a sourced script (via -x, via run-test.py)."""
from __future__ import print_function
import gdb
import sys
n_failures = 0
def report(cond, msg):
"""Report success/fail of a test"""
if cond:
print("PASS: {}".format(msg))
else:
print("FAIL: {}".format(msg))
global n_failures
n_failures += 1
def run_test():
"""Run through the tests one by one"""
try:
mappings = gdb.execute("info proc mappings", False, True)
except gdb.error as exc:
exc_str = str(exc)
if "Not supported on this target." in exc_str:
# Detect failures due to an outstanding issue with how GDB handles
# the x86_64 QEMU's target.xml, which does not contain the
# definition of orig_rax. Skip the test in this case.
print("SKIP: {}".format(exc_str))
return
raise
report(isinstance(mappings, str), "Fetched the mappings from the inferior")
report("/sha1" in mappings, "Found the test binary name in the mappings")
def main():
"""Prepare the environment and run through the tests"""
try:
inferior = gdb.selected_inferior()
print("ATTACHED: {}".format(inferior.architecture().name()))
except (gdb.error, AttributeError):
print("SKIPPING (not connected)")
exit(0)
if gdb.parse_and_eval('$pc') == 0:
print("SKIP: PC not set")
exit(0)
try:
# These are not very useful in scripts
gdb.execute("set pagination off")
gdb.execute("set confirm off")
# Run the actual tests
run_test()
except gdb.error:
report(False, "GDB Exception: {}".format(sys.exc_info()[0]))
print("All tests complete: %d failures" % n_failures)
exit(n_failures)
main()