Merge tag 'qga-pull-2023-03-08' of github.com:kostyanf14/qemu into staging
qga-pull-2023-03-08
# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCgAdFiEEwsLBCepDxjwUI+uE711egWG6hOcFAmQI6V0ACgkQ711egWG6
# hOegDBAAk9S6bszwuvPUIupNofujYkFKrrgHiujTOmPVXvD52C8FhojKTlW3d1QT
# f50vkMkMgavewPzsJU0SAu9kX80uOprDZwUYZ/3awSRPgL1zfFnZSZj5C/Pk4tD2
# 9rD8YjVPgvRpIhqZGTaAr97NFpigslMdba8SaucHPA1FmwRUzs1lWCX2hK9ewWuD
# /3/6Dy9mVoFGdEru2kNO5uZFUVsfatZMUQS8oOdgwHtYRkVwV7olPglQZ/iqACor
# yegxAt5tUL3WJIiYAVntiGSos0QnD7AgrGnSM5398uA4/oMVdehpAf5TyUOJ0QEy
# aq51TGZQ6Vc/0sYrsO65zaNXNsgNx1jAl7BcBleawyrdM8q/ILStXelk3MFR7Dbz
# dNi5NNHK4acEStk5XJZHc+bPQybjeWGCsQY9NBO5zLmZO2gCWnjN/nWxT6ivAgzF
# JlYfiiuLku/sZBGun7giHsKQ0EFeMzi+DdKsX3AoJhA+RJ/XEa88MTIh4EIK/tsj
# BwoPtrngsHvkazwgpb1Fa204kTAhmjx+2bpyEiNAxcRTgShxXIsm09xGOi5w8z3Q
# 48kLmkPL/xwKLImh1hx4z612VhCwdhMaLgKmri5i99jWoKJqwpnUf04JtEvyYM0d
# ErlBQsz1GOjVZTm9yCtqZwjZeM5kK83lRu7fUxmtTTA3G1H/EAM=
# =wWvd
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 08 Mar 2023 20:00:29 GMT
# gpg: using RSA key C2C2C109EA43C63C1423EB84EF5D5E8161BA84E7
# gpg: Good signature from "Kostiantyn Kostiuk (Upstream PR sign) <kkostiuk@redhat.com>" [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: C2C2 C109 EA43 C63C 1423 EB84 EF5D 5E81 61BA 84E7
* tag 'qga-pull-2023-03-08' of github.com:kostyanf14/qemu:
qga/win/vss: requester_freeze changes
qga/win/vss: query VSS backup type
qga/win/installer: add VssOption to installer
qga/win32: Use rundll for VSS installation
qga/win32: Remove change action from MSI installer
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
diff --git a/.gitlab-ci.d/crossbuilds.yml b/.gitlab-ci.d/crossbuilds.yml
index d3a31a2..61b8ac8 100644
--- a/.gitlab-ci.d/crossbuilds.yml
+++ b/.gitlab-ci.d/crossbuilds.yml
@@ -1,13 +1,6 @@
include:
- local: '/.gitlab-ci.d/crossbuild-template.yml'
-cross-armel-system:
- extends: .cross_system_build_job
- needs:
- job: armel-debian-cross-container
- variables:
- IMAGE: debian-armel-cross
-
cross-armel-user:
extends: .cross_user_build_job
needs:
@@ -15,13 +8,6 @@
variables:
IMAGE: debian-armel-cross
-cross-armhf-system:
- extends: .cross_system_build_job
- needs:
- job: armhf-debian-cross-container
- variables:
- IMAGE: debian-armhf-cross
-
cross-armhf-user:
extends: .cross_user_build_job
needs:
@@ -43,16 +29,6 @@
variables:
IMAGE: debian-arm64-cross
-cross-i386-system:
- extends:
- - .cross_system_build_job
- - .cross_test_artifacts
- needs:
- job: i386-fedora-cross-container
- variables:
- IMAGE: fedora-i386-cross
- MAKE_CHECK_ARGS: check-qtest
-
cross-i386-user:
extends:
- .cross_user_build_job
diff --git a/MAINTAINERS b/MAINTAINERS
index 5340de0..3f24bc2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -136,6 +136,8 @@
F: docs/devel/tcg*
F: include/exec/cpu*.h
F: include/exec/exec-all.h
+F: include/exec/tb-flush.h
+F: include/exec/target_long.h
F: include/exec/helper*.h
F: include/sysemu/cpus.h
F: include/sysemu/tcg.h
@@ -443,6 +445,15 @@
F: target/i386/sev*
F: scripts/kvm/vmxcap
+Xen emulation on X86 KVM CPUs
+M: David Woodhouse <dwmw2@infradead.org>
+M: Paul Durrant <paul@xen.org>
+S: Supported
+F: include/sysemu/kvm_xen.h
+F: target/i386/kvm/xen*
+F: hw/i386/kvm/xen*
+F: tests/avocado/xen_guest.py
+
Guest CPU Cores (other accelerators)
------------------------------------
Overall
@@ -2742,9 +2753,11 @@
F: docs/system/gdb.rst
F: gdbstub/*
F: include/exec/gdbstub.h
+F: include/gdbstub/*
F: gdb-xml/
F: tests/tcg/multiarch/gdbstub/
F: scripts/feature_to_c.sh
+F: scripts/probe-gdb-support.py
Memory API
M: Paolo Bonzini <pbonzini@redhat.com>
diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c
index fbf4fe3..457eafa 100644
--- a/accel/kvm/kvm-accel-ops.c
+++ b/accel/kvm/kvm-accel-ops.c
@@ -86,6 +86,13 @@
return !kvm_enabled() || kvm_cpu_check_are_resettable();
}
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+static int kvm_update_guest_debug_ops(CPUState *cpu)
+{
+ return kvm_update_guest_debug(cpu, 0);
+}
+#endif
+
static void kvm_accel_ops_class_init(ObjectClass *oc, void *data)
{
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
@@ -99,6 +106,7 @@
ops->synchronize_pre_loadvm = kvm_cpu_synchronize_pre_loadvm;
#ifdef KVM_CAP_SET_GUEST_DEBUG
+ ops->update_guest_debug = kvm_update_guest_debug_ops;
ops->supports_guest_debug = kvm_supports_guest_debug;
ops->insert_breakpoint = kvm_insert_breakpoint;
ops->remove_breakpoint = kvm_remove_breakpoint;
diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c
index 96af23d..813695b 100644
--- a/accel/stubs/tcg-stub.c
+++ b/accel/stubs/tcg-stub.c
@@ -11,6 +11,7 @@
*/
#include "qemu/osdep.h"
+#include "exec/tb-flush.h"
#include "exec/exec-all.h"
void tb_flush(CPUState *cpu)
diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c
index efefa08..7246c1c 100644
--- a/accel/tcg/tb-maint.c
+++ b/accel/tcg/tb-maint.c
@@ -22,6 +22,7 @@
#include "exec/cputlb.h"
#include "exec/log.h"
#include "exec/exec-all.h"
+#include "exec/tb-flush.h"
#include "exec/translate-all.h"
#include "sysemu/tcg.h"
#include "tcg/tcg.h"
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
index a5bea8f..74deb18 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -47,6 +47,7 @@
#include "exec/cputlb.h"
#include "exec/translate-all.h"
#include "exec/translator.h"
+#include "exec/tb-flush.h"
#include "qemu/bitmap.h"
#include "qemu/qemu-print.h"
#include "qemu/main-loop.h"
diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c
index e85e4ae..00221e2 100644
--- a/accel/xen/xen-all.c
+++ b/accel/xen/xen-all.c
@@ -12,6 +12,7 @@
#include "qemu/error-report.h"
#include "qemu/module.h"
#include "qapi/error.h"
+#include "hw/xen/xen_native.h"
#include "hw/xen/xen-legacy-backend.h"
#include "hw/xen/xen_pt.h"
#include "chardev/char.h"
@@ -29,73 +30,15 @@
xenforeignmemory_handle *xen_fmem;
xendevicemodel_handle *xen_dmod;
-static int store_dev_info(int domid, Chardev *cs, const char *string)
+static void xenstore_record_dm_state(const char *state)
{
- struct xs_handle *xs = NULL;
- char *path = NULL;
- char *newpath = NULL;
- char *pts = NULL;
- int ret = -1;
-
- /* Only continue if we're talking to a pty. */
- if (!CHARDEV_IS_PTY(cs)) {
- return 0;
- }
- pts = cs->filename + 4;
+ struct xs_handle *xs;
+ char path[50];
/* We now have everything we need to set the xenstore entry. */
xs = xs_open(0);
if (xs == NULL) {
fprintf(stderr, "Could not contact XenStore\n");
- goto out;
- }
-
- path = xs_get_domain_path(xs, domid);
- if (path == NULL) {
- fprintf(stderr, "xs_get_domain_path() error\n");
- goto out;
- }
- newpath = realloc(path, (strlen(path) + strlen(string) +
- strlen("/tty") + 1));
- if (newpath == NULL) {
- fprintf(stderr, "realloc error\n");
- goto out;
- }
- path = newpath;
-
- strcat(path, string);
- strcat(path, "/tty");
- if (!xs_write(xs, XBT_NULL, path, pts, strlen(pts))) {
- fprintf(stderr, "xs_write for '%s' fail", string);
- goto out;
- }
- ret = 0;
-
-out:
- free(path);
- xs_close(xs);
-
- return ret;
-}
-
-void xenstore_store_pv_console_info(int i, Chardev *chr)
-{
- if (i == 0) {
- store_dev_info(xen_domid, chr, "/console");
- } else {
- char buf[32];
- snprintf(buf, sizeof(buf), "/device/console/%d", i);
- store_dev_info(xen_domid, chr, buf);
- }
-}
-
-
-static void xenstore_record_dm_state(struct xs_handle *xs, const char *state)
-{
- char path[50];
-
- if (xs == NULL) {
- error_report("xenstore connection not initialized");
exit(1);
}
@@ -109,6 +52,8 @@
error_report("error recording dm state");
exit(1);
}
+
+ xs_close(xs);
}
@@ -117,7 +62,7 @@
{
if (running) {
/* record state running */
- xenstore_record_dm_state(xenstore, "running");
+ xenstore_record_dm_state("running");
}
}
diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c
index 179a20c..c8f998e 100644
--- a/bsd-user/freebsd/os-syscall.c
+++ b/bsd-user/freebsd/os-syscall.c
@@ -38,6 +38,8 @@
#include <sys/sysctl.h>
#include <utime.h>
+#include "include/gdbstub/syscalls.h"
+
#include "qemu.h"
#include "signal-common.h"
#include "user/syscall-trace.h"
diff --git a/bsd-user/main.c b/bsd-user/main.c
index 41290e1..89f225d 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -44,6 +44,7 @@
#include "trace/control.h"
#include "crypto/init.h"
#include "qemu/guest-random.h"
+#include "gdbstub/user.h"
#include "host-os.h"
#include "target_arch_cpu.h"
diff --git a/bsd-user/signal.c b/bsd-user/signal.c
index 58a5386..f4e078e 100644
--- a/bsd-user/signal.c
+++ b/bsd-user/signal.c
@@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qemu.h"
+#include "gdbstub/user.h"
#include "signal-common.h"
#include "trace.h"
#include "hw/core/tcg-cpu-ops.h"
diff --git a/configure b/configure
index 219ff13..05bed4f 100755
--- a/configure
+++ b/configure
@@ -230,6 +230,7 @@
safe_stack=""
use_containers="yes"
gdb_bin=$(command -v "gdb-multiarch" || command -v "gdb")
+gdb_arches=""
if test -e "$source_path/.git"
then
@@ -2262,6 +2263,7 @@
# tests might fail. Prefer to keep the relevant files in their own
# directory and symlink the directory instead.
LINKS="Makefile"
+LINKS="$LINKS docs/config"
LINKS="$LINKS pc-bios/optionrom/Makefile"
LINKS="$LINKS pc-bios/s390-ccw/Makefile"
LINKS="$LINKS pc-bios/vof/Makefile"
@@ -2395,6 +2397,7 @@
gdb_version=$($gdb_bin --version | head -n 1)
if version_ge ${gdb_version##* } 9.1; then
echo "HAVE_GDB_BIN=$gdb_bin" >> $config_host_mak
+ gdb_arches=$("$source_path/scripts/probe-gdb-support.py" $gdb_bin)
else
gdb_bin=""
fi
@@ -2519,6 +2522,12 @@
write_target_makefile "build-tcg-tests-$target" >> "$config_target_mak"
echo "BUILD_STATIC=$build_static" >> "$config_target_mak"
echo "QEMU=$PWD/$qemu" >> "$config_target_mak"
+
+ # will GDB work with these binaries?
+ if test "${gdb_arches#*$arch}" != "$gdb_arches"; then
+ echo "HOST_GDB_SUPPORTS_ARCH=y" >> "$config_target_mak"
+ fi
+
echo "run-tcg-tests-$target: $qemu\$(EXESUF)" >> Makefile.prereqs
tcg_tests_targets="$tcg_tests_targets $target"
fi
diff --git a/cpu.c b/cpu.c
index 2e9f931..567b23a 100644
--- a/cpu.c
+++ b/cpu.c
@@ -31,11 +31,12 @@
#include "hw/core/sysemu-cpu-ops.h"
#include "exec/address-spaces.h"
#endif
+#include "sysemu/cpus.h"
#include "sysemu/tcg.h"
-#include "sysemu/kvm.h"
#include "exec/replay-core.h"
#include "exec/cpu-common.h"
#include "exec/exec-all.h"
+#include "exec/tb-flush.h"
#include "exec/translate-all.h"
#include "exec/log.h"
#include "hw/core/accel-cpu.h"
@@ -325,9 +326,14 @@
{
if (cpu->singlestep_enabled != enabled) {
cpu->singlestep_enabled = enabled;
- if (kvm_enabled()) {
- kvm_update_guest_debug(cpu, 0);
+
+#if !defined(CONFIG_USER_ONLY)
+ const AccelOpsClass *ops = cpus_get_accel();
+ if (ops->update_guest_debug) {
+ ops->update_guest_debug(cpu);
}
+#endif
+
trace_breakpoint_singlestep(cpu->cpu_index, enabled);
}
}
diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst
index 20b97c3..89cae5a 100644
--- a/docs/about/build-platforms.rst
+++ b/docs/about/build-platforms.rst
@@ -67,7 +67,8 @@
Linux OS, macOS, FreeBSD, NetBSD, OpenBSD
-----------------------------------------
-The project aims to support the most recent major version at all times. Support
+The project aims to support the most recent major version at all times for
+up to five years after its initial release. Support
for the previous major version will be dropped 2 years after the new major
version is released or when the vendor itself drops support, whichever comes
first. In this context, third-party efforts to extend the lifetime of a distro
diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst
index 15084f7..33b9422 100644
--- a/docs/about/deprecated.rst
+++ b/docs/about/deprecated.rst
@@ -196,6 +196,26 @@
completes. The little endian variants of MIPS (both 32 and 64 bit) are
still a supported host architecture.
+System emulation on 32-bit x86 hosts (since 8.0)
+''''''''''''''''''''''''''''''''''''''''''''''''
+
+Support for 32-bit x86 host deployments is increasingly uncommon in mainstream
+OS distributions given the widespread availability of 64-bit x86 hardware.
+The QEMU project no longer considers 32-bit x86 support for system emulation to
+be an effective use of its limited resources, and thus intends to discontinue
+it. Since all recent x86 hardware from the past >10 years is capable of the
+64-bit x86 extensions, a corresponding 64-bit OS should be used instead.
+
+System emulation on 32-bit arm hosts (since 8.0)
+''''''''''''''''''''''''''''''''''''''''''''''''
+
+Since QEMU needs a strong host machine for running full system emulation, and
+all recent powerful arm hosts support 64-bit, the QEMU project deprecates the
+support for running any system emulation on 32-bit arm hosts in general. Use
+64-bit arm hosts for system emulation instead. (Note: "user" mode emulation
+continues to be supported on 32-bit arm hosts, too)
+
+
QEMU API (QAPI) events
----------------------
diff --git a/docs/config/mach-virt-graphical.cfg b/docs/config/mach-virt-graphical.cfg
index d6d31b1..eba76eb 100644
--- a/docs/config/mach-virt-graphical.cfg
+++ b/docs/config/mach-virt-graphical.cfg
@@ -56,9 +56,11 @@
[machine]
type = "virt"
- accel = "kvm"
gic-version = "host"
+[accel]
+ accel = "kvm"
+
[memory]
size = "1024"
diff --git a/docs/config/mach-virt-serial.cfg b/docs/config/mach-virt-serial.cfg
index 18a7c83..324b054 100644
--- a/docs/config/mach-virt-serial.cfg
+++ b/docs/config/mach-virt-serial.cfg
@@ -62,9 +62,11 @@
[machine]
type = "virt"
- accel = "kvm"
gic-version = "host"
+[accel]
+ accel = "kvm"
+
[memory]
size = "1024"
diff --git a/docs/config/q35-emulated.cfg b/docs/config/q35-emulated.cfg
index 99ac918..c8806e6 100644
--- a/docs/config/q35-emulated.cfg
+++ b/docs/config/q35-emulated.cfg
@@ -61,6 +61,8 @@
[machine]
type = "q35"
+
+[accel]
accel = "kvm"
[memory]
diff --git a/docs/config/q35-virtio-graphical.cfg b/docs/config/q35-virtio-graphical.cfg
index 4207f11..148b5d2 100644
--- a/docs/config/q35-virtio-graphical.cfg
+++ b/docs/config/q35-virtio-graphical.cfg
@@ -55,6 +55,8 @@
[machine]
type = "q35"
+
+[accel]
accel = "kvm"
[memory]
diff --git a/docs/config/q35-virtio-serial.cfg b/docs/config/q35-virtio-serial.cfg
index d2830ae..0232913 100644
--- a/docs/config/q35-virtio-serial.cfg
+++ b/docs/config/q35-virtio-serial.cfg
@@ -60,6 +60,8 @@
[machine]
type = "q35"
+
+[accel]
accel = "kvm"
[memory]
diff --git a/docs/devel/atomics.rst b/docs/devel/atomics.rst
index 7957310..633df65 100644
--- a/docs/devel/atomics.rst
+++ b/docs/devel/atomics.rst
@@ -27,7 +27,8 @@
- weak atomic access and manual memory barriers: ``qatomic_read()``,
``qatomic_set()``, ``smp_rmb()``, ``smp_wmb()``, ``smp_mb()``,
- ``smp_mb_acquire()``, ``smp_mb_release()``, ``smp_read_barrier_depends()``;
+ ``smp_mb_acquire()``, ``smp_mb_release()``, ``smp_read_barrier_depends()``,
+ ``smp_mb__before_rmw()``, ``smp_mb__after_rmw()``;
- sequentially consistent atomic access: everything else.
@@ -472,7 +473,7 @@
sequential consistency.
- in QEMU, ``qatomic_read()`` and ``qatomic_set()`` do not participate in
- the total ordering enforced by sequentially-consistent operations.
+ the ordering enforced by read-modify-write operations.
This is because QEMU uses the C11 memory model. The following example
is correct in Linux but not in QEMU:
@@ -488,9 +489,24 @@
because the read of ``y`` can be moved (by either the processor or the
compiler) before the write of ``x``.
- Fixing this requires an ``smp_mb()`` memory barrier between the write
- of ``x`` and the read of ``y``. In the common case where only one thread
- writes ``x``, it is also possible to write it like this:
+ Fixing this requires a full memory barrier between the write of ``x`` and
+ the read of ``y``. QEMU provides ``smp_mb__before_rmw()`` and
+ ``smp_mb__after_rmw()``; they act both as an optimization,
+ avoiding the memory barrier on processors where it is unnecessary,
+ and as a clarification of this corner case of the C11 memory model:
+
+ +--------------------------------+
+ | QEMU (correct) |
+ +================================+
+ | :: |
+ | |
+ | a = qatomic_fetch_add(&x, 2);|
+ | smp_mb__after_rmw(); |
+ | b = qatomic_read(&y); |
+ +--------------------------------+
+
+ In the common case where only one thread writes ``x``, it is also possible
+ to write it like this:
+--------------------------------+
| QEMU (correct) |
diff --git a/docs/devel/vfio-migration.rst b/docs/devel/vfio-migration.rst
index c214c73..1b68ccf 100644
--- a/docs/devel/vfio-migration.rst
+++ b/docs/devel/vfio-migration.rst
@@ -59,22 +59,37 @@
----------------------------------
A ``log_global_start`` and ``log_global_stop`` memory listener callback informs
-the VFIO IOMMU module to start and stop dirty page tracking. A ``log_sync``
-memory listener callback marks those system memory pages as dirty which are
-used for DMA by the VFIO device. The dirty pages bitmap is queried per
-container. All pages pinned by the vendor driver through external APIs have to
-be marked as dirty during migration. When there are CPU writes, CPU dirty page
-tracking can identify dirtied pages, but any page pinned by the vendor driver
-can also be written by the device. There is currently no device or IOMMU
-support for dirty page tracking in hardware.
+the VFIO dirty tracking module to start and stop dirty page tracking. A
+``log_sync`` memory listener callback queries the dirty page bitmap from the
+dirty tracking module and marks system memory pages which were DMA-ed by the
+VFIO device as dirty. The dirty page bitmap is queried per container.
+
+Currently there are two ways dirty page tracking can be done:
+(1) Device dirty tracking:
+In this method the device is responsible to log and report its DMAs. This
+method can be used only if the device is capable of tracking its DMAs.
+Discovering device capability, starting and stopping dirty tracking, and
+syncing the dirty bitmaps from the device are done using the DMA logging uAPI.
+More info about the uAPI can be found in the comments of the
+``vfio_device_feature_dma_logging_control`` and
+``vfio_device_feature_dma_logging_report`` structures in the header file
+linux-headers/linux/vfio.h.
+
+(2) VFIO IOMMU module:
+In this method dirty tracking is done by IOMMU. However, there is currently no
+IOMMU support for dirty page tracking. For this reason, all pages are
+perpetually marked dirty, unless the device driver pins pages through external
+APIs in which case only those pinned pages are perpetually marked dirty.
+
+If the above two methods are not supported, all pages are perpetually marked
+dirty by QEMU.
By default, dirty pages are tracked during pre-copy as well as stop-and-copy
-phase. So, a page pinned by the vendor driver will be copied to the destination
-in both phases. Copying dirty pages in pre-copy phase helps QEMU to predict if
-it can achieve its downtime tolerances. If QEMU during pre-copy phase keeps
-finding dirty pages continuously, then it understands that even in stop-and-copy
-phase, it is likely to find dirty pages and can predict the downtime
-accordingly.
+phase. So, a page marked as dirty will be copied to the destination in both
+phases. Copying dirty pages in pre-copy phase helps QEMU to predict if it can
+achieve its downtime tolerances. If QEMU during pre-copy phase keeps finding
+dirty pages continuously, then it understands that even in stop-and-copy phase,
+it is likely to find dirty pages and can predict the downtime accordingly.
QEMU also provides a per device opt-out option ``pre-copy-dirty-page-tracking``
which disables querying the dirty bitmap during pre-copy phase. If it is set to
@@ -89,7 +104,8 @@
that range and QEMU reports corresponding guest physical pages dirty. During
stop-and-copy phase, an IOMMU notifier is used to get a callback for mapped
pages and then dirty pages bitmap is fetched from VFIO IOMMU modules for those
-mapped ranges.
+mapped ranges. If device dirty tracking is enabled with vIOMMU, live migration
+will be blocked.
Flow of state changes during Live migration
===========================================
diff --git a/docs/system/i386/xen.rst b/docs/system/i386/xen.rst
index a00523b..f06765e 100644
--- a/docs/system/i386/xen.rst
+++ b/docs/system/i386/xen.rst
@@ -9,6 +9,8 @@
channel (Xen PV interrupt) delivery. This allows guests which expect to be
run under Xen to be hosted in QEMU under Linux/KVM instead.
+Using the split irqchip is mandatory for Xen support.
+
Setup
-----
@@ -17,14 +19,14 @@
.. parsed-literal::
- |qemu_system| --accel kvm,xen-version=0x4000a
+ |qemu_system| --accel kvm,xen-version=0x4000a,kernel-irqchip=split
Additionally, virtual APIC support can be advertised to the guest through the
``xen-vapic`` CPU flag:
.. parsed-literal::
- |qemu_system| --accel kvm,xen-version=0x4000a --cpu host,+xen_vapic
+ |qemu_system| --accel kvm,xen-version=0x4000a,kernel-irqchip=split --cpu host,+xen_vapic
When Xen support is enabled, QEMU changes hypervisor identification (CPUID
0x40000000..0x4000000A) to Xen. The KVM identification and features are not
@@ -33,11 +35,25 @@
The Xen platform device is enabled automatically for a Xen guest. This allows
a guest to unplug all emulated devices, in order to use Xen PV block and network
-drivers instead. Note that until the Xen PV device back ends are enabled to work
-with Xen mode in QEMU, that is unlikely to cause significant joy. Linux guests
-can be dissuaded from this by adding 'xen_emul_unplug=never' on their command
-line, and it can also be noted that AHCI disk controllers are exempt from being
-unplugged, as are passthrough VFIO PCI devices.
+drivers instead. Under Xen, the boot disk is typically available both via IDE
+emulation, and as a PV block device. Guest bootloaders typically use IDE to load
+the guest kernel, which then unplugs the IDE and continues with the Xen PV block
+device.
+
+This configuration can be achieved as follows
+
+.. parsed-literal::
+
+ |qemu_system| -M pc --accel kvm,xen-version=0x4000a,kernel-irqchip=split \\
+ -drive file=${GUEST_IMAGE},if=none,id=disk,file.locking=off -device xen-disk,drive=disk,vdev=xvda \\
+ -drive file=${GUEST_IMAGE},index=2,media=disk,file.locking=off,if=ide
+
+It is necessary to use the pc machine type, as the q35 machine uses AHCI instead
+of legacy IDE, and AHCI disks are not unplugged through the Xen PV unplug
+mechanism.
+
+VirtIO devices can also be used; Linux guests may need to be dissuaded from
+umplugging them by adding 'xen_emul_unplug=never' on their command line.
Properties
----------
diff --git a/docs/system/target-mips.rst b/docs/system/target-mips.rst
index 138441b..83239fb 100644
--- a/docs/system/target-mips.rst
+++ b/docs/system/target-mips.rst
@@ -8,8 +8,6 @@
``qemu-system-mips64`` and ``qemu-system-mips64el``. Five different
machine types are emulated:
-- A generic ISA PC-like machine \"mips\"
-
- The MIPS Malta prototype board \"malta\"
- An ACER Pica \"pica61\". This machine needs the 64-bit emulator.
@@ -19,18 +17,6 @@
- A MIPS Magnum R4000 machine \"magnum\". This machine needs the
64-bit emulator.
-The generic emulation is supported by Debian 'Etch' and is able to
-install Debian into a virtual disk image. The following devices are
-emulated:
-
-- A range of MIPS CPUs, default is the 24Kf
-
-- PC style serial port
-
-- PC style IDE disk
-
-- NE2000 network card
-
The Malta emulation supports the following devices:
- Core board with MIPS 24Kf CPU and Galileo system controller
diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index fb9c49e..d9e9bf9 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -24,298 +24,26 @@
*/
#include "qemu/osdep.h"
-#include "qapi/error.h"
-#include "qemu/error-report.h"
#include "qemu/ctype.h"
#include "qemu/cutils.h"
#include "qemu/module.h"
#include "trace.h"
#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
#ifdef CONFIG_USER_ONLY
-#include "qemu.h"
+#include "gdbstub/user.h"
#else
-#include "monitor/monitor.h"
-#include "chardev/char.h"
-#include "chardev/char-fe.h"
#include "hw/cpu/cluster.h"
#include "hw/boards.h"
#endif
-#define MAX_PACKET_LENGTH 4096
-
-#include "qemu/sockets.h"
#include "sysemu/hw_accel.h"
#include "sysemu/runstate.h"
-#include "semihosting/semihost.h"
-#include "exec/exec-all.h"
#include "exec/replay-core.h"
+#include "exec/hwaddr.h"
#include "internals.h"
-#ifdef CONFIG_USER_ONLY
-#define GDB_ATTACHED "0"
-#else
-#define GDB_ATTACHED "1"
-#endif
-
-#ifndef CONFIG_USER_ONLY
-static int phy_memory_mode;
-#endif
-
-static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr,
- uint8_t *buf, int len, bool is_write)
-{
- CPUClass *cc;
-
-#ifndef CONFIG_USER_ONLY
- if (phy_memory_mode) {
- if (is_write) {
- cpu_physical_memory_write(addr, buf, len);
- } else {
- cpu_physical_memory_read(addr, buf, len);
- }
- return 0;
- }
-#endif
-
- cc = CPU_GET_CLASS(cpu);
- if (cc->memory_rw_debug) {
- return cc->memory_rw_debug(cpu, addr, buf, len, is_write);
- }
- return cpu_memory_rw_debug(cpu, addr, buf, len, is_write);
-}
-
-/* Return the GDB index for a given vCPU state.
- *
- * For user mode this is simply the thread id. In system mode GDB
- * numbers CPUs from 1 as 0 is reserved as an "any cpu" index.
- */
-static inline int cpu_gdb_index(CPUState *cpu)
-{
-#if defined(CONFIG_USER_ONLY)
- TaskState *ts = (TaskState *) cpu->opaque;
- return ts ? ts->ts_tid : -1;
-#else
- return cpu->cpu_index + 1;
-#endif
-}
-
-enum {
- GDB_SIGNAL_0 = 0,
- GDB_SIGNAL_INT = 2,
- GDB_SIGNAL_QUIT = 3,
- GDB_SIGNAL_TRAP = 5,
- GDB_SIGNAL_ABRT = 6,
- GDB_SIGNAL_ALRM = 14,
- GDB_SIGNAL_IO = 23,
- GDB_SIGNAL_XCPU = 24,
- GDB_SIGNAL_UNKNOWN = 143
-};
-
-#ifdef CONFIG_USER_ONLY
-
-/* Map target signal numbers to GDB protocol signal numbers and vice
- * versa. For user emulation's currently supported systems, we can
- * assume most signals are defined.
- */
-
-static int gdb_signal_table[] = {
- 0,
- TARGET_SIGHUP,
- TARGET_SIGINT,
- TARGET_SIGQUIT,
- TARGET_SIGILL,
- TARGET_SIGTRAP,
- TARGET_SIGABRT,
- -1, /* SIGEMT */
- TARGET_SIGFPE,
- TARGET_SIGKILL,
- TARGET_SIGBUS,
- TARGET_SIGSEGV,
- TARGET_SIGSYS,
- TARGET_SIGPIPE,
- TARGET_SIGALRM,
- TARGET_SIGTERM,
- TARGET_SIGURG,
- TARGET_SIGSTOP,
- TARGET_SIGTSTP,
- TARGET_SIGCONT,
- TARGET_SIGCHLD,
- TARGET_SIGTTIN,
- TARGET_SIGTTOU,
- TARGET_SIGIO,
- TARGET_SIGXCPU,
- TARGET_SIGXFSZ,
- TARGET_SIGVTALRM,
- TARGET_SIGPROF,
- TARGET_SIGWINCH,
- -1, /* SIGLOST */
- TARGET_SIGUSR1,
- TARGET_SIGUSR2,
-#ifdef TARGET_SIGPWR
- TARGET_SIGPWR,
-#else
- -1,
-#endif
- -1, /* SIGPOLL */
- -1,
- -1,
- -1,
- -1,
- -1,
- -1,
- -1,
- -1,
- -1,
- -1,
- -1,
-#ifdef __SIGRTMIN
- __SIGRTMIN + 1,
- __SIGRTMIN + 2,
- __SIGRTMIN + 3,
- __SIGRTMIN + 4,
- __SIGRTMIN + 5,
- __SIGRTMIN + 6,
- __SIGRTMIN + 7,
- __SIGRTMIN + 8,
- __SIGRTMIN + 9,
- __SIGRTMIN + 10,
- __SIGRTMIN + 11,
- __SIGRTMIN + 12,
- __SIGRTMIN + 13,
- __SIGRTMIN + 14,
- __SIGRTMIN + 15,
- __SIGRTMIN + 16,
- __SIGRTMIN + 17,
- __SIGRTMIN + 18,
- __SIGRTMIN + 19,
- __SIGRTMIN + 20,
- __SIGRTMIN + 21,
- __SIGRTMIN + 22,
- __SIGRTMIN + 23,
- __SIGRTMIN + 24,
- __SIGRTMIN + 25,
- __SIGRTMIN + 26,
- __SIGRTMIN + 27,
- __SIGRTMIN + 28,
- __SIGRTMIN + 29,
- __SIGRTMIN + 30,
- __SIGRTMIN + 31,
- -1, /* SIGCANCEL */
- __SIGRTMIN,
- __SIGRTMIN + 32,
- __SIGRTMIN + 33,
- __SIGRTMIN + 34,
- __SIGRTMIN + 35,
- __SIGRTMIN + 36,
- __SIGRTMIN + 37,
- __SIGRTMIN + 38,
- __SIGRTMIN + 39,
- __SIGRTMIN + 40,
- __SIGRTMIN + 41,
- __SIGRTMIN + 42,
- __SIGRTMIN + 43,
- __SIGRTMIN + 44,
- __SIGRTMIN + 45,
- __SIGRTMIN + 46,
- __SIGRTMIN + 47,
- __SIGRTMIN + 48,
- __SIGRTMIN + 49,
- __SIGRTMIN + 50,
- __SIGRTMIN + 51,
- __SIGRTMIN + 52,
- __SIGRTMIN + 53,
- __SIGRTMIN + 54,
- __SIGRTMIN + 55,
- __SIGRTMIN + 56,
- __SIGRTMIN + 57,
- __SIGRTMIN + 58,
- __SIGRTMIN + 59,
- __SIGRTMIN + 60,
- __SIGRTMIN + 61,
- __SIGRTMIN + 62,
- __SIGRTMIN + 63,
- __SIGRTMIN + 64,
- __SIGRTMIN + 65,
- __SIGRTMIN + 66,
- __SIGRTMIN + 67,
- __SIGRTMIN + 68,
- __SIGRTMIN + 69,
- __SIGRTMIN + 70,
- __SIGRTMIN + 71,
- __SIGRTMIN + 72,
- __SIGRTMIN + 73,
- __SIGRTMIN + 74,
- __SIGRTMIN + 75,
- __SIGRTMIN + 76,
- __SIGRTMIN + 77,
- __SIGRTMIN + 78,
- __SIGRTMIN + 79,
- __SIGRTMIN + 80,
- __SIGRTMIN + 81,
- __SIGRTMIN + 82,
- __SIGRTMIN + 83,
- __SIGRTMIN + 84,
- __SIGRTMIN + 85,
- __SIGRTMIN + 86,
- __SIGRTMIN + 87,
- __SIGRTMIN + 88,
- __SIGRTMIN + 89,
- __SIGRTMIN + 90,
- __SIGRTMIN + 91,
- __SIGRTMIN + 92,
- __SIGRTMIN + 93,
- __SIGRTMIN + 94,
- __SIGRTMIN + 95,
- -1, /* SIGINFO */
- -1, /* UNKNOWN */
- -1, /* DEFAULT */
- -1,
- -1,
- -1,
- -1,
- -1,
- -1
-#endif
-};
-#else
-/* In system mode we only need SIGINT and SIGTRAP; other signals
- are not yet supported. */
-
-enum {
- TARGET_SIGINT = 2,
- TARGET_SIGTRAP = 5
-};
-
-static int gdb_signal_table[] = {
- -1,
- -1,
- TARGET_SIGINT,
- -1,
- -1,
- TARGET_SIGTRAP
-};
-#endif
-
-#ifdef CONFIG_USER_ONLY
-static int target_signal_to_gdb (int sig)
-{
- int i;
- for (i = 0; i < ARRAY_SIZE (gdb_signal_table); i++)
- if (gdb_signal_table[i] == sig)
- return i;
- return GDB_SIGNAL_UNKNOWN;
-}
-#endif
-
-static int gdb_signal_to_target (int sig)
-{
- if (sig < ARRAY_SIZE (gdb_signal_table))
- return gdb_signal_table[sig];
- else
- return -1;
-}
-
typedef struct GDBRegisterState {
int base_reg;
int num_regs;
@@ -325,56 +53,9 @@
struct GDBRegisterState *next;
} GDBRegisterState;
-typedef struct GDBProcess {
- uint32_t pid;
- bool attached;
+GDBState gdbserver_state;
- char target_xml[1024];
-} GDBProcess;
-
-enum RSState {
- RS_INACTIVE,
- RS_IDLE,
- RS_GETLINE,
- RS_GETLINE_ESC,
- RS_GETLINE_RLE,
- RS_CHKSUM1,
- RS_CHKSUM2,
-};
-typedef struct GDBState {
- bool init; /* have we been initialised? */
- CPUState *c_cpu; /* current CPU for step/continue ops */
- CPUState *g_cpu; /* current CPU for other ops */
- CPUState *query_cpu; /* for q{f|s}ThreadInfo */
- enum RSState state; /* parsing state */
- char line_buf[MAX_PACKET_LENGTH];
- int line_buf_index;
- int line_sum; /* running checksum */
- int line_csum; /* checksum at the end of the packet */
- GByteArray *last_packet;
- int signal;
-#ifdef CONFIG_USER_ONLY
- int fd;
- char *socket_path;
- int running_state;
-#else
- CharBackend chr;
- Chardev *mon_chr;
-#endif
- bool multiprocess;
- GDBProcess *processes;
- int process_num;
- char syscall_buf[256];
- gdb_syscall_complete_cb current_syscall_cb;
- GString *str_buf;
- GByteArray *mem_buf;
- int sstep_flags;
- int supported_sstep_flags;
-} GDBState;
-
-static GDBState gdbserver_state;
-
-static void init_gdbserver_state(void)
+void gdb_init_gdbserver_state(void)
{
g_assert(!gdbserver_state.init);
memset(&gdbserver_state, 0, sizeof(GDBState));
@@ -393,211 +74,10 @@
gdbserver_state.sstep_flags &= gdbserver_state.supported_sstep_flags;
}
-#ifndef CONFIG_USER_ONLY
-static void reset_gdbserver_state(void)
-{
- g_free(gdbserver_state.processes);
- gdbserver_state.processes = NULL;
- gdbserver_state.process_num = 0;
-}
-#endif
-
bool gdb_has_xml;
-#ifdef CONFIG_USER_ONLY
-
-static int get_char(void)
-{
- uint8_t ch;
- int ret;
-
- for(;;) {
- ret = recv(gdbserver_state.fd, &ch, 1, 0);
- if (ret < 0) {
- if (errno == ECONNRESET)
- gdbserver_state.fd = -1;
- if (errno != EINTR)
- return -1;
- } else if (ret == 0) {
- close(gdbserver_state.fd);
- gdbserver_state.fd = -1;
- return -1;
- } else {
- break;
- }
- }
- return ch;
-}
-#endif
-
-/*
- * Return true if there is a GDB currently connected to the stub
- * and attached to a CPU
- */
-static bool gdb_attached(void)
-{
- return gdbserver_state.init && gdbserver_state.c_cpu;
-}
-
-static enum {
- GDB_SYS_UNKNOWN,
- GDB_SYS_ENABLED,
- GDB_SYS_DISABLED,
-} gdb_syscall_mode;
-
-/* Decide if either remote gdb syscalls or native file IO should be used. */
-int use_gdb_syscalls(void)
-{
- SemihostingTarget target = semihosting_get_target();
- if (target == SEMIHOSTING_TARGET_NATIVE) {
- /* -semihosting-config target=native */
- return false;
- } else if (target == SEMIHOSTING_TARGET_GDB) {
- /* -semihosting-config target=gdb */
- return true;
- }
-
- /* -semihosting-config target=auto */
- /* On the first call check if gdb is connected and remember. */
- if (gdb_syscall_mode == GDB_SYS_UNKNOWN) {
- gdb_syscall_mode = gdb_attached() ? GDB_SYS_ENABLED : GDB_SYS_DISABLED;
- }
- return gdb_syscall_mode == GDB_SYS_ENABLED;
-}
-
-static bool stub_can_reverse(void)
-{
-#ifdef CONFIG_USER_ONLY
- return false;
-#else
- return replay_mode == REPLAY_MODE_PLAY;
-#endif
-}
-
-/* Resume execution. */
-static inline void gdb_continue(void)
-{
-
-#ifdef CONFIG_USER_ONLY
- gdbserver_state.running_state = 1;
- trace_gdbstub_op_continue();
-#else
- if (!runstate_needs_reset()) {
- trace_gdbstub_op_continue();
- vm_start();
- }
-#endif
-}
-
-/*
- * Resume execution, per CPU actions. For user-mode emulation it's
- * equivalent to gdb_continue.
- */
-static int gdb_continue_partial(char *newstates)
-{
- CPUState *cpu;
- int res = 0;
-#ifdef CONFIG_USER_ONLY
- /*
- * This is not exactly accurate, but it's an improvement compared to the
- * previous situation, where only one CPU would be single-stepped.
- */
- CPU_FOREACH(cpu) {
- if (newstates[cpu->cpu_index] == 's') {
- trace_gdbstub_op_stepping(cpu->cpu_index);
- cpu_single_step(cpu, gdbserver_state.sstep_flags);
- }
- }
- gdbserver_state.running_state = 1;
-#else
- int flag = 0;
-
- if (!runstate_needs_reset()) {
- bool step_requested = false;
- CPU_FOREACH(cpu) {
- if (newstates[cpu->cpu_index] == 's') {
- step_requested = true;
- break;
- }
- }
-
- if (vm_prepare_start(step_requested)) {
- return 0;
- }
-
- CPU_FOREACH(cpu) {
- switch (newstates[cpu->cpu_index]) {
- case 0:
- case 1:
- break; /* nothing to do here */
- case 's':
- trace_gdbstub_op_stepping(cpu->cpu_index);
- cpu_single_step(cpu, gdbserver_state.sstep_flags);
- cpu_resume(cpu);
- flag = 1;
- break;
- case 'c':
- trace_gdbstub_op_continue_cpu(cpu->cpu_index);
- cpu_resume(cpu);
- flag = 1;
- break;
- default:
- res = -1;
- break;
- }
- }
- }
- if (flag) {
- qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true);
- }
-#endif
- return res;
-}
-
-static void put_buffer(const uint8_t *buf, int len)
-{
-#ifdef CONFIG_USER_ONLY
- int ret;
-
- while (len > 0) {
- ret = send(gdbserver_state.fd, buf, len, 0);
- if (ret < 0) {
- if (errno != EINTR)
- return;
- } else {
- buf += ret;
- len -= ret;
- }
- }
-#else
- /* XXX this blocks entire thread. Rewrite to use
- * qemu_chr_fe_write and background I/O callbacks */
- qemu_chr_fe_write_all(&gdbserver_state.chr, buf, len);
-#endif
-}
-
-static inline int fromhex(int v)
-{
- if (v >= '0' && v <= '9')
- return v - '0';
- else if (v >= 'A' && v <= 'F')
- return v - 'A' + 10;
- else if (v >= 'a' && v <= 'f')
- return v - 'a' + 10;
- else
- return 0;
-}
-
-static inline int tohex(int v)
-{
- if (v < 10)
- return v + '0';
- else
- return v - 10 + 'a';
-}
-
/* writes 2*len+1 bytes in buf */
-static void memtohex(GString *buf, const uint8_t *mem, int len)
+void gdb_memtohex(GString *buf, const uint8_t *mem, int len)
{
int i, c;
for(i = 0; i < len; i++) {
@@ -608,7 +88,7 @@
g_string_append_c(buf, '\0');
}
-static void hextomem(GByteArray *mem, const char *buf, int len)
+void gdb_hextomem(GByteArray *mem, const char *buf, int len)
{
int i;
@@ -653,7 +133,7 @@
}
/* return -1 if error, 0 if OK */
-static int put_packet_binary(const char *buf, int len, bool dump)
+int gdb_put_packet_binary(const char *buf, int len, bool dump)
{
int csum, i;
uint8_t footer[3];
@@ -677,37 +157,31 @@
footer[2] = tohex((csum) & 0xf);
g_byte_array_append(gdbserver_state.last_packet, footer, 3);
- put_buffer(gdbserver_state.last_packet->data,
+ gdb_put_buffer(gdbserver_state.last_packet->data,
gdbserver_state.last_packet->len);
-#ifdef CONFIG_USER_ONLY
- i = get_char();
- if (i < 0)
- return -1;
- if (i == '+')
+ if (gdb_got_immediate_ack()) {
break;
-#else
- break;
-#endif
+ }
}
return 0;
}
/* return -1 if error, 0 if OK */
-static int put_packet(const char *buf)
+int gdb_put_packet(const char *buf)
{
trace_gdbstub_io_reply(buf);
- return put_packet_binary(buf, strlen(buf), false);
+ return gdb_put_packet_binary(buf, strlen(buf), false);
}
-static void put_strbuf(void)
+void gdb_put_strbuf(void)
{
- put_packet(gdbserver_state.str_buf->str);
+ gdb_put_packet(gdbserver_state.str_buf->str);
}
/* Encode data using the encoding for 'x' packets. */
-static void memtox(GString *buf, const char *mem, int len)
+void gdb_memtox(GString *buf, const char *mem, int len)
{
char c;
@@ -764,7 +238,7 @@
CPUState *cpu;
CPU_FOREACH(cpu) {
- if (cpu_gdb_index(cpu) == thread_id) {
+ if (gdb_get_cpu_index(cpu) == thread_id) {
return cpu;
}
}
@@ -818,7 +292,7 @@
}
/* Return the first attached cpu */
-static CPUState *gdb_first_attached_cpu(void)
+CPUState *gdb_first_attached_cpu(void)
{
CPUState *cpu = first_cpu;
GDBProcess *process = gdb_get_cpu_process(cpu);
@@ -1024,7 +498,7 @@
}
-static void gdb_set_cpu_pc(target_ulong pc)
+static void gdb_set_cpu_pc(vaddr pc)
{
CPUState *cpu = gdbserver_state.c_cpu;
@@ -1032,23 +506,16 @@
cpu_set_pc(cpu, pc);
}
-static void gdb_append_thread_id(CPUState *cpu, GString *buf)
+void gdb_append_thread_id(CPUState *cpu, GString *buf)
{
if (gdbserver_state.multiprocess) {
g_string_append_printf(buf, "p%02x.%02x",
- gdb_get_cpu_pid(cpu), cpu_gdb_index(cpu));
+ gdb_get_cpu_pid(cpu), gdb_get_cpu_index(cpu));
} else {
- g_string_append_printf(buf, "%02x", cpu_gdb_index(cpu));
+ g_string_append_printf(buf, "%02x", gdb_get_cpu_index(cpu));
}
}
-typedef enum GDBThreadIdKind {
- GDB_ONE_THREAD = 0,
- GDB_ALL_THREADS, /* One process, all threads */
- GDB_ALL_PROCESSES,
- GDB_READ_THREAD_ERR
-} GDBThreadIdKind;
-
static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf,
uint32_t *pid, uint32_t *tid)
{
@@ -1111,16 +578,7 @@
GDBProcess *process;
CPUState *cpu;
GDBThreadIdKind kind;
-#ifdef CONFIG_USER_ONLY
- int max_cpus = 1; /* global variable max_cpus exists only in system mode */
-
- CPU_FOREACH(cpu) {
- max_cpus = max_cpus <= cpu->cpu_index ? cpu->cpu_index + 1 : max_cpus;
- }
-#else
- MachineState *ms = MACHINE(qdev_get_machine());
- unsigned int max_cpus = ms->smp.max_cpus;
-#endif
+ unsigned int max_cpus = gdb_get_max_cpus();
/* uninitialised CPUs stay 0 */
newstates = g_new0(char, max_cpus);
@@ -1229,20 +687,6 @@
return res;
}
-typedef union GdbCmdVariant {
- const char *data;
- uint8_t opcode;
- unsigned long val_ul;
- unsigned long long val_ull;
- struct {
- GDBThreadIdKind kind;
- uint32_t pid;
- uint32_t tid;
- } thread_id;
-} GdbCmdVariant;
-
-#define get_param(p, i) (&g_array_index(p, GdbCmdVariant, i))
-
static const char *cmd_next_param(const char *param, const char delimiter)
{
static const char all_delimiters[] = ",;:=";
@@ -1409,7 +853,7 @@
/* In case there was an error during the command parsing we must
* send a NULL packet to indicate the command is not supported */
if (process_string_cmd(NULL, data, cmd, 1)) {
- put_packet("");
+ gdb_put_packet("");
}
}
@@ -1420,7 +864,7 @@
if (gdbserver_state.multiprocess) {
if (!params->len) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
@@ -1441,10 +885,10 @@
if (!gdbserver_state.c_cpu) {
/* No more process attached */
- gdb_syscall_mode = GDB_SYS_DISABLED;
+ gdb_disable_syscalls();
gdb_continue();
}
- put_packet("OK");
+ gdb_put_packet("OK");
}
static void handle_thread_alive(GArray *params, void *user_ctx)
@@ -1452,23 +896,23 @@
CPUState *cpu;
if (!params->len) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
if (get_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
cpu = gdb_get_cpu(get_param(params, 0)->thread_id.pid,
get_param(params, 0)->thread_id.tid);
if (!cpu) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
- put_packet("OK");
+ gdb_put_packet("OK");
}
static void handle_continue(GArray *params, void *user_ctx)
@@ -1505,24 +949,24 @@
CPUState *cpu;
if (params->len != 2) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
if (get_param(params, 1)->thread_id.kind == GDB_READ_THREAD_ERR) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
if (get_param(params, 1)->thread_id.kind != GDB_ONE_THREAD) {
- put_packet("OK");
+ gdb_put_packet("OK");
return;
}
cpu = gdb_get_cpu(get_param(params, 1)->thread_id.pid,
get_param(params, 1)->thread_id.tid);
if (!cpu) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
@@ -1533,14 +977,14 @@
switch (get_param(params, 0)->opcode) {
case 'c':
gdbserver_state.c_cpu = cpu;
- put_packet("OK");
+ gdb_put_packet("OK");
break;
case 'g':
gdbserver_state.g_cpu = cpu;
- put_packet("OK");
+ gdb_put_packet("OK");
break;
default:
- put_packet("E22");
+ gdb_put_packet("E22");
break;
}
}
@@ -1550,7 +994,7 @@
int res;
if (params->len != 3) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
@@ -1559,14 +1003,14 @@
get_param(params, 1)->val_ull,
get_param(params, 2)->val_ull);
if (res >= 0) {
- put_packet("OK");
+ gdb_put_packet("OK");
return;
} else if (res == -ENOSYS) {
- put_packet("");
+ gdb_put_packet("");
return;
}
- put_packet("E22");
+ gdb_put_packet("E22");
}
static void handle_remove_bp(GArray *params, void *user_ctx)
@@ -1574,7 +1018,7 @@
int res;
if (params->len != 3) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
@@ -1583,14 +1027,14 @@
get_param(params, 1)->val_ull,
get_param(params, 2)->val_ull);
if (res >= 0) {
- put_packet("OK");
+ gdb_put_packet("OK");
return;
} else if (res == -ENOSYS) {
- put_packet("");
+ gdb_put_packet("");
return;
}
- put_packet("E22");
+ gdb_put_packet("E22");
}
/*
@@ -1609,20 +1053,20 @@
int reg_size;
if (!gdb_has_xml) {
- put_packet("");
+ gdb_put_packet("");
return;
}
if (params->len != 2) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
reg_size = strlen(get_param(params, 1)->data) / 2;
- hextomem(gdbserver_state.mem_buf, get_param(params, 1)->data, reg_size);
+ gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 1)->data, reg_size);
gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data,
get_param(params, 0)->val_ull);
- put_packet("OK");
+ gdb_put_packet("OK");
}
static void handle_get_reg(GArray *params, void *user_ctx)
@@ -1630,12 +1074,12 @@
int reg_size;
if (!gdb_has_xml) {
- put_packet("");
+ gdb_put_packet("");
return;
}
if (!params->len) {
- put_packet("E14");
+ gdb_put_packet("E14");
return;
}
@@ -1643,75 +1087,77 @@
gdbserver_state.mem_buf,
get_param(params, 0)->val_ull);
if (!reg_size) {
- put_packet("E14");
+ gdb_put_packet("E14");
return;
} else {
g_byte_array_set_size(gdbserver_state.mem_buf, reg_size);
}
- memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, reg_size);
- put_strbuf();
+ gdb_memtohex(gdbserver_state.str_buf,
+ gdbserver_state.mem_buf->data, reg_size);
+ gdb_put_strbuf();
}
static void handle_write_mem(GArray *params, void *user_ctx)
{
if (params->len != 3) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
- /* hextomem() reads 2*len bytes */
+ /* gdb_hextomem() reads 2*len bytes */
if (get_param(params, 1)->val_ull >
strlen(get_param(params, 2)->data) / 2) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
- hextomem(gdbserver_state.mem_buf, get_param(params, 2)->data,
- get_param(params, 1)->val_ull);
- if (target_memory_rw_debug(gdbserver_state.g_cpu,
- get_param(params, 0)->val_ull,
- gdbserver_state.mem_buf->data,
- gdbserver_state.mem_buf->len, true)) {
- put_packet("E14");
+ gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 2)->data,
+ get_param(params, 1)->val_ull);
+ if (gdb_target_memory_rw_debug(gdbserver_state.g_cpu,
+ get_param(params, 0)->val_ull,
+ gdbserver_state.mem_buf->data,
+ gdbserver_state.mem_buf->len, true)) {
+ gdb_put_packet("E14");
return;
}
- put_packet("OK");
+ gdb_put_packet("OK");
}
static void handle_read_mem(GArray *params, void *user_ctx)
{
if (params->len != 2) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
- /* memtohex() doubles the required space */
+ /* gdb_memtohex() doubles the required space */
if (get_param(params, 1)->val_ull > MAX_PACKET_LENGTH / 2) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
g_byte_array_set_size(gdbserver_state.mem_buf,
get_param(params, 1)->val_ull);
- if (target_memory_rw_debug(gdbserver_state.g_cpu,
- get_param(params, 0)->val_ull,
- gdbserver_state.mem_buf->data,
- gdbserver_state.mem_buf->len, false)) {
- put_packet("E14");
+ if (gdb_target_memory_rw_debug(gdbserver_state.g_cpu,
+ get_param(params, 0)->val_ull,
+ gdbserver_state.mem_buf->data,
+ gdbserver_state.mem_buf->len, false)) {
+ gdb_put_packet("E14");
return;
}
- memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data,
+ gdb_memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data,
gdbserver_state.mem_buf->len);
- put_strbuf();
+ gdb_put_strbuf();
}
static void handle_write_all_regs(GArray *params, void *user_ctx)
{
- target_ulong addr, len;
+ int reg_id;
+ size_t len;
uint8_t *registers;
int reg_size;
@@ -1721,94 +1167,42 @@
cpu_synchronize_state(gdbserver_state.g_cpu);
len = strlen(get_param(params, 0)->data) / 2;
- hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len);
+ gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len);
registers = gdbserver_state.mem_buf->data;
- for (addr = 0; addr < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0;
- addr++) {
- reg_size = gdb_write_register(gdbserver_state.g_cpu, registers, addr);
+ for (reg_id = 0;
+ reg_id < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0;
+ reg_id++) {
+ reg_size = gdb_write_register(gdbserver_state.g_cpu, registers, reg_id);
len -= reg_size;
registers += reg_size;
}
- put_packet("OK");
+ gdb_put_packet("OK");
}
static void handle_read_all_regs(GArray *params, void *user_ctx)
{
- target_ulong addr, len;
+ int reg_id;
+ size_t len;
cpu_synchronize_state(gdbserver_state.g_cpu);
g_byte_array_set_size(gdbserver_state.mem_buf, 0);
len = 0;
- for (addr = 0; addr < gdbserver_state.g_cpu->gdb_num_g_regs; addr++) {
+ for (reg_id = 0; reg_id < gdbserver_state.g_cpu->gdb_num_g_regs; reg_id++) {
len += gdb_read_register(gdbserver_state.g_cpu,
gdbserver_state.mem_buf,
- addr);
+ reg_id);
}
g_assert(len == gdbserver_state.mem_buf->len);
- memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, len);
- put_strbuf();
+ gdb_memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, len);
+ gdb_put_strbuf();
}
-static void handle_file_io(GArray *params, void *user_ctx)
-{
- if (params->len >= 1 && gdbserver_state.current_syscall_cb) {
- uint64_t ret;
- int err;
-
- ret = get_param(params, 0)->val_ull;
- if (params->len >= 2) {
- err = get_param(params, 1)->val_ull;
- } else {
- err = 0;
- }
-
- /* Convert GDB error numbers back to host error numbers. */
-#define E(X) case GDB_E##X: err = E##X; break
- switch (err) {
- case 0:
- break;
- E(PERM);
- E(NOENT);
- E(INTR);
- E(BADF);
- E(ACCES);
- E(FAULT);
- E(BUSY);
- E(EXIST);
- E(NODEV);
- E(NOTDIR);
- E(ISDIR);
- E(INVAL);
- E(NFILE);
- E(MFILE);
- E(FBIG);
- E(NOSPC);
- E(SPIPE);
- E(ROFS);
- E(NAMETOOLONG);
- default:
- err = EINVAL;
- break;
- }
-#undef E
-
- gdbserver_state.current_syscall_cb(gdbserver_state.c_cpu, ret, err);
- gdbserver_state.current_syscall_cb = NULL;
- }
-
- if (params->len >= 3 && get_param(params, 2)->opcode == (uint8_t)'C') {
- put_packet("T02");
- return;
- }
-
- gdb_continue();
-}
static void handle_step(GArray *params, void *user_ctx)
{
if (params->len) {
- gdb_set_cpu_pc((target_ulong)get_param(params, 0)->val_ull);
+ gdb_set_cpu_pc(get_param(params, 0)->val_ull);
}
cpu_single_step(gdbserver_state.c_cpu, gdbserver_state.sstep_flags);
@@ -1817,8 +1211,8 @@
static void handle_backward(GArray *params, void *user_ctx)
{
- if (!stub_can_reverse()) {
- put_packet("E22");
+ if (!gdb_can_reverse()) {
+ gdb_put_packet("E22");
}
if (params->len == 1) {
switch (get_param(params, 0)->opcode) {
@@ -1826,26 +1220,26 @@
if (replay_reverse_step()) {
gdb_continue();
} else {
- put_packet("E14");
+ gdb_put_packet("E14");
}
return;
case 'c':
if (replay_reverse_continue()) {
gdb_continue();
} else {
- put_packet("E14");
+ gdb_put_packet("E14");
}
return;
}
}
/* Default invalid command */
- put_packet("");
+ gdb_put_packet("");
}
static void handle_v_cont_query(GArray *params, void *user_ctx)
{
- put_packet("vCont;c;C;s;S");
+ gdb_put_packet("vCont;c;C;s;S");
}
static void handle_v_cont(GArray *params, void *user_ctx)
@@ -1858,9 +1252,9 @@
res = gdb_handle_vcont(get_param(params, 0)->data);
if ((res == -EINVAL) || (res == -ERANGE)) {
- put_packet("E22");
+ gdb_put_packet("E22");
} else if (res) {
- put_packet("");
+ gdb_put_packet("");
}
}
@@ -1892,13 +1286,13 @@
gdb_append_thread_id(cpu, gdbserver_state.str_buf);
g_string_append_c(gdbserver_state.str_buf, ';');
cleanup:
- put_strbuf();
+ gdb_put_strbuf();
}
static void handle_v_kill(GArray *params, void *user_ctx)
{
/* Kill the target */
- put_packet("OK");
+ gdb_put_packet("OK");
error_report("QEMU: Terminated via GDBstub");
gdb_exit(0);
exit(0);
@@ -1939,7 +1333,7 @@
if (process_string_cmd(NULL, get_param(params, 0)->data,
gdb_v_commands_table,
ARRAY_SIZE(gdb_v_commands_table))) {
- put_packet("");
+ gdb_put_packet("");
}
}
@@ -1957,7 +1351,7 @@
SSTEP_NOTIMER);
}
- put_strbuf();
+ gdb_put_strbuf();
}
static void handle_set_qemu_sstep(GArray *params, void *user_ctx)
@@ -1971,19 +1365,19 @@
new_sstep_flags = get_param(params, 0)->val_ul;
if (new_sstep_flags & ~gdbserver_state.supported_sstep_flags) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
gdbserver_state.sstep_flags = new_sstep_flags;
- put_packet("OK");
+ gdb_put_packet("OK");
}
static void handle_query_qemu_sstep(GArray *params, void *user_ctx)
{
g_string_printf(gdbserver_state.str_buf, "0x%x",
gdbserver_state.sstep_flags);
- put_strbuf();
+ gdb_put_strbuf();
}
static void handle_query_curr_tid(GArray *params, void *user_ctx)
@@ -2000,19 +1394,19 @@
cpu = get_first_cpu_in_process(process);
g_string_assign(gdbserver_state.str_buf, "QC");
gdb_append_thread_id(cpu, gdbserver_state.str_buf);
- put_strbuf();
+ gdb_put_strbuf();
}
static void handle_query_threads(GArray *params, void *user_ctx)
{
if (!gdbserver_state.query_cpu) {
- put_packet("l");
+ gdb_put_packet("l");
return;
}
g_string_assign(gdbserver_state.str_buf, "m");
gdb_append_thread_id(gdbserver_state.query_cpu, gdbserver_state.str_buf);
- put_strbuf();
+ gdb_put_strbuf();
gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu);
}
@@ -2029,7 +1423,7 @@
if (!params->len ||
get_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
@@ -2054,52 +1448,10 @@
cpu->halted ? "halted " : "running");
}
trace_gdbstub_op_extra_info(rs->str);
- memtohex(gdbserver_state.str_buf, (uint8_t *)rs->str, rs->len);
- put_strbuf();
+ gdb_memtohex(gdbserver_state.str_buf, (uint8_t *)rs->str, rs->len);
+ gdb_put_strbuf();
}
-#ifdef CONFIG_USER_ONLY
-static void handle_query_offsets(GArray *params, void *user_ctx)
-{
- TaskState *ts;
-
- ts = gdbserver_state.c_cpu->opaque;
- g_string_printf(gdbserver_state.str_buf,
- "Text=" TARGET_ABI_FMT_lx
- ";Data=" TARGET_ABI_FMT_lx
- ";Bss=" TARGET_ABI_FMT_lx,
- ts->info->code_offset,
- ts->info->data_offset,
- ts->info->data_offset);
- put_strbuf();
-}
-#else
-static void handle_query_rcmd(GArray *params, void *user_ctx)
-{
- const guint8 zero = 0;
- int len;
-
- if (!params->len) {
- put_packet("E22");
- return;
- }
-
- len = strlen(get_param(params, 0)->data);
- if (len % 2) {
- put_packet("E01");
- return;
- }
-
- g_assert(gdbserver_state.mem_buf->len == 0);
- len = len / 2;
- hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len);
- g_byte_array_append(gdbserver_state.mem_buf, &zero, 1);
- qemu_chr_be_write(gdbserver_state.mon_chr, gdbserver_state.mem_buf->data,
- gdbserver_state.mem_buf->len);
- put_packet("OK");
-}
-#endif
-
static void handle_query_supported(GArray *params, void *user_ctx)
{
CPUClass *cc;
@@ -2110,7 +1462,7 @@
g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+");
}
- if (stub_can_reverse()) {
+ if (gdb_can_reverse()) {
g_string_append(gdbserver_state.str_buf,
";ReverseStep+;ReverseContinue+");
}
@@ -2127,7 +1479,7 @@
}
g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+");
- put_strbuf();
+ gdb_put_strbuf();
}
static void handle_query_xfer_features(GArray *params, void *user_ctx)
@@ -2139,14 +1491,14 @@
const char *p;
if (params->len < 3) {
- put_packet("E22");
+ gdb_put_packet("E22");
return;
}
process = gdb_get_cpu_process(gdbserver_state.g_cpu);
cc = CPU_GET_CLASS(gdbserver_state.g_cpu);
if (!cc->gdb_core_xml_file) {
- put_packet("");
+ gdb_put_packet("");
return;
}
@@ -2154,7 +1506,7 @@
p = get_param(params, 0)->data;
xml = get_feature_xml(p, &p, process);
if (!xml) {
- put_packet("E00");
+ gdb_put_packet("E00");
return;
}
@@ -2162,7 +1514,7 @@
len = get_param(params, 2)->val_ul;
total_len = strlen(xml);
if (addr > total_len) {
- put_packet("E00");
+ gdb_put_packet("E00");
return;
}
@@ -2172,101 +1524,25 @@
if (len < total_len - addr) {
g_string_assign(gdbserver_state.str_buf, "m");
- memtox(gdbserver_state.str_buf, xml + addr, len);
+ gdb_memtox(gdbserver_state.str_buf, xml + addr, len);
} else {
g_string_assign(gdbserver_state.str_buf, "l");
- memtox(gdbserver_state.str_buf, xml + addr, total_len - addr);
+ gdb_memtox(gdbserver_state.str_buf, xml + addr, total_len - addr);
}
- put_packet_binary(gdbserver_state.str_buf->str,
+ gdb_put_packet_binary(gdbserver_state.str_buf->str,
gdbserver_state.str_buf->len, true);
}
-#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX_USER)
-static void handle_query_xfer_auxv(GArray *params, void *user_ctx)
-{
- TaskState *ts;
- unsigned long offset, len, saved_auxv, auxv_len;
-
- if (params->len < 2) {
- put_packet("E22");
- return;
- }
-
- offset = get_param(params, 0)->val_ul;
- len = get_param(params, 1)->val_ul;
- ts = gdbserver_state.c_cpu->opaque;
- saved_auxv = ts->info->saved_auxv;
- auxv_len = ts->info->auxv_len;
-
- if (offset >= auxv_len) {
- put_packet("E00");
- return;
- }
-
- if (len > (MAX_PACKET_LENGTH - 5) / 2) {
- len = (MAX_PACKET_LENGTH - 5) / 2;
- }
-
- if (len < auxv_len - offset) {
- g_string_assign(gdbserver_state.str_buf, "m");
- } else {
- g_string_assign(gdbserver_state.str_buf, "l");
- len = auxv_len - offset;
- }
-
- g_byte_array_set_size(gdbserver_state.mem_buf, len);
- if (target_memory_rw_debug(gdbserver_state.g_cpu, saved_auxv + offset,
- gdbserver_state.mem_buf->data, len, false)) {
- put_packet("E14");
- return;
- }
-
- memtox(gdbserver_state.str_buf,
- (const char *)gdbserver_state.mem_buf->data, len);
- put_packet_binary(gdbserver_state.str_buf->str,
- gdbserver_state.str_buf->len, true);
-}
-#endif
-
-static void handle_query_attached(GArray *params, void *user_ctx)
-{
- put_packet(GDB_ATTACHED);
-}
-
static void handle_query_qemu_supported(GArray *params, void *user_ctx)
{
g_string_printf(gdbserver_state.str_buf, "sstepbits;sstep");
#ifndef CONFIG_USER_ONLY
g_string_append(gdbserver_state.str_buf, ";PhyMemMode");
#endif
- put_strbuf();
+ gdb_put_strbuf();
}
-#ifndef CONFIG_USER_ONLY
-static void handle_query_qemu_phy_mem_mode(GArray *params,
- void *user_ctx)
-{
- g_string_printf(gdbserver_state.str_buf, "%d", phy_memory_mode);
- put_strbuf();
-}
-
-static void handle_set_qemu_phy_mem_mode(GArray *params, void *user_ctx)
-{
- if (!params->len) {
- put_packet("E22");
- return;
- }
-
- if (!get_param(params, 0)->val_ul) {
- phy_memory_mode = 0;
- } else {
- phy_memory_mode = 1;
- }
- put_packet("OK");
-}
-#endif
-
static const GdbCmdParseEntry gdb_gen_query_set_common_table[] = {
/* Order is important if has same prefix */
{
@@ -2306,12 +1582,12 @@
},
#ifdef CONFIG_USER_ONLY
{
- .handler = handle_query_offsets,
+ .handler = gdb_handle_query_offsets,
.cmd = "Offsets",
},
#else
{
- .handler = handle_query_rcmd,
+ .handler = gdb_handle_query_rcmd,
.cmd = "Rcmd,",
.cmd_startswith = 1,
.schema = "s0"
@@ -2334,21 +1610,21 @@
.cmd_startswith = 1,
.schema = "s:l,l0"
},
-#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX_USER)
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX)
{
- .handler = handle_query_xfer_auxv,
+ .handler = gdb_handle_query_xfer_auxv,
.cmd = "Xfer:auxv:read::",
.cmd_startswith = 1,
.schema = "l,l0"
},
#endif
{
- .handler = handle_query_attached,
+ .handler = gdb_handle_query_attached,
.cmd = "Attached:",
.cmd_startswith = 1
},
{
- .handler = handle_query_attached,
+ .handler = gdb_handle_query_attached,
.cmd = "Attached",
},
{
@@ -2357,7 +1633,7 @@
},
#ifndef CONFIG_USER_ONLY
{
- .handler = handle_query_qemu_phy_mem_mode,
+ .handler = gdb_handle_query_qemu_phy_mem_mode,
.cmd = "qemu.PhyMemMode",
},
#endif
@@ -2373,7 +1649,7 @@
},
#ifndef CONFIG_USER_ONLY
{
- .handler = handle_set_qemu_phy_mem_mode,
+ .handler = gdb_handle_set_qemu_phy_mem_mode,
.cmd = "qemu.PhyMemMode:",
.cmd_startswith = 1,
.schema = "l0"
@@ -2396,7 +1672,7 @@
if (process_string_cmd(NULL, get_param(params, 0)->data,
gdb_gen_query_table,
ARRAY_SIZE(gdb_gen_query_table))) {
- put_packet("");
+ gdb_put_packet("");
}
}
@@ -2415,7 +1691,7 @@
if (process_string_cmd(NULL, get_param(params, 0)->data,
gdb_gen_set_table,
ARRAY_SIZE(gdb_gen_set_table))) {
- put_packet("");
+ gdb_put_packet("");
}
}
@@ -2424,7 +1700,7 @@
g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP);
gdb_append_thread_id(gdbserver_state.c_cpu, gdbserver_state.str_buf);
g_string_append_c(gdbserver_state.str_buf, ';');
- put_strbuf();
+ gdb_put_strbuf();
/*
* Remove all the breakpoints when this query is issued,
* because gdb is doing an initial connect and the state
@@ -2441,7 +1717,7 @@
switch (line_buf[0]) {
case '!':
- put_packet("OK");
+ gdb_put_packet("OK");
break;
case '?':
{
@@ -2527,7 +1803,7 @@
case 'F':
{
static const GdbCmdParseEntry file_io_cmd_desc = {
- .handler = handle_file_io,
+ .handler = gdb_handle_file_io,
.cmd = "F",
.cmd_startswith = 1,
.schema = "L,L,o0"
@@ -2668,7 +1944,7 @@
break;
default:
/* put empty packet */
- put_packet("");
+ gdb_put_packet("");
break;
}
@@ -2695,183 +1971,7 @@
gdbserver_state.g_cpu = cpu;
}
-#ifndef CONFIG_USER_ONLY
-static void gdb_vm_state_change(void *opaque, bool running, RunState state)
-{
- CPUState *cpu = gdbserver_state.c_cpu;
- g_autoptr(GString) buf = g_string_new(NULL);
- g_autoptr(GString) tid = g_string_new(NULL);
- const char *type;
- int ret;
-
- if (running || gdbserver_state.state == RS_INACTIVE) {
- return;
- }
- /* Is there a GDB syscall waiting to be sent? */
- if (gdbserver_state.current_syscall_cb) {
- put_packet(gdbserver_state.syscall_buf);
- return;
- }
-
- if (cpu == NULL) {
- /* No process attached */
- return;
- }
-
- gdb_append_thread_id(cpu, tid);
-
- switch (state) {
- case RUN_STATE_DEBUG:
- if (cpu->watchpoint_hit) {
- switch (cpu->watchpoint_hit->flags & BP_MEM_ACCESS) {
- case BP_MEM_READ:
- type = "r";
- break;
- case BP_MEM_ACCESS:
- type = "a";
- break;
- default:
- type = "";
- break;
- }
- trace_gdbstub_hit_watchpoint(type, cpu_gdb_index(cpu),
- (target_ulong)cpu->watchpoint_hit->vaddr);
- g_string_printf(buf, "T%02xthread:%s;%swatch:" TARGET_FMT_lx ";",
- GDB_SIGNAL_TRAP, tid->str, type,
- (target_ulong)cpu->watchpoint_hit->vaddr);
- cpu->watchpoint_hit = NULL;
- goto send_packet;
- } else {
- trace_gdbstub_hit_break();
- }
- tb_flush(cpu);
- ret = GDB_SIGNAL_TRAP;
- break;
- case RUN_STATE_PAUSED:
- trace_gdbstub_hit_paused();
- ret = GDB_SIGNAL_INT;
- break;
- case RUN_STATE_SHUTDOWN:
- trace_gdbstub_hit_shutdown();
- ret = GDB_SIGNAL_QUIT;
- break;
- case RUN_STATE_IO_ERROR:
- trace_gdbstub_hit_io_error();
- ret = GDB_SIGNAL_IO;
- break;
- case RUN_STATE_WATCHDOG:
- trace_gdbstub_hit_watchdog();
- ret = GDB_SIGNAL_ALRM;
- break;
- case RUN_STATE_INTERNAL_ERROR:
- trace_gdbstub_hit_internal_error();
- ret = GDB_SIGNAL_ABRT;
- break;
- case RUN_STATE_SAVE_VM:
- case RUN_STATE_RESTORE_VM:
- return;
- case RUN_STATE_FINISH_MIGRATE:
- ret = GDB_SIGNAL_XCPU;
- break;
- default:
- trace_gdbstub_hit_unknown(state);
- ret = GDB_SIGNAL_UNKNOWN;
- break;
- }
- gdb_set_stop_cpu(cpu);
- g_string_printf(buf, "T%02xthread:%s;", ret, tid->str);
-
-send_packet:
- put_packet(buf->str);
-
- /* disable single step if it was enabled */
- cpu_single_step(cpu, 0);
-}
-#endif
-
-/* Send a gdb syscall request.
- This accepts limited printf-style format specifiers, specifically:
- %x - target_ulong argument printed in hex.
- %lx - 64-bit argument printed in hex.
- %s - string pointer (target_ulong) and length (int) pair. */
-void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va)
-{
- char *p;
- char *p_end;
- target_ulong addr;
- uint64_t i64;
-
- if (!gdb_attached()) {
- return;
- }
-
- gdbserver_state.current_syscall_cb = cb;
-#ifndef CONFIG_USER_ONLY
- vm_stop(RUN_STATE_DEBUG);
-#endif
- p = &gdbserver_state.syscall_buf[0];
- p_end = &gdbserver_state.syscall_buf[sizeof(gdbserver_state.syscall_buf)];
- *(p++) = 'F';
- while (*fmt) {
- if (*fmt == '%') {
- fmt++;
- switch (*fmt++) {
- case 'x':
- addr = va_arg(va, target_ulong);
- p += snprintf(p, p_end - p, TARGET_FMT_lx, addr);
- break;
- case 'l':
- if (*(fmt++) != 'x')
- goto bad_format;
- i64 = va_arg(va, uint64_t);
- p += snprintf(p, p_end - p, "%" PRIx64, i64);
- break;
- case 's':
- addr = va_arg(va, target_ulong);
- p += snprintf(p, p_end - p, TARGET_FMT_lx "/%x",
- addr, va_arg(va, int));
- break;
- default:
- bad_format:
- error_report("gdbstub: Bad syscall format string '%s'",
- fmt - 1);
- break;
- }
- } else {
- *(p++) = *(fmt++);
- }
- }
- *p = 0;
-#ifdef CONFIG_USER_ONLY
- put_packet(gdbserver_state.syscall_buf);
- /* Return control to gdb for it to process the syscall request.
- * Since the protocol requires that gdb hands control back to us
- * using a "here are the results" F packet, we don't need to check
- * gdb_handlesig's return value (which is the signal to deliver if
- * execution was resumed via a continue packet).
- */
- gdb_handlesig(gdbserver_state.c_cpu, 0);
-#else
- /* In this case wait to send the syscall packet until notification that
- the CPU has stopped. This must be done because if the packet is sent
- now the reply from the syscall request could be received while the CPU
- is still in the running state, which can cause packets to be dropped
- and state transition 'T' packets to be sent while the syscall is still
- being processed. */
- qemu_cpu_kick(gdbserver_state.c_cpu);
-#endif
-}
-
-void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
-{
- va_list va;
-
- va_start(va, fmt);
- gdb_do_syscallv(cb, fmt, va);
- va_end(va);
-}
-
-static void gdb_read_byte(uint8_t ch)
+void gdb_read_byte(uint8_t ch)
{
uint8_t reply;
@@ -2881,7 +1981,7 @@
of a new command then abandon the previous response. */
if (ch == '-') {
trace_gdbstub_err_got_nack();
- put_buffer(gdbserver_state.last_packet->data,
+ gdb_put_buffer(gdbserver_state.last_packet->data,
gdbserver_state.last_packet->len);
} else if (ch == '+') {
trace_gdbstub_io_got_ack();
@@ -3003,12 +2103,12 @@
trace_gdbstub_err_checksum_incorrect(gdbserver_state.line_sum, gdbserver_state.line_csum);
/* send NAK reply */
reply = '-';
- put_buffer(&reply, 1);
+ gdb_put_buffer(&reply, 1);
gdbserver_state.state = RS_IDLE;
} else {
/* send ACK reply */
reply = '+';
- put_buffer(&reply, 1);
+ gdb_put_buffer(&reply, 1);
gdbserver_state.state = gdb_handle_packet(gdbserver_state.line_buf);
}
break;
@@ -3018,39 +2118,12 @@
}
}
-/* Tell the remote gdb that the process has exited. */
-void gdb_exit(int code)
-{
- char buf[4];
-
- if (!gdbserver_state.init) {
- return;
- }
-#ifdef CONFIG_USER_ONLY
- if (gdbserver_state.socket_path) {
- unlink(gdbserver_state.socket_path);
- }
- if (gdbserver_state.fd < 0) {
- return;
- }
-#endif
-
- trace_gdbstub_op_exiting((uint8_t)code);
-
- snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code);
- put_packet(buf);
-
-#ifndef CONFIG_USER_ONLY
- qemu_chr_fe_deinit(&gdbserver_state.chr, true);
-#endif
-}
-
/*
* Create the process that will contain all the "orphan" CPUs (that are not
* part of a CPU cluster). Note that if this process contains no CPUs, it won't
* be attachable and thus will be invisible to the user.
*/
-static void create_default_process(GDBState *s)
+void gdb_create_default_process(GDBState *s)
{
GDBProcess *process;
int max_pid = 0;
@@ -3070,447 +2143,3 @@
process->target_xml[0] = '\0';
}
-#ifdef CONFIG_USER_ONLY
-int
-gdb_handlesig(CPUState *cpu, int sig)
-{
- char buf[256];
- int n;
-
- if (!gdbserver_state.init || gdbserver_state.fd < 0) {
- return sig;
- }
-
- /* disable single step if it was enabled */
- cpu_single_step(cpu, 0);
- tb_flush(cpu);
-
- if (sig != 0) {
- gdb_set_stop_cpu(cpu);
- g_string_printf(gdbserver_state.str_buf,
- "T%02xthread:", target_signal_to_gdb(sig));
- gdb_append_thread_id(cpu, gdbserver_state.str_buf);
- g_string_append_c(gdbserver_state.str_buf, ';');
- put_strbuf();
- }
- /* put_packet() might have detected that the peer terminated the
- connection. */
- if (gdbserver_state.fd < 0) {
- return sig;
- }
-
- sig = 0;
- gdbserver_state.state = RS_IDLE;
- gdbserver_state.running_state = 0;
- while (gdbserver_state.running_state == 0) {
- n = read(gdbserver_state.fd, buf, 256);
- if (n > 0) {
- int i;
-
- for (i = 0; i < n; i++) {
- gdb_read_byte(buf[i]);
- }
- } else {
- /* XXX: Connection closed. Should probably wait for another
- connection before continuing. */
- if (n == 0) {
- close(gdbserver_state.fd);
- }
- gdbserver_state.fd = -1;
- return sig;
- }
- }
- sig = gdbserver_state.signal;
- gdbserver_state.signal = 0;
- return sig;
-}
-
-/* Tell the remote gdb that the process has exited due to SIG. */
-void gdb_signalled(CPUArchState *env, int sig)
-{
- char buf[4];
-
- if (!gdbserver_state.init || gdbserver_state.fd < 0) {
- return;
- }
-
- snprintf(buf, sizeof(buf), "X%02x", target_signal_to_gdb(sig));
- put_packet(buf);
-}
-
-static void gdb_accept_init(int fd)
-{
- init_gdbserver_state();
- create_default_process(&gdbserver_state);
- gdbserver_state.processes[0].attached = true;
- gdbserver_state.c_cpu = gdb_first_attached_cpu();
- gdbserver_state.g_cpu = gdbserver_state.c_cpu;
- gdbserver_state.fd = fd;
- gdb_has_xml = false;
-}
-
-static bool gdb_accept_socket(int gdb_fd)
-{
- int fd;
-
- for(;;) {
- fd = accept(gdb_fd, NULL, NULL);
- if (fd < 0 && errno != EINTR) {
- perror("accept socket");
- return false;
- } else if (fd >= 0) {
- qemu_set_cloexec(fd);
- break;
- }
- }
-
- gdb_accept_init(fd);
- return true;
-}
-
-static int gdbserver_open_socket(const char *path)
-{
- struct sockaddr_un sockaddr = {};
- int fd, ret;
-
- fd = socket(AF_UNIX, SOCK_STREAM, 0);
- if (fd < 0) {
- perror("create socket");
- return -1;
- }
-
- sockaddr.sun_family = AF_UNIX;
- pstrcpy(sockaddr.sun_path, sizeof(sockaddr.sun_path) - 1, path);
- ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
- if (ret < 0) {
- perror("bind socket");
- close(fd);
- return -1;
- }
- ret = listen(fd, 1);
- if (ret < 0) {
- perror("listen socket");
- close(fd);
- return -1;
- }
-
- return fd;
-}
-
-static bool gdb_accept_tcp(int gdb_fd)
-{
- struct sockaddr_in sockaddr = {};
- socklen_t len;
- int fd;
-
- for(;;) {
- len = sizeof(sockaddr);
- fd = accept(gdb_fd, (struct sockaddr *)&sockaddr, &len);
- if (fd < 0 && errno != EINTR) {
- perror("accept");
- return false;
- } else if (fd >= 0) {
- qemu_set_cloexec(fd);
- break;
- }
- }
-
- /* set short latency */
- if (socket_set_nodelay(fd)) {
- perror("setsockopt");
- close(fd);
- return false;
- }
-
- gdb_accept_init(fd);
- return true;
-}
-
-static int gdbserver_open_port(int port)
-{
- struct sockaddr_in sockaddr;
- int fd, ret;
-
- fd = socket(PF_INET, SOCK_STREAM, 0);
- if (fd < 0) {
- perror("socket");
- return -1;
- }
- qemu_set_cloexec(fd);
-
- socket_set_fast_reuse(fd);
-
- sockaddr.sin_family = AF_INET;
- sockaddr.sin_port = htons(port);
- sockaddr.sin_addr.s_addr = 0;
- ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
- if (ret < 0) {
- perror("bind");
- close(fd);
- return -1;
- }
- ret = listen(fd, 1);
- if (ret < 0) {
- perror("listen");
- close(fd);
- return -1;
- }
-
- return fd;
-}
-
-int gdbserver_start(const char *port_or_path)
-{
- int port = g_ascii_strtoull(port_or_path, NULL, 10);
- int gdb_fd;
-
- if (port > 0) {
- gdb_fd = gdbserver_open_port(port);
- } else {
- gdb_fd = gdbserver_open_socket(port_or_path);
- }
-
- if (gdb_fd < 0) {
- return -1;
- }
-
- if (port > 0 && gdb_accept_tcp(gdb_fd)) {
- return 0;
- } else if (gdb_accept_socket(gdb_fd)) {
- gdbserver_state.socket_path = g_strdup(port_or_path);
- return 0;
- }
-
- /* gone wrong */
- close(gdb_fd);
- return -1;
-}
-
-/* Disable gdb stub for child processes. */
-void gdbserver_fork(CPUState *cpu)
-{
- if (!gdbserver_state.init || gdbserver_state.fd < 0) {
- return;
- }
- close(gdbserver_state.fd);
- gdbserver_state.fd = -1;
- cpu_breakpoint_remove_all(cpu, BP_GDB);
- cpu_watchpoint_remove_all(cpu, BP_GDB);
-}
-#else
-static int gdb_chr_can_receive(void *opaque)
-{
- /* We can handle an arbitrarily large amount of data.
- Pick the maximum packet size, which is as good as anything. */
- return MAX_PACKET_LENGTH;
-}
-
-static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size)
-{
- int i;
-
- for (i = 0; i < size; i++) {
- gdb_read_byte(buf[i]);
- }
-}
-
-static void gdb_chr_event(void *opaque, QEMUChrEvent event)
-{
- int i;
- GDBState *s = (GDBState *) opaque;
-
- switch (event) {
- case CHR_EVENT_OPENED:
- /* Start with first process attached, others detached */
- for (i = 0; i < s->process_num; i++) {
- s->processes[i].attached = !i;
- }
-
- s->c_cpu = gdb_first_attached_cpu();
- s->g_cpu = s->c_cpu;
-
- vm_stop(RUN_STATE_PAUSED);
- replay_gdb_attached();
- gdb_has_xml = false;
- break;
- default:
- break;
- }
-}
-
-static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len)
-{
- g_autoptr(GString) hex_buf = g_string_new("O");
- memtohex(hex_buf, buf, len);
- put_packet(hex_buf->str);
- return len;
-}
-
-#ifndef _WIN32
-static void gdb_sigterm_handler(int signal)
-{
- if (runstate_is_running()) {
- vm_stop(RUN_STATE_PAUSED);
- }
-}
-#endif
-
-static void gdb_monitor_open(Chardev *chr, ChardevBackend *backend,
- bool *be_opened, Error **errp)
-{
- *be_opened = false;
-}
-
-static void char_gdb_class_init(ObjectClass *oc, void *data)
-{
- ChardevClass *cc = CHARDEV_CLASS(oc);
-
- cc->internal = true;
- cc->open = gdb_monitor_open;
- cc->chr_write = gdb_monitor_write;
-}
-
-#define TYPE_CHARDEV_GDB "chardev-gdb"
-
-static const TypeInfo char_gdb_type_info = {
- .name = TYPE_CHARDEV_GDB,
- .parent = TYPE_CHARDEV,
- .class_init = char_gdb_class_init,
-};
-
-static int find_cpu_clusters(Object *child, void *opaque)
-{
- if (object_dynamic_cast(child, TYPE_CPU_CLUSTER)) {
- GDBState *s = (GDBState *) opaque;
- CPUClusterState *cluster = CPU_CLUSTER(child);
- GDBProcess *process;
-
- s->processes = g_renew(GDBProcess, s->processes, ++s->process_num);
-
- process = &s->processes[s->process_num - 1];
-
- /*
- * GDB process IDs -1 and 0 are reserved. To avoid subtle errors at
- * runtime, we enforce here that the machine does not use a cluster ID
- * that would lead to PID 0.
- */
- assert(cluster->cluster_id != UINT32_MAX);
- process->pid = cluster->cluster_id + 1;
- process->attached = false;
- process->target_xml[0] = '\0';
-
- return 0;
- }
-
- return object_child_foreach(child, find_cpu_clusters, opaque);
-}
-
-static int pid_order(const void *a, const void *b)
-{
- GDBProcess *pa = (GDBProcess *) a;
- GDBProcess *pb = (GDBProcess *) b;
-
- if (pa->pid < pb->pid) {
- return -1;
- } else if (pa->pid > pb->pid) {
- return 1;
- } else {
- return 0;
- }
-}
-
-static void create_processes(GDBState *s)
-{
- object_child_foreach(object_get_root(), find_cpu_clusters, s);
-
- if (gdbserver_state.processes) {
- /* Sort by PID */
- qsort(gdbserver_state.processes, gdbserver_state.process_num, sizeof(gdbserver_state.processes[0]), pid_order);
- }
-
- create_default_process(s);
-}
-
-int gdbserver_start(const char *device)
-{
- trace_gdbstub_op_start(device);
-
- char gdbstub_device_name[128];
- Chardev *chr = NULL;
- Chardev *mon_chr;
-
- if (!first_cpu) {
- error_report("gdbstub: meaningless to attach gdb to a "
- "machine without any CPU.");
- return -1;
- }
-
- if (!gdb_supports_guest_debug()) {
- error_report("gdbstub: current accelerator doesn't support guest debugging");
- return -1;
- }
-
- if (!device)
- return -1;
- if (strcmp(device, "none") != 0) {
- if (strstart(device, "tcp:", NULL)) {
- /* enforce required TCP attributes */
- snprintf(gdbstub_device_name, sizeof(gdbstub_device_name),
- "%s,wait=off,nodelay=on,server=on", device);
- device = gdbstub_device_name;
- }
-#ifndef _WIN32
- else if (strcmp(device, "stdio") == 0) {
- struct sigaction act;
-
- memset(&act, 0, sizeof(act));
- act.sa_handler = gdb_sigterm_handler;
- sigaction(SIGINT, &act, NULL);
- }
-#endif
- /*
- * 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);
- if (!chr)
- return -1;
- }
-
- if (!gdbserver_state.init) {
- init_gdbserver_state();
-
- qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL);
-
- /* Initialize a monitor terminal for gdb */
- mon_chr = qemu_chardev_new(NULL, TYPE_CHARDEV_GDB,
- NULL, NULL, &error_abort);
- monitor_init_hmp(mon_chr, false, &error_abort);
- } else {
- qemu_chr_fe_deinit(&gdbserver_state.chr, true);
- mon_chr = gdbserver_state.mon_chr;
- reset_gdbserver_state();
- }
-
- create_processes(&gdbserver_state);
-
- if (chr) {
- qemu_chr_fe_init(&gdbserver_state.chr, chr, &error_abort);
- qemu_chr_fe_set_handlers(&gdbserver_state.chr, gdb_chr_can_receive,
- gdb_chr_receive, gdb_chr_event,
- NULL, &gdbserver_state, NULL, true);
- }
- gdbserver_state.state = chr ? RS_IDLE : RS_INACTIVE;
- gdbserver_state.mon_chr = mon_chr;
- gdbserver_state.current_syscall_cb = NULL;
-
- return 0;
-}
-
-static void register_types(void)
-{
- type_register_static(&char_gdb_type_info);
-}
-
-type_init(register_types);
-#endif
diff --git a/gdbstub/internals.h b/gdbstub/internals.h
index b23999f..94ddff4 100644
--- a/gdbstub/internals.h
+++ b/gdbstub/internals.h
@@ -6,14 +6,220 @@
* SPDX-License-Identifier: GPL-2.0-or-later
*/
-#ifndef _INTERNALS_H_
-#define _INTERNALS_H_
+#ifndef GDBSTUB_INTERNALS_H
+#define GDBSTUB_INTERNALS_H
#include "exec/cpu-common.h"
+#define MAX_PACKET_LENGTH 4096
+
+/*
+ * Shared structures and definitions
+ */
+
+enum {
+ GDB_SIGNAL_0 = 0,
+ GDB_SIGNAL_INT = 2,
+ GDB_SIGNAL_QUIT = 3,
+ GDB_SIGNAL_TRAP = 5,
+ GDB_SIGNAL_ABRT = 6,
+ GDB_SIGNAL_ALRM = 14,
+ GDB_SIGNAL_IO = 23,
+ GDB_SIGNAL_XCPU = 24,
+ GDB_SIGNAL_UNKNOWN = 143
+};
+
+typedef struct GDBProcess {
+ uint32_t pid;
+ bool attached;
+
+ char target_xml[1024];
+} GDBProcess;
+
+enum RSState {
+ RS_INACTIVE,
+ RS_IDLE,
+ RS_GETLINE,
+ RS_GETLINE_ESC,
+ RS_GETLINE_RLE,
+ RS_CHKSUM1,
+ RS_CHKSUM2,
+};
+
+typedef struct GDBState {
+ bool init; /* have we been initialised? */
+ CPUState *c_cpu; /* current CPU for step/continue ops */
+ CPUState *g_cpu; /* current CPU for other ops */
+ CPUState *query_cpu; /* for q{f|s}ThreadInfo */
+ enum RSState state; /* parsing state */
+ char line_buf[MAX_PACKET_LENGTH];
+ int line_buf_index;
+ int line_sum; /* running checksum */
+ int line_csum; /* checksum at the end of the packet */
+ GByteArray *last_packet;
+ int signal;
+ bool multiprocess;
+ GDBProcess *processes;
+ int process_num;
+ GString *str_buf;
+ GByteArray *mem_buf;
+ int sstep_flags;
+ int supported_sstep_flags;
+} GDBState;
+
+/* lives in main gdbstub.c */
+extern GDBState gdbserver_state;
+
+/*
+ * Inline utility function, convert from int to hex and back
+ */
+
+static inline int fromhex(int v)
+{
+ if (v >= '0' && v <= '9') {
+ return v - '0';
+ } else if (v >= 'A' && v <= 'F') {
+ return v - 'A' + 10;
+ } else if (v >= 'a' && v <= 'f') {
+ return v - 'a' + 10;
+ } else {
+ return 0;
+ }
+}
+
+static inline int tohex(int v)
+{
+ if (v < 10) {
+ return v + '0';
+ } else {
+ return v - 10 + 'a';
+ }
+}
+
+/*
+ * Connection helpers for both softmmu and user backends
+ */
+
+void gdb_put_strbuf(void);
+int gdb_put_packet(const char *buf);
+int gdb_put_packet_binary(const char *buf, int len, bool dump);
+void gdb_hextomem(GByteArray *mem, const char *buf, int len);
+void gdb_memtohex(GString *buf, const uint8_t *mem, int len);
+void gdb_memtox(GString *buf, const char *mem, int len);
+void gdb_read_byte(uint8_t ch);
+
+/*
+ * Packet acknowledgement - we handle this slightly differently
+ * between user and softmmu mode, mainly to deal with the differences
+ * between the flexible chardev and the direct fd approaches.
+ *
+ * We currently don't support a negotiated QStartNoAckMode
+ */
+
+/**
+ * gdb_got_immediate_ack() - check ok to continue
+ *
+ * Returns true to continue, false to re-transmit for user only, the
+ * softmmu stub always returns true.
+ */
+bool gdb_got_immediate_ack(void);
+/* utility helpers */
+CPUState *gdb_first_attached_cpu(void);
+void gdb_append_thread_id(CPUState *cpu, GString *buf);
+int gdb_get_cpu_index(CPUState *cpu);
+unsigned int gdb_get_max_cpus(void); /* both */
+bool gdb_can_reverse(void); /* softmmu, stub for user */
+
+void gdb_create_default_process(GDBState *s);
+
+/* signal mapping, common for softmmu, specialised for user-mode */
+int gdb_signal_to_target(int sig);
+int gdb_target_signal_to_gdb(int sig);
+
+int gdb_get_char(void); /* user only */
+
+/**
+ * gdb_continue() - handle continue in mode specific way.
+ */
+void gdb_continue(void);
+
+/**
+ * gdb_continue_partial() - handle partial continue in mode specific way.
+ */
+int gdb_continue_partial(char *newstates);
+
+/*
+ * Helpers with separate softmmu and user implementations
+ */
+void gdb_put_buffer(const uint8_t *buf, int len);
+
+/*
+ * Command handlers - either specialised or softmmu or user only
+ */
+void gdb_init_gdbserver_state(void);
+
+typedef enum GDBThreadIdKind {
+ GDB_ONE_THREAD = 0,
+ GDB_ALL_THREADS, /* One process, all threads */
+ GDB_ALL_PROCESSES,
+ GDB_READ_THREAD_ERR
+} GDBThreadIdKind;
+
+typedef union GdbCmdVariant {
+ const char *data;
+ uint8_t opcode;
+ unsigned long val_ul;
+ unsigned long long val_ull;
+ struct {
+ GDBThreadIdKind kind;
+ uint32_t pid;
+ uint32_t tid;
+ } thread_id;
+} GdbCmdVariant;
+
+#define get_param(p, i) (&g_array_index(p, GdbCmdVariant, i))
+
+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_query_attached(GArray *params, void *user_ctx); /* both */
+
+/* softmmu only */
+void gdb_handle_query_qemu_phy_mem_mode(GArray *params, void *user_ctx);
+void gdb_handle_set_qemu_phy_mem_mode(GArray *params, void *user_ctx);
+
+/* sycall handling */
+void gdb_handle_file_io(GArray *params, void *user_ctx);
+bool gdb_handled_syscall(void);
+void gdb_disable_syscalls(void);
+void gdb_syscall_reset(void);
+
+/* user/softmmu specific syscall handling */
+void gdb_syscall_handling(const char *syscall_packet);
+
+/*
+ * Break/Watch point support - there is an implementation for softmmu
+ * and user mode.
+ */
bool gdb_supports_guest_debug(void);
int gdb_breakpoint_insert(CPUState *cs, int type, vaddr addr, vaddr len);
int gdb_breakpoint_remove(CPUState *cs, int type, vaddr addr, vaddr len);
void gdb_breakpoint_remove_all(CPUState *cs);
-#endif /* _INTERNALS_H_ */
+/**
+ * gdb_target_memory_rw_debug() - handle debug access to memory
+ * @cs: CPUState
+ * @addr: nominal address, could be an entire physical address
+ * @buf: data
+ * @len: length of access
+ * @is_write: is it a write operation
+ *
+ * This function is specialised depending on the mode we are running
+ * in. For softmmu guests we can switch the interpretation of the
+ * address to a physical address.
+ */
+int gdb_target_memory_rw_debug(CPUState *cs, hwaddr addr,
+ uint8_t *buf, int len, bool is_write);
+
+#endif /* GDBSTUB_INTERNALS_H */
diff --git a/gdbstub/meson.build b/gdbstub/meson.build
index fc895a2..bd5c5cd 100644
--- a/gdbstub/meson.build
+++ b/gdbstub/meson.build
@@ -4,6 +4,34 @@
# types such as hwaddr.
#
-specific_ss.add(files('gdbstub.c'))
-softmmu_ss.add(files('softmmu.c'))
-user_ss.add(files('user.c'))
+# We need to build the core gdb code via a library to be able to tweak
+# cflags so:
+
+gdb_user_ss = ss.source_set()
+gdb_softmmu_ss = ss.source_set()
+
+# We build two versions of gdbstub, one for each mode
+gdb_user_ss.add(files('gdbstub.c', 'user.c'))
+gdb_softmmu_ss.add(files('gdbstub.c', 'softmmu.c'))
+
+gdb_user_ss = gdb_user_ss.apply(config_host, strict: false)
+gdb_softmmu_ss = gdb_softmmu_ss.apply(config_host, strict: false)
+
+libgdb_user = static_library('gdb_user',
+ gdb_user_ss.sources() + genh,
+ name_suffix: 'fa',
+ c_args: '-DCONFIG_USER_ONLY')
+
+libgdb_softmmu = static_library('gdb_softmmu',
+ gdb_softmmu_ss.sources() + genh,
+ name_suffix: 'fa')
+
+gdb_user = declare_dependency(link_whole: libgdb_user)
+user_ss.add(gdb_user)
+gdb_softmmu = declare_dependency(link_whole: libgdb_softmmu)
+softmmu_ss.add(gdb_softmmu)
+
+common_ss.add(files('syscalls.c'))
+
+# The user-target is specialised by the guest
+specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-target.c'))
diff --git a/gdbstub/softmmu.c b/gdbstub/softmmu.c
index 129575e..22ecd09 100644
--- a/gdbstub/softmmu.c
+++ b/gdbstub/softmmu.c
@@ -4,16 +4,617 @@
* Debug integration depends on support from the individual
* accelerators so most of this involves calling the ops helpers.
*
+ * Copyright (c) 2003-2005 Fabrice Bellard
* Copyright (c) 2022 Linaro Ltd
*
- * SPDX-License-Identifier: GPL-2.0-or-later
+ * SPDX-License-Identifier: LGPL-2.0+
*/
#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/cutils.h"
#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
+#include "exec/hwaddr.h"
+#include "exec/tb-flush.h"
#include "sysemu/cpus.h"
+#include "sysemu/runstate.h"
+#include "sysemu/replay.h"
+#include "hw/core/cpu.h"
+#include "hw/cpu/cluster.h"
+#include "hw/boards.h"
+#include "chardev/char.h"
+#include "chardev/char-fe.h"
+#include "monitor/monitor.h"
+#include "trace.h"
#include "internals.h"
+/* System emulation specific state */
+typedef struct {
+ CharBackend chr;
+ Chardev *mon_chr;
+} GDBSystemState;
+
+GDBSystemState gdbserver_system_state;
+
+static void reset_gdbserver_state(void)
+{
+ g_free(gdbserver_state.processes);
+ gdbserver_state.processes = NULL;
+ gdbserver_state.process_num = 0;
+}
+
+/*
+ * Return the GDB index for a given vCPU state.
+ *
+ * In system mode GDB numbers CPUs from 1 as 0 is reserved as an "any
+ * cpu" index.
+ */
+int gdb_get_cpu_index(CPUState *cpu)
+{
+ return cpu->cpu_index + 1;
+}
+
+/*
+ * We check the status of the last message in the chardev receive code
+ */
+bool gdb_got_immediate_ack(void)
+{
+ return true;
+}
+
+/*
+ * GDB Connection management. For system emulation we do all of this
+ * via our existing Chardev infrastructure which allows us to support
+ * network and unix sockets.
+ */
+
+void gdb_put_buffer(const uint8_t *buf, int len)
+{
+ /*
+ * XXX this blocks entire thread. Rewrite to use
+ * qemu_chr_fe_write and background I/O callbacks
+ */
+ qemu_chr_fe_write_all(&gdbserver_system_state.chr, buf, len);
+}
+
+static void gdb_chr_event(void *opaque, QEMUChrEvent event)
+{
+ int i;
+ GDBState *s = (GDBState *) opaque;
+
+ switch (event) {
+ case CHR_EVENT_OPENED:
+ /* Start with first process attached, others detached */
+ for (i = 0; i < s->process_num; i++) {
+ s->processes[i].attached = !i;
+ }
+
+ s->c_cpu = gdb_first_attached_cpu();
+ s->g_cpu = s->c_cpu;
+
+ vm_stop(RUN_STATE_PAUSED);
+ replay_gdb_attached();
+ gdb_has_xml = false;
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * In softmmu mode we stop the VM and wait to send the syscall packet
+ * until notification that the CPU has stopped. This must be done
+ * because if the packet is sent now the reply from the syscall
+ * request could be received while the CPU is still in the running
+ * state, which can cause packets to be dropped and state transition
+ * 'T' packets to be sent while the syscall is still being processed.
+ */
+void gdb_syscall_handling(const char *syscall_packet)
+{
+ vm_stop(RUN_STATE_DEBUG);
+ qemu_cpu_kick(gdbserver_state.c_cpu);
+}
+
+static void gdb_vm_state_change(void *opaque, bool running, RunState state)
+{
+ CPUState *cpu = gdbserver_state.c_cpu;
+ g_autoptr(GString) buf = g_string_new(NULL);
+ g_autoptr(GString) tid = g_string_new(NULL);
+ const char *type;
+ int ret;
+
+ if (running || gdbserver_state.state == RS_INACTIVE) {
+ return;
+ }
+
+ /* Is there a GDB syscall waiting to be sent? */
+ if (gdb_handled_syscall()) {
+ return;
+ }
+
+ if (cpu == NULL) {
+ /* No process attached */
+ return;
+ }
+
+ gdb_append_thread_id(cpu, tid);
+
+ switch (state) {
+ case RUN_STATE_DEBUG:
+ if (cpu->watchpoint_hit) {
+ switch (cpu->watchpoint_hit->flags & BP_MEM_ACCESS) {
+ case BP_MEM_READ:
+ type = "r";
+ break;
+ case BP_MEM_ACCESS:
+ type = "a";
+ break;
+ default:
+ type = "";
+ break;
+ }
+ trace_gdbstub_hit_watchpoint(type,
+ gdb_get_cpu_index(cpu),
+ cpu->watchpoint_hit->vaddr);
+ g_string_printf(buf, "T%02xthread:%s;%swatch:%" VADDR_PRIx ";",
+ GDB_SIGNAL_TRAP, tid->str, type,
+ cpu->watchpoint_hit->vaddr);
+ cpu->watchpoint_hit = NULL;
+ goto send_packet;
+ } else {
+ trace_gdbstub_hit_break();
+ }
+ tb_flush(cpu);
+ ret = GDB_SIGNAL_TRAP;
+ break;
+ case RUN_STATE_PAUSED:
+ trace_gdbstub_hit_paused();
+ ret = GDB_SIGNAL_INT;
+ break;
+ case RUN_STATE_SHUTDOWN:
+ trace_gdbstub_hit_shutdown();
+ ret = GDB_SIGNAL_QUIT;
+ break;
+ case RUN_STATE_IO_ERROR:
+ trace_gdbstub_hit_io_error();
+ ret = GDB_SIGNAL_IO;
+ break;
+ case RUN_STATE_WATCHDOG:
+ trace_gdbstub_hit_watchdog();
+ ret = GDB_SIGNAL_ALRM;
+ break;
+ case RUN_STATE_INTERNAL_ERROR:
+ trace_gdbstub_hit_internal_error();
+ ret = GDB_SIGNAL_ABRT;
+ break;
+ case RUN_STATE_SAVE_VM:
+ case RUN_STATE_RESTORE_VM:
+ return;
+ case RUN_STATE_FINISH_MIGRATE:
+ ret = GDB_SIGNAL_XCPU;
+ break;
+ default:
+ trace_gdbstub_hit_unknown(state);
+ ret = GDB_SIGNAL_UNKNOWN;
+ break;
+ }
+ gdb_set_stop_cpu(cpu);
+ g_string_printf(buf, "T%02xthread:%s;", ret, tid->str);
+
+send_packet:
+ gdb_put_packet(buf->str);
+
+ /* disable single step if it was enabled */
+ cpu_single_step(cpu, 0);
+}
+
+#ifndef _WIN32
+static void gdb_sigterm_handler(int signal)
+{
+ if (runstate_is_running()) {
+ vm_stop(RUN_STATE_PAUSED);
+ }
+}
+#endif
+
+static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len)
+{
+ g_autoptr(GString) hex_buf = g_string_new("O");
+ gdb_memtohex(hex_buf, buf, len);
+ gdb_put_packet(hex_buf->str);
+ return len;
+}
+
+static void gdb_monitor_open(Chardev *chr, ChardevBackend *backend,
+ bool *be_opened, Error **errp)
+{
+ *be_opened = false;
+}
+
+static void char_gdb_class_init(ObjectClass *oc, void *data)
+{
+ ChardevClass *cc = CHARDEV_CLASS(oc);
+
+ cc->internal = true;
+ cc->open = gdb_monitor_open;
+ cc->chr_write = gdb_monitor_write;
+}
+
+#define TYPE_CHARDEV_GDB "chardev-gdb"
+
+static const TypeInfo char_gdb_type_info = {
+ .name = TYPE_CHARDEV_GDB,
+ .parent = TYPE_CHARDEV,
+ .class_init = char_gdb_class_init,
+};
+
+static int gdb_chr_can_receive(void *opaque)
+{
+ /*
+ * We can handle an arbitrarily large amount of data.
+ * Pick the maximum packet size, which is as good as anything.
+ */
+ return MAX_PACKET_LENGTH;
+}
+
+static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ gdb_read_byte(buf[i]);
+ }
+}
+
+static int find_cpu_clusters(Object *child, void *opaque)
+{
+ if (object_dynamic_cast(child, TYPE_CPU_CLUSTER)) {
+ GDBState *s = (GDBState *) opaque;
+ CPUClusterState *cluster = CPU_CLUSTER(child);
+ GDBProcess *process;
+
+ s->processes = g_renew(GDBProcess, s->processes, ++s->process_num);
+
+ process = &s->processes[s->process_num - 1];
+
+ /*
+ * GDB process IDs -1 and 0 are reserved. To avoid subtle errors at
+ * runtime, we enforce here that the machine does not use a cluster ID
+ * that would lead to PID 0.
+ */
+ assert(cluster->cluster_id != UINT32_MAX);
+ process->pid = cluster->cluster_id + 1;
+ process->attached = false;
+ process->target_xml[0] = '\0';
+
+ return 0;
+ }
+
+ return object_child_foreach(child, find_cpu_clusters, opaque);
+}
+
+static int pid_order(const void *a, const void *b)
+{
+ GDBProcess *pa = (GDBProcess *) a;
+ GDBProcess *pb = (GDBProcess *) b;
+
+ if (pa->pid < pb->pid) {
+ return -1;
+ } else if (pa->pid > pb->pid) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static void create_processes(GDBState *s)
+{
+ object_child_foreach(object_get_root(), find_cpu_clusters, s);
+
+ if (gdbserver_state.processes) {
+ /* Sort by PID */
+ qsort(gdbserver_state.processes,
+ gdbserver_state.process_num,
+ sizeof(gdbserver_state.processes[0]),
+ pid_order);
+ }
+
+ gdb_create_default_process(s);
+}
+
+int gdbserver_start(const char *device)
+{
+ trace_gdbstub_op_start(device);
+
+ char gdbstub_device_name[128];
+ Chardev *chr = NULL;
+ Chardev *mon_chr;
+
+ if (!first_cpu) {
+ error_report("gdbstub: meaningless to attach gdb to a "
+ "machine without any CPU.");
+ return -1;
+ }
+
+ if (!gdb_supports_guest_debug()) {
+ error_report("gdbstub: current accelerator doesn't "
+ "support guest debugging");
+ return -1;
+ }
+
+ if (!device) {
+ return -1;
+ }
+ if (strcmp(device, "none") != 0) {
+ if (strstart(device, "tcp:", NULL)) {
+ /* enforce required TCP attributes */
+ snprintf(gdbstub_device_name, sizeof(gdbstub_device_name),
+ "%s,wait=off,nodelay=on,server=on", device);
+ device = gdbstub_device_name;
+ }
+#ifndef _WIN32
+ else if (strcmp(device, "stdio") == 0) {
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = gdb_sigterm_handler;
+ sigaction(SIGINT, &act, NULL);
+ }
+#endif
+ /*
+ * 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);
+ if (!chr) {
+ return -1;
+ }
+ }
+
+ if (!gdbserver_state.init) {
+ gdb_init_gdbserver_state();
+
+ qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL);
+
+ /* Initialize a monitor terminal for gdb */
+ mon_chr = qemu_chardev_new(NULL, TYPE_CHARDEV_GDB,
+ NULL, NULL, &error_abort);
+ monitor_init_hmp(mon_chr, false, &error_abort);
+ } else {
+ qemu_chr_fe_deinit(&gdbserver_system_state.chr, true);
+ mon_chr = gdbserver_system_state.mon_chr;
+ reset_gdbserver_state();
+ }
+
+ create_processes(&gdbserver_state);
+
+ if (chr) {
+ qemu_chr_fe_init(&gdbserver_system_state.chr, chr, &error_abort);
+ qemu_chr_fe_set_handlers(&gdbserver_system_state.chr,
+ gdb_chr_can_receive,
+ gdb_chr_receive, gdb_chr_event,
+ NULL, &gdbserver_state, NULL, true);
+ }
+ gdbserver_state.state = chr ? RS_IDLE : RS_INACTIVE;
+ gdbserver_system_state.mon_chr = mon_chr;
+ gdb_syscall_reset();
+
+ return 0;
+}
+
+static void register_types(void)
+{
+ type_register_static(&char_gdb_type_info);
+}
+
+type_init(register_types);
+
+/* Tell the remote gdb that the process has exited. */
+void gdb_exit(int code)
+{
+ char buf[4];
+
+ if (!gdbserver_state.init) {
+ return;
+ }
+
+ trace_gdbstub_op_exiting((uint8_t)code);
+
+ snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code);
+ gdb_put_packet(buf);
+
+ qemu_chr_fe_deinit(&gdbserver_system_state.chr, true);
+}
+
+/*
+ * Memory access
+ */
+static int phy_memory_mode;
+
+int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr,
+ uint8_t *buf, int len, bool is_write)
+{
+ CPUClass *cc;
+
+ if (phy_memory_mode) {
+ if (is_write) {
+ cpu_physical_memory_write(addr, buf, len);
+ } else {
+ cpu_physical_memory_read(addr, buf, len);
+ }
+ return 0;
+ }
+
+ cc = CPU_GET_CLASS(cpu);
+ if (cc->memory_rw_debug) {
+ return cc->memory_rw_debug(cpu, addr, buf, len, is_write);
+ }
+
+ return cpu_memory_rw_debug(cpu, addr, buf, len, is_write);
+}
+
+/*
+ * cpu helpers
+ */
+
+unsigned int gdb_get_max_cpus(void)
+{
+ MachineState *ms = MACHINE(qdev_get_machine());
+ return ms->smp.max_cpus;
+}
+
+bool gdb_can_reverse(void)
+{
+ return replay_mode == REPLAY_MODE_PLAY;
+}
+
+/*
+ * Softmmu specific command helpers
+ */
+
+void gdb_handle_query_qemu_phy_mem_mode(GArray *params,
+ void *user_ctx)
+{
+ g_string_printf(gdbserver_state.str_buf, "%d", phy_memory_mode);
+ gdb_put_strbuf();
+}
+
+void gdb_handle_set_qemu_phy_mem_mode(GArray *params, void *user_ctx)
+{
+ if (!params->len) {
+ gdb_put_packet("E22");
+ return;
+ }
+
+ if (!get_param(params, 0)->val_ul) {
+ phy_memory_mode = 0;
+ } else {
+ phy_memory_mode = 1;
+ }
+ gdb_put_packet("OK");
+}
+
+void gdb_handle_query_rcmd(GArray *params, void *user_ctx)
+{
+ const guint8 zero = 0;
+ int len;
+
+ if (!params->len) {
+ gdb_put_packet("E22");
+ return;
+ }
+
+ len = strlen(get_param(params, 0)->data);
+ if (len % 2) {
+ gdb_put_packet("E01");
+ return;
+ }
+
+ g_assert(gdbserver_state.mem_buf->len == 0);
+ len = len / 2;
+ gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len);
+ g_byte_array_append(gdbserver_state.mem_buf, &zero, 1);
+ qemu_chr_be_write(gdbserver_system_state.mon_chr,
+ gdbserver_state.mem_buf->data,
+ gdbserver_state.mem_buf->len);
+ gdb_put_packet("OK");
+}
+
+/*
+ * Execution state helpers
+ */
+
+void gdb_handle_query_attached(GArray *params, void *user_ctx)
+{
+ gdb_put_packet("1");
+}
+
+void gdb_continue(void)
+{
+ if (!runstate_needs_reset()) {
+ trace_gdbstub_op_continue();
+ vm_start();
+ }
+}
+
+/*
+ * Resume execution, per CPU actions.
+ */
+int gdb_continue_partial(char *newstates)
+{
+ CPUState *cpu;
+ int res = 0;
+ int flag = 0;
+
+ if (!runstate_needs_reset()) {
+ bool step_requested = false;
+ CPU_FOREACH(cpu) {
+ if (newstates[cpu->cpu_index] == 's') {
+ step_requested = true;
+ break;
+ }
+ }
+
+ if (vm_prepare_start(step_requested)) {
+ return 0;
+ }
+
+ CPU_FOREACH(cpu) {
+ switch (newstates[cpu->cpu_index]) {
+ case 0:
+ case 1:
+ break; /* nothing to do here */
+ case 's':
+ trace_gdbstub_op_stepping(cpu->cpu_index);
+ cpu_single_step(cpu, gdbserver_state.sstep_flags);
+ cpu_resume(cpu);
+ flag = 1;
+ break;
+ case 'c':
+ trace_gdbstub_op_continue_cpu(cpu->cpu_index);
+ cpu_resume(cpu);
+ flag = 1;
+ break;
+ default:
+ res = -1;
+ break;
+ }
+ }
+ }
+ if (flag) {
+ qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true);
+ }
+ return res;
+}
+
+/*
+ * Signal Handling - in system mode we only need SIGINT and SIGTRAP; other
+ * signals are not yet supported.
+ */
+
+enum {
+ TARGET_SIGINT = 2,
+ TARGET_SIGTRAP = 5
+};
+
+int gdb_signal_to_target(int sig)
+{
+ switch (sig) {
+ case 2:
+ return TARGET_SIGINT;
+ case 5:
+ return TARGET_SIGTRAP;
+ default:
+ return -1;
+ }
+}
+
+/*
+ * Break/Watch point helpers
+ */
+
bool gdb_supports_guest_debug(void)
{
const AccelOpsClass *ops = cpus_get_accel();
diff --git a/gdbstub/syscalls.c b/gdbstub/syscalls.c
new file mode 100644
index 0000000..02e3a8f
--- /dev/null
+++ b/gdbstub/syscalls.c
@@ -0,0 +1,205 @@
+/*
+ * GDB Syscall Handling
+ *
+ * GDB can execute syscalls on the guests behalf, currently used by
+ * the various semihosting extensions.
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2023 Linaro Ltd
+ *
+ * SPDX-License-Identifier: LGPL-2.0+
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "semihosting/semihost.h"
+#include "sysemu/runstate.h"
+#include "gdbstub/user.h"
+#include "gdbstub/syscalls.h"
+#include "trace.h"
+#include "internals.h"
+
+/* Syscall specific state */
+typedef struct {
+ char syscall_buf[256];
+ gdb_syscall_complete_cb current_syscall_cb;
+} GDBSyscallState;
+
+static GDBSyscallState gdbserver_syscall_state;
+
+/*
+ * Return true if there is a GDB currently connected to the stub
+ * and attached to a CPU
+ */
+static bool gdb_attached(void)
+{
+ return gdbserver_state.init && gdbserver_state.c_cpu;
+}
+
+static enum {
+ GDB_SYS_UNKNOWN,
+ GDB_SYS_ENABLED,
+ GDB_SYS_DISABLED,
+} gdb_syscall_mode;
+
+/* Decide if either remote gdb syscalls or native file IO should be used. */
+int use_gdb_syscalls(void)
+{
+ SemihostingTarget target = semihosting_get_target();
+ if (target == SEMIHOSTING_TARGET_NATIVE) {
+ /* -semihosting-config target=native */
+ return false;
+ } else if (target == SEMIHOSTING_TARGET_GDB) {
+ /* -semihosting-config target=gdb */
+ return true;
+ }
+
+ /* -semihosting-config target=auto */
+ /* On the first call check if gdb is connected and remember. */
+ if (gdb_syscall_mode == GDB_SYS_UNKNOWN) {
+ gdb_syscall_mode = gdb_attached() ? GDB_SYS_ENABLED : GDB_SYS_DISABLED;
+ }
+ return gdb_syscall_mode == GDB_SYS_ENABLED;
+}
+
+/* called when the stub detaches */
+void gdb_disable_syscalls(void)
+{
+ gdb_syscall_mode = GDB_SYS_DISABLED;
+}
+
+void gdb_syscall_reset(void)
+{
+ gdbserver_syscall_state.current_syscall_cb = NULL;
+}
+
+bool gdb_handled_syscall(void)
+{
+ if (gdbserver_syscall_state.current_syscall_cb) {
+ gdb_put_packet(gdbserver_syscall_state.syscall_buf);
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Send a gdb syscall request.
+ * This accepts limited printf-style format specifiers, specifically:
+ * %x - target_ulong argument printed in hex.
+ * %lx - 64-bit argument printed in hex.
+ * %s - string pointer (target_ulong) and length (int) pair.
+ */
+void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
+{
+ char *p, *p_end;
+ va_list va;
+
+ if (!gdb_attached()) {
+ return;
+ }
+
+ gdbserver_syscall_state.current_syscall_cb = cb;
+ va_start(va, fmt);
+
+ p = gdbserver_syscall_state.syscall_buf;
+ p_end = p + sizeof(gdbserver_syscall_state.syscall_buf);
+ *(p++) = 'F';
+ while (*fmt) {
+ if (*fmt == '%') {
+ uint64_t i64;
+ uint32_t i32;
+
+ fmt++;
+ switch (*fmt++) {
+ case 'x':
+ i32 = va_arg(va, uint32_t);
+ p += snprintf(p, p_end - p, "%" PRIx32, i32);
+ break;
+ case 'l':
+ if (*(fmt++) != 'x') {
+ goto bad_format;
+ }
+ i64 = va_arg(va, uint64_t);
+ p += snprintf(p, p_end - p, "%" PRIx64, i64);
+ break;
+ case 's':
+ i64 = va_arg(va, uint64_t);
+ i32 = va_arg(va, uint32_t);
+ p += snprintf(p, p_end - p, "%" PRIx64 "/%x" PRIx32, i64, i32);
+ break;
+ default:
+ bad_format:
+ error_report("gdbstub: Bad syscall format string '%s'",
+ fmt - 1);
+ break;
+ }
+ } else {
+ *(p++) = *(fmt++);
+ }
+ }
+ *p = 0;
+
+ va_end(va);
+ gdb_syscall_handling(gdbserver_syscall_state.syscall_buf);
+}
+
+/*
+ * GDB Command Handlers
+ */
+
+void gdb_handle_file_io(GArray *params, void *user_ctx)
+{
+ if (params->len >= 1 && gdbserver_syscall_state.current_syscall_cb) {
+ uint64_t ret;
+ int err;
+
+ ret = get_param(params, 0)->val_ull;
+ if (params->len >= 2) {
+ err = get_param(params, 1)->val_ull;
+ } else {
+ err = 0;
+ }
+
+ /* Convert GDB error numbers back to host error numbers. */
+#define E(X) case GDB_E##X: err = E##X; break
+ switch (err) {
+ case 0:
+ break;
+ E(PERM);
+ E(NOENT);
+ E(INTR);
+ E(BADF);
+ E(ACCES);
+ E(FAULT);
+ E(BUSY);
+ E(EXIST);
+ E(NODEV);
+ E(NOTDIR);
+ E(ISDIR);
+ E(INVAL);
+ E(NFILE);
+ E(MFILE);
+ E(FBIG);
+ E(NOSPC);
+ E(SPIPE);
+ E(ROFS);
+ E(NAMETOOLONG);
+ default:
+ err = EINVAL;
+ break;
+ }
+#undef E
+
+ gdbserver_syscall_state.current_syscall_cb(gdbserver_state.c_cpu,
+ ret, err);
+ gdbserver_syscall_state.current_syscall_cb = NULL;
+ }
+
+ if (params->len >= 3 && get_param(params, 2)->opcode == (uint8_t)'C') {
+ gdb_put_packet("T02");
+ return;
+ }
+
+ gdb_continue();
+}
diff --git a/gdbstub/trace-events b/gdbstub/trace-events
index 03f0c30..0c18a4d 100644
--- a/gdbstub/trace-events
+++ b/gdbstub/trace-events
@@ -7,7 +7,6 @@
gdbstub_op_continue_cpu(int cpu_index) "Continuing CPU %d"
gdbstub_op_stepping(int cpu_index) "Stepping CPU %d"
gdbstub_op_extra_info(const char *info) "Thread extra info: %s"
-gdbstub_hit_watchpoint(const char *type, int cpu_gdb_index, uint64_t vaddr) "Watchpoint hit, type=\"%s\" cpu=%d, vaddr=0x%" PRIx64 ""
gdbstub_hit_internal_error(void) "RUN_STATE_INTERNAL_ERROR"
gdbstub_hit_break(void) "RUN_STATE_DEBUG"
gdbstub_hit_paused(void) "RUN_STATE_PAUSED"
@@ -27,3 +26,6 @@
gdbstub_err_invalid_rle(void) "got invalid RLE sequence"
gdbstub_err_checksum_invalid(uint8_t ch) "got invalid command checksum digit: 0x%02x"
gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packet with incorrect checksum, expected=0x%02x, received=0x%02x"
+
+# softmmu.c
+gdbstub_hit_watchpoint(const char *type, int cpu_gdb_index, uint64_t vaddr) "Watchpoint hit, type=\"%s\" cpu=%d, vaddr=0x%" PRIx64 ""
diff --git a/gdbstub/user-target.c b/gdbstub/user-target.c
new file mode 100644
index 0000000..fa0e59e
--- /dev/null
+++ b/gdbstub/user-target.c
@@ -0,0 +1,283 @@
+/*
+ * Target specific user-mode handling
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2022 Linaro Ltd
+ *
+ * SPDX-License-Identifier: LGPL-2.0+
+ */
+
+#include "qemu/osdep.h"
+#include "exec/gdbstub.h"
+#include "qemu.h"
+#include "internals.h"
+
+/*
+ * Map target signal numbers to GDB protocol signal numbers and vice
+ * versa. For user emulation's currently supported systems, we can
+ * assume most signals are defined.
+ */
+
+static int gdb_signal_table[] = {
+ 0,
+ TARGET_SIGHUP,
+ TARGET_SIGINT,
+ TARGET_SIGQUIT,
+ TARGET_SIGILL,
+ TARGET_SIGTRAP,
+ TARGET_SIGABRT,
+ -1, /* SIGEMT */
+ TARGET_SIGFPE,
+ TARGET_SIGKILL,
+ TARGET_SIGBUS,
+ TARGET_SIGSEGV,
+ TARGET_SIGSYS,
+ TARGET_SIGPIPE,
+ TARGET_SIGALRM,
+ TARGET_SIGTERM,
+ TARGET_SIGURG,
+ TARGET_SIGSTOP,
+ TARGET_SIGTSTP,
+ TARGET_SIGCONT,
+ TARGET_SIGCHLD,
+ TARGET_SIGTTIN,
+ TARGET_SIGTTOU,
+ TARGET_SIGIO,
+ TARGET_SIGXCPU,
+ TARGET_SIGXFSZ,
+ TARGET_SIGVTALRM,
+ TARGET_SIGPROF,
+ TARGET_SIGWINCH,
+ -1, /* SIGLOST */
+ TARGET_SIGUSR1,
+ TARGET_SIGUSR2,
+#ifdef TARGET_SIGPWR
+ TARGET_SIGPWR,
+#else
+ -1,
+#endif
+ -1, /* SIGPOLL */
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+#ifdef __SIGRTMIN
+ __SIGRTMIN + 1,
+ __SIGRTMIN + 2,
+ __SIGRTMIN + 3,
+ __SIGRTMIN + 4,
+ __SIGRTMIN + 5,
+ __SIGRTMIN + 6,
+ __SIGRTMIN + 7,
+ __SIGRTMIN + 8,
+ __SIGRTMIN + 9,
+ __SIGRTMIN + 10,
+ __SIGRTMIN + 11,
+ __SIGRTMIN + 12,
+ __SIGRTMIN + 13,
+ __SIGRTMIN + 14,
+ __SIGRTMIN + 15,
+ __SIGRTMIN + 16,
+ __SIGRTMIN + 17,
+ __SIGRTMIN + 18,
+ __SIGRTMIN + 19,
+ __SIGRTMIN + 20,
+ __SIGRTMIN + 21,
+ __SIGRTMIN + 22,
+ __SIGRTMIN + 23,
+ __SIGRTMIN + 24,
+ __SIGRTMIN + 25,
+ __SIGRTMIN + 26,
+ __SIGRTMIN + 27,
+ __SIGRTMIN + 28,
+ __SIGRTMIN + 29,
+ __SIGRTMIN + 30,
+ __SIGRTMIN + 31,
+ -1, /* SIGCANCEL */
+ __SIGRTMIN,
+ __SIGRTMIN + 32,
+ __SIGRTMIN + 33,
+ __SIGRTMIN + 34,
+ __SIGRTMIN + 35,
+ __SIGRTMIN + 36,
+ __SIGRTMIN + 37,
+ __SIGRTMIN + 38,
+ __SIGRTMIN + 39,
+ __SIGRTMIN + 40,
+ __SIGRTMIN + 41,
+ __SIGRTMIN + 42,
+ __SIGRTMIN + 43,
+ __SIGRTMIN + 44,
+ __SIGRTMIN + 45,
+ __SIGRTMIN + 46,
+ __SIGRTMIN + 47,
+ __SIGRTMIN + 48,
+ __SIGRTMIN + 49,
+ __SIGRTMIN + 50,
+ __SIGRTMIN + 51,
+ __SIGRTMIN + 52,
+ __SIGRTMIN + 53,
+ __SIGRTMIN + 54,
+ __SIGRTMIN + 55,
+ __SIGRTMIN + 56,
+ __SIGRTMIN + 57,
+ __SIGRTMIN + 58,
+ __SIGRTMIN + 59,
+ __SIGRTMIN + 60,
+ __SIGRTMIN + 61,
+ __SIGRTMIN + 62,
+ __SIGRTMIN + 63,
+ __SIGRTMIN + 64,
+ __SIGRTMIN + 65,
+ __SIGRTMIN + 66,
+ __SIGRTMIN + 67,
+ __SIGRTMIN + 68,
+ __SIGRTMIN + 69,
+ __SIGRTMIN + 70,
+ __SIGRTMIN + 71,
+ __SIGRTMIN + 72,
+ __SIGRTMIN + 73,
+ __SIGRTMIN + 74,
+ __SIGRTMIN + 75,
+ __SIGRTMIN + 76,
+ __SIGRTMIN + 77,
+ __SIGRTMIN + 78,
+ __SIGRTMIN + 79,
+ __SIGRTMIN + 80,
+ __SIGRTMIN + 81,
+ __SIGRTMIN + 82,
+ __SIGRTMIN + 83,
+ __SIGRTMIN + 84,
+ __SIGRTMIN + 85,
+ __SIGRTMIN + 86,
+ __SIGRTMIN + 87,
+ __SIGRTMIN + 88,
+ __SIGRTMIN + 89,
+ __SIGRTMIN + 90,
+ __SIGRTMIN + 91,
+ __SIGRTMIN + 92,
+ __SIGRTMIN + 93,
+ __SIGRTMIN + 94,
+ __SIGRTMIN + 95,
+ -1, /* SIGINFO */
+ -1, /* UNKNOWN */
+ -1, /* DEFAULT */
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1
+#endif
+};
+
+int gdb_signal_to_target(int sig)
+{
+ if (sig < ARRAY_SIZE(gdb_signal_table)) {
+ return gdb_signal_table[sig];
+ } else {
+ return -1;
+ }
+}
+
+int gdb_target_signal_to_gdb(int sig)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(gdb_signal_table); i++) {
+ if (gdb_signal_table[i] == sig) {
+ return i;
+ }
+ }
+ return GDB_SIGNAL_UNKNOWN;
+}
+
+int gdb_get_cpu_index(CPUState *cpu)
+{
+ TaskState *ts = (TaskState *) cpu->opaque;
+ return ts ? ts->ts_tid : -1;
+}
+
+/*
+ * User-mode specific command helpers
+ */
+
+void gdb_handle_query_offsets(GArray *params, void *user_ctx)
+{
+ TaskState *ts;
+
+ ts = gdbserver_state.c_cpu->opaque;
+ g_string_printf(gdbserver_state.str_buf,
+ "Text=" TARGET_ABI_FMT_lx
+ ";Data=" TARGET_ABI_FMT_lx
+ ";Bss=" TARGET_ABI_FMT_lx,
+ ts->info->code_offset,
+ ts->info->data_offset,
+ ts->info->data_offset);
+ gdb_put_strbuf();
+}
+
+#if defined(CONFIG_LINUX)
+/* Partial user only duplicate of helper in gdbstub.c */
+static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr,
+ uint8_t *buf, int len, bool is_write)
+{
+ CPUClass *cc;
+ cc = CPU_GET_CLASS(cpu);
+ if (cc->memory_rw_debug) {
+ return cc->memory_rw_debug(cpu, addr, buf, len, is_write);
+ }
+ return cpu_memory_rw_debug(cpu, addr, buf, len, is_write);
+}
+
+void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx)
+{
+ TaskState *ts;
+ unsigned long offset, len, saved_auxv, auxv_len;
+
+ if (params->len < 2) {
+ gdb_put_packet("E22");
+ return;
+ }
+
+ offset = get_param(params, 0)->val_ul;
+ len = get_param(params, 1)->val_ul;
+ ts = gdbserver_state.c_cpu->opaque;
+ saved_auxv = ts->info->saved_auxv;
+ auxv_len = ts->info->auxv_len;
+
+ if (offset >= auxv_len) {
+ gdb_put_packet("E00");
+ return;
+ }
+
+ if (len > (MAX_PACKET_LENGTH - 5) / 2) {
+ len = (MAX_PACKET_LENGTH - 5) / 2;
+ }
+
+ if (len < auxv_len - offset) {
+ g_string_assign(gdbserver_state.str_buf, "m");
+ } else {
+ g_string_assign(gdbserver_state.str_buf, "l");
+ len = auxv_len - offset;
+ }
+
+ g_byte_array_set_size(gdbserver_state.mem_buf, len);
+ if (target_memory_rw_debug(gdbserver_state.g_cpu, saved_auxv + offset,
+ gdbserver_state.mem_buf->data, len, false)) {
+ gdb_put_packet("E14");
+ return;
+ }
+
+ gdb_memtox(gdbserver_state.str_buf,
+ (const char *)gdbserver_state.mem_buf->data, len);
+ gdb_put_packet_binary(gdbserver_state.str_buf->str,
+ gdbserver_state.str_buf->len, true);
+}
+#endif
diff --git a/gdbstub/user.c b/gdbstub/user.c
index 484bd8f..80488b6 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -3,16 +3,423 @@
*
* We know for user-mode we are using TCG so we can call stuff directly.
*
+ * Copyright (c) 2003-2005 Fabrice Bellard
* Copyright (c) 2022 Linaro Ltd
*
- * SPDX-License-Identifier: GPL-2.0-or-later
+ * SPDX-License-Identifier: LGPL-2.0+
*/
#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "qemu/sockets.h"
+#include "exec/hwaddr.h"
+#include "exec/tb-flush.h"
#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
+#include "gdbstub/user.h"
#include "hw/core/cpu.h"
+#include "trace.h"
#include "internals.h"
+/* User-mode specific state */
+typedef struct {
+ int fd;
+ char *socket_path;
+ int running_state;
+} GDBUserState;
+
+static GDBUserState gdbserver_user_state;
+
+int gdb_get_char(void)
+{
+ uint8_t ch;
+ int ret;
+
+ for (;;) {
+ ret = recv(gdbserver_user_state.fd, &ch, 1, 0);
+ if (ret < 0) {
+ if (errno == ECONNRESET) {
+ gdbserver_user_state.fd = -1;
+ }
+ if (errno != EINTR) {
+ return -1;
+ }
+ } else if (ret == 0) {
+ close(gdbserver_user_state.fd);
+ gdbserver_user_state.fd = -1;
+ return -1;
+ } else {
+ break;
+ }
+ }
+ return ch;
+}
+
+bool gdb_got_immediate_ack(void)
+{
+ int i;
+
+ i = gdb_get_char();
+ if (i < 0) {
+ /* no response, continue anyway */
+ return true;
+ }
+
+ if (i == '+') {
+ /* received correctly, continue */
+ return true;
+ }
+
+ /* anything else, including '-' then try again */
+ return false;
+}
+
+void gdb_put_buffer(const uint8_t *buf, int len)
+{
+ int ret;
+
+ while (len > 0) {
+ ret = send(gdbserver_user_state.fd, buf, len, 0);
+ if (ret < 0) {
+ if (errno != EINTR) {
+ return;
+ }
+ } else {
+ buf += ret;
+ len -= ret;
+ }
+ }
+}
+
+/* Tell the remote gdb that the process has exited. */
+void gdb_exit(int code)
+{
+ char buf[4];
+
+ if (!gdbserver_state.init) {
+ return;
+ }
+ if (gdbserver_user_state.socket_path) {
+ unlink(gdbserver_user_state.socket_path);
+ }
+ if (gdbserver_user_state.fd < 0) {
+ return;
+ }
+
+ trace_gdbstub_op_exiting((uint8_t)code);
+
+ snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code);
+ gdb_put_packet(buf);
+}
+
+int gdb_handlesig(CPUState *cpu, int sig)
+{
+ char buf[256];
+ int n;
+
+ if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
+ return sig;
+ }
+
+ /* disable single step if it was enabled */
+ cpu_single_step(cpu, 0);
+ tb_flush(cpu);
+
+ if (sig != 0) {
+ gdb_set_stop_cpu(cpu);
+ g_string_printf(gdbserver_state.str_buf,
+ "T%02xthread:", gdb_target_signal_to_gdb(sig));
+ gdb_append_thread_id(cpu, gdbserver_state.str_buf);
+ g_string_append_c(gdbserver_state.str_buf, ';');
+ gdb_put_strbuf();
+ }
+ /*
+ * gdb_put_packet() might have detected that the peer terminated the
+ * connection.
+ */
+ if (gdbserver_user_state.fd < 0) {
+ return sig;
+ }
+
+ sig = 0;
+ gdbserver_state.state = RS_IDLE;
+ gdbserver_user_state.running_state = 0;
+ while (gdbserver_user_state.running_state == 0) {
+ n = read(gdbserver_user_state.fd, buf, 256);
+ if (n > 0) {
+ int i;
+
+ for (i = 0; i < n; i++) {
+ gdb_read_byte(buf[i]);
+ }
+ } else {
+ /*
+ * XXX: Connection closed. Should probably wait for another
+ * connection before continuing.
+ */
+ if (n == 0) {
+ close(gdbserver_user_state.fd);
+ }
+ gdbserver_user_state.fd = -1;
+ return sig;
+ }
+ }
+ sig = gdbserver_state.signal;
+ gdbserver_state.signal = 0;
+ return sig;
+}
+
+/* Tell the remote gdb that the process has exited due to SIG. */
+void gdb_signalled(CPUArchState *env, int sig)
+{
+ char buf[4];
+
+ if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
+ return;
+ }
+
+ snprintf(buf, sizeof(buf), "X%02x", gdb_target_signal_to_gdb(sig));
+ gdb_put_packet(buf);
+}
+
+static void gdb_accept_init(int fd)
+{
+ gdb_init_gdbserver_state();
+ gdb_create_default_process(&gdbserver_state);
+ gdbserver_state.processes[0].attached = true;
+ gdbserver_state.c_cpu = gdb_first_attached_cpu();
+ gdbserver_state.g_cpu = gdbserver_state.c_cpu;
+ gdbserver_user_state.fd = fd;
+ gdb_has_xml = false;
+}
+
+static bool gdb_accept_socket(int gdb_fd)
+{
+ int fd;
+
+ for (;;) {
+ fd = accept(gdb_fd, NULL, NULL);
+ if (fd < 0 && errno != EINTR) {
+ perror("accept socket");
+ return false;
+ } else if (fd >= 0) {
+ qemu_set_cloexec(fd);
+ break;
+ }
+ }
+
+ gdb_accept_init(fd);
+ return true;
+}
+
+static int gdbserver_open_socket(const char *path)
+{
+ struct sockaddr_un sockaddr = {};
+ int fd, ret;
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("create socket");
+ return -1;
+ }
+
+ sockaddr.sun_family = AF_UNIX;
+ pstrcpy(sockaddr.sun_path, sizeof(sockaddr.sun_path) - 1, path);
+ ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
+ if (ret < 0) {
+ perror("bind socket");
+ close(fd);
+ return -1;
+ }
+ ret = listen(fd, 1);
+ if (ret < 0) {
+ perror("listen socket");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static bool gdb_accept_tcp(int gdb_fd)
+{
+ struct sockaddr_in sockaddr = {};
+ socklen_t len;
+ int fd;
+
+ for (;;) {
+ len = sizeof(sockaddr);
+ fd = accept(gdb_fd, (struct sockaddr *)&sockaddr, &len);
+ if (fd < 0 && errno != EINTR) {
+ perror("accept");
+ return false;
+ } else if (fd >= 0) {
+ qemu_set_cloexec(fd);
+ break;
+ }
+ }
+
+ /* set short latency */
+ if (socket_set_nodelay(fd)) {
+ perror("setsockopt");
+ close(fd);
+ return false;
+ }
+
+ gdb_accept_init(fd);
+ return true;
+}
+
+static int gdbserver_open_port(int port)
+{
+ struct sockaddr_in sockaddr;
+ int fd, ret;
+
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ return -1;
+ }
+ qemu_set_cloexec(fd);
+
+ socket_set_fast_reuse(fd);
+
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons(port);
+ sockaddr.sin_addr.s_addr = 0;
+ ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
+ if (ret < 0) {
+ perror("bind");
+ close(fd);
+ return -1;
+ }
+ ret = listen(fd, 1);
+ if (ret < 0) {
+ perror("listen");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+int gdbserver_start(const char *port_or_path)
+{
+ int port = g_ascii_strtoull(port_or_path, NULL, 10);
+ int gdb_fd;
+
+ if (port > 0) {
+ gdb_fd = gdbserver_open_port(port);
+ } else {
+ gdb_fd = gdbserver_open_socket(port_or_path);
+ }
+
+ if (gdb_fd < 0) {
+ return -1;
+ }
+
+ if (port > 0 && gdb_accept_tcp(gdb_fd)) {
+ return 0;
+ } else if (gdb_accept_socket(gdb_fd)) {
+ gdbserver_user_state.socket_path = g_strdup(port_or_path);
+ return 0;
+ }
+
+ /* gone wrong */
+ close(gdb_fd);
+ return -1;
+}
+
+/* Disable gdb stub for child processes. */
+void gdbserver_fork(CPUState *cpu)
+{
+ if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
+ return;
+ }
+ close(gdbserver_user_state.fd);
+ gdbserver_user_state.fd = -1;
+ cpu_breakpoint_remove_all(cpu, BP_GDB);
+ /* no cpu_watchpoint_remove_all for user-mode */
+}
+
+/*
+ * Execution state helpers
+ */
+
+void gdb_handle_query_attached(GArray *params, void *user_ctx)
+{
+ gdb_put_packet("0");
+}
+
+void gdb_continue(void)
+{
+ gdbserver_user_state.running_state = 1;
+ trace_gdbstub_op_continue();
+}
+
+/*
+ * Resume execution, for user-mode emulation it's equivalent to
+ * gdb_continue.
+ */
+int gdb_continue_partial(char *newstates)
+{
+ CPUState *cpu;
+ int res = 0;
+ /*
+ * This is not exactly accurate, but it's an improvement compared to the
+ * previous situation, where only one CPU would be single-stepped.
+ */
+ CPU_FOREACH(cpu) {
+ if (newstates[cpu->cpu_index] == 's') {
+ trace_gdbstub_op_stepping(cpu->cpu_index);
+ cpu_single_step(cpu, gdbserver_state.sstep_flags);
+ }
+ }
+ gdbserver_user_state.running_state = 1;
+ return res;
+}
+
+/*
+ * Memory access helpers
+ */
+int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr,
+ uint8_t *buf, int len, bool is_write)
+{
+ CPUClass *cc;
+
+ cc = CPU_GET_CLASS(cpu);
+ if (cc->memory_rw_debug) {
+ return cc->memory_rw_debug(cpu, addr, buf, len, is_write);
+ }
+ return cpu_memory_rw_debug(cpu, addr, buf, len, is_write);
+}
+
+/*
+ * cpu helpers
+ */
+
+unsigned int gdb_get_max_cpus(void)
+{
+ CPUState *cpu;
+ unsigned int max_cpus = 1;
+
+ CPU_FOREACH(cpu) {
+ max_cpus = max_cpus <= cpu->cpu_index ? cpu->cpu_index + 1 : max_cpus;
+ }
+
+ return max_cpus;
+}
+
+/* replay not supported for user-mode */
+bool gdb_can_reverse(void)
+{
+ return false;
+}
+
+/*
+ * Break/Watch point helpers
+ */
+
bool gdb_supports_guest_debug(void)
{
/* user-mode == TCG == supported */
@@ -65,3 +472,17 @@
{
cpu_breakpoint_remove_all(cs, BP_GDB);
}
+
+/*
+ * For user-mode syscall support we send the system call immediately
+ * and then return control to gdb for it to process the syscall request.
+ * Since the protocol requires that gdb hands control back to us
+ * using a "here are the results" F packet, we don't need to check
+ * gdb_handlesig's return value (which is the signal to deliver if
+ * execution was resumed via a continue packet).
+ */
+void gdb_syscall_handling(const char *syscall_packet)
+{
+ gdb_put_packet(syscall_packet);
+ gdb_handlesig(gdbserver_state.c_cpu, 0);
+}
diff --git a/hw/9pfs/meson.build b/hw/9pfs/meson.build
index 12443b6..fd37b7a 100644
--- a/hw/9pfs/meson.build
+++ b/hw/9pfs/meson.build
@@ -15,7 +15,7 @@
))
fs_ss.add(when: 'CONFIG_LINUX', if_true: files('9p-util-linux.c'))
fs_ss.add(when: 'CONFIG_DARWIN', if_true: files('9p-util-darwin.c'))
-fs_ss.add(when: 'CONFIG_XEN', if_true: files('xen-9p-backend.c'))
+fs_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen-9p-backend.c'))
softmmu_ss.add_all(when: 'CONFIG_FSDEV_9P', if_true: fs_ss)
specific_ss.add(when: 'CONFIG_VIRTIO_9P', if_true: files('virtio-9p-device.c'))
diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c
index 65c4979..74f3a05 100644
--- a/hw/9pfs/xen-9p-backend.c
+++ b/hw/9pfs/xen-9p-backend.c
@@ -22,6 +22,7 @@
#include "qemu/config-file.h"
#include "qemu/main-loop.h"
#include "qemu/option.h"
+#include "qemu/iov.h"
#include "fsdev/qemu-fsdev.h"
#define VERSIONS "1"
@@ -241,7 +242,7 @@
xen_wmb();
ring->inprogress = false;
- xenevtchn_notify(ring->evtchndev, ring->local_port);
+ qemu_xen_evtchn_notify(ring->evtchndev, ring->local_port);
qemu_bh_schedule(ring->bh);
}
@@ -324,8 +325,8 @@
Xen9pfsRing *ring = opaque;
evtchn_port_t port;
- port = xenevtchn_pending(ring->evtchndev);
- xenevtchn_unmask(ring->evtchndev, port);
+ port = qemu_xen_evtchn_pending(ring->evtchndev);
+ qemu_xen_evtchn_unmask(ring->evtchndev, port);
qemu_bh_schedule(ring->bh);
}
@@ -337,10 +338,10 @@
for (i = 0; i < xen_9pdev->num_rings; i++) {
if (xen_9pdev->rings[i].evtchndev != NULL) {
- qemu_set_fd_handler(xenevtchn_fd(xen_9pdev->rings[i].evtchndev),
- NULL, NULL, NULL);
- xenevtchn_unbind(xen_9pdev->rings[i].evtchndev,
- xen_9pdev->rings[i].local_port);
+ qemu_set_fd_handler(qemu_xen_evtchn_fd(xen_9pdev->rings[i].evtchndev),
+ NULL, NULL, NULL);
+ qemu_xen_evtchn_unbind(xen_9pdev->rings[i].evtchndev,
+ xen_9pdev->rings[i].local_port);
xen_9pdev->rings[i].evtchndev = NULL;
}
}
@@ -359,12 +360,13 @@
if (xen_9pdev->rings[i].data != NULL) {
xen_be_unmap_grant_refs(&xen_9pdev->xendev,
xen_9pdev->rings[i].data,
+ xen_9pdev->rings[i].intf->ref,
(1 << xen_9pdev->rings[i].ring_order));
}
if (xen_9pdev->rings[i].intf != NULL) {
- xen_be_unmap_grant_refs(&xen_9pdev->xendev,
- xen_9pdev->rings[i].intf,
- 1);
+ xen_be_unmap_grant_ref(&xen_9pdev->xendev,
+ xen_9pdev->rings[i].intf,
+ xen_9pdev->rings[i].ref);
}
if (xen_9pdev->rings[i].bh != NULL) {
qemu_bh_delete(xen_9pdev->rings[i].bh);
@@ -447,12 +449,12 @@
xen_9pdev->rings[i].inprogress = false;
- xen_9pdev->rings[i].evtchndev = xenevtchn_open(NULL, 0);
+ xen_9pdev->rings[i].evtchndev = qemu_xen_evtchn_open();
if (xen_9pdev->rings[i].evtchndev == NULL) {
goto out;
}
- qemu_set_cloexec(xenevtchn_fd(xen_9pdev->rings[i].evtchndev));
- xen_9pdev->rings[i].local_port = xenevtchn_bind_interdomain
+ qemu_set_cloexec(qemu_xen_evtchn_fd(xen_9pdev->rings[i].evtchndev));
+ xen_9pdev->rings[i].local_port = qemu_xen_evtchn_bind_interdomain
(xen_9pdev->rings[i].evtchndev,
xendev->dom,
xen_9pdev->rings[i].evtchn);
@@ -463,8 +465,8 @@
goto out;
}
xen_pv_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port);
- qemu_set_fd_handler(xenevtchn_fd(xen_9pdev->rings[i].evtchndev),
- xen_9pfs_evtchn_event, NULL, &xen_9pdev->rings[i]);
+ qemu_set_fd_handler(qemu_xen_evtchn_fd(xen_9pdev->rings[i].evtchndev),
+ xen_9pfs_evtchn_event, NULL, &xen_9pdev->rings[i]);
}
xen_9pdev->security_model = xenstore_read_be_str(xendev, "security_model");
diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c
index 86601cb..c1f2b9c 100644
--- a/hw/arm/aspeed.c
+++ b/hw/arm/aspeed.c
@@ -524,6 +524,11 @@
at24c_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 4), 0x51, 128 * KiB);
at24c_eeprom_init_rom(aspeed_i2c_get_bus(&soc->i2c, 8), 0x51, 128 * KiB,
yosemitev2_bmc_fruid, yosemitev2_bmc_fruid_len);
+ /* TMP421 */
+ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 11), "tmp421", 0x1f);
+ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 9), "tmp421", 0x4e);
+ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 9), "tmp421", 0x4f);
+
}
static void romulus_bmc_i2c_init(AspeedMachineState *bmc)
@@ -542,6 +547,10 @@
at24c_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 4), 0x54, 128 * KiB);
at24c_eeprom_init_rom(aspeed_i2c_get_bus(&soc->i2c, 6), 0x54, 128 * KiB,
tiogapass_bmc_fruid, tiogapass_bmc_fruid_len);
+ /* TMP421 */
+ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 8), "tmp421", 0x1f);
+ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 6), "tmp421", 0x4f);
+ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 6), "tmp421", 0x4e);
}
static void create_pca9552(AspeedSoCState *soc, int bus_id, int addr)
diff --git a/hw/arm/aspeed_eeprom.c b/hw/arm/aspeed_eeprom.c
index 2fb2d5d..dc33a88 100644
--- a/hw/arm/aspeed_eeprom.c
+++ b/hw/arm/aspeed_eeprom.c
@@ -101,17 +101,17 @@
/* Yosemite V2 BMC FRU */
const uint8_t yosemitev2_bmc_fruid[] = {
0x01, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x00, 0xf1, 0x01, 0x0c, 0x00, 0x36,
- 0xe6, 0xd0, 0xc6, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2, 0x42, 0x4d,
- 0x43, 0x20, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x20, 0x4d, 0x6f,
- 0x64, 0x75, 0x6c, 0x65, 0xcd, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
+ 0xe6, 0xd0, 0xc6, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2, 0x42, 0x61,
+ 0x73, 0x65, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x20, 0x4d, 0x50, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xcd, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xce, 0x58, 0x58, 0x58, 0x58, 0x58,
0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc3, 0x31, 0x2e,
0x30, 0xc9, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2,
0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc1, 0x39, 0x01, 0x0c, 0x00, 0xc6,
0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2, 0x59, 0x6f, 0x73, 0x65, 0x6d,
- 0x69, 0x74, 0x65, 0x20, 0x56, 0x32, 0x2e, 0x30, 0x20, 0x45, 0x56, 0x54,
- 0x32, 0xce, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
+ 0x69, 0x74, 0x65, 0x20, 0x56, 0x32, 0x20, 0x4d, 0x50, 0x00, 0x00, 0x00,
+ 0x00, 0xce, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
0x58, 0x58, 0x58, 0x58, 0xc4, 0x45, 0x56, 0x54, 0x32, 0xcd, 0x58, 0x58,
0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc7,
0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc3, 0x31, 0x2e, 0x30, 0xc9,
diff --git a/hw/audio/trace-events b/hw/audio/trace-events
index e0e71cd..4dec48a 100644
--- a/hw/audio/trace-events
+++ b/hw/audio/trace-events
@@ -11,3 +11,9 @@
hda_audio_format(const char *stream, int chan, const char *fmt, int freq) "st %s, %d x %s @ %d Hz"
hda_audio_adjust(const char *stream, int pos) "st %s, pos %d"
hda_audio_overrun(const char *stream) "st %s"
+
+#via-ac97.c
+via_ac97_codec_write(uint8_t addr, uint16_t val) "0x%x <- 0x%x"
+via_ac97_sgd_fetch(uint32_t curr, uint32_t addr, char stop, char eol, char flag, uint32_t len) "curr=0x%x addr=0x%x %c%c%c len=%d"
+via_ac97_sgd_read(uint64_t addr, unsigned size, uint64_t val) "0x%"PRIx64" %d -> 0x%"PRIx64
+via_ac97_sgd_write(uint64_t addr, unsigned size, uint64_t val) "0x%"PRIx64" %d <- 0x%"PRIx64
diff --git a/hw/audio/via-ac97.c b/hw/audio/via-ac97.c
index d1a856f..676254b 100644
--- a/hw/audio/via-ac97.c
+++ b/hw/audio/via-ac97.c
@@ -1,39 +1,482 @@
/*
* VIA south bridges sound support
*
+ * Copyright (c) 2022-2023 BALATON Zoltan
+ *
* This work is licensed under the GNU GPL license version 2 or later.
*/
/*
- * TODO: This is entirely boiler plate just registering empty PCI devices
- * with the right ID guests expect, functionality should be added here.
+ * TODO: This is only a basic implementation of one audio playback channel
+ * more functionality should be added here.
*/
#include "qemu/osdep.h"
+#include "qemu/log.h"
#include "hw/isa/vt82c686.h"
-#include "hw/pci/pci_device.h"
+#include "ac97.h"
+#include "trace.h"
+
+#define CLEN_IS_EOL(x) ((x)->clen & BIT(31))
+#define CLEN_IS_FLAG(x) ((x)->clen & BIT(30))
+#define CLEN_IS_STOP(x) ((x)->clen & BIT(29))
+#define CLEN_LEN(x) ((x)->clen & 0xffffff)
+
+#define STAT_ACTIVE BIT(7)
+#define STAT_PAUSED BIT(6)
+#define STAT_TRIG BIT(3)
+#define STAT_STOP BIT(2)
+#define STAT_EOL BIT(1)
+#define STAT_FLAG BIT(0)
+
+#define CNTL_START BIT(7)
+#define CNTL_TERM BIT(6)
+#define CNTL_PAUSE BIT(3)
+
+static void open_voice_out(ViaAC97State *s);
+
+static uint16_t codec_rates[] = { 8000, 11025, 16000, 22050, 32000, 44100,
+ 48000 };
+
+#define CODEC_REG(s, o) ((s)->codec_regs[(o) / 2])
+#define CODEC_VOL(vol, mask) ((255 * ((vol) & mask)) / mask)
+
+static void codec_volume_set_out(ViaAC97State *s)
+{
+ int lvol, rvol, mute;
+
+ lvol = 255 - CODEC_VOL(CODEC_REG(s, AC97_Master_Volume_Mute) >> 8, 0x1f);
+ lvol *= 255 - CODEC_VOL(CODEC_REG(s, AC97_PCM_Out_Volume_Mute) >> 8, 0x1f);
+ lvol /= 255;
+ rvol = 255 - CODEC_VOL(CODEC_REG(s, AC97_Master_Volume_Mute), 0x1f);
+ rvol *= 255 - CODEC_VOL(CODEC_REG(s, AC97_PCM_Out_Volume_Mute), 0x1f);
+ rvol /= 255;
+ mute = CODEC_REG(s, AC97_Master_Volume_Mute) >> MUTE_SHIFT;
+ mute |= CODEC_REG(s, AC97_PCM_Out_Volume_Mute) >> MUTE_SHIFT;
+ AUD_set_volume_out(s->vo, mute, lvol, rvol);
+}
+
+static void codec_reset(ViaAC97State *s)
+{
+ memset(s->codec_regs, 0, sizeof(s->codec_regs));
+ CODEC_REG(s, AC97_Reset) = 0x6a90;
+ CODEC_REG(s, AC97_Master_Volume_Mute) = 0x8000;
+ CODEC_REG(s, AC97_Headphone_Volume_Mute) = 0x8000;
+ CODEC_REG(s, AC97_Master_Volume_Mono_Mute) = 0x8000;
+ CODEC_REG(s, AC97_Phone_Volume_Mute) = 0x8008;
+ CODEC_REG(s, AC97_Mic_Volume_Mute) = 0x8008;
+ CODEC_REG(s, AC97_Line_In_Volume_Mute) = 0x8808;
+ CODEC_REG(s, AC97_CD_Volume_Mute) = 0x8808;
+ CODEC_REG(s, AC97_Video_Volume_Mute) = 0x8808;
+ CODEC_REG(s, AC97_Aux_Volume_Mute) = 0x8808;
+ CODEC_REG(s, AC97_PCM_Out_Volume_Mute) = 0x8808;
+ CODEC_REG(s, AC97_Record_Gain_Mute) = 0x8000;
+ CODEC_REG(s, AC97_Powerdown_Ctrl_Stat) = 0x000f;
+ CODEC_REG(s, AC97_Extended_Audio_ID) = 0x0a05;
+ CODEC_REG(s, AC97_Extended_Audio_Ctrl_Stat) = 0x0400;
+ CODEC_REG(s, AC97_PCM_Front_DAC_Rate) = 48000;
+ CODEC_REG(s, AC97_PCM_LR_ADC_Rate) = 48000;
+ /* Sigmatel 9766 (STAC9766) */
+ CODEC_REG(s, AC97_Vendor_ID1) = 0x8384;
+ CODEC_REG(s, AC97_Vendor_ID2) = 0x7666;
+}
+
+static uint16_t codec_read(ViaAC97State *s, uint8_t addr)
+{
+ return CODEC_REG(s, addr);
+}
+
+static void codec_write(ViaAC97State *s, uint8_t addr, uint16_t val)
+{
+ trace_via_ac97_codec_write(addr, val);
+ switch (addr) {
+ case AC97_Reset:
+ codec_reset(s);
+ return;
+ case AC97_Master_Volume_Mute:
+ case AC97_PCM_Out_Volume_Mute:
+ if (addr == AC97_Master_Volume_Mute) {
+ if (val & BIT(13)) {
+ val |= 0x1f00;
+ }
+ if (val & BIT(5)) {
+ val |= 0x1f;
+ }
+ }
+ CODEC_REG(s, addr) = val & 0x9f1f;
+ codec_volume_set_out(s);
+ return;
+ case AC97_Extended_Audio_Ctrl_Stat:
+ CODEC_REG(s, addr) &= ~EACS_VRA;
+ CODEC_REG(s, addr) |= val & EACS_VRA;
+ if (!(val & EACS_VRA)) {
+ CODEC_REG(s, AC97_PCM_Front_DAC_Rate) = 48000;
+ CODEC_REG(s, AC97_PCM_LR_ADC_Rate) = 48000;
+ open_voice_out(s);
+ }
+ return;
+ case AC97_PCM_Front_DAC_Rate:
+ case AC97_PCM_LR_ADC_Rate:
+ if (CODEC_REG(s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA) {
+ int i;
+ uint16_t rate = val;
+
+ for (i = 0; i < ARRAY_SIZE(codec_rates) - 1; i++) {
+ if (rate < codec_rates[i] +
+ (codec_rates[i + 1] - codec_rates[i]) / 2) {
+ rate = codec_rates[i];
+ break;
+ }
+ }
+ if (rate > 48000) {
+ rate = 48000;
+ }
+ CODEC_REG(s, addr) = rate;
+ open_voice_out(s);
+ }
+ return;
+ case AC97_Powerdown_Ctrl_Stat:
+ CODEC_REG(s, addr) = (val & 0xff00) | (CODEC_REG(s, addr) & 0xff);
+ return;
+ case AC97_Extended_Audio_ID:
+ case AC97_Vendor_ID1:
+ case AC97_Vendor_ID2:
+ /* Read only registers */
+ return;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "via-ac97: Unimplemented codec register 0x%x\n", addr);
+ CODEC_REG(s, addr) = val;
+ }
+}
+
+static void fetch_sgd(ViaAC97SGDChannel *c, PCIDevice *d)
+{
+ uint32_t b[2];
+
+ if (c->curr < c->base) {
+ c->curr = c->base;
+ }
+ if (unlikely(pci_dma_read(d, c->curr, b, sizeof(b)) != MEMTX_OK)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "via-ac97: DMA error reading SGD table\n");
+ return;
+ }
+ c->addr = le32_to_cpu(b[0]);
+ c->clen = le32_to_cpu(b[1]);
+ trace_via_ac97_sgd_fetch(c->curr, c->addr, CLEN_IS_STOP(c) ? 'S' : '-',
+ CLEN_IS_EOL(c) ? 'E' : '-',
+ CLEN_IS_FLAG(c) ? 'F' : '-', CLEN_LEN(c));
+}
+
+static void out_cb(void *opaque, int avail)
+{
+ ViaAC97State *s = opaque;
+ ViaAC97SGDChannel *c = &s->aur;
+ int temp, to_copy, copied;
+ bool stop = false;
+ uint8_t tmpbuf[4096];
+
+ if (c->stat & STAT_PAUSED) {
+ return;
+ }
+ c->stat |= STAT_ACTIVE;
+ while (avail && !stop) {
+ if (!c->clen) {
+ fetch_sgd(c, &s->dev);
+ }
+ temp = MIN(CLEN_LEN(c), avail);
+ while (temp) {
+ to_copy = MIN(temp, sizeof(tmpbuf));
+ pci_dma_read(&s->dev, c->addr, tmpbuf, to_copy);
+ copied = AUD_write(s->vo, tmpbuf, to_copy);
+ if (!copied) {
+ stop = true;
+ break;
+ }
+ temp -= copied;
+ avail -= copied;
+ c->addr += copied;
+ c->clen -= copied;
+ }
+ if (CLEN_LEN(c) == 0) {
+ c->curr += 8;
+ if (CLEN_IS_EOL(c)) {
+ c->stat |= STAT_EOL;
+ if (c->type & CNTL_START) {
+ c->curr = c->base;
+ c->stat |= STAT_PAUSED;
+ } else {
+ c->stat &= ~STAT_ACTIVE;
+ AUD_set_active_out(s->vo, 0);
+ }
+ if (c->type & STAT_EOL) {
+ pci_set_irq(&s->dev, 1);
+ }
+ }
+ if (CLEN_IS_FLAG(c)) {
+ c->stat |= STAT_FLAG;
+ c->stat |= STAT_PAUSED;
+ if (c->type & STAT_FLAG) {
+ pci_set_irq(&s->dev, 1);
+ }
+ }
+ if (CLEN_IS_STOP(c)) {
+ c->stat |= STAT_STOP;
+ c->stat |= STAT_PAUSED;
+ }
+ c->clen = 0;
+ stop = true;
+ }
+ }
+}
+
+static void open_voice_out(ViaAC97State *s)
+{
+ struct audsettings as = {
+ .freq = CODEC_REG(s, AC97_PCM_Front_DAC_Rate),
+ .nchannels = s->aur.type & BIT(4) ? 2 : 1,
+ .fmt = s->aur.type & BIT(5) ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_S8,
+ .endianness = 0,
+ };
+ s->vo = AUD_open_out(&s->card, s->vo, "via-ac97.out", s, out_cb, &as);
+}
+
+static uint64_t sgd_read(void *opaque, hwaddr addr, unsigned size)
+{
+ ViaAC97State *s = opaque;
+ uint64_t val = 0;
+
+ switch (addr) {
+ case 0:
+ val = s->aur.stat;
+ if (s->aur.type & CNTL_START) {
+ val |= STAT_TRIG;
+ }
+ break;
+ case 1:
+ val = s->aur.stat & STAT_PAUSED ? BIT(3) : 0;
+ break;
+ case 2:
+ val = s->aur.type;
+ break;
+ case 4:
+ val = s->aur.curr;
+ break;
+ case 0xc:
+ val = CLEN_LEN(&s->aur);
+ break;
+ case 0x10:
+ /* silence unimplemented log message that happens at every IRQ */
+ break;
+ case 0x80:
+ val = s->ac97_cmd;
+ break;
+ case 0x84:
+ val = s->aur.stat & STAT_FLAG;
+ if (s->aur.stat & STAT_EOL) {
+ val |= BIT(4);
+ }
+ if (s->aur.stat & STAT_STOP) {
+ val |= BIT(8);
+ }
+ if (s->aur.stat & STAT_ACTIVE) {
+ val |= BIT(12);
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "via-ac97: Unimplemented register read 0x%"
+ HWADDR_PRIx"\n", addr);
+ }
+ trace_via_ac97_sgd_read(addr, size, val);
+ return val;
+}
+
+static void sgd_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+ ViaAC97State *s = opaque;
+
+ trace_via_ac97_sgd_write(addr, size, val);
+ switch (addr) {
+ case 0:
+ if (val & STAT_STOP) {
+ s->aur.stat &= ~STAT_PAUSED;
+ }
+ if (val & STAT_EOL) {
+ s->aur.stat &= ~(STAT_EOL | STAT_PAUSED);
+ if (s->aur.type & STAT_EOL) {
+ pci_set_irq(&s->dev, 0);
+ }
+ }
+ if (val & STAT_FLAG) {
+ s->aur.stat &= ~(STAT_FLAG | STAT_PAUSED);
+ if (s->aur.type & STAT_FLAG) {
+ pci_set_irq(&s->dev, 0);
+ }
+ }
+ break;
+ case 1:
+ if (val & CNTL_START) {
+ AUD_set_active_out(s->vo, 1);
+ s->aur.stat = STAT_ACTIVE;
+ }
+ if (val & CNTL_TERM) {
+ AUD_set_active_out(s->vo, 0);
+ s->aur.stat &= ~(STAT_ACTIVE | STAT_PAUSED);
+ s->aur.clen = 0;
+ }
+ if (val & CNTL_PAUSE) {
+ AUD_set_active_out(s->vo, 0);
+ s->aur.stat &= ~STAT_ACTIVE;
+ s->aur.stat |= STAT_PAUSED;
+ } else if (!(val & CNTL_PAUSE) && (s->aur.stat & STAT_PAUSED)) {
+ AUD_set_active_out(s->vo, 1);
+ s->aur.stat |= STAT_ACTIVE;
+ s->aur.stat &= ~STAT_PAUSED;
+ }
+ break;
+ case 2:
+ {
+ uint32_t oldval = s->aur.type;
+ s->aur.type = val;
+ if ((oldval & 0x30) != (val & 0x30)) {
+ open_voice_out(s);
+ }
+ break;
+ }
+ case 4:
+ s->aur.base = val & ~1ULL;
+ s->aur.curr = s->aur.base;
+ break;
+ case 0x80:
+ if (val >> 30) {
+ /* we only have primary codec */
+ break;
+ }
+ if (val & BIT(23)) { /* read reg */
+ s->ac97_cmd = val & 0xc0ff0000ULL;
+ s->ac97_cmd |= codec_read(s, (val >> 16) & 0x7f);
+ s->ac97_cmd |= BIT(25); /* data valid */
+ } else {
+ s->ac97_cmd = val & 0xc0ffffffULL;
+ codec_write(s, (val >> 16) & 0x7f, val);
+ }
+ break;
+ case 0xc:
+ case 0x84:
+ /* Read only */
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "via-ac97: Unimplemented register write 0x%"
+ HWADDR_PRIx"\n", addr);
+ }
+}
+
+static const MemoryRegionOps sgd_ops = {
+ .read = sgd_read,
+ .write = sgd_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t fm_read(void *opaque, hwaddr addr, unsigned size)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: 0x%"HWADDR_PRIx" %d\n", __func__, addr, size);
+ return 0;
+}
+
+static void fm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: 0x%"HWADDR_PRIx" %d <= 0x%"PRIX64"\n",
+ __func__, addr, size, val);
+}
+
+static const MemoryRegionOps fm_ops = {
+ .read = fm_read,
+ .write = fm_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t midi_read(void *opaque, hwaddr addr, unsigned size)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: 0x%"HWADDR_PRIx" %d\n", __func__, addr, size);
+ return 0;
+}
+
+static void midi_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: 0x%"HWADDR_PRIx" %d <= 0x%"PRIX64"\n",
+ __func__, addr, size, val);
+}
+
+static const MemoryRegionOps midi_ops = {
+ .read = midi_read,
+ .write = midi_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void via_ac97_reset(DeviceState *dev)
+{
+ ViaAC97State *s = VIA_AC97(dev);
+
+ codec_reset(s);
+}
static void via_ac97_realize(PCIDevice *pci_dev, Error **errp)
{
- pci_set_word(pci_dev->config + PCI_COMMAND,
- PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY);
+ ViaAC97State *s = VIA_AC97(pci_dev);
+ Object *o = OBJECT(s);
+
+ /*
+ * Command register Bus Master bit is documented to be fixed at 0 but it's
+ * needed for PCI DMA to work in QEMU. The pegasos2 firmware writes 0 here
+ * and the AmigaOS driver writes 1 only enabling IO bit which works on
+ * real hardware. So set it here and fix it to 1 to allow DMA.
+ */
+ pci_set_word(pci_dev->config + PCI_COMMAND, PCI_COMMAND_MASTER);
+ pci_set_word(pci_dev->wmask + PCI_COMMAND, PCI_COMMAND_IO);
pci_set_word(pci_dev->config + PCI_STATUS,
PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_MEDIUM);
pci_set_long(pci_dev->config + PCI_INTERRUPT_PIN, 0x03);
+ pci_set_byte(pci_dev->config + 0x40, 1); /* codec ready */
+
+ memory_region_init_io(&s->sgd, o, &sgd_ops, s, "via-ac97.sgd", 256);
+ pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->sgd);
+ memory_region_init_io(&s->fm, o, &fm_ops, s, "via-ac97.fm", 4);
+ pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->fm);
+ memory_region_init_io(&s->midi, o, &midi_ops, s, "via-ac97.midi", 4);
+ pci_register_bar(pci_dev, 2, PCI_BASE_ADDRESS_SPACE_IO, &s->midi);
+
+ AUD_register_card ("via-ac97", &s->card);
}
+static void via_ac97_exit(PCIDevice *dev)
+{
+ ViaAC97State *s = VIA_AC97(dev);
+
+ AUD_close_out(&s->card, s->vo);
+ AUD_remove_card(&s->card);
+}
+
+static Property via_ac97_properties[] = {
+ DEFINE_AUDIO_PROPERTIES(ViaAC97State, card),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void via_ac97_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->realize = via_ac97_realize;
+ k->exit = via_ac97_exit;
k->vendor_id = PCI_VENDOR_ID_VIA;
k->device_id = PCI_DEVICE_ID_VIA_AC97;
k->revision = 0x50;
k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO;
+ device_class_set_props(dc, via_ac97_properties);
set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
dc->desc = "VIA AC97";
+ dc->reset = via_ac97_reset;
/* Reason: Part of a south bridge chip */
dc->user_creatable = false;
}
@@ -41,7 +484,7 @@
static const TypeInfo via_ac97_info = {
.name = TYPE_VIA_AC97,
.parent = TYPE_PCI_DEVICE,
- .instance_size = sizeof(PCIDevice),
+ .instance_size = sizeof(ViaAC97State),
.class_init = via_ac97_class_init,
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
diff --git a/hw/block/block.c b/hw/block/block.c
index af0710e..9f52ee6 100644
--- a/hw/block/block.c
+++ b/hw/block/block.c
@@ -39,8 +39,7 @@
return ret;
}
if (!(ret & BDRV_BLOCK_ZERO)) {
- ret = bdrv_pread(bs->file, offset, bytes,
- (uint8_t *) buf + offset, 0);
+ ret = blk_pread(blk, offset, bytes, (uint8_t *) buf + offset, 0);
if (ret < 0) {
return ret;
}
diff --git a/hw/block/dataplane/meson.build b/hw/block/dataplane/meson.build
index 12c6a26..78d7ac1 100644
--- a/hw/block/dataplane/meson.build
+++ b/hw/block/dataplane/meson.build
@@ -1,2 +1,2 @@
specific_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk.c'))
-specific_ss.add(when: 'CONFIG_XEN', if_true: files('xen-block.c'))
+specific_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen-block.c'))
diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c
index 2785b9e..734da42 100644
--- a/hw/block/dataplane/xen-block.c
+++ b/hw/block/dataplane/xen-block.c
@@ -23,8 +23,9 @@
#include "qemu/main-loop.h"
#include "qemu/memalign.h"
#include "qapi/error.h"
-#include "hw/xen/xen_common.h"
+#include "hw/xen/xen.h"
#include "hw/block/xen_blkif.h"
+#include "hw/xen/interface/io/ring.h"
#include "sysemu/block-backend.h"
#include "sysemu/iothread.h"
#include "xen-block.h"
@@ -101,9 +102,9 @@
* re-use requests, allocate the memory once here. It will be freed
* xen_block_dataplane_destroy() when the request list is freed.
*/
- request->buf = qemu_memalign(XC_PAGE_SIZE,
+ request->buf = qemu_memalign(XEN_PAGE_SIZE,
BLKIF_MAX_SEGMENTS_PER_REQUEST *
- XC_PAGE_SIZE);
+ XEN_PAGE_SIZE);
dataplane->requests_total++;
qemu_iovec_init(&request->v, 1);
} else {
@@ -185,7 +186,7 @@
goto err;
}
if (request->req.seg[i].last_sect * dataplane->sector_size >=
- XC_PAGE_SIZE) {
+ XEN_PAGE_SIZE) {
error_report("error: page crossing");
goto err;
}
@@ -705,6 +706,7 @@
Error *local_err = NULL;
xen_device_unmap_grant_refs(xendev, dataplane->sring,
+ dataplane->ring_ref,
dataplane->nr_ring_ref, &local_err);
dataplane->sring = NULL;
@@ -739,7 +741,7 @@
dataplane->protocol = protocol;
- ring_size = XC_PAGE_SIZE * dataplane->nr_ring_ref;
+ ring_size = XEN_PAGE_SIZE * dataplane->nr_ring_ref;
switch (dataplane->protocol) {
case BLKIF_PROTOCOL_NATIVE:
{
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index 802d2eb..dc5ffbc 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -24,6 +24,7 @@
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "sysemu/block-backend.h"
+#include "hw/block/block.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "hw/ssi/ssi.h"
@@ -1615,8 +1616,7 @@
trace_m25p80_binding(s);
s->storage = blk_blockalign(s->blk, s->size);
- if (blk_pread(s->blk, 0, s->size, s->storage, 0) < 0) {
- error_setg(errp, "failed to read the initial flash content");
+ if (!blk_check_size_and_read_all(s->blk, s->storage, s->size, errp)) {
return;
}
} else {
diff --git a/hw/block/meson.build b/hw/block/meson.build
index b434d56..cc2a75c 100644
--- a/hw/block/meson.build
+++ b/hw/block/meson.build
@@ -14,7 +14,7 @@
softmmu_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80.c'))
softmmu_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80_sfdp.c'))
softmmu_ss.add(when: 'CONFIG_SWIM', if_true: files('swim.c'))
-softmmu_ss.add(when: 'CONFIG_XEN', if_true: files('xen-block.c'))
+softmmu_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen-block.c'))
softmmu_ss.add(when: 'CONFIG_TC58128', if_true: files('tc58128.c'))
specific_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk.c', 'virtio-blk-common.c'))
diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c
index 345b284..f5a7445 100644
--- a/hw/block/xen-block.c
+++ b/hw/block/xen-block.c
@@ -19,7 +19,6 @@
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qom/object_interfaces.h"
-#include "hw/xen/xen_common.h"
#include "hw/block/xen_blkif.h"
#include "hw/qdev-properties.h"
#include "hw/xen/xen-block.h"
@@ -84,7 +83,8 @@
g_free(ring_ref);
return;
}
- } else if (order <= blockdev->props.max_ring_page_order) {
+ } else if (qemu_xen_gnttab_can_map_multi() &&
+ order <= blockdev->props.max_ring_page_order) {
unsigned int i;
nr_ring_ref = 1 << order;
@@ -256,8 +256,12 @@
}
xen_device_backend_printf(xendev, "feature-flush-cache", "%u", 1);
- xen_device_backend_printf(xendev, "max-ring-page-order", "%u",
- blockdev->props.max_ring_page_order);
+
+ if (qemu_xen_gnttab_can_map_multi()) {
+ xen_device_backend_printf(xendev, "max-ring-page-order", "%u",
+ blockdev->props.max_ring_page_order);
+ }
+
xen_device_backend_printf(xendev, "info", "%u", blockdev->info);
xen_device_frontend_printf(xendev, "virtual-device", "%lu",
diff --git a/hw/char/meson.build b/hw/char/meson.build
index 7b594f5..e02c60d 100644
--- a/hw/char/meson.build
+++ b/hw/char/meson.build
@@ -18,7 +18,7 @@
softmmu_ss.add(when: 'CONFIG_SERIAL_PCI_MULTI', if_true: files('serial-pci-multi.c'))
softmmu_ss.add(when: 'CONFIG_SHAKTI_UART', if_true: files('shakti_uart.c'))
softmmu_ss.add(when: 'CONFIG_VIRTIO_SERIAL', if_true: files('virtio-console.c'))
-softmmu_ss.add(when: 'CONFIG_XEN', if_true: files('xen_console.c'))
+softmmu_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen_console.c'))
softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_uartlite.c'))
softmmu_ss.add(when: 'CONFIG_AVR_USART', if_true: files('avr_usart.c'))
diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c
index 63153df..c7a19c0 100644
--- a/hw/char/xen_console.c
+++ b/hw/char/xen_console.c
@@ -173,6 +173,48 @@
/* -------------------------------------------------------------------- */
+static int store_con_info(struct XenConsole *con)
+{
+ Chardev *cs = qemu_chr_fe_get_driver(&con->chr);
+ char *pts = NULL;
+ char *dom_path;
+ GString *path;
+ int ret = -1;
+
+ /* Only continue if we're talking to a pty. */
+ if (!CHARDEV_IS_PTY(cs)) {
+ return 0;
+ }
+ pts = cs->filename + 4;
+
+ dom_path = qemu_xen_xs_get_domain_path(xenstore, xen_domid);
+ if (!dom_path) {
+ return 0;
+ }
+
+ path = g_string_new(dom_path);
+ free(dom_path);
+
+ if (con->xendev.dev) {
+ g_string_append_printf(path, "/device/console/%d", con->xendev.dev);
+ } else {
+ g_string_append(path, "/console");
+ }
+ g_string_append(path, "/tty");
+
+ if (xenstore_write_str(con->console, path->str, pts)) {
+ fprintf(stderr, "xenstore_write_str for '%s' fail", path->str);
+ goto out;
+ }
+ ret = 0;
+
+out:
+ g_string_free(path, true);
+ free(path);
+
+ return ret;
+}
+
static int con_init(struct XenLegacyDevice *xendev)
{
struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
@@ -181,7 +223,7 @@
const char *output;
/* setup */
- dom = xs_get_domain_path(xenstore, con->xendev.dom);
+ dom = qemu_xen_xs_get_domain_path(xenstore, con->xendev.dom);
if (!xendev->dev) {
snprintf(con->console, sizeof(con->console), "%s/console", dom);
} else {
@@ -215,8 +257,7 @@
&error_abort);
}
- xenstore_store_pv_console_info(con->xendev.dev,
- qemu_chr_fe_get_driver(&con->chr));
+ store_con_info(con);
out:
g_free(type);
@@ -237,9 +278,9 @@
if (!xendev->dev) {
xen_pfn_t mfn = con->ring_ref;
- con->sring = xenforeignmemory_map(xen_fmem, con->xendev.dom,
- PROT_READ | PROT_WRITE,
- 1, &mfn, NULL);
+ con->sring = qemu_xen_foreignmem_map(con->xendev.dom, NULL,
+ PROT_READ | PROT_WRITE,
+ 1, &mfn, NULL);
} else {
con->sring = xen_be_map_grant_ref(xendev, con->ring_ref,
PROT_READ | PROT_WRITE);
@@ -269,9 +310,9 @@
if (con->sring) {
if (!xendev->dev) {
- xenforeignmemory_unmap(xen_fmem, con->sring, 1);
+ qemu_xen_foreignmem_unmap(con->sring, 1);
} else {
- xen_be_unmap_grant_ref(xendev, con->sring);
+ xen_be_unmap_grant_ref(xendev, con->sring, con->ring_ref);
}
con->sring = NULL;
}
diff --git a/hw/display/meson.build b/hw/display/meson.build
index f470179..4191694 100644
--- a/hw/display/meson.build
+++ b/hw/display/meson.build
@@ -14,7 +14,7 @@
softmmu_ss.add(when: 'CONFIG_SII9022', if_true: files('sii9022.c'))
softmmu_ss.add(when: 'CONFIG_SSD0303', if_true: files('ssd0303.c'))
softmmu_ss.add(when: 'CONFIG_SSD0323', if_true: files('ssd0323.c'))
-softmmu_ss.add(when: 'CONFIG_XEN', if_true: files('xenfb.c'))
+softmmu_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xenfb.c'))
softmmu_ss.add(when: 'CONFIG_VGA_PCI', if_true: files('vga-pci.c'))
softmmu_ss.add(when: 'CONFIG_VGA_ISA', if_true: files('vga-isa.c'))
diff --git a/hw/display/sm501.c b/hw/display/sm501.c
index 1783515..dbabbc4 100644
--- a/hw/display/sm501.c
+++ b/hw/display/sm501.c
@@ -465,6 +465,7 @@
uint32_t last_width;
uint32_t last_height;
bool do_full_update; /* perform a full update next time */
+ uint8_t use_pixman;
I2CBus *i2c_bus;
/* mmio registers */
@@ -827,7 +828,7 @@
de = db + (width + (height - 1) * dst_pitch) * bypp;
overlap = (db < se && sb < de);
}
- if (overlap) {
+ if (overlap && (s->use_pixman & BIT(2))) {
/* pixman can't do reverse blit: copy via temporary */
int tmp_stride = DIV_ROUND_UP(width * bypp, sizeof(uint32_t));
uint32_t *tmp = tmp_buf;
@@ -852,13 +853,15 @@
if (tmp != tmp_buf) {
g_free(tmp);
}
- } else {
+ } else if (!overlap && (s->use_pixman & BIT(1))) {
fallback = !pixman_blt((uint32_t *)&s->local_mem[src_base],
(uint32_t *)&s->local_mem[dst_base],
src_pitch * bypp / sizeof(uint32_t),
dst_pitch * bypp / sizeof(uint32_t),
8 * bypp, 8 * bypp, src_x, src_y,
dst_x, dst_y, width, height);
+ } else {
+ fallback = true;
}
if (fallback) {
uint8_t *sp = s->local_mem + src_base;
@@ -891,7 +894,7 @@
color = cpu_to_le16(color);
}
- if ((width == 1 && height == 1) ||
+ if (!(s->use_pixman & BIT(0)) || (width == 1 && height == 1) ||
!pixman_fill((uint32_t *)&s->local_mem[dst_base],
dst_pitch * bypp / sizeof(uint32_t), 8 * bypp,
dst_x, dst_y, width, height, color)) {
@@ -2035,6 +2038,7 @@
static Property sm501_sysbus_properties[] = {
DEFINE_PROP_UINT32("vram-size", SM501SysBusState, vram_size, 0),
+ DEFINE_PROP_UINT8("x-pixman", SM501SysBusState, state.use_pixman, 7),
DEFINE_PROP_END_OF_LIST(),
};
@@ -2122,6 +2126,7 @@
static Property sm501_pci_properties[] = {
DEFINE_PROP_UINT32("vram-size", SM501PCIState, vram_size, 64 * MiB),
+ DEFINE_PROP_UINT8("x-pixman", SM501PCIState, state.use_pixman, 7),
DEFINE_PROP_END_OF_LIST(),
};
@@ -2162,11 +2167,18 @@
dc->vmsd = &vmstate_sm501_pci;
}
+static void sm501_pci_init(Object *o)
+{
+ object_property_set_description(o, "x-pixman", "Use pixman for: "
+ "1: fill, 2: blit, 4: overlap blit");
+}
+
static const TypeInfo sm501_pci_info = {
.name = TYPE_PCI_SM501,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(SM501PCIState),
.class_init = sm501_pci_class_init,
+ .instance_init = sm501_pci_init,
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ },
diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c
index 260eb38..0074a9b 100644
--- a/hw/display/xenfb.c
+++ b/hw/display/xenfb.c
@@ -98,8 +98,9 @@
if (xenstore_read_fe_int(&c->xendev, "event-channel", &c->xendev.remote_port) == -1)
return -1;
- c->page = xenforeignmemory_map(xen_fmem, c->xendev.dom,
- PROT_READ | PROT_WRITE, 1, &mfn, NULL);
+ c->page = qemu_xen_foreignmem_map(c->xendev.dom, NULL,
+ PROT_READ | PROT_WRITE, 1, &mfn,
+ NULL);
if (c->page == NULL)
return -1;
@@ -115,7 +116,7 @@
{
xen_pv_unbind_evtchn(&c->xendev);
if (c->page) {
- xenforeignmemory_unmap(xen_fmem, c->page, 1);
+ qemu_xen_foreignmem_unmap(c->page, 1);
c->page = NULL;
}
}
@@ -488,27 +489,28 @@
}
if (xenfb->pixels) {
- munmap(xenfb->pixels, xenfb->fbpages * XC_PAGE_SIZE);
+ munmap(xenfb->pixels, xenfb->fbpages * XEN_PAGE_SIZE);
xenfb->pixels = NULL;
}
- xenfb->fbpages = DIV_ROUND_UP(xenfb->fb_len, XC_PAGE_SIZE);
+ xenfb->fbpages = DIV_ROUND_UP(xenfb->fb_len, XEN_PAGE_SIZE);
n_fbdirs = xenfb->fbpages * mode / 8;
- n_fbdirs = DIV_ROUND_UP(n_fbdirs, XC_PAGE_SIZE);
+ n_fbdirs = DIV_ROUND_UP(n_fbdirs, XEN_PAGE_SIZE);
pgmfns = g_new0(xen_pfn_t, n_fbdirs);
fbmfns = g_new0(xen_pfn_t, xenfb->fbpages);
xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd);
- map = xenforeignmemory_map(xen_fmem, xenfb->c.xendev.dom,
- PROT_READ, n_fbdirs, pgmfns, NULL);
+ map = qemu_xen_foreignmem_map(xenfb->c.xendev.dom, NULL, PROT_READ,
+ n_fbdirs, pgmfns, NULL);
if (map == NULL)
goto out;
xenfb_copy_mfns(mode, xenfb->fbpages, fbmfns, map);
- xenforeignmemory_unmap(xen_fmem, map, n_fbdirs);
+ qemu_xen_foreignmem_unmap(map, n_fbdirs);
- xenfb->pixels = xenforeignmemory_map(xen_fmem, xenfb->c.xendev.dom,
- PROT_READ, xenfb->fbpages, fbmfns, NULL);
+ xenfb->pixels = qemu_xen_foreignmem_map(xenfb->c.xendev.dom, NULL,
+ PROT_READ, xenfb->fbpages,
+ fbmfns, NULL);
if (xenfb->pixels == NULL)
goto out;
@@ -526,8 +528,8 @@
{
size_t mfn_sz = sizeof_field(struct xenfb_page, pd[0]);
size_t pd_len = sizeof_field(struct xenfb_page, pd) / mfn_sz;
- size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz;
- size_t fb_len_max = fb_pages * XC_PAGE_SIZE;
+ size_t fb_pages = pd_len * XEN_PAGE_SIZE / mfn_sz;
+ size_t fb_len_max = fb_pages * XEN_PAGE_SIZE;
int max_width, max_height;
if (fb_len_lim > fb_len_max) {
@@ -927,8 +929,8 @@
* Replacing the framebuffer with anonymous shared memory
* instead. This releases the guest pages and keeps qemu happy.
*/
- xenforeignmemory_unmap(xen_fmem, fb->pixels, fb->fbpages);
- fb->pixels = mmap(fb->pixels, fb->fbpages * XC_PAGE_SIZE,
+ qemu_xen_foreignmem_unmap(fb->pixels, fb->fbpages);
+ fb->pixels = mmap(fb->pixels, fb->fbpages * XEN_PAGE_SIZE,
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON,
-1, 0);
if (fb->pixels == MAP_FAILED) {
diff --git a/hw/i386/kvm/meson.build b/hw/i386/kvm/meson.build
index 82dd6ae..6621ba5 100644
--- a/hw/i386/kvm/meson.build
+++ b/hw/i386/kvm/meson.build
@@ -9,6 +9,7 @@
'xen_evtchn.c',
'xen_gnttab.c',
'xen_xenstore.c',
+ 'xenstore_impl.c',
))
i386_ss.add_all(when: 'CONFIG_KVM', if_true: i386_kvm_ss)
diff --git a/hw/i386/kvm/trace-events b/hw/i386/kvm/trace-events
index b83c3eb..e4c82de 100644
--- a/hw/i386/kvm/trace-events
+++ b/hw/i386/kvm/trace-events
@@ -3,3 +3,18 @@
kvm_xen_get_free_pirq(int pirq, int type) "pirq %d type %d"
kvm_xen_bind_pirq(int pirq, int port) "pirq %d port %d"
kvm_xen_unmask_pirq(int pirq, char *dev, int vector) "pirq %d dev %s vector %d"
+xenstore_error(unsigned int id, unsigned int tx_id, const char *err) "req %u tx %u err %s"
+xenstore_read(unsigned int tx_id, const char *path) "tx %u path %s"
+xenstore_write(unsigned int tx_id, const char *path) "tx %u path %s"
+xenstore_mkdir(unsigned int tx_id, const char *path) "tx %u path %s"
+xenstore_directory(unsigned int tx_id, const char *path) "tx %u path %s"
+xenstore_directory_part(unsigned int tx_id, const char *path, unsigned int offset) "tx %u path %s offset %u"
+xenstore_transaction_start(unsigned int new_tx) "new_tx %u"
+xenstore_transaction_end(unsigned int tx_id, bool commit) "tx %u commit %d"
+xenstore_rm(unsigned int tx_id, const char *path) "tx %u path %s"
+xenstore_get_perms(unsigned int tx_id, const char *path) "tx %u path %s"
+xenstore_set_perms(unsigned int tx_id, const char *path) "tx %u path %s"
+xenstore_watch(const char *path, const char *token) "path %s token %s"
+xenstore_unwatch(const char *path, const char *token) "path %s token %s"
+xenstore_reset_watches(void) ""
+xenstore_watch_event(const char *path, const char *token) "path %s token %s"
diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c
index 886fbf6..98a7b85 100644
--- a/hw/i386/kvm/xen_evtchn.c
+++ b/hw/i386/kvm/xen_evtchn.c
@@ -34,6 +34,7 @@
#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
#include "hw/irq.h"
+#include "hw/xen/xen_backend_ops.h"
#include "xen_evtchn.h"
#include "xen_overlay.h"
@@ -278,6 +279,17 @@
.class_init = xen_evtchn_class_init,
};
+static struct evtchn_backend_ops emu_evtchn_backend_ops = {
+ .open = xen_be_evtchn_open,
+ .bind_interdomain = xen_be_evtchn_bind_interdomain,
+ .unbind = xen_be_evtchn_unbind,
+ .close = xen_be_evtchn_close,
+ .get_fd = xen_be_evtchn_fd,
+ .notify = xen_be_evtchn_notify,
+ .unmask = xen_be_evtchn_unmask,
+ .pending = xen_be_evtchn_pending,
+};
+
static void gsi_assert_bh(void *opaque)
{
struct vcpu_info *vi = kvm_xen_get_vcpu_info_hva(0);
@@ -318,6 +330,9 @@
s->nr_pirq_inuse_words = DIV_ROUND_UP(s->nr_pirqs, 64);
s->pirq_inuse_bitmap = g_new0(uint64_t, s->nr_pirq_inuse_words);
s->pirq = g_new0(struct pirq_info, s->nr_pirqs);
+
+ /* Set event channel functions for backend drivers to use */
+ xen_evtchn_ops = &emu_evtchn_backend_ops;
}
void xen_evtchn_connect_gsis(qemu_irq *system_gsis)
diff --git a/hw/i386/kvm/xen_gnttab.c b/hw/i386/kvm/xen_gnttab.c
index 1e691de..21c30e3 100644
--- a/hw/i386/kvm/xen_gnttab.c
+++ b/hw/i386/kvm/xen_gnttab.c
@@ -22,6 +22,7 @@
#include "hw/sysbus.h"
#include "hw/xen/xen.h"
+#include "hw/xen/xen_backend_ops.h"
#include "xen_overlay.h"
#include "xen_gnttab.h"
@@ -34,11 +35,10 @@
#define TYPE_XEN_GNTTAB "xen-gnttab"
OBJECT_DECLARE_SIMPLE_TYPE(XenGnttabState, XEN_GNTTAB)
-#define XEN_PAGE_SHIFT 12
-#define XEN_PAGE_SIZE (1ULL << XEN_PAGE_SHIFT)
-
#define ENTRIES_PER_FRAME_V1 (XEN_PAGE_SIZE / sizeof(grant_entry_v1_t))
+static struct gnttab_backend_ops emu_gnttab_backend_ops;
+
struct XenGnttabState {
/*< private >*/
SysBusDevice busdev;
@@ -57,6 +57,8 @@
MemoryRegion gnt_frames;
MemoryRegion *gnt_aliases;
uint64_t *gnt_frame_gpas;
+
+ uint8_t *map_track;
};
struct XenGnttabState *xen_gnttab_singleton;
@@ -70,13 +72,11 @@
error_setg(errp, "Xen grant table support is for Xen emulation");
return;
}
- s->nr_frames = 0;
s->max_frames = kvm_xen_get_gnttab_max_frames();
memory_region_init_ram(&s->gnt_frames, OBJECT(dev), "xen:grant_table",
XEN_PAGE_SIZE * s->max_frames, &error_abort);
memory_region_set_enabled(&s->gnt_frames, true);
s->entries.v1 = memory_region_get_ram_ptr(&s->gnt_frames);
- memset(s->entries.v1, 0, XEN_PAGE_SIZE * s->max_frames);
/* Create individual page-sizes aliases for overlays */
s->gnt_aliases = (void *)g_new0(MemoryRegion, s->max_frames);
@@ -88,9 +88,18 @@
s->gnt_frame_gpas[i] = INVALID_GPA;
}
+ s->nr_frames = 0;
+ memset(s->entries.v1, 0, XEN_PAGE_SIZE * s->max_frames);
+ s->entries.v1[GNTTAB_RESERVED_XENSTORE].flags = GTF_permit_access;
+ s->entries.v1[GNTTAB_RESERVED_XENSTORE].frame = XEN_SPECIAL_PFN(XENSTORE);
+
qemu_mutex_init(&s->gnt_lock);
xen_gnttab_singleton = s;
+
+ s->map_track = g_new0(uint8_t, s->max_frames * ENTRIES_PER_FRAME_V1);
+
+ xen_gnttab_ops = &emu_gnttab_backend_ops;
}
static int xen_gnttab_post_load(void *opaque, int version_id)
@@ -230,3 +239,309 @@
size->max_nr_frames = s->max_frames;
return 0;
}
+
+/* Track per-open refs, to allow close() to clean up. */
+struct active_ref {
+ MemoryRegionSection mrs;
+ void *virtaddr;
+ uint32_t refcnt;
+ int prot;
+};
+
+static void gnt_unref(XenGnttabState *s, grant_ref_t ref,
+ MemoryRegionSection *mrs, int prot)
+{
+ if (mrs && mrs->mr) {
+ if (prot & PROT_WRITE) {
+ memory_region_set_dirty(mrs->mr, mrs->offset_within_region,
+ XEN_PAGE_SIZE);
+ }
+ memory_region_unref(mrs->mr);
+ mrs->mr = NULL;
+ }
+ assert(s->map_track[ref] != 0);
+
+ if (--s->map_track[ref] == 0) {
+ grant_entry_v1_t *gnt_p = &s->entries.v1[ref];
+ qatomic_and(&gnt_p->flags, (uint16_t)~(GTF_reading | GTF_writing));
+ }
+}
+
+static uint64_t gnt_ref(XenGnttabState *s, grant_ref_t ref, int prot)
+{
+ uint16_t mask = GTF_type_mask | GTF_sub_page;
+ grant_entry_v1_t gnt, *gnt_p;
+ int retries = 0;
+
+ if (ref >= s->max_frames * ENTRIES_PER_FRAME_V1 ||
+ s->map_track[ref] == UINT8_MAX) {
+ return INVALID_GPA;
+ }
+
+ if (prot & PROT_WRITE) {
+ mask |= GTF_readonly;
+ }
+
+ gnt_p = &s->entries.v1[ref];
+
+ /*
+ * The guest can legitimately be changing the GTF_readonly flag. Allow
+ * that, but don't let a malicious guest cause a livelock.
+ */
+ for (retries = 0; retries < 5; retries++) {
+ uint16_t new_flags;
+
+ /* Read the entry before an atomic operation on its flags */
+ gnt = *(volatile grant_entry_v1_t *)gnt_p;
+
+ if ((gnt.flags & mask) != GTF_permit_access ||
+ gnt.domid != DOMID_QEMU) {
+ return INVALID_GPA;
+ }
+
+ new_flags = gnt.flags | GTF_reading;
+ if (prot & PROT_WRITE) {
+ new_flags |= GTF_writing;
+ }
+
+ if (qatomic_cmpxchg(&gnt_p->flags, gnt.flags, new_flags) == gnt.flags) {
+ return (uint64_t)gnt.frame << XEN_PAGE_SHIFT;
+ }
+ }
+
+ return INVALID_GPA;
+}
+
+struct xengntdev_handle {
+ GHashTable *active_maps;
+};
+
+static int xen_be_gnttab_set_max_grants(struct xengntdev_handle *xgt,
+ uint32_t nr_grants)
+{
+ return 0;
+}
+
+static void *xen_be_gnttab_map_refs(struct xengntdev_handle *xgt,
+ uint32_t count, uint32_t domid,
+ uint32_t *refs, int prot)
+{
+ XenGnttabState *s = xen_gnttab_singleton;
+ struct active_ref *act;
+
+ if (!s) {
+ errno = ENOTSUP;
+ return NULL;
+ }
+
+ if (domid != xen_domid) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (!count || count > 4096) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /*
+ * Making a contiguous mapping from potentially discontiguous grant
+ * references would be... distinctly non-trivial. We don't support it.
+ * Even changing the API to return an array of pointers, one per page,
+ * wouldn't be simple to use in PV backends because some structures
+ * actually cross page boundaries (e.g. 32-bit blkif_response ring
+ * entries are 12 bytes).
+ */
+ if (count != 1) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ QEMU_LOCK_GUARD(&s->gnt_lock);
+
+ act = g_hash_table_lookup(xgt->active_maps, GINT_TO_POINTER(refs[0]));
+ if (act) {
+ if ((prot & PROT_WRITE) && !(act->prot & PROT_WRITE)) {
+ if (gnt_ref(s, refs[0], prot) == INVALID_GPA) {
+ return NULL;
+ }
+ act->prot |= PROT_WRITE;
+ }
+ act->refcnt++;
+ } else {
+ uint64_t gpa = gnt_ref(s, refs[0], prot);
+ if (gpa == INVALID_GPA) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ act = g_new0(struct active_ref, 1);
+ act->prot = prot;
+ act->refcnt = 1;
+ act->mrs = memory_region_find(get_system_memory(), gpa, XEN_PAGE_SIZE);
+
+ if (act->mrs.mr &&
+ !int128_lt(act->mrs.size, int128_make64(XEN_PAGE_SIZE)) &&
+ memory_region_get_ram_addr(act->mrs.mr) != RAM_ADDR_INVALID) {
+ act->virtaddr = qemu_map_ram_ptr(act->mrs.mr->ram_block,
+ act->mrs.offset_within_region);
+ }
+ if (!act->virtaddr) {
+ gnt_unref(s, refs[0], &act->mrs, 0);
+ g_free(act);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ s->map_track[refs[0]]++;
+ g_hash_table_insert(xgt->active_maps, GINT_TO_POINTER(refs[0]), act);
+ }
+
+ return act->virtaddr;
+}
+
+static gboolean do_unmap(gpointer key, gpointer value, gpointer user_data)
+{
+ XenGnttabState *s = user_data;
+ grant_ref_t gref = GPOINTER_TO_INT(key);
+ struct active_ref *act = value;
+
+ gnt_unref(s, gref, &act->mrs, act->prot);
+ g_free(act);
+ return true;
+}
+
+static int xen_be_gnttab_unmap(struct xengntdev_handle *xgt,
+ void *start_address, uint32_t *refs,
+ uint32_t count)
+{
+ XenGnttabState *s = xen_gnttab_singleton;
+ struct active_ref *act;
+
+ if (!s) {
+ return -ENOTSUP;
+ }
+
+ if (count != 1) {
+ return -EINVAL;
+ }
+
+ QEMU_LOCK_GUARD(&s->gnt_lock);
+
+ act = g_hash_table_lookup(xgt->active_maps, GINT_TO_POINTER(refs[0]));
+ if (!act) {
+ return -ENOENT;
+ }
+
+ if (act->virtaddr != start_address) {
+ return -EINVAL;
+ }
+
+ if (!--act->refcnt) {
+ do_unmap(GINT_TO_POINTER(refs[0]), act, s);
+ g_hash_table_remove(xgt->active_maps, GINT_TO_POINTER(refs[0]));
+ }
+
+ return 0;
+}
+
+/*
+ * This looks a bit like the one for true Xen in xen-operations.c but
+ * in emulation we don't support multi-page mappings. And under Xen we
+ * *want* the multi-page mappings so we have fewer bounces through the
+ * kernel and the hypervisor. So the code paths end up being similar,
+ * but different.
+ */
+static int xen_be_gnttab_copy(struct xengntdev_handle *xgt, bool to_domain,
+ uint32_t domid, XenGrantCopySegment *segs,
+ uint32_t nr_segs, Error **errp)
+{
+ int prot = to_domain ? PROT_WRITE : PROT_READ;
+ unsigned int i;
+
+ for (i = 0; i < nr_segs; i++) {
+ XenGrantCopySegment *seg = &segs[i];
+ void *page;
+ uint32_t ref = to_domain ? seg->dest.foreign.ref :
+ seg->source.foreign.ref;
+
+ page = xen_be_gnttab_map_refs(xgt, 1, domid, &ref, prot);
+ if (!page) {
+ if (errp) {
+ error_setg_errno(errp, errno,
+ "xen_be_gnttab_map_refs failed");
+ }
+ return -errno;
+ }
+
+ if (to_domain) {
+ memcpy(page + seg->dest.foreign.offset, seg->source.virt,
+ seg->len);
+ } else {
+ memcpy(seg->dest.virt, page + seg->source.foreign.offset,
+ seg->len);
+ }
+
+ if (xen_be_gnttab_unmap(xgt, page, &ref, 1)) {
+ if (errp) {
+ error_setg_errno(errp, errno, "xen_be_gnttab_unmap failed");
+ }
+ return -errno;
+ }
+ }
+
+ return 0;
+}
+
+static struct xengntdev_handle *xen_be_gnttab_open(void)
+{
+ struct xengntdev_handle *xgt = g_new0(struct xengntdev_handle, 1);
+
+ xgt->active_maps = g_hash_table_new(g_direct_hash, g_direct_equal);
+ return xgt;
+}
+
+static int xen_be_gnttab_close(struct xengntdev_handle *xgt)
+{
+ XenGnttabState *s = xen_gnttab_singleton;
+
+ if (!s) {
+ return -ENOTSUP;
+ }
+
+ g_hash_table_foreach_remove(xgt->active_maps, do_unmap, s);
+ g_hash_table_destroy(xgt->active_maps);
+ g_free(xgt);
+ return 0;
+}
+
+static struct gnttab_backend_ops emu_gnttab_backend_ops = {
+ .open = xen_be_gnttab_open,
+ .close = xen_be_gnttab_close,
+ .grant_copy = xen_be_gnttab_copy,
+ .set_max_grants = xen_be_gnttab_set_max_grants,
+ .map_refs = xen_be_gnttab_map_refs,
+ .unmap = xen_be_gnttab_unmap,
+};
+
+int xen_gnttab_reset(void)
+{
+ XenGnttabState *s = xen_gnttab_singleton;
+
+ if (!s) {
+ return -ENOTSUP;
+ }
+
+ QEMU_LOCK_GUARD(&s->gnt_lock);
+
+ s->nr_frames = 0;
+
+ memset(s->entries.v1, 0, XEN_PAGE_SIZE * s->max_frames);
+
+ s->entries.v1[GNTTAB_RESERVED_XENSTORE].flags = GTF_permit_access;
+ s->entries.v1[GNTTAB_RESERVED_XENSTORE].frame = XEN_SPECIAL_PFN(XENSTORE);
+
+ memset(s->map_track, 0, s->max_frames * ENTRIES_PER_FRAME_V1);
+
+ return 0;
+}
diff --git a/hw/i386/kvm/xen_gnttab.h b/hw/i386/kvm/xen_gnttab.h
index 3bdbe96..ee21523 100644
--- a/hw/i386/kvm/xen_gnttab.h
+++ b/hw/i386/kvm/xen_gnttab.h
@@ -13,6 +13,7 @@
#define QEMU_XEN_GNTTAB_H
void xen_gnttab_create(void);
+int xen_gnttab_reset(void);
int xen_gnttab_map_page(uint64_t idx, uint64_t gfn);
struct gnttab_set_version;
diff --git a/hw/i386/kvm/xen_xenstore.c b/hw/i386/kvm/xen_xenstore.c
index 14193ef..2cadafd 100644
--- a/hw/i386/kvm/xen_xenstore.c
+++ b/hw/i386/kvm/xen_xenstore.c
@@ -21,6 +21,7 @@
#include "hw/sysbus.h"
#include "hw/xen/xen.h"
+#include "hw/xen/xen_backend_ops.h"
#include "xen_overlay.h"
#include "xen_evtchn.h"
#include "xen_xenstore.h"
@@ -28,15 +29,17 @@
#include "sysemu/kvm.h"
#include "sysemu/kvm_xen.h"
+#include "trace.h"
+
+#include "xenstore_impl.h"
+
#include "hw/xen/interface/io/xs_wire.h"
#include "hw/xen/interface/event_channel.h"
+#include "hw/xen/interface/grant_table.h"
#define TYPE_XEN_XENSTORE "xen-xenstore"
OBJECT_DECLARE_SIMPLE_TYPE(XenXenstoreState, XEN_XENSTORE)
-#define XEN_PAGE_SHIFT 12
-#define XEN_PAGE_SIZE (1ULL << XEN_PAGE_SHIFT)
-
#define ENTRIES_PER_FRAME_V1 (XEN_PAGE_SIZE / sizeof(grant_entry_v1_t))
#define ENTRIES_PER_FRAME_V2 (XEN_PAGE_SIZE / sizeof(grant_entry_v2_t))
@@ -47,6 +50,9 @@
SysBusDevice busdev;
/*< public >*/
+ XenstoreImplState *impl;
+ GList *watch_events; /* for the guest */
+
MemoryRegion xenstore_page;
struct xenstore_domain_interface *xs;
uint8_t req_data[XENSTORE_HEADER_SIZE + XENSTORE_PAYLOAD_MAX];
@@ -59,15 +65,54 @@
evtchn_port_t guest_port;
evtchn_port_t be_port;
struct xenevtchn_handle *eh;
+
+ uint8_t *impl_state;
+ uint32_t impl_state_size;
+
+ struct xengntdev_handle *gt;
+ void *granted_xs;
};
struct XenXenstoreState *xen_xenstore_singleton;
static void xen_xenstore_event(void *opaque);
+static void fire_watch_cb(void *opaque, const char *path, const char *token);
+
+static struct xenstore_backend_ops emu_xenstore_backend_ops;
+
+static void G_GNUC_PRINTF (4, 5) relpath_printf(XenXenstoreState *s,
+ GList *perms,
+ const char *relpath,
+ const char *fmt, ...)
+{
+ gchar *abspath;
+ gchar *value;
+ va_list args;
+ GByteArray *data;
+ int err;
+
+ abspath = g_strdup_printf("/local/domain/%u/%s", xen_domid, relpath);
+ va_start(args, fmt);
+ value = g_strdup_vprintf(fmt, args);
+ va_end(args);
+
+ data = g_byte_array_new_take((void *)value, strlen(value));
+
+ err = xs_impl_write(s->impl, DOMID_QEMU, XBT_NULL, abspath, data);
+ assert(!err);
+
+ g_byte_array_unref(data);
+
+ err = xs_impl_set_perms(s->impl, DOMID_QEMU, XBT_NULL, abspath, perms);
+ assert(!err);
+
+ g_free(abspath);
+}
static void xen_xenstore_realize(DeviceState *dev, Error **errp)
{
XenXenstoreState *s = XEN_XENSTORE(dev);
+ GList *perms;
if (xen_mode != XEN_EMULATE) {
error_setg(errp, "Xen xenstore support is for Xen emulation");
@@ -89,6 +134,50 @@
}
aio_set_fd_handler(qemu_get_aio_context(), xen_be_evtchn_fd(s->eh), true,
xen_xenstore_event, NULL, NULL, NULL, s);
+
+ s->impl = xs_impl_create(xen_domid);
+
+ /* Populate the default nodes */
+
+ /* Nodes owned by 'dom0' but readable by the guest */
+ perms = g_list_append(NULL, xs_perm_as_string(XS_PERM_NONE, DOMID_QEMU));
+ perms = g_list_append(perms, xs_perm_as_string(XS_PERM_READ, xen_domid));
+
+ relpath_printf(s, perms, "", "%s", "");
+
+ relpath_printf(s, perms, "domid", "%u", xen_domid);
+
+ relpath_printf(s, perms, "control/platform-feature-xs_reset_watches", "%u", 1);
+ relpath_printf(s, perms, "control/platform-feature-multiprocessor-suspend", "%u", 1);
+
+ relpath_printf(s, perms, "platform/acpi", "%u", 1);
+ relpath_printf(s, perms, "platform/acpi_s3", "%u", 1);
+ relpath_printf(s, perms, "platform/acpi_s4", "%u", 1);
+ relpath_printf(s, perms, "platform/acpi_laptop_slate", "%u", 0);
+
+ g_list_free_full(perms, g_free);
+
+ /* Nodes owned by the guest */
+ perms = g_list_append(NULL, xs_perm_as_string(XS_PERM_NONE, xen_domid));
+
+ relpath_printf(s, perms, "attr", "%s", "");
+
+ relpath_printf(s, perms, "control/shutdown", "%s", "");
+ relpath_printf(s, perms, "control/feature-poweroff", "%u", 1);
+ relpath_printf(s, perms, "control/feature-reboot", "%u", 1);
+ relpath_printf(s, perms, "control/feature-suspend", "%u", 1);
+ relpath_printf(s, perms, "control/feature-s3", "%u", 1);
+ relpath_printf(s, perms, "control/feature-s4", "%u", 1);
+
+ relpath_printf(s, perms, "data", "%s", "");
+ relpath_printf(s, perms, "device", "%s", "");
+ relpath_printf(s, perms, "drivers", "%s", "");
+ relpath_printf(s, perms, "error", "%s", "");
+ relpath_printf(s, perms, "feature", "%s", "");
+
+ g_list_free_full(perms, g_free);
+
+ xen_xenstore_ops = &emu_xenstore_backend_ops;
}
static bool xen_xenstore_is_needed(void *opaque)
@@ -99,16 +188,26 @@
static int xen_xenstore_pre_save(void *opaque)
{
XenXenstoreState *s = opaque;
+ GByteArray *save;
if (s->eh) {
s->guest_port = xen_be_evtchn_get_guest_port(s->eh);
}
+
+ g_free(s->impl_state);
+ save = xs_impl_serialize(s->impl);
+ s->impl_state = save->data;
+ s->impl_state_size = save->len;
+ g_byte_array_free(save, false);
+
return 0;
}
static int xen_xenstore_post_load(void *opaque, int ver)
{
XenXenstoreState *s = opaque;
+ GByteArray *save;
+ int ret;
/*
* As qemu/dom0, rebind to the guest's port. The Windows drivers may
@@ -125,11 +224,18 @@
}
s->be_port = be_port;
}
- return 0;
+
+ save = g_byte_array_new_take(s->impl_state, s->impl_state_size);
+ s->impl_state = NULL;
+ s->impl_state_size = 0;
+
+ ret = xs_impl_deserialize(s->impl, save, xen_domid, fire_watch_cb, s);
+ return ret;
}
static const VMStateDescription xen_xenstore_vmstate = {
.name = "xen_xenstore",
+ .unmigratable = 1, /* The PV back ends don't migrate yet */
.version_id = 1,
.minimum_version_id = 1,
.needed = xen_xenstore_is_needed,
@@ -145,6 +251,10 @@
VMSTATE_BOOL(rsp_pending, XenXenstoreState),
VMSTATE_UINT32(guest_port, XenXenstoreState),
VMSTATE_BOOL(fatal_error, XenXenstoreState),
+ VMSTATE_UINT32(impl_state_size, XenXenstoreState),
+ VMSTATE_VARRAY_UINT32_ALLOC(impl_state, XenXenstoreState,
+ impl_state_size, 0,
+ vmstate_info_uint8, uint8_t),
VMSTATE_END_OF_LIST()
}
};
@@ -213,20 +323,761 @@
s->rsp_offset = 0;
}
+static void xs_error(XenXenstoreState *s, unsigned int id,
+ xs_transaction_t tx_id, int errnum)
+{
+ struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data;
+ const char *errstr = NULL;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(xsd_errors); i++) {
+ struct xsd_errors *xsd_error = &xsd_errors[i];
+
+ if (xsd_error->errnum == errnum) {
+ errstr = xsd_error->errstring;
+ break;
+ }
+ }
+ assert(errstr);
+
+ trace_xenstore_error(id, tx_id, errstr);
+
+ rsp->type = XS_ERROR;
+ rsp->req_id = id;
+ rsp->tx_id = tx_id;
+ rsp->len = (uint32_t)strlen(errstr) + 1;
+
+ memcpy(&rsp[1], errstr, rsp->len);
+}
+
+static void xs_ok(XenXenstoreState *s, unsigned int type, unsigned int req_id,
+ xs_transaction_t tx_id)
+{
+ struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data;
+ const char *okstr = "OK";
+
+ rsp->type = type;
+ rsp->req_id = req_id;
+ rsp->tx_id = tx_id;
+ rsp->len = (uint32_t)strlen(okstr) + 1;
+
+ memcpy(&rsp[1], okstr, rsp->len);
+}
+
+/*
+ * The correct request and response formats are documented in xen.git:
+ * docs/misc/xenstore.txt. A summary is given below for convenience.
+ * The '|' symbol represents a NUL character.
+ *
+ * ---------- Database read, write and permissions operations ----------
+ *
+ * READ <path>| <value|>
+ * WRITE <path>|<value|>
+ * Store and read the octet string <value> at <path>.
+ * WRITE creates any missing parent paths, with empty values.
+ *
+ * MKDIR <path>|
+ * Ensures that the <path> exists, by necessary by creating
+ * it and any missing parents with empty values. If <path>
+ * or any parent already exists, its value is left unchanged.
+ *
+ * RM <path>|
+ * Ensures that the <path> does not exist, by deleting
+ * it and all of its children. It is not an error if <path> does
+ * not exist, but it _is_ an error if <path>'s immediate parent
+ * does not exist either.
+ *
+ * DIRECTORY <path>| <child-leaf-name>|*
+ * Gives a list of the immediate children of <path>, as only the
+ * leafnames. The resulting children are each named
+ * <path>/<child-leaf-name>.
+ *
+ * DIRECTORY_PART <path>|<offset> <gencnt>|<child-leaf-name>|*
+ * Same as DIRECTORY, but to be used for children lists longer than
+ * XENSTORE_PAYLOAD_MAX. Input are <path> and the byte offset into
+ * the list of children to return. Return values are the generation
+ * count <gencnt> of the node (to be used to ensure the node hasn't
+ * changed between two reads: <gencnt> being the same for multiple
+ * reads guarantees the node hasn't changed) and the list of children
+ * starting at the specified <offset> of the complete list.
+ *
+ * GET_PERMS <path>| <perm-as-string>|+
+ * SET_PERMS <path>|<perm-as-string>|+?
+ * <perm-as-string> is one of the following
+ * w<domid> write only
+ * r<domid> read only
+ * b<domid> both read and write
+ * n<domid> no access
+ * See https://wiki.xen.org/wiki/XenBus section
+ * `Permissions' for details of the permissions system.
+ * It is possible to set permissions for the special watch paths
+ * "@introduceDomain" and "@releaseDomain" to enable receiving those
+ * watches in unprivileged domains.
+ *
+ * ---------- Watches ----------
+ *
+ * WATCH <wpath>|<token>|?
+ * Adds a watch.
+ *
+ * When a <path> is modified (including path creation, removal,
+ * contents change or permissions change) this generates an event
+ * on the changed <path>. Changes made in transactions cause an
+ * event only if and when committed. Each occurring event is
+ * matched against all the watches currently set up, and each
+ * matching watch results in a WATCH_EVENT message (see below).
+ *
+ * The event's path matches the watch's <wpath> if it is an child
+ * of <wpath>.
+ *
+ * <wpath> can be a <path> to watch or @<wspecial>. In the
+ * latter case <wspecial> may have any syntax but it matches
+ * (according to the rules above) only the following special
+ * events which are invented by xenstored:
+ * @introduceDomain occurs on INTRODUCE
+ * @releaseDomain occurs on any domain crash or
+ * shutdown, and also on RELEASE
+ * and domain destruction
+ * <wspecial> events are sent to privileged callers or explicitly
+ * via SET_PERMS enabled domains only.
+ *
+ * When a watch is first set up it is triggered once straight
+ * away, with <path> equal to <wpath>. Watches may be triggered
+ * spuriously. The tx_id in a WATCH request is ignored.
+ *
+ * Watches are supposed to be restricted by the permissions
+ * system but in practice the implementation is imperfect.
+ * Applications should not rely on being sent a notification for
+ * paths that they cannot read; however, an application may rely
+ * on being sent a watch when a path which it _is_ able to read
+ * is deleted even if that leaves only a nonexistent unreadable
+ * parent. A notification may omitted if a node's permissions
+ * are changed so as to make it unreadable, in which case future
+ * notifications may be suppressed (and if the node is later made
+ * readable, some notifications may have been lost).
+ *
+ * WATCH_EVENT <epath>|<token>|
+ * Unsolicited `reply' generated for matching modification events
+ * as described above. req_id and tx_id are both 0.
+ *
+ * <epath> is the event's path, ie the actual path that was
+ * modified; however if the event was the recursive removal of an
+ * parent of <wpath>, <epath> is just
+ * <wpath> (rather than the actual path which was removed). So
+ * <epath> is a child of <wpath>, regardless.
+ *
+ * Iff <wpath> for the watch was specified as a relative pathname,
+ * the <epath> path will also be relative (with the same base,
+ * obviously).
+ *
+ * UNWATCH <wpath>|<token>|?
+ *
+ * RESET_WATCHES |
+ * Reset all watches and transactions of the caller.
+ *
+ * ---------- Transactions ----------
+ *
+ * TRANSACTION_START | <transid>|
+ * <transid> is an opaque uint32_t allocated by xenstored
+ * represented as unsigned decimal. After this, transaction may
+ * be referenced by using <transid> (as 32-bit binary) in the
+ * tx_id request header field. When transaction is started whole
+ * db is copied; reads and writes happen on the copy.
+ * It is not legal to send non-0 tx_id in TRANSACTION_START.
+ *
+ * TRANSACTION_END T|
+ * TRANSACTION_END F|
+ * tx_id must refer to existing transaction. After this
+ * request the tx_id is no longer valid and may be reused by
+ * xenstore. If F, the transaction is discarded. If T,
+ * it is committed: if there were any other intervening writes
+ * then our END gets get EAGAIN.
+ *
+ * The plan is that in the future only intervening `conflicting'
+ * writes cause EAGAIN, meaning only writes or other commits
+ * which changed paths which were read or written in the
+ * transaction at hand.
+ *
+ */
+
+static void xs_read(XenXenstoreState *s, unsigned int req_id,
+ xs_transaction_t tx_id, uint8_t *req_data, unsigned int len)
+{
+ const char *path = (const char *)req_data;
+ struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data;
+ uint8_t *rsp_data = (uint8_t *)&rsp[1];
+ g_autoptr(GByteArray) data = g_byte_array_new();
+ int err;
+
+ if (len == 0 || req_data[len - 1] != '\0') {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+
+ trace_xenstore_read(tx_id, path);
+ err = xs_impl_read(s->impl, xen_domid, tx_id, path, data);
+ if (err) {
+ xs_error(s, req_id, tx_id, err);
+ return;
+ }
+
+ rsp->type = XS_READ;
+ rsp->req_id = req_id;
+ rsp->tx_id = tx_id;
+ rsp->len = 0;
+
+ len = data->len;
+ if (len > XENSTORE_PAYLOAD_MAX) {
+ xs_error(s, req_id, tx_id, E2BIG);
+ return;
+ }
+
+ memcpy(&rsp_data[rsp->len], data->data, len);
+ rsp->len += len;
+}
+
+static void xs_write(XenXenstoreState *s, unsigned int req_id,
+ xs_transaction_t tx_id, uint8_t *req_data,
+ unsigned int len)
+{
+ g_autoptr(GByteArray) data = g_byte_array_new();
+ const char *path;
+ int err;
+
+ if (len == 0) {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+
+ path = (const char *)req_data;
+
+ while (len--) {
+ if (*req_data++ == '\0') {
+ break;
+ }
+ if (len == 0) {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+ }
+
+ g_byte_array_append(data, req_data, len);
+
+ trace_xenstore_write(tx_id, path);
+ err = xs_impl_write(s->impl, xen_domid, tx_id, path, data);
+ if (err) {
+ xs_error(s, req_id, tx_id, err);
+ return;
+ }
+
+ xs_ok(s, XS_WRITE, req_id, tx_id);
+}
+
+static void xs_mkdir(XenXenstoreState *s, unsigned int req_id,
+ xs_transaction_t tx_id, uint8_t *req_data,
+ unsigned int len)
+{
+ g_autoptr(GByteArray) data = g_byte_array_new();
+ const char *path;
+ int err;
+
+ if (len == 0 || req_data[len - 1] != '\0') {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+
+ path = (const char *)req_data;
+
+ trace_xenstore_mkdir(tx_id, path);
+ err = xs_impl_read(s->impl, xen_domid, tx_id, path, data);
+ if (err == ENOENT) {
+ err = xs_impl_write(s->impl, xen_domid, tx_id, path, data);
+ }
+
+ if (!err) {
+ xs_error(s, req_id, tx_id, err);
+ return;
+ }
+
+ xs_ok(s, XS_MKDIR, req_id, tx_id);
+}
+
+static void xs_append_strings(XenXenstoreState *s, struct xsd_sockmsg *rsp,
+ GList *strings, unsigned int start, bool truncate)
+{
+ uint8_t *rsp_data = (uint8_t *)&rsp[1];
+ GList *l;
+
+ for (l = strings; l; l = l->next) {
+ size_t len = strlen(l->data) + 1; /* Including the NUL termination */
+ char *str = l->data;
+
+ if (rsp->len + len > XENSTORE_PAYLOAD_MAX) {
+ if (truncate) {
+ len = XENSTORE_PAYLOAD_MAX - rsp->len;
+ if (!len) {
+ return;
+ }
+ } else {
+ xs_error(s, rsp->req_id, rsp->tx_id, E2BIG);
+ return;
+ }
+ }
+
+ if (start) {
+ if (start >= len) {
+ start -= len;
+ continue;
+ }
+
+ str += start;
+ len -= start;
+ start = 0;
+ }
+
+ memcpy(&rsp_data[rsp->len], str, len);
+ rsp->len += len;
+ }
+ /* XS_DIRECTORY_PART wants an extra NUL to indicate the end */
+ if (truncate && rsp->len < XENSTORE_PAYLOAD_MAX) {
+ rsp_data[rsp->len++] = '\0';
+ }
+}
+
+static void xs_directory(XenXenstoreState *s, unsigned int req_id,
+ xs_transaction_t tx_id, uint8_t *req_data,
+ unsigned int len)
+{
+ struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data;
+ GList *items = NULL;
+ const char *path;
+ int err;
+
+ if (len == 0 || req_data[len - 1] != '\0') {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+
+ path = (const char *)req_data;
+
+ trace_xenstore_directory(tx_id, path);
+ err = xs_impl_directory(s->impl, xen_domid, tx_id, path, NULL, &items);
+ if (err != 0) {
+ xs_error(s, req_id, tx_id, err);
+ return;
+ }
+
+ rsp->type = XS_DIRECTORY;
+ rsp->req_id = req_id;
+ rsp->tx_id = tx_id;
+ rsp->len = 0;
+
+ xs_append_strings(s, rsp, items, 0, false);
+
+ g_list_free_full(items, g_free);
+}
+
+static void xs_directory_part(XenXenstoreState *s, unsigned int req_id,
+ xs_transaction_t tx_id, uint8_t *req_data,
+ unsigned int len)
+{
+ const char *offset_str, *path = (const char *)req_data;
+ struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data;
+ char *rsp_data = (char *)&rsp[1];
+ uint64_t gencnt = 0;
+ unsigned int offset;
+ GList *items = NULL;
+ int err;
+
+ if (len == 0) {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+
+ while (len--) {
+ if (*req_data++ == '\0') {
+ break;
+ }
+ if (len == 0) {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+ }
+
+ offset_str = (const char *)req_data;
+ while (len--) {
+ if (*req_data++ == '\0') {
+ break;
+ }
+ if (len == 0) {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+ }
+
+ if (len) {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+
+ if (qemu_strtoui(offset_str, NULL, 10, &offset) < 0) {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+
+ trace_xenstore_directory_part(tx_id, path, offset);
+ err = xs_impl_directory(s->impl, xen_domid, tx_id, path, &gencnt, &items);
+ if (err != 0) {
+ xs_error(s, req_id, tx_id, err);
+ return;
+ }
+
+ rsp->type = XS_DIRECTORY_PART;
+ rsp->req_id = req_id;
+ rsp->tx_id = tx_id;
+ rsp->len = snprintf(rsp_data, XENSTORE_PAYLOAD_MAX, "%" PRIu64, gencnt) + 1;
+
+ xs_append_strings(s, rsp, items, offset, true);
+
+ g_list_free_full(items, g_free);
+}
+
+static void xs_transaction_start(XenXenstoreState *s, unsigned int req_id,
+ xs_transaction_t tx_id, uint8_t *req_data,
+ unsigned int len)
+{
+ struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data;
+ char *rsp_data = (char *)&rsp[1];
+ int err;
+
+ if (len != 1 || req_data[0] != '\0') {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+
+ rsp->type = XS_TRANSACTION_START;
+ rsp->req_id = req_id;
+ rsp->tx_id = tx_id;
+ rsp->len = 0;
+
+ err = xs_impl_transaction_start(s->impl, xen_domid, &tx_id);
+ if (err) {
+ xs_error(s, req_id, tx_id, err);
+ return;
+ }
+
+ trace_xenstore_transaction_start(tx_id);
+
+ rsp->len = snprintf(rsp_data, XENSTORE_PAYLOAD_MAX, "%u", tx_id);
+ assert(rsp->len < XENSTORE_PAYLOAD_MAX);
+ rsp->len++;
+}
+
+static void xs_transaction_end(XenXenstoreState *s, unsigned int req_id,
+ xs_transaction_t tx_id, uint8_t *req_data,
+ unsigned int len)
+{
+ bool commit;
+ int err;
+
+ if (len != 2 || req_data[1] != '\0') {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+
+ switch (req_data[0]) {
+ case 'T':
+ commit = true;
+ break;
+ case 'F':
+ commit = false;
+ break;
+ default:
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+
+ trace_xenstore_transaction_end(tx_id, commit);
+ err = xs_impl_transaction_end(s->impl, xen_domid, tx_id, commit);
+ if (err) {
+ xs_error(s, req_id, tx_id, err);
+ return;
+ }
+
+ xs_ok(s, XS_TRANSACTION_END, req_id, tx_id);
+}
+
+static void xs_rm(XenXenstoreState *s, unsigned int req_id,
+ xs_transaction_t tx_id, uint8_t *req_data, unsigned int len)
+{
+ const char *path = (const char *)req_data;
+ int err;
+
+ if (len == 0 || req_data[len - 1] != '\0') {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+
+ trace_xenstore_rm(tx_id, path);
+ err = xs_impl_rm(s->impl, xen_domid, tx_id, path);
+ if (err) {
+ xs_error(s, req_id, tx_id, err);
+ return;
+ }
+
+ xs_ok(s, XS_RM, req_id, tx_id);
+}
+
+static void xs_get_perms(XenXenstoreState *s, unsigned int req_id,
+ xs_transaction_t tx_id, uint8_t *req_data,
+ unsigned int len)
+{
+ const char *path = (const char *)req_data;
+ struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data;
+ GList *perms = NULL;
+ int err;
+
+ if (len == 0 || req_data[len - 1] != '\0') {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+
+ trace_xenstore_get_perms(tx_id, path);
+ err = xs_impl_get_perms(s->impl, xen_domid, tx_id, path, &perms);
+ if (err) {
+ xs_error(s, req_id, tx_id, err);
+ return;
+ }
+
+ rsp->type = XS_GET_PERMS;
+ rsp->req_id = req_id;
+ rsp->tx_id = tx_id;
+ rsp->len = 0;
+
+ xs_append_strings(s, rsp, perms, 0, false);
+
+ g_list_free_full(perms, g_free);
+}
+
+static void xs_set_perms(XenXenstoreState *s, unsigned int req_id,
+ xs_transaction_t tx_id, uint8_t *req_data,
+ unsigned int len)
+{
+ const char *path = (const char *)req_data;
+ uint8_t *perm;
+ GList *perms = NULL;
+ int err;
+
+ if (len == 0) {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+
+ while (len--) {
+ if (*req_data++ == '\0') {
+ break;
+ }
+ if (len == 0) {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+ }
+
+ perm = req_data;
+ while (len--) {
+ if (*req_data++ == '\0') {
+ perms = g_list_append(perms, perm);
+ perm = req_data;
+ }
+ }
+
+ /*
+ * Note that there may be trailing garbage at the end of the buffer.
+ * This is explicitly permitted by the '?' at the end of the definition:
+ *
+ * SET_PERMS <path>|<perm-as-string>|+?
+ */
+
+ trace_xenstore_set_perms(tx_id, path);
+ err = xs_impl_set_perms(s->impl, xen_domid, tx_id, path, perms);
+ g_list_free(perms);
+ if (err) {
+ xs_error(s, req_id, tx_id, err);
+ return;
+ }
+
+ xs_ok(s, XS_SET_PERMS, req_id, tx_id);
+}
+
+static void xs_watch(XenXenstoreState *s, unsigned int req_id,
+ xs_transaction_t tx_id, uint8_t *req_data,
+ unsigned int len)
+{
+ const char *token, *path = (const char *)req_data;
+ int err;
+
+ if (len == 0) {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+
+ while (len--) {
+ if (*req_data++ == '\0') {
+ break;
+ }
+ if (len == 0) {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+ }
+
+ token = (const char *)req_data;
+ while (len--) {
+ if (*req_data++ == '\0') {
+ break;
+ }
+ if (len == 0) {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+ }
+
+ /*
+ * Note that there may be trailing garbage at the end of the buffer.
+ * This is explicitly permitted by the '?' at the end of the definition:
+ *
+ * WATCH <wpath>|<token>|?
+ */
+
+ trace_xenstore_watch(path, token);
+ err = xs_impl_watch(s->impl, xen_domid, path, token, fire_watch_cb, s);
+ if (err) {
+ xs_error(s, req_id, tx_id, err);
+ return;
+ }
+
+ xs_ok(s, XS_WATCH, req_id, tx_id);
+}
+
+static void xs_unwatch(XenXenstoreState *s, unsigned int req_id,
+ xs_transaction_t tx_id, uint8_t *req_data,
+ unsigned int len)
+{
+ const char *token, *path = (const char *)req_data;
+ int err;
+
+ if (len == 0) {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+
+ while (len--) {
+ if (*req_data++ == '\0') {
+ break;
+ }
+ if (len == 0) {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+ }
+
+ token = (const char *)req_data;
+ while (len--) {
+ if (*req_data++ == '\0') {
+ break;
+ }
+ if (len == 0) {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+ }
+
+ trace_xenstore_unwatch(path, token);
+ err = xs_impl_unwatch(s->impl, xen_domid, path, token, fire_watch_cb, s);
+ if (err) {
+ xs_error(s, req_id, tx_id, err);
+ return;
+ }
+
+ xs_ok(s, XS_UNWATCH, req_id, tx_id);
+}
+
+static void xs_reset_watches(XenXenstoreState *s, unsigned int req_id,
+ xs_transaction_t tx_id, uint8_t *req_data,
+ unsigned int len)
+{
+ if (len == 0 || req_data[len - 1] != '\0') {
+ xs_error(s, req_id, tx_id, EINVAL);
+ return;
+ }
+
+ trace_xenstore_reset_watches();
+ xs_impl_reset_watches(s->impl, xen_domid);
+
+ xs_ok(s, XS_RESET_WATCHES, req_id, tx_id);
+}
+
+static void xs_priv(XenXenstoreState *s, unsigned int req_id,
+ xs_transaction_t tx_id, uint8_t *data,
+ unsigned int len)
+{
+ xs_error(s, req_id, tx_id, EACCES);
+}
+
+static void xs_unimpl(XenXenstoreState *s, unsigned int req_id,
+ xs_transaction_t tx_id, uint8_t *data,
+ unsigned int len)
+{
+ xs_error(s, req_id, tx_id, ENOSYS);
+}
+
+typedef void (*xs_impl)(XenXenstoreState *s, unsigned int req_id,
+ xs_transaction_t tx_id, uint8_t *data,
+ unsigned int len);
+
+struct xsd_req {
+ const char *name;
+ xs_impl fn;
+};
+#define XSD_REQ(_type, _fn) \
+ [_type] = { .name = #_type, .fn = _fn }
+
+struct xsd_req xsd_reqs[] = {
+ XSD_REQ(XS_READ, xs_read),
+ XSD_REQ(XS_WRITE, xs_write),
+ XSD_REQ(XS_MKDIR, xs_mkdir),
+ XSD_REQ(XS_DIRECTORY, xs_directory),
+ XSD_REQ(XS_DIRECTORY_PART, xs_directory_part),
+ XSD_REQ(XS_TRANSACTION_START, xs_transaction_start),
+ XSD_REQ(XS_TRANSACTION_END, xs_transaction_end),
+ XSD_REQ(XS_RM, xs_rm),
+ XSD_REQ(XS_GET_PERMS, xs_get_perms),
+ XSD_REQ(XS_SET_PERMS, xs_set_perms),
+ XSD_REQ(XS_WATCH, xs_watch),
+ XSD_REQ(XS_UNWATCH, xs_unwatch),
+ XSD_REQ(XS_CONTROL, xs_priv),
+ XSD_REQ(XS_INTRODUCE, xs_priv),
+ XSD_REQ(XS_RELEASE, xs_priv),
+ XSD_REQ(XS_IS_DOMAIN_INTRODUCED, xs_priv),
+ XSD_REQ(XS_RESUME, xs_priv),
+ XSD_REQ(XS_SET_TARGET, xs_priv),
+ XSD_REQ(XS_RESET_WATCHES, xs_reset_watches),
+};
+
static void process_req(XenXenstoreState *s)
{
struct xsd_sockmsg *req = (struct xsd_sockmsg *)s->req_data;
- struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data;
- const char enosys[] = "ENOSYS";
+ xs_impl handler = NULL;
assert(req_pending(s));
assert(!s->rsp_pending);
- rsp->type = XS_ERROR;
- rsp->req_id = req->req_id;
- rsp->tx_id = req->tx_id;
- rsp->len = sizeof(enosys);
- memcpy((void *)&rsp[1], enosys, sizeof(enosys));
+ if (req->type < ARRAY_SIZE(xsd_reqs)) {
+ handler = xsd_reqs[req->type].fn;
+ }
+ if (!handler) {
+ handler = &xs_unimpl;
+ }
+
+ handler(s, req->req_id, req->tx_id, (uint8_t *)&req[1], req->len);
s->rsp_pending = true;
reset_req(s);
@@ -415,6 +1266,113 @@
return copylen;
}
+static void deliver_watch(XenXenstoreState *s, const char *path,
+ const char *token)
+{
+ struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data;
+ uint8_t *rsp_data = (uint8_t *)&rsp[1];
+ unsigned int len;
+
+ assert(!s->rsp_pending);
+
+ trace_xenstore_watch_event(path, token);
+
+ rsp->type = XS_WATCH_EVENT;
+ rsp->req_id = 0;
+ rsp->tx_id = 0;
+ rsp->len = 0;
+
+ len = strlen(path);
+
+ /* XENSTORE_ABS/REL_PATH_MAX should ensure there can be no overflow */
+ assert(rsp->len + len < XENSTORE_PAYLOAD_MAX);
+
+ memcpy(&rsp_data[rsp->len], path, len);
+ rsp->len += len;
+ rsp_data[rsp->len] = '\0';
+ rsp->len++;
+
+ len = strlen(token);
+ /*
+ * It is possible for the guest to have chosen a token that will
+ * not fit (along with the patch) into a watch event. We have no
+ * choice but to drop the event if this is the case.
+ */
+ if (rsp->len + len >= XENSTORE_PAYLOAD_MAX) {
+ return;
+ }
+
+ memcpy(&rsp_data[rsp->len], token, len);
+ rsp->len += len;
+ rsp_data[rsp->len] = '\0';
+ rsp->len++;
+
+ s->rsp_pending = true;
+}
+
+struct watch_event {
+ char *path;
+ char *token;
+};
+
+static void free_watch_event(struct watch_event *ev)
+{
+ if (ev) {
+ g_free(ev->path);
+ g_free(ev->token);
+ g_free(ev);
+ }
+}
+
+static void queue_watch(XenXenstoreState *s, const char *path,
+ const char *token)
+{
+ struct watch_event *ev = g_new0(struct watch_event, 1);
+
+ ev->path = g_strdup(path);
+ ev->token = g_strdup(token);
+
+ s->watch_events = g_list_append(s->watch_events, ev);
+}
+
+static void fire_watch_cb(void *opaque, const char *path, const char *token)
+{
+ XenXenstoreState *s = opaque;
+
+ assert(qemu_mutex_iothread_locked());
+
+ /*
+ * If there's a response pending, we obviously can't scribble over
+ * it. But if there's a request pending, it has dibs on the buffer
+ * too.
+ *
+ * In the common case of a watch firing due to backend activity
+ * when the ring was otherwise idle, we should be able to copy the
+ * strings directly into the rsp_data and thence the actual ring,
+ * without needing to perform any allocations and queue them.
+ */
+ if (s->rsp_pending || req_pending(s)) {
+ queue_watch(s, path, token);
+ } else {
+ deliver_watch(s, path, token);
+ /*
+ * If the message was queued because there was already ring activity,
+ * no need to wake the guest. But if not, we need to send the evtchn.
+ */
+ xen_be_evtchn_notify(s->eh, s->be_port);
+ }
+}
+
+static void process_watch_events(XenXenstoreState *s)
+{
+ struct watch_event *ev = s->watch_events->data;
+
+ deliver_watch(s, ev->path, ev->token);
+
+ s->watch_events = g_list_remove(s->watch_events, ev);
+ free_watch_event(ev);
+}
+
static void xen_xenstore_event(void *opaque)
{
XenXenstoreState *s = opaque;
@@ -433,6 +1391,10 @@
copied_to = copied_from = 0;
processed = false;
+ if (!s->rsp_pending && s->watch_events) {
+ process_watch_events(s);
+ }
+
if (s->rsp_pending) {
copied_to = put_rsp(s);
}
@@ -441,7 +1403,7 @@
copied_from = get_req(s);
}
- if (req_pending(s) && !s->rsp_pending) {
+ if (req_pending(s) && !s->rsp_pending && !s->watch_events) {
process_req(s);
processed = true;
}
@@ -496,5 +1458,270 @@
}
s->be_port = err;
+ /*
+ * We don't actually access the guest's page through the grant, because
+ * this isn't real Xen, and we can just use the page we gave it in the
+ * first place. Map the grant anyway, mostly for cosmetic purposes so
+ * it *looks* like it's in use in the guest-visible grant table.
+ */
+ s->gt = qemu_xen_gnttab_open();
+ uint32_t xs_gntref = GNTTAB_RESERVED_XENSTORE;
+ s->granted_xs = qemu_xen_gnttab_map_refs(s->gt, 1, xen_domid, &xs_gntref,
+ PROT_READ | PROT_WRITE);
+
return 0;
}
+
+struct qemu_xs_handle {
+ XenstoreImplState *impl;
+ GList *watches;
+ QEMUBH *watch_bh;
+};
+
+struct qemu_xs_watch {
+ struct qemu_xs_handle *h;
+ char *path;
+ xs_watch_fn fn;
+ void *opaque;
+ GList *events;
+};
+
+static char *xs_be_get_domain_path(struct qemu_xs_handle *h, unsigned int domid)
+{
+ return g_strdup_printf("/local/domain/%u", domid);
+}
+
+static char **xs_be_directory(struct qemu_xs_handle *h, xs_transaction_t t,
+ const char *path, unsigned int *num)
+{
+ GList *items = NULL, *l;
+ unsigned int i = 0;
+ char **items_ret;
+ int err;
+
+ err = xs_impl_directory(h->impl, DOMID_QEMU, t, path, NULL, &items);
+ if (err) {
+ errno = err;
+ return NULL;
+ }
+
+ items_ret = g_new0(char *, g_list_length(items) + 1);
+ *num = 0;
+ for (l = items; l; l = l->next) {
+ items_ret[i++] = l->data;
+ (*num)++;
+ }
+ g_list_free(items);
+ return items_ret;
+}
+
+static void *xs_be_read(struct qemu_xs_handle *h, xs_transaction_t t,
+ const char *path, unsigned int *len)
+{
+ GByteArray *data = g_byte_array_new();
+ bool free_segment = false;
+ int err;
+
+ err = xs_impl_read(h->impl, DOMID_QEMU, t, path, data);
+ if (err) {
+ free_segment = true;
+ errno = err;
+ } else {
+ if (len) {
+ *len = data->len;
+ }
+ /* The xen-bus-helper code expects to get NUL terminated string! */
+ g_byte_array_append(data, (void *)"", 1);
+ }
+
+ return g_byte_array_free(data, free_segment);
+}
+
+static bool xs_be_write(struct qemu_xs_handle *h, xs_transaction_t t,
+ const char *path, const void *data, unsigned int len)
+{
+ GByteArray *gdata = g_byte_array_new();
+ int err;
+
+ g_byte_array_append(gdata, data, len);
+ err = xs_impl_write(h->impl, DOMID_QEMU, t, path, gdata);
+ g_byte_array_unref(gdata);
+ if (err) {
+ errno = err;
+ return false;
+ }
+ return true;
+}
+
+static bool xs_be_create(struct qemu_xs_handle *h, xs_transaction_t t,
+ unsigned int owner, unsigned int domid,
+ unsigned int perms, const char *path)
+{
+ g_autoptr(GByteArray) data = g_byte_array_new();
+ GList *perms_list = NULL;
+ int err;
+
+ /* mkdir does this */
+ err = xs_impl_read(h->impl, DOMID_QEMU, t, path, data);
+ if (err == ENOENT) {
+ err = xs_impl_write(h->impl, DOMID_QEMU, t, path, data);
+ }
+ if (err) {
+ errno = err;
+ return false;
+ }
+
+ perms_list = g_list_append(perms_list,
+ xs_perm_as_string(XS_PERM_NONE, owner));
+ perms_list = g_list_append(perms_list,
+ xs_perm_as_string(perms, domid));
+
+ err = xs_impl_set_perms(h->impl, DOMID_QEMU, t, path, perms_list);
+ g_list_free_full(perms_list, g_free);
+ if (err) {
+ errno = err;
+ return false;
+ }
+ return true;
+}
+
+static bool xs_be_destroy(struct qemu_xs_handle *h, xs_transaction_t t,
+ const char *path)
+{
+ int err = xs_impl_rm(h->impl, DOMID_QEMU, t, path);
+ if (err) {
+ errno = err;
+ return false;
+ }
+ return true;
+}
+
+static void be_watch_bh(void *_h)
+{
+ struct qemu_xs_handle *h = _h;
+ GList *l;
+
+ for (l = h->watches; l; l = l->next) {
+ struct qemu_xs_watch *w = l->data;
+
+ while (w->events) {
+ struct watch_event *ev = w->events->data;
+
+ w->fn(w->opaque, ev->path);
+
+ w->events = g_list_remove(w->events, ev);
+ free_watch_event(ev);
+ }
+ }
+}
+
+static void xs_be_watch_cb(void *opaque, const char *path, const char *token)
+{
+ struct watch_event *ev = g_new0(struct watch_event, 1);
+ struct qemu_xs_watch *w = opaque;
+
+ /* We don't care about the token */
+ ev->path = g_strdup(path);
+ w->events = g_list_append(w->events, ev);
+
+ qemu_bh_schedule(w->h->watch_bh);
+}
+
+static struct qemu_xs_watch *xs_be_watch(struct qemu_xs_handle *h,
+ const char *path, xs_watch_fn fn,
+ void *opaque)
+{
+ struct qemu_xs_watch *w = g_new0(struct qemu_xs_watch, 1);
+ int err;
+
+ w->h = h;
+ w->fn = fn;
+ w->opaque = opaque;
+
+ err = xs_impl_watch(h->impl, DOMID_QEMU, path, NULL, xs_be_watch_cb, w);
+ if (err) {
+ errno = err;
+ g_free(w);
+ return NULL;
+ }
+
+ w->path = g_strdup(path);
+ h->watches = g_list_append(h->watches, w);
+ return w;
+}
+
+static void xs_be_unwatch(struct qemu_xs_handle *h, struct qemu_xs_watch *w)
+{
+ xs_impl_unwatch(h->impl, DOMID_QEMU, w->path, NULL, xs_be_watch_cb, w);
+
+ h->watches = g_list_remove(h->watches, w);
+ g_list_free_full(w->events, (GDestroyNotify)free_watch_event);
+ g_free(w->path);
+ g_free(w);
+}
+
+static xs_transaction_t xs_be_transaction_start(struct qemu_xs_handle *h)
+{
+ unsigned int new_tx = XBT_NULL;
+ int err = xs_impl_transaction_start(h->impl, DOMID_QEMU, &new_tx);
+ if (err) {
+ errno = err;
+ return XBT_NULL;
+ }
+ return new_tx;
+}
+
+static bool xs_be_transaction_end(struct qemu_xs_handle *h, xs_transaction_t t,
+ bool abort)
+{
+ int err = xs_impl_transaction_end(h->impl, DOMID_QEMU, t, !abort);
+ if (err) {
+ errno = err;
+ return false;
+ }
+ return true;
+}
+
+static struct qemu_xs_handle *xs_be_open(void)
+{
+ XenXenstoreState *s = xen_xenstore_singleton;
+ struct qemu_xs_handle *h;
+
+ if (!s && !s->impl) {
+ errno = -ENOSYS;
+ return NULL;
+ }
+
+ h = g_new0(struct qemu_xs_handle, 1);
+ h->impl = s->impl;
+
+ h->watch_bh = aio_bh_new(qemu_get_aio_context(), be_watch_bh, h);
+
+ return h;
+}
+
+static void xs_be_close(struct qemu_xs_handle *h)
+{
+ while (h->watches) {
+ struct qemu_xs_watch *w = h->watches->data;
+ xs_be_unwatch(h, w);
+ }
+
+ qemu_bh_delete(h->watch_bh);
+ g_free(h);
+}
+
+static struct xenstore_backend_ops emu_xenstore_backend_ops = {
+ .open = xs_be_open,
+ .close = xs_be_close,
+ .get_domain_path = xs_be_get_domain_path,
+ .directory = xs_be_directory,
+ .read = xs_be_read,
+ .write = xs_be_write,
+ .create = xs_be_create,
+ .destroy = xs_be_destroy,
+ .watch = xs_be_watch,
+ .unwatch = xs_be_unwatch,
+ .transaction_start = xs_be_transaction_start,
+ .transaction_end = xs_be_transaction_end,
+};
diff --git a/hw/i386/kvm/xenstore_impl.c b/hw/i386/kvm/xenstore_impl.c
new file mode 100644
index 0000000..305fe75
--- /dev/null
+++ b/hw/i386/kvm/xenstore_impl.c
@@ -0,0 +1,1927 @@
+/*
+ * QEMU Xen emulation: The actual implementation of XenStore
+ *
+ * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Authors: David Woodhouse <dwmw2@infradead.org>, Paul Durrant <paul@xen.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qom/object.h"
+
+#include "hw/xen/xen.h"
+
+#include "xen_xenstore.h"
+#include "xenstore_impl.h"
+
+#include "hw/xen/interface/io/xs_wire.h"
+
+#define XS_MAX_WATCHES 128
+#define XS_MAX_DOMAIN_NODES 1000
+#define XS_MAX_NODE_SIZE 2048
+#define XS_MAX_TRANSACTIONS 10
+#define XS_MAX_PERMS_PER_NODE 5
+
+#define XS_VALID_CHARS "abcdefghijklmnopqrstuvwxyz" \
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+ "0123456789-/_"
+
+typedef struct XsNode {
+ uint32_t ref;
+ GByteArray *content;
+ GList *perms;
+ GHashTable *children;
+ uint64_t gencnt;
+ bool deleted_in_tx;
+ bool modified_in_tx;
+ unsigned int serialized_tx;
+#ifdef XS_NODE_UNIT_TEST
+ gchar *name; /* debug only */
+#endif
+} XsNode;
+
+typedef struct XsWatch {
+ struct XsWatch *next;
+ xs_impl_watch_fn *cb;
+ void *cb_opaque;
+ char *token;
+ unsigned int dom_id;
+ int rel_prefix;
+} XsWatch;
+
+typedef struct XsTransaction {
+ XsNode *root;
+ unsigned int nr_nodes;
+ unsigned int base_tx;
+ unsigned int tx_id;
+ unsigned int dom_id;
+} XsTransaction;
+
+struct XenstoreImplState {
+ XsNode *root;
+ unsigned int nr_nodes;
+ GHashTable *watches;
+ unsigned int nr_domu_watches;
+ GHashTable *transactions;
+ unsigned int nr_domu_transactions;
+ unsigned int root_tx;
+ unsigned int last_tx;
+ bool serialized;
+};
+
+
+static void nobble_tx(gpointer key, gpointer value, gpointer user_data)
+{
+ unsigned int *new_tx_id = user_data;
+ XsTransaction *tx = value;
+
+ if (tx->base_tx == *new_tx_id) {
+ /* Transactions based on XBT_NULL will always fail */
+ tx->base_tx = XBT_NULL;
+ }
+}
+
+static inline unsigned int next_tx(struct XenstoreImplState *s)
+{
+ unsigned int tx_id;
+
+ /* Find the next TX id which isn't either XBT_NULL or in use. */
+ do {
+ tx_id = ++s->last_tx;
+ } while (tx_id == XBT_NULL || tx_id == s->root_tx ||
+ g_hash_table_lookup(s->transactions, GINT_TO_POINTER(tx_id)));
+
+ /*
+ * It is vanishingly unlikely, but ensure that no outstanding transaction
+ * is based on the (previous incarnation of the) newly-allocated TX id.
+ */
+ g_hash_table_foreach(s->transactions, nobble_tx, &tx_id);
+
+ return tx_id;
+}
+
+static inline XsNode *xs_node_new(void)
+{
+ XsNode *n = g_new0(XsNode, 1);
+ n->ref = 1;
+
+#ifdef XS_NODE_UNIT_TEST
+ nr_xs_nodes++;
+ xs_node_list = g_list_prepend(xs_node_list, n);
+#endif
+ return n;
+}
+
+static inline XsNode *xs_node_ref(XsNode *n)
+{
+ /* With just 10 transactions, it can never get anywhere near this. */
+ g_assert(n->ref < INT_MAX);
+
+ g_assert(n->ref);
+ n->ref++;
+ return n;
+}
+
+static inline void xs_node_unref(XsNode *n)
+{
+ if (!n) {
+ return;
+ }
+ g_assert(n->ref);
+ if (--n->ref) {
+ return;
+ }
+
+ if (n->content) {
+ g_byte_array_unref(n->content);
+ }
+ if (n->perms) {
+ g_list_free_full(n->perms, g_free);
+ }
+ if (n->children) {
+ g_hash_table_unref(n->children);
+ }
+#ifdef XS_NODE_UNIT_TEST
+ g_free(n->name);
+ nr_xs_nodes--;
+ xs_node_list = g_list_remove(xs_node_list, n);
+#endif
+ g_free(n);
+}
+
+char *xs_perm_as_string(unsigned int perm, unsigned int domid)
+{
+ char letter;
+
+ switch (perm) {
+ case XS_PERM_READ | XS_PERM_WRITE:
+ letter = 'b';
+ break;
+ case XS_PERM_READ:
+ letter = 'r';
+ break;
+ case XS_PERM_WRITE:
+ letter = 'w';
+ break;
+ case XS_PERM_NONE:
+ default:
+ letter = 'n';
+ break;
+ }
+
+ return g_strdup_printf("%c%u", letter, domid);
+}
+
+static gpointer do_perm_copy(gconstpointer src, gpointer user_data)
+{
+ return g_strdup(src);
+}
+
+static XsNode *xs_node_create(const char *name, GList *perms)
+{
+ XsNode *n = xs_node_new();
+
+#ifdef XS_NODE_UNIT_TEST
+ if (name) {
+ n->name = g_strdup(name);
+ }
+#endif
+
+ n->perms = g_list_copy_deep(perms, do_perm_copy, NULL);
+
+ return n;
+}
+
+/* For copying from one hash table to another using g_hash_table_foreach() */
+static void do_child_insert(gpointer key, gpointer value, gpointer user_data)
+{
+ g_hash_table_insert(user_data, g_strdup(key), xs_node_ref(value));
+}
+
+static XsNode *xs_node_copy(XsNode *old)
+{
+ XsNode *n = xs_node_new();
+
+ n->gencnt = old->gencnt;
+
+#ifdef XS_NODE_UNIT_TEST
+ if (n->name) {
+ n->name = g_strdup(old->name);
+ }
+#endif
+
+ assert(old);
+ if (old->children) {
+ n->children = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+ (GDestroyNotify)xs_node_unref);
+ g_hash_table_foreach(old->children, do_child_insert, n->children);
+ }
+ if (old->perms) {
+ n->perms = g_list_copy_deep(old->perms, do_perm_copy, NULL);
+ }
+ if (old->content) {
+ n->content = g_byte_array_ref(old->content);
+ }
+ return n;
+}
+
+/* Returns true if it made a change to the hash table */
+static bool xs_node_add_child(XsNode *n, const char *path_elem, XsNode *child)
+{
+ assert(!strchr(path_elem, '/'));
+
+ if (!child) {
+ assert(n->children);
+ return g_hash_table_remove(n->children, path_elem);
+ }
+
+#ifdef XS_NODE_UNIT_TEST
+ g_free(child->name);
+ child->name = g_strdup(path_elem);
+#endif
+ if (!n->children) {
+ n->children = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+ (GDestroyNotify)xs_node_unref);
+ }
+
+ /*
+ * The documentation for g_hash_table_insert() says that it "returns a
+ * boolean value to indicate whether the newly added value was already
+ * in the hash table or not."
+ *
+ * It could perhaps be clearer that returning TRUE means it wasn't,
+ */
+ return g_hash_table_insert(n->children, g_strdup(path_elem), child);
+}
+
+struct walk_op {
+ struct XenstoreImplState *s;
+ char path[XENSTORE_ABS_PATH_MAX + 2]; /* Two NUL terminators */
+ int (*op_fn)(XsNode **n, struct walk_op *op);
+ void *op_opaque;
+ void *op_opaque2;
+
+ GList *watches;
+ unsigned int dom_id;
+ unsigned int tx_id;
+
+ /* The number of nodes which will exist in the tree if this op succeeds. */
+ unsigned int new_nr_nodes;
+
+ /*
+ * This is maintained on the way *down* the walk to indicate
+ * whether nodes can be modified in place or whether COW is
+ * required. It starts off being true, as we're always going to
+ * replace the root node. If we walk into a shared subtree it
+ * becomes false. If we start *creating* new nodes for a write,
+ * it becomes true again.
+ *
+ * Do not use it on the way back up.
+ */
+ bool inplace;
+ bool mutating;
+ bool create_dirs;
+ bool in_transaction;
+
+ /* Tracking during recursion so we know which is first. */
+ bool deleted_in_tx;
+};
+
+static void fire_watches(struct walk_op *op, bool parents)
+{
+ GList *l = NULL;
+ XsWatch *w;
+
+ if (!op->mutating || op->in_transaction) {
+ return;
+ }
+
+ if (parents) {
+ l = op->watches;
+ }
+
+ w = g_hash_table_lookup(op->s->watches, op->path);
+ while (w || l) {
+ if (!w) {
+ /* Fire the parent nodes from 'op' if asked to */
+ w = l->data;
+ l = l->next;
+ continue;
+ }
+
+ assert(strlen(op->path) > w->rel_prefix);
+ w->cb(w->cb_opaque, op->path + w->rel_prefix, w->token);
+
+ w = w->next;
+ }
+}
+
+static int xs_node_add_content(XsNode **n, struct walk_op *op)
+{
+ GByteArray *data = op->op_opaque;
+
+ if (op->dom_id) {
+ /*
+ * The real XenStored includes permissions and names of child nodes
+ * in the calculated datasize but life's too short. For a single
+ * tenant internal XenStore, we don't have to be quite as pedantic.
+ */
+ if (data->len > XS_MAX_NODE_SIZE) {
+ return E2BIG;
+ }
+ }
+ /* We *are* the node to be written. Either this or a copy. */
+ if (!op->inplace) {
+ XsNode *old = *n;
+ *n = xs_node_copy(old);
+ xs_node_unref(old);
+ }
+
+ if ((*n)->content) {
+ g_byte_array_unref((*n)->content);
+ }
+ (*n)->content = g_byte_array_ref(data);
+ if (op->tx_id != XBT_NULL) {
+ (*n)->modified_in_tx = true;
+ }
+ return 0;
+}
+
+static int xs_node_get_content(XsNode **n, struct walk_op *op)
+{
+ GByteArray *data = op->op_opaque;
+ GByteArray *node_data;
+
+ assert(op->inplace);
+ assert(*n);
+
+ node_data = (*n)->content;
+ if (node_data) {
+ g_byte_array_append(data, node_data->data, node_data->len);
+ }
+
+ return 0;
+}
+
+static int node_rm_recurse(gpointer key, gpointer value, gpointer user_data)
+{
+ struct walk_op *op = user_data;
+ int path_len = strlen(op->path);
+ int key_len = strlen(key);
+ XsNode *n = value;
+ bool this_inplace = op->inplace;
+
+ if (n->ref != 1) {
+ op->inplace = 0;
+ }
+
+ assert(key_len + path_len + 2 <= sizeof(op->path));
+ op->path[path_len] = '/';
+ memcpy(op->path + path_len + 1, key, key_len + 1);
+
+ if (n->children) {
+ g_hash_table_foreach_remove(n->children, node_rm_recurse, op);
+ }
+ op->new_nr_nodes--;
+
+ /*
+ * Fire watches on *this* node but not the parents because they are
+ * going to be deleted too, so the watch will fire for them anyway.
+ */
+ fire_watches(op, false);
+ op->path[path_len] = '\0';
+
+ /*
+ * Actually deleting the child here is just an optimisation; if we
+ * don't then the final unref on the topmost victim will just have
+ * to cascade down again repeating all the g_hash_table_foreach()
+ * calls.
+ */
+ return this_inplace;
+}
+
+static XsNode *xs_node_copy_deleted(XsNode *old, struct walk_op *op);
+static void copy_deleted_recurse(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ struct walk_op *op = user_data;
+ GHashTable *siblings = op->op_opaque2;
+ XsNode *n = xs_node_copy_deleted(value, op);
+
+ /*
+ * Reinsert the deleted_in_tx copy of the node into the parent's
+ * 'children' hash table. Having stashed it from op->op_opaque2
+ * before the recursive call to xs_node_copy_deleted() scribbled
+ * over it.
+ */
+ g_hash_table_insert(siblings, g_strdup(key), n);
+}
+
+static XsNode *xs_node_copy_deleted(XsNode *old, struct walk_op *op)
+{
+ XsNode *n = xs_node_new();
+
+ n->gencnt = old->gencnt;
+
+#ifdef XS_NODE_UNIT_TEST
+ if (old->name) {
+ n->name = g_strdup(old->name);
+ }
+#endif
+
+ if (old->children) {
+ n->children = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+ (GDestroyNotify)xs_node_unref);
+ op->op_opaque2 = n->children;
+ g_hash_table_foreach(old->children, copy_deleted_recurse, op);
+ }
+ if (old->perms) {
+ n->perms = g_list_copy_deep(old->perms, do_perm_copy, NULL);
+ }
+ n->deleted_in_tx = true;
+ /* If it gets resurrected we only fire a watch if it lost its content */
+ if (old->content) {
+ n->modified_in_tx = true;
+ }
+ op->new_nr_nodes--;
+ return n;
+}
+
+static int xs_node_rm(XsNode **n, struct walk_op *op)
+{
+ bool this_inplace = op->inplace;
+
+ if (op->tx_id != XBT_NULL) {
+ /* It's not trivial to do inplace handling for this one */
+ XsNode *old = *n;
+ *n = xs_node_copy_deleted(old, op);
+ xs_node_unref(old);
+ return 0;
+ }
+
+ /* Fire watches for, and count, nodes in the subtree which get deleted */
+ if ((*n)->children) {
+ g_hash_table_foreach_remove((*n)->children, node_rm_recurse, op);
+ }
+ op->new_nr_nodes--;
+
+ if (this_inplace) {
+ xs_node_unref(*n);
+ }
+ *n = NULL;
+ return 0;
+}
+
+static int xs_node_get_perms(XsNode **n, struct walk_op *op)
+{
+ GList **perms = op->op_opaque;
+
+ assert(op->inplace);
+ assert(*n);
+
+ *perms = g_list_copy_deep((*n)->perms, do_perm_copy, NULL);
+ return 0;
+}
+
+static void parse_perm(const char *perm, char *letter, unsigned int *dom_id)
+{
+ unsigned int n = sscanf(perm, "%c%u", letter, dom_id);
+
+ assert(n == 2);
+}
+
+static bool can_access(unsigned int dom_id, GList *perms, const char *letters)
+{
+ unsigned int i, n;
+ char perm_letter;
+ unsigned int perm_dom_id;
+ bool access;
+
+ if (dom_id == 0) {
+ return true;
+ }
+
+ n = g_list_length(perms);
+ assert(n >= 1);
+
+ /*
+ * The dom_id of the first perm is the owner, and the owner always has
+ * read-write access.
+ */
+ parse_perm(g_list_nth_data(perms, 0), &perm_letter, &perm_dom_id);
+ if (dom_id == perm_dom_id) {
+ return true;
+ }
+
+ /*
+ * The letter of the first perm specified the default access for all other
+ * domains.
+ */
+ access = !!strchr(letters, perm_letter);
+ for (i = 1; i < n; i++) {
+ parse_perm(g_list_nth_data(perms, i), &perm_letter, &perm_dom_id);
+ if (dom_id != perm_dom_id) {
+ continue;
+ }
+ access = !!strchr(letters, perm_letter);
+ }
+
+ return access;
+}
+
+static int xs_node_set_perms(XsNode **n, struct walk_op *op)
+{
+ GList *perms = op->op_opaque;
+
+ if (op->dom_id) {
+ unsigned int perm_dom_id;
+ char perm_letter;
+
+ /* A guest may not change permissions on nodes it does not own */
+ if (!can_access(op->dom_id, (*n)->perms, "")) {
+ return EPERM;
+ }
+
+ /* A guest may not change the owner of a node it owns. */
+ parse_perm(perms->data, &perm_letter, &perm_dom_id);
+ if (perm_dom_id != op->dom_id) {
+ return EPERM;
+ }
+
+ if (g_list_length(perms) > XS_MAX_PERMS_PER_NODE) {
+ return ENOSPC;
+ }
+ }
+
+ /* We *are* the node to be written. Either this or a copy. */
+ if (!op->inplace) {
+ XsNode *old = *n;
+ *n = xs_node_copy(old);
+ xs_node_unref(old);
+ }
+
+ if ((*n)->perms) {
+ g_list_free_full((*n)->perms, g_free);
+ }
+ (*n)->perms = g_list_copy_deep(perms, do_perm_copy, NULL);
+ if (op->tx_id != XBT_NULL) {
+ (*n)->modified_in_tx = true;
+ }
+ return 0;
+}
+
+/*
+ * Passed a full reference in *n which it may free if it needs to COW.
+ *
+ * When changing the tree, the op->inplace flag indicates whether this
+ * node may be modified in place (i.e. it and all its parents had a
+ * refcount of one). If walking down the tree we find a node whose
+ * refcount is higher, we must clear op->inplace and COW from there
+ * down. Unless we are creating new nodes as scaffolding for a write
+ * (which works like 'mkdir -p' does). In which case those newly
+ * created nodes can (and must) be modified in place again.
+ */
+static int xs_node_walk(XsNode **n, struct walk_op *op)
+{
+ char *child_name = NULL;
+ size_t namelen;
+ XsNode *old = *n, *child = NULL;
+ bool stole_child = false;
+ bool this_inplace;
+ XsWatch *watch;
+ int err;
+
+ namelen = strlen(op->path);
+ watch = g_hash_table_lookup(op->s->watches, op->path);
+
+ /* Is there a child, or do we hit the double-NUL termination? */
+ if (op->path[namelen + 1]) {
+ char *slash;
+ child_name = op->path + namelen + 1;
+ slash = strchr(child_name, '/');
+ if (slash) {
+ *slash = '\0';
+ }
+ op->path[namelen] = '/';
+ }
+
+ /* If we walk into a subtree which is shared, we must COW */
+ if (op->mutating && old->ref != 1) {
+ op->inplace = false;
+ }
+
+ if (!child_name) {
+ const char *letters = op->mutating ? "wb" : "rb";
+
+ if (!can_access(op->dom_id, old->perms, letters)) {
+ err = EACCES;
+ goto out;
+ }
+
+ /* This is the actual node on which the operation shall be performed */
+ err = op->op_fn(n, op);
+ if (!err) {
+ fire_watches(op, true);
+ }
+ goto out;
+ }
+
+ /* op->inplace will be further modified during the recursion */
+ this_inplace = op->inplace;
+
+ if (old && old->children) {
+ child = g_hash_table_lookup(old->children, child_name);
+ /* This is a *weak* reference to 'child', owned by the hash table */
+ }
+
+ if (child) {
+ if (child->deleted_in_tx) {
+ assert(child->ref == 1);
+ /* Cannot actually set child->deleted_in_tx = false until later */
+ }
+ xs_node_ref(child);
+ /*
+ * Now we own it too. But if we can modify inplace, that's going to
+ * foil the check and force it to COW. We want to be the *only* owner
+ * so that it can be modified in place, so remove it from the hash
+ * table in that case. We'll add it (or its replacement) back later.
+ */
+ if (op->mutating && this_inplace) {
+ g_hash_table_remove(old->children, child_name);
+ stole_child = true;
+ }
+ } else if (op->create_dirs) {
+ assert(op->mutating);
+
+ if (!can_access(op->dom_id, old->perms, "wb")) {
+ err = EACCES;
+ goto out;
+ }
+
+ if (op->dom_id && op->new_nr_nodes >= XS_MAX_DOMAIN_NODES) {
+ err = ENOSPC;
+ goto out;
+ }
+
+ child = xs_node_create(child_name, old->perms);
+ op->new_nr_nodes++;
+
+ /*
+ * If we're creating a new child, we can clearly modify it (and its
+ * children) in place from here on down.
+ */
+ op->inplace = true;
+ } else {
+ err = ENOENT;
+ goto out;
+ }
+
+ /*
+ * If there's a watch on this node, add it to the list to be fired
+ * (with the correct full pathname for the modified node) at the end.
+ */
+ if (watch) {
+ op->watches = g_list_append(op->watches, watch);
+ }
+
+ /*
+ * Except for the temporary child-stealing as noted, our node has not
+ * changed yet. We don't yet know the overall operation will complete.
+ */
+ err = xs_node_walk(&child, op);
+
+ if (watch) {
+ op->watches = g_list_remove(op->watches, watch);
+ }
+
+ if (err || !op->mutating) {
+ if (stole_child) {
+ /* Put it back as it was. */
+ g_hash_table_replace(old->children, g_strdup(child_name), child);
+ } else {
+ xs_node_unref(child);
+ }
+ goto out;
+ }
+
+ /*
+ * Now we know the operation has completed successfully and we're on
+ * the way back up. Make the change, substituting 'child' in the
+ * node at our level.
+ */
+ if (!this_inplace) {
+ *n = xs_node_copy(old);
+ xs_node_unref(old);
+ }
+
+ /*
+ * If we resurrected a deleted_in_tx node, we can mark it as no longer
+ * deleted now that we know the overall operation has succeeded.
+ */
+ if (op->create_dirs && child && child->deleted_in_tx) {
+ op->new_nr_nodes++;
+ child->deleted_in_tx = false;
+ }
+
+ /*
+ * The child may be NULL here, for a remove operation. Either way,
+ * xs_node_add_child() will do the right thing and return a value
+ * indicating whether it changed the parent's hash table or not.
+ *
+ * We bump the parent gencnt if it adds a child that we *didn't*
+ * steal from it in the first place, or if child==NULL and was
+ * thus removed (whether we stole it earlier and didn't put it
+ * back, or xs_node_add_child() actually removed it now).
+ */
+ if ((xs_node_add_child(*n, child_name, child) && !stole_child) || !child) {
+ (*n)->gencnt++;
+ }
+
+ out:
+ op->path[namelen] = '\0';
+ if (!namelen) {
+ assert(!op->watches);
+ /*
+ * On completing the recursion back up the path walk and reaching the
+ * top, assign the new node count if the operation was successful. If
+ * the main tree was changed, bump its tx ID so that outstanding
+ * transactions correctly fail. But don't bump it every time; only
+ * if it makes a difference.
+ */
+ if (!err && op->mutating) {
+ if (!op->in_transaction) {
+ if (op->s->root_tx != op->s->last_tx) {
+ op->s->root_tx = next_tx(op->s);
+ }
+ op->s->nr_nodes = op->new_nr_nodes;
+ } else {
+ XsTransaction *tx = g_hash_table_lookup(op->s->transactions,
+ GINT_TO_POINTER(op->tx_id));
+ assert(tx);
+ tx->nr_nodes = op->new_nr_nodes;
+ }
+ }
+ }
+ return err;
+}
+
+static void append_directory_item(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ GList **items = user_data;
+
+ *items = g_list_insert_sorted(*items, g_strdup(key), (GCompareFunc)strcmp);
+}
+
+/* Populates items with char * names which caller must free. */
+static int xs_node_directory(XsNode **n, struct walk_op *op)
+{
+ GList **items = op->op_opaque;
+
+ assert(op->inplace);
+ assert(*n);
+
+ if ((*n)->children) {
+ g_hash_table_foreach((*n)->children, append_directory_item, items);
+ }
+
+ if (op->op_opaque2) {
+ *(uint64_t *)op->op_opaque2 = (*n)->gencnt;
+ }
+
+ return 0;
+}
+
+static int validate_path(char *outpath, const char *userpath,
+ unsigned int dom_id)
+{
+ size_t i, pathlen = strlen(userpath);
+
+ if (!pathlen || userpath[pathlen] == '/' || strstr(userpath, "//")) {
+ return EINVAL;
+ }
+ for (i = 0; i < pathlen; i++) {
+ if (!strchr(XS_VALID_CHARS, userpath[i])) {
+ return EINVAL;
+ }
+ }
+ if (userpath[0] == '/') {
+ if (pathlen > XENSTORE_ABS_PATH_MAX) {
+ return E2BIG;
+ }
+ memcpy(outpath, userpath, pathlen + 1);
+ } else {
+ if (pathlen > XENSTORE_REL_PATH_MAX) {
+ return E2BIG;
+ }
+ snprintf(outpath, XENSTORE_ABS_PATH_MAX, "/local/domain/%u/%s", dom_id,
+ userpath);
+ }
+ return 0;
+}
+
+
+static int init_walk_op(XenstoreImplState *s, struct walk_op *op,
+ xs_transaction_t tx_id, unsigned int dom_id,
+ const char *path, XsNode ***rootp)
+{
+ int ret = validate_path(op->path, path, dom_id);
+ if (ret) {
+ return ret;
+ }
+
+ /*
+ * We use *two* NUL terminators at the end of the path, as during the walk
+ * we will temporarily turn each '/' into a NUL to allow us to use that
+ * path element for the lookup.
+ */
+ op->path[strlen(op->path) + 1] = '\0';
+ op->watches = NULL;
+ op->path[0] = '\0';
+ op->inplace = true;
+ op->mutating = false;
+ op->create_dirs = false;
+ op->in_transaction = false;
+ op->dom_id = dom_id;
+ op->tx_id = tx_id;
+ op->s = s;
+
+ if (tx_id == XBT_NULL) {
+ *rootp = &s->root;
+ op->new_nr_nodes = s->nr_nodes;
+ } else {
+ XsTransaction *tx = g_hash_table_lookup(s->transactions,
+ GINT_TO_POINTER(tx_id));
+ if (!tx) {
+ return ENOENT;
+ }
+ *rootp = &tx->root;
+ op->new_nr_nodes = tx->nr_nodes;
+ op->in_transaction = true;
+ }
+
+ return 0;
+}
+
+int xs_impl_read(XenstoreImplState *s, unsigned int dom_id,
+ xs_transaction_t tx_id, const char *path, GByteArray *data)
+{
+ /*
+ * The data GByteArray shall exist, and will be freed by caller.
+ * Just g_byte_array_append() to it.
+ */
+ struct walk_op op;
+ XsNode **n;
+ int ret;
+
+ ret = init_walk_op(s, &op, tx_id, dom_id, path, &n);
+ if (ret) {
+ return ret;
+ }
+ op.op_fn = xs_node_get_content;
+ op.op_opaque = data;
+ return xs_node_walk(n, &op);
+}
+
+int xs_impl_write(XenstoreImplState *s, unsigned int dom_id,
+ xs_transaction_t tx_id, const char *path, GByteArray *data)
+{
+ /*
+ * The data GByteArray shall exist, will be freed by caller. You are
+ * free to use g_byte_array_steal() and keep the data. Or just ref it.
+ */
+ struct walk_op op;
+ XsNode **n;
+ int ret;
+
+ ret = init_walk_op(s, &op, tx_id, dom_id, path, &n);
+ if (ret) {
+ return ret;
+ }
+ op.op_fn = xs_node_add_content;
+ op.op_opaque = data;
+ op.mutating = true;
+ op.create_dirs = true;
+ return xs_node_walk(n, &op);
+}
+
+int xs_impl_directory(XenstoreImplState *s, unsigned int dom_id,
+ xs_transaction_t tx_id, const char *path,
+ uint64_t *gencnt, GList **items)
+{
+ /*
+ * The items are (char *) to be freed by caller. Although it's consumed
+ * immediately so if you want to change it to (const char *) and keep
+ * them, go ahead and change the caller.
+ */
+ struct walk_op op;
+ XsNode **n;
+ int ret;
+
+ ret = init_walk_op(s, &op, tx_id, dom_id, path, &n);
+ if (ret) {
+ return ret;
+ }
+ op.op_fn = xs_node_directory;
+ op.op_opaque = items;
+ op.op_opaque2 = gencnt;
+ return xs_node_walk(n, &op);
+}
+
+int xs_impl_transaction_start(XenstoreImplState *s, unsigned int dom_id,
+ xs_transaction_t *tx_id)
+{
+ XsTransaction *tx;
+
+ if (*tx_id != XBT_NULL) {
+ return EINVAL;
+ }
+
+ if (dom_id && s->nr_domu_transactions >= XS_MAX_TRANSACTIONS) {
+ return ENOSPC;
+ }
+
+ tx = g_new0(XsTransaction, 1);
+
+ tx->nr_nodes = s->nr_nodes;
+ tx->tx_id = next_tx(s);
+ tx->base_tx = s->root_tx;
+ tx->root = xs_node_ref(s->root);
+ tx->dom_id = dom_id;
+
+ g_hash_table_insert(s->transactions, GINT_TO_POINTER(tx->tx_id), tx);
+ if (dom_id) {
+ s->nr_domu_transactions++;
+ }
+ *tx_id = tx->tx_id;
+ return 0;
+}
+
+static gboolean tx_commit_walk(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ struct walk_op *op = user_data;
+ int path_len = strlen(op->path);
+ int key_len = strlen(key);
+ bool fire_parents = true;
+ XsWatch *watch;
+ XsNode *n = value;
+
+ if (n->ref != 1) {
+ return false;
+ }
+
+ if (n->deleted_in_tx) {
+ /*
+ * We fire watches on our parents if we are the *first* node
+ * to be deleted (the topmost one). This matches the behaviour
+ * when deleting in the live tree.
+ */
+ fire_parents = !op->deleted_in_tx;
+
+ /* Only used on the way down so no need to clear it later */
+ op->deleted_in_tx = true;
+ }
+
+ assert(key_len + path_len + 2 <= sizeof(op->path));
+ op->path[path_len] = '/';
+ memcpy(op->path + path_len + 1, key, key_len + 1);
+
+ watch = g_hash_table_lookup(op->s->watches, op->path);
+ if (watch) {
+ op->watches = g_list_append(op->watches, watch);
+ }
+
+ if (n->children) {
+ g_hash_table_foreach_remove(n->children, tx_commit_walk, op);
+ }
+
+ if (watch) {
+ op->watches = g_list_remove(op->watches, watch);
+ }
+
+ /*
+ * Don't fire watches if this node was only copied because a
+ * descendent was changed. The modified_in_tx flag indicates the
+ * ones which were really changed.
+ */
+ if (n->modified_in_tx || n->deleted_in_tx) {
+ fire_watches(op, fire_parents);
+ n->modified_in_tx = false;
+ }
+ op->path[path_len] = '\0';
+
+ /* Deleted nodes really do get expunged when we commit */
+ return n->deleted_in_tx;
+}
+
+static int transaction_commit(XenstoreImplState *s, XsTransaction *tx)
+{
+ struct walk_op op;
+ XsNode **n;
+
+ if (s->root_tx != tx->base_tx) {
+ return EAGAIN;
+ }
+ xs_node_unref(s->root);
+ s->root = tx->root;
+ tx->root = NULL;
+ s->root_tx = tx->tx_id;
+ s->nr_nodes = tx->nr_nodes;
+
+ init_walk_op(s, &op, XBT_NULL, tx->dom_id, "/", &n);
+ op.deleted_in_tx = false;
+ op.mutating = true;
+
+ /*
+ * Walk the new root and fire watches on any node which has a
+ * refcount of one (which is therefore unique to this transaction).
+ */
+ if (s->root->children) {
+ g_hash_table_foreach_remove(s->root->children, tx_commit_walk, &op);
+ }
+
+ return 0;
+}
+
+int xs_impl_transaction_end(XenstoreImplState *s, unsigned int dom_id,
+ xs_transaction_t tx_id, bool commit)
+{
+ int ret = 0;
+ XsTransaction *tx = g_hash_table_lookup(s->transactions,
+ GINT_TO_POINTER(tx_id));
+
+ if (!tx || tx->dom_id != dom_id) {
+ return ENOENT;
+ }
+
+ if (commit) {
+ ret = transaction_commit(s, tx);
+ }
+
+ g_hash_table_remove(s->transactions, GINT_TO_POINTER(tx_id));
+ if (dom_id) {
+ assert(s->nr_domu_transactions);
+ s->nr_domu_transactions--;
+ }
+ return ret;
+}
+
+int xs_impl_rm(XenstoreImplState *s, unsigned int dom_id,
+ xs_transaction_t tx_id, const char *path)
+{
+ struct walk_op op;
+ XsNode **n;
+ int ret;
+
+ ret = init_walk_op(s, &op, tx_id, dom_id, path, &n);
+ if (ret) {
+ return ret;
+ }
+ op.op_fn = xs_node_rm;
+ op.mutating = true;
+ return xs_node_walk(n, &op);
+}
+
+int xs_impl_get_perms(XenstoreImplState *s, unsigned int dom_id,
+ xs_transaction_t tx_id, const char *path, GList **perms)
+{
+ struct walk_op op;
+ XsNode **n;
+ int ret;
+
+ ret = init_walk_op(s, &op, tx_id, dom_id, path, &n);
+ if (ret) {
+ return ret;
+ }
+ op.op_fn = xs_node_get_perms;
+ op.op_opaque = perms;
+ return xs_node_walk(n, &op);
+}
+
+static void is_valid_perm(gpointer data, gpointer user_data)
+{
+ char *perm = data;
+ bool *valid = user_data;
+ char letter;
+ unsigned int dom_id;
+
+ if (!*valid) {
+ return;
+ }
+
+ if (sscanf(perm, "%c%u", &letter, &dom_id) != 2) {
+ *valid = false;
+ return;
+ }
+
+ switch (letter) {
+ case 'n':
+ case 'r':
+ case 'w':
+ case 'b':
+ break;
+
+ default:
+ *valid = false;
+ break;
+ }
+}
+
+int xs_impl_set_perms(XenstoreImplState *s, unsigned int dom_id,
+ xs_transaction_t tx_id, const char *path, GList *perms)
+{
+ struct walk_op op;
+ XsNode **n;
+ bool valid = true;
+ int ret;
+
+ if (!g_list_length(perms)) {
+ return EINVAL;
+ }
+
+ g_list_foreach(perms, is_valid_perm, &valid);
+ if (!valid) {
+ return EINVAL;
+ }
+
+ ret = init_walk_op(s, &op, tx_id, dom_id, path, &n);
+ if (ret) {
+ return ret;
+ }
+ op.op_fn = xs_node_set_perms;
+ op.op_opaque = perms;
+ op.mutating = true;
+ return xs_node_walk(n, &op);
+}
+
+static int do_xs_impl_watch(XenstoreImplState *s, unsigned int dom_id,
+ const char *path, const char *token,
+ xs_impl_watch_fn fn, void *opaque)
+
+{
+ char abspath[XENSTORE_ABS_PATH_MAX + 1];
+ XsWatch *w, *l;
+ int ret;
+
+ ret = validate_path(abspath, path, dom_id);
+ if (ret) {
+ return ret;
+ }
+
+ /* Check for duplicates */
+ l = w = g_hash_table_lookup(s->watches, abspath);
+ while (w) {
+ if (!g_strcmp0(token, w->token) && opaque == w->cb_opaque &&
+ fn == w->cb && dom_id == w->dom_id) {
+ return EEXIST;
+ }
+ w = w->next;
+ }
+
+ if (dom_id && s->nr_domu_watches >= XS_MAX_WATCHES) {
+ return E2BIG;
+ }
+
+ w = g_new0(XsWatch, 1);
+ w->token = g_strdup(token);
+ w->cb = fn;
+ w->cb_opaque = opaque;
+ w->dom_id = dom_id;
+ w->rel_prefix = strlen(abspath) - strlen(path);
+
+ /* l was looked up above when checking for duplicates */
+ if (l) {
+ w->next = l->next;
+ l->next = w;
+ } else {
+ g_hash_table_insert(s->watches, g_strdup(abspath), w);
+ }
+ if (dom_id) {
+ s->nr_domu_watches++;
+ }
+
+ return 0;
+}
+
+int xs_impl_watch(XenstoreImplState *s, unsigned int dom_id, const char *path,
+ const char *token, xs_impl_watch_fn fn, void *opaque)
+{
+ int ret = do_xs_impl_watch(s, dom_id, path, token, fn, opaque);
+
+ if (!ret) {
+ /* A new watch should fire immediately */
+ fn(opaque, path, token);
+ }
+
+ return ret;
+}
+
+static XsWatch *free_watch(XenstoreImplState *s, XsWatch *w)
+{
+ XsWatch *next = w->next;
+
+ if (w->dom_id) {
+ assert(s->nr_domu_watches);
+ s->nr_domu_watches--;
+ }
+
+ g_free(w->token);
+ g_free(w);
+
+ return next;
+}
+
+int xs_impl_unwatch(XenstoreImplState *s, unsigned int dom_id,
+ const char *path, const char *token,
+ xs_impl_watch_fn fn, void *opaque)
+{
+ char abspath[XENSTORE_ABS_PATH_MAX + 1];
+ XsWatch *w, **l;
+ int ret;
+
+ ret = validate_path(abspath, path, dom_id);
+ if (ret) {
+ return ret;
+ }
+
+ w = g_hash_table_lookup(s->watches, abspath);
+ if (!w) {
+ return ENOENT;
+ }
+
+ /*
+ * The hash table contains the first element of a list of
+ * watches. Removing the first element in the list is a
+ * special case because we have to update the hash table to
+ * point to the next (or remove it if there's nothing left).
+ */
+ if (!g_strcmp0(token, w->token) && fn == w->cb && opaque == w->cb_opaque &&
+ dom_id == w->dom_id) {
+ if (w->next) {
+ /* Insert the previous 'next' into the hash table */
+ g_hash_table_insert(s->watches, g_strdup(abspath), w->next);
+ } else {
+ /* Nothing left; remove from hash table */
+ g_hash_table_remove(s->watches, abspath);
+ }
+ free_watch(s, w);
+ return 0;
+ }
+
+ /*
+ * We're all done messing with the hash table because the element
+ * it points to has survived the cull. Now it's just a simple
+ * linked list removal operation.
+ */
+ for (l = &w->next; *l; l = &w->next) {
+ w = *l;
+
+ if (!g_strcmp0(token, w->token) && fn == w->cb &&
+ opaque != w->cb_opaque && dom_id == w->dom_id) {
+ *l = free_watch(s, w);
+ return 0;
+ }
+ }
+
+ return ENOENT;
+}
+
+int xs_impl_reset_watches(XenstoreImplState *s, unsigned int dom_id)
+{
+ char **watch_paths;
+ guint nr_watch_paths;
+ guint i;
+
+ watch_paths = (char **)g_hash_table_get_keys_as_array(s->watches,
+ &nr_watch_paths);
+
+ for (i = 0; i < nr_watch_paths; i++) {
+ XsWatch *w1 = g_hash_table_lookup(s->watches, watch_paths[i]);
+ XsWatch *w2, *w, **l;
+
+ /*
+ * w1 is the original list. The hash table has this pointer.
+ * w2 is the head of our newly-filtered list.
+ * w and l are temporary for processing. w is somewhat redundant
+ * with *l but makes my eyes bleed less.
+ */
+
+ w = w2 = w1;
+ l = &w;
+ while (w) {
+ if (w->dom_id == dom_id) {
+ /* If we're freeing the head of the list, bump w2 */
+ if (w2 == w) {
+ w2 = w->next;
+ }
+ *l = free_watch(s, w);
+ } else {
+ l = &w->next;
+ }
+ w = *l;
+ }
+ /*
+ * If the head of the list survived the cull, we don't need to
+ * touch the hash table and we're done with this path. Else...
+ */
+ if (w1 != w2) {
+ g_hash_table_steal(s->watches, watch_paths[i]);
+
+ /*
+ * It was already freed. (Don't worry, this whole thing is
+ * single-threaded and nobody saw it in the meantime). And
+ * having *stolen* it, we now own the watch_paths[i] string
+ * so if we don't give it back to the hash table, we need
+ * to free it.
+ */
+ if (w2) {
+ g_hash_table_insert(s->watches, watch_paths[i], w2);
+ } else {
+ g_free(watch_paths[i]);
+ }
+ }
+ }
+ g_free(watch_paths);
+ return 0;
+}
+
+static void xs_tx_free(void *_tx)
+{
+ XsTransaction *tx = _tx;
+ if (tx->root) {
+ xs_node_unref(tx->root);
+ }
+ g_free(tx);
+}
+
+XenstoreImplState *xs_impl_create(unsigned int dom_id)
+{
+ XenstoreImplState *s = g_new0(XenstoreImplState, 1);
+ GList *perms;
+
+ s->watches = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+ s->transactions = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ NULL, xs_tx_free);
+
+ perms = g_list_append(NULL, xs_perm_as_string(XS_PERM_NONE, 0));
+ s->root = xs_node_create("/", perms);
+ g_list_free_full(perms, g_free);
+ s->nr_nodes = 1;
+
+ s->root_tx = s->last_tx = 1;
+ return s;
+}
+
+
+static void clear_serialized_tx(gpointer key, gpointer value, gpointer opaque)
+{
+ XsNode *n = value;
+
+ n->serialized_tx = XBT_NULL;
+ if (n->children) {
+ g_hash_table_foreach(n->children, clear_serialized_tx, NULL);
+ }
+}
+
+static void clear_tx_serialized_tx(gpointer key, gpointer value,
+ gpointer opaque)
+{
+ XsTransaction *t = value;
+
+ clear_serialized_tx(NULL, t->root, NULL);
+}
+
+static void write_be32(GByteArray *save, uint32_t val)
+{
+ uint32_t be = htonl(val);
+ g_byte_array_append(save, (void *)&be, sizeof(be));
+}
+
+
+struct save_state {
+ GByteArray *bytes;
+ unsigned int tx_id;
+};
+
+#define MODIFIED_IN_TX (1U << 0)
+#define DELETED_IN_TX (1U << 1)
+#define NODE_REF (1U << 2)
+
+static void save_node(gpointer key, gpointer value, gpointer opaque)
+{
+ struct save_state *ss = opaque;
+ XsNode *n = value;
+ char *name = key;
+ uint8_t flag = 0;
+
+ /* Child nodes (i.e. anything but the root) have a name */
+ if (name) {
+ g_byte_array_append(ss->bytes, key, strlen(key) + 1);
+ }
+
+ /*
+ * If we already wrote this node, refer to the previous copy.
+ * There's no rename/move in XenStore, so all we need to find
+ * it is the tx_id of the transation in which it exists. Which
+ * may be the root tx.
+ */
+ if (n->serialized_tx != XBT_NULL) {
+ flag = NODE_REF;
+ g_byte_array_append(ss->bytes, &flag, 1);
+ write_be32(ss->bytes, n->serialized_tx);
+ } else {
+ GList *l;
+ n->serialized_tx = ss->tx_id;
+
+ if (n->modified_in_tx) {
+ flag |= MODIFIED_IN_TX;
+ }
+ if (n->deleted_in_tx) {
+ flag |= DELETED_IN_TX;
+ }
+ g_byte_array_append(ss->bytes, &flag, 1);
+
+ if (n->content) {
+ write_be32(ss->bytes, n->content->len);
+ g_byte_array_append(ss->bytes, n->content->data, n->content->len);
+ } else {
+ write_be32(ss->bytes, 0);
+ }
+
+ for (l = n->perms; l; l = l->next) {
+ g_byte_array_append(ss->bytes, l->data, strlen(l->data) + 1);
+ }
+ /* NUL termination after perms */
+ g_byte_array_append(ss->bytes, (void *)"", 1);
+
+ if (n->children) {
+ g_hash_table_foreach(n->children, save_node, ss);
+ }
+ /* NUL termination after children (child name is NUL) */
+ g_byte_array_append(ss->bytes, (void *)"", 1);
+ }
+}
+
+static void save_tree(struct save_state *ss, uint32_t tx_id, XsNode *root)
+{
+ write_be32(ss->bytes, tx_id);
+ ss->tx_id = tx_id;
+ save_node(NULL, root, ss);
+}
+
+static void save_tx(gpointer key, gpointer value, gpointer opaque)
+{
+ uint32_t tx_id = GPOINTER_TO_INT(key);
+ struct save_state *ss = opaque;
+ XsTransaction *n = value;
+
+ write_be32(ss->bytes, n->base_tx);
+ write_be32(ss->bytes, n->dom_id);
+
+ save_tree(ss, tx_id, n->root);
+}
+
+static void save_watch(gpointer key, gpointer value, gpointer opaque)
+{
+ struct save_state *ss = opaque;
+ XsWatch *w = value;
+
+ /* We only save the *guest* watches. */
+ if (w->dom_id) {
+ gpointer relpath = key + w->rel_prefix;
+ g_byte_array_append(ss->bytes, relpath, strlen(relpath) + 1);
+ g_byte_array_append(ss->bytes, (void *)w->token, strlen(w->token) + 1);
+ }
+}
+
+GByteArray *xs_impl_serialize(XenstoreImplState *s)
+{
+ struct save_state ss;
+
+ ss.bytes = g_byte_array_new();
+
+ /*
+ * node = flags [ real_node / node_ref ]
+ * flags = uint8_t (MODIFIED_IN_TX | DELETED_IN_TX | NODE_REF)
+ * node_ref = tx_id (in which the original version of this node exists)
+ * real_node = content perms child* NUL
+ * content = len data
+ * len = uint32_t
+ * data = uint8_t{len}
+ * perms = perm* NUL
+ * perm = asciiz
+ * child = name node
+ * name = asciiz
+ *
+ * tree = tx_id node
+ * tx_id = uint32_t
+ *
+ * transaction = base_tx_id dom_id tree
+ * base_tx_id = uint32_t
+ * dom_id = uint32_t
+ *
+ * tx_list = tree transaction* XBT_NULL
+ *
+ * watch = path token
+ * path = asciiz
+ * token = asciiz
+ *
+ * watch_list = watch* NUL
+ *
+ * xs_serialize_stream = last_tx tx_list watch_list
+ * last_tx = uint32_t
+ */
+
+ /* Clear serialized_tx in every node. */
+ if (s->serialized) {
+ clear_serialized_tx(NULL, s->root, NULL);
+ g_hash_table_foreach(s->transactions, clear_tx_serialized_tx, NULL);
+ }
+
+ s->serialized = true;
+
+ write_be32(ss.bytes, s->last_tx);
+ save_tree(&ss, s->root_tx, s->root);
+ g_hash_table_foreach(s->transactions, save_tx, &ss);
+
+ write_be32(ss.bytes, XBT_NULL);
+
+ g_hash_table_foreach(s->watches, save_watch, &ss);
+ g_byte_array_append(ss.bytes, (void *)"", 1);
+
+ return ss.bytes;
+}
+
+struct unsave_state {
+ char path[XENSTORE_ABS_PATH_MAX + 1];
+ XenstoreImplState *s;
+ GByteArray *bytes;
+ uint8_t *d;
+ size_t l;
+ bool root_walk;
+};
+
+static int consume_be32(struct unsave_state *us, unsigned int *val)
+{
+ uint32_t d;
+
+ if (us->l < sizeof(d)) {
+ return -EINVAL;
+ }
+ memcpy(&d, us->d, sizeof(d));
+ *val = ntohl(d);
+ us->d += sizeof(d);
+ us->l -= sizeof(d);
+ return 0;
+}
+
+static int consume_string(struct unsave_state *us, char **str, size_t *len)
+{
+ size_t l;
+
+ if (!us->l) {
+ return -EINVAL;
+ }
+
+ l = strnlen((void *)us->d, us->l);
+ if (l == us->l) {
+ return -EINVAL;
+ }
+
+ if (str) {
+ *str = (void *)us->d;
+ }
+ if (len) {
+ *len = l;
+ }
+
+ us->d += l + 1;
+ us->l -= l + 1;
+ return 0;
+}
+
+static XsNode *lookup_node(XsNode *n, char *path)
+{
+ char *slash = strchr(path, '/');
+ XsNode *child;
+
+ if (path[0] == '\0') {
+ return n;
+ }
+
+ if (slash) {
+ *slash = '\0';
+ }
+
+ if (!n->children) {
+ return NULL;
+ }
+ child = g_hash_table_lookup(n->children, path);
+ if (!slash) {
+ return child;
+ }
+
+ *slash = '/';
+ if (!child) {
+ return NULL;
+ }
+ return lookup_node(child, slash + 1);
+}
+
+static XsNode *lookup_tx_node(struct unsave_state *us, unsigned int tx_id)
+{
+ XsTransaction *t;
+ if (tx_id == us->s->root_tx) {
+ return lookup_node(us->s->root, us->path + 1);
+ }
+
+ t = g_hash_table_lookup(us->s->transactions, GINT_TO_POINTER(tx_id));
+ if (!t) {
+ return NULL;
+ }
+ g_assert(t->root);
+ return lookup_node(t->root, us->path + 1);
+}
+
+static void count_child_nodes(gpointer key, gpointer value, gpointer user_data)
+{
+ unsigned int *nr_nodes = user_data;
+ XsNode *n = value;
+
+ (*nr_nodes)++;
+
+ if (n->children) {
+ g_hash_table_foreach(n->children, count_child_nodes, nr_nodes);
+ }
+}
+
+static int consume_node(struct unsave_state *us, XsNode **nodep,
+ unsigned int *nr_nodes)
+{
+ XsNode *n = NULL;
+ uint8_t flags;
+ int ret;
+
+ if (us->l < 1) {
+ return -EINVAL;
+ }
+ flags = us->d[0];
+ us->d++;
+ us->l--;
+
+ if (flags == NODE_REF) {
+ unsigned int tx;
+
+ ret = consume_be32(us, &tx);
+ if (ret) {
+ return ret;
+ }
+
+ n = lookup_tx_node(us, tx);
+ if (!n) {
+ return -EINVAL;
+ }
+ n->ref++;
+ if (n->children) {
+ g_hash_table_foreach(n->children, count_child_nodes, nr_nodes);
+ }
+ } else {
+ uint32_t datalen;
+
+ if (flags & ~(DELETED_IN_TX | MODIFIED_IN_TX)) {
+ return -EINVAL;
+ }
+ n = xs_node_new();
+
+ if (flags & DELETED_IN_TX) {
+ n->deleted_in_tx = true;
+ }
+ if (flags & MODIFIED_IN_TX) {
+ n->modified_in_tx = true;
+ }
+ ret = consume_be32(us, &datalen);
+ if (ret) {
+ xs_node_unref(n);
+ return -EINVAL;
+ }
+ if (datalen) {
+ if (datalen > us->l) {
+ xs_node_unref(n);
+ return -EINVAL;
+ }
+
+ GByteArray *node_data = g_byte_array_new();
+ g_byte_array_append(node_data, us->d, datalen);
+ us->d += datalen;
+ us->l -= datalen;
+ n->content = node_data;
+
+ if (us->root_walk) {
+ n->modified_in_tx = true;
+ }
+ }
+ while (1) {
+ char *perm = NULL;
+ size_t permlen = 0;
+
+ ret = consume_string(us, &perm, &permlen);
+ if (ret) {
+ xs_node_unref(n);
+ return ret;
+ }
+
+ if (!permlen) {
+ break;
+ }
+
+ n->perms = g_list_append(n->perms, g_strdup(perm));
+ }
+
+ /* Now children */
+ while (1) {
+ size_t childlen;
+ char *childname;
+ char *pathend;
+ XsNode *child = NULL;
+
+ ret = consume_string(us, &childname, &childlen);
+ if (ret) {
+ xs_node_unref(n);
+ return ret;
+ }
+
+ if (!childlen) {
+ break;
+ }
+
+ pathend = us->path + strlen(us->path);
+ strncat(us->path, "/", sizeof(us->path) - 1);
+ strncat(us->path, childname, sizeof(us->path) - 1);
+
+ ret = consume_node(us, &child, nr_nodes);
+ *pathend = '\0';
+ if (ret) {
+ xs_node_unref(n);
+ return ret;
+ }
+ g_assert(child);
+ xs_node_add_child(n, childname, child);
+ }
+
+ /*
+ * If the node has no data and no children we still want to fire
+ * a watch on it.
+ */
+ if (us->root_walk && !n->children) {
+ n->modified_in_tx = true;
+ }
+ }
+
+ if (!n->deleted_in_tx) {
+ (*nr_nodes)++;
+ }
+
+ *nodep = n;
+ return 0;
+}
+
+static int consume_tree(struct unsave_state *us, XsTransaction *t)
+{
+ int ret;
+
+ ret = consume_be32(us, &t->tx_id);
+ if (ret) {
+ return ret;
+ }
+
+ if (t->tx_id > us->s->last_tx) {
+ return -EINVAL;
+ }
+
+ us->path[0] = '\0';
+
+ return consume_node(us, &t->root, &t->nr_nodes);
+}
+
+int xs_impl_deserialize(XenstoreImplState *s, GByteArray *bytes,
+ unsigned int dom_id, xs_impl_watch_fn watch_fn,
+ void *watch_opaque)
+{
+ struct unsave_state us;
+ XsTransaction base_t = { 0 };
+ int ret;
+
+ us.s = s;
+ us.bytes = bytes;
+ us.d = bytes->data;
+ us.l = bytes->len;
+
+ xs_impl_reset_watches(s, dom_id);
+ g_hash_table_remove_all(s->transactions);
+
+ xs_node_unref(s->root);
+ s->root = NULL;
+ s->root_tx = s->last_tx = XBT_NULL;
+
+ ret = consume_be32(&us, &s->last_tx);
+ if (ret) {
+ return ret;
+ }
+
+ /*
+ * Consume the base tree into a transaction so that watches can be
+ * fired as we commit it. By setting us.root_walk we cause the nodes
+ * to be marked as 'modified_in_tx' as they are created, so that the
+ * watches are triggered on them.
+ */
+ base_t.dom_id = dom_id;
+ base_t.base_tx = XBT_NULL;
+ us.root_walk = true;
+ ret = consume_tree(&us, &base_t);
+ if (ret) {
+ return ret;
+ }
+ us.root_walk = false;
+
+ /*
+ * Commit the transaction now while the refcount on all nodes is 1.
+ * Note that we haven't yet reinstated the *guest* watches but that's
+ * OK because we don't want the guest to see any changes. Even any
+ * backend nodes which get recreated should be *precisely* as they
+ * were before the migration. Back ends may have been instantiated
+ * already, and will see the frontend magically blink into existence
+ * now (well, from the aio_bh which fires the watches). It's their
+ * responsibility to rebuild everything precisely as it was before.
+ */
+ ret = transaction_commit(s, &base_t);
+ if (ret) {
+ return ret;
+ }
+
+ while (1) {
+ unsigned int base_tx;
+ XsTransaction *t;
+
+ ret = consume_be32(&us, &base_tx);
+ if (ret) {
+ return ret;
+ }
+ if (base_tx == XBT_NULL) {
+ break;
+ }
+
+ t = g_new0(XsTransaction, 1);
+ t->base_tx = base_tx;
+
+ ret = consume_be32(&us, &t->dom_id);
+ if (!ret) {
+ ret = consume_tree(&us, t);
+ }
+ if (ret) {
+ g_free(t);
+ return ret;
+ }
+ g_assert(t->root);
+ if (t->dom_id) {
+ s->nr_domu_transactions++;
+ }
+ g_hash_table_insert(s->transactions, GINT_TO_POINTER(t->tx_id), t);
+ }
+
+ while (1) {
+ char *path, *token;
+ size_t pathlen, toklen;
+
+ ret = consume_string(&us, &path, &pathlen);
+ if (ret) {
+ return ret;
+ }
+ if (!pathlen) {
+ break;
+ }
+
+ ret = consume_string(&us, &token, &toklen);
+ if (ret) {
+ return ret;
+ }
+
+ if (!watch_fn) {
+ continue;
+ }
+
+ ret = do_xs_impl_watch(s, dom_id, path, token, watch_fn, watch_opaque);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ if (us.l) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
diff --git a/hw/i386/kvm/xenstore_impl.h b/hw/i386/kvm/xenstore_impl.h
new file mode 100644
index 0000000..0df2a91
--- /dev/null
+++ b/hw/i386/kvm/xenstore_impl.h
@@ -0,0 +1,63 @@
+/*
+ * QEMU Xen emulation: The actual implementation of XenStore
+ *
+ * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Authors: David Woodhouse <dwmw2@infradead.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef QEMU_XENSTORE_IMPL_H
+#define QEMU_XENSTORE_IMPL_H
+
+#include "hw/xen/xen_backend_ops.h"
+
+typedef struct XenstoreImplState XenstoreImplState;
+
+XenstoreImplState *xs_impl_create(unsigned int dom_id);
+
+char *xs_perm_as_string(unsigned int perm, unsigned int domid);
+
+/*
+ * These functions return *positive* error numbers. This is a little
+ * unconventional but it helps to keep us honest because there is
+ * also a very limited set of error numbers that they are permitted
+ * to return (those in xsd_errors).
+ */
+
+int xs_impl_read(XenstoreImplState *s, unsigned int dom_id,
+ xs_transaction_t tx_id, const char *path, GByteArray *data);
+int xs_impl_write(XenstoreImplState *s, unsigned int dom_id,
+ xs_transaction_t tx_id, const char *path, GByteArray *data);
+int xs_impl_directory(XenstoreImplState *s, unsigned int dom_id,
+ xs_transaction_t tx_id, const char *path,
+ uint64_t *gencnt, GList **items);
+int xs_impl_transaction_start(XenstoreImplState *s, unsigned int dom_id,
+ xs_transaction_t *tx_id);
+int xs_impl_transaction_end(XenstoreImplState *s, unsigned int dom_id,
+ xs_transaction_t tx_id, bool commit);
+int xs_impl_rm(XenstoreImplState *s, unsigned int dom_id,
+ xs_transaction_t tx_id, const char *path);
+int xs_impl_get_perms(XenstoreImplState *s, unsigned int dom_id,
+ xs_transaction_t tx_id, const char *path, GList **perms);
+int xs_impl_set_perms(XenstoreImplState *s, unsigned int dom_id,
+ xs_transaction_t tx_id, const char *path, GList *perms);
+
+/* This differs from xs_watch_fn because it has the token */
+typedef void(xs_impl_watch_fn)(void *opaque, const char *path,
+ const char *token);
+int xs_impl_watch(XenstoreImplState *s, unsigned int dom_id, const char *path,
+ const char *token, xs_impl_watch_fn fn, void *opaque);
+int xs_impl_unwatch(XenstoreImplState *s, unsigned int dom_id,
+ const char *path, const char *token, xs_impl_watch_fn fn,
+ void *opaque);
+int xs_impl_reset_watches(XenstoreImplState *s, unsigned int dom_id);
+
+GByteArray *xs_impl_serialize(XenstoreImplState *s);
+int xs_impl_deserialize(XenstoreImplState *s, GByteArray *bytes,
+ unsigned int dom_id, xs_impl_watch_fn watch_fn,
+ void *watch_opaque);
+
+#endif /* QEMU_XENSTORE_IMPL_H */
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 7bebea5..1489abf 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -102,6 +102,11 @@
#include "trace.h"
#include CONFIG_DEVICES
+#ifdef CONFIG_XEN_EMU
+#include "hw/xen/xen-legacy-backend.h"
+#include "hw/xen/xen-bus.h"
+#endif
+
/*
* Helper for setting model-id for CPU models that changed model-id
* depending on QEMU versions up to QEMU 2.4.
@@ -1318,6 +1323,8 @@
if (pcms->bus) {
pci_create_simple(pcms->bus, -1, "xen-platform");
}
+ xen_bus_init();
+ xen_be_init();
}
#endif
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 4bf15f9..30eedd6 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -47,8 +47,6 @@
#include "hw/kvm/clock.h"
#include "hw/sysbus.h"
#include "hw/i2c/smbus_eeprom.h"
-#include "hw/xen/xen-x86.h"
-#include "hw/xen/xen.h"
#include "exec/memory.h"
#include "hw/acpi/acpi.h"
#include "hw/acpi/piix4.h"
@@ -60,6 +58,8 @@
#include <xen/hvm/hvm_info_table.h>
#include "hw/xen/xen_pt.h"
#endif
+#include "hw/xen/xen-x86.h"
+#include "hw/xen/xen.h"
#include "migration/global_state.h"
#include "migration/misc.h"
#include "sysemu/numa.h"
diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c
index e5a1dd1..56641a5 100644
--- a/hw/i386/xen/xen-hvm.c
+++ b/hw/i386/xen/xen-hvm.c
@@ -18,7 +18,7 @@
#include "hw/irq.h"
#include "hw/hw.h"
#include "hw/i386/apic-msidef.h"
-#include "hw/xen/xen_common.h"
+#include "hw/xen/xen_native.h"
#include "hw/xen/xen-legacy-backend.h"
#include "hw/xen/xen-bus.h"
#include "hw/xen/xen-x86.h"
@@ -52,10 +52,11 @@
/* Compatibility with older version */
-/* This allows QEMU to build on a system that has Xen 4.5 or earlier
- * installed. This here (not in hw/xen/xen_common.h) because xen/hvm/ioreq.h
- * needs to be included before this block and hw/xen/xen_common.h needs to
- * be included before xen/hvm/ioreq.h
+/*
+ * This allows QEMU to build on a system that has Xen 4.5 or earlier installed.
+ * This is here (not in hw/xen/xen_native.h) because xen/hvm/ioreq.h needs to
+ * be included before this block and hw/xen/xen_native.h needs to be included
+ * before xen/hvm/ioreq.h
*/
#ifndef IOREQ_TYPE_VMWARE_PORT
#define IOREQ_TYPE_VMWARE_PORT 3
@@ -761,7 +762,7 @@
int i;
evtchn_port_t port;
- port = xenevtchn_pending(state->xce_handle);
+ port = qemu_xen_evtchn_pending(state->xce_handle);
if (port == state->bufioreq_local_port) {
timer_mod(state->buffered_io_timer,
BUFFER_IO_MAX_DELAY + qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
@@ -780,7 +781,7 @@
}
/* unmask the wanted port again */
- xenevtchn_unmask(state->xce_handle, port);
+ qemu_xen_evtchn_unmask(state->xce_handle, port);
/* get the io packet from shared memory */
state->send_vcpu = i;
@@ -1147,7 +1148,7 @@
BUFFER_IO_MAX_DELAY + qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
} else {
timer_del(state->buffered_io_timer);
- xenevtchn_unmask(state->xce_handle, state->bufioreq_local_port);
+ qemu_xen_evtchn_unmask(state->xce_handle, state->bufioreq_local_port);
}
}
@@ -1196,8 +1197,8 @@
}
req->state = STATE_IORESP_READY;
- xenevtchn_notify(state->xce_handle,
- state->ioreq_local_port[state->send_vcpu]);
+ qemu_xen_evtchn_notify(state->xce_handle,
+ state->ioreq_local_port[state->send_vcpu]);
}
}
@@ -1206,7 +1207,7 @@
int evtchn_fd = -1;
if (state->xce_handle != NULL) {
- evtchn_fd = xenevtchn_fd(state->xce_handle);
+ evtchn_fd = qemu_xen_evtchn_fd(state->xce_handle);
}
state->buffered_io_timer = timer_new_ms(QEMU_CLOCK_REALTIME, handle_buffered_io,
@@ -1249,7 +1250,7 @@
xenforeignmemory_unmap_resource(xen_fmem, state->fres);
}
- xenevtchn_close(state->xce_handle);
+ qemu_xen_evtchn_close(state->xce_handle);
xs_daemon_close(state->xenstore);
}
@@ -1397,9 +1398,11 @@
xen_pfn_t ioreq_pfn;
XenIOState *state;
+ setup_xen_backend_ops();
+
state = g_new0(XenIOState, 1);
- state->xce_handle = xenevtchn_open(NULL, 0);
+ state->xce_handle = qemu_xen_evtchn_open();
if (state->xce_handle == NULL) {
perror("xen: event channel open");
goto err;
@@ -1463,8 +1466,9 @@
/* FIXME: how about if we overflow the page here? */
for (i = 0; i < max_cpus; i++) {
- rc = xenevtchn_bind_interdomain(state->xce_handle, xen_domid,
- xen_vcpu_eport(state->shared_page, i));
+ rc = qemu_xen_evtchn_bind_interdomain(state->xce_handle, xen_domid,
+ xen_vcpu_eport(state->shared_page,
+ i));
if (rc == -1) {
error_report("shared evtchn %d bind error %d", i, errno);
goto err;
@@ -1472,8 +1476,8 @@
state->ioreq_local_port[i] = rc;
}
- rc = xenevtchn_bind_interdomain(state->xce_handle, xen_domid,
- state->bufioreq_remote_port);
+ rc = qemu_xen_evtchn_bind_interdomain(state->xce_handle, xen_domid,
+ state->bufioreq_remote_port);
if (rc == -1) {
error_report("buffered evtchn bind error %d", errno);
goto err;
diff --git a/hw/i386/xen/xen-mapcache.c b/hw/i386/xen/xen-mapcache.c
index 1d0879d..f7d9746 100644
--- a/hw/i386/xen/xen-mapcache.c
+++ b/hw/i386/xen/xen-mapcache.c
@@ -14,7 +14,7 @@
#include <sys/resource.h>
-#include "hw/xen/xen-legacy-backend.h"
+#include "hw/xen/xen_native.h"
#include "qemu/bitmap.h"
#include "sysemu/runstate.h"
diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c
index 539f7da..57f1d74 100644
--- a/hw/i386/xen/xen_platform.c
+++ b/hw/i386/xen/xen_platform.c
@@ -28,7 +28,6 @@
#include "hw/ide/pci.h"
#include "hw/pci/pci.h"
#include "migration/vmstate.h"
-#include "hw/xen/xen.h"
#include "net/net.h"
#include "trace.h"
#include "sysemu/xen.h"
@@ -38,10 +37,12 @@
#include "qom/object.h"
#ifdef CONFIG_XEN
-#include "hw/xen/xen_common.h"
-#include "hw/xen/xen-legacy-backend.h"
+#include "hw/xen/xen_native.h"
#endif
+/* The rule is that xen_native.h must come first */
+#include "hw/xen/xen.h"
+
//#define DEBUG_PLATFORM
#ifdef DEBUG_PLATFORM
diff --git a/hw/intc/i8259.c b/hw/intc/i8259.c
index 17910f3..bbae2d8 100644
--- a/hw/intc/i8259.c
+++ b/hw/intc/i8259.c
@@ -133,7 +133,7 @@
}
#endif
- if (s->elcr & mask) {
+ if (s->ltim || (s->elcr & mask)) {
/* level triggered */
if (level) {
s->irr |= mask;
@@ -167,7 +167,7 @@
s->isr |= (1 << irq);
}
/* We don't clear a level sensitive interrupt here */
- if (!(s->elcr & (1 << irq))) {
+ if (!s->ltim && !(s->elcr & (1 << irq))) {
s->irr &= ~(1 << irq);
}
pic_update_irq(s);
@@ -224,6 +224,7 @@
PICCommonState *s = PIC_COMMON(dev);
s->elcr = 0;
+ s->ltim = 0;
pic_init_reset(s);
}
@@ -243,10 +244,7 @@
s->init_state = 1;
s->init4 = val & 1;
s->single_mode = val & 2;
- if (val & 0x08) {
- qemu_log_mask(LOG_UNIMP,
- "i8259: level sensitive irq not supported\n");
- }
+ s->ltim = val & 8;
} else if (val & 0x08) {
if (val & 0x04) {
s->poll = 1;
diff --git a/hw/intc/i8259_common.c b/hw/intc/i8259_common.c
index af2e4a2..c931dc2 100644
--- a/hw/intc/i8259_common.c
+++ b/hw/intc/i8259_common.c
@@ -51,7 +51,7 @@
s->special_fully_nested_mode = 0;
s->init4 = 0;
s->single_mode = 0;
- /* Note: ELCR is not reset */
+ /* Note: ELCR and LTIM are not reset */
}
static int pic_dispatch_pre_save(void *opaque)
@@ -144,6 +144,24 @@
s->special_fully_nested_mode);
}
+static bool ltim_state_needed(void *opaque)
+{
+ PICCommonState *s = PIC_COMMON(opaque);
+
+ return !!s->ltim;
+}
+
+static const VMStateDescription vmstate_pic_ltim = {
+ .name = "i8259/ltim",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = ltim_state_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(ltim, PICCommonState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_pic_common = {
.name = "i8259",
.version_id = 1,
@@ -168,6 +186,10 @@
VMSTATE_UINT8(single_mode, PICCommonState),
VMSTATE_UINT8(elcr, PICCommonState),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_pic_ltim,
+ NULL
}
};
diff --git a/hw/intc/mips_gic.c b/hw/intc/mips_gic.c
index bda4549..4bdc3b1 100644
--- a/hw/intc/mips_gic.c
+++ b/hw/intc/mips_gic.c
@@ -439,8 +439,8 @@
}
static Property mips_gic_properties[] = {
- DEFINE_PROP_INT32("num-vp", MIPSGICState, num_vps, 1),
- DEFINE_PROP_INT32("num-irq", MIPSGICState, num_irq, 256),
+ DEFINE_PROP_UINT32("num-vp", MIPSGICState, num_vps, 1),
+ DEFINE_PROP_UINT32("num-irq", MIPSGICState, num_irq, 256),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/isa/i82378.c b/hw/isa/i82378.c
index 233059c..5432ab5 100644
--- a/hw/isa/i82378.c
+++ b/hw/isa/i82378.c
@@ -47,6 +47,12 @@
},
};
+static void i82378_request_out0_irq(void *opaque, int irq, int level)
+{
+ I82378State *s = opaque;
+ qemu_set_irq(s->cpu_intr, level);
+}
+
static void i82378_request_pic_irq(void *opaque, int irq, int level)
{
DeviceState *dev = opaque;
@@ -88,7 +94,9 @@
*/
/* 2 82C59 (irq) */
- s->isa_irqs_in = i8259_init(isabus, s->cpu_intr);
+ s->isa_irqs_in = i8259_init(isabus,
+ qemu_allocate_irq(i82378_request_out0_irq,
+ s, 0));
isa_bus_register_input_irqs(isabus, s->isa_irqs_in);
/* 1 82C54 (pit) */
diff --git a/hw/isa/trace-events b/hw/isa/trace-events
index c4567a9..1816e83 100644
--- a/hw/isa/trace-events
+++ b/hw/isa/trace-events
@@ -16,6 +16,7 @@
# vt82c686.c
via_isa_write(uint32_t addr, uint32_t val, int len) "addr 0x%x val 0x%x len 0x%x"
+via_pm_read(uint32_t addr, uint32_t val, int len) "addr 0x%x val 0x%x len 0x%x"
via_pm_write(uint32_t addr, uint32_t val, int len) "addr 0x%x val 0x%x len 0x%x"
via_pm_io_read(uint32_t addr, uint32_t val, int len) "addr 0x%x val 0x%x len 0x%x"
via_pm_io_write(uint32_t addr, uint32_t val, int len) "addr 0x%x val 0x%x len 0x%x"
diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c
index f4c4096..ca89119 100644
--- a/hw/isa/vt82c686.c
+++ b/hw/isa/vt82c686.c
@@ -554,7 +554,7 @@
PCIIDEState ide;
UHCIState uhci[2];
ViaPMState pm;
- PCIDevice ac97;
+ ViaAC97State ac97;
PCIDevice mc97;
};
@@ -598,15 +598,63 @@
qemu_set_irq(s->isa_irqs_in[n], level);
}
+static void via_isa_request_i8259_irq(void *opaque, int irq, int level)
+{
+ ViaISAState *s = opaque;
+ qemu_set_irq(s->cpu_intr, level);
+}
+
+static int via_isa_get_pci_irq(const ViaISAState *s, int irq_num)
+{
+ switch (irq_num) {
+ case 0:
+ return s->dev.config[0x55] >> 4;
+ case 1:
+ return s->dev.config[0x56] & 0xf;
+ case 2:
+ return s->dev.config[0x56] >> 4;
+ case 3:
+ return s->dev.config[0x57] >> 4;
+ }
+ return 0;
+}
+
+static void via_isa_set_pci_irq(void *opaque, int irq_num, int level)
+{
+ ViaISAState *s = opaque;
+ PCIBus *bus = pci_get_bus(&s->dev);
+ int i, pic_level, pic_irq = via_isa_get_pci_irq(s, irq_num);
+
+ /* IRQ 0: disabled, IRQ 2,8,13: reserved */
+ if (!pic_irq) {
+ return;
+ }
+ if (unlikely(pic_irq == 2 || pic_irq == 8 || pic_irq == 13)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Invalid ISA IRQ routing");
+ }
+
+ /* The pic level is the logical OR of all the PCI irqs mapped to it. */
+ pic_level = 0;
+ for (i = 0; i < PCI_NUM_PINS; i++) {
+ if (pic_irq == via_isa_get_pci_irq(s, i)) {
+ pic_level |= pci_bus_get_irq_level(bus, i);
+ }
+ }
+ /* Now we change the pic irq level according to the via irq mappings. */
+ qemu_set_irq(s->isa_irqs_in[pic_irq], pic_level);
+}
+
static void via_isa_realize(PCIDevice *d, Error **errp)
{
ViaISAState *s = VIA_ISA(d);
DeviceState *dev = DEVICE(d);
PCIBus *pci_bus = pci_get_bus(d);
+ qemu_irq *isa_irq;
ISABus *isa_bus;
int i;
qdev_init_gpio_out(dev, &s->cpu_intr, 1);
+ isa_irq = qemu_allocate_irqs(via_isa_request_i8259_irq, s, 1);
isa_bus = isa_bus_new(dev, pci_address_space(d), pci_address_space_io(d),
errp);
@@ -614,11 +662,13 @@
return;
}
- s->isa_irqs_in = i8259_init(isa_bus, s->cpu_intr);
+ s->isa_irqs_in = i8259_init(isa_bus, *isa_irq);
isa_bus_register_input_irqs(isa_bus, s->isa_irqs_in);
i8254_pit_init(isa_bus, 0x40, 0, NULL);
i8257_dma_init(isa_bus, 0);
+ qdev_init_gpio_in_named(dev, via_isa_set_pci_irq, "pirq", PCI_NUM_PINS);
+
/* RTC */
qdev_prop_set_int32(DEVICE(&s->rtc), "base_year", 2000);
if (!qdev_realize(DEVICE(&s->rtc), BUS(isa_bus), errp)) {
diff --git a/hw/mips/boston.c b/hw/mips/boston.c
index a9d87f3..21ad844 100644
--- a/hw/mips/boston.c
+++ b/hw/mips/boston.c
@@ -702,7 +702,7 @@
object_initialize_child(OBJECT(machine), "cps", &s->cps, TYPE_MIPS_CPS);
object_property_set_str(OBJECT(&s->cps), "cpu-type", machine->cpu_type,
&error_fatal);
- object_property_set_int(OBJECT(&s->cps), "num-vp", machine->smp.cpus,
+ object_property_set_uint(OBJECT(&s->cps), "num-vp", machine->smp.cpus,
&error_fatal);
qdev_connect_clock_in(DEVICE(&s->cps), "clk-in",
qdev_get_clock_out(dev, "cpu-refclk"));
diff --git a/hw/mips/cps.c b/hw/mips/cps.c
index 2b43670..2b5269e 100644
--- a/hw/mips/cps.c
+++ b/hw/mips/cps.c
@@ -66,20 +66,17 @@
static void mips_cps_realize(DeviceState *dev, Error **errp)
{
MIPSCPSState *s = MIPS_CPS(dev);
- CPUMIPSState *env;
- MIPSCPU *cpu;
- int i;
target_ulong gcr_base;
bool itu_present = false;
- bool saar_present = false;
if (!clock_get(s->clock)) {
error_setg(errp, "CPS input clock is not connected to an output clock");
return;
}
- for (i = 0; i < s->num_vp; i++) {
- cpu = MIPS_CPU(object_new(s->cpu_type));
+ for (int i = 0; i < s->num_vp; i++) {
+ MIPSCPU *cpu = MIPS_CPU(object_new(s->cpu_type));
+ CPUMIPSState *env = &cpu->env;
/* All VPs are halted on reset. Leave powering up to CPC. */
if (!object_property_set_bool(OBJECT(cpu), "start-powered-off", true,
@@ -97,7 +94,6 @@
cpu_mips_irq_init_cpu(cpu);
cpu_mips_clock_init(cpu);
- env = &cpu->env;
if (cpu_mips_itu_supported(env)) {
itu_present = true;
/* Attach ITC Tag to the VP */
@@ -107,22 +103,15 @@
qemu_register_reset(main_cpu_reset, cpu);
}
- cpu = MIPS_CPU(first_cpu);
- env = &cpu->env;
- saar_present = (bool)env->saarp;
-
/* Inter-Thread Communication Unit */
if (itu_present) {
object_initialize_child(OBJECT(dev), "itu", &s->itu, TYPE_MIPS_ITU);
- object_property_set_int(OBJECT(&s->itu), "num-fifo", 16,
+ object_property_set_link(OBJECT(&s->itu), "cpu[0]",
+ OBJECT(first_cpu), &error_abort);
+ object_property_set_uint(OBJECT(&s->itu), "num-fifo", 16,
&error_abort);
- object_property_set_int(OBJECT(&s->itu), "num-semaphores", 16,
+ object_property_set_uint(OBJECT(&s->itu), "num-semaphores", 16,
&error_abort);
- object_property_set_bool(OBJECT(&s->itu), "saar-present", saar_present,
- &error_abort);
- if (saar_present) {
- s->itu.saar = &env->CP0_SAAR;
- }
if (!sysbus_realize(SYS_BUS_DEVICE(&s->itu), errp)) {
return;
}
@@ -133,7 +122,7 @@
/* Cluster Power Controller */
object_initialize_child(OBJECT(dev), "cpc", &s->cpc, TYPE_MIPS_CPC);
- object_property_set_int(OBJECT(&s->cpc), "num-vp", s->num_vp,
+ object_property_set_uint(OBJECT(&s->cpc), "num-vp", s->num_vp,
&error_abort);
object_property_set_int(OBJECT(&s->cpc), "vp-start-running", 1,
&error_abort);
@@ -146,9 +135,9 @@
/* Global Interrupt Controller */
object_initialize_child(OBJECT(dev), "gic", &s->gic, TYPE_MIPS_GIC);
- object_property_set_int(OBJECT(&s->gic), "num-vp", s->num_vp,
+ object_property_set_uint(OBJECT(&s->gic), "num-vp", s->num_vp,
&error_abort);
- object_property_set_int(OBJECT(&s->gic), "num-irq", 128,
+ object_property_set_uint(OBJECT(&s->gic), "num-irq", 128,
&error_abort);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->gic), errp)) {
return;
@@ -158,10 +147,10 @@
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gic), 0));
/* Global Configuration Registers */
- gcr_base = env->CP0_CMGCRBase << 4;
+ gcr_base = MIPS_CPU(first_cpu)->env.CP0_CMGCRBase << 4;
object_initialize_child(OBJECT(dev), "gcr", &s->gcr, TYPE_MIPS_GCR);
- object_property_set_int(OBJECT(&s->gcr), "num-vp", s->num_vp,
+ object_property_set_uint(OBJECT(&s->gcr), "num-vp", s->num_vp,
&error_abort);
object_property_set_int(OBJECT(&s->gcr), "gcr-rev", 0x800,
&error_abort);
diff --git a/hw/mips/malta.c b/hw/mips/malta.c
index ec172b1..af90213 100644
--- a/hw/mips/malta.c
+++ b/hw/mips/malta.c
@@ -1066,7 +1066,7 @@
object_initialize_child(OBJECT(s), "cps", &s->cps, TYPE_MIPS_CPS);
object_property_set_str(OBJECT(&s->cps), "cpu-type", ms->cpu_type,
&error_fatal);
- object_property_set_int(OBJECT(&s->cps), "num-vp", ms->smp.cpus,
+ object_property_set_uint(OBJECT(&s->cps), "num-vp", ms->smp.cpus,
&error_fatal);
qdev_connect_clock_in(DEVICE(&s->cps), "clk-in", s->cpuclk);
sysbus_realize(SYS_BUS_DEVICE(&s->cps), &error_fatal);
diff --git a/hw/misc/edu.c b/hw/misc/edu.c
index e935c41..a1f8bc7 100644
--- a/hw/misc/edu.c
+++ b/hw/misc/edu.c
@@ -267,6 +267,8 @@
case 0x20:
if (val & EDU_STATUS_IRQFACT) {
qatomic_or(&edu->status, EDU_STATUS_IRQFACT);
+ /* Order check of the COMPUTING flag after setting IRQFACT. */
+ smp_mb__after_rmw();
} else {
qatomic_and(&edu->status, ~EDU_STATUS_IRQFACT);
}
@@ -349,6 +351,9 @@
qemu_mutex_unlock(&edu->thr_mutex);
qatomic_and(&edu->status, ~EDU_STATUS_COMPUTING);
+ /* Clear COMPUTING flag before checking IRQFACT. */
+ smp_mb__after_rmw();
+
if (qatomic_read(&edu->status) & EDU_STATUS_IRQFACT) {
qemu_mutex_lock_iothread();
edu_raise_irq(edu, FACT_IRQ);
diff --git a/hw/misc/mips_cmgcr.c b/hw/misc/mips_cmgcr.c
index 3c8b37f..66eb116 100644
--- a/hw/misc/mips_cmgcr.c
+++ b/hw/misc/mips_cmgcr.c
@@ -212,7 +212,7 @@
};
static Property mips_gcr_properties[] = {
- DEFINE_PROP_INT32("num-vp", MIPSGCRState, num_vps, 1),
+ DEFINE_PROP_UINT32("num-vp", MIPSGCRState, num_vps, 1),
DEFINE_PROP_INT32("gcr-rev", MIPSGCRState, gcr_rev, 0x800),
DEFINE_PROP_UINT64("gcr-base", MIPSGCRState, gcr_base, GCR_BASE_ADDR),
DEFINE_PROP_LINK("gic", MIPSGCRState, gic_mr, TYPE_MEMORY_REGION,
diff --git a/hw/misc/mips_itu.c b/hw/misc/mips_itu.c
index badef5c..0eda302 100644
--- a/hw/misc/mips_itu.c
+++ b/hw/misc/mips_itu.c
@@ -93,10 +93,10 @@
uint64_t size = (1 * KiB) + (am[1] & ITC_AM1_ADDR_MASK_MASK);
bool is_enabled = (am[0] & ITC_AM0_EN_MASK) != 0;
- if (tag->saar_present) {
- address = ((*(uint64_t *) tag->saar) & 0xFFFFFFFFE000ULL) << 4;
- size = 1ULL << ((*(uint64_t *) tag->saar >> 1) & 0x1f);
- is_enabled = *(uint64_t *) tag->saar & 1;
+ if (tag->saar) {
+ address = (tag->saar[0] & 0xFFFFFFFFE000ULL) << 4;
+ size = 1ULL << ((tag->saar[0] >> 1) & 0x1f);
+ is_enabled = tag->saar[0] & 1;
}
memory_region_transaction_begin();
@@ -157,7 +157,7 @@
static inline int get_cell_stride_shift(const MIPSITUState *s)
{
/* Minimum interval (for EntryGain = 0) is 128 B */
- if (s->saar_present) {
+ if (s->saar) {
return 7 + ((s->icr0 >> ITC_ICR0_BLK_GRAIN) &
ITC_ICR0_BLK_GRAIN_MASK);
} else {
@@ -515,6 +515,7 @@
static void mips_itu_realize(DeviceState *dev, Error **errp)
{
MIPSITUState *s = MIPS_ITU(dev);
+ CPUMIPSState *env;
if (s->num_fifo > ITC_FIFO_NUM_MAX) {
error_setg(errp, "Exceed maximum number of FIFO cells: %d",
@@ -526,6 +527,15 @@
s->num_semaphores);
return;
}
+ if (!s->cpu0) {
+ error_setg(errp, "Missing 'cpu[0]' property");
+ return;
+ }
+
+ env = &s->cpu0->env;
+ if (env->saarp) {
+ s->saar = env->CP0_SAAR;
+ }
s->cell = g_new(ITCStorageCell, get_num_cells(s));
}
@@ -534,8 +544,8 @@
{
MIPSITUState *s = MIPS_ITU(dev);
- if (s->saar_present) {
- *(uint64_t *) s->saar = 0x11 << 1;
+ if (s->saar) {
+ s->saar[0] = 0x11 << 1;
s->icr0 = get_num_cells(s) << ITC_ICR0_CELL_NUM;
} else {
s->ITCAddressMap[0] = 0;
@@ -549,11 +559,11 @@
}
static Property mips_itu_properties[] = {
- DEFINE_PROP_INT32("num-fifo", MIPSITUState, num_fifo,
+ DEFINE_PROP_UINT32("num-fifo", MIPSITUState, num_fifo,
ITC_FIFO_NUM_MAX),
- DEFINE_PROP_INT32("num-semaphores", MIPSITUState, num_semaphores,
+ DEFINE_PROP_UINT32("num-semaphores", MIPSITUState, num_semaphores,
ITC_SEMAPH_NUM_MAX),
- DEFINE_PROP_BOOL("saar-present", MIPSITUState, saar_present, false),
+ DEFINE_PROP_LINK("cpu[0]", MIPSITUState, cpu0, TYPE_MIPS_CPU, MIPSCPU *),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c
index 7d92c2d..9bbf659 100644
--- a/hw/net/xen_nic.c
+++ b/hw/net/xen_nic.c
@@ -145,7 +145,7 @@
continue;
}
- if ((txreq.offset + txreq.size) > XC_PAGE_SIZE) {
+ if ((txreq.offset + txreq.size) > XEN_PAGE_SIZE) {
xen_pv_printf(&netdev->xendev, 0, "error: page crossing\n");
net_tx_error(netdev, &txreq, rc);
continue;
@@ -171,7 +171,7 @@
if (txreq.flags & NETTXF_csum_blank) {
/* have read-only mapping -> can't fill checksum in-place */
if (!tmpbuf) {
- tmpbuf = g_malloc(XC_PAGE_SIZE);
+ tmpbuf = g_malloc(XEN_PAGE_SIZE);
}
memcpy(tmpbuf, page + txreq.offset, txreq.size);
net_checksum_calculate(tmpbuf, txreq.size, CSUM_ALL);
@@ -181,7 +181,7 @@
qemu_send_packet(qemu_get_queue(netdev->nic),
page + txreq.offset, txreq.size);
}
- xen_be_unmap_grant_ref(&netdev->xendev, page);
+ xen_be_unmap_grant_ref(&netdev->xendev, page, txreq.gref);
net_tx_response(netdev, &txreq, NETIF_RSP_OKAY);
}
if (!netdev->tx_work) {
@@ -243,9 +243,9 @@
if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) {
return 0;
}
- if (size > XC_PAGE_SIZE - NET_IP_ALIGN) {
+ if (size > XEN_PAGE_SIZE - NET_IP_ALIGN) {
xen_pv_printf(&netdev->xendev, 0, "packet too big (%lu > %ld)",
- (unsigned long)size, XC_PAGE_SIZE - NET_IP_ALIGN);
+ (unsigned long)size, XEN_PAGE_SIZE - NET_IP_ALIGN);
return -1;
}
@@ -261,7 +261,7 @@
return -1;
}
memcpy(page + NET_IP_ALIGN, buf, size);
- xen_be_unmap_grant_ref(&netdev->xendev, page);
+ xen_be_unmap_grant_ref(&netdev->xendev, page, rxreq.gref);
net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0);
return size;
@@ -343,12 +343,13 @@
netdev->rx_ring_ref,
PROT_READ | PROT_WRITE);
if (!netdev->rxs) {
- xen_be_unmap_grant_ref(&netdev->xendev, netdev->txs);
+ xen_be_unmap_grant_ref(&netdev->xendev, netdev->txs,
+ netdev->tx_ring_ref);
netdev->txs = NULL;
return -1;
}
- BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XC_PAGE_SIZE);
- BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XC_PAGE_SIZE);
+ BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XEN_PAGE_SIZE);
+ BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XEN_PAGE_SIZE);
xen_be_bind_evtchn(&netdev->xendev);
@@ -368,11 +369,13 @@
xen_pv_unbind_evtchn(&netdev->xendev);
if (netdev->txs) {
- xen_be_unmap_grant_ref(&netdev->xendev, netdev->txs);
+ xen_be_unmap_grant_ref(&netdev->xendev, netdev->txs,
+ netdev->tx_ring_ref);
netdev->txs = NULL;
}
if (netdev->rxs) {
- xen_be_unmap_grant_ref(&netdev->xendev, netdev->rxs);
+ xen_be_unmap_grant_ref(&netdev->xendev, netdev->rxs,
+ netdev->rx_ring_ref);
netdev->rxs = NULL;
}
}
diff --git a/hw/pci-host/mv64361.c b/hw/pci-host/mv64361.c
index 298564f..19e8031 100644
--- a/hw/pci-host/mv64361.c
+++ b/hw/pci-host/mv64361.c
@@ -873,10 +873,6 @@
}
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->cpu_irq);
qdev_init_gpio_in_named(dev, mv64361_gpp_irq, "gpp", 32);
- /* FIXME: PCI IRQ connections may be board specific */
- for (i = 0; i < PCI_NUM_PINS; i++) {
- s->pci[1].irq[i] = qdev_get_gpio_in_named(dev, "gpp", 12 + i);
- }
}
static void mv64361_reset(DeviceState *dev)
diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c
index 7cc375d..f1650be 100644
--- a/hw/ppc/pegasos2.c
+++ b/hw/ppc/pegasos2.c
@@ -73,6 +73,8 @@
MachineState parent_obj;
PowerPCCPU *cpu;
DeviceState *mv;
+ qemu_irq mv_pirq[PCI_NUM_PINS];
+ qemu_irq via_pirq[PCI_NUM_PINS];
Vof *vof;
void *fdt_blob;
uint64_t kernel_addr;
@@ -95,6 +97,15 @@
}
}
+static void pegasos2_pci_irq(void *opaque, int n, int level)
+{
+ Pegasos2MachineState *pm = opaque;
+
+ /* PCI interrupt lines are connected to both MV64361 and VT8231 */
+ qemu_set_irq(pm->mv_pirq[n], level);
+ qemu_set_irq(pm->via_pirq[n], level);
+}
+
static void pegasos2_init(MachineState *machine)
{
Pegasos2MachineState *pm = PEGASOS2_MACHINE(machine);
@@ -106,7 +117,7 @@
I2CBus *i2c_bus;
const char *fwname = machine->firmware ?: PROM_FILENAME;
char *filename;
- int sz;
+ int i, sz;
uint8_t *spd_data;
/* init CPU */
@@ -156,11 +167,18 @@
/* Marvell Discovery II system controller */
pm->mv = DEVICE(sysbus_create_simple(TYPE_MV64361, -1,
qdev_get_gpio_in(DEVICE(pm->cpu), PPC6xx_INPUT_INT)));
+ for (i = 0; i < PCI_NUM_PINS; i++) {
+ pm->mv_pirq[i] = qdev_get_gpio_in_named(pm->mv, "gpp", 12 + i);
+ }
pci_bus = mv64361_get_pci_bus(pm->mv, 1);
+ pci_bus_irqs(pci_bus, pegasos2_pci_irq, pm, PCI_NUM_PINS);
/* VIA VT8231 South Bridge (multifunction PCI device) */
via = OBJECT(pci_create_simple_multifunction(pci_bus, PCI_DEVFN(12, 0),
true, TYPE_VT8231_ISA));
+ for (i = 0; i < PCI_NUM_PINS; i++) {
+ pm->via_pirq[i] = qdev_get_gpio_in_named(DEVICE(via), "pirq", i);
+ }
object_property_add_alias(OBJECT(machine), "rtc-time",
object_resolve_path_component(via, "rtc"),
"date");
@@ -267,6 +285,12 @@
PCI_INTERRUPT_LINE, 2, 0x9);
pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) |
0x50, 1, 0x2);
+ pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) |
+ 0x55, 1, 0x90);
+ pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) |
+ 0x56, 1, 0x99);
+ pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) |
+ 0x57, 1, 0x90);
pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) |
PCI_INTERRUPT_LINE, 2, 0x109);
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index 925ff52..ec4def6 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -8,6 +8,7 @@
#include "qemu/module.h"
#include "qemu/error-report.h"
#include "exec/exec-all.h"
+#include "exec/tb-flush.h"
#include "helper_regs.h"
#include "hw/ppc/ppc.h"
#include "hw/ppc/spapr.h"
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
index 6f8b543..88d2b4b 100644
--- a/hw/usb/hcd-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -1410,6 +1410,18 @@
}
}
+/* This is the one state transition the controller can do by itself */
+static bool ohci_resume(OHCIState *s)
+{
+ if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) {
+ trace_usb_ohci_remote_wakeup(s->name);
+ s->ctl &= ~OHCI_CTL_HCFS;
+ s->ctl |= OHCI_USB_RESUME;
+ return true;
+ }
+ return false;
+}
+
/*
* Sets a flag in a port status reg but only set it if the port is connected.
* If not set ConnectStatusChange flag. If flag is enabled return 1.
@@ -1426,7 +1438,10 @@
if (!(ohci->rhport[i].ctrl & OHCI_PORT_CCS)) {
ohci->rhport[i].ctrl |= OHCI_PORT_CSC;
if (ohci->rhstatus & OHCI_RHS_DRWE) {
- /* TODO: CSC is a wakeup event */
+ /* CSC is a wakeup event */
+ if (ohci_resume(ohci)) {
+ ohci_set_interrupt(ohci, OHCI_INTR_RD);
+ }
}
return 0;
}
@@ -1828,11 +1843,7 @@
intr = OHCI_INTR_RHSC;
}
/* Note that the controller can be suspended even if this port is not */
- if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) {
- trace_usb_ohci_remote_wakeup(s->name);
- /* This is the one state transition the controller can do by itself */
- s->ctl &= ~OHCI_CTL_HCFS;
- s->ctl |= OHCI_USB_RESUME;
+ if (ohci_resume(s)) {
/*
* In suspend mode only ResumeDetected is possible, not RHSC:
* see the OHCI spec 5.1.2.3.
diff --git a/hw/usb/meson.build b/hw/usb/meson.build
index bdf34cb..599dc24 100644
--- a/hw/usb/meson.build
+++ b/hw/usb/meson.build
@@ -84,6 +84,6 @@
hw_usb_modules += {'host': usbhost_ss}
endif
-softmmu_ss.add(when: ['CONFIG_USB', 'CONFIG_XEN', libusb], if_true: files('xen-usb.c'))
+softmmu_ss.add(when: ['CONFIG_USB', 'CONFIG_XEN_BUS', libusb], if_true: files('xen-usb.c'))
modules += { 'hw-usb': hw_usb_modules }
diff --git a/hw/usb/vt82c686-uhci-pci.c b/hw/usb/vt82c686-uhci-pci.c
index 46a901f..b4884c9 100644
--- a/hw/usb/vt82c686-uhci-pci.c
+++ b/hw/usb/vt82c686-uhci-pci.c
@@ -1,17 +1,7 @@
#include "qemu/osdep.h"
-#include "hw/irq.h"
#include "hw/isa/vt82c686.h"
#include "hcd-uhci.h"
-static void uhci_isa_set_irq(void *opaque, int irq_num, int level)
-{
- UHCIState *s = opaque;
- uint8_t irq = pci_get_byte(s->dev.config + PCI_INTERRUPT_LINE);
- if (irq > 0 && irq < 15) {
- via_isa_set_irq(pci_get_function_0(&s->dev), irq, level);
- }
-}
-
static void usb_uhci_vt82c686b_realize(PCIDevice *dev, Error **errp)
{
UHCIState *s = UHCI(dev);
@@ -25,8 +15,6 @@
pci_set_long(pci_conf + 0xc0, 0x00002000);
usb_uhci_common_realize(dev, errp);
- object_unref(s->irq);
- s->irq = qemu_allocate_irq(uhci_isa_set_irq, s, 0);
}
static UHCIInfo uhci_info[] = {
diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c
index 0f7369e..66cb3f7 100644
--- a/hw/usb/xen-usb.c
+++ b/hw/usb/xen-usb.c
@@ -101,6 +101,8 @@
struct usbback_info {
struct XenLegacyDevice xendev; /* must be first */
USBBus bus;
+ uint32_t urb_ring_ref;
+ uint32_t conn_ring_ref;
void *urb_sring;
void *conn_sring;
struct usbif_urb_back_ring urb_ring;
@@ -159,7 +161,7 @@
for (i = 0; i < nr_segs; i++) {
if ((unsigned)usbback_req->req.seg[i].offset +
- (unsigned)usbback_req->req.seg[i].length > XC_PAGE_SIZE) {
+ (unsigned)usbback_req->req.seg[i].length > XEN_PAGE_SIZE) {
xen_pv_printf(xendev, 0, "segment crosses page boundary\n");
return -EINVAL;
}
@@ -183,7 +185,7 @@
for (i = 0; i < usbback_req->nr_buffer_segs; i++) {
seg = usbback_req->req.seg + i;
- addr = usbback_req->buffer + i * XC_PAGE_SIZE + seg->offset;
+ addr = usbback_req->buffer + i * XEN_PAGE_SIZE + seg->offset;
qemu_iovec_add(&usbback_req->packet.iov, addr, seg->length);
}
}
@@ -277,10 +279,11 @@
static void usbback_do_response(struct usbback_req *usbback_req, int32_t status,
int32_t actual_length, int32_t error_count)
{
+ uint32_t ref[USBIF_MAX_SEGMENTS_PER_REQUEST];
struct usbback_info *usbif;
struct usbif_urb_response *res;
struct XenLegacyDevice *xendev;
- unsigned int notify;
+ unsigned int notify, i;
usbif = usbback_req->usbif;
xendev = &usbif->xendev;
@@ -293,13 +296,19 @@
}
if (usbback_req->buffer) {
- xen_be_unmap_grant_refs(xendev, usbback_req->buffer,
+ for (i = 0; i < usbback_req->nr_buffer_segs; i++) {
+ ref[i] = usbback_req->req.seg[i].gref;
+ }
+ xen_be_unmap_grant_refs(xendev, usbback_req->buffer, ref,
usbback_req->nr_buffer_segs);
usbback_req->buffer = NULL;
}
if (usbback_req->isoc_buffer) {
- xen_be_unmap_grant_refs(xendev, usbback_req->isoc_buffer,
+ for (i = 0; i < usbback_req->nr_extra_segs; i++) {
+ ref[i] = usbback_req->req.seg[i + usbback_req->req.nr_buffer_segs].gref;
+ }
+ xen_be_unmap_grant_refs(xendev, usbback_req->isoc_buffer, ref,
usbback_req->nr_extra_segs);
usbback_req->isoc_buffer = NULL;
}
@@ -832,11 +841,11 @@
xen_pv_unbind_evtchn(xendev);
if (usbif->urb_sring) {
- xen_be_unmap_grant_ref(xendev, usbif->urb_sring);
+ xen_be_unmap_grant_ref(xendev, usbif->urb_sring, usbif->urb_ring_ref);
usbif->urb_sring = NULL;
}
if (usbif->conn_sring) {
- xen_be_unmap_grant_ref(xendev, usbif->conn_sring);
+ xen_be_unmap_grant_ref(xendev, usbif->conn_sring, usbif->conn_ring_ref);
usbif->conn_sring = NULL;
}
@@ -889,10 +898,12 @@
return -1;
}
+ usbif->urb_ring_ref = urb_ring_ref;
+ usbif->conn_ring_ref = conn_ring_ref;
urb_sring = usbif->urb_sring;
conn_sring = usbif->conn_sring;
- BACK_RING_INIT(&usbif->urb_ring, urb_sring, XC_PAGE_SIZE);
- BACK_RING_INIT(&usbif->conn_ring, conn_sring, XC_PAGE_SIZE);
+ BACK_RING_INIT(&usbif->urb_ring, urb_sring, XEN_PAGE_SIZE);
+ BACK_RING_INIT(&usbif->conn_ring, conn_sring, XEN_PAGE_SIZE);
xen_be_bind_evtchn(xendev);
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index bab83c0..4d01ea3 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -42,6 +42,7 @@
#include "migration/migration.h"
#include "migration/misc.h"
#include "migration/blocker.h"
+#include "migration/qemu-file.h"
#include "sysemu/tpm.h"
VFIOGroupList vfio_group_list =
@@ -319,6 +320,28 @@
* Device state interfaces
*/
+typedef struct {
+ unsigned long *bitmap;
+ hwaddr size;
+ hwaddr pages;
+} VFIOBitmap;
+
+static int vfio_bitmap_alloc(VFIOBitmap *vbmap, hwaddr size)
+{
+ vbmap->pages = REAL_HOST_PAGE_ALIGN(size) / qemu_real_host_page_size();
+ vbmap->size = ROUND_UP(vbmap->pages, sizeof(__u64) * BITS_PER_BYTE) /
+ BITS_PER_BYTE;
+ vbmap->bitmap = g_try_malloc0(vbmap->size);
+ if (!vbmap->bitmap) {
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int vfio_get_dirty_bitmap(VFIOContainer *container, uint64_t iova,
+ uint64_t size, ram_addr_t ram_addr);
+
bool vfio_mig_active(void)
{
VFIOGroup *group;
@@ -339,6 +362,7 @@
}
static Error *multiple_devices_migration_blocker;
+static Error *giommu_migration_blocker;
static unsigned int vfio_migratable_device_num(void)
{
@@ -390,6 +414,64 @@
multiple_devices_migration_blocker = NULL;
}
+static bool vfio_viommu_preset(void)
+{
+ VFIOAddressSpace *space;
+
+ QLIST_FOREACH(space, &vfio_address_spaces, list) {
+ if (space->as != &address_space_memory) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int vfio_block_giommu_migration(Error **errp)
+{
+ int ret;
+
+ if (giommu_migration_blocker ||
+ !vfio_viommu_preset()) {
+ return 0;
+ }
+
+ error_setg(&giommu_migration_blocker,
+ "Migration is currently not supported with vIOMMU enabled");
+ ret = migrate_add_blocker(giommu_migration_blocker, errp);
+ if (ret < 0) {
+ error_free(giommu_migration_blocker);
+ giommu_migration_blocker = NULL;
+ }
+
+ return ret;
+}
+
+void vfio_migration_finalize(void)
+{
+ if (!giommu_migration_blocker ||
+ vfio_viommu_preset()) {
+ return;
+ }
+
+ migrate_del_blocker(giommu_migration_blocker);
+ error_free(giommu_migration_blocker);
+ giommu_migration_blocker = NULL;
+}
+
+static void vfio_set_migration_error(int err)
+{
+ MigrationState *ms = migrate_get_current();
+
+ if (migration_is_setup_or_active(ms->state)) {
+ WITH_QEMU_LOCK_GUARD(&ms->qemu_file_lock) {
+ if (ms->to_dst_file) {
+ qemu_file_set_error(ms->to_dst_file, err);
+ }
+ }
+ }
+}
+
static bool vfio_devices_all_dirty_tracking(VFIOContainer *container)
{
VFIOGroup *group;
@@ -417,6 +499,22 @@
return true;
}
+static bool vfio_devices_all_device_dirty_tracking(VFIOContainer *container)
+{
+ VFIOGroup *group;
+ VFIODevice *vbasedev;
+
+ QLIST_FOREACH(group, &container->group_list, container_next) {
+ QLIST_FOREACH(vbasedev, &group->device_list, next) {
+ if (!vbasedev->dirty_pages_supported) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
/*
* Check if all VFIO devices are running and migration is active, which is
* essentially equivalent to the migration being in pre-copy phase.
@@ -454,9 +552,14 @@
{
struct vfio_iommu_type1_dma_unmap *unmap;
struct vfio_bitmap *bitmap;
- uint64_t pages = REAL_HOST_PAGE_ALIGN(size) / qemu_real_host_page_size();
+ VFIOBitmap vbmap;
int ret;
+ ret = vfio_bitmap_alloc(&vbmap, size);
+ if (ret) {
+ return ret;
+ }
+
unmap = g_malloc0(sizeof(*unmap) + sizeof(*bitmap));
unmap->argsz = sizeof(*unmap) + sizeof(*bitmap);
@@ -470,35 +573,28 @@
* qemu_real_host_page_size to mark those dirty. Hence set bitmap_pgsize
* to qemu_real_host_page_size.
*/
-
bitmap->pgsize = qemu_real_host_page_size();
- bitmap->size = ROUND_UP(pages, sizeof(__u64) * BITS_PER_BYTE) /
- BITS_PER_BYTE;
+ bitmap->size = vbmap.size;
+ bitmap->data = (__u64 *)vbmap.bitmap;
- if (bitmap->size > container->max_dirty_bitmap_size) {
- error_report("UNMAP: Size of bitmap too big 0x%"PRIx64,
- (uint64_t)bitmap->size);
+ if (vbmap.size > container->max_dirty_bitmap_size) {
+ error_report("UNMAP: Size of bitmap too big 0x%"PRIx64, vbmap.size);
ret = -E2BIG;
goto unmap_exit;
}
- bitmap->data = g_try_malloc0(bitmap->size);
- if (!bitmap->data) {
- ret = -ENOMEM;
- goto unmap_exit;
- }
-
ret = ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, unmap);
if (!ret) {
- cpu_physical_memory_set_dirty_lebitmap((unsigned long *)bitmap->data,
- iotlb->translated_addr, pages);
+ cpu_physical_memory_set_dirty_lebitmap(vbmap.bitmap,
+ iotlb->translated_addr, vbmap.pages);
} else {
error_report("VFIO_UNMAP_DMA with DIRTY_BITMAP : %m");
}
- g_free(bitmap->data);
unmap_exit:
g_free(unmap);
+ g_free(vbmap.bitmap);
+
return ret;
}
@@ -515,10 +611,16 @@
.iova = iova,
.size = size,
};
+ bool need_dirty_sync = false;
+ int ret;
- if (iotlb && container->dirty_pages_supported &&
- vfio_devices_all_running_and_mig_active(container)) {
- return vfio_dma_unmap_bitmap(container, iova, size, iotlb);
+ if (iotlb && vfio_devices_all_running_and_mig_active(container)) {
+ if (!vfio_devices_all_device_dirty_tracking(container) &&
+ container->dirty_pages_supported) {
+ return vfio_dma_unmap_bitmap(container, iova, size, iotlb);
+ }
+
+ need_dirty_sync = true;
}
while (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) {
@@ -544,10 +646,12 @@
return -errno;
}
- if (iotlb && vfio_devices_all_running_and_mig_active(container)) {
- cpu_physical_memory_set_dirty_range(iotlb->translated_addr, size,
- tcg_enabled() ? DIRTY_CLIENTS_ALL :
- DIRTY_CLIENTS_NOCODE);
+ if (need_dirty_sync) {
+ ret = vfio_get_dirty_bitmap(container, iova, size,
+ iotlb->translated_addr);
+ if (ret) {
+ return ret;
+ }
}
return 0;
@@ -680,6 +784,7 @@
if (iotlb->target_as != &address_space_memory) {
error_report("Wrong target AS \"%s\", only system memory is allowed",
iotlb->target_as->name ? iotlb->target_as->name : "none");
+ vfio_set_migration_error(-EINVAL);
return;
}
@@ -703,17 +808,18 @@
read_only);
if (ret) {
error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", "
- "0x%"HWADDR_PRIx", %p) = %d (%m)",
+ "0x%"HWADDR_PRIx", %p) = %d (%s)",
container, iova,
- iotlb->addr_mask + 1, vaddr, ret);
+ iotlb->addr_mask + 1, vaddr, ret, strerror(-ret));
}
} else {
ret = vfio_dma_unmap(container, iova, iotlb->addr_mask + 1, iotlb);
if (ret) {
error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", "
- "0x%"HWADDR_PRIx") = %d (%m)",
+ "0x%"HWADDR_PRIx") = %d (%s)",
container, iova,
- iotlb->addr_mask + 1, ret);
+ iotlb->addr_mask + 1, ret, strerror(-ret));
+ vfio_set_migration_error(ret);
}
}
out:
@@ -868,6 +974,22 @@
g_free(vrdl);
}
+static VFIOHostDMAWindow *vfio_find_hostwin(VFIOContainer *container,
+ hwaddr iova, hwaddr end)
+{
+ VFIOHostDMAWindow *hostwin;
+ bool hostwin_found = false;
+
+ QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) {
+ if (hostwin->min_iova <= iova && end <= hostwin->max_iova) {
+ hostwin_found = true;
+ break;
+ }
+ }
+
+ return hostwin_found ? hostwin : NULL;
+}
+
static bool vfio_known_safe_misalignment(MemoryRegionSection *section)
{
MemoryRegion *mr = section->mr;
@@ -884,24 +1006,15 @@
return true;
}
-static void vfio_listener_region_add(MemoryListener *listener,
- MemoryRegionSection *section)
+static bool vfio_listener_valid_section(MemoryRegionSection *section,
+ const char *name)
{
- VFIOContainer *container = container_of(listener, VFIOContainer, listener);
- hwaddr iova, end;
- Int128 llend, llsize;
- void *vaddr;
- int ret;
- VFIOHostDMAWindow *hostwin;
- bool hostwin_found;
- Error *err = NULL;
-
if (vfio_listener_skipped_section(section)) {
- trace_vfio_listener_region_add_skip(
+ trace_vfio_listener_region_skip(name,
section->offset_within_address_space,
section->offset_within_address_space +
int128_get64(int128_sub(section->size, int128_one())));
- return;
+ return false;
}
if (unlikely((section->offset_within_address_space &
@@ -916,15 +1029,53 @@
section->offset_within_region,
qemu_real_host_page_size());
}
- return;
+ return false;
}
+ return true;
+}
+
+static bool vfio_get_section_iova_range(VFIOContainer *container,
+ MemoryRegionSection *section,
+ hwaddr *out_iova, hwaddr *out_end,
+ Int128 *out_llend)
+{
+ Int128 llend;
+ hwaddr iova;
+
iova = REAL_HOST_PAGE_ALIGN(section->offset_within_address_space);
llend = int128_make64(section->offset_within_address_space);
llend = int128_add(llend, section->size);
llend = int128_and(llend, int128_exts64(qemu_real_host_page_mask()));
if (int128_ge(int128_make64(iova), llend)) {
+ return false;
+ }
+
+ *out_iova = iova;
+ *out_end = int128_get64(int128_sub(llend, int128_one()));
+ if (out_llend) {
+ *out_llend = llend;
+ }
+ return true;
+}
+
+static void vfio_listener_region_add(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ VFIOContainer *container = container_of(listener, VFIOContainer, listener);
+ hwaddr iova, end;
+ Int128 llend, llsize;
+ void *vaddr;
+ int ret;
+ VFIOHostDMAWindow *hostwin;
+ Error *err = NULL;
+
+ if (!vfio_listener_valid_section(section, "region_add")) {
+ return;
+ }
+
+ if (!vfio_get_section_iova_range(container, section, &iova, &end, &llend)) {
if (memory_region_is_ram_device(section->mr)) {
trace_vfio_listener_region_add_no_dma_map(
memory_region_name(section->mr),
@@ -934,7 +1085,6 @@
}
return;
}
- end = int128_get64(int128_sub(llend, int128_one()));
if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) {
hwaddr pgsize = 0;
@@ -994,15 +1144,8 @@
#endif
}
- hostwin_found = false;
- QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) {
- if (hostwin->min_iova <= iova && end <= hostwin->max_iova) {
- hostwin_found = true;
- break;
- }
- }
-
- if (!hostwin_found) {
+ hostwin = vfio_find_hostwin(container, iova, end);
+ if (!hostwin) {
error_setg(&err, "Container %p can't map guest IOVA region"
" 0x%"HWADDR_PRIx"..0x%"HWADDR_PRIx, container, iova, end);
goto fail;
@@ -1095,8 +1238,9 @@
vaddr, section->readonly);
if (ret) {
error_setg(&err, "vfio_dma_map(%p, 0x%"HWADDR_PRIx", "
- "0x%"HWADDR_PRIx", %p) = %d (%m)",
- container, iova, int128_get64(llsize), vaddr, ret);
+ "0x%"HWADDR_PRIx", %p) = %d (%s)",
+ container, iova, int128_get64(llsize), vaddr, ret,
+ strerror(-ret));
if (memory_region_is_ram_device(section->mr)) {
/* Allow unexpected mappings not to be fatal for RAM devices */
error_report_err(err);
@@ -1140,26 +1284,7 @@
int ret;
bool try_unmap = true;
- if (vfio_listener_skipped_section(section)) {
- trace_vfio_listener_region_del_skip(
- section->offset_within_address_space,
- section->offset_within_address_space +
- int128_get64(int128_sub(section->size, int128_one())));
- return;
- }
-
- if (unlikely((section->offset_within_address_space &
- ~qemu_real_host_page_mask()) !=
- (section->offset_within_region & ~qemu_real_host_page_mask()))) {
- if (!vfio_known_safe_misalignment(section)) {
- error_report("%s received unaligned region %s iova=0x%"PRIx64
- " offset_within_region=0x%"PRIx64
- " qemu_real_host_page_size=0x%"PRIxPTR,
- __func__, memory_region_name(section->mr),
- section->offset_within_address_space,
- section->offset_within_region,
- qemu_real_host_page_size());
- }
+ if (!vfio_listener_valid_section(section, "region_del")) {
return;
}
@@ -1186,15 +1311,9 @@
*/
}
- iova = REAL_HOST_PAGE_ALIGN(section->offset_within_address_space);
- llend = int128_make64(section->offset_within_address_space);
- llend = int128_add(llend, section->size);
- llend = int128_and(llend, int128_exts64(qemu_real_host_page_mask()));
-
- if (int128_ge(int128_make64(iova), llend)) {
+ if (!vfio_get_section_iova_range(container, section, &iova, &end, &llend)) {
return;
}
- end = int128_get64(int128_sub(llend, int128_one()));
llsize = int128_sub(llend, int128_make64(iova));
@@ -1203,15 +1322,9 @@
if (memory_region_is_ram_device(section->mr)) {
hwaddr pgmask;
VFIOHostDMAWindow *hostwin;
- bool hostwin_found = false;
- QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) {
- if (hostwin->min_iova <= iova && end <= hostwin->max_iova) {
- hostwin_found = true;
- break;
- }
- }
- assert(hostwin_found); /* or region_add() would have failed */
+ hostwin = vfio_find_hostwin(container, iova, end);
+ assert(hostwin); /* or region_add() would have failed */
pgmask = (1ULL << ctz64(hostwin->iova_pgsizes)) - 1;
try_unmap = !((iova & pgmask) || (int128_get64(llsize) & pgmask));
@@ -1228,16 +1341,18 @@
ret = vfio_dma_unmap(container, iova, int128_get64(llsize), NULL);
if (ret) {
error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", "
- "0x%"HWADDR_PRIx") = %d (%m)",
- container, iova, int128_get64(llsize), ret);
+ "0x%"HWADDR_PRIx") = %d (%s)",
+ container, iova, int128_get64(llsize), ret,
+ strerror(-ret));
}
iova += int128_get64(llsize);
}
ret = vfio_dma_unmap(container, iova, int128_get64(llsize), NULL);
if (ret) {
error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", "
- "0x%"HWADDR_PRIx") = %d (%m)",
- container, iova, int128_get64(llsize), ret);
+ "0x%"HWADDR_PRIx") = %d (%s)",
+ container, iova, int128_get64(llsize), ret,
+ strerror(-ret));
}
}
@@ -1256,7 +1371,7 @@
}
}
-static void vfio_set_dirty_page_tracking(VFIOContainer *container, bool start)
+static int vfio_set_dirty_page_tracking(VFIOContainer *container, bool start)
{
int ret;
struct vfio_iommu_type1_dirty_bitmap dirty = {
@@ -1264,7 +1379,7 @@
};
if (!container->dirty_pages_supported) {
- return;
+ return 0;
}
if (start) {
@@ -1275,40 +1390,327 @@
ret = ioctl(container->fd, VFIO_IOMMU_DIRTY_PAGES, &dirty);
if (ret) {
+ ret = -errno;
error_report("Failed to set dirty tracking flag 0x%x errno: %d",
dirty.flags, errno);
}
+
+ return ret;
+}
+
+typedef struct VFIODirtyRanges {
+ hwaddr min32;
+ hwaddr max32;
+ hwaddr min64;
+ hwaddr max64;
+} VFIODirtyRanges;
+
+typedef struct VFIODirtyRangesListener {
+ VFIOContainer *container;
+ VFIODirtyRanges ranges;
+ MemoryListener listener;
+} VFIODirtyRangesListener;
+
+static void vfio_dirty_tracking_update(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ VFIODirtyRangesListener *dirty = container_of(listener,
+ VFIODirtyRangesListener,
+ listener);
+ VFIODirtyRanges *range = &dirty->ranges;
+ hwaddr iova, end, *min, *max;
+
+ if (!vfio_listener_valid_section(section, "tracking_update") ||
+ !vfio_get_section_iova_range(dirty->container, section,
+ &iova, &end, NULL)) {
+ return;
+ }
+
+ /*
+ * The address space passed to the dirty tracker is reduced to two ranges:
+ * one for 32-bit DMA ranges, and another one for 64-bit DMA ranges.
+ * The underlying reports of dirty will query a sub-interval of each of
+ * these ranges.
+ *
+ * The purpose of the dual range handling is to handle known cases of big
+ * holes in the address space, like the x86 AMD 1T hole. The alternative
+ * would be an IOVATree but that has a much bigger runtime overhead and
+ * unnecessary complexity.
+ */
+ min = (end <= UINT32_MAX) ? &range->min32 : &range->min64;
+ max = (end <= UINT32_MAX) ? &range->max32 : &range->max64;
+
+ if (*min > iova) {
+ *min = iova;
+ }
+ if (*max < end) {
+ *max = end;
+ }
+
+ trace_vfio_device_dirty_tracking_update(iova, end, *min, *max);
+ return;
+}
+
+static const MemoryListener vfio_dirty_tracking_listener = {
+ .name = "vfio-tracking",
+ .region_add = vfio_dirty_tracking_update,
+};
+
+static void vfio_dirty_tracking_init(VFIOContainer *container,
+ VFIODirtyRanges *ranges)
+{
+ VFIODirtyRangesListener dirty;
+
+ memset(&dirty, 0, sizeof(dirty));
+ dirty.ranges.min32 = UINT32_MAX;
+ dirty.ranges.min64 = UINT64_MAX;
+ dirty.listener = vfio_dirty_tracking_listener;
+ dirty.container = container;
+
+ memory_listener_register(&dirty.listener,
+ container->space->as);
+
+ *ranges = dirty.ranges;
+
+ /*
+ * The memory listener is synchronous, and used to calculate the range
+ * to dirty tracking. Unregister it after we are done as we are not
+ * interested in any follow-up updates.
+ */
+ memory_listener_unregister(&dirty.listener);
+}
+
+static void vfio_devices_dma_logging_stop(VFIOContainer *container)
+{
+ uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature),
+ sizeof(uint64_t))] = {};
+ struct vfio_device_feature *feature = (struct vfio_device_feature *)buf;
+ VFIODevice *vbasedev;
+ VFIOGroup *group;
+
+ feature->argsz = sizeof(buf);
+ feature->flags = VFIO_DEVICE_FEATURE_SET |
+ VFIO_DEVICE_FEATURE_DMA_LOGGING_STOP;
+
+ QLIST_FOREACH(group, &container->group_list, container_next) {
+ QLIST_FOREACH(vbasedev, &group->device_list, next) {
+ if (!vbasedev->dirty_tracking) {
+ continue;
+ }
+
+ if (ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature)) {
+ warn_report("%s: Failed to stop DMA logging, err %d (%s)",
+ vbasedev->name, -errno, strerror(errno));
+ }
+ vbasedev->dirty_tracking = false;
+ }
+ }
+}
+
+static struct vfio_device_feature *
+vfio_device_feature_dma_logging_start_create(VFIOContainer *container,
+ VFIODirtyRanges *tracking)
+{
+ struct vfio_device_feature *feature;
+ size_t feature_size;
+ struct vfio_device_feature_dma_logging_control *control;
+ struct vfio_device_feature_dma_logging_range *ranges;
+
+ feature_size = sizeof(struct vfio_device_feature) +
+ sizeof(struct vfio_device_feature_dma_logging_control);
+ feature = g_try_malloc0(feature_size);
+ if (!feature) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ feature->argsz = feature_size;
+ feature->flags = VFIO_DEVICE_FEATURE_SET |
+ VFIO_DEVICE_FEATURE_DMA_LOGGING_START;
+
+ control = (struct vfio_device_feature_dma_logging_control *)feature->data;
+ control->page_size = qemu_real_host_page_size();
+
+ /*
+ * DMA logging uAPI guarantees to support at least a number of ranges that
+ * fits into a single host kernel base page.
+ */
+ control->num_ranges = !!tracking->max32 + !!tracking->max64;
+ ranges = g_try_new0(struct vfio_device_feature_dma_logging_range,
+ control->num_ranges);
+ if (!ranges) {
+ g_free(feature);
+ errno = ENOMEM;
+
+ return NULL;
+ }
+
+ control->ranges = (__u64)(uintptr_t)ranges;
+ if (tracking->max32) {
+ ranges->iova = tracking->min32;
+ ranges->length = (tracking->max32 - tracking->min32) + 1;
+ ranges++;
+ }
+ if (tracking->max64) {
+ ranges->iova = tracking->min64;
+ ranges->length = (tracking->max64 - tracking->min64) + 1;
+ }
+
+ trace_vfio_device_dirty_tracking_start(control->num_ranges,
+ tracking->min32, tracking->max32,
+ tracking->min64, tracking->max64);
+
+ return feature;
+}
+
+static void vfio_device_feature_dma_logging_start_destroy(
+ struct vfio_device_feature *feature)
+{
+ struct vfio_device_feature_dma_logging_control *control =
+ (struct vfio_device_feature_dma_logging_control *)feature->data;
+ struct vfio_device_feature_dma_logging_range *ranges =
+ (struct vfio_device_feature_dma_logging_range *)(uintptr_t)control->ranges;
+
+ g_free(ranges);
+ g_free(feature);
+}
+
+static int vfio_devices_dma_logging_start(VFIOContainer *container)
+{
+ struct vfio_device_feature *feature;
+ VFIODirtyRanges ranges;
+ VFIODevice *vbasedev;
+ VFIOGroup *group;
+ int ret = 0;
+
+ vfio_dirty_tracking_init(container, &ranges);
+ feature = vfio_device_feature_dma_logging_start_create(container,
+ &ranges);
+ if (!feature) {
+ return -errno;
+ }
+
+ QLIST_FOREACH(group, &container->group_list, container_next) {
+ QLIST_FOREACH(vbasedev, &group->device_list, next) {
+ if (vbasedev->dirty_tracking) {
+ continue;
+ }
+
+ ret = ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature);
+ if (ret) {
+ ret = -errno;
+ error_report("%s: Failed to start DMA logging, err %d (%s)",
+ vbasedev->name, ret, strerror(errno));
+ goto out;
+ }
+ vbasedev->dirty_tracking = true;
+ }
+ }
+
+out:
+ if (ret) {
+ vfio_devices_dma_logging_stop(container);
+ }
+
+ vfio_device_feature_dma_logging_start_destroy(feature);
+
+ return ret;
}
static void vfio_listener_log_global_start(MemoryListener *listener)
{
VFIOContainer *container = container_of(listener, VFIOContainer, listener);
+ int ret;
- vfio_set_dirty_page_tracking(container, true);
+ if (vfio_devices_all_device_dirty_tracking(container)) {
+ ret = vfio_devices_dma_logging_start(container);
+ } else {
+ ret = vfio_set_dirty_page_tracking(container, true);
+ }
+
+ if (ret) {
+ error_report("vfio: Could not start dirty page tracking, err: %d (%s)",
+ ret, strerror(-ret));
+ vfio_set_migration_error(ret);
+ }
}
static void vfio_listener_log_global_stop(MemoryListener *listener)
{
VFIOContainer *container = container_of(listener, VFIOContainer, listener);
+ int ret = 0;
- vfio_set_dirty_page_tracking(container, false);
+ if (vfio_devices_all_device_dirty_tracking(container)) {
+ vfio_devices_dma_logging_stop(container);
+ } else {
+ ret = vfio_set_dirty_page_tracking(container, false);
+ }
+
+ if (ret) {
+ error_report("vfio: Could not stop dirty page tracking, err: %d (%s)",
+ ret, strerror(-ret));
+ vfio_set_migration_error(ret);
+ }
}
-static int vfio_get_dirty_bitmap(VFIOContainer *container, uint64_t iova,
- uint64_t size, ram_addr_t ram_addr)
+static int vfio_device_dma_logging_report(VFIODevice *vbasedev, hwaddr iova,
+ hwaddr size, void *bitmap)
+{
+ uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature) +
+ sizeof(struct vfio_device_feature_dma_logging_report),
+ sizeof(__u64))] = {};
+ struct vfio_device_feature *feature = (struct vfio_device_feature *)buf;
+ struct vfio_device_feature_dma_logging_report *report =
+ (struct vfio_device_feature_dma_logging_report *)feature->data;
+
+ report->iova = iova;
+ report->length = size;
+ report->page_size = qemu_real_host_page_size();
+ report->bitmap = (__u64)(uintptr_t)bitmap;
+
+ feature->argsz = sizeof(buf);
+ feature->flags = VFIO_DEVICE_FEATURE_GET |
+ VFIO_DEVICE_FEATURE_DMA_LOGGING_REPORT;
+
+ if (ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature)) {
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int vfio_devices_query_dirty_bitmap(VFIOContainer *container,
+ VFIOBitmap *vbmap, hwaddr iova,
+ hwaddr size)
+{
+ VFIODevice *vbasedev;
+ VFIOGroup *group;
+ int ret;
+
+ QLIST_FOREACH(group, &container->group_list, container_next) {
+ QLIST_FOREACH(vbasedev, &group->device_list, next) {
+ ret = vfio_device_dma_logging_report(vbasedev, iova, size,
+ vbmap->bitmap);
+ if (ret) {
+ error_report("%s: Failed to get DMA logging report, iova: "
+ "0x%" HWADDR_PRIx ", size: 0x%" HWADDR_PRIx
+ ", err: %d (%s)",
+ vbasedev->name, iova, size, ret, strerror(-ret));
+
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int vfio_query_dirty_bitmap(VFIOContainer *container, VFIOBitmap *vbmap,
+ hwaddr iova, hwaddr size)
{
struct vfio_iommu_type1_dirty_bitmap *dbitmap;
struct vfio_iommu_type1_dirty_bitmap_get *range;
- uint64_t pages;
int ret;
- if (!container->dirty_pages_supported) {
- cpu_physical_memory_set_dirty_range(ram_addr, size,
- tcg_enabled() ? DIRTY_CLIENTS_ALL :
- DIRTY_CLIENTS_NOCODE);
- return 0;
- }
-
dbitmap = g_malloc0(sizeof(*dbitmap) + sizeof(*range));
dbitmap->argsz = sizeof(*dbitmap) + sizeof(*range);
@@ -1323,36 +1725,63 @@
* to qemu_real_host_page_size.
*/
range->bitmap.pgsize = qemu_real_host_page_size();
-
- pages = REAL_HOST_PAGE_ALIGN(range->size) / qemu_real_host_page_size();
- range->bitmap.size = ROUND_UP(pages, sizeof(__u64) * BITS_PER_BYTE) /
- BITS_PER_BYTE;
- range->bitmap.data = g_try_malloc0(range->bitmap.size);
- if (!range->bitmap.data) {
- ret = -ENOMEM;
- goto err_out;
- }
+ range->bitmap.size = vbmap->size;
+ range->bitmap.data = (__u64 *)vbmap->bitmap;
ret = ioctl(container->fd, VFIO_IOMMU_DIRTY_PAGES, dbitmap);
if (ret) {
+ ret = -errno;
error_report("Failed to get dirty bitmap for iova: 0x%"PRIx64
" size: 0x%"PRIx64" err: %d", (uint64_t)range->iova,
(uint64_t)range->size, errno);
- goto err_out;
}
- cpu_physical_memory_set_dirty_lebitmap((unsigned long *)range->bitmap.data,
- ram_addr, pages);
-
- trace_vfio_get_dirty_bitmap(container->fd, range->iova, range->size,
- range->bitmap.size, ram_addr);
-err_out:
- g_free(range->bitmap.data);
g_free(dbitmap);
return ret;
}
+static int vfio_get_dirty_bitmap(VFIOContainer *container, uint64_t iova,
+ uint64_t size, ram_addr_t ram_addr)
+{
+ bool all_device_dirty_tracking =
+ vfio_devices_all_device_dirty_tracking(container);
+ VFIOBitmap vbmap;
+ int ret;
+
+ if (!container->dirty_pages_supported && !all_device_dirty_tracking) {
+ cpu_physical_memory_set_dirty_range(ram_addr, size,
+ tcg_enabled() ? DIRTY_CLIENTS_ALL :
+ DIRTY_CLIENTS_NOCODE);
+ return 0;
+ }
+
+ ret = vfio_bitmap_alloc(&vbmap, size);
+ if (ret) {
+ return ret;
+ }
+
+ if (all_device_dirty_tracking) {
+ ret = vfio_devices_query_dirty_bitmap(container, &vbmap, iova, size);
+ } else {
+ ret = vfio_query_dirty_bitmap(container, &vbmap, iova, size);
+ }
+
+ if (ret) {
+ goto out;
+ }
+
+ cpu_physical_memory_set_dirty_lebitmap(vbmap.bitmap, ram_addr,
+ vbmap.pages);
+
+ trace_vfio_get_dirty_bitmap(container->fd, iova, size, vbmap.size,
+ ram_addr);
+out:
+ g_free(vbmap.bitmap);
+
+ return ret;
+}
+
typedef struct {
IOMMUNotifier n;
VFIOGuestIOMMU *giommu;
@@ -1366,29 +1795,33 @@
VFIOContainer *container = giommu->container;
hwaddr iova = iotlb->iova + giommu->iommu_offset;
ram_addr_t translated_addr;
+ int ret = -EINVAL;
trace_vfio_iommu_map_dirty_notify(iova, iova + iotlb->addr_mask);
if (iotlb->target_as != &address_space_memory) {
error_report("Wrong target AS \"%s\", only system memory is allowed",
iotlb->target_as->name ? iotlb->target_as->name : "none");
- return;
+ goto out;
}
rcu_read_lock();
if (vfio_get_xlat_addr(iotlb, NULL, &translated_addr, NULL)) {
- int ret;
-
ret = vfio_get_dirty_bitmap(container, iova, iotlb->addr_mask + 1,
translated_addr);
if (ret) {
error_report("vfio_iommu_map_dirty_notify(%p, 0x%"HWADDR_PRIx", "
- "0x%"HWADDR_PRIx") = %d (%m)",
- container, iova,
- iotlb->addr_mask + 1, ret);
+ "0x%"HWADDR_PRIx") = %d (%s)",
+ container, iova, iotlb->addr_mask + 1, ret,
+ strerror(-ret));
}
}
rcu_read_unlock();
+
+out:
+ if (ret) {
+ vfio_set_migration_error(ret);
+ }
}
static int vfio_ram_discard_get_dirty_bitmap(MemoryRegionSection *section,
@@ -1481,13 +1914,19 @@
MemoryRegionSection *section)
{
VFIOContainer *container = container_of(listener, VFIOContainer, listener);
+ int ret;
if (vfio_listener_skipped_section(section)) {
return;
}
if (vfio_devices_all_dirty_tracking(container)) {
- vfio_sync_dirty_bitmap(container, section);
+ ret = vfio_sync_dirty_bitmap(container, section);
+ if (ret) {
+ error_report("vfio: Failed to sync dirty bitmap, err: %d (%s)", ret,
+ strerror(-ret));
+ vfio_set_migration_error(ret);
+ }
}
}
diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
index a2c3d9b..1a1a865 100644
--- a/hw/vfio/migration.c
+++ b/hw/vfio/migration.c
@@ -521,7 +521,7 @@
}
}
-static void vfio_migration_exit(VFIODevice *vbasedev)
+static void vfio_migration_free(VFIODevice *vbasedev)
{
g_free(vbasedev->migration);
vbasedev->migration = NULL;
@@ -555,6 +555,19 @@
return 0;
}
+static bool vfio_dma_logging_supported(VFIODevice *vbasedev)
+{
+ uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature),
+ sizeof(uint64_t))] = {};
+ struct vfio_device_feature *feature = (struct vfio_device_feature *)buf;
+
+ feature->argsz = sizeof(buf);
+ feature->flags = VFIO_DEVICE_FEATURE_PROBE |
+ VFIO_DEVICE_FEATURE_DMA_LOGGING_START;
+
+ return !ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature);
+}
+
static int vfio_migration_init(VFIODevice *vbasedev)
{
int ret;
@@ -589,6 +602,8 @@
migration->device_state = VFIO_DEVICE_STATE_RUNNING;
migration->data_fd = -1;
+ vbasedev->dirty_pages_supported = vfio_dma_logging_supported(vbasedev);
+
oid = vmstate_if_get_id(VMSTATE_IF(DEVICE(obj)));
if (oid) {
path = g_strdup_printf("%s/vfio", oid);
@@ -616,7 +631,7 @@
return bytes_transferred;
}
-int vfio_migration_probe(VFIODevice *vbasedev, Error **errp)
+int vfio_migration_realize(VFIODevice *vbasedev, Error **errp)
{
int ret = -ENOTSUP;
@@ -634,6 +649,11 @@
return ret;
}
+ ret = vfio_block_giommu_migration(errp);
+ if (ret) {
+ return ret;
+ }
+
trace_vfio_migration_probe(vbasedev->name);
return 0;
@@ -649,7 +669,7 @@
return ret;
}
-void vfio_migration_finalize(VFIODevice *vbasedev)
+void vfio_migration_exit(VFIODevice *vbasedev)
{
if (vbasedev->migration) {
VFIOMigration *migration = vbasedev->migration;
@@ -657,7 +677,7 @@
remove_migration_state_change_notifier(&migration->migration_state);
qemu_del_vm_change_state_handler(migration->vm_state);
unregister_savevm(VMSTATE_IF(vbasedev->dev), "vfio", vbasedev);
- vfio_migration_exit(vbasedev);
+ vfio_migration_free(vbasedev);
vfio_unblock_multiple_devices_migration();
}
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 939dcc3..ec9a854 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -3145,7 +3145,7 @@
}
if (!pdev->failover_pair_id) {
- ret = vfio_migration_probe(vbasedev, errp);
+ ret = vfio_migration_realize(vbasedev, errp);
if (ret) {
error_report("%s: Migration disabled", vbasedev->name);
}
@@ -3185,6 +3185,7 @@
*/
vfio_put_device(vdev);
vfio_put_group(group);
+ vfio_migration_finalize();
}
static void vfio_exitfn(PCIDevice *pdev)
@@ -3203,7 +3204,7 @@
}
vfio_teardown_msi(vdev);
vfio_bars_exit(vdev);
- vfio_migration_finalize(&vdev->vbasedev);
+ vfio_migration_exit(&vdev->vbasedev);
}
static void vfio_pci_reset(DeviceState *dev)
diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events
index 669d9fe..646e42f 100644
--- a/hw/vfio/trace-events
+++ b/hw/vfio/trace-events
@@ -96,14 +96,15 @@
vfio_region_write(const char *name, int index, uint64_t addr, uint64_t data, unsigned size) " (%s:region%d+0x%"PRIx64", 0x%"PRIx64 ", %d)"
vfio_region_read(char *name, int index, uint64_t addr, unsigned size, uint64_t data) " (%s:region%d+0x%"PRIx64", %d) = 0x%"PRIx64
vfio_iommu_map_notify(const char *op, uint64_t iova_start, uint64_t iova_end) "iommu %s @ 0x%"PRIx64" - 0x%"PRIx64
-vfio_listener_region_add_skip(uint64_t start, uint64_t end) "SKIPPING region_add 0x%"PRIx64" - 0x%"PRIx64
+vfio_listener_region_skip(const char *name, uint64_t start, uint64_t end) "SKIPPING %s 0x%"PRIx64" - 0x%"PRIx64
vfio_spapr_group_attach(int groupfd, int tablefd) "Attached groupfd %d to liobn fd %d"
vfio_listener_region_add_iommu(uint64_t start, uint64_t end) "region_add [iommu] 0x%"PRIx64" - 0x%"PRIx64
vfio_listener_region_add_ram(uint64_t iova_start, uint64_t iova_end, void *vaddr) "region_add [ram] 0x%"PRIx64" - 0x%"PRIx64" [%p]"
vfio_known_safe_misalignment(const char *name, uint64_t iova, uint64_t offset_within_region, uintptr_t page_size) "Region \"%s\" iova=0x%"PRIx64" offset_within_region=0x%"PRIx64" qemu_real_host_page_size=0x%"PRIxPTR
vfio_listener_region_add_no_dma_map(const char *name, uint64_t iova, uint64_t size, uint64_t page_size) "Region \"%s\" 0x%"PRIx64" size=0x%"PRIx64" is not aligned to 0x%"PRIx64" and cannot be mapped for DMA"
-vfio_listener_region_del_skip(uint64_t start, uint64_t end) "SKIPPING region_del 0x%"PRIx64" - 0x%"PRIx64
vfio_listener_region_del(uint64_t start, uint64_t end) "region_del 0x%"PRIx64" - 0x%"PRIx64
+vfio_device_dirty_tracking_update(uint64_t start, uint64_t end, uint64_t min, uint64_t max) "section 0x%"PRIx64" - 0x%"PRIx64" -> update [0x%"PRIx64" - 0x%"PRIx64"]"
+vfio_device_dirty_tracking_start(int nr_ranges, uint64_t min32, uint64_t max32, uint64_t min64, uint64_t max64) "nr_ranges %d 32:[0x%"PRIx64" - 0x%"PRIx64"], 64:[0x%"PRIx64" - 0x%"PRIx64"]"
vfio_disconnect_container(int fd) "close container->fd=%d"
vfio_put_group(int fd) "close group->fd=%d"
vfio_get_device(const char * name, unsigned int flags, unsigned int num_regions, unsigned int num_irqs) "Device %s flags: %u, regions: %u, irqs: %u"
@@ -117,7 +118,7 @@
vfio_region_unmap(const char *name, unsigned long offset, unsigned long end) "Region %s unmap [0x%lx - 0x%lx]"
vfio_region_sparse_mmap_header(const char *name, int index, int nr_areas) "Device %s region %d: %d sparse mmap entries"
vfio_region_sparse_mmap_entry(int i, unsigned long start, unsigned long end) "sparse entry %d [0x%lx - 0x%lx]"
-vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%0x8"
+vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%08x"
vfio_dma_unmap_overflow_workaround(void) ""
vfio_get_dirty_bitmap(int fd, uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start) "container fd=%d, iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64
vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64
diff --git a/hw/xen/meson.build b/hw/xen/meson.build
index ae0ace3..19c6aab 100644
--- a/hw/xen/meson.build
+++ b/hw/xen/meson.build
@@ -1,4 +1,4 @@
-softmmu_ss.add(when: ['CONFIG_XEN', xen], if_true: files(
+softmmu_ss.add(when: ['CONFIG_XEN_BUS'], if_true: files(
'xen-backend.c',
'xen-bus-helper.c',
'xen-bus.c',
@@ -7,6 +7,10 @@
'xen_pvdev.c',
))
+softmmu_ss.add(when: ['CONFIG_XEN', xen], if_true: files(
+ 'xen-operations.c',
+))
+
xen_specific_ss = ss.source_set()
if have_xen_pci_passthrough
xen_specific_ss.add(files(
diff --git a/hw/xen/trace-events b/hw/xen/trace-events
index 3da3fd8..55c9e1d 100644
--- a/hw/xen/trace-events
+++ b/hw/xen/trace-events
@@ -1,6 +1,6 @@
# See docs/devel/tracing.rst for syntax documentation.
-# ../../include/hw/xen/xen_common.h
+# ../../include/hw/xen/xen_native.h
xen_default_ioreq_server(void) ""
xen_ioreq_server_create(uint32_t id) "id: %u"
xen_ioreq_server_destroy(uint32_t id) "id: %u"
diff --git a/hw/xen/xen-bus-helper.c b/hw/xen/xen-bus-helper.c
index 5a1e12b..b2b2cc9 100644
--- a/hw/xen/xen-bus-helper.c
+++ b/hw/xen/xen-bus-helper.c
@@ -10,6 +10,7 @@
#include "hw/xen/xen-bus.h"
#include "hw/xen/xen-bus-helper.h"
#include "qapi/error.h"
+#include "trace.h"
#include <glib/gprintf.h>
@@ -46,34 +47,28 @@
return "INVALID";
}
-void xs_node_create(struct xs_handle *xsh, xs_transaction_t tid,
- const char *node, struct xs_permissions perms[],
- unsigned int nr_perms, Error **errp)
+void xs_node_create(struct qemu_xs_handle *h, xs_transaction_t tid,
+ const char *node, unsigned int owner, unsigned int domid,
+ unsigned int perms, Error **errp)
{
trace_xs_node_create(node);
- if (!xs_write(xsh, tid, node, "", 0)) {
+ if (!qemu_xen_xs_create(h, tid, owner, domid, perms, node)) {
error_setg_errno(errp, errno, "failed to create node '%s'", node);
- return;
- }
-
- if (!xs_set_permissions(xsh, tid, node, perms, nr_perms)) {
- error_setg_errno(errp, errno, "failed to set node '%s' permissions",
- node);
}
}
-void xs_node_destroy(struct xs_handle *xsh, xs_transaction_t tid,
+void xs_node_destroy(struct qemu_xs_handle *h, xs_transaction_t tid,
const char *node, Error **errp)
{
trace_xs_node_destroy(node);
- if (!xs_rm(xsh, tid, node)) {
+ if (!qemu_xen_xs_destroy(h, tid, node)) {
error_setg_errno(errp, errno, "failed to destroy node '%s'", node);
}
}
-void xs_node_vprintf(struct xs_handle *xsh, xs_transaction_t tid,
+void xs_node_vprintf(struct qemu_xs_handle *h, xs_transaction_t tid,
const char *node, const char *key, Error **errp,
const char *fmt, va_list ap)
{
@@ -86,7 +81,7 @@
trace_xs_node_vprintf(path, value);
- if (!xs_write(xsh, tid, path, value, len)) {
+ if (!qemu_xen_xs_write(h, tid, path, value, len)) {
error_setg_errno(errp, errno, "failed to write '%s' to '%s'",
value, path);
}
@@ -95,18 +90,18 @@
g_free(path);
}
-void xs_node_printf(struct xs_handle *xsh, xs_transaction_t tid,
+void xs_node_printf(struct qemu_xs_handle *h, xs_transaction_t tid,
const char *node, const char *key, Error **errp,
const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
- xs_node_vprintf(xsh, tid, node, key, errp, fmt, ap);
+ xs_node_vprintf(h, tid, node, key, errp, fmt, ap);
va_end(ap);
}
-int xs_node_vscanf(struct xs_handle *xsh, xs_transaction_t tid,
+int xs_node_vscanf(struct qemu_xs_handle *h, xs_transaction_t tid,
const char *node, const char *key, Error **errp,
const char *fmt, va_list ap)
{
@@ -115,7 +110,7 @@
path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) :
g_strdup(key);
- value = xs_read(xsh, tid, path, NULL);
+ value = qemu_xen_xs_read(h, tid, path, NULL);
trace_xs_node_vscanf(path, value);
@@ -133,7 +128,7 @@
return rc;
}
-int xs_node_scanf(struct xs_handle *xsh, xs_transaction_t tid,
+int xs_node_scanf(struct qemu_xs_handle *h, xs_transaction_t tid,
const char *node, const char *key, Error **errp,
const char *fmt, ...)
{
@@ -141,42 +136,35 @@
int rc;
va_start(ap, fmt);
- rc = xs_node_vscanf(xsh, tid, node, key, errp, fmt, ap);
+ rc = xs_node_vscanf(h, tid, node, key, errp, fmt, ap);
va_end(ap);
return rc;
}
-void xs_node_watch(struct xs_handle *xsh, const char *node, const char *key,
- char *token, Error **errp)
+struct qemu_xs_watch *xs_node_watch(struct qemu_xs_handle *h, const char *node,
+ const char *key, xs_watch_fn fn,
+ void *opaque, Error **errp)
{
char *path;
+ struct qemu_xs_watch *w;
path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) :
g_strdup(key);
trace_xs_node_watch(path);
- if (!xs_watch(xsh, path, token)) {
+ w = qemu_xen_xs_watch(h, path, fn, opaque);
+ if (!w) {
error_setg_errno(errp, errno, "failed to watch node '%s'", path);
}
g_free(path);
+
+ return w;
}
-void xs_node_unwatch(struct xs_handle *xsh, const char *node,
- const char *key, const char *token, Error **errp)
+void xs_node_unwatch(struct qemu_xs_handle *h, struct qemu_xs_watch *w)
{
- char *path;
-
- path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) :
- g_strdup(key);
-
- trace_xs_node_unwatch(path);
-
- if (!xs_unwatch(xsh, path, token)) {
- error_setg_errno(errp, errno, "failed to unwatch node '%s'", path);
- }
-
- g_free(path);
+ qemu_xen_xs_unwatch(h, w);
}
diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c
index df3f6b9..c59850b 100644
--- a/hw/xen/xen-bus.c
+++ b/hw/xen/xen-bus.c
@@ -62,7 +62,7 @@
/* Mimic the way the Xen toolstack does an unplug */
again:
- tid = xs_transaction_start(xenbus->xsh);
+ tid = qemu_xen_xs_transaction_start(xenbus->xsh);
if (tid == XBT_NULL) {
error_setg_errno(errp, errno, "failed xs_transaction_start");
return;
@@ -80,7 +80,7 @@
goto abort;
}
- if (!xs_transaction_end(xenbus->xsh, tid, false)) {
+ if (!qemu_xen_xs_transaction_end(xenbus->xsh, tid, false)) {
if (errno == EAGAIN) {
goto again;
}
@@ -95,7 +95,7 @@
* We only abort if there is already a failure so ignore any error
* from ending the transaction.
*/
- xs_transaction_end(xenbus->xsh, tid, true);
+ qemu_xen_xs_transaction_end(xenbus->xsh, tid, true);
}
static void xen_bus_print_dev(Monitor *mon, DeviceState *dev, int indent)
@@ -111,143 +111,6 @@
return xen_device_get_backend_path(XEN_DEVICE(dev));
}
-struct XenWatch {
- char *node, *key;
- char *token;
- XenWatchHandler handler;
- void *opaque;
- Notifier notifier;
-};
-
-static void watch_notify(Notifier *n, void *data)
-{
- XenWatch *watch = container_of(n, XenWatch, notifier);
- const char *token = data;
-
- if (!strcmp(watch->token, token)) {
- watch->handler(watch->opaque);
- }
-}
-
-static XenWatch *new_watch(const char *node, const char *key,
- XenWatchHandler handler, void *opaque)
-{
- XenWatch *watch = g_new0(XenWatch, 1);
- QemuUUID uuid;
-
- qemu_uuid_generate(&uuid);
-
- watch->token = qemu_uuid_unparse_strdup(&uuid);
- watch->node = g_strdup(node);
- watch->key = g_strdup(key);
- watch->handler = handler;
- watch->opaque = opaque;
- watch->notifier.notify = watch_notify;
-
- return watch;
-}
-
-static void free_watch(XenWatch *watch)
-{
- g_free(watch->token);
- g_free(watch->key);
- g_free(watch->node);
-
- g_free(watch);
-}
-
-struct XenWatchList {
- struct xs_handle *xsh;
- NotifierList notifiers;
-};
-
-static void watch_list_event(void *opaque)
-{
- XenWatchList *watch_list = opaque;
- char **v;
- const char *token;
-
- v = xs_check_watch(watch_list->xsh);
- if (!v) {
- return;
- }
-
- token = v[XS_WATCH_TOKEN];
-
- notifier_list_notify(&watch_list->notifiers, (void *)token);
-
- free(v);
-}
-
-static XenWatchList *watch_list_create(struct xs_handle *xsh)
-{
- XenWatchList *watch_list = g_new0(XenWatchList, 1);
-
- g_assert(xsh);
-
- watch_list->xsh = xsh;
- notifier_list_init(&watch_list->notifiers);
- qemu_set_fd_handler(xs_fileno(watch_list->xsh), watch_list_event, NULL,
- watch_list);
-
- return watch_list;
-}
-
-static void watch_list_destroy(XenWatchList *watch_list)
-{
- g_assert(notifier_list_empty(&watch_list->notifiers));
- qemu_set_fd_handler(xs_fileno(watch_list->xsh), NULL, NULL, NULL);
- g_free(watch_list);
-}
-
-static XenWatch *watch_list_add(XenWatchList *watch_list, const char *node,
- const char *key, XenWatchHandler handler,
- void *opaque, Error **errp)
-{
- ERRP_GUARD();
- XenWatch *watch = new_watch(node, key, handler, opaque);
-
- notifier_list_add(&watch_list->notifiers, &watch->notifier);
-
- xs_node_watch(watch_list->xsh, node, key, watch->token, errp);
- if (*errp) {
- notifier_remove(&watch->notifier);
- free_watch(watch);
-
- return NULL;
- }
-
- return watch;
-}
-
-static void watch_list_remove(XenWatchList *watch_list, XenWatch *watch,
- Error **errp)
-{
- xs_node_unwatch(watch_list->xsh, watch->node, watch->key, watch->token,
- errp);
-
- notifier_remove(&watch->notifier);
- free_watch(watch);
-}
-
-static XenWatch *xen_bus_add_watch(XenBus *xenbus, const char *node,
- const char *key, XenWatchHandler handler,
- Error **errp)
-{
- trace_xen_bus_add_watch(node, key);
-
- return watch_list_add(xenbus->watch_list, node, key, handler, xenbus,
- errp);
-}
-
-static void xen_bus_remove_watch(XenBus *xenbus, XenWatch *watch,
- Error **errp)
-{
- trace_xen_bus_remove_watch(watch->node, watch->key);
-
- watch_list_remove(xenbus->watch_list, watch, errp);
-}
-
static void xen_bus_backend_create(XenBus *xenbus, const char *type,
const char *name, char *path,
Error **errp)
@@ -261,15 +124,15 @@
trace_xen_bus_backend_create(type, path);
again:
- tid = xs_transaction_start(xenbus->xsh);
+ tid = qemu_xen_xs_transaction_start(xenbus->xsh);
if (tid == XBT_NULL) {
error_setg(errp, "failed xs_transaction_start");
return;
}
- key = xs_directory(xenbus->xsh, tid, path, &n);
+ key = qemu_xen_xs_directory(xenbus->xsh, tid, path, &n);
if (!key) {
- if (!xs_transaction_end(xenbus->xsh, tid, true)) {
+ if (!qemu_xen_xs_transaction_end(xenbus->xsh, tid, true)) {
error_setg_errno(errp, errno, "failed xs_transaction_end");
}
return;
@@ -300,7 +163,7 @@
free(key);
- if (!xs_transaction_end(xenbus->xsh, tid, false)) {
+ if (!qemu_xen_xs_transaction_end(xenbus->xsh, tid, false)) {
qobject_unref(opts);
if (errno == EAGAIN) {
@@ -327,7 +190,7 @@
trace_xen_bus_type_enumerate(type);
- backend = xs_directory(xenbus->xsh, XBT_NULL, domain_path, &n);
+ backend = qemu_xen_xs_directory(xenbus->xsh, XBT_NULL, domain_path, &n);
if (!backend) {
goto out;
}
@@ -372,7 +235,7 @@
trace_xen_bus_enumerate();
- type = xs_directory(xenbus->xsh, XBT_NULL, "backend", &n);
+ type = qemu_xen_xs_directory(xenbus->xsh, XBT_NULL, "backend", &n);
if (!type) {
return;
}
@@ -415,7 +278,7 @@
}
}
-static void xen_bus_backend_changed(void *opaque)
+static void xen_bus_backend_changed(void *opaque, const char *path)
{
XenBus *xenbus = opaque;
@@ -434,7 +297,7 @@
for (i = 0; i < xenbus->backend_types; i++) {
if (xenbus->backend_watch[i]) {
- xen_bus_remove_watch(xenbus, xenbus->backend_watch[i], NULL);
+ xs_node_unwatch(xenbus->xsh, xenbus->backend_watch[i]);
}
}
@@ -442,13 +305,8 @@
xenbus->backend_watch = NULL;
}
- if (xenbus->watch_list) {
- watch_list_destroy(xenbus->watch_list);
- xenbus->watch_list = NULL;
- }
-
if (xenbus->xsh) {
- xs_close(xenbus->xsh);
+ qemu_xen_xs_close(xenbus->xsh);
}
}
@@ -463,7 +321,7 @@
trace_xen_bus_realize();
- xenbus->xsh = xs_open(0);
+ xenbus->xsh = qemu_xen_xs_open();
if (!xenbus->xsh) {
error_setg_errno(errp, errno, "failed xs_open");
goto fail;
@@ -476,19 +334,18 @@
xenbus->backend_id = 0; /* Assume lack of node means dom0 */
}
- xenbus->watch_list = watch_list_create(xenbus->xsh);
-
module_call_init(MODULE_INIT_XEN_BACKEND);
type = xen_backend_get_types(&xenbus->backend_types);
- xenbus->backend_watch = g_new(XenWatch *, xenbus->backend_types);
+ xenbus->backend_watch = g_new(struct qemu_xs_watch *,
+ xenbus->backend_types);
for (i = 0; i < xenbus->backend_types; i++) {
char *node = g_strdup_printf("backend/%s", type[i]);
xenbus->backend_watch[i] =
- xen_bus_add_watch(xenbus, node, key, xen_bus_backend_changed,
- &local_err);
+ xs_node_watch(xenbus->xsh, node, key, xen_bus_backend_changed,
+ xenbus, &local_err);
if (local_err) {
/* This need not be treated as a hard error so don't propagate */
error_reportf_err(local_err,
@@ -631,7 +488,7 @@
}
}
-static void xen_device_backend_changed(void *opaque)
+static void xen_device_backend_changed(void *opaque, const char *path)
{
XenDevice *xendev = opaque;
const char *type = object_get_typename(OBJECT(xendev));
@@ -685,66 +542,35 @@
}
}
-static XenWatch *xen_device_add_watch(XenDevice *xendev, const char *node,
- const char *key,
- XenWatchHandler handler,
- Error **errp)
-{
- const char *type = object_get_typename(OBJECT(xendev));
-
- trace_xen_device_add_watch(type, xendev->name, node, key);
-
- return watch_list_add(xendev->watch_list, node, key, handler, xendev,
- errp);
-}
-
-static void xen_device_remove_watch(XenDevice *xendev, XenWatch *watch,
- Error **errp)
-{
- const char *type = object_get_typename(OBJECT(xendev));
-
- trace_xen_device_remove_watch(type, xendev->name, watch->node,
- watch->key);
-
- watch_list_remove(xendev->watch_list, watch, errp);
-}
-
-
static void xen_device_backend_create(XenDevice *xendev, Error **errp)
{
ERRP_GUARD();
XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
- struct xs_permissions perms[2];
xendev->backend_path = xen_device_get_backend_path(xendev);
- perms[0].id = xenbus->backend_id;
- perms[0].perms = XS_PERM_NONE;
- perms[1].id = xendev->frontend_id;
- perms[1].perms = XS_PERM_READ;
-
g_assert(xenbus->xsh);
- xs_node_create(xenbus->xsh, XBT_NULL, xendev->backend_path, perms,
- ARRAY_SIZE(perms), errp);
+ xs_node_create(xenbus->xsh, XBT_NULL, xendev->backend_path,
+ xenbus->backend_id, xendev->frontend_id, XS_PERM_READ, errp);
if (*errp) {
error_prepend(errp, "failed to create backend: ");
return;
}
xendev->backend_state_watch =
- xen_device_add_watch(xendev, xendev->backend_path,
- "state", xen_device_backend_changed,
- errp);
+ xs_node_watch(xendev->xsh, xendev->backend_path,
+ "state", xen_device_backend_changed, xendev,
+ errp);
if (*errp) {
error_prepend(errp, "failed to watch backend state: ");
return;
}
xendev->backend_online_watch =
- xen_device_add_watch(xendev, xendev->backend_path,
- "online", xen_device_backend_changed,
- errp);
+ xs_node_watch(xendev->xsh, xendev->backend_path,
+ "online", xen_device_backend_changed, xendev,
+ errp);
if (*errp) {
error_prepend(errp, "failed to watch backend online: ");
return;
@@ -757,12 +583,12 @@
Error *local_err = NULL;
if (xendev->backend_online_watch) {
- xen_device_remove_watch(xendev, xendev->backend_online_watch, NULL);
+ xs_node_unwatch(xendev->xsh, xendev->backend_online_watch);
xendev->backend_online_watch = NULL;
}
if (xendev->backend_state_watch) {
- xen_device_remove_watch(xendev, xendev->backend_state_watch, NULL);
+ xs_node_unwatch(xendev->xsh, xendev->backend_state_watch);
xendev->backend_state_watch = NULL;
}
@@ -837,7 +663,7 @@
}
}
-static void xen_device_frontend_changed(void *opaque)
+static void xen_device_frontend_changed(void *opaque, const char *path)
{
XenDevice *xendev = opaque;
XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev);
@@ -885,7 +711,6 @@
{
ERRP_GUARD();
XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
- struct xs_permissions perms[2];
xendev->frontend_path = xen_device_get_frontend_path(xendev);
@@ -894,15 +719,11 @@
* toolstack.
*/
if (!xen_device_frontend_exists(xendev)) {
- perms[0].id = xendev->frontend_id;
- perms[0].perms = XS_PERM_NONE;
- perms[1].id = xenbus->backend_id;
- perms[1].perms = XS_PERM_READ | XS_PERM_WRITE;
-
g_assert(xenbus->xsh);
- xs_node_create(xenbus->xsh, XBT_NULL, xendev->frontend_path, perms,
- ARRAY_SIZE(perms), errp);
+ xs_node_create(xenbus->xsh, XBT_NULL, xendev->frontend_path,
+ xendev->frontend_id, xenbus->backend_id,
+ XS_PERM_READ | XS_PERM_WRITE, errp);
if (*errp) {
error_prepend(errp, "failed to create frontend: ");
return;
@@ -910,8 +731,8 @@
}
xendev->frontend_state_watch =
- xen_device_add_watch(xendev, xendev->frontend_path, "state",
- xen_device_frontend_changed, errp);
+ xs_node_watch(xendev->xsh, xendev->frontend_path, "state",
+ xen_device_frontend_changed, xendev, errp);
if (*errp) {
error_prepend(errp, "failed to watch frontend state: ");
}
@@ -923,8 +744,7 @@
Error *local_err = NULL;
if (xendev->frontend_state_watch) {
- xen_device_remove_watch(xendev, xendev->frontend_state_watch,
- NULL);
+ xs_node_unwatch(xendev->xsh, xendev->frontend_state_watch);
xendev->frontend_state_watch = NULL;
}
@@ -947,7 +767,7 @@
void xen_device_set_max_grant_refs(XenDevice *xendev, unsigned int nr_refs,
Error **errp)
{
- if (xengnttab_set_max_grants(xendev->xgth, nr_refs)) {
+ if (qemu_xen_gnttab_set_max_grants(xendev->xgth, nr_refs)) {
error_setg_errno(errp, errno, "xengnttab_set_max_grants failed");
}
}
@@ -956,9 +776,8 @@
unsigned int nr_refs, int prot,
Error **errp)
{
- void *map = xengnttab_map_domain_grant_refs(xendev->xgth, nr_refs,
- xendev->frontend_id, refs,
- prot);
+ void *map = qemu_xen_gnttab_map_refs(xendev->xgth, nr_refs,
+ xendev->frontend_id, refs, prot);
if (!map) {
error_setg_errno(errp, errno,
@@ -968,112 +787,20 @@
return map;
}
-void xen_device_unmap_grant_refs(XenDevice *xendev, void *map,
+void xen_device_unmap_grant_refs(XenDevice *xendev, void *map, uint32_t *refs,
unsigned int nr_refs, Error **errp)
{
- if (xengnttab_unmap(xendev->xgth, map, nr_refs)) {
+ if (qemu_xen_gnttab_unmap(xendev->xgth, map, refs, nr_refs)) {
error_setg_errno(errp, errno, "xengnttab_unmap failed");
}
}
-static void compat_copy_grant_refs(XenDevice *xendev, bool to_domain,
- XenDeviceGrantCopySegment segs[],
- unsigned int nr_segs, Error **errp)
-{
- uint32_t *refs = g_new(uint32_t, nr_segs);
- int prot = to_domain ? PROT_WRITE : PROT_READ;
- void *map;
- unsigned int i;
-
- for (i = 0; i < nr_segs; i++) {
- XenDeviceGrantCopySegment *seg = &segs[i];
-
- refs[i] = to_domain ? seg->dest.foreign.ref :
- seg->source.foreign.ref;
- }
-
- map = xengnttab_map_domain_grant_refs(xendev->xgth, nr_segs,
- xendev->frontend_id, refs,
- prot);
- if (!map) {
- error_setg_errno(errp, errno,
- "xengnttab_map_domain_grant_refs failed");
- goto done;
- }
-
- for (i = 0; i < nr_segs; i++) {
- XenDeviceGrantCopySegment *seg = &segs[i];
- void *page = map + (i * XC_PAGE_SIZE);
-
- if (to_domain) {
- memcpy(page + seg->dest.foreign.offset, seg->source.virt,
- seg->len);
- } else {
- memcpy(seg->dest.virt, page + seg->source.foreign.offset,
- seg->len);
- }
- }
-
- if (xengnttab_unmap(xendev->xgth, map, nr_segs)) {
- error_setg_errno(errp, errno, "xengnttab_unmap failed");
- }
-
-done:
- g_free(refs);
-}
-
void xen_device_copy_grant_refs(XenDevice *xendev, bool to_domain,
XenDeviceGrantCopySegment segs[],
unsigned int nr_segs, Error **errp)
{
- xengnttab_grant_copy_segment_t *xengnttab_segs;
- unsigned int i;
-
- if (!xendev->feature_grant_copy) {
- compat_copy_grant_refs(xendev, to_domain, segs, nr_segs, errp);
- return;
- }
-
- xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs);
-
- for (i = 0; i < nr_segs; i++) {
- XenDeviceGrantCopySegment *seg = &segs[i];
- xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i];
-
- if (to_domain) {
- xengnttab_seg->flags = GNTCOPY_dest_gref;
- xengnttab_seg->dest.foreign.domid = xendev->frontend_id;
- xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref;
- xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset;
- xengnttab_seg->source.virt = seg->source.virt;
- } else {
- xengnttab_seg->flags = GNTCOPY_source_gref;
- xengnttab_seg->source.foreign.domid = xendev->frontend_id;
- xengnttab_seg->source.foreign.ref = seg->source.foreign.ref;
- xengnttab_seg->source.foreign.offset =
- seg->source.foreign.offset;
- xengnttab_seg->dest.virt = seg->dest.virt;
- }
-
- xengnttab_seg->len = seg->len;
- }
-
- if (xengnttab_grant_copy(xendev->xgth, nr_segs, xengnttab_segs)) {
- error_setg_errno(errp, errno, "xengnttab_grant_copy failed");
- goto done;
- }
-
- for (i = 0; i < nr_segs; i++) {
- xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i];
-
- if (xengnttab_seg->status != GNTST_okay) {
- error_setg(errp, "xengnttab_grant_copy seg[%u] failed", i);
- break;
- }
- }
-
-done:
- g_free(xengnttab_segs);
+ qemu_xen_gnttab_grant_copy(xendev->xgth, to_domain, xendev->frontend_id,
+ (XenGrantCopySegment *)segs, nr_segs, errp);
}
struct XenEventChannel {
@@ -1095,12 +822,12 @@
static void xen_device_event(void *opaque)
{
XenEventChannel *channel = opaque;
- unsigned long port = xenevtchn_pending(channel->xeh);
+ unsigned long port = qemu_xen_evtchn_pending(channel->xeh);
if (port == channel->local_port) {
xen_device_poll(channel);
- xenevtchn_unmask(channel->xeh, port);
+ qemu_xen_evtchn_unmask(channel->xeh, port);
}
}
@@ -1115,11 +842,11 @@
}
if (channel->ctx)
- aio_set_fd_handler(channel->ctx, xenevtchn_fd(channel->xeh), true,
+ aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), true,
NULL, NULL, NULL, NULL, NULL);
channel->ctx = ctx;
- aio_set_fd_handler(channel->ctx, xenevtchn_fd(channel->xeh), true,
+ aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), true,
xen_device_event, NULL, xen_device_poll, NULL, channel);
}
@@ -1131,13 +858,13 @@
XenEventChannel *channel = g_new0(XenEventChannel, 1);
xenevtchn_port_or_error_t local_port;
- channel->xeh = xenevtchn_open(NULL, 0);
+ channel->xeh = qemu_xen_evtchn_open();
if (!channel->xeh) {
error_setg_errno(errp, errno, "failed xenevtchn_open");
goto fail;
}
- local_port = xenevtchn_bind_interdomain(channel->xeh,
+ local_port = qemu_xen_evtchn_bind_interdomain(channel->xeh,
xendev->frontend_id,
port);
if (local_port < 0) {
@@ -1160,7 +887,7 @@
fail:
if (channel->xeh) {
- xenevtchn_close(channel->xeh);
+ qemu_xen_evtchn_close(channel->xeh);
}
g_free(channel);
@@ -1177,7 +904,7 @@
return;
}
- if (xenevtchn_notify(channel->xeh, channel->local_port) < 0) {
+ if (qemu_xen_evtchn_notify(channel->xeh, channel->local_port) < 0) {
error_setg_errno(errp, errno, "xenevtchn_notify failed");
}
}
@@ -1193,14 +920,14 @@
QLIST_REMOVE(channel, list);
- aio_set_fd_handler(channel->ctx, xenevtchn_fd(channel->xeh), true,
+ aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), true,
NULL, NULL, NULL, NULL, NULL);
- if (xenevtchn_unbind(channel->xeh, channel->local_port) < 0) {
+ if (qemu_xen_evtchn_unbind(channel->xeh, channel->local_port) < 0) {
error_setg_errno(errp, errno, "xenevtchn_unbind failed");
}
- xenevtchn_close(channel->xeh);
+ qemu_xen_evtchn_close(channel->xeh);
g_free(channel);
}
@@ -1235,17 +962,12 @@
xen_device_backend_destroy(xendev);
if (xendev->xgth) {
- xengnttab_close(xendev->xgth);
+ qemu_xen_gnttab_close(xendev->xgth);
xendev->xgth = NULL;
}
- if (xendev->watch_list) {
- watch_list_destroy(xendev->watch_list);
- xendev->watch_list = NULL;
- }
-
if (xendev->xsh) {
- xs_close(xendev->xsh);
+ qemu_xen_xs_close(xendev->xsh);
xendev->xsh = NULL;
}
@@ -1290,23 +1012,18 @@
trace_xen_device_realize(type, xendev->name);
- xendev->xsh = xs_open(0);
+ xendev->xsh = qemu_xen_xs_open();
if (!xendev->xsh) {
error_setg_errno(errp, errno, "failed xs_open");
goto unrealize;
}
- xendev->watch_list = watch_list_create(xendev->xsh);
-
- xendev->xgth = xengnttab_open(NULL, 0);
+ xendev->xgth = qemu_xen_gnttab_open();
if (!xendev->xgth) {
error_setg_errno(errp, errno, "failed xengnttab_open");
goto unrealize;
}
- xendev->feature_grant_copy =
- (xengnttab_grant_copy(xendev->xgth, 0, NULL) == 0);
-
xen_device_backend_create(xendev, errp);
if (*errp) {
goto unrealize;
@@ -1317,13 +1034,6 @@
goto unrealize;
}
- if (xendev_class->realize) {
- xendev_class->realize(xendev, errp);
- if (*errp) {
- goto unrealize;
- }
- }
-
xen_device_backend_printf(xendev, "frontend", "%s",
xendev->frontend_path);
xen_device_backend_printf(xendev, "frontend-id", "%u",
@@ -1342,6 +1052,13 @@
xen_device_frontend_set_state(xendev, XenbusStateInitialising, true);
}
+ if (xendev_class->realize) {
+ xendev_class->realize(xendev, errp);
+ if (*errp) {
+ goto unrealize;
+ }
+ }
+
xendev->exit.notify = xen_device_exit;
qemu_add_exit_notifier(&xendev->exit);
return;
diff --git a/hw/xen/xen-legacy-backend.c b/hw/xen/xen-legacy-backend.c
index afba71f..4ded3ce 100644
--- a/hw/xen/xen-legacy-backend.c
+++ b/hw/xen/xen-legacy-backend.c
@@ -39,11 +39,10 @@
/* ------------------------------------------------------------- */
/* public */
-struct xs_handle *xenstore;
+struct qemu_xs_handle *xenstore;
const char *xen_protocol;
/* private */
-static bool xen_feature_grant_copy;
static int debug;
int xenstore_write_be_str(struct XenLegacyDevice *xendev, const char *node,
@@ -113,7 +112,7 @@
{
assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV);
- if (xengnttab_set_max_grants(xendev->gnttabdev, nr_refs)) {
+ if (qemu_xen_gnttab_set_max_grants(xendev->gnttabdev, nr_refs)) {
xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n",
strerror(errno));
}
@@ -126,8 +125,8 @@
assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV);
- ptr = xengnttab_map_domain_grant_refs(xendev->gnttabdev, nr_refs,
- xen_domid, refs, prot);
+ ptr = qemu_xen_gnttab_map_refs(xendev->gnttabdev, nr_refs, xen_domid, refs,
+ prot);
if (!ptr) {
xen_pv_printf(xendev, 0,
"xengnttab_map_domain_grant_refs failed: %s\n",
@@ -138,123 +137,31 @@
}
void xen_be_unmap_grant_refs(struct XenLegacyDevice *xendev, void *ptr,
- unsigned int nr_refs)
+ uint32_t *refs, unsigned int nr_refs)
{
assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV);
- if (xengnttab_unmap(xendev->gnttabdev, ptr, nr_refs)) {
+ if (qemu_xen_gnttab_unmap(xendev->gnttabdev, ptr, refs, nr_refs)) {
xen_pv_printf(xendev, 0, "xengnttab_unmap failed: %s\n",
strerror(errno));
}
}
-static int compat_copy_grant_refs(struct XenLegacyDevice *xendev,
- bool to_domain,
- XenGrantCopySegment segs[],
- unsigned int nr_segs)
-{
- uint32_t *refs = g_new(uint32_t, nr_segs);
- int prot = to_domain ? PROT_WRITE : PROT_READ;
- void *pages;
- unsigned int i;
-
- for (i = 0; i < nr_segs; i++) {
- XenGrantCopySegment *seg = &segs[i];
-
- refs[i] = to_domain ?
- seg->dest.foreign.ref : seg->source.foreign.ref;
- }
-
- pages = xengnttab_map_domain_grant_refs(xendev->gnttabdev, nr_segs,
- xen_domid, refs, prot);
- if (!pages) {
- xen_pv_printf(xendev, 0,
- "xengnttab_map_domain_grant_refs failed: %s\n",
- strerror(errno));
- g_free(refs);
- return -1;
- }
-
- for (i = 0; i < nr_segs; i++) {
- XenGrantCopySegment *seg = &segs[i];
- void *page = pages + (i * XC_PAGE_SIZE);
-
- if (to_domain) {
- memcpy(page + seg->dest.foreign.offset, seg->source.virt,
- seg->len);
- } else {
- memcpy(seg->dest.virt, page + seg->source.foreign.offset,
- seg->len);
- }
- }
-
- if (xengnttab_unmap(xendev->gnttabdev, pages, nr_segs)) {
- xen_pv_printf(xendev, 0, "xengnttab_unmap failed: %s\n",
- strerror(errno));
- }
-
- g_free(refs);
- return 0;
-}
-
int xen_be_copy_grant_refs(struct XenLegacyDevice *xendev,
bool to_domain,
XenGrantCopySegment segs[],
unsigned int nr_segs)
{
- xengnttab_grant_copy_segment_t *xengnttab_segs;
- unsigned int i;
int rc;
assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV);
- if (!xen_feature_grant_copy) {
- return compat_copy_grant_refs(xendev, to_domain, segs, nr_segs);
- }
-
- xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs);
-
- for (i = 0; i < nr_segs; i++) {
- XenGrantCopySegment *seg = &segs[i];
- xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i];
-
- if (to_domain) {
- xengnttab_seg->flags = GNTCOPY_dest_gref;
- xengnttab_seg->dest.foreign.domid = xen_domid;
- xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref;
- xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset;
- xengnttab_seg->source.virt = seg->source.virt;
- } else {
- xengnttab_seg->flags = GNTCOPY_source_gref;
- xengnttab_seg->source.foreign.domid = xen_domid;
- xengnttab_seg->source.foreign.ref = seg->source.foreign.ref;
- xengnttab_seg->source.foreign.offset =
- seg->source.foreign.offset;
- xengnttab_seg->dest.virt = seg->dest.virt;
- }
-
- xengnttab_seg->len = seg->len;
- }
-
- rc = xengnttab_grant_copy(xendev->gnttabdev, nr_segs, xengnttab_segs);
-
+ rc = qemu_xen_gnttab_grant_copy(xendev->gnttabdev, to_domain, xen_domid,
+ segs, nr_segs, NULL);
if (rc) {
- xen_pv_printf(xendev, 0, "xengnttab_copy failed: %s\n",
- strerror(errno));
+ xen_pv_printf(xendev, 0, "xengnttab_grant_copy failed: %s\n",
+ strerror(-rc));
}
-
- for (i = 0; i < nr_segs; i++) {
- xengnttab_grant_copy_segment_t *xengnttab_seg =
- &xengnttab_segs[i];
-
- if (xengnttab_seg->status != GNTST_okay) {
- xen_pv_printf(xendev, 0, "segment[%u] status: %d\n", i,
- xengnttab_seg->status);
- rc = -1;
- }
- }
-
- g_free(xengnttab_segs);
return rc;
}
@@ -294,13 +201,13 @@
xendev->debug = debug;
xendev->local_port = -1;
- xendev->evtchndev = xenevtchn_open(NULL, 0);
+ xendev->evtchndev = qemu_xen_evtchn_open();
if (xendev->evtchndev == NULL) {
xen_pv_printf(NULL, 0, "can't open evtchn device\n");
qdev_unplug(DEVICE(xendev), NULL);
return NULL;
}
- qemu_set_cloexec(xenevtchn_fd(xendev->evtchndev));
+ qemu_set_cloexec(qemu_xen_evtchn_fd(xendev->evtchndev));
xen_pv_insert_xendev(xendev);
@@ -367,6 +274,25 @@
}
}
+static void xenstore_update_fe(void *opaque, const char *watch)
+{
+ struct XenLegacyDevice *xendev = opaque;
+ const char *node;
+ unsigned int len;
+
+ len = strlen(xendev->fe);
+ if (strncmp(xendev->fe, watch, len) != 0) {
+ return;
+ }
+ if (watch[len] != '/') {
+ return;
+ }
+ node = watch + len + 1;
+
+ xen_be_frontend_changed(xendev, node);
+ xen_be_check_state(xendev);
+}
+
/* ------------------------------------------------------------- */
/* Check for possible state transitions and perform them. */
@@ -380,7 +306,6 @@
*/
static int xen_be_try_setup(struct XenLegacyDevice *xendev)
{
- char token[XEN_BUFSIZE];
int be_state;
if (xenstore_read_be_int(xendev, "state", &be_state) == -1) {
@@ -401,8 +326,9 @@
}
/* setup frontend watch */
- snprintf(token, sizeof(token), "fe:%p", xendev);
- if (!xs_watch(xenstore, xendev->fe, token)) {
+ xendev->watch = qemu_xen_xs_watch(xenstore, xendev->fe, xenstore_update_fe,
+ xendev);
+ if (!xendev->watch) {
xen_pv_printf(xendev, 0, "watching frontend path (%s) failed\n",
xendev->fe);
return -1;
@@ -466,7 +392,7 @@
}
if (xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV) {
- xendev->gnttabdev = xengnttab_open(NULL, 0);
+ xendev->gnttabdev = qemu_xen_gnttab_open();
if (xendev->gnttabdev == NULL) {
xen_pv_printf(NULL, 0, "can't open gnttab device\n");
return -1;
@@ -524,7 +450,7 @@
xendev->ops->disconnect(xendev);
}
if (xendev->gnttabdev) {
- xengnttab_close(xendev->gnttabdev);
+ qemu_xen_gnttab_close(xendev->gnttabdev);
xendev->gnttabdev = NULL;
}
if (xendev->be_state != state) {
@@ -591,24 +517,67 @@
/* ------------------------------------------------------------- */
+struct xenstore_be {
+ const char *type;
+ int dom;
+ struct XenDevOps *ops;
+};
+
+static void xenstore_update_be(void *opaque, const char *watch)
+{
+ struct xenstore_be *be = opaque;
+ struct XenLegacyDevice *xendev;
+ char path[XEN_BUFSIZE], *bepath;
+ unsigned int len, dev;
+
+ len = snprintf(path, sizeof(path), "backend/%s/%d", be->type, be->dom);
+ if (strncmp(path, watch, len) != 0) {
+ return;
+ }
+ if (sscanf(watch + len, "/%u/%255s", &dev, path) != 2) {
+ strcpy(path, "");
+ if (sscanf(watch + len, "/%u", &dev) != 1) {
+ dev = -1;
+ }
+ }
+ if (dev == -1) {
+ return;
+ }
+
+ xendev = xen_be_get_xendev(be->type, be->dom, dev, be->ops);
+ if (xendev != NULL) {
+ bepath = qemu_xen_xs_read(xenstore, 0, xendev->be, &len);
+ if (bepath == NULL) {
+ xen_pv_del_xendev(xendev);
+ } else {
+ free(bepath);
+ xen_be_backend_changed(xendev, path);
+ xen_be_check_state(xendev);
+ }
+ }
+}
+
static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops)
{
struct XenLegacyDevice *xendev;
- char path[XEN_BUFSIZE], token[XEN_BUFSIZE];
+ char path[XEN_BUFSIZE];
+ struct xenstore_be *be = g_new0(struct xenstore_be, 1);
char **dev = NULL;
unsigned int cdev, j;
/* setup watch */
- snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops);
+ be->type = type;
+ be->dom = dom;
+ be->ops = ops;
snprintf(path, sizeof(path), "backend/%s/%d", type, dom);
- if (!xs_watch(xenstore, path, token)) {
+ if (!qemu_xen_xs_watch(xenstore, path, xenstore_update_be, be)) {
xen_pv_printf(NULL, 0, "xen be: watching backend path (%s) failed\n",
path);
return -1;
}
/* look for backends */
- dev = xs_directory(xenstore, 0, path, &cdev);
+ dev = qemu_xen_xs_directory(xenstore, 0, path, &cdev);
if (!dev) {
return 0;
}
@@ -623,57 +592,6 @@
return 0;
}
-void xenstore_update_be(char *watch, char *type, int dom,
- struct XenDevOps *ops)
-{
- struct XenLegacyDevice *xendev;
- char path[XEN_BUFSIZE], *bepath;
- unsigned int len, dev;
-
- len = snprintf(path, sizeof(path), "backend/%s/%d", type, dom);
- if (strncmp(path, watch, len) != 0) {
- return;
- }
- if (sscanf(watch + len, "/%u/%255s", &dev, path) != 2) {
- strcpy(path, "");
- if (sscanf(watch + len, "/%u", &dev) != 1) {
- dev = -1;
- }
- }
- if (dev == -1) {
- return;
- }
-
- xendev = xen_be_get_xendev(type, dom, dev, ops);
- if (xendev != NULL) {
- bepath = xs_read(xenstore, 0, xendev->be, &len);
- if (bepath == NULL) {
- xen_pv_del_xendev(xendev);
- } else {
- free(bepath);
- xen_be_backend_changed(xendev, path);
- xen_be_check_state(xendev);
- }
- }
-}
-
-void xenstore_update_fe(char *watch, struct XenLegacyDevice *xendev)
-{
- char *node;
- unsigned int len;
-
- len = strlen(xendev->fe);
- if (strncmp(xendev->fe, watch, len) != 0) {
- return;
- }
- if (watch[len] != '/') {
- return;
- }
- node = watch + len + 1;
-
- xen_be_frontend_changed(xendev, node);
- xen_be_check_state(xendev);
-}
/* -------------------------------------------------------------------- */
static void xen_set_dynamic_sysbus(void)
@@ -687,29 +605,17 @@
void xen_be_init(void)
{
- xengnttab_handle *gnttabdev;
-
- xenstore = xs_daemon_open();
+ xenstore = qemu_xen_xs_open();
if (!xenstore) {
xen_pv_printf(NULL, 0, "can't connect to xenstored\n");
exit(1);
}
- qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL);
-
- if (xen_xc == NULL || xen_fmem == NULL) {
+ if (xen_evtchn_ops == NULL || xen_gnttab_ops == NULL) {
xen_pv_printf(NULL, 0, "Xen operations not set up\n");
exit(1);
}
- gnttabdev = xengnttab_open(NULL, 0);
- if (gnttabdev != NULL) {
- if (xengnttab_grant_copy(gnttabdev, 0, NULL) == 0) {
- xen_feature_grant_copy = true;
- }
- xengnttab_close(gnttabdev);
- }
-
xen_sysdev = qdev_new(TYPE_XENSYSDEV);
sysbus_realize_and_unref(SYS_BUS_DEVICE(xen_sysdev), &error_fatal);
xen_sysbus = qbus_new(TYPE_XENSYSBUS, xen_sysdev, "xen-sysbus");
@@ -751,14 +657,14 @@
if (xendev->local_port != -1) {
return 0;
}
- xendev->local_port = xenevtchn_bind_interdomain
+ xendev->local_port = qemu_xen_evtchn_bind_interdomain
(xendev->evtchndev, xendev->dom, xendev->remote_port);
if (xendev->local_port == -1) {
xen_pv_printf(xendev, 0, "xenevtchn_bind_interdomain failed\n");
return -1;
}
xen_pv_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port);
- qemu_set_fd_handler(xenevtchn_fd(xendev->evtchndev),
+ qemu_set_fd_handler(qemu_xen_evtchn_fd(xendev->evtchndev),
xen_pv_evtchn_event, NULL, xendev);
return 0;
}
diff --git a/hw/xen/xen-operations.c b/hw/xen/xen-operations.c
new file mode 100644
index 0000000..4b78fbf
--- /dev/null
+++ b/hw/xen/xen-operations.c
@@ -0,0 +1,478 @@
+/*
+ * QEMU Xen backend support: Operations for true Xen
+ *
+ * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Authors: David Woodhouse <dwmw2@infradead.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/uuid.h"
+#include "qapi/error.h"
+
+#include "hw/xen/xen_native.h"
+#include "hw/xen/xen_backend_ops.h"
+
+/*
+ * If we have new enough libxenctrl then we do not want/need these compat
+ * interfaces, despite what the user supplied cflags might say. They
+ * must be undefined before including xenctrl.h
+ */
+#undef XC_WANT_COMPAT_EVTCHN_API
+#undef XC_WANT_COMPAT_GNTTAB_API
+#undef XC_WANT_COMPAT_MAP_FOREIGN_API
+
+#include <xenctrl.h>
+
+/*
+ * We don't support Xen prior to 4.2.0.
+ */
+
+/* Xen 4.2 through 4.6 */
+#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40701
+
+typedef xc_evtchn xenevtchn_handle;
+typedef evtchn_port_or_error_t xenevtchn_port_or_error_t;
+
+#define xenevtchn_open(l, f) xc_evtchn_open(l, f);
+#define xenevtchn_close(h) xc_evtchn_close(h)
+#define xenevtchn_fd(h) xc_evtchn_fd(h)
+#define xenevtchn_pending(h) xc_evtchn_pending(h)
+#define xenevtchn_notify(h, p) xc_evtchn_notify(h, p)
+#define xenevtchn_bind_interdomain(h, d, p) xc_evtchn_bind_interdomain(h, d, p)
+#define xenevtchn_unmask(h, p) xc_evtchn_unmask(h, p)
+#define xenevtchn_unbind(h, p) xc_evtchn_unbind(h, p)
+
+typedef xc_gnttab xengnttab_handle;
+
+#define xengnttab_open(l, f) xc_gnttab_open(l, f)
+#define xengnttab_close(h) xc_gnttab_close(h)
+#define xengnttab_set_max_grants(h, n) xc_gnttab_set_max_grants(h, n)
+#define xengnttab_map_grant_ref(h, d, r, p) xc_gnttab_map_grant_ref(h, d, r, p)
+#define xengnttab_unmap(h, a, n) xc_gnttab_munmap(h, a, n)
+#define xengnttab_map_grant_refs(h, c, d, r, p) \
+ xc_gnttab_map_grant_refs(h, c, d, r, p)
+#define xengnttab_map_domain_grant_refs(h, c, d, r, p) \
+ xc_gnttab_map_domain_grant_refs(h, c, d, r, p)
+
+typedef xc_interface xenforeignmemory_handle;
+
+#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40701 */
+
+#include <xenevtchn.h>
+#include <xengnttab.h>
+#include <xenforeignmemory.h>
+
+#endif
+
+/* Xen before 4.8 */
+
+static int libxengnttab_fallback_grant_copy(xengnttab_handle *xgt,
+ bool to_domain, uint32_t domid,
+ XenGrantCopySegment segs[],
+ unsigned int nr_segs, Error **errp)
+{
+ uint32_t *refs = g_new(uint32_t, nr_segs);
+ int prot = to_domain ? PROT_WRITE : PROT_READ;
+ void *map;
+ unsigned int i;
+ int rc = 0;
+
+ for (i = 0; i < nr_segs; i++) {
+ XenGrantCopySegment *seg = &segs[i];
+
+ refs[i] = to_domain ? seg->dest.foreign.ref :
+ seg->source.foreign.ref;
+ }
+ map = xengnttab_map_domain_grant_refs(xgt, nr_segs, domid, refs, prot);
+ if (!map) {
+ if (errp) {
+ error_setg_errno(errp, errno,
+ "xengnttab_map_domain_grant_refs failed");
+ }
+ rc = -errno;
+ goto done;
+ }
+
+ for (i = 0; i < nr_segs; i++) {
+ XenGrantCopySegment *seg = &segs[i];
+ void *page = map + (i * XEN_PAGE_SIZE);
+
+ if (to_domain) {
+ memcpy(page + seg->dest.foreign.offset, seg->source.virt,
+ seg->len);
+ } else {
+ memcpy(seg->dest.virt, page + seg->source.foreign.offset,
+ seg->len);
+ }
+ }
+
+ if (xengnttab_unmap(xgt, map, nr_segs)) {
+ if (errp) {
+ error_setg_errno(errp, errno, "xengnttab_unmap failed");
+ }
+ rc = -errno;
+ }
+
+done:
+ g_free(refs);
+ return rc;
+}
+
+#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40800
+
+static int libxengnttab_backend_grant_copy(xengnttab_handle *xgt,
+ bool to_domain, uint32_t domid,
+ XenGrantCopySegment *segs,
+ uint32_t nr_segs, Error **errp)
+{
+ xengnttab_grant_copy_segment_t *xengnttab_segs;
+ unsigned int i;
+ int rc;
+
+ xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs);
+
+ for (i = 0; i < nr_segs; i++) {
+ XenGrantCopySegment *seg = &segs[i];
+ xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i];
+
+ if (to_domain) {
+ xengnttab_seg->flags = GNTCOPY_dest_gref;
+ xengnttab_seg->dest.foreign.domid = domid;
+ xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref;
+ xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset;
+ xengnttab_seg->source.virt = seg->source.virt;
+ } else {
+ xengnttab_seg->flags = GNTCOPY_source_gref;
+ xengnttab_seg->source.foreign.domid = domid;
+ xengnttab_seg->source.foreign.ref = seg->source.foreign.ref;
+ xengnttab_seg->source.foreign.offset =
+ seg->source.foreign.offset;
+ xengnttab_seg->dest.virt = seg->dest.virt;
+ }
+
+ xengnttab_seg->len = seg->len;
+ }
+
+ if (xengnttab_grant_copy(xgt, nr_segs, xengnttab_segs)) {
+ if (errp) {
+ error_setg_errno(errp, errno, "xengnttab_grant_copy failed");
+ }
+ rc = -errno;
+ goto done;
+ }
+
+ rc = 0;
+ for (i = 0; i < nr_segs; i++) {
+ xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i];
+
+ if (xengnttab_seg->status != GNTST_okay) {
+ if (errp) {
+ error_setg(errp, "xengnttab_grant_copy seg[%u] failed", i);
+ }
+ rc = -EIO;
+ break;
+ }
+ }
+
+done:
+ g_free(xengnttab_segs);
+ return rc;
+}
+#endif
+
+static xenevtchn_handle *libxenevtchn_backend_open(void)
+{
+ return xenevtchn_open(NULL, 0);
+}
+
+struct evtchn_backend_ops libxenevtchn_backend_ops = {
+ .open = libxenevtchn_backend_open,
+ .close = xenevtchn_close,
+ .bind_interdomain = xenevtchn_bind_interdomain,
+ .unbind = xenevtchn_unbind,
+ .get_fd = xenevtchn_fd,
+ .notify = xenevtchn_notify,
+ .unmask = xenevtchn_unmask,
+ .pending = xenevtchn_pending,
+};
+
+static xengnttab_handle *libxengnttab_backend_open(void)
+{
+ return xengnttab_open(NULL, 0);
+}
+
+static int libxengnttab_backend_unmap(xengnttab_handle *xgt,
+ void *start_address, uint32_t *refs,
+ uint32_t count)
+{
+ return xengnttab_unmap(xgt, start_address, count);
+}
+
+
+static struct gnttab_backend_ops libxengnttab_backend_ops = {
+ .features = XEN_GNTTAB_OP_FEATURE_MAP_MULTIPLE,
+ .open = libxengnttab_backend_open,
+ .close = xengnttab_close,
+ .grant_copy = libxengnttab_fallback_grant_copy,
+ .set_max_grants = xengnttab_set_max_grants,
+ .map_refs = xengnttab_map_domain_grant_refs,
+ .unmap = libxengnttab_backend_unmap,
+};
+
+#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40701
+
+static void *libxenforeignmem_backend_map(uint32_t dom, void *addr, int prot,
+ size_t pages, xfn_pfn_t *pfns,
+ int *errs)
+{
+ if (errs) {
+ return xc_map_foreign_bulk(xen_xc, dom, prot, pfns, errs, pages);
+ } else {
+ return xc_map_foreign_pages(xen_xc, dom, prot, pfns, pages);
+ }
+}
+
+static int libxenforeignmem_backend_unmap(void *addr, size_t pages)
+{
+ return munmap(addr, pages * XC_PAGE_SIZE);
+}
+
+#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40701 */
+
+static void *libxenforeignmem_backend_map(uint32_t dom, void *addr, int prot,
+ size_t pages, xen_pfn_t *pfns,
+ int *errs)
+{
+ return xenforeignmemory_map2(xen_fmem, dom, addr, prot, 0, pages, pfns,
+ errs);
+}
+
+static int libxenforeignmem_backend_unmap(void *addr, size_t pages)
+{
+ return xenforeignmemory_unmap(xen_fmem, addr, pages);
+}
+
+#endif
+
+struct foreignmem_backend_ops libxenforeignmem_backend_ops = {
+ .map = libxenforeignmem_backend_map,
+ .unmap = libxenforeignmem_backend_unmap,
+};
+
+struct qemu_xs_handle {
+ struct xs_handle *xsh;
+ NotifierList notifiers;
+};
+
+static void watch_event(void *opaque)
+{
+ struct qemu_xs_handle *h = opaque;
+
+ for (;;) {
+ char **v = xs_check_watch(h->xsh);
+
+ if (!v) {
+ break;
+ }
+
+ notifier_list_notify(&h->notifiers, v);
+ free(v);
+ }
+}
+
+static struct qemu_xs_handle *libxenstore_open(void)
+{
+ struct xs_handle *xsh = xs_open(0);
+ struct qemu_xs_handle *h = g_new0(struct qemu_xs_handle, 1);
+
+ if (!xsh) {
+ return NULL;
+ }
+
+ h = g_new0(struct qemu_xs_handle, 1);
+ h->xsh = xsh;
+
+ notifier_list_init(&h->notifiers);
+ qemu_set_fd_handler(xs_fileno(h->xsh), watch_event, NULL, h);
+
+ return h;
+}
+
+static void libxenstore_close(struct qemu_xs_handle *h)
+{
+ g_assert(notifier_list_empty(&h->notifiers));
+ qemu_set_fd_handler(xs_fileno(h->xsh), NULL, NULL, NULL);
+ xs_close(h->xsh);
+ g_free(h);
+}
+
+static char *libxenstore_get_domain_path(struct qemu_xs_handle *h,
+ unsigned int domid)
+{
+ return xs_get_domain_path(h->xsh, domid);
+}
+
+static char **libxenstore_directory(struct qemu_xs_handle *h,
+ xs_transaction_t t, const char *path,
+ unsigned int *num)
+{
+ return xs_directory(h->xsh, t, path, num);
+}
+
+static void *libxenstore_read(struct qemu_xs_handle *h, xs_transaction_t t,
+ const char *path, unsigned int *len)
+{
+ return xs_read(h->xsh, t, path, len);
+}
+
+static bool libxenstore_write(struct qemu_xs_handle *h, xs_transaction_t t,
+ const char *path, const void *data,
+ unsigned int len)
+{
+ return xs_write(h->xsh, t, path, data, len);
+}
+
+static bool libxenstore_create(struct qemu_xs_handle *h, xs_transaction_t t,
+ unsigned int owner, unsigned int domid,
+ unsigned int perms, const char *path)
+{
+ struct xs_permissions perms_list[] = {
+ {
+ .id = owner,
+ .perms = XS_PERM_NONE,
+ },
+ {
+ .id = domid,
+ .perms = perms,
+ },
+ };
+
+ if (!xs_mkdir(h->xsh, t, path)) {
+ return false;
+ }
+
+ return xs_set_permissions(h->xsh, t, path, perms_list,
+ ARRAY_SIZE(perms_list));
+}
+
+static bool libxenstore_destroy(struct qemu_xs_handle *h, xs_transaction_t t,
+ const char *path)
+{
+ return xs_rm(h->xsh, t, path);
+}
+
+struct qemu_xs_watch {
+ char *path;
+ char *token;
+ xs_watch_fn fn;
+ void *opaque;
+ Notifier notifier;
+};
+
+static void watch_notify(Notifier *n, void *data)
+{
+ struct qemu_xs_watch *w = container_of(n, struct qemu_xs_watch, notifier);
+ const char **v = data;
+
+ if (!strcmp(w->token, v[XS_WATCH_TOKEN])) {
+ w->fn(w->opaque, v[XS_WATCH_PATH]);
+ }
+}
+
+static struct qemu_xs_watch *new_watch(const char *path, xs_watch_fn fn,
+ void *opaque)
+{
+ struct qemu_xs_watch *w = g_new0(struct qemu_xs_watch, 1);
+ QemuUUID uuid;
+
+ qemu_uuid_generate(&uuid);
+
+ w->token = qemu_uuid_unparse_strdup(&uuid);
+ w->path = g_strdup(path);
+ w->fn = fn;
+ w->opaque = opaque;
+ w->notifier.notify = watch_notify;
+
+ return w;
+}
+
+static void free_watch(struct qemu_xs_watch *w)
+{
+ g_free(w->token);
+ g_free(w->path);
+
+ g_free(w);
+}
+
+static struct qemu_xs_watch *libxenstore_watch(struct qemu_xs_handle *h,
+ const char *path, xs_watch_fn fn,
+ void *opaque)
+{
+ struct qemu_xs_watch *w = new_watch(path, fn, opaque);
+
+ notifier_list_add(&h->notifiers, &w->notifier);
+
+ if (!xs_watch(h->xsh, path, w->token)) {
+ notifier_remove(&w->notifier);
+ free_watch(w);
+ return NULL;
+ }
+
+ return w;
+}
+
+static void libxenstore_unwatch(struct qemu_xs_handle *h,
+ struct qemu_xs_watch *w)
+{
+ xs_unwatch(h->xsh, w->path, w->token);
+ notifier_remove(&w->notifier);
+ free_watch(w);
+}
+
+static xs_transaction_t libxenstore_transaction_start(struct qemu_xs_handle *h)
+{
+ return xs_transaction_start(h->xsh);
+}
+
+static bool libxenstore_transaction_end(struct qemu_xs_handle *h,
+ xs_transaction_t t, bool abort)
+{
+ return xs_transaction_end(h->xsh, t, abort);
+}
+
+struct xenstore_backend_ops libxenstore_backend_ops = {
+ .open = libxenstore_open,
+ .close = libxenstore_close,
+ .get_domain_path = libxenstore_get_domain_path,
+ .directory = libxenstore_directory,
+ .read = libxenstore_read,
+ .write = libxenstore_write,
+ .create = libxenstore_create,
+ .destroy = libxenstore_destroy,
+ .watch = libxenstore_watch,
+ .unwatch = libxenstore_unwatch,
+ .transaction_start = libxenstore_transaction_start,
+ .transaction_end = libxenstore_transaction_end,
+};
+
+void setup_xen_backend_ops(void)
+{
+#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40800
+ xengnttab_handle *xgt = xengnttab_open(NULL, 0);
+
+ if (xgt) {
+ if (xengnttab_grant_copy(xgt, 0, NULL) == 0) {
+ libxengnttab_backend_ops.grant_copy = libxengnttab_backend_grant_copy;
+ }
+ xengnttab_close(xgt);
+ }
+#endif
+ xen_evtchn_ops = &libxenevtchn_backend_ops;
+ xen_gnttab_ops = &libxengnttab_backend_ops;
+ xen_foreignmem_ops = &libxenforeignmem_backend_ops;
+ xen_xenstore_ops = &libxenstore_backend_ops;
+}
diff --git a/hw/xen/xen_devconfig.c b/hw/xen/xen_devconfig.c
index 46ee4a7..9b7304e 100644
--- a/hw/xen/xen_devconfig.c
+++ b/hw/xen/xen_devconfig.c
@@ -11,11 +11,11 @@
{
char *dom;
- dom = xs_get_domain_path(xenstore, xen_domid);
+ dom = qemu_xen_xs_get_domain_path(xenstore, xen_domid);
snprintf(fe, len, "%s/device/%s/%d", dom, ftype, vdev);
free(dom);
- dom = xs_get_domain_path(xenstore, 0);
+ dom = qemu_xen_xs_get_domain_path(xenstore, 0);
snprintf(be, len, "%s/backend/%s/%d/%d", dom, btype, xen_domid, vdev);
free(dom);
diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c
index 85c93cf..2d33d17 100644
--- a/hw/xen/xen_pt.c
+++ b/hw/xen/xen_pt.c
@@ -60,9 +60,9 @@
#include "hw/pci/pci_bus.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
+#include "xen_pt.h"
#include "hw/xen/xen.h"
#include "hw/xen/xen-legacy-backend.h"
-#include "xen_pt.h"
#include "qemu/range.h"
static bool has_igd_gfx_passthru;
diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h
index e184699..b20744f 100644
--- a/hw/xen/xen_pt.h
+++ b/hw/xen/xen_pt.h
@@ -1,7 +1,7 @@
#ifndef XEN_PT_H
#define XEN_PT_H
-#include "hw/xen/xen_common.h"
+#include "hw/xen/xen_native.h"
#include "xen-host-pci-device.h"
#include "qom/object.h"
diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c
index 8b9b554..2b8680b 100644
--- a/hw/xen/xen_pt_config_init.c
+++ b/hw/xen/xen_pt_config_init.c
@@ -15,8 +15,8 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu/timer.h"
-#include "hw/xen/xen-legacy-backend.h"
#include "xen_pt.h"
+#include "hw/xen/xen-legacy-backend.h"
#define XEN_PT_MERGE_VALUE(value, data, val_mask) \
(((value) & (val_mask)) | ((data) & ~(val_mask)))
diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c
index f303f67..0aed3bb 100644
--- a/hw/xen/xen_pt_graphics.c
+++ b/hw/xen/xen_pt_graphics.c
@@ -5,7 +5,6 @@
#include "qapi/error.h"
#include "xen_pt.h"
#include "xen-host-pci-device.h"
-#include "hw/xen/xen-legacy-backend.h"
static unsigned long igd_guest_opregion;
static unsigned long igd_host_opregion;
diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c
index b71563f..09cca4e 100644
--- a/hw/xen/xen_pt_msi.c
+++ b/hw/xen/xen_pt_msi.c
@@ -11,9 +11,9 @@
#include "qemu/osdep.h"
-#include "hw/xen/xen-legacy-backend.h"
-#include "xen_pt.h"
#include "hw/i386/apic-msidef.h"
+#include "xen_pt.h"
+#include "hw/xen/xen-legacy-backend.h"
#define XEN_PT_AUTO_ASSIGN -1
diff --git a/hw/xen/xen_pvdev.c b/hw/xen/xen_pvdev.c
index 1a5177b..be1504b 100644
--- a/hw/xen/xen_pvdev.c
+++ b/hw/xen/xen_pvdev.c
@@ -54,31 +54,17 @@
struct xs_dirs *d;
QTAILQ_FOREACH(d, &xs_cleanup, list) {
- xs_rm(xenstore, 0, d->xs_dir);
+ qemu_xen_xs_destroy(xenstore, 0, d->xs_dir);
}
}
int xenstore_mkdir(char *path, int p)
{
- struct xs_permissions perms[2] = {
- {
- .id = 0, /* set owner: dom0 */
- }, {
- .id = xen_domid,
- .perms = p,
- }
- };
-
- if (!xs_mkdir(xenstore, 0, path)) {
+ if (!qemu_xen_xs_create(xenstore, 0, 0, xen_domid, p, path)) {
xen_pv_printf(NULL, 0, "xs_mkdir %s: failed\n", path);
return -1;
}
xenstore_cleanup_dir(g_strdup(path));
-
- if (!xs_set_permissions(xenstore, 0, path, perms, 2)) {
- xen_pv_printf(NULL, 0, "xs_set_permissions %s: failed\n", path);
- return -1;
- }
return 0;
}
@@ -87,7 +73,7 @@
char abspath[XEN_BUFSIZE];
snprintf(abspath, sizeof(abspath), "%s/%s", base, node);
- if (!xs_write(xenstore, 0, abspath, val, strlen(val))) {
+ if (!qemu_xen_xs_write(xenstore, 0, abspath, val, strlen(val))) {
return -1;
}
return 0;
@@ -100,7 +86,7 @@
char *str, *ret = NULL;
snprintf(abspath, sizeof(abspath), "%s/%s", base, node);
- str = xs_read(xenstore, 0, abspath, &len);
+ str = qemu_xen_xs_read(xenstore, 0, abspath, &len);
if (str != NULL) {
/* move to qemu-allocated memory to make sure
* callers can savely g_free() stuff. */
@@ -152,29 +138,6 @@
return rc;
}
-void xenstore_update(void *unused)
-{
- char **vec = NULL;
- intptr_t type, ops, ptr;
- unsigned int dom, count;
-
- vec = xs_read_watch(xenstore, &count);
- if (vec == NULL) {
- goto cleanup;
- }
-
- if (sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR,
- &type, &dom, &ops) == 3) {
- xenstore_update_be(vec[XS_WATCH_PATH], (void *)type, dom, (void*)ops);
- }
- if (sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr) == 1) {
- xenstore_update_fe(vec[XS_WATCH_PATH], (void *)ptr);
- }
-
-cleanup:
- free(vec);
-}
-
const char *xenbus_strstate(enum xenbus_state state)
{
static const char *const name[] = {
@@ -238,14 +201,14 @@
struct XenLegacyDevice *xendev = opaque;
evtchn_port_t port;
- port = xenevtchn_pending(xendev->evtchndev);
+ port = qemu_xen_evtchn_pending(xendev->evtchndev);
if (port != xendev->local_port) {
xen_pv_printf(xendev, 0,
"xenevtchn_pending returned %d (expected %d)\n",
port, xendev->local_port);
return;
}
- xenevtchn_unmask(xendev->evtchndev, port);
+ qemu_xen_evtchn_unmask(xendev->evtchndev, port);
if (xendev->ops->event) {
xendev->ops->event(xendev);
@@ -257,15 +220,15 @@
if (xendev->local_port == -1) {
return;
}
- qemu_set_fd_handler(xenevtchn_fd(xendev->evtchndev), NULL, NULL, NULL);
- xenevtchn_unbind(xendev->evtchndev, xendev->local_port);
+ qemu_set_fd_handler(qemu_xen_evtchn_fd(xendev->evtchndev), NULL, NULL, NULL);
+ qemu_xen_evtchn_unbind(xendev->evtchndev, xendev->local_port);
xen_pv_printf(xendev, 2, "unbind evtchn port %d\n", xendev->local_port);
xendev->local_port = -1;
}
int xen_pv_send_notify(struct XenLegacyDevice *xendev)
{
- return xenevtchn_notify(xendev->evtchndev, xendev->local_port);
+ return qemu_xen_evtchn_notify(xendev->evtchndev, xendev->local_port);
}
/* ------------------------------------------------------------- */
@@ -299,17 +262,15 @@
}
if (xendev->fe) {
- char token[XEN_BUFSIZE];
- snprintf(token, sizeof(token), "fe:%p", xendev);
- xs_unwatch(xenstore, xendev->fe, token);
+ qemu_xen_xs_unwatch(xenstore, xendev->watch);
g_free(xendev->fe);
}
if (xendev->evtchndev != NULL) {
- xenevtchn_close(xendev->evtchndev);
+ qemu_xen_evtchn_close(xendev->evtchndev);
}
if (xendev->gnttabdev != NULL) {
- xengnttab_close(xendev->gnttabdev);
+ qemu_xen_gnttab_close(xendev->gnttabdev);
}
QTAILQ_REMOVE(&xendevs, xendev, next);
diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h
index dd9a7f6..da13357 100644
--- a/include/block/aio-wait.h
+++ b/include/block/aio-wait.h
@@ -85,7 +85,7 @@
/* Increment wait_->num_waiters before evaluating cond. */ \
qatomic_inc(&wait_->num_waiters); \
/* Paired with smp_mb in aio_wait_kick(). */ \
- smp_mb(); \
+ smp_mb__after_rmw(); \
if (ctx_ && in_aio_context_home_thread(ctx_)) { \
while ((cond)) { \
aio_poll(ctx_, true); \
diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h
index be920d4..cd8aa17 100644
--- a/include/exec/cpu-defs.h
+++ b/include/exec/cpu-defs.h
@@ -55,24 +55,7 @@
# endif
#endif
-#define TARGET_LONG_SIZE (TARGET_LONG_BITS / 8)
-
-/* target_ulong is the type of a virtual address */
-#if TARGET_LONG_SIZE == 4
-typedef int32_t target_long;
-typedef uint32_t target_ulong;
-#define TARGET_FMT_lx "%08x"
-#define TARGET_FMT_ld "%d"
-#define TARGET_FMT_lu "%u"
-#elif TARGET_LONG_SIZE == 8
-typedef int64_t target_long;
-typedef uint64_t target_ulong;
-#define TARGET_FMT_lx "%016" PRIx64
-#define TARGET_FMT_ld "%" PRId64
-#define TARGET_FMT_lu "%" PRIu64
-#else
-#error TARGET_LONG_SIZE undefined
-#endif
+#include "exec/target_long.h"
#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG)
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index e092543..ad9eb60 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -677,7 +677,6 @@
#else
void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs);
#endif
-void tb_flush(CPUState *cpu);
void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr);
void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end);
void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr);
diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
index f667014..7d743fe 100644
--- a/include/exec/gdbstub.h
+++ b/include/exec/gdbstub.h
@@ -10,122 +10,7 @@
#define GDB_WATCHPOINT_READ 3
#define GDB_WATCHPOINT_ACCESS 4
-/* For gdb file i/o remote protocol open flags. */
-#define GDB_O_RDONLY 0
-#define GDB_O_WRONLY 1
-#define GDB_O_RDWR 2
-#define GDB_O_APPEND 8
-#define GDB_O_CREAT 0x200
-#define GDB_O_TRUNC 0x400
-#define GDB_O_EXCL 0x800
-/* For gdb file i/o remote protocol errno values */
-#define GDB_EPERM 1
-#define GDB_ENOENT 2
-#define GDB_EINTR 4
-#define GDB_EBADF 9
-#define GDB_EACCES 13
-#define GDB_EFAULT 14
-#define GDB_EBUSY 16
-#define GDB_EEXIST 17
-#define GDB_ENODEV 19
-#define GDB_ENOTDIR 20
-#define GDB_EISDIR 21
-#define GDB_EINVAL 22
-#define GDB_ENFILE 23
-#define GDB_EMFILE 24
-#define GDB_EFBIG 27
-#define GDB_ENOSPC 28
-#define GDB_ESPIPE 29
-#define GDB_EROFS 30
-#define GDB_ENAMETOOLONG 91
-#define GDB_EUNKNOWN 9999
-
-/* For gdb file i/o remote protocol lseek whence. */
-#define GDB_SEEK_SET 0
-#define GDB_SEEK_CUR 1
-#define GDB_SEEK_END 2
-
-/* For gdb file i/o stat/fstat. */
-typedef uint32_t gdb_mode_t;
-typedef uint32_t gdb_time_t;
-
-struct gdb_stat {
- uint32_t gdb_st_dev; /* device */
- uint32_t gdb_st_ino; /* inode */
- gdb_mode_t gdb_st_mode; /* protection */
- uint32_t gdb_st_nlink; /* number of hard links */
- uint32_t gdb_st_uid; /* user ID of owner */
- uint32_t gdb_st_gid; /* group ID of owner */
- uint32_t gdb_st_rdev; /* device type (if inode device) */
- uint64_t gdb_st_size; /* total size, in bytes */
- uint64_t gdb_st_blksize; /* blocksize for filesystem I/O */
- uint64_t gdb_st_blocks; /* number of blocks allocated */
- gdb_time_t gdb_st_atime; /* time of last access */
- gdb_time_t gdb_st_mtime; /* time of last modification */
- gdb_time_t gdb_st_ctime; /* time of last change */
-} QEMU_PACKED;
-
-struct gdb_timeval {
- gdb_time_t tv_sec; /* second */
- uint64_t tv_usec; /* microsecond */
-} QEMU_PACKED;
-
-#ifdef NEED_CPU_H
-#include "cpu.h"
-
-typedef void (*gdb_syscall_complete_cb)(CPUState *cpu, uint64_t ret, int err);
-
-/**
- * gdb_do_syscall:
- * @cb: function to call when the system call has completed
- * @fmt: gdb syscall format string
- * ...: list of arguments to interpolate into @fmt
- *
- * Send a GDB syscall request. This function will return immediately;
- * the callback function will be called later when the remote system
- * call has completed.
- *
- * @fmt should be in the 'call-id,parameter,parameter...' format documented
- * for the F request packet in the GDB remote protocol. A limited set of
- * printf-style format specifiers is supported:
- * %x - target_ulong argument printed in hex
- * %lx - 64-bit argument printed in hex
- * %s - string pointer (target_ulong) and length (int) pair
- */
-void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...);
-/**
- * gdb_do_syscallv:
- * @cb: function to call when the system call has completed
- * @fmt: gdb syscall format string
- * @va: arguments to interpolate into @fmt
- *
- * As gdb_do_syscall, but taking a va_list rather than a variable
- * argument list.
- */
-void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va);
-int use_gdb_syscalls(void);
-
-#ifdef CONFIG_USER_ONLY
-/**
- * gdb_handlesig: yield control to gdb
- * @cpu: CPU
- * @sig: if non-zero, the signal number which caused us to stop
- *
- * This function yields control to gdb, when a user-mode-only target
- * needs to stop execution. If @sig is non-zero, then we will send a
- * stop packet to tell gdb that we have stopped because of this signal.
- *
- * This function will block (handling protocol requests from gdb)
- * until gdb tells us to continue target execution. When it does
- * return, the return value is a signal to deliver to the target,
- * or 0 if no signal should be delivered, ie the signal that caused
- * us to stop should be ignored.
- */
-int gdb_handlesig(CPUState *, int);
-void gdb_signalled(CPUArchState *, int);
-void gdbserver_fork(CPUState *);
-#endif
/* Get or set a register. Returns the size of the register. */
typedef int (*gdb_get_reg_cb)(CPUArchState *env, GByteArray *buf, int reg);
typedef int (*gdb_set_reg_cb)(CPUArchState *env, uint8_t *buf, int reg);
@@ -133,89 +18,6 @@
gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
int num_regs, const char *xml, int g_pos);
-/*
- * The GDB remote protocol transfers values in target byte order. As
- * the gdbstub may be batching up several register values we always
- * append to the array.
- */
-
-static inline int gdb_get_reg8(GByteArray *buf, uint8_t val)
-{
- g_byte_array_append(buf, &val, 1);
- return 1;
-}
-
-static inline int gdb_get_reg16(GByteArray *buf, uint16_t val)
-{
- uint16_t to_word = tswap16(val);
- g_byte_array_append(buf, (uint8_t *) &to_word, 2);
- return 2;
-}
-
-static inline int gdb_get_reg32(GByteArray *buf, uint32_t val)
-{
- uint32_t to_long = tswap32(val);
- g_byte_array_append(buf, (uint8_t *) &to_long, 4);
- return 4;
-}
-
-static inline int gdb_get_reg64(GByteArray *buf, uint64_t val)
-{
- uint64_t to_quad = tswap64(val);
- g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
- return 8;
-}
-
-static inline int gdb_get_reg128(GByteArray *buf, uint64_t val_hi,
- uint64_t val_lo)
-{
- uint64_t to_quad;
-#if TARGET_BIG_ENDIAN
- to_quad = tswap64(val_hi);
- g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
- to_quad = tswap64(val_lo);
- g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
-#else
- to_quad = tswap64(val_lo);
- g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
- to_quad = tswap64(val_hi);
- g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
-#endif
- return 16;
-}
-
-static inline int gdb_get_zeroes(GByteArray *array, size_t len)
-{
- guint oldlen = array->len;
- g_byte_array_set_size(array, oldlen + len);
- memset(array->data + oldlen, 0, len);
-
- return len;
-}
-
-/**
- * gdb_get_reg_ptr: get pointer to start of last element
- * @len: length of element
- *
- * This is a helper function to extract the pointer to the last
- * element for additional processing. Some front-ends do additional
- * dynamic swapping of the elements based on CPU state.
- */
-static inline uint8_t * gdb_get_reg_ptr(GByteArray *buf, int len)
-{
- return buf->data + buf->len - len;
-}
-
-#if TARGET_LONG_BITS == 64
-#define gdb_get_regl(buf, val) gdb_get_reg64(buf, val)
-#define ldtul_p(addr) ldq_p(addr)
-#else
-#define gdb_get_regl(buf, val) gdb_get_reg32(buf, val)
-#define ldtul_p(addr) ldl_p(addr)
-#endif
-
-#endif /* NEED_CPU_H */
-
/**
* gdbserver_start: start the gdb server
* @port_or_device: connection spec for gdb
@@ -226,16 +28,6 @@
*/
int gdbserver_start(const char *port_or_device);
-/**
- * gdb_exit: exit gdb session, reporting inferior status
- * @code: exit code reported
- *
- * This closes the session and sends a final packet to GDB reporting
- * the exit status of the program. It also cleans up any connections
- * detritus before returning.
- */
-void gdb_exit(int code);
-
void gdb_set_stop_cpu(CPUState *cpu);
/**
diff --git a/include/exec/target_long.h b/include/exec/target_long.h
new file mode 100644
index 0000000..93c9472
--- /dev/null
+++ b/include/exec/target_long.h
@@ -0,0 +1,42 @@
+/*
+ * Target Long Definitions
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2023 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _TARGET_LONG_H_
+#define _TARGET_LONG_H_
+
+/*
+ * Usually this should only be included via cpu-defs.h however for
+ * certain cases where we want to build only two versions of a binary
+ * object we can include directly. However the build-system must
+ * ensure TARGET_LONG_BITS is defined directly.
+ */
+#ifndef TARGET_LONG_BITS
+#error TARGET_LONG_BITS not defined
+#endif
+
+#define TARGET_LONG_SIZE (TARGET_LONG_BITS / 8)
+
+/* target_ulong is the type of a virtual address */
+#if TARGET_LONG_SIZE == 4
+typedef int32_t target_long;
+typedef uint32_t target_ulong;
+#define TARGET_FMT_lx "%08x"
+#define TARGET_FMT_ld "%d"
+#define TARGET_FMT_lu "%u"
+#elif TARGET_LONG_SIZE == 8
+typedef int64_t target_long;
+typedef uint64_t target_ulong;
+#define TARGET_FMT_lx "%016" PRIx64
+#define TARGET_FMT_ld "%" PRId64
+#define TARGET_FMT_lu "%" PRIu64
+#else
+#error TARGET_LONG_SIZE undefined
+#endif
+
+#endif /* _TARGET_LONG_H_ */
diff --git a/include/exec/tb-flush.h b/include/exec/tb-flush.h
new file mode 100644
index 0000000..d92d065
--- /dev/null
+++ b/include/exec/tb-flush.h
@@ -0,0 +1,26 @@
+/*
+ * tb-flush prototype for use by the rest of the system.
+ *
+ * Copyright (c) 2022 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef _TB_FLUSH_H_
+#define _TB_FLUSH_H_
+
+/**
+ * tb_flush() - flush all translation blocks
+ * @cs: CPUState (must be valid, but treated as anonymous pointer)
+ *
+ * Used to flush all the translation blocks in the system. Sometimes
+ * it is simpler to flush everything than work out which individual
+ * translations are now invalid and ensure they are not called
+ * anymore.
+ *
+ * tb_flush() takes care of running the flush in an exclusive context
+ * if it is not already running in one. This means no guest code will
+ * run until this complete.
+ */
+void tb_flush(CPUState *cs);
+
+#endif /* _TB_FLUSH_H_ */
diff --git a/include/gdbstub/helpers.h b/include/gdbstub/helpers.h
new file mode 100644
index 0000000..c573aef
--- /dev/null
+++ b/include/gdbstub/helpers.h
@@ -0,0 +1,103 @@
+/*
+ * gdbstub helpers
+ *
+ * These are all used by the various frontends and have to be host
+ * aware to ensure things are store in target order.
+ *
+ * Copyright (c) 2022 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _GDBSTUB_HELPERS_H_
+#define _GDBSTUB_HELPERS_H_
+
+#ifdef NEED_CPU_H
+#include "cpu.h"
+
+/*
+ * The GDB remote protocol transfers values in target byte order. As
+ * the gdbstub may be batching up several register values we always
+ * append to the array.
+ */
+
+static inline int gdb_get_reg8(GByteArray *buf, uint8_t val)
+{
+ g_byte_array_append(buf, &val, 1);
+ return 1;
+}
+
+static inline int gdb_get_reg16(GByteArray *buf, uint16_t val)
+{
+ uint16_t to_word = tswap16(val);
+ g_byte_array_append(buf, (uint8_t *) &to_word, 2);
+ return 2;
+}
+
+static inline int gdb_get_reg32(GByteArray *buf, uint32_t val)
+{
+ uint32_t to_long = tswap32(val);
+ g_byte_array_append(buf, (uint8_t *) &to_long, 4);
+ return 4;
+}
+
+static inline int gdb_get_reg64(GByteArray *buf, uint64_t val)
+{
+ uint64_t to_quad = tswap64(val);
+ g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
+ return 8;
+}
+
+static inline int gdb_get_reg128(GByteArray *buf, uint64_t val_hi,
+ uint64_t val_lo)
+{
+ uint64_t to_quad;
+#if TARGET_BIG_ENDIAN
+ to_quad = tswap64(val_hi);
+ g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
+ to_quad = tswap64(val_lo);
+ g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
+#else
+ to_quad = tswap64(val_lo);
+ g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
+ to_quad = tswap64(val_hi);
+ g_byte_array_append(buf, (uint8_t *) &to_quad, 8);
+#endif
+ return 16;
+}
+
+static inline int gdb_get_zeroes(GByteArray *array, size_t len)
+{
+ guint oldlen = array->len;
+ g_byte_array_set_size(array, oldlen + len);
+ memset(array->data + oldlen, 0, len);
+
+ return len;
+}
+
+/**
+ * gdb_get_reg_ptr: get pointer to start of last element
+ * @len: length of element
+ *
+ * This is a helper function to extract the pointer to the last
+ * element for additional processing. Some front-ends do additional
+ * dynamic swapping of the elements based on CPU state.
+ */
+static inline uint8_t *gdb_get_reg_ptr(GByteArray *buf, int len)
+{
+ return buf->data + buf->len - len;
+}
+
+#if TARGET_LONG_BITS == 64
+#define gdb_get_regl(buf, val) gdb_get_reg64(buf, val)
+#define ldtul_p(addr) ldq_p(addr)
+#else
+#define gdb_get_regl(buf, val) gdb_get_reg32(buf, val)
+#define ldtul_p(addr) ldl_p(addr)
+#endif
+
+#else
+#error "gdbstub helpers should only be included by target specific code"
+#endif
+
+#endif /* _GDBSTUB_HELPERS_H_ */
diff --git a/include/gdbstub/syscalls.h b/include/gdbstub/syscalls.h
new file mode 100644
index 0000000..243eaf8
--- /dev/null
+++ b/include/gdbstub/syscalls.h
@@ -0,0 +1,113 @@
+/*
+ * GDB Syscall support
+ *
+ * Copyright (c) 2023 Linaro Ltd
+ *
+ * SPDX-License-Identifier: LGPL-2.0+
+ */
+
+#ifndef _SYSCALLS_H_
+#define _SYSCALLS_H_
+
+/* For gdb file i/o remote protocol open flags. */
+#define GDB_O_RDONLY 0
+#define GDB_O_WRONLY 1
+#define GDB_O_RDWR 2
+#define GDB_O_APPEND 8
+#define GDB_O_CREAT 0x200
+#define GDB_O_TRUNC 0x400
+#define GDB_O_EXCL 0x800
+
+/* For gdb file i/o remote protocol errno values */
+#define GDB_EPERM 1
+#define GDB_ENOENT 2
+#define GDB_EINTR 4
+#define GDB_EBADF 9
+#define GDB_EACCES 13
+#define GDB_EFAULT 14
+#define GDB_EBUSY 16
+#define GDB_EEXIST 17
+#define GDB_ENODEV 19
+#define GDB_ENOTDIR 20
+#define GDB_EISDIR 21
+#define GDB_EINVAL 22
+#define GDB_ENFILE 23
+#define GDB_EMFILE 24
+#define GDB_EFBIG 27
+#define GDB_ENOSPC 28
+#define GDB_ESPIPE 29
+#define GDB_EROFS 30
+#define GDB_ENAMETOOLONG 91
+#define GDB_EUNKNOWN 9999
+
+/* For gdb file i/o remote protocol lseek whence. */
+#define GDB_SEEK_SET 0
+#define GDB_SEEK_CUR 1
+#define GDB_SEEK_END 2
+
+/* For gdb file i/o stat/fstat. */
+typedef uint32_t gdb_mode_t;
+typedef uint32_t gdb_time_t;
+
+struct gdb_stat {
+ uint32_t gdb_st_dev; /* device */
+ uint32_t gdb_st_ino; /* inode */
+ gdb_mode_t gdb_st_mode; /* protection */
+ uint32_t gdb_st_nlink; /* number of hard links */
+ uint32_t gdb_st_uid; /* user ID of owner */
+ uint32_t gdb_st_gid; /* group ID of owner */
+ uint32_t gdb_st_rdev; /* device type (if inode device) */
+ uint64_t gdb_st_size; /* total size, in bytes */
+ uint64_t gdb_st_blksize; /* blocksize for filesystem I/O */
+ uint64_t gdb_st_blocks; /* number of blocks allocated */
+ gdb_time_t gdb_st_atime; /* time of last access */
+ gdb_time_t gdb_st_mtime; /* time of last modification */
+ gdb_time_t gdb_st_ctime; /* time of last change */
+} QEMU_PACKED;
+
+struct gdb_timeval {
+ gdb_time_t tv_sec; /* second */
+ uint64_t tv_usec; /* microsecond */
+} QEMU_PACKED;
+
+typedef void (*gdb_syscall_complete_cb)(CPUState *cpu, uint64_t ret, int err);
+
+/**
+ * gdb_do_syscall:
+ * @cb: function to call when the system call has completed
+ * @fmt: gdb syscall format string
+ * ...: list of arguments to interpolate into @fmt
+ *
+ * Send a GDB syscall request. This function will return immediately;
+ * the callback function will be called later when the remote system
+ * call has completed.
+ *
+ * @fmt should be in the 'call-id,parameter,parameter...' format documented
+ * for the F request packet in the GDB remote protocol. A limited set of
+ * printf-style format specifiers is supported:
+ * %x - target_ulong argument printed in hex
+ * %lx - 64-bit argument printed in hex
+ * %s - string pointer (target_ulong) and length (int) pair
+ */
+void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...);
+
+/**
+ * use_gdb_syscalls() - report if GDB should be used for syscalls
+ *
+ * This is mostly driven by the semihosting mode the user configures
+ * but assuming GDB is allowed by that we report true if GDB is
+ * connected to the stub.
+ */
+int use_gdb_syscalls(void);
+
+/**
+ * gdb_exit: exit gdb session, reporting inferior status
+ * @code: exit code reported
+ *
+ * This closes the session and sends a final packet to GDB reporting
+ * the exit status of the program. It also cleans up any connections
+ * detritus before returning.
+ */
+void gdb_exit(int code);
+
+#endif /* _SYSCALLS_H_ */
diff --git a/include/gdbstub/user.h b/include/gdbstub/user.h
new file mode 100644
index 0000000..d392e51
--- /dev/null
+++ b/include/gdbstub/user.h
@@ -0,0 +1,43 @@
+/*
+ * gdbstub user-mode only APIs
+ *
+ * Copyright (c) 2022 Linaro Ltd
+ *
+ * SPDX-License-Identifier: LGPL-2.0+
+ */
+
+#ifndef GDBSTUB_USER_H
+#define GDBSTUB_USER_H
+
+/**
+ * gdb_handlesig() - yield control to gdb
+ * @cpu: CPU
+ * @sig: if non-zero, the signal number which caused us to stop
+ *
+ * This function yields control to gdb, when a user-mode-only target
+ * needs to stop execution. If @sig is non-zero, then we will send a
+ * stop packet to tell gdb that we have stopped because of this signal.
+ *
+ * This function will block (handling protocol requests from gdb)
+ * until gdb tells us to continue target execution. When it does
+ * return, the return value is a signal to deliver to the target,
+ * or 0 if no signal should be delivered, ie the signal that caused
+ * us to stop should be ignored.
+ */
+int gdb_handlesig(CPUState *, int);
+
+/**
+ * gdb_signalled() - inform remote gdb of sig exit
+ * @as: current CPUArchState
+ * @sig: signal number
+ */
+void gdb_signalled(CPUArchState *as, int sig);
+
+/**
+ * gdbserver_fork() - disable gdb stub for child processes.
+ * @cs: CPU
+ */
+void gdbserver_fork(CPUState *cs);
+
+
+#endif /* GDBSTUB_USER_H */
diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h
index 0b337a0..da19ae1 100644
--- a/include/hw/i386/x86.h
+++ b/include/hw/i386/x86.h
@@ -18,10 +18,8 @@
#define HW_I386_X86_H
#include "exec/hwaddr.h"
-#include "qemu/notify.h"
#include "hw/boards.h"
-#include "hw/nmi.h"
#include "hw/intc/ioapic.h"
#include "hw/isa/isa.h"
#include "qom/object.h"
diff --git a/include/hw/intc/mips_gic.h b/include/hw/intc/mips_gic.h
index eeb136e..5e4c71e 100644
--- a/include/hw/intc/mips_gic.h
+++ b/include/hw/intc/mips_gic.h
@@ -211,8 +211,8 @@
/* GIC VP Timer */
MIPSGICTimerState *gic_timer;
- int32_t num_vps;
- int32_t num_irq;
+ uint32_t num_vps;
+ uint32_t num_irq;
};
#endif /* MIPS_GIC_H */
diff --git a/include/hw/isa/i8259_internal.h b/include/hw/isa/i8259_internal.h
index 155b098..f9dcc41 100644
--- a/include/hw/isa/i8259_internal.h
+++ b/include/hw/isa/i8259_internal.h
@@ -61,6 +61,7 @@
uint8_t single_mode; /* true if slave pic is not initialized */
uint8_t elcr; /* PIIX edge/trigger selection*/
uint8_t elcr_mask;
+ uint8_t ltim; /* Edge/Level Bank Select (pre-PIIX, chip-wide) */
qemu_irq int_out[1];
uint32_t master; /* reflects /SP input pin */
uint32_t iobase;
diff --git a/include/hw/isa/vt82c686.h b/include/hw/isa/vt82c686.h
index e273cd3..da1722d 100644
--- a/include/hw/isa/vt82c686.h
+++ b/include/hw/isa/vt82c686.h
@@ -1,6 +1,8 @@
#ifndef HW_VT82C686_H
#define HW_VT82C686_H
+#include "hw/pci/pci_device.h"
+#include "audio/audio.h"
#define TYPE_VT82C686B_ISA "vt82c686b-isa"
#define TYPE_VT82C686B_USB_UHCI "vt82c686b-usb-uhci"
@@ -9,6 +11,29 @@
#define TYPE_VIA_IDE "via-ide"
#define TYPE_VIA_MC97 "via-mc97"
+typedef struct {
+ uint8_t stat;
+ uint8_t type;
+ uint32_t base;
+ uint32_t curr;
+ uint32_t addr;
+ uint32_t clen;
+} ViaAC97SGDChannel;
+
+OBJECT_DECLARE_SIMPLE_TYPE(ViaAC97State, VIA_AC97);
+
+struct ViaAC97State {
+ PCIDevice dev;
+ QEMUSoundCard card;
+ MemoryRegion sgd;
+ MemoryRegion fm;
+ MemoryRegion midi;
+ SWVoiceOut *vo;
+ ViaAC97SGDChannel aur;
+ uint16_t codec_regs[128];
+ uint32_t ac97_cmd;
+};
+
void via_isa_set_irq(PCIDevice *d, int n, int level);
#endif
diff --git a/include/hw/misc/mips_cmgcr.h b/include/hw/misc/mips_cmgcr.h
index 9fa5894..db4bf5f 100644
--- a/include/hw/misc/mips_cmgcr.h
+++ b/include/hw/misc/mips_cmgcr.h
@@ -75,7 +75,7 @@
SysBusDevice parent_obj;
int32_t gcr_rev;
- int32_t num_vps;
+ uint32_t num_vps;
hwaddr gcr_base;
MemoryRegion iomem;
MemoryRegion *cpc_mr;
diff --git a/include/hw/misc/mips_itu.h b/include/hw/misc/mips_itu.h
index 50d9611..35218b2 100644
--- a/include/hw/misc/mips_itu.h
+++ b/include/hw/misc/mips_itu.h
@@ -57,8 +57,8 @@
SysBusDevice parent_obj;
/*< public >*/
- int32_t num_fifo;
- int32_t num_semaphores;
+ uint32_t num_fifo;
+ uint32_t num_semaphores;
/* ITC Storage */
ITCStorageCell *cell;
@@ -72,9 +72,8 @@
uint64_t icr0;
/* SAAR */
- bool saar_present;
- void *saar;
-
+ uint64_t *saar;
+ MIPSCPU *cpu0;
};
/* Get ITC Configuration Tag memory region. */
diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h
index 87524c6..eed244f 100644
--- a/include/hw/vfio/vfio-common.h
+++ b/include/hw/vfio/vfio-common.h
@@ -143,6 +143,8 @@
VFIOMigration *migration;
Error *migration_blocker;
OnOffAuto pre_copy_dirty_page_tracking;
+ bool dirty_pages_supported;
+ bool dirty_tracking;
} VFIODevice;
struct VFIODeviceOps {
@@ -220,6 +222,7 @@
bool vfio_mig_active(void);
int vfio_block_multiple_devices_migration(Error **errp);
void vfio_unblock_multiple_devices_migration(void);
+int vfio_block_giommu_migration(Error **errp);
int64_t vfio_mig_bytes_transferred(void);
#ifdef CONFIG_LINUX
@@ -243,7 +246,8 @@
int vfio_spapr_remove_window(VFIOContainer *container,
hwaddr offset_within_address_space);
-int vfio_migration_probe(VFIODevice *vbasedev, Error **errp);
-void vfio_migration_finalize(VFIODevice *vbasedev);
+int vfio_migration_realize(VFIODevice *vbasedev, Error **errp);
+void vfio_migration_exit(VFIODevice *vbasedev);
+void vfio_migration_finalize(void);
#endif /* HW_VFIO_VFIO_COMMON_H */
diff --git a/include/hw/xen/xen-bus-helper.h b/include/hw/xen/xen-bus-helper.h
index 8782f30..d8dcc2f 100644
--- a/include/hw/xen/xen-bus-helper.h
+++ b/include/hw/xen/xen-bus-helper.h
@@ -8,40 +8,40 @@
#ifndef HW_XEN_BUS_HELPER_H
#define HW_XEN_BUS_HELPER_H
-#include "hw/xen/xen_common.h"
+#include "hw/xen/xen_backend_ops.h"
const char *xs_strstate(enum xenbus_state state);
-void xs_node_create(struct xs_handle *xsh, xs_transaction_t tid,
- const char *node, struct xs_permissions perms[],
- unsigned int nr_perms, Error **errp);
-void xs_node_destroy(struct xs_handle *xsh, xs_transaction_t tid,
+void xs_node_create(struct qemu_xs_handle *h, xs_transaction_t tid,
+ const char *node, unsigned int owner, unsigned int domid,
+ unsigned int perms, Error **errp);
+void xs_node_destroy(struct qemu_xs_handle *h, xs_transaction_t tid,
const char *node, Error **errp);
/* Write to node/key unless node is empty, in which case write to key */
-void xs_node_vprintf(struct xs_handle *xsh, xs_transaction_t tid,
+void xs_node_vprintf(struct qemu_xs_handle *h, xs_transaction_t tid,
const char *node, const char *key, Error **errp,
const char *fmt, va_list ap)
G_GNUC_PRINTF(6, 0);
-void xs_node_printf(struct xs_handle *xsh, xs_transaction_t tid,
+void xs_node_printf(struct qemu_xs_handle *h, xs_transaction_t tid,
const char *node, const char *key, Error **errp,
const char *fmt, ...)
G_GNUC_PRINTF(6, 7);
/* Read from node/key unless node is empty, in which case read from key */
-int xs_node_vscanf(struct xs_handle *xsh, xs_transaction_t tid,
+int xs_node_vscanf(struct qemu_xs_handle *h, xs_transaction_t tid,
const char *node, const char *key, Error **errp,
const char *fmt, va_list ap)
G_GNUC_SCANF(6, 0);
-int xs_node_scanf(struct xs_handle *xsh, xs_transaction_t tid,
+int xs_node_scanf(struct qemu_xs_handle *h, xs_transaction_t tid,
const char *node, const char *key, Error **errp,
const char *fmt, ...)
G_GNUC_SCANF(6, 7);
/* Watch node/key unless node is empty, in which case watch key */
-void xs_node_watch(struct xs_handle *xsh, const char *node, const char *key,
- char *token, Error **errp);
-void xs_node_unwatch(struct xs_handle *xsh, const char *node, const char *key,
- const char *token, Error **errp);
+struct qemu_xs_watch *xs_node_watch(struct qemu_xs_handle *h, const char *node,
+ const char *key, xs_watch_fn fn,
+ void *opaque, Error **errp);
+void xs_node_unwatch(struct qemu_xs_handle *h, struct qemu_xs_watch *w);
#endif /* HW_XEN_BUS_HELPER_H */
diff --git a/include/hw/xen/xen-bus.h b/include/hw/xen/xen-bus.h
index 4d966a2..f435898 100644
--- a/include/hw/xen/xen-bus.h
+++ b/include/hw/xen/xen-bus.h
@@ -8,31 +8,25 @@
#ifndef HW_XEN_BUS_H
#define HW_XEN_BUS_H
-#include "hw/xen/xen_common.h"
+#include "hw/xen/xen_backend_ops.h"
#include "hw/sysbus.h"
#include "qemu/notify.h"
#include "qom/object.h"
-typedef void (*XenWatchHandler)(void *opaque);
-
-typedef struct XenWatchList XenWatchList;
-typedef struct XenWatch XenWatch;
typedef struct XenEventChannel XenEventChannel;
struct XenDevice {
DeviceState qdev;
domid_t frontend_id;
char *name;
- struct xs_handle *xsh;
- XenWatchList *watch_list;
+ struct qemu_xs_handle *xsh;
char *backend_path, *frontend_path;
enum xenbus_state backend_state, frontend_state;
Notifier exit;
- XenWatch *backend_state_watch, *frontend_state_watch;
+ struct qemu_xs_watch *backend_state_watch, *frontend_state_watch;
bool backend_online;
- XenWatch *backend_online_watch;
+ struct qemu_xs_watch *backend_online_watch;
xengnttab_handle *xgth;
- bool feature_grant_copy;
bool inactive;
QLIST_HEAD(, XenEventChannel) event_channels;
QLIST_ENTRY(XenDevice) list;
@@ -64,10 +58,9 @@
struct XenBus {
BusState qbus;
domid_t backend_id;
- struct xs_handle *xsh;
- XenWatchList *watch_list;
+ struct qemu_xs_handle *xsh;
unsigned int backend_types;
- XenWatch **backend_watch;
+ struct qemu_xs_watch **backend_watch;
QLIST_HEAD(, XenDevice) inactive_devices;
};
@@ -102,7 +95,7 @@
void *xen_device_map_grant_refs(XenDevice *xendev, uint32_t *refs,
unsigned int nr_refs, int prot,
Error **errp);
-void xen_device_unmap_grant_refs(XenDevice *xendev, void *map,
+void xen_device_unmap_grant_refs(XenDevice *xendev, void *map, uint32_t *refs,
unsigned int nr_refs, Error **errp);
typedef struct XenDeviceGrantCopySegment {
diff --git a/include/hw/xen/xen-legacy-backend.h b/include/hw/xen/xen-legacy-backend.h
index e31cd3a..6c307c5 100644
--- a/include/hw/xen/xen-legacy-backend.h
+++ b/include/hw/xen/xen-legacy-backend.h
@@ -1,7 +1,7 @@
#ifndef HW_XEN_LEGACY_BACKEND_H
#define HW_XEN_LEGACY_BACKEND_H
-#include "hw/xen/xen_common.h"
+#include "hw/xen/xen_backend_ops.h"
#include "hw/xen/xen_pvdev.h"
#include "net/net.h"
#include "qom/object.h"
@@ -15,7 +15,7 @@
TYPE_XENBACKEND)
/* variables */
-extern struct xs_handle *xenstore;
+extern struct qemu_xs_handle *xenstore;
extern const char *xen_protocol;
extern DeviceState *xen_sysdev;
extern BusState *xen_sysbus;
@@ -30,9 +30,6 @@
char *xenstore_read_be_str(struct XenLegacyDevice *xendev, const char *node);
int xenstore_read_be_int(struct XenLegacyDevice *xendev, const char *node,
int *ival);
-void xenstore_update_fe(char *watch, struct XenLegacyDevice *xendev);
-void xenstore_update_be(char *watch, char *type, int dom,
- struct XenDevOps *ops);
char *xenstore_read_fe_str(struct XenLegacyDevice *xendev, const char *node);
int xenstore_read_fe_int(struct XenLegacyDevice *xendev, const char *node,
int *ival);
@@ -51,18 +48,7 @@
void *xen_be_map_grant_refs(struct XenLegacyDevice *xendev, uint32_t *refs,
unsigned int nr_refs, int prot);
void xen_be_unmap_grant_refs(struct XenLegacyDevice *xendev, void *ptr,
- unsigned int nr_refs);
-
-typedef struct XenGrantCopySegment {
- union {
- void *virt;
- struct {
- uint32_t ref;
- off_t offset;
- } foreign;
- } source, dest;
- size_t len;
-} XenGrantCopySegment;
+ uint32_t *refs, unsigned int nr_refs);
int xen_be_copy_grant_refs(struct XenLegacyDevice *xendev,
bool to_domain, XenGrantCopySegment segs[],
@@ -75,9 +61,9 @@
}
static inline void xen_be_unmap_grant_ref(struct XenLegacyDevice *xendev,
- void *ptr)
+ void *ptr, uint32_t ref)
{
- return xen_be_unmap_grant_refs(xendev, ptr, 1);
+ return xen_be_unmap_grant_refs(xendev, ptr, &ref, 1);
}
/* actual backend drivers */
diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h
index 0398393..2bd8ec7 100644
--- a/include/hw/xen/xen.h
+++ b/include/hw/xen/xen.h
@@ -8,15 +8,21 @@
#define QEMU_HW_XEN_H
/*
- * As a temporary measure while the headers are being untangled, define
- * __XEN_TOOLS__ here before any Xen headers are included. Otherwise, if
- * the Xen toolstack library headers are later included, they will find
- * some of the "internal" definitions missing and the build will fail. In
- * later commits, we'll end up with a rule that the native libraries have
- * to be included first, which will ensure that the libraries get the
- * version of Xen libraries that they expect.
+ * C files using Xen toolstack libraries will have included those headers
+ * already via xen_native.h, and having __XEM_TOOLS__ defined will have
+ * automatically set __XEN_INTERFACE_VERSION__ to the latest supported
+ * by the *system* Xen headers which were transitively included.
+ *
+ * C files which are part of the internal emulation, and which did not
+ * include xen_native.h, may need this defined so that the Xen headers
+ * imported to include/hw/xen/interface/ will expose the appropriate API
+ * version.
+ *
+ * This is why there's a rule that xen_native.h must be included first.
*/
-#define __XEN_TOOLS__ 1
+#ifndef __XEN_INTERFACE_VERSION__
+#define __XEN_INTERFACE_VERSION__ 0x00040e00
+#endif
#include "exec/cpu-common.h"
@@ -39,8 +45,6 @@
qemu_irq *xen_interrupt_controller_init(void);
-void xenstore_store_pv_console_info(int i, Chardev *chr);
-
void xen_register_framebuffer(struct MemoryRegion *mr);
#endif /* QEMU_HW_XEN_H */
diff --git a/include/hw/xen/xen_backend_ops.h b/include/hw/xen/xen_backend_ops.h
new file mode 100644
index 0000000..90cca85
--- /dev/null
+++ b/include/hw/xen/xen_backend_ops.h
@@ -0,0 +1,408 @@
+/*
+ * QEMU Xen backend support
+ *
+ * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Authors: David Woodhouse <dwmw2@infradead.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef QEMU_XEN_BACKEND_OPS_H
+#define QEMU_XEN_BACKEND_OPS_H
+
+#include "hw/xen/xen.h"
+#include "hw/xen/interface/xen.h"
+#include "hw/xen/interface/io/xenbus.h"
+
+/*
+ * For the time being, these operations map fairly closely to the API of
+ * the actual Xen libraries, e.g. libxenevtchn. As we complete the migration
+ * from XenLegacyDevice back ends to the new XenDevice model, they may
+ * evolve to slightly higher-level APIs.
+ *
+ * The internal emulations do not emulate the Xen APIs entirely faithfully;
+ * only enough to be used by the Xen backend devices. For example, only one
+ * event channel can be bound to each handle, since that's sufficient for
+ * the device support (only the true Xen HVM backend uses more). And the
+ * behaviour of unmask() and pending() is different too because the device
+ * backends don't care.
+ */
+
+typedef struct xenevtchn_handle xenevtchn_handle;
+typedef int xenevtchn_port_or_error_t;
+typedef uint32_t evtchn_port_t;
+typedef uint16_t domid_t;
+typedef uint32_t grant_ref_t;
+
+#define XEN_PAGE_SHIFT 12
+#define XEN_PAGE_SIZE (1UL << XEN_PAGE_SHIFT)
+#define XEN_PAGE_MASK (~(XEN_PAGE_SIZE - 1))
+
+#ifndef xen_rmb
+#define xen_rmb() smp_rmb()
+#endif
+#ifndef xen_wmb
+#define xen_wmb() smp_wmb()
+#endif
+#ifndef xen_mb
+#define xen_mb() smp_mb()
+#endif
+
+struct evtchn_backend_ops {
+ xenevtchn_handle *(*open)(void);
+ int (*bind_interdomain)(xenevtchn_handle *xc, uint32_t domid,
+ evtchn_port_t guest_port);
+ int (*unbind)(xenevtchn_handle *xc, evtchn_port_t port);
+ int (*close)(struct xenevtchn_handle *xc);
+ int (*get_fd)(struct xenevtchn_handle *xc);
+ int (*notify)(struct xenevtchn_handle *xc, evtchn_port_t port);
+ int (*unmask)(struct xenevtchn_handle *xc, evtchn_port_t port);
+ int (*pending)(struct xenevtchn_handle *xc);
+};
+
+extern struct evtchn_backend_ops *xen_evtchn_ops;
+
+static inline xenevtchn_handle *qemu_xen_evtchn_open(void)
+{
+ if (!xen_evtchn_ops) {
+ return NULL;
+ }
+ return xen_evtchn_ops->open();
+}
+
+static inline int qemu_xen_evtchn_bind_interdomain(xenevtchn_handle *xc,
+ uint32_t domid,
+ evtchn_port_t guest_port)
+{
+ if (!xen_evtchn_ops) {
+ return -ENOSYS;
+ }
+ return xen_evtchn_ops->bind_interdomain(xc, domid, guest_port);
+}
+
+static inline int qemu_xen_evtchn_unbind(xenevtchn_handle *xc,
+ evtchn_port_t port)
+{
+ if (!xen_evtchn_ops) {
+ return -ENOSYS;
+ }
+ return xen_evtchn_ops->unbind(xc, port);
+}
+
+static inline int qemu_xen_evtchn_close(xenevtchn_handle *xc)
+{
+ if (!xen_evtchn_ops) {
+ return -ENOSYS;
+ }
+ return xen_evtchn_ops->close(xc);
+}
+
+static inline int qemu_xen_evtchn_fd(xenevtchn_handle *xc)
+{
+ if (!xen_evtchn_ops) {
+ return -ENOSYS;
+ }
+ return xen_evtchn_ops->get_fd(xc);
+}
+
+static inline int qemu_xen_evtchn_notify(xenevtchn_handle *xc,
+ evtchn_port_t port)
+{
+ if (!xen_evtchn_ops) {
+ return -ENOSYS;
+ }
+ return xen_evtchn_ops->notify(xc, port);
+}
+
+static inline int qemu_xen_evtchn_unmask(xenevtchn_handle *xc,
+ evtchn_port_t port)
+{
+ if (!xen_evtchn_ops) {
+ return -ENOSYS;
+ }
+ return xen_evtchn_ops->unmask(xc, port);
+}
+
+static inline int qemu_xen_evtchn_pending(xenevtchn_handle *xc)
+{
+ if (!xen_evtchn_ops) {
+ return -ENOSYS;
+ }
+ return xen_evtchn_ops->pending(xc);
+}
+
+typedef struct xengntdev_handle xengnttab_handle;
+
+typedef struct XenGrantCopySegment {
+ union {
+ void *virt;
+ struct {
+ uint32_t ref;
+ off_t offset;
+ } foreign;
+ } source, dest;
+ size_t len;
+} XenGrantCopySegment;
+
+#define XEN_GNTTAB_OP_FEATURE_MAP_MULTIPLE (1U << 0)
+
+struct gnttab_backend_ops {
+ uint32_t features;
+ xengnttab_handle *(*open)(void);
+ int (*close)(xengnttab_handle *xgt);
+ int (*grant_copy)(xengnttab_handle *xgt, bool to_domain, uint32_t domid,
+ XenGrantCopySegment *segs, uint32_t nr_segs,
+ Error **errp);
+ int (*set_max_grants)(xengnttab_handle *xgt, uint32_t nr_grants);
+ void *(*map_refs)(xengnttab_handle *xgt, uint32_t count, uint32_t domid,
+ uint32_t *refs, int prot);
+ int (*unmap)(xengnttab_handle *xgt, void *start_address, uint32_t *refs,
+ uint32_t count);
+};
+
+extern struct gnttab_backend_ops *xen_gnttab_ops;
+
+static inline bool qemu_xen_gnttab_can_map_multi(void)
+{
+ return xen_gnttab_ops &&
+ !!(xen_gnttab_ops->features & XEN_GNTTAB_OP_FEATURE_MAP_MULTIPLE);
+}
+
+static inline xengnttab_handle *qemu_xen_gnttab_open(void)
+{
+ if (!xen_gnttab_ops) {
+ return NULL;
+ }
+ return xen_gnttab_ops->open();
+}
+
+static inline int qemu_xen_gnttab_close(xengnttab_handle *xgt)
+{
+ if (!xen_gnttab_ops) {
+ return -ENOSYS;
+ }
+ return xen_gnttab_ops->close(xgt);
+}
+
+static inline int qemu_xen_gnttab_grant_copy(xengnttab_handle *xgt,
+ bool to_domain, uint32_t domid,
+ XenGrantCopySegment *segs,
+ uint32_t nr_segs, Error **errp)
+{
+ if (!xen_gnttab_ops) {
+ return -ENOSYS;
+ }
+
+ return xen_gnttab_ops->grant_copy(xgt, to_domain, domid, segs, nr_segs,
+ errp);
+}
+
+static inline int qemu_xen_gnttab_set_max_grants(xengnttab_handle *xgt,
+ uint32_t nr_grants)
+{
+ if (!xen_gnttab_ops) {
+ return -ENOSYS;
+ }
+ return xen_gnttab_ops->set_max_grants(xgt, nr_grants);
+}
+
+static inline void *qemu_xen_gnttab_map_refs(xengnttab_handle *xgt,
+ uint32_t count, uint32_t domid,
+ uint32_t *refs, int prot)
+{
+ if (!xen_gnttab_ops) {
+ return NULL;
+ }
+ return xen_gnttab_ops->map_refs(xgt, count, domid, refs, prot);
+}
+
+static inline int qemu_xen_gnttab_unmap(xengnttab_handle *xgt,
+ void *start_address, uint32_t *refs,
+ uint32_t count)
+{
+ if (!xen_gnttab_ops) {
+ return -ENOSYS;
+ }
+ return xen_gnttab_ops->unmap(xgt, start_address, refs, count);
+}
+
+struct foreignmem_backend_ops {
+ void *(*map)(uint32_t dom, void *addr, int prot, size_t pages,
+ xen_pfn_t *pfns, int *errs);
+ int (*unmap)(void *addr, size_t pages);
+};
+
+extern struct foreignmem_backend_ops *xen_foreignmem_ops;
+
+static inline void *qemu_xen_foreignmem_map(uint32_t dom, void *addr, int prot,
+ size_t pages, xen_pfn_t *pfns,
+ int *errs)
+{
+ if (!xen_foreignmem_ops) {
+ return NULL;
+ }
+ return xen_foreignmem_ops->map(dom, addr, prot, pages, pfns, errs);
+}
+
+static inline int qemu_xen_foreignmem_unmap(void *addr, size_t pages)
+{
+ if (!xen_foreignmem_ops) {
+ return -ENOSYS;
+ }
+ return xen_foreignmem_ops->unmap(addr, pages);
+}
+
+typedef void (*xs_watch_fn)(void *opaque, const char *path);
+
+struct qemu_xs_handle;
+struct qemu_xs_watch;
+typedef uint32_t xs_transaction_t;
+
+#define XBT_NULL 0
+
+#define XS_PERM_NONE 0x00
+#define XS_PERM_READ 0x01
+#define XS_PERM_WRITE 0x02
+
+struct xenstore_backend_ops {
+ struct qemu_xs_handle *(*open)(void);
+ void (*close)(struct qemu_xs_handle *h);
+ char *(*get_domain_path)(struct qemu_xs_handle *h, unsigned int domid);
+ char **(*directory)(struct qemu_xs_handle *h, xs_transaction_t t,
+ const char *path, unsigned int *num);
+ void *(*read)(struct qemu_xs_handle *h, xs_transaction_t t,
+ const char *path, unsigned int *len);
+ bool (*write)(struct qemu_xs_handle *h, xs_transaction_t t,
+ const char *path, const void *data, unsigned int len);
+ bool (*create)(struct qemu_xs_handle *h, xs_transaction_t t,
+ unsigned int owner, unsigned int domid,
+ unsigned int perms, const char *path);
+ bool (*destroy)(struct qemu_xs_handle *h, xs_transaction_t t,
+ const char *path);
+ struct qemu_xs_watch *(*watch)(struct qemu_xs_handle *h, const char *path,
+ xs_watch_fn fn, void *opaque);
+ void (*unwatch)(struct qemu_xs_handle *h, struct qemu_xs_watch *w);
+ xs_transaction_t (*transaction_start)(struct qemu_xs_handle *h);
+ bool (*transaction_end)(struct qemu_xs_handle *h, xs_transaction_t t,
+ bool abort);
+};
+
+extern struct xenstore_backend_ops *xen_xenstore_ops;
+
+static inline struct qemu_xs_handle *qemu_xen_xs_open(void)
+{
+ if (!xen_xenstore_ops) {
+ return NULL;
+ }
+ return xen_xenstore_ops->open();
+}
+
+static inline void qemu_xen_xs_close(struct qemu_xs_handle *h)
+{
+ if (!xen_xenstore_ops) {
+ return;
+ }
+ xen_xenstore_ops->close(h);
+}
+
+static inline char *qemu_xen_xs_get_domain_path(struct qemu_xs_handle *h,
+ unsigned int domid)
+{
+ if (!xen_xenstore_ops) {
+ return NULL;
+ }
+ return xen_xenstore_ops->get_domain_path(h, domid);
+}
+
+static inline char **qemu_xen_xs_directory(struct qemu_xs_handle *h,
+ xs_transaction_t t, const char *path,
+ unsigned int *num)
+{
+ if (!xen_xenstore_ops) {
+ return NULL;
+ }
+ return xen_xenstore_ops->directory(h, t, path, num);
+}
+
+static inline void *qemu_xen_xs_read(struct qemu_xs_handle *h,
+ xs_transaction_t t, const char *path,
+ unsigned int *len)
+{
+ if (!xen_xenstore_ops) {
+ return NULL;
+ }
+ return xen_xenstore_ops->read(h, t, path, len);
+}
+
+static inline bool qemu_xen_xs_write(struct qemu_xs_handle *h,
+ xs_transaction_t t, const char *path,
+ const void *data, unsigned int len)
+{
+ if (!xen_xenstore_ops) {
+ return false;
+ }
+ return xen_xenstore_ops->write(h, t, path, data, len);
+}
+
+static inline bool qemu_xen_xs_create(struct qemu_xs_handle *h,
+ xs_transaction_t t, unsigned int owner,
+ unsigned int domid, unsigned int perms,
+ const char *path)
+{
+ if (!xen_xenstore_ops) {
+ return false;
+ }
+ return xen_xenstore_ops->create(h, t, owner, domid, perms, path);
+}
+
+static inline bool qemu_xen_xs_destroy(struct qemu_xs_handle *h,
+ xs_transaction_t t, const char *path)
+{
+ if (!xen_xenstore_ops) {
+ return false;
+ }
+ return xen_xenstore_ops->destroy(h, t, path);
+}
+
+static inline struct qemu_xs_watch *qemu_xen_xs_watch(struct qemu_xs_handle *h,
+ const char *path,
+ xs_watch_fn fn,
+ void *opaque)
+{
+ if (!xen_xenstore_ops) {
+ return NULL;
+ }
+ return xen_xenstore_ops->watch(h, path, fn, opaque);
+}
+
+static inline void qemu_xen_xs_unwatch(struct qemu_xs_handle *h,
+ struct qemu_xs_watch *w)
+{
+ if (!xen_xenstore_ops) {
+ return;
+ }
+ xen_xenstore_ops->unwatch(h, w);
+}
+
+static inline xs_transaction_t qemu_xen_xs_transaction_start(struct qemu_xs_handle *h)
+{
+ if (!xen_xenstore_ops) {
+ return XBT_NULL;
+ }
+ return xen_xenstore_ops->transaction_start(h);
+}
+
+static inline bool qemu_xen_xs_transaction_end(struct qemu_xs_handle *h,
+ xs_transaction_t t, bool abort)
+{
+ if (!xen_xenstore_ops) {
+ return false;
+ }
+ return xen_xenstore_ops->transaction_end(h, t, abort);
+}
+
+void setup_xen_backend_ops(void);
+
+#endif /* QEMU_XEN_BACKEND_OPS_H */
diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_native.h
similarity index 88%
rename from include/hw/xen/xen_common.h
rename to include/hw/xen/xen_native.h
index 9a13a75..6bcc83b 100644
--- a/include/hw/xen/xen_common.h
+++ b/include/hw/xen/xen_native.h
@@ -1,5 +1,9 @@
-#ifndef QEMU_HW_XEN_COMMON_H
-#define QEMU_HW_XEN_COMMON_H
+#ifndef QEMU_HW_XEN_NATIVE_H
+#define QEMU_HW_XEN_NATIVE_H
+
+#ifdef __XEN_INTERFACE_VERSION__
+#error In Xen native files, include xen_native.h before other Xen headers
+#endif
/*
* If we have new enough libxenctrl then we do not want/need these compat
@@ -12,7 +16,6 @@
#include <xenctrl.h>
#include <xenstore.h>
-#include "hw/xen/interface/io/xenbus.h"
#include "hw/xen/xen.h"
#include "hw/pci/pci_device.h"
@@ -28,49 +31,12 @@
#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40701
typedef xc_interface xenforeignmemory_handle;
-typedef xc_evtchn xenevtchn_handle;
-typedef xc_gnttab xengnttab_handle;
-typedef evtchn_port_or_error_t xenevtchn_port_or_error_t;
-
-#define xenevtchn_open(l, f) xc_evtchn_open(l, f);
-#define xenevtchn_close(h) xc_evtchn_close(h)
-#define xenevtchn_fd(h) xc_evtchn_fd(h)
-#define xenevtchn_pending(h) xc_evtchn_pending(h)
-#define xenevtchn_notify(h, p) xc_evtchn_notify(h, p)
-#define xenevtchn_bind_interdomain(h, d, p) xc_evtchn_bind_interdomain(h, d, p)
-#define xenevtchn_unmask(h, p) xc_evtchn_unmask(h, p)
-#define xenevtchn_unbind(h, p) xc_evtchn_unbind(h, p)
-
-#define xengnttab_open(l, f) xc_gnttab_open(l, f)
-#define xengnttab_close(h) xc_gnttab_close(h)
-#define xengnttab_set_max_grants(h, n) xc_gnttab_set_max_grants(h, n)
-#define xengnttab_map_grant_ref(h, d, r, p) xc_gnttab_map_grant_ref(h, d, r, p)
-#define xengnttab_unmap(h, a, n) xc_gnttab_munmap(h, a, n)
-#define xengnttab_map_grant_refs(h, c, d, r, p) \
- xc_gnttab_map_grant_refs(h, c, d, r, p)
-#define xengnttab_map_domain_grant_refs(h, c, d, r, p) \
- xc_gnttab_map_domain_grant_refs(h, c, d, r, p)
#define xenforeignmemory_open(l, f) xen_xc
#define xenforeignmemory_close(h)
-static inline void *xenforeignmemory_map(xc_interface *h, uint32_t dom,
- int prot, size_t pages,
- const xen_pfn_t arr[/*pages*/],
- int err[/*pages*/])
-{
- if (err)
- return xc_map_foreign_bulk(h, dom, prot, arr, err, pages);
- else
- return xc_map_foreign_pages(h, dom, prot, arr, pages);
-}
-
-#define xenforeignmemory_unmap(h, p, s) munmap(p, s * XC_PAGE_SIZE)
-
#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40701 */
-#include <xenevtchn.h>
-#include <xengnttab.h>
#include <xenforeignmemory.h>
#endif
@@ -660,31 +626,4 @@
#endif
-/* Xen before 4.8 */
-
-#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40800
-
-struct xengnttab_grant_copy_segment {
- union xengnttab_copy_ptr {
- void *virt;
- struct {
- uint32_t ref;
- uint16_t offset;
- uint16_t domid;
- } foreign;
- } source, dest;
- uint16_t len;
- uint16_t flags;
- int16_t status;
-};
-
-typedef struct xengnttab_grant_copy_segment xengnttab_grant_copy_segment_t;
-
-static inline int xengnttab_grant_copy(xengnttab_handle *xgt, uint32_t count,
- xengnttab_grant_copy_segment_t *segs)
-{
- return -ENOSYS;
-}
-#endif
-
-#endif /* QEMU_HW_XEN_COMMON_H */
+#endif /* QEMU_HW_XEN_NATIVE_H */
diff --git a/include/hw/xen/xen_pvdev.h b/include/hw/xen/xen_pvdev.h
index 7cd4bc2..ddad4b9 100644
--- a/include/hw/xen/xen_pvdev.h
+++ b/include/hw/xen/xen_pvdev.h
@@ -1,7 +1,9 @@
#ifndef QEMU_HW_XEN_PVDEV_H
#define QEMU_HW_XEN_PVDEV_H
-#include "hw/xen/xen_common.h"
+#include "hw/qdev-core.h"
+#include "hw/xen/xen_backend_ops.h"
+
/* ------------------------------------------------------------- */
#define XEN_BUFSIZE 1024
@@ -38,6 +40,7 @@
char name[64];
int debug;
+ struct qemu_xs_watch *watch;
enum xenbus_state be_state;
enum xenbus_state fe_state;
int online;
@@ -63,7 +66,6 @@
char *xenstore_read_str(const char *base, const char *node);
int xenstore_read_int(const char *base, const char *node, int *ival);
int xenstore_read_uint64(const char *base, const char *node, uint64_t *uval);
-void xenstore_update(void *unused);
const char *xenbus_strstate(enum xenbus_state state);
diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h
index 874134f..f85834e 100644
--- a/include/qemu/atomic.h
+++ b/include/qemu/atomic.h
@@ -245,6 +245,20 @@
#define smp_wmb() smp_mb_release()
#define smp_rmb() smp_mb_acquire()
+/*
+ * SEQ_CST is weaker than the older __sync_* builtins and Linux
+ * kernel read-modify-write atomics. Provide a macro to obtain
+ * the same semantics.
+ */
+#if !defined(QEMU_SANITIZE_THREAD) && \
+ (defined(__i386__) || defined(__x86_64__) || defined(__s390x__))
+# define smp_mb__before_rmw() signal_barrier()
+# define smp_mb__after_rmw() signal_barrier()
+#else
+# define smp_mb__before_rmw() smp_mb()
+# define smp_mb__after_rmw() smp_mb()
+#endif
+
/* qatomic_mb_read/set semantics map Java volatile variables. They are
* less expensive on some platforms (notably POWER) than fully
* sequentially consistent operations.
@@ -259,7 +273,8 @@
#if !defined(QEMU_SANITIZE_THREAD) && \
(defined(__i386__) || defined(__x86_64__) || defined(__s390x__))
/* This is more efficient than a store plus a fence. */
-# define qatomic_mb_set(ptr, i) ((void)qatomic_xchg(ptr, i))
+# define qatomic_mb_set(ptr, i) \
+ ({ (void)qatomic_xchg(ptr, i); smp_mb__after_rmw(); })
#else
# define qatomic_mb_set(ptr, i) \
({ qatomic_store_release(ptr, i); smp_mb(); })
diff --git a/include/sysemu/accel-ops.h b/include/sysemu/accel-ops.h
index 30690c7..3c1fab4 100644
--- a/include/sysemu/accel-ops.h
+++ b/include/sysemu/accel-ops.h
@@ -48,6 +48,7 @@
/* gdbstub hooks */
bool (*supports_guest_debug)(void);
+ int (*update_guest_debug)(CPUState *cpu);
int (*insert_breakpoint)(CPUState *cpu, int type, vaddr addr, vaddr len);
int (*remove_breakpoint)(CPUState *cpu, int type, vaddr addr, vaddr len);
void (*remove_all_breakpoints)(CPUState *cpu);
diff --git a/linux-user/exit.c b/linux-user/exit.c
index 607b6da..fd49d76 100644
--- a/linux-user/exit.c
+++ b/linux-user/exit.c
@@ -18,7 +18,7 @@
*/
#include "qemu/osdep.h"
#include "accel/tcg/perf.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
#include "qemu.h"
#include "user-internals.h"
#ifdef CONFIG_GPROF
diff --git a/linux-user/main.c b/linux-user/main.c
index 4ff30ff..75dbb52 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -40,6 +40,7 @@
#include "qemu/plugin.h"
#include "exec/exec-all.h"
#include "exec/gdbstub.h"
+#include "gdbstub/user.h"
#include "tcg/tcg.h"
#include "qemu/timer.h"
#include "qemu/envlist.h"
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 098f3a7..748a98f 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -18,7 +18,7 @@
*/
#include "qemu/osdep.h"
#include "qemu/bitops.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/user.h"
#include "hw/core/tcg-cpu-ops.h"
#include <sys/ucontext.h>
diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h
index 3576da4..9333db4 100644
--- a/linux-user/user-internals.h
+++ b/linux-user/user-internals.h
@@ -20,6 +20,7 @@
#include "exec/user/thunk.h"
#include "exec/exec-all.h"
+#include "exec/tb-flush.h"
#include "qemu/log.h"
extern char *exec_path;
diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc
index d8203a5..4af6002 100644
--- a/pc-bios/openbios-ppc
+++ b/pc-bios/openbios-ppc
Binary files differ
diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32
index 118b1cc..41b6a60 100644
--- a/pc-bios/openbios-sparc32
+++ b/pc-bios/openbios-sparc32
Binary files differ
diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64
index 846cb48..902b4b3 100644
--- a/pc-bios/openbios-sparc64
+++ b/pc-bios/openbios-sparc64
Binary files differ
diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img
index 554fcbd..c9a5a21 100644
--- a/pc-bios/s390-ccw.img
+++ b/pc-bios/s390-ccw.img
Binary files differ
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 994e59c..a213744 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -72,42 +72,74 @@
"Bad block size in zIPL section of the 1st record.");
}
-static block_number_t eckd_block_num(EckdCHS *chs)
+static void eckd_format_chs(ExtEckdBlockPtr *ptr, bool ldipl,
+ uint64_t *c,
+ uint64_t *h,
+ uint64_t *s)
+{
+ if (ldipl) {
+ *c = ptr->ldptr.chs.cylinder;
+ *h = ptr->ldptr.chs.head;
+ *s = ptr->ldptr.chs.sector;
+ } else {
+ *c = ptr->bptr.chs.cylinder;
+ *h = ptr->bptr.chs.head;
+ *s = ptr->bptr.chs.sector;
+ }
+}
+
+static block_number_t eckd_chs_to_block(uint64_t c, uint64_t h, uint64_t s)
{
const uint64_t sectors = virtio_get_sectors();
const uint64_t heads = virtio_get_heads();
- const uint64_t cylinder = chs->cylinder
- + ((chs->head & 0xfff0) << 12);
- const uint64_t head = chs->head & 0x000f;
+ const uint64_t cylinder = c + ((h & 0xfff0) << 12);
+ const uint64_t head = h & 0x000f;
const block_number_t block = sectors * heads * cylinder
+ sectors * head
- + chs->sector
- - 1; /* block nr starts with zero */
+ + s - 1; /* block nr starts with zero */
return block;
}
-static bool eckd_valid_address(BootMapPointer *p)
+static block_number_t eckd_block_num(EckdCHS *chs)
{
- const uint64_t head = p->eckd.chs.head & 0x000f;
+ return eckd_chs_to_block(chs->cylinder, chs->head, chs->sector);
+}
+static block_number_t gen_eckd_block_num(ExtEckdBlockPtr *ptr, bool ldipl)
+{
+ uint64_t cyl, head, sec;
+ eckd_format_chs(ptr, ldipl, &cyl, &head, &sec);
+ return eckd_chs_to_block(cyl, head, sec);
+}
+
+static bool eckd_valid_chs(uint64_t cyl, uint64_t head, uint64_t sector)
+{
if (head >= virtio_get_heads()
- || p->eckd.chs.sector > virtio_get_sectors()
- || p->eckd.chs.sector <= 0) {
+ || sector > virtio_get_sectors()
+ || sector <= 0) {
return false;
}
if (!virtio_guessed_disk_nature() &&
- eckd_block_num(&p->eckd.chs) >= virtio_get_blocks()) {
+ eckd_chs_to_block(cyl, head, sector) >= virtio_get_blocks()) {
return false;
}
return true;
}
-static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address)
+static bool eckd_valid_address(ExtEckdBlockPtr *ptr, bool ldipl)
+{
+ uint64_t cyl, head, sec;
+ eckd_format_chs(ptr, ldipl, &cyl, &head, &sec);
+ return eckd_valid_chs(cyl, head, sec);
+}
+
+static block_number_t load_eckd_segments(block_number_t blk, bool ldipl,
+ uint64_t *address)
{
block_number_t block_nr;
- int j, rc;
+ int j, rc, count;
BootMapPointer *bprs = (void *)_bprs;
bool more_data;
@@ -117,7 +149,7 @@
do {
more_data = false;
for (j = 0;; j++) {
- block_nr = eckd_block_num(&bprs[j].xeckd.bptr.chs);
+ block_nr = gen_eckd_block_num(&bprs[j].xeckd, ldipl);
if (is_null_block_number(block_nr)) { /* end of chunk */
break;
}
@@ -129,11 +161,26 @@
break;
}
- IPL_assert(block_size_ok(bprs[j].xeckd.bptr.size),
+ /* List directed pointer does not store block size */
+ IPL_assert(ldipl || block_size_ok(bprs[j].xeckd.bptr.size),
"bad chunk block size");
- IPL_assert(eckd_valid_address(&bprs[j]), "bad chunk ECKD addr");
- if ((bprs[j].xeckd.bptr.count == 0) && unused_space(&(bprs[j+1]),
+ if (!eckd_valid_address(&bprs[j].xeckd, ldipl)) {
+ /*
+ * If an invalid address is found during LD-IPL then break and
+ * retry as CCW
+ */
+ IPL_assert(ldipl, "bad chunk ECKD addr");
+ break;
+ }
+
+ if (ldipl) {
+ count = bprs[j].xeckd.ldptr.count;
+ } else {
+ count = bprs[j].xeckd.bptr.count;
+ }
+
+ if (count == 0 && unused_space(&bprs[j + 1],
sizeof(EckdBlockPtr))) {
/* This is a "continue" pointer.
* This ptr should be the last one in the current
@@ -149,11 +196,10 @@
/* Load (count+1) blocks of code at (block_nr)
* to memory (address).
*/
- rc = virtio_read_many(block_nr, (void *)(*address),
- bprs[j].xeckd.bptr.count+1);
+ rc = virtio_read_many(block_nr, (void *)(*address), count + 1);
IPL_assert(rc == 0, "code chunk read failed");
- *address += (bprs[j].xeckd.bptr.count+1) * virtio_get_block_size();
+ *address += (count + 1) * virtio_get_block_size();
}
} while (more_data);
return block_nr;
@@ -237,8 +283,10 @@
uint64_t address;
BootMapTable *bmt = (void *)sec;
BootMapScript *bms = (void *)sec;
+ /* The S1B block number is NULL_BLOCK_NR if and only if it's an LD-IPL */
+ bool ldipl = (s1b_block_nr == NULL_BLOCK_NR);
- if (menu_is_enabled_zipl()) {
+ if (menu_is_enabled_zipl() && !ldipl) {
loadparm = eckd_get_boot_menu_index(s1b_block_nr);
}
@@ -249,7 +297,7 @@
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
read_block(bmt_block_nr, sec, "Cannot read Boot Map Table");
- block_nr = eckd_block_num(&bmt->entry[loadparm].xeckd.bptr.chs);
+ block_nr = gen_eckd_block_num(&bmt->entry[loadparm].xeckd, ldipl);
IPL_assert(block_nr != -1, "Cannot find Boot Map Table Entry");
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
@@ -264,13 +312,18 @@
}
address = bms->entry[i].address.load_address;
- block_nr = eckd_block_num(&bms->entry[i].blkptr.xeckd.bptr.chs);
+ block_nr = gen_eckd_block_num(&bms->entry[i].blkptr.xeckd, ldipl);
do {
- block_nr = load_eckd_segments(block_nr, &address);
+ block_nr = load_eckd_segments(block_nr, ldipl, &address);
} while (block_nr != -1);
}
+ if (ldipl && bms->entry[i].type != BOOT_SCRIPT_EXEC) {
+ /* Abort LD-IPL and retry as CCW-IPL */
+ return;
+ }
+
IPL_assert(bms->entry[i].type == BOOT_SCRIPT_EXEC,
"Unknown script entry type");
write_reset_psw(bms->entry[i].address.load_address); /* no return */
@@ -380,6 +433,23 @@
/* no return */
}
+static block_number_t eckd_find_bmt(ExtEckdBlockPtr *ptr)
+{
+ block_number_t blockno;
+ uint8_t tmp_sec[MAX_SECTOR_SIZE];
+ BootRecord *br;
+
+ blockno = gen_eckd_block_num(ptr, 0);
+ read_block(blockno, tmp_sec, "Cannot read boot record");
+ br = (BootRecord *)tmp_sec;
+ if (!magic_match(br->magic, ZIPL_MAGIC)) {
+ /* If the boot record is invalid, return and try CCW-IPL instead */
+ return NULL_BLOCK_NR;
+ }
+
+ return gen_eckd_block_num(&br->pgt.xeckd, 1);
+}
+
static void print_eckd_msg(void)
{
char msg[] = "Using ECKD scheme (block size *****), ";
@@ -401,28 +471,43 @@
static void ipl_eckd(void)
{
- XEckdMbr *mbr = (void *)sec;
- LDL_VTOC *vlbl = (void *)sec;
+ IplVolumeLabel *vlbl = (void *)sec;
+ LDL_VTOC *vtoc = (void *)sec;
+ block_number_t ldipl_bmt; /* Boot Map Table for List-Directed IPL */
print_eckd_msg();
- /* Grab the MBR again */
- memset(sec, FREE_SPACE_FILLER, sizeof(sec));
- read_block(0, mbr, "Cannot read block 0 on DASD");
-
- if (magic_match(mbr->magic, IPL1_MAGIC)) {
- ipl_eckd_cdl(); /* only returns in case of error */
- return;
- }
-
- /* LDL/CMS? */
+ /* Block 2 can contain either the CDL VOL1 label or the LDL VTOC */
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
read_block(2, vlbl, "Cannot read block 2");
- if (magic_match(vlbl->magic, CMS1_MAGIC)) {
+ /*
+ * First check for a list-directed-format pointer which would
+ * supersede the CCW pointer.
+ */
+ if (eckd_valid_address((ExtEckdBlockPtr *)&vlbl->f.br, 0)) {
+ ldipl_bmt = eckd_find_bmt((ExtEckdBlockPtr *)&vlbl->f.br);
+ if (ldipl_bmt) {
+ sclp_print("List-Directed\n");
+ /* LD-IPL does not use the S1B bock, just make it NULL */
+ run_eckd_boot_script(ldipl_bmt, NULL_BLOCK_NR);
+ /* Only return in error, retry as CCW-IPL */
+ sclp_print("Retrying IPL ");
+ print_eckd_msg();
+ }
+ memset(sec, FREE_SPACE_FILLER, sizeof(sec));
+ read_block(2, vtoc, "Cannot read block 2");
+ }
+
+ /* Not list-directed */
+ if (magic_match(vtoc->magic, VOL1_MAGIC)) {
+ ipl_eckd_cdl(); /* may return in error */
+ }
+
+ if (magic_match(vtoc->magic, CMS1_MAGIC)) {
ipl_eckd_ldl(ECKD_CMS); /* no return */
}
- if (magic_match(vlbl->magic, LNX1_MAGIC)) {
+ if (magic_match(vtoc->magic, LNX1_MAGIC)) {
ipl_eckd_ldl(ECKD_LDL); /* no return */
}
diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
index 3946aa3..d4690a8 100644
--- a/pc-bios/s390-ccw/bootmap.h
+++ b/pc-bios/s390-ccw/bootmap.h
@@ -45,9 +45,23 @@
* it's 0 for TablePtr, ScriptPtr, and SectionPtr */
} __attribute__ ((packed)) EckdBlockPtr;
-typedef struct ExtEckdBlockPtr {
+typedef struct LdEckdCHS {
+ uint32_t cylinder;
+ uint8_t head;
+ uint8_t sector;
+} __attribute__ ((packed)) LdEckdCHS;
+
+typedef struct LdEckdBlockPtr {
+ LdEckdCHS chs; /* cylinder/head/sector is an address of the block */
+ uint8_t reserved[4];
+ uint16_t count;
+ uint32_t pad;
+} __attribute__ ((packed)) LdEckdBlockPtr;
+
+/* bptr is used for CCW type IPL, while ldptr is for list-directed IPL */
+typedef union ExtEckdBlockPtr {
EckdBlockPtr bptr;
- uint8_t reserved[8];
+ LdEckdBlockPtr ldptr;
} __attribute__ ((packed)) ExtEckdBlockPtr;
typedef union BootMapPointer {
@@ -57,6 +71,15 @@
ExtEckdBlockPtr xeckd;
} __attribute__ ((packed)) BootMapPointer;
+typedef struct BootRecord {
+ uint8_t magic[4];
+ uint32_t version;
+ uint64_t res1;
+ BootMapPointer pgt;
+ uint8_t reserved[510 - 32];
+ uint16_t os_id;
+} __attribute__ ((packed)) BootRecord;
+
/* aka Program Table */
typedef struct BootMapTable {
uint8_t magic[4];
@@ -292,7 +315,8 @@
struct {
unsigned char key[4]; /* == "VOL1" */
unsigned char volser[6];
- unsigned char reserved[6];
+ unsigned char reserved[64];
+ EckdCHS br; /* Location of Boot Record for list-directed IPL */
} f;
};
} __attribute__((packed)) IplVolumeLabel;
diff --git a/plugins/core.c b/plugins/core.c
index e04ffa1..0463288 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -24,6 +24,7 @@
#include "exec/cpu-common.h"
#include "exec/exec-all.h"
+#include "exec/tb-flush.h"
#include "exec/helper-proto.h"
#include "tcg/tcg.h"
#include "tcg/tcg-op.h"
diff --git a/plugins/loader.c b/plugins/loader.c
index 88c30bd..809f3f9 100644
--- a/plugins/loader.c
+++ b/plugins/loader.c
@@ -29,7 +29,7 @@
#include "qemu/plugin.h"
#include "qemu/memalign.h"
#include "hw/core/cpu.h"
-#include "exec/exec-all.h"
+#include "exec/tb-flush.h"
#ifndef CONFIG_USER_ONLY
#include "hw/boards.h"
#endif
diff --git a/roms/openbios b/roms/openbios
index 0e0afae..af97fd7 160000
--- a/roms/openbios
+++ b/roms/openbios
@@ -1 +1 @@
-Subproject commit 0e0afae6579c1efe9f0d85505b75ffe989554133
+Subproject commit af97fd7af5e7c18f591a7b987291d3db4ffb28b5
diff --git a/scripts/probe-gdb-support.py b/scripts/probe-gdb-support.py
new file mode 100755
index 0000000..5755255
--- /dev/null
+++ b/scripts/probe-gdb-support.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python3
+# coding: utf-8
+#
+# Probe gdb for supported architectures.
+#
+# This is required to support testing of the gdbstub as its hard to
+# handle errors gracefully during the test. Instead this script when
+# passed a GDB binary will probe its architecture support and return a
+# string of supported arches, stripped of guff.
+#
+# Copyright 2023 Linaro Ltd
+#
+# Author: Alex Bennée <alex.bennee@linaro.org>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import argparse
+import re
+from subprocess import check_output, STDOUT
+
+# mappings from gdb arch to QEMU target
+mappings = {
+ "alpha" : "alpha",
+ "aarch64" : ["aarch64", "aarch64_be"],
+ "armv7": "arm",
+ "armv8-a" : ["aarch64", "aarch64_be"],
+ "avr" : "avr",
+ "cris" : "cris",
+ # no hexagon in upstream gdb
+ "hppa1.0" : "hppa",
+ "i386" : "i386",
+ "i386:x86-64" : "x86_64",
+ "Loongarch64" : "loongarch64",
+ "m68k" : "m68k",
+ "MicroBlaze" : "microblaze",
+ "mips:isa64" : ["mips64", "mips64el"],
+ "nios2" : "nios2",
+ "or1k" : "or1k",
+ "powerpc:common" : "ppc",
+ "powerpc:common64" : ["ppc64", "ppc64le"],
+ "riscv:rv32" : "riscv32",
+ "riscv:rv64" : "riscv64",
+ "s390:64-bit" : "s390x",
+ "sh4" : ["sh4", "sh4eb"],
+ "sparc": "sparc",
+ "sparc:v8plus": "sparc32plus",
+ "sparc:v9a" : "sparc64",
+ # no tricore in upstream gdb
+ "xtensa" : ["xtensa", "xtensaeb"]
+}
+
+def do_probe(gdb):
+ gdb_out = check_output([gdb,
+ "-ex", "set architecture",
+ "-ex", "quit"], stderr=STDOUT)
+
+ m = re.search(r"Valid arguments are (.*)",
+ gdb_out.decode("utf-8"))
+
+ valid_arches = set()
+
+ if m.group(1):
+ for arch in m.group(1).split(", "):
+ if arch in mappings:
+ mapping = mappings[arch]
+ if isinstance(mapping, str):
+ valid_arches.add(mapping)
+ else:
+ for entry in mapping:
+ valid_arches.add(entry)
+
+ return valid_arches
+
+def main() -> None:
+ parser = argparse.ArgumentParser(description='Probe GDB Architectures')
+ parser.add_argument('gdb', help='Path to GDB binary.')
+
+ args = parser.parse_args()
+
+ supported = do_probe(args.gdb)
+
+ print(" ".join(supported))
+
+if __name__ == '__main__':
+ main()
diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c
index 62d8bae..564fe17 100644
--- a/semihosting/arm-compat-semi.c
+++ b/semihosting/arm-compat-semi.c
@@ -34,6 +34,7 @@
#include "qemu/osdep.h"
#include "qemu/timer.h"
#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
#include "semihosting/semihost.h"
#include "semihosting/console.h"
#include "semihosting/common-semi.h"
diff --git a/semihosting/guestfd.c b/semihosting/guestfd.c
index b05c52f..acb86b5 100644
--- a/semihosting/guestfd.c
+++ b/semihosting/guestfd.c
@@ -9,7 +9,7 @@
*/
#include "qemu/osdep.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
#include "semihosting/semihost.h"
#include "semihosting/guestfd.h"
#ifdef CONFIG_USER_ONLY
diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c
index e89992c..68899eb 100644
--- a/semihosting/syscalls.c
+++ b/semihosting/syscalls.c
@@ -7,7 +7,8 @@
*/
#include "qemu/osdep.h"
-#include "exec/gdbstub.h"
+#include "cpu.h"
+#include "gdbstub/syscalls.h"
#include "semihosting/guestfd.h"
#include "semihosting/syscalls.h"
#include "semihosting/console.h"
@@ -138,46 +139,48 @@
gdb_open_complete = complete;
gdb_do_syscall(gdb_open_cb, "open,%s,%x,%x",
- fname, len, (target_ulong)gdb_flags, (target_ulong)mode);
+ (uint64_t)fname, (uint32_t)len,
+ (uint32_t)gdb_flags, (uint32_t)mode);
}
static void gdb_close(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf)
{
- gdb_do_syscall(complete, "close,%x", (target_ulong)gf->hostfd);
+ gdb_do_syscall(complete, "close,%x", (uint32_t)gf->hostfd);
}
static void gdb_read(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf, target_ulong buf, target_ulong len)
{
- gdb_do_syscall(complete, "read,%x,%x,%x",
- (target_ulong)gf->hostfd, buf, len);
+ gdb_do_syscall(complete, "read,%x,%lx,%lx",
+ (uint32_t)gf->hostfd, (uint64_t)buf, (uint64_t)len);
}
static void gdb_write(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf, target_ulong buf, target_ulong len)
{
- gdb_do_syscall(complete, "write,%x,%x,%x",
- (target_ulong)gf->hostfd, buf, len);
+ gdb_do_syscall(complete, "write,%x,%lx,%lx",
+ (uint32_t)gf->hostfd, (uint64_t)buf, (uint64_t)len);
}
static void gdb_lseek(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf, int64_t off, int gdb_whence)
{
gdb_do_syscall(complete, "lseek,%x,%lx,%x",
- (target_ulong)gf->hostfd, off, (target_ulong)gdb_whence);
+ (uint32_t)gf->hostfd, off, (uint32_t)gdb_whence);
}
static void gdb_isatty(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf)
{
- gdb_do_syscall(complete, "isatty,%x", (target_ulong)gf->hostfd);
+ gdb_do_syscall(complete, "isatty,%x", (uint32_t)gf->hostfd);
}
static void gdb_fstat(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf, target_ulong addr)
{
- gdb_do_syscall(complete, "fstat,%x,%x", (target_ulong)gf->hostfd, addr);
+ gdb_do_syscall(complete, "fstat,%x,%lx",
+ (uint32_t)gf->hostfd, (uint64_t)addr);
}
static void gdb_stat(CPUState *cs, gdb_syscall_complete_cb complete,
@@ -190,7 +193,8 @@
return;
}
- gdb_do_syscall(complete, "stat,%s,%x", fname, len, addr);
+ gdb_do_syscall(complete, "stat,%s,%lx",
+ (uint64_t)fname, (uint32_t)len, (uint64_t)addr);
}
static void gdb_remove(CPUState *cs, gdb_syscall_complete_cb complete,
@@ -202,7 +206,7 @@
return;
}
- gdb_do_syscall(complete, "unlink,%s", fname, len);
+ gdb_do_syscall(complete, "unlink,%s", (uint64_t)fname, (uint32_t)len);
}
static void gdb_rename(CPUState *cs, gdb_syscall_complete_cb complete,
@@ -222,7 +226,9 @@
return;
}
- gdb_do_syscall(complete, "rename,%s,%s", oname, olen, nname, nlen);
+ gdb_do_syscall(complete, "rename,%s,%s",
+ (uint64_t)oname, (uint32_t)olen,
+ (uint64_t)nname, (uint32_t)nlen);
}
static void gdb_system(CPUState *cs, gdb_syscall_complete_cb complete,
@@ -234,13 +240,14 @@
return;
}
- gdb_do_syscall(complete, "system,%s", cmd, len);
+ gdb_do_syscall(complete, "system,%s", (uint64_t)cmd, (uint32_t)len);
}
static void gdb_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete,
target_ulong tv_addr, target_ulong tz_addr)
{
- gdb_do_syscall(complete, "gettimeofday,%x,%x", tv_addr, tz_addr);
+ gdb_do_syscall(complete, "gettimeofday,%lx,%lx",
+ (uint64_t)tv_addr, (uint64_t)tz_addr);
}
/*
diff --git a/softmmu/globals.c b/softmmu/globals.c
index 0a44056..39678aa 100644
--- a/softmmu/globals.c
+++ b/softmmu/globals.c
@@ -65,3 +65,7 @@
uint32_t xen_domid;
enum xen_mode xen_mode = XEN_DISABLED;
bool xen_domid_restrict;
+struct evtchn_backend_ops *xen_evtchn_ops;
+struct gnttab_backend_ops *xen_gnttab_ops;
+struct foreignmem_backend_ops *xen_foreignmem_ops;
+struct xenstore_backend_ops *xen_xenstore_ops;
diff --git a/softmmu/physmem.c b/softmmu/physmem.c
index 47143ed..fb412a5 100644
--- a/softmmu/physmem.c
+++ b/softmmu/physmem.c
@@ -1126,15 +1126,21 @@
GString *buf = g_string_new("");
RCU_READ_LOCK_GUARD();
- g_string_append_printf(buf, "%24s %8s %18s %18s %18s\n",
- "Block Name", "PSize", "Offset", "Used", "Total");
+ g_string_append_printf(buf, "%24s %8s %18s %18s %18s %18s %3s\n",
+ "Block Name", "PSize", "Offset", "Used", "Total",
+ "HVA", "RO");
+
RAMBLOCK_FOREACH(block) {
psize = size_to_str(block->page_size);
g_string_append_printf(buf, "%24s %8s 0x%016" PRIx64 " 0x%016" PRIx64
- " 0x%016" PRIx64 "\n", block->idstr, psize,
+ " 0x%016" PRIx64 " 0x%016" PRIx64 " %3s\n",
+ block->idstr, psize,
(uint64_t)block->offset,
(uint64_t)block->used_length,
- (uint64_t)block->max_length);
+ (uint64_t)block->max_length,
+ (uint64_t)(uintptr_t)block->host,
+ block->mr->readonly ? "ro" : "rw");
+
g_free(psize);
}
@@ -2927,6 +2933,8 @@
qemu_mutex_lock(&map_client_list_lock);
client->bh = bh;
QLIST_INSERT_HEAD(&map_client_list, client, link);
+ /* Write map_client_list before reading in_use. */
+ smp_mb();
if (!qatomic_read(&bounce.in_use)) {
cpu_notify_map_clients_locked();
}
@@ -3116,6 +3124,7 @@
qemu_vfree(bounce.buffer);
bounce.buffer = NULL;
memory_region_unref(bounce.mr);
+ /* Clear in_use before reading map_client_list. */
qatomic_mb_set(&bounce.in_use, false);
cpu_notify_map_clients();
}
diff --git a/softmmu/runstate.c b/softmmu/runstate.c
index 9b3611d..d1e0458 100644
--- a/softmmu/runstate.c
+++ b/softmmu/runstate.c
@@ -30,7 +30,7 @@
#include "crypto/cipher.h"
#include "crypto/init.h"
#include "exec/cpu-common.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
#include "hw/boards.h"
#include "migration/misc.h"
#include "migration/postcopy-ram.h"
diff --git a/stubs/meson.build b/stubs/meson.build
index 7657467..b2b5956 100644
--- a/stubs/meson.build
+++ b/stubs/meson.build
@@ -61,4 +61,5 @@
else
stub_ss.add(files('qdev.c'))
endif
+stub_ss.add(files('semihost-all.c'))
stub_ss.add(when: 'CONFIG_VFIO_USER_SERVER', if_false: files('vfio-user-obj.c'))
diff --git a/stubs/semihost-all.c b/stubs/semihost-all.c
new file mode 100644
index 0000000..a2a1fc9
--- /dev/null
+++ b/stubs/semihost-all.c
@@ -0,0 +1,17 @@
+/*
+ * Semihosting Stubs for all targets
+ *
+ * Copyright (c) 2023 Linaro Ltd
+ *
+ * Stubs for all targets that don't actually do semihosting.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "semihosting/semihost.h"
+
+SemihostingTarget semihosting_get_target(void)
+{
+ return SEMIHOSTING_TARGET_AUTO;
+}
diff --git a/stubs/semihost.c b/stubs/semihost.c
index d65c9fd..aad7a70 100644
--- a/stubs/semihost.c
+++ b/stubs/semihost.c
@@ -28,11 +28,6 @@
return false;
}
-SemihostingTarget semihosting_get_target(void)
-{
- return SEMIHOSTING_TARGET_AUTO;
-}
-
/*
* All the rest are empty subs. We could g_assert_not_reached() but
* that adds extra weight to the final binary. Waste not want not.
diff --git a/target/alpha/gdbstub.c b/target/alpha/gdbstub.c
index 7db14f4..0f8fa15 100644
--- a/target/alpha/gdbstub.c
+++ b/target/alpha/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
int alpha_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
{
diff --git a/target/alpha/sys_helper.c b/target/alpha/sys_helper.c
index 25f6cb8..c83c92d 100644
--- a/target/alpha/sys_helper.c
+++ b/target/alpha/sys_helper.c
@@ -20,6 +20,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/exec-all.h"
+#include "exec/tb-flush.h"
#include "exec/helper-proto.h"
#include "sysemu/runstate.h"
#include "sysemu/sysemu.h"
diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c
index 3f799f5..78105b8 100644
--- a/target/arm/gdbstub.c
+++ b/target/arm/gdbstub.c
@@ -20,6 +20,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "internals.h"
#include "cpregs.h"
diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c
index 3bee892..ec1e07f 100644
--- a/target/arm/gdbstub64.c
+++ b/target/arm/gdbstub64.c
@@ -20,7 +20,7 @@
#include "qemu/log.h"
#include "cpu.h"
#include "internals.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
int aarch64_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
{
diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c
index 0972a4b..c3edf16 100644
--- a/target/arm/tcg/helper-a64.c
+++ b/target/arm/tcg/helper-a64.c
@@ -20,7 +20,7 @@
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "exec/helper-proto.h"
#include "qemu/host-utils.h"
#include "qemu/log.h"
diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c
index 081fc3f..9758f22 100644
--- a/target/arm/tcg/m_helper.c
+++ b/target/arm/tcg/m_helper.c
@@ -9,6 +9,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "internals.h"
+#include "gdbstub/helpers.h"
#include "exec/helper-proto.h"
#include "qemu/main-loop.h"
#include "qemu/bitops.h"
diff --git a/target/avr/gdbstub.c b/target/avr/gdbstub.c
index 1c1b908..150344d 100644
--- a/target/avr/gdbstub.c
+++ b/target/avr/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
int avr_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
{
diff --git a/target/cris/gdbstub.c b/target/cris/gdbstub.c
index 2418d57..25c0ca3 100644
--- a/target/cris/gdbstub.c
+++ b/target/cris/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
int crisv10_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
{
diff --git a/target/hexagon/README b/target/hexagon/README
index 251960b..ebafc78 100644
--- a/target/hexagon/README
+++ b/target/hexagon/README
@@ -52,6 +52,7 @@
gen_tcg_func_table.py -> tcg_func_table_generated.c.inc
gen_helper_funcs.py -> helper_funcs_generated.c.inc
gen_idef_parser_funcs.py -> idef_parser_input.h
+ gen_analyze_funcs.py -> analyze_funcs_generated.c.inc
Qemu helper functions have 3 parts
DEF_HELPER declaration indicates the signature of the helper
@@ -87,7 +88,6 @@
TCGv RtV = hex_gpr[insn->regno[2]];
gen_helper_A2_add(RdV, cpu_env, RsV, RtV);
gen_log_reg_write(RdN, RdV);
- ctx_log_reg_write(ctx, RdN);
}
helper_funcs_generated.c.inc
@@ -136,12 +136,9 @@
won't fit in a TCGv or TCGv_i64, so we pass TCGv_ptr variables to pass the
address to helper functions. Here's an example for an HVX vector-add-word
istruction.
- static void generate_V6_vaddw(
- CPUHexagonState *env,
- DisasContext *ctx,
- Insn *insn,
- Packet *pkt)
+ static void generate_V6_vaddw(DisasContext *ctx)
{
+ Insn *insn __attribute__((unused)) = ctx->insn;
const int VdN = insn->regno[0];
const intptr_t VdV_off =
ctx_future_vreg_off(ctx, VdN, 1, true);
@@ -157,10 +154,7 @@
TCGv_ptr VvV = tcg_temp_new_ptr();
tcg_gen_addi_ptr(VuV, cpu_env, VuV_off);
tcg_gen_addi_ptr(VvV, cpu_env, VvV_off);
- TCGv slot = tcg_constant_tl(insn->slot);
- gen_helper_V6_vaddw(cpu_env, VdV, VuV, VvV, slot);
- gen_log_vreg_write(ctx, VdV_off, VdN, EXT_DFL, insn->slot, false);
- ctx_log_vreg_write(ctx, VdN, EXT_DFL, false);
+ gen_helper_V6_vaddw(cpu_env, VdV, VuV, VvV);
}
Notice that we also generate a variable named <operand>_off for each operand of
@@ -173,12 +167,9 @@
Finally, we notice that the override doesn't use the TCGv_ptr variables, so
we don't generate them when an override is present. Here is what we generate
when the override is present.
- static void generate_V6_vaddw(
- CPUHexagonState *env,
- DisasContext *ctx,
- Insn *insn,
- Packet *pkt)
+ static void generate_V6_vaddw(DisasContext *ctx)
{
+ Insn *insn __attribute__((unused)) = ctx->insn;
const int VdN = insn->regno[0];
const intptr_t VdV_off =
ctx_future_vreg_off(ctx, VdN, 1, true);
@@ -189,10 +180,14 @@
const intptr_t VvV_off =
vreg_src_off(ctx, VvN);
fGEN_TCG_V6_vaddw({ fHIDE(int i;) fVFOREACH(32, i) { VdV.w[i] = VuV.w[i] + VvV.w[i] ; } });
- gen_log_vreg_write(ctx, VdV_off, VdN, EXT_DFL, insn->slot, false);
- ctx_log_vreg_write(ctx, VdN, EXT_DFL, false);
}
+We also generate an analyze_<tag> function for each instruction. Currently,
+these functions record the writes to registers by calling ctx_log_*. During
+gen_start_packet, we invoke the analyze_<tag> function for each instruction in
+the packet, and we mark the implicit writes. After the analysis is performed,
+we initialize hex_new_value for each of the predicated assignments.
+
In addition to instruction semantics, we use a generator to create the decode
tree. This generation is also a two step process. The first step is to run
target/hexagon/gen_dectree_import.c to produce
@@ -277,10 +272,8 @@
VRegs Vector registers
future_VRegs Registers to be stored during packet commit
tmp_VRegs Temporary registers *not* stored during commit
- VRegs_updated Mask of predicated vector writes
QRegs Q (vector predicate) registers
future_QRegs Registers to be stored during packet commit
- QRegs_updated Mask of predicated vector writes
*** Debugging ***
diff --git a/target/hexagon/attribs_def.h.inc b/target/hexagon/attribs_def.h.inc
index 5d2a102..9874d16 100644
--- a/target/hexagon/attribs_def.h.inc
+++ b/target/hexagon/attribs_def.h.inc
@@ -44,6 +44,7 @@
DEF_ATTRIB(MEMSIZE_2B, "Memory width is 2 bytes", "", "")
DEF_ATTRIB(MEMSIZE_4B, "Memory width is 4 bytes", "", "")
DEF_ATTRIB(MEMSIZE_8B, "Memory width is 8 bytes", "", "")
+DEF_ATTRIB(SCALAR_LOAD, "Load is scalar", "", "")
DEF_ATTRIB(SCALAR_STORE, "Store is scalar", "", "")
DEF_ATTRIB(REGWRSIZE_1B, "Memory width is 1 byte", "", "")
DEF_ATTRIB(REGWRSIZE_2B, "Memory width is 2 bytes", "", "")
diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h
index 34c0ae0..81b663e 100644
--- a/target/hexagon/cpu.h
+++ b/target/hexagon/cpu.h
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -111,11 +111,8 @@
MMVector future_VRegs[VECTOR_TEMPS_MAX] QEMU_ALIGNED(16);
MMVector tmp_VRegs[VECTOR_TEMPS_MAX] QEMU_ALIGNED(16);
- VRegMask VRegs_updated;
-
MMQReg QRegs[NUM_QREGS] QEMU_ALIGNED(16);
MMQReg future_QRegs[NUM_QREGS] QEMU_ALIGNED(16);
- QRegMask QRegs_updated;
/* Temporaries used within instructions */
MMVectorPair VuuV QEMU_ALIGNED(16);
diff --git a/target/hexagon/gdbstub.c b/target/hexagon/gdbstub.c
index d152d01..46083da 100644
--- a/target/hexagon/gdbstub.c
+++ b/target/hexagon/gdbstub.c
@@ -16,7 +16,7 @@
*/
#include "qemu/osdep.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "cpu.h"
#include "internal.h"
diff --git a/target/hexagon/gen_analyze_funcs.py b/target/hexagon/gen_analyze_funcs.py
new file mode 100755
index 0000000..ebd3e7a
--- /dev/null
+++ b/target/hexagon/gen_analyze_funcs.py
@@ -0,0 +1,252 @@
+#!/usr/bin/env python3
+
+##
+## Copyright(c) 2022-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sys
+import re
+import string
+import hex_common
+
+##
+## Helpers for gen_analyze_func
+##
+def is_predicated(tag):
+ return 'A_CONDEXEC' in hex_common.attribdict[tag]
+
+def analyze_opn_old(f, tag, regtype, regid, regno):
+ regN = "%s%sN" % (regtype, regid)
+ predicated = "true" if is_predicated(tag) else "false"
+ if (regtype == "R"):
+ if (regid in {"ss", "tt"}):
+ f.write("// const int %s = insn->regno[%d];\n" % \
+ (regN, regno))
+ elif (regid in {"dd", "ee", "xx", "yy"}):
+ f.write(" const int %s = insn->regno[%d];\n" % (regN, regno))
+ f.write(" ctx_log_reg_write_pair(ctx, %s, %s);\n" % \
+ (regN, predicated))
+ elif (regid in {"s", "t", "u", "v"}):
+ f.write("// const int %s = insn->regno[%d];\n" % \
+ (regN, regno))
+ elif (regid in {"d", "e", "x", "y"}):
+ f.write(" const int %s = insn->regno[%d];\n" % (regN, regno))
+ f.write(" ctx_log_reg_write(ctx, %s, %s);\n" % \
+ (regN, predicated))
+ else:
+ print("Bad register parse: ", regtype, regid)
+ elif (regtype == "P"):
+ if (regid in {"s", "t", "u", "v"}):
+ f.write("// const int %s = insn->regno[%d];\n" % \
+ (regN, regno))
+ elif (regid in {"d", "e", "x"}):
+ f.write(" const int %s = insn->regno[%d];\n" % (regN, regno))
+ f.write(" ctx_log_pred_write(ctx, %s);\n" % (regN))
+ else:
+ print("Bad register parse: ", regtype, regid)
+ elif (regtype == "C"):
+ if (regid == "ss"):
+ f.write("// const int %s = insn->regno[%d] + HEX_REG_SA0;\n" % \
+ (regN, regno))
+ elif (regid == "dd"):
+ f.write(" const int %s = insn->regno[%d] + HEX_REG_SA0;\n" % \
+ (regN, regno))
+ f.write(" ctx_log_reg_write_pair(ctx, %s, %s);\n" % \
+ (regN, predicated))
+ elif (regid == "s"):
+ f.write("// const int %s = insn->regno[%d] + HEX_REG_SA0;\n" % \
+ (regN, regno))
+ elif (regid == "d"):
+ f.write(" const int %s = insn->regno[%d] + HEX_REG_SA0;\n" % \
+ (regN, regno))
+ f.write(" ctx_log_reg_write(ctx, %s, %s);\n" % \
+ (regN, predicated))
+ else:
+ print("Bad register parse: ", regtype, regid)
+ elif (regtype == "M"):
+ if (regid == "u"):
+ f.write("// const int %s = insn->regno[%d];\n"% \
+ (regN, regno))
+ else:
+ print("Bad register parse: ", regtype, regid)
+ elif (regtype == "V"):
+ newv = "EXT_DFL"
+ if (hex_common.is_new_result(tag)):
+ newv = "EXT_NEW"
+ elif (hex_common.is_tmp_result(tag)):
+ newv = "EXT_TMP"
+ if (regid in {"dd", "xx"}):
+ f.write(" const int %s = insn->regno[%d];\n" %\
+ (regN, regno))
+ f.write(" ctx_log_vreg_write_pair(ctx, %s, %s, %s);\n" % \
+ (regN, newv, predicated))
+ elif (regid in {"uu", "vv"}):
+ f.write("// const int %s = insn->regno[%d];\n" % \
+ (regN, regno))
+ elif (regid in {"s", "u", "v", "w"}):
+ f.write("// const int %s = insn->regno[%d];\n" % \
+ (regN, regno))
+ elif (regid in {"d", "x", "y"}):
+ f.write(" const int %s = insn->regno[%d];\n" % \
+ (regN, regno))
+ f.write(" ctx_log_vreg_write(ctx, %s, %s, %s);\n" % \
+ (regN, newv, predicated))
+ else:
+ print("Bad register parse: ", regtype, regid)
+ elif (regtype == "Q"):
+ if (regid in {"d", "e", "x"}):
+ f.write(" const int %s = insn->regno[%d];\n" % \
+ (regN, regno))
+ f.write(" ctx_log_qreg_write(ctx, %s);\n" % (regN))
+ elif (regid in {"s", "t", "u", "v"}):
+ f.write("// const int %s = insn->regno[%d];\n" % \
+ (regN, regno))
+ else:
+ print("Bad register parse: ", regtype, regid)
+ elif (regtype == "G"):
+ if (regid in {"dd"}):
+ f.write("// const int %s = insn->regno[%d];\n" % \
+ (regN, regno))
+ elif (regid in {"d"}):
+ f.write("// const int %s = insn->regno[%d];\n" % \
+ (regN, regno))
+ elif (regid in {"ss"}):
+ f.write("// const int %s = insn->regno[%d];\n" % \
+ (regN, regno))
+ elif (regid in {"s"}):
+ f.write("// const int %s = insn->regno[%d];\n" % \
+ (regN, regno))
+ else:
+ print("Bad register parse: ", regtype, regid)
+ elif (regtype == "S"):
+ if (regid in {"dd"}):
+ f.write("// const int %s = insn->regno[%d];\n" % \
+ (regN, regno))
+ elif (regid in {"d"}):
+ f.write("// const int %s = insn->regno[%d];\n" % \
+ (regN, regno))
+ elif (regid in {"ss"}):
+ f.write("// const int %s = insn->regno[%d];\n" % \
+ (regN, regno))
+ elif (regid in {"s"}):
+ f.write("// const int %s = insn->regno[%d];\n" % \
+ (regN, regno))
+ else:
+ print("Bad register parse: ", regtype, regid)
+ else:
+ print("Bad register parse: ", regtype, regid)
+
+def analyze_opn_new(f, tag, regtype, regid, regno):
+ regN = "%s%sN" % (regtype, regid)
+ if (regtype == "N"):
+ if (regid in {"s", "t"}):
+ f.write("// const int %s = insn->regno[%d];\n" % \
+ (regN, regno))
+ else:
+ print("Bad register parse: ", regtype, regid)
+ elif (regtype == "P"):
+ if (regid in {"t", "u", "v"}):
+ f.write("// const int %s = insn->regno[%d];\n" % \
+ (regN, regno))
+ else:
+ print("Bad register parse: ", regtype, regid)
+ elif (regtype == "O"):
+ if (regid == "s"):
+ f.write("// const int %s = insn->regno[%d];\n" % \
+ (regN, regno))
+ else:
+ print("Bad register parse: ", regtype, regid)
+ else:
+ print("Bad register parse: ", regtype, regid)
+
+def analyze_opn(f, tag, regtype, regid, toss, numregs, i):
+ if (hex_common.is_pair(regid)):
+ analyze_opn_old(f, tag, regtype, regid, i)
+ elif (hex_common.is_single(regid)):
+ if hex_common.is_old_val(regtype, regid, tag):
+ analyze_opn_old(f,tag, regtype, regid, i)
+ elif hex_common.is_new_val(regtype, regid, tag):
+ analyze_opn_new(f, tag, regtype, regid, i)
+ else:
+ print("Bad register parse: ", regtype, regid, toss, numregs)
+ else:
+ print("Bad register parse: ", regtype, regid, toss, numregs)
+
+##
+## Generate the code to analyze the instruction
+## For A2_add: Rd32=add(Rs32,Rt32), { RdV=RsV+RtV;}
+## We produce:
+## static void analyze_A2_add(DisasContext *ctx)
+## {
+## Insn *insn G_GNUC_UNUSED = ctx->insn;
+## const int RdN = insn->regno[0];
+## ctx_log_reg_write(ctx, RdN, false);
+## // const int RsN = insn->regno[1];
+## // const int RtN = insn->regno[2];
+## }
+##
+def gen_analyze_func(f, tag, regs, imms):
+ f.write("static void analyze_%s(DisasContext *ctx)\n" %tag)
+ f.write('{\n')
+
+ f.write(" Insn *insn G_GNUC_UNUSED = ctx->insn;\n")
+
+ i=0
+ ## Analyze all the registers
+ for regtype, regid, toss, numregs in regs:
+ analyze_opn(f, tag, regtype, regid, toss, numregs, i)
+ i += 1
+
+ has_generated_helper = (not hex_common.skip_qemu_helper(tag) and
+ not hex_common.is_idef_parser_enabled(tag))
+ if (has_generated_helper and
+ 'A_SCALAR_LOAD' in hex_common.attribdict[tag]):
+ f.write(" ctx->need_pkt_has_store_s1 = true;\n")
+
+ f.write("}\n\n")
+
+def main():
+ hex_common.read_semantics_file(sys.argv[1])
+ hex_common.read_attribs_file(sys.argv[2])
+ hex_common.read_overrides_file(sys.argv[3])
+ hex_common.read_overrides_file(sys.argv[4])
+ ## Whether or not idef-parser is enabled is
+ ## determined by the number of arguments to
+ ## this script:
+ ##
+ ## 5 args. -> not enabled,
+ ## 6 args. -> idef-parser enabled.
+ ##
+ ## The 6:th arg. then holds a list of the successfully
+ ## parsed instructions.
+ is_idef_parser_enabled = len(sys.argv) > 6
+ if is_idef_parser_enabled:
+ hex_common.read_idef_parser_enabled_file(sys.argv[5])
+ hex_common.calculate_attribs()
+ tagregs = hex_common.get_tagregs()
+ tagimms = hex_common.get_tagimms()
+
+ with open(sys.argv[-1], 'w') as f:
+ f.write("#ifndef HEXAGON_TCG_FUNCS_H\n")
+ f.write("#define HEXAGON_TCG_FUNCS_H\n\n")
+
+ for tag in hex_common.tags:
+ gen_analyze_func(f, tag, tagregs[tag], tagimms[tag])
+
+ f.write("#endif /* HEXAGON_TCG_FUNCS_H */\n")
+
+if __name__ == "__main__":
+ main()
diff --git a/target/hexagon/gen_helper_funcs.py b/target/hexagon/gen_helper_funcs.py
index 19e9883..7a224b6 100755
--- a/target/hexagon/gen_helper_funcs.py
+++ b/target/hexagon/gen_helper_funcs.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
##
-## Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
+## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
@@ -226,6 +226,14 @@ def gen_helper_function(f, tag, tagregs, tagimms):
print("Bad register parse: ",regtype,regid,toss,numregs)
i += 1
+ ## For conditional instructions, we pass in the destination register
+ if 'A_CONDEXEC' in hex_common.attribdict[tag]:
+ for regtype, regid, toss, numregs in regs:
+ if (hex_common.is_writeonly(regid) and
+ not hex_common.is_hvx_reg(regtype)):
+ gen_helper_arg_opn(f, regtype, regid, i, tag)
+ i += 1
+
## Arguments to the helper function are the source regs and immediates
for regtype,regid,toss,numregs in regs:
if (hex_common.is_read(regid)):
@@ -262,10 +270,11 @@ def gen_helper_function(f, tag, tagregs, tagimms):
if hex_common.need_ea(tag): gen_decl_ea(f)
## Declare the return variable
i=0
- for regtype,regid,toss,numregs in regs:
- if (hex_common.is_writeonly(regid)):
- gen_helper_dest_decl_opn(f,regtype,regid,i)
- i += 1
+ if 'A_CONDEXEC' not in hex_common.attribdict[tag]:
+ for regtype,regid,toss,numregs in regs:
+ if (hex_common.is_writeonly(regid)):
+ gen_helper_dest_decl_opn(f,regtype,regid,i)
+ i += 1
for regtype,regid,toss,numregs in regs:
if (hex_common.is_read(regid)):
diff --git a/target/hexagon/gen_helper_protos.py b/target/hexagon/gen_helper_protos.py
index 674bf37..ddddc9e 100755
--- a/target/hexagon/gen_helper_protos.py
+++ b/target/hexagon/gen_helper_protos.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
##
-## Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
+## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
@@ -87,6 +87,7 @@ def gen_helper_prototype(f, tag, tagregs, tagimms):
if hex_common.need_slot(tag): def_helper_size += 1
if hex_common.need_PC(tag): def_helper_size += 1
if hex_common.helper_needs_next_PC(tag): def_helper_size += 1
+ if hex_common.need_condexec_reg(tag, regs): def_helper_size += 1
f.write('DEF_HELPER_%s(%s' % (def_helper_size, tag))
## The return type is void
f.write(', void' )
@@ -96,6 +97,7 @@ def gen_helper_prototype(f, tag, tagregs, tagimms):
if hex_common.need_part1(tag): def_helper_size += 1
if hex_common.need_slot(tag): def_helper_size += 1
if hex_common.need_PC(tag): def_helper_size += 1
+ if hex_common.need_condexec_reg(tag, regs): def_helper_size += 1
if hex_common.helper_needs_next_PC(tag): def_helper_size += 1
f.write('DEF_HELPER_%s(%s' % (def_helper_size, tag))
@@ -121,6 +123,14 @@ def gen_helper_prototype(f, tag, tagregs, tagimms):
gen_def_helper_opn(f, tag, regtype, regid, toss, numregs, i)
i += 1
+ ## For conditional instructions, we pass in the destination register
+ if 'A_CONDEXEC' in hex_common.attribdict[tag]:
+ for regtype, regid, toss, numregs in regs:
+ if (hex_common.is_writeonly(regid) and
+ not hex_common.is_hvx_reg(regtype)):
+ gen_def_helper_opn(f, tag, regtype, regid, toss, numregs, i)
+ i += 1
+
## Generate the qemu type for each input operand (regs and immediates)
for regtype,regid,toss,numregs in regs:
if (hex_common.is_read(regid)):
diff --git a/target/hexagon/gen_tcg.h b/target/hexagon/gen_tcg.h
index b2e7880..bcf0cf4 100644
--- a/target/hexagon/gen_tcg.h
+++ b/target/hexagon/gen_tcg.h
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -332,8 +332,6 @@
tcg_gen_movi_tl(EA, 0); \
PRED; \
CHECK_NOSHUF_PRED(GET_EA, SIZE, LSB); \
- PRED_LOAD_CANCEL(LSB, EA); \
- tcg_gen_movi_tl(RdV, 0); \
tcg_gen_brcondi_tl(TCG_COND_EQ, LSB, 0, label); \
fLOAD(1, SIZE, SIGN, EA, RdV); \
gen_set_label(label); \
@@ -391,8 +389,6 @@
tcg_gen_movi_tl(EA, 0); \
PRED; \
CHECK_NOSHUF_PRED(GET_EA, 8, LSB); \
- PRED_LOAD_CANCEL(LSB, EA); \
- tcg_gen_movi_i64(RddV, 0); \
tcg_gen_brcondi_tl(TCG_COND_EQ, LSB, 0, label); \
fLOAD(1, 8, u, EA, RddV); \
gen_set_label(label); \
@@ -419,16 +415,16 @@
#define fGEN_TCG_STORE(SHORTCODE) \
do { \
- TCGv HALF = tcg_temp_new(); \
- TCGv BYTE = tcg_temp_new(); \
+ TCGv HALF G_GNUC_UNUSED = tcg_temp_new(); \
+ TCGv BYTE G_GNUC_UNUSED = tcg_temp_new(); \
SHORTCODE; \
} while (0)
#define fGEN_TCG_STORE_pcr(SHIFT, STORE) \
do { \
TCGv ireg = tcg_temp_new(); \
- TCGv HALF = tcg_temp_new(); \
- TCGv BYTE = tcg_temp_new(); \
+ TCGv HALF G_GNUC_UNUSED = tcg_temp_new(); \
+ TCGv BYTE G_GNUC_UNUSED = tcg_temp_new(); \
tcg_gen_mov_tl(EA, RxV); \
gen_read_ireg(ireg, MuV, SHIFT); \
gen_helper_fcircadd(RxV, RxV, ireg, MuV, hex_gpr[HEX_REG_CS0 + MuN]); \
@@ -492,6 +488,59 @@
fGEN_TCG_STORE_pcr(2, fSTORE(1, 4, EA, NtN))
/*
+ * dealloc_return
+ * Assembler mapped to
+ * r31:30 = dealloc_return(r30):raw
+ */
+#define fGEN_TCG_L4_return(SHORTCODE) \
+ gen_return(ctx, RddV, RsV)
+
+/*
+ * sub-instruction version (no RddV, so handle it manually)
+ */
+#define fGEN_TCG_SL2_return(SHORTCODE) \
+ do { \
+ TCGv_i64 RddV = get_result_gpr_pair(ctx, HEX_REG_FP); \
+ gen_return(ctx, RddV, hex_gpr[HEX_REG_FP]); \
+ gen_log_reg_write_pair(HEX_REG_FP, RddV); \
+ } while (0)
+
+/*
+ * Conditional returns follow this naming convention
+ * _t predicate true
+ * _f predicate false
+ * _tnew_pt predicate.new true predict taken
+ * _fnew_pt predicate.new false predict taken
+ * _tnew_pnt predicate.new true predict not taken
+ * _fnew_pnt predicate.new false predict not taken
+ * Predictions are not modelled in QEMU
+ *
+ * Example:
+ * if (p1) r31:30 = dealloc_return(r30):raw
+ */
+#define fGEN_TCG_L4_return_t(SHORTCODE) \
+ gen_cond_return(ctx, RddV, RsV, PvV, TCG_COND_EQ);
+#define fGEN_TCG_L4_return_f(SHORTCODE) \
+ gen_cond_return(ctx, RddV, RsV, PvV, TCG_COND_NE)
+#define fGEN_TCG_L4_return_tnew_pt(SHORTCODE) \
+ gen_cond_return(ctx, RddV, RsV, PvN, TCG_COND_EQ)
+#define fGEN_TCG_L4_return_fnew_pt(SHORTCODE) \
+ gen_cond_return(ctx, RddV, RsV, PvN, TCG_COND_NE)
+#define fGEN_TCG_L4_return_tnew_pnt(SHORTCODE) \
+ gen_cond_return(ctx, RddV, RsV, PvN, TCG_COND_EQ)
+#define fGEN_TCG_L4_return_fnew_pnt(SHORTCODE) \
+ gen_cond_return(ctx, RddV, RsV, PvN, TCG_COND_NE)
+
+#define fGEN_TCG_SL2_return_t(SHORTCODE) \
+ gen_cond_return_subinsn(ctx, TCG_COND_EQ, hex_pred[0])
+#define fGEN_TCG_SL2_return_f(SHORTCODE) \
+ gen_cond_return_subinsn(ctx, TCG_COND_NE, hex_pred[0])
+#define fGEN_TCG_SL2_return_tnew(SHORTCODE) \
+ gen_cond_return_subinsn(ctx, TCG_COND_EQ, hex_new_pred_value[0])
+#define fGEN_TCG_SL2_return_fnew(SHORTCODE) \
+ gen_cond_return_subinsn(ctx, TCG_COND_NE, hex_new_pred_value[0])
+
+/*
* Mathematical operations with more than one definition require
* special handling
*/
@@ -589,14 +638,24 @@
#define fGEN_TCG_J2_call(SHORTCODE) \
gen_call(ctx, riV)
+#define fGEN_TCG_J2_callr(SHORTCODE) \
+ gen_callr(ctx, RsV)
#define fGEN_TCG_J2_callt(SHORTCODE) \
gen_cond_call(ctx, PuV, TCG_COND_EQ, riV)
#define fGEN_TCG_J2_callf(SHORTCODE) \
gen_cond_call(ctx, PuV, TCG_COND_NE, riV)
+#define fGEN_TCG_J2_callrt(SHORTCODE) \
+ gen_cond_callr(ctx, TCG_COND_EQ, PuV, RsV)
+#define fGEN_TCG_J2_callrf(SHORTCODE) \
+ gen_cond_callr(ctx, TCG_COND_NE, PuV, RsV)
#define fGEN_TCG_J2_endloop0(SHORTCODE) \
gen_endloop0(ctx)
+#define fGEN_TCG_J2_endloop1(SHORTCODE) \
+ gen_endloop1(ctx)
+#define fGEN_TCG_J2_endloop01(SHORTCODE) \
+ gen_endloop01(ctx)
/*
* Compound compare and jump instructions
@@ -986,6 +1045,19 @@
#define fGEN_TCG_S2_asl_r_r_sat(SHORTCODE) \
gen_asl_r_r_sat(RdV, RsV, RtV)
+#define fGEN_TCG_SL2_jumpr31(SHORTCODE) \
+ gen_jumpr(ctx, hex_gpr[HEX_REG_LR])
+
+#define fGEN_TCG_SL2_jumpr31_t(SHORTCODE) \
+ gen_cond_jumpr31(ctx, TCG_COND_EQ, hex_pred[0])
+#define fGEN_TCG_SL2_jumpr31_f(SHORTCODE) \
+ gen_cond_jumpr31(ctx, TCG_COND_NE, hex_pred[0])
+
+#define fGEN_TCG_SL2_jumpr31_tnew(SHORTCODE) \
+ gen_cond_jumpr31(ctx, TCG_COND_EQ, hex_new_pred_value[0])
+#define fGEN_TCG_SL2_jumpr31_fnew(SHORTCODE) \
+ gen_cond_jumpr31(ctx, TCG_COND_NE, hex_new_pred_value[0])
+
/* Floating point */
#define fGEN_TCG_F2_conv_sf2df(SHORTCODE) \
gen_helper_conv_sf2df(RddV, cpu_env, RsV)
diff --git a/target/hexagon/gen_tcg_funcs.py b/target/hexagon/gen_tcg_funcs.py
index 02cb52c..fa93e18 100755
--- a/target/hexagon/gen_tcg_funcs.py
+++ b/target/hexagon/gen_tcg_funcs.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
##
-## Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
+## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
@@ -30,37 +30,33 @@ def gen_decl_ea_tcg(f, tag):
def genptr_decl_pair_writable(f, tag, regtype, regid, regno):
regN="%s%sN" % (regtype,regid)
- f.write(" TCGv_i64 %s%sV = tcg_temp_new_i64();\n" % \
- (regtype, regid))
- if (regtype == "C"):
+ if (regtype == "R"):
+ f.write(" const int %s = insn->regno[%d];\n" % (regN, regno))
+ elif (regtype == "C"):
f.write(" const int %s = insn->regno[%d] + HEX_REG_SA0;\n" % \
(regN, regno))
else:
- f.write(" const int %s = insn->regno[%d];\n" % (regN, regno))
- if ('A_CONDEXEC' in hex_common.attribdict[tag]):
- f.write(" if (!is_preloaded(ctx, %s)) {\n" % regN)
- f.write(" tcg_gen_mov_tl(hex_new_value[%s], hex_gpr[%s]);\n" % \
- (regN, regN))
- f.write(" }\n")
- f.write(" if (!is_preloaded(ctx, %s + 1)) {\n" % regN)
- f.write(" tcg_gen_mov_tl(hex_new_value[%s + 1], hex_gpr[%s + 1]);\n" % \
- (regN, regN))
- f.write(" }\n")
+ print("Bad register parse: ", regtype, regid)
+ f.write(" TCGv_i64 %s%sV = get_result_gpr_pair(ctx, %s);\n" % \
+ (regtype, regid, regN))
def genptr_decl_writable(f, tag, regtype, regid, regno):
regN="%s%sN" % (regtype,regid)
- f.write(" TCGv %s%sV = tcg_temp_new();\n" % \
- (regtype, regid))
- if (regtype == "C"):
+ if (regtype == "R"):
+ f.write(" const int %s = insn->regno[%d];\n" % (regN, regno))
+ f.write(" TCGv %s%sV = get_result_gpr(ctx, %s);\n" % \
+ (regtype, regid, regN))
+ elif (regtype == "C"):
f.write(" const int %s = insn->regno[%d] + HEX_REG_SA0;\n" % \
(regN, regno))
- else:
+ f.write(" TCGv %s%sV = get_result_gpr(ctx, %s);\n" % \
+ (regtype, regid, regN))
+ elif (regtype == "P"):
f.write(" const int %s = insn->regno[%d];\n" % (regN, regno))
- if ('A_CONDEXEC' in hex_common.attribdict[tag]):
- f.write(" if (!is_preloaded(ctx, %s)) {\n" % regN)
- f.write(" tcg_gen_mov_tl(hex_new_value[%s], hex_gpr[%s]);\n" % \
- (regN, regN))
- f.write(" }\n")
+ f.write(" TCGv %s%sV = tcg_temp_new();\n" % \
+ (regtype, regid))
+ else:
+ print("Bad register parse: ", regtype, regid)
def genptr_decl(f, tag, regtype, regid, regno):
regN="%s%sN" % (regtype,regid)
@@ -166,17 +162,6 @@ def genptr_decl(f, tag, regtype, regid, regno):
f.write(" ctx_future_vreg_off(ctx, %s%sN," % \
(regtype, regid))
f.write(" 1, true);\n");
- if 'A_CONDEXEC' in hex_common.attribdict[tag]:
- f.write(" if (!is_vreg_preloaded(ctx, %s)) {\n" % (regN))
- f.write(" intptr_t src_off =")
- f.write(" offsetof(CPUHexagonState, VRegs[%s%sN]);\n"% \
- (regtype, regid))
- f.write(" tcg_gen_gvec_mov(MO_64, %s%sV_off,\n" % \
- (regtype, regid))
- f.write(" src_off,\n")
- f.write(" sizeof(MMVector),\n")
- f.write(" sizeof(MMVector));\n")
- f.write(" }\n")
if (not hex_common.skip_qemu_helper(tag)):
f.write(" TCGv_ptr %s%sV = tcg_temp_new_ptr();\n" % \
@@ -191,8 +176,7 @@ def genptr_decl(f, tag, regtype, regid, regno):
(regtype, regid, regno))
f.write(" const intptr_t %s%sV_off =\n" % \
(regtype, regid))
- f.write(" offsetof(CPUHexagonState,\n")
- f.write(" future_QRegs[%s%sN]);\n" % \
+ f.write(" get_result_qreg(ctx, %s%sN);\n" % \
(regtype, regid))
if (not hex_common.skip_qemu_helper(tag)):
f.write(" TCGv_ptr %s%sV = tcg_temp_new_ptr();\n" % \
@@ -274,8 +258,12 @@ def genptr_src_read(f, tag, regtype, regid):
f.write(" hex_gpr[%s%sN + 1]);\n" % \
(regtype, regid))
elif (regid in {"x", "y"}):
- f.write(" tcg_gen_mov_tl(%s%sV, hex_gpr[%s%sN]);\n" % \
- (regtype,regid,regtype,regid))
+ ## For read/write registers, we need to get the original value into
+ ## the result TCGv. For conditional instructions, this is done in
+ ## gen_start_packet. For unconditional instructions, we do it here.
+ if ('A_CONDEXEC' not in hex_common.attribdict[tag]):
+ f.write(" tcg_gen_mov_tl(%s%sV, hex_gpr[%s%sN]);\n" % \
+ (regtype, regid, regtype, regid))
elif (regid not in {"s", "t", "u", "v"}):
print("Bad register parse: ", regtype, regid)
elif (regtype == "P"):
@@ -385,37 +373,22 @@ def gen_helper_call_imm(f,immlett):
f.write(", tcgv_%s" % hex_common.imm_name(immlett))
def genptr_dst_write_pair(f, tag, regtype, regid):
- if ('A_CONDEXEC' in hex_common.attribdict[tag]):
- f.write(" gen_log_predicated_reg_write_pair(%s%sN, %s%sV, insn->slot);\n" % \
- (regtype, regid, regtype, regid))
- else:
- f.write(" gen_log_reg_write_pair(%s%sN, %s%sV);\n" % \
- (regtype, regid, regtype, regid))
- f.write(" ctx_log_reg_write_pair(ctx, %s%sN);\n" % \
- (regtype, regid))
+ f.write(" gen_log_reg_write_pair(%s%sN, %s%sV);\n" % \
+ (regtype, regid, regtype, regid))
def genptr_dst_write(f, tag, regtype, regid):
if (regtype == "R"):
if (regid in {"dd", "xx", "yy"}):
genptr_dst_write_pair(f, tag, regtype, regid)
elif (regid in {"d", "e", "x", "y"}):
- if ('A_CONDEXEC' in hex_common.attribdict[tag]):
- f.write(" gen_log_predicated_reg_write(%s%sN, %s%sV,\n" % \
- (regtype, regid, regtype, regid))
- f.write(" insn->slot);\n")
- else:
- f.write(" gen_log_reg_write(%s%sN, %s%sV);\n" % \
- (regtype, regid, regtype, regid))
- f.write(" ctx_log_reg_write(ctx, %s%sN);\n" % \
- (regtype, regid))
+ f.write(" gen_log_reg_write(%s%sN, %s%sV);\n" % \
+ (regtype, regid, regtype, regid))
else:
print("Bad register parse: ", regtype, regid)
elif (regtype == "P"):
if (regid in {"d", "e", "x"}):
f.write(" gen_log_pred_write(ctx, %s%sN, %s%sV);\n" % \
(regtype, regid, regtype, regid))
- f.write(" ctx_log_pred_write(ctx, %s%sN);\n" % \
- (regtype, regid))
else:
print("Bad register parse: ", regtype, regid)
elif (regtype == "C"):
@@ -432,43 +405,18 @@ def genptr_dst_write(f, tag, regtype, regid):
def genptr_dst_write_ext(f, tag, regtype, regid, newv="EXT_DFL"):
if (regtype == "V"):
- if (regid in {"dd", "xx", "yy"}):
- if ('A_CONDEXEC' in hex_common.attribdict[tag]):
- is_predicated = "true"
- else:
- is_predicated = "false"
+ if (regid in {"xx"}):
f.write(" gen_log_vreg_write_pair(ctx, %s%sV_off, %s%sN, " % \
(regtype, regid, regtype, regid))
- f.write("%s, insn->slot, %s);\n" % \
- (newv, is_predicated))
- f.write(" ctx_log_vreg_write_pair(ctx, %s%sN, %s,\n" % \
- (regtype, regid, newv))
- f.write(" %s);\n" % (is_predicated))
- elif (regid in {"d", "x", "y"}):
- if ('A_CONDEXEC' in hex_common.attribdict[tag]):
- is_predicated = "true"
- else:
- is_predicated = "false"
- f.write(" gen_log_vreg_write(ctx, %s%sV_off, %s%sN, %s, " % \
+ f.write("%s);\n" % \
+ (newv))
+ elif (regid in {"y"}):
+ f.write(" gen_log_vreg_write(ctx, %s%sV_off, %s%sN, %s);\n" % \
(regtype, regid, regtype, regid, newv))
- f.write("insn->slot, %s);\n" % \
- (is_predicated))
- f.write(" ctx_log_vreg_write(ctx, %s%sN, %s, %s);\n" % \
- (regtype, regid, newv, is_predicated))
- else:
+ elif (regid not in {"dd", "d", "x"}):
print("Bad register parse: ", regtype, regid)
elif (regtype == "Q"):
- if (regid in {"d", "e", "x"}):
- if ('A_CONDEXEC' in hex_common.attribdict[tag]):
- is_predicated = "true"
- else:
- is_predicated = "false"
- f.write(" gen_log_qreg_write(%s%sV_off, %s%sN, %s, " % \
- (regtype, regid, regtype, regid, newv))
- f.write("insn->slot, %s);\n" % (is_predicated))
- f.write(" ctx_log_qreg_write(ctx, %s%sN, %s);\n" % \
- (regtype, regid, is_predicated))
- else:
+ if (regid not in {"d", "e", "x"}):
print("Bad register parse: ", regtype, regid)
else:
print("Bad register parse: ", regtype, regid)
@@ -500,15 +448,15 @@ def genptr_dst_write_opn(f,regtype, regid, tag):
## For A2_add: Rd32=add(Rs32,Rt32), { RdV=RsV+RtV;}
## We produce:
## static void generate_A2_add(DisasContext *ctx)
-## {
-## TCGv RdV = tcg_temp_new();
-## const int RdN = insn->regno[0];
-## TCGv RsV = hex_gpr[insn->regno[1]];
-## TCGv RtV = hex_gpr[insn->regno[2]];
-## <GEN>
-## gen_log_reg_write(RdN, RdV);
-## ctx_log_reg_write(ctx, RdN);
-## }
+## {
+## Insn *insn __attribute__((unused)) = ctx->insn;
+## const int RdN = insn->regno[0];
+## TCGv RdV = get_result_gpr(ctx, RdN);
+## TCGv RsV = hex_gpr[insn->regno[1]];
+## TCGv RtV = hex_gpr[insn->regno[2]];
+## <GEN>
+## gen_log_reg_write(RdN, RdV);
+## }
##
## where <GEN> depends on hex_common.skip_qemu_helper(tag)
## if hex_common.skip_qemu_helper(tag) is True
@@ -592,6 +540,14 @@ def gen_tcg_func(f, tag, regs, imms):
if (i > 0): f.write(", ")
f.write("cpu_env")
i=1
+ ## For conditional instructions, we pass in the destination register
+ if 'A_CONDEXEC' in hex_common.attribdict[tag]:
+ for regtype, regid, toss, numregs in regs:
+ if (hex_common.is_writeonly(regid) and
+ not hex_common.is_hvx_reg(regtype)):
+ gen_helper_call_opn(f, tag, regtype, regid, toss, \
+ numregs, i)
+ i += 1
for regtype,regid,toss,numregs in regs:
if (hex_common.is_written(regid)):
if (not hex_common.is_hvx_reg(regtype)):
diff --git a/target/hexagon/gen_tcg_hvx.h b/target/hexagon/gen_tcg_hvx.h
index 94f272e..d4aefe8 100644
--- a/target/hexagon/gen_tcg_hvx.h
+++ b/target/hexagon/gen_tcg_hvx.h
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -133,16 +133,11 @@
do { \
TCGv lsb = tcg_temp_new(); \
TCGLabel *false_label = gen_new_label(); \
- TCGLabel *end_label = gen_new_label(); \
tcg_gen_andi_tl(lsb, PsV, 1); \
tcg_gen_brcondi_tl(TCG_COND_NE, lsb, PRED, false_label); \
tcg_gen_gvec_mov(MO_64, VdV_off, VuV_off, \
sizeof(MMVector), sizeof(MMVector)); \
- tcg_gen_br(end_label); \
gen_set_label(false_label); \
- tcg_gen_ori_tl(hex_slot_cancelled, hex_slot_cancelled, \
- 1 << insn->slot); \
- gen_set_label(end_label); \
} while (0)
@@ -547,17 +542,12 @@
do { \
TCGv LSB = tcg_temp_new(); \
TCGLabel *false_label = gen_new_label(); \
- TCGLabel *end_label = gen_new_label(); \
GET_EA; \
PRED; \
tcg_gen_brcondi_tl(TCG_COND_EQ, LSB, 0, false_label); \
gen_vreg_load(ctx, DSTOFF, EA, true); \
INC; \
- tcg_gen_br(end_label); \
gen_set_label(false_label); \
- tcg_gen_ori_tl(hex_slot_cancelled, hex_slot_cancelled, \
- 1 << insn->slot); \
- gen_set_label(end_label); \
} while (0)
#define fGEN_TCG_PRED_VEC_LOAD_pred_pi \
@@ -717,17 +707,12 @@
do { \
TCGv LSB = tcg_temp_new(); \
TCGLabel *false_label = gen_new_label(); \
- TCGLabel *end_label = gen_new_label(); \
GET_EA; \
PRED; \
tcg_gen_brcondi_tl(TCG_COND_EQ, LSB, 0, false_label); \
gen_vreg_store(ctx, EA, SRCOFF, insn->slot, ALIGN); \
INC; \
- tcg_gen_br(end_label); \
gen_set_label(false_label); \
- tcg_gen_ori_tl(hex_slot_cancelled, hex_slot_cancelled, \
- 1 << insn->slot); \
- gen_set_label(end_label); \
} while (0)
#define fGEN_TCG_PRED_VEC_STORE_pred_pi(ALIGN) \
diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c
index 86bd093..bb274d4 100644
--- a/target/hexagon/genptr.c
+++ b/target/hexagon/genptr.c
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -68,26 +68,17 @@
}
}
-static inline void gen_log_predicated_reg_write(int rnum, TCGv val,
- uint32_t slot)
+static TCGv get_result_gpr(DisasContext *ctx, int rnum)
{
- TCGv zero = tcg_constant_tl(0);
- TCGv slot_mask = tcg_temp_new();
+ return hex_new_value[rnum];
+}
- tcg_gen_andi_tl(slot_mask, hex_slot_cancelled, 1 << slot);
- tcg_gen_movcond_tl(TCG_COND_EQ, hex_new_value[rnum], slot_mask, zero,
- val, hex_new_value[rnum]);
- if (HEX_DEBUG) {
- /*
- * Do this so HELPER(debug_commit_end) will know
- *
- * Note that slot_mask indicates the value is not written
- * (i.e., slot was cancelled), so we create a true/false value before
- * or'ing with hex_reg_written[rnum].
- */
- tcg_gen_setcond_tl(TCG_COND_EQ, slot_mask, slot_mask, zero);
- tcg_gen_or_tl(hex_reg_written[rnum], hex_reg_written[rnum], slot_mask);
- }
+static TCGv_i64 get_result_gpr_pair(DisasContext *ctx, int rnum)
+{
+ TCGv_i64 result = tcg_temp_new_i64();
+ tcg_gen_concat_i32_i64(result, hex_new_value[rnum],
+ hex_new_value[rnum + 1]);
+ return result;
}
void gen_log_reg_write(int rnum, TCGv val)
@@ -102,39 +93,6 @@
}
}
-static void gen_log_predicated_reg_write_pair(int rnum, TCGv_i64 val,
- uint32_t slot)
-{
- TCGv val32 = tcg_temp_new();
- TCGv zero = tcg_constant_tl(0);
- TCGv slot_mask = tcg_temp_new();
-
- tcg_gen_andi_tl(slot_mask, hex_slot_cancelled, 1 << slot);
- /* Low word */
- tcg_gen_extrl_i64_i32(val32, val);
- tcg_gen_movcond_tl(TCG_COND_EQ, hex_new_value[rnum],
- slot_mask, zero,
- val32, hex_new_value[rnum]);
- /* High word */
- tcg_gen_extrh_i64_i32(val32, val);
- tcg_gen_movcond_tl(TCG_COND_EQ, hex_new_value[rnum + 1],
- slot_mask, zero,
- val32, hex_new_value[rnum + 1]);
- if (HEX_DEBUG) {
- /*
- * Do this so HELPER(debug_commit_end) will know
- *
- * Note that slot_mask indicates the value is not written
- * (i.e., slot was cancelled), so we create a true/false value before
- * or'ing with hex_reg_written[rnum].
- */
- tcg_gen_setcond_tl(TCG_COND_EQ, slot_mask, slot_mask, zero);
- tcg_gen_or_tl(hex_reg_written[rnum], hex_reg_written[rnum], slot_mask);
- tcg_gen_or_tl(hex_reg_written[rnum + 1], hex_reg_written[rnum + 1],
- slot_mask);
- }
-}
-
static void gen_log_reg_write_pair(int rnum, TCGv_i64 val)
{
const target_ulong reg_mask_low = reg_immut_masks[rnum];
@@ -180,6 +138,7 @@
hex_new_pred_value[pnum], base_val);
}
tcg_gen_ori_tl(hex_pred_written, hex_pred_written, 1 << pnum);
+ set_bit(pnum, ctx->pregs_written);
}
static inline void gen_read_p3_0(TCGv control_reg)
@@ -256,7 +215,6 @@
for (int i = 0; i < NUM_PREGS; i++) {
tcg_gen_extract_tl(hex_p8, control_reg, i * 8, 8);
gen_log_pred_write(ctx, i, hex_p8);
- ctx_log_pred_write(ctx, i);
}
}
@@ -274,7 +232,6 @@
gen_write_p3_0(ctx, val);
} else {
gen_log_reg_write(reg_num, val);
- ctx_log_reg_write(ctx, reg_num);
if (reg_num == HEX_REG_QEMU_PKT_CNT) {
ctx->num_packets = 0;
}
@@ -291,15 +248,14 @@
TCGv_i64 val)
{
if (reg_num == HEX_REG_P3_0_ALIASED) {
+ TCGv result = get_result_gpr(ctx, reg_num + 1);
TCGv val32 = tcg_temp_new();
tcg_gen_extrl_i64_i32(val32, val);
gen_write_p3_0(ctx, val32);
tcg_gen_extrh_i64_i32(val32, val);
- gen_log_reg_write(reg_num + 1, val32);
- ctx_log_reg_write(ctx, reg_num + 1);
+ tcg_gen_mov_tl(result, val32);
} else {
gen_log_reg_write_pair(reg_num, val);
- ctx_log_reg_write_pair(ctx, reg_num);
if (reg_num == HEX_REG_QEMU_PKT_CNT) {
ctx->num_packets = 0;
ctx->num_insns = 0;
@@ -571,6 +527,13 @@
gen_write_new_pc_addr(ctx, dst_pc, cond, pred);
}
+static void gen_cond_jumpr31(DisasContext *ctx, TCGCond cond, TCGv pred)
+{
+ TCGv LSB = tcg_temp_new();
+ tcg_gen_andi_tl(LSB, pred, 1);
+ gen_cond_jumpr(ctx, hex_gpr[HEX_REG_LR], cond, LSB);
+}
+
static void gen_cond_jump(DisasContext *ctx, TCGCond cond, TCGv pred,
int pc_off)
{
@@ -669,27 +632,99 @@
static void gen_call(DisasContext *ctx, int pc_off)
{
- TCGv next_PC =
- tcg_constant_tl(ctx->pkt->pc + ctx->pkt->encod_pkt_size_in_bytes);
- gen_log_reg_write(HEX_REG_LR, next_PC);
+ TCGv lr = get_result_gpr(ctx, HEX_REG_LR);
+ tcg_gen_movi_tl(lr, ctx->next_PC);
gen_write_new_pc_pcrel(ctx, pc_off, TCG_COND_ALWAYS, NULL);
}
+static void gen_callr(DisasContext *ctx, TCGv new_pc)
+{
+ TCGv lr = get_result_gpr(ctx, HEX_REG_LR);
+ tcg_gen_movi_tl(lr, ctx->next_PC);
+ gen_write_new_pc_addr(ctx, new_pc, TCG_COND_ALWAYS, NULL);
+}
+
static void gen_cond_call(DisasContext *ctx, TCGv pred,
TCGCond cond, int pc_off)
{
- TCGv next_PC;
+ TCGv lr = get_result_gpr(ctx, HEX_REG_LR);
TCGv lsb = tcg_temp_new();
TCGLabel *skip = gen_new_label();
tcg_gen_andi_tl(lsb, pred, 1);
gen_write_new_pc_pcrel(ctx, pc_off, cond, lsb);
tcg_gen_brcondi_tl(cond, lsb, 0, skip);
- next_PC =
- tcg_constant_tl(ctx->pkt->pc + ctx->pkt->encod_pkt_size_in_bytes);
- gen_log_reg_write(HEX_REG_LR, next_PC);
+ tcg_gen_movi_tl(lr, ctx->next_PC);
gen_set_label(skip);
}
+static void gen_cond_callr(DisasContext *ctx,
+ TCGCond cond, TCGv pred, TCGv new_pc)
+{
+ TCGv lsb = tcg_temp_new();
+ TCGLabel *skip = gen_new_label();
+ tcg_gen_andi_tl(lsb, pred, 1);
+ tcg_gen_brcondi_tl(cond, lsb, 0, skip);
+ gen_callr(ctx, new_pc);
+ gen_set_label(skip);
+}
+
+/* frame ^= (int64_t)FRAMEKEY << 32 */
+static void gen_frame_unscramble(TCGv_i64 frame)
+{
+ TCGv_i64 framekey = tcg_temp_new_i64();
+ tcg_gen_extu_i32_i64(framekey, hex_gpr[HEX_REG_FRAMEKEY]);
+ tcg_gen_shli_i64(framekey, framekey, 32);
+ tcg_gen_xor_i64(frame, frame, framekey);
+}
+
+static void gen_load_frame(DisasContext *ctx, TCGv_i64 frame, TCGv EA)
+{
+ Insn *insn = ctx->insn; /* Needed for CHECK_NOSHUF */
+ CHECK_NOSHUF(EA, 8);
+ tcg_gen_qemu_ld64(frame, EA, ctx->mem_idx);
+}
+
+static void gen_return(DisasContext *ctx, TCGv_i64 dst, TCGv src)
+{
+ /*
+ * frame = *src
+ * dst = frame_unscramble(frame)
+ * SP = src + 8
+ * PC = dst.w[1]
+ */
+ TCGv_i64 frame = tcg_temp_new_i64();
+ TCGv r31 = tcg_temp_new();
+ TCGv r29 = get_result_gpr(ctx, HEX_REG_SP);
+
+ gen_load_frame(ctx, frame, src);
+ gen_frame_unscramble(frame);
+ tcg_gen_mov_i64(dst, frame);
+ tcg_gen_addi_tl(r29, src, 8);
+ tcg_gen_extrh_i64_i32(r31, dst);
+ gen_jumpr(ctx, r31);
+}
+
+/* if (pred) dst = dealloc_return(src):raw */
+static void gen_cond_return(DisasContext *ctx, TCGv_i64 dst, TCGv src,
+ TCGv pred, TCGCond cond)
+{
+ TCGv LSB = tcg_temp_new();
+ TCGLabel *skip = gen_new_label();
+ tcg_gen_andi_tl(LSB, pred, 1);
+
+ tcg_gen_brcondi_tl(cond, LSB, 0, skip);
+ gen_return(ctx, dst, src);
+ gen_set_label(skip);
+}
+
+/* sub-instruction version (no RddV, so handle it manually) */
+static void gen_cond_return_subinsn(DisasContext *ctx, TCGCond cond, TCGv pred)
+{
+ TCGv_i64 RddV = get_result_gpr_pair(ctx, HEX_REG_FP);
+ gen_cond_return(ctx, RddV, hex_gpr[HEX_REG_FP], pred, cond);
+ gen_log_reg_write_pair(HEX_REG_FP, RddV);
+}
+
static void gen_endloop0(DisasContext *ctx)
{
TCGv lpcfg = tcg_temp_new();
@@ -737,14 +772,95 @@
TCGLabel *label3 = gen_new_label();
tcg_gen_brcondi_tl(TCG_COND_LEU, hex_gpr[HEX_REG_LC0], 1, label3);
{
+ TCGv lc0 = get_result_gpr(ctx, HEX_REG_LC0);
gen_jumpr(ctx, hex_gpr[HEX_REG_SA0]);
- tcg_gen_subi_tl(hex_new_value[HEX_REG_LC0],
- hex_gpr[HEX_REG_LC0], 1);
+ tcg_gen_subi_tl(lc0, hex_gpr[HEX_REG_LC0], 1);
}
gen_set_label(label3);
}
}
+static void gen_endloop1(DisasContext *ctx)
+{
+ /*
+ * if (hex_gpr[HEX_REG_LC1] > 1) {
+ * PC = hex_gpr[HEX_REG_SA1];
+ * hex_new_value[HEX_REG_LC1] = hex_gpr[HEX_REG_LC1] - 1;
+ * }
+ */
+ TCGLabel *label = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_LEU, hex_gpr[HEX_REG_LC1], 1, label);
+ {
+ TCGv lc1 = get_result_gpr(ctx, HEX_REG_LC1);
+ gen_jumpr(ctx, hex_gpr[HEX_REG_SA1]);
+ tcg_gen_subi_tl(lc1, hex_gpr[HEX_REG_LC1], 1);
+ }
+ gen_set_label(label);
+}
+
+static void gen_endloop01(DisasContext *ctx)
+{
+ TCGv lpcfg = tcg_temp_new();
+ TCGLabel *label1 = gen_new_label();
+ TCGLabel *label2 = gen_new_label();
+ TCGLabel *label3 = gen_new_label();
+ TCGLabel *done = gen_new_label();
+
+ GET_USR_FIELD(USR_LPCFG, lpcfg);
+
+ /*
+ * if (lpcfg == 1) {
+ * hex_new_pred_value[3] = 0xff;
+ * hex_pred_written |= 1 << 3;
+ * }
+ */
+ tcg_gen_brcondi_tl(TCG_COND_NE, lpcfg, 1, label1);
+ {
+ tcg_gen_movi_tl(hex_new_pred_value[3], 0xff);
+ tcg_gen_ori_tl(hex_pred_written, hex_pred_written, 1 << 3);
+ }
+ gen_set_label(label1);
+
+ /*
+ * if (lpcfg) {
+ * SET_USR_FIELD(USR_LPCFG, lpcfg - 1);
+ * }
+ */
+ tcg_gen_brcondi_tl(TCG_COND_EQ, lpcfg, 0, label2);
+ {
+ tcg_gen_subi_tl(lpcfg, lpcfg, 1);
+ SET_USR_FIELD(USR_LPCFG, lpcfg);
+ }
+ gen_set_label(label2);
+
+ /*
+ * if (hex_gpr[HEX_REG_LC0] > 1) {
+ * PC = hex_gpr[HEX_REG_SA0];
+ * hex_new_value[HEX_REG_LC0] = hex_gpr[HEX_REG_LC0] - 1;
+ * } else {
+ * if (hex_gpr[HEX_REG_LC1] > 1) {
+ * hex_next_pc = hex_gpr[HEX_REG_SA1];
+ * hex_new_value[HEX_REG_LC1] = hex_gpr[HEX_REG_LC1] - 1;
+ * }
+ * }
+ */
+ tcg_gen_brcondi_tl(TCG_COND_LEU, hex_gpr[HEX_REG_LC0], 1, label3);
+ {
+ TCGv lc0 = get_result_gpr(ctx, HEX_REG_LC0);
+ gen_jumpr(ctx, hex_gpr[HEX_REG_SA0]);
+ tcg_gen_subi_tl(lc0, hex_gpr[HEX_REG_LC0], 1);
+ tcg_gen_br(done);
+ }
+ gen_set_label(label3);
+ tcg_gen_brcondi_tl(TCG_COND_LEU, hex_gpr[HEX_REG_LC1], 1, done);
+ {
+ TCGv lc1 = get_result_gpr(ctx, HEX_REG_LC1);
+ gen_jumpr(ctx, hex_gpr[HEX_REG_SA1]);
+ tcg_gen_subi_tl(lc1, hex_gpr[HEX_REG_LC1], 1);
+ }
+ gen_set_label(done);
+}
+
static void gen_cmp_jumpnv(DisasContext *ctx,
TCGCond cond, TCGv val, TCGv src, int pc_off)
{
@@ -869,68 +985,32 @@
}
static void gen_log_vreg_write(DisasContext *ctx, intptr_t srcoff, int num,
- VRegWriteType type, int slot_num,
- bool is_predicated)
+ VRegWriteType type)
{
- TCGLabel *label_end = NULL;
intptr_t dstoff;
- if (is_predicated) {
- TCGv cancelled = tcg_temp_new();
- label_end = gen_new_label();
-
- /* Don't do anything if the slot was cancelled */
- tcg_gen_extract_tl(cancelled, hex_slot_cancelled, slot_num, 1);
- tcg_gen_brcondi_tl(TCG_COND_NE, cancelled, 0, label_end);
- }
-
if (type != EXT_TMP) {
dstoff = ctx_future_vreg_off(ctx, num, 1, true);
tcg_gen_gvec_mov(MO_64, dstoff, srcoff,
sizeof(MMVector), sizeof(MMVector));
- tcg_gen_ori_tl(hex_VRegs_updated, hex_VRegs_updated, 1 << num);
} else {
dstoff = ctx_tmp_vreg_off(ctx, num, 1, false);
tcg_gen_gvec_mov(MO_64, dstoff, srcoff,
sizeof(MMVector), sizeof(MMVector));
}
-
- if (is_predicated) {
- gen_set_label(label_end);
- }
}
static void gen_log_vreg_write_pair(DisasContext *ctx, intptr_t srcoff, int num,
- VRegWriteType type, int slot_num,
- bool is_predicated)
+ VRegWriteType type)
{
- gen_log_vreg_write(ctx, srcoff, num ^ 0, type, slot_num, is_predicated);
+ gen_log_vreg_write(ctx, srcoff, num ^ 0, type);
srcoff += sizeof(MMVector);
- gen_log_vreg_write(ctx, srcoff, num ^ 1, type, slot_num, is_predicated);
+ gen_log_vreg_write(ctx, srcoff, num ^ 1, type);
}
-static void gen_log_qreg_write(intptr_t srcoff, int num, int vnew,
- int slot_num, bool is_predicated)
+static intptr_t get_result_qreg(DisasContext *ctx, int qnum)
{
- TCGLabel *label_end = NULL;
- intptr_t dstoff;
-
- if (is_predicated) {
- TCGv cancelled = tcg_temp_new();
- label_end = gen_new_label();
-
- /* Don't do anything if the slot was cancelled */
- tcg_gen_extract_tl(cancelled, hex_slot_cancelled, slot_num, 1);
- tcg_gen_brcondi_tl(TCG_COND_NE, cancelled, 0, label_end);
- }
-
- dstoff = offsetof(CPUHexagonState, future_QRegs[num]);
- tcg_gen_gvec_mov(MO_64, dstoff, srcoff, sizeof(MMQReg), sizeof(MMQReg));
-
- if (is_predicated) {
- tcg_gen_ori_tl(hex_QRegs_updated, hex_QRegs_updated, 1 << num);
- gen_set_label(label_end);
- }
+ return offsetof(CPUHexagonState, future_QRegs[qnum]);
}
static void gen_vreg_load(DisasContext *ctx, intptr_t dstoff, TCGv src,
diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py
index a29f61b..0200a66 100755
--- a/target/hexagon/hex_common.py
+++ b/target/hexagon/hex_common.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
##
-## Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
+## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
@@ -89,6 +89,7 @@ def calculate_attribs():
add_qemu_macro_attrib('fWRITE_P3', 'A_WRITES_PRED_REG')
add_qemu_macro_attrib('fSET_OVERFLOW', 'A_IMPLICIT_WRITES_USR')
add_qemu_macro_attrib('fSET_LPCFG', 'A_IMPLICIT_WRITES_USR')
+ add_qemu_macro_attrib('fLOAD', 'A_SCALAR_LOAD')
add_qemu_macro_attrib('fSTORE', 'A_SCALAR_STORE')
# Recurse down macros, find attributes from sub-macros
@@ -236,6 +237,13 @@ def helper_needs_next_PC(tag):
def need_pkt_has_multi_cof(tag):
return 'A_COF' in attribdict[tag]
+def need_condexec_reg(tag, regs):
+ if 'A_CONDEXEC' in attribdict[tag]:
+ for regtype, regid, toss, numregs in regs:
+ if is_writeonly(regid) and not is_hvx_reg(regtype):
+ return True
+ return False
+
def skip_qemu_helper(tag):
return tag in overrides.keys()
diff --git a/target/hexagon/idef-parser/idef-parser.h b/target/hexagon/idef-parser/idef-parser.h
index 17d2ebf..d23e71f 100644
--- a/target/hexagon/idef-parser/idef-parser.h
+++ b/target/hexagon/idef-parser/idef-parser.h
@@ -82,7 +82,6 @@
VALUE,
QEMU_TMP,
IMM_PC,
- IMM_NPC,
IMM_CONSTEXT,
};
diff --git a/target/hexagon/idef-parser/idef-parser.lex b/target/hexagon/idef-parser/idef-parser.lex
index ff87a02..5eb8ac5 100644
--- a/target/hexagon/idef-parser/idef-parser.lex
+++ b/target/hexagon/idef-parser/idef-parser.lex
@@ -5,7 +5,7 @@
%{
/*
- * Copyright(c) 2019-2022 rev.ng Labs Srl. All Rights Reserved.
+ * Copyright(c) 2019-2023 rev.ng Labs Srl. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -140,8 +140,6 @@
yylval->rvalue.is_dotnew = true;
yylval->rvalue.signedness = SIGNED;
return PRED; }
-"IV1DEAD()" |
-"fPAUSE(uiV);" { return ';'; }
"+=" { return INC; }
"-=" { return DEC; }
"++" { return PLUSPLUS; }
@@ -159,9 +157,8 @@
"else" { return ELSE; }
"for" { return FOR; }
"fREAD_IREG" { return ICIRC; }
-"fPART1" { return PART1; }
"if" { return IF; }
-"fFRAME_SCRAMBLE" { return FSCR; }
+"fFRAME_SCRAMBLE" |
"fFRAME_UNSCRAMBLE" { return FSCR; }
"fFRAMECHECK" { return FCHK; }
"Constant_extended" { return CONSTEXT; }
@@ -312,14 +309,10 @@
"(unsigned int)" { yylval->cast.bit_width = 32;
yylval->cast.signedness = UNSIGNED;
return CAST; }
-"fREAD_PC()" |
-"PC" { return PC; }
-"fREAD_NPC()" |
-"NPC" { return NPC; }
-"fGET_LPCFG" |
+"fREAD_PC()" { return PC; }
"USR.LPCFG" { return LPCFG; }
"LOAD_CANCEL(EA)" { return LOAD_CANCEL; }
-"STORE_CANCEL(EA)" |
+"STORE_CANCEL(EA)" { return STORE_CANCEL; }
"CANCEL" { return CANCEL; }
"N"{LOWER_ID}"N" { yylval->rvalue.type = REGISTER_ARG;
yylval->rvalue.reg.type = DOTNEW;
@@ -360,14 +353,6 @@
yylval->rvalue.bit_width = 32;
yylval->rvalue.signedness = UNSIGNED;
return REG; }
-"fREAD_LC"[01] { yylval->rvalue.type = REGISTER;
- yylval->rvalue.reg.type = CONTROL;
- yylval->rvalue.reg.id = HEX_REG_LC0
- + (yytext[8] - '0') * 2;
- yylval->rvalue.reg.bit_width = 32;
- yylval->rvalue.bit_width = 32;
- yylval->rvalue.signedness = UNSIGNED;
- return REG; }
"LC"[01] { yylval->rvalue.type = REGISTER;
yylval->rvalue.reg.type = CONTROL;
yylval->rvalue.reg.id = HEX_REG_LC0
@@ -376,14 +361,6 @@
yylval->rvalue.bit_width = 32;
yylval->rvalue.signedness = UNSIGNED;
return REG; }
-"fREAD_SA"[01] { yylval->rvalue.type = REGISTER;
- yylval->rvalue.reg.type = CONTROL;
- yylval->rvalue.reg.id = HEX_REG_SA0
- + (yytext[8] - '0') * 2;
- yylval->rvalue.reg.bit_width = 32;
- yylval->rvalue.bit_width = 32;
- yylval->rvalue.signedness = UNSIGNED;
- return REG; }
"SA"[01] { yylval->rvalue.type = REGISTER;
yylval->rvalue.reg.type = CONTROL;
yylval->rvalue.reg.id = HEX_REG_SA0
diff --git a/target/hexagon/idef-parser/idef-parser.y b/target/hexagon/idef-parser/idef-parser.y
index c784726..7d05773 100644
--- a/target/hexagon/idef-parser/idef-parser.y
+++ b/target/hexagon/idef-parser/idef-parser.y
@@ -1,6 +1,6 @@
%{
/*
- * Copyright(c) 2019-2022 rev.ng Labs Srl. All Rights Reserved.
+ * Copyright(c) 2019-2023 rev.ng Labs Srl. All Rights Reserved.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -52,8 +52,8 @@
%token IN INAME VAR
%token ABS CROUND ROUND CIRCADD COUNTONES INC DEC ANDA ORA XORA PLUSPLUS ASL
%token ASR LSR EQ NEQ LTE GTE MIN MAX ANDL FOR ICIRC IF MUN FSCR FCHK SXT
-%token ZXT CONSTEXT LOCNT BREV SIGN LOAD STORE PC NPC LPCFG
-%token LOAD_CANCEL CANCEL IDENTITY PART1 ROTL INSBITS SETBITS EXTRANGE
+%token ZXT CONSTEXT LOCNT BREV SIGN LOAD STORE PC LPCFG
+%token LOAD_CANCEL STORE_CANCEL CANCEL IDENTITY ROTL INSBITS SETBITS EXTRANGE
%token CAST4_8U FAIL CARRY_FROM_ADD ADDSAT64 LSBNEW
%token TYPE_SIZE_T TYPE_INT TYPE_SIGNED TYPE_UNSIGNED TYPE_LONG
@@ -336,15 +336,6 @@
OUT(c, &@1, &$1, " = ", &$3, ";\n");
$$ = $1;
}
- | PC '=' rvalue
- {
- @1.last_column = @3.last_column;
- yyassert(c, &@1, !is_inside_ternary(c),
- "Assignment side-effect not modeled!");
- $3 = gen_rvalue_truncate(c, &@1, &$3);
- $3 = rvalue_materialize(c, &@1, &$3);
- OUT(c, &@1, "gen_write_new_pc(", &$3, ");\n");
- }
| LOAD '(' IMM ',' IMM ',' SIGN ',' var ',' lvalue ')'
{
@1.last_column = @12.last_column;
@@ -412,7 +403,6 @@
| cancel_statement
| if_statement
| for_statement
- | fpart1_statement
;
frame_check : FCHK '(' rvalue ',' rvalue ')' ';'
@@ -422,10 +412,11 @@
{
gen_load_cancel(c, &@1);
}
- | CANCEL
+ | STORE_CANCEL
{
gen_cancel(c, &@1);
}
+ | CANCEL
;
if_statement : if_stmt
@@ -462,17 +453,6 @@
}
;
-fpart1_statement : PART1
- {
- OUT(c, &@1, "if (insn->part1) {\n");
- }
- '(' statements ')'
- {
- @1.last_column = @3.last_column;
- OUT(c, &@1, "return; }\n");
- }
- ;
-
if_stmt : IF '(' rvalue ')'
{
@1.last_column = @3.last_column;
@@ -512,20 +492,6 @@
rvalue.signedness = UNSIGNED;
$$ = rvalue;
}
- | NPC
- {
- /*
- * NPC is only read from CALLs, so we can hardcode it
- * at translation time
- */
- HexValue rvalue;
- memset(&rvalue, 0, sizeof(HexValue));
- rvalue.type = IMMEDIATE;
- rvalue.imm.type = IMM_NPC;
- rvalue.bit_width = 32;
- rvalue.signedness = UNSIGNED;
- $$ = rvalue;
- }
| CONSTEXT
{
HexValue rvalue;
@@ -781,11 +747,6 @@
/* Ones count */
$$ = gen_ctpop_op(c, &@1, &$3);
}
- | LPCFG
- {
- $$ = gen_tmp(c, &@1, 32, UNSIGNED);
- OUT(c, &@1, "GET_USR_FIELD(USR_LPCFG, ", &$$, ");\n");
- }
| EXTRACT '(' rvalue ',' rvalue ')'
{
@1.last_column = @6.last_column;
diff --git a/target/hexagon/idef-parser/macros.inc b/target/hexagon/idef-parser/macros.inc
index 6b697da..7478d4d 100644
--- a/target/hexagon/idef-parser/macros.inc
+++ b/target/hexagon/idef-parser/macros.inc
@@ -97,16 +97,8 @@
#define fWRITE_LR(A) (LR = A)
#define fWRITE_FP(A) (FP = A)
#define fWRITE_SP(A) (SP = A)
-/*
- * Note: There is a rule in the parser that matches `PC = ...` and emits
- * a call to `gen_write_new_pc`. We need to call `gen_write_new_pc` to
- * get the correct semantics when there are multiple stores in a packet.
- */
-#define fBRANCH(LOC, TYPE) (PC = LOC)
-#define fJUMPR(REGNO, TARGET, TYPE) (PC = TARGET)
#define fWRITE_LOOP_REGS0(START, COUNT) SA0 = START; (LC0 = COUNT)
#define fWRITE_LOOP_REGS1(START, COUNT) SA1 = START; (LC1 = COUNT)
-#define fWRITE_LC0(VAL) (LC0 = VAL)
#define fWRITE_LC1(VAL) (LC1 = VAL)
#define fSET_LPCFG(VAL) (USR.LPCFG = VAL)
#define fWRITE_P0(VAL) P0 = VAL;
@@ -121,7 +113,6 @@
#define fEA_GPI(IMM) (EA = fREAD_GP() + IMM)
#define fPM_I(REG, IMM) (REG = REG + IMM)
#define fPM_M(REG, MVAL) (REG = REG + MVAL)
-#define fWRITE_NPC(VAL) (PC = VAL)
/* Unary operators */
#define fROUND(A) (A + 0x8000)
diff --git a/target/hexagon/idef-parser/parser-helpers.c b/target/hexagon/idef-parser/parser-helpers.c
index e1a5541..18cde6a 100644
--- a/target/hexagon/idef-parser/parser-helpers.c
+++ b/target/hexagon/idef-parser/parser-helpers.c
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2019-2022 rev.ng Labs Srl. All Rights Reserved.
+ * Copyright(c) 2019-2023 rev.ng Labs Srl. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -185,9 +185,6 @@
case IMM_PC:
EMIT(c, "ctx->base.pc_next");
break;
- case IMM_NPC:
- EMIT(c, "ctx->npc");
- break;
case IMM_CONSTEXT:
EMIT(c, "insn->extension_valid");
break;
@@ -1323,10 +1320,6 @@
locp,
"gen_log_reg_write(", ®->reg.id, ", ",
&value_m, ");\n");
- OUT(c,
- locp,
- "ctx_log_reg_write(ctx, ", ®->reg.id,
- ");\n");
}
void gen_assign(Context *c,
@@ -1675,9 +1668,7 @@
for (unsigned i = 0; i < c->inst.init_list->len; i++) {
HexValue *val = &g_array_index(c->inst.init_list, HexValue, i);
if (val->type == REGISTER_ARG) {
- char reg_id[5];
- reg_compose(c, locp, &val->reg, reg_id);
- EMIT_HEAD(c, "tcg_gen_movi_i%u(%s, 0);\n", val->bit_width, reg_id);
+ /* Nothing to do here */
} else if (val->type == PREDICATE) {
char suffix = val->is_dotnew ? 'N' : 'V';
EMIT_HEAD(c, "tcg_gen_movi_i%u(P%c%c, 0);\n", val->bit_width,
@@ -1722,13 +1713,10 @@
*left_pred = gen_tmp(c, locp, 32, UNSIGNED);
}
/* Extract first 8 bits, and store new predicate value */
- OUT(c, locp, "tcg_gen_mov_i32(", left_pred, ", ", &r, ");\n");
- OUT(c, locp, "tcg_gen_andi_i32(", left_pred, ", ", left_pred,
- ", 0xff);\n");
+ OUT(c, locp, "tcg_gen_andi_i32(", left_pred, ", ", &r, ", 0xff);\n");
if (is_direct) {
OUT(c, locp, "gen_log_pred_write(ctx, ", pred_id, ", ", left_pred,
");\n");
- OUT(c, locp, "ctx_log_pred_write(ctx, ", pred_id, ");\n");
}
}
@@ -1739,7 +1727,6 @@
void gen_load_cancel(Context *c, YYLTYPE *locp)
{
- gen_cancel(c, locp);
OUT(c, locp, "if (insn->slot == 0 && pkt->pkt_has_store_s1) {\n");
OUT(c, locp, "ctx->s1_store_processed = false;\n");
OUT(c, locp, "process_store(ctx, 1);\n");
diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h
index 17facad..482a9c7 100644
--- a/target/hexagon/macros.h
+++ b/target/hexagon/macros.h
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -205,26 +205,11 @@
#define CANCEL gen_cancel(slot);
#else
-#define CANCEL cancel_slot(env, slot)
+#define CANCEL do { } while (0)
#endif
#define LOAD_CANCEL(EA) do { CANCEL; } while (0)
-#ifdef QEMU_GENERATE
-static inline void gen_pred_cancel(TCGv pred, uint32_t slot_num)
- {
- TCGv slot_mask = tcg_temp_new();
- TCGv tmp = tcg_temp_new();
- TCGv zero = tcg_constant_tl(0);
- tcg_gen_ori_tl(slot_mask, hex_slot_cancelled, 1 << slot_num);
- tcg_gen_andi_tl(tmp, pred, 1);
- tcg_gen_movcond_tl(TCG_COND_EQ, hex_slot_cancelled, tmp, zero,
- slot_mask, hex_slot_cancelled);
-}
-#define PRED_LOAD_CANCEL(PRED, EA) \
- gen_pred_cancel(PRED, insn->is_endloop ? 4 : insn->slot)
-#endif
-
#define STORE_CANCEL(EA) { env->slot_cancelled |= (1 << slot); }
#define fMAX(A, B) (((A) > (B)) ? (A) : (B))
@@ -415,16 +400,6 @@
#define fBRANCH(LOC, TYPE) fWRITE_NPC(LOC)
#define fJUMPR(REGNO, TARGET, TYPE) fBRANCH(TARGET, COF_TYPE_JUMPR)
#define fHINTJR(TARGET) { /* Not modelled in qemu */}
-#define fCALL(A) \
- do { \
- fWRITE_LR(fREAD_NPC()); \
- fBRANCH(A, COF_TYPE_CALL); \
- } while (0)
-#define fCALLR(A) \
- do { \
- fWRITE_LR(fREAD_NPC()); \
- fBRANCH(A, COF_TYPE_CALLR); \
- } while (0)
#define fWRITE_LOOP_REGS0(START, COUNT) \
do { \
WRITE_RREG(HEX_REG_LC0, COUNT); \
diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build
index c9d31d0..da8e608 100644
--- a/target/hexagon/meson.build
+++ b/target/hexagon/meson.build
@@ -1,5 +1,5 @@
##
-## Copyright(c) 2020-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
+## Copyright(c) 2020-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
@@ -183,7 +183,7 @@
)
bison = generator(
- find_program('bison'),
+ find_program('bison', version: '>=3.0'),
output: ['@BASENAME@.tab.c', '@BASENAME@.tab.h'],
arguments: ['@INPUT@', '--defines=@OUTPUT1@', '--output=@OUTPUT0@']
)
@@ -276,4 +276,13 @@
)
hexagon_ss.add(tcg_funcs_generated)
+analyze_funcs_generated = custom_target(
+ 'analyze_funcs_generated.c.inc',
+ output: 'analyze_funcs_generated.c.inc',
+ depends: helper_dep,
+ depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h],
+ command: [python, files('gen_analyze_funcs.py'), helper_in, '@OUTPUT@'],
+)
+hexagon_ss.add(analyze_funcs_generated)
+
target_arch += {'hexagon': hexagon_ss}
diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
index 35449ef..c9a1560 100644
--- a/target/hexagon/op_helper.c
+++ b/target/hexagon/op_helper.c
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -30,6 +30,7 @@
#include "mmvec/mmvec.h"
#include "mmvec/macros.h"
#include "op_helper.h"
+#include "translate.h"
#define SF_BIAS 127
#define SF_MANTBITS 23
@@ -105,30 +106,6 @@
env->mem_log_stores[slot].data64 = val;
}
-void write_new_pc(CPUHexagonState *env, bool pkt_has_multi_cof,
- target_ulong addr)
-{
- HEX_DEBUG_LOG("write_new_pc(0x" TARGET_FMT_lx ")\n", addr);
-
- if (pkt_has_multi_cof) {
- /*
- * If more than one branch is taken in a packet, only the first one
- * is actually done.
- */
- if (env->branch_taken) {
- HEX_DEBUG_LOG("INFO: multiple branches taken in same packet, "
- "ignoring the second one\n");
- } else {
- fCHECK_PCALIGN(addr);
- env->gpr[HEX_REG_PC] = addr;
- env->branch_taken = 1;
- }
- } else {
- fCHECK_PCALIGN(addr);
- env->gpr[HEX_REG_PC] = addr;
- }
-}
-
/* Handy place to set a breakpoint */
void HELPER(debug_start_packet)(CPUHexagonState *env)
{
@@ -439,9 +416,10 @@
return PeV;
}
-static void probe_store(CPUHexagonState *env, int slot, int mmu_idx)
+static void probe_store(CPUHexagonState *env, int slot, int mmu_idx,
+ bool is_predicated)
{
- if (!(env->slot_cancelled & (1 << slot))) {
+ if (!is_predicated || !(env->slot_cancelled & (1 << slot))) {
size1u_t width = env->mem_log_stores[slot].width;
target_ulong va = env->mem_log_stores[slot].va;
uintptr_t ra = GETPC();
@@ -461,9 +439,12 @@
}
/* Called during packet commit when there are two scalar stores */
-void HELPER(probe_pkt_scalar_store_s0)(CPUHexagonState *env, int mmu_idx)
+void HELPER(probe_pkt_scalar_store_s0)(CPUHexagonState *env, int args)
{
- probe_store(env, 0, mmu_idx);
+ int mmu_idx = FIELD_EX32(args, PROBE_PKT_SCALAR_STORE_S0, MMU_IDX);
+ bool is_predicated =
+ FIELD_EX32(args, PROBE_PKT_SCALAR_STORE_S0, IS_PREDICATED);
+ probe_store(env, 0, mmu_idx, is_predicated);
}
void HELPER(probe_hvx_stores)(CPUHexagonState *env, int mmu_idx)
@@ -510,15 +491,18 @@
void HELPER(probe_pkt_scalar_hvx_stores)(CPUHexagonState *env, int mask,
int mmu_idx)
{
- bool has_st0 = (mask >> 0) & 1;
- bool has_st1 = (mask >> 1) & 1;
- bool has_hvx_stores = (mask >> 2) & 1;
+ bool has_st0 = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, HAS_ST0);
+ bool has_st1 = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, HAS_ST1);
+ bool has_hvx_stores =
+ FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, HAS_HVX_STORES);
+ bool s0_is_pred = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, S0_IS_PRED);
+ bool s1_is_pred = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, S1_IS_PRED);
if (has_st0) {
- probe_store(env, 0, mmu_idx);
+ probe_store(env, 0, mmu_idx, s0_is_pred);
}
if (has_st1) {
- probe_store(env, 1, mmu_idx);
+ probe_store(env, 1, mmu_idx, s1_is_pred);
}
if (has_hvx_stores) {
HELPER(probe_hvx_stores)(env, mmu_idx);
@@ -1193,7 +1177,7 @@
{
float32 neg_RsV;
arch_fpop_start(env);
- neg_RsV = float32_sub(float32_zero, RsV, &env->fp_status);
+ neg_RsV = float32_set_sign(RsV, float32_is_neg(RsV) ? 0 : 1);
RxV = internal_fmafx(neg_RsV, RtV, RxV, 0, &env->fp_status);
arch_fpop_end(env);
return RxV;
@@ -1468,12 +1452,6 @@
}
}
-void cancel_slot(CPUHexagonState *env, uint32_t slot)
-{
- HEX_DEBUG_LOG("Slot %d cancelled\n", slot);
- env->slot_cancelled |= (1 << slot);
-}
-
/* These macros can be referenced in the generated helper functions */
#define warn(...) /* Nothing */
#define fatal(...) g_assert_not_reached();
diff --git a/target/hexagon/op_helper.h b/target/hexagon/op_helper.h
index 02347ed..34b3a53 100644
--- a/target/hexagon/op_helper.h
+++ b/target/hexagon/op_helper.h
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,7 +19,6 @@
#define HEXAGON_OP_HELPER_H
/* Misc functions */
-void cancel_slot(CPUHexagonState *env, uint32_t slot);
void write_new_pc(CPUHexagonState *env, bool pkt_has_multi_cof, target_ulong addr);
uint8_t mem_load1(CPUHexagonState *env, uint32_t slot, target_ulong vaddr);
diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c
index 93fd1b5..665476a 100644
--- a/target/hexagon/translate.c
+++ b/target/hexagon/translate.c
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,6 +29,15 @@
#include "translate.h"
#include "printinsn.h"
+#include "analyze_funcs_generated.c.inc"
+
+typedef void (*AnalyzeInsn)(DisasContext *ctx);
+static const AnalyzeInsn opcode_analyze[XX_LAST_OPCODE] = {
+#define OPCODE(X) [X] = analyze_##X
+#include "opcodes_def_generated.h.inc"
+#undef OPCODE
+};
+
TCGv hex_gpr[TOTAL_PER_THREAD_REGS];
TCGv hex_pred[NUM_PREGS];
TCGv hex_this_PC;
@@ -47,8 +56,6 @@
TCGv hex_llsc_addr;
TCGv hex_llsc_val;
TCGv_i64 hex_llsc_val_i64;
-TCGv hex_VRegs_updated;
-TCGv hex_QRegs_updated;
TCGv hex_vstore_addr[VSTORES_MAX];
TCGv hex_vstore_size[VSTORES_MAX];
TCGv hex_vstore_pending[VSTORES_MAX];
@@ -239,7 +246,15 @@
static bool need_slot_cancelled(Packet *pkt)
{
- return check_for_attrib(pkt, A_CONDEXEC);
+ /* We only need slot_cancelled for conditional store instructions */
+ for (int i = 0; i < pkt->num_insns; i++) {
+ uint16_t opcode = pkt->insn[i].opcode;
+ if (GET_ATTRIB(opcode, A_CONDEXEC) &&
+ GET_ATTRIB(opcode, A_SCALAR_STORE)) {
+ return true;
+ }
+ }
+ return false;
}
static bool need_pred_written(Packet *pkt)
@@ -265,6 +280,77 @@
return false;
}
+/*
+ * The opcode_analyze functions mark most of the writes in a packet
+ * However, there are some implicit writes marked as attributes
+ * of the applicable instructions.
+ */
+static void mark_implicit_reg_write(DisasContext *ctx, int attrib, int rnum)
+{
+ uint16_t opcode = ctx->insn->opcode;
+ if (GET_ATTRIB(opcode, attrib)) {
+ /*
+ * USR is used to set overflow and FP exceptions,
+ * so treat it as conditional
+ */
+ bool is_predicated = GET_ATTRIB(opcode, A_CONDEXEC) ||
+ rnum == HEX_REG_USR;
+
+ /* LC0/LC1 is conditionally written by endloop instructions */
+ if ((rnum == HEX_REG_LC0 || rnum == HEX_REG_LC1) &&
+ (opcode == J2_endloop0 ||
+ opcode == J2_endloop1 ||
+ opcode == J2_endloop01)) {
+ is_predicated = true;
+ }
+
+ ctx_log_reg_write(ctx, rnum, is_predicated);
+ }
+}
+
+static void mark_implicit_reg_writes(DisasContext *ctx)
+{
+ mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_FP, HEX_REG_FP);
+ mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_SP, HEX_REG_SP);
+ mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_LR, HEX_REG_LR);
+ mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_LC0, HEX_REG_LC0);
+ mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_SA0, HEX_REG_SA0);
+ mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_LC1, HEX_REG_LC1);
+ mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_SA1, HEX_REG_SA1);
+ mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_USR, HEX_REG_USR);
+ mark_implicit_reg_write(ctx, A_FPOP, HEX_REG_USR);
+}
+
+static void mark_implicit_pred_write(DisasContext *ctx, int attrib, int pnum)
+{
+ if (GET_ATTRIB(ctx->insn->opcode, attrib)) {
+ ctx_log_pred_write(ctx, pnum);
+ }
+}
+
+static void mark_implicit_pred_writes(DisasContext *ctx)
+{
+ mark_implicit_pred_write(ctx, A_IMPLICIT_WRITES_P0, 0);
+ mark_implicit_pred_write(ctx, A_IMPLICIT_WRITES_P1, 1);
+ mark_implicit_pred_write(ctx, A_IMPLICIT_WRITES_P2, 2);
+ mark_implicit_pred_write(ctx, A_IMPLICIT_WRITES_P3, 3);
+}
+
+static void analyze_packet(DisasContext *ctx)
+{
+ Packet *pkt = ctx->pkt;
+ ctx->need_pkt_has_store_s1 = false;
+ for (int i = 0; i < pkt->num_insns; i++) {
+ Insn *insn = &pkt->insn[i];
+ ctx->insn = insn;
+ if (opcode_analyze[insn->opcode]) {
+ opcode_analyze[insn->opcode](ctx);
+ }
+ mark_implicit_reg_writes(ctx);
+ mark_implicit_pred_writes(ctx);
+ }
+}
+
static void gen_start_packet(DisasContext *ctx)
{
Packet *pkt = ctx->pkt;
@@ -275,6 +361,7 @@
ctx->next_PC = next_PC;
ctx->reg_log_idx = 0;
bitmap_zero(ctx->regs_written, TOTAL_PER_THREAD_REGS);
+ bitmap_zero(ctx->predicated_regs, TOTAL_PER_THREAD_REGS);
ctx->preg_log_idx = 0;
bitmap_zero(ctx->pregs_written, NUM_PREGS);
ctx->future_vregs_idx = 0;
@@ -283,14 +370,27 @@
bitmap_zero(ctx->vregs_updated_tmp, NUM_VREGS);
bitmap_zero(ctx->vregs_updated, NUM_VREGS);
bitmap_zero(ctx->vregs_select, NUM_VREGS);
+ bitmap_zero(ctx->predicated_future_vregs, NUM_VREGS);
+ bitmap_zero(ctx->predicated_tmp_vregs, NUM_VREGS);
ctx->qreg_log_idx = 0;
for (i = 0; i < STORES_MAX; i++) {
ctx->store_width[i] = 0;
}
- tcg_gen_movi_tl(hex_pkt_has_store_s1, pkt->pkt_has_store_s1);
ctx->s1_store_processed = false;
ctx->pre_commit = true;
+ analyze_packet(ctx);
+
+ if (ctx->need_pkt_has_store_s1) {
+ tcg_gen_movi_tl(hex_pkt_has_store_s1, pkt->pkt_has_store_s1);
+ }
+
+ /*
+ * pregs_written is used both in the analyze phase as well as the code
+ * gen phase, so clear it again.
+ */
+ bitmap_zero(ctx->pregs_written, NUM_PREGS);
+
if (HEX_DEBUG) {
/* Handy place to set a breakpoint before the packet executes */
gen_helper_debug_start_packet(cpu_env);
@@ -313,9 +413,42 @@
tcg_gen_movi_tl(hex_pred_written, 0);
}
- if (pkt->pkt_has_hvx) {
- tcg_gen_movi_tl(hex_VRegs_updated, 0);
- tcg_gen_movi_tl(hex_QRegs_updated, 0);
+ /* Preload the predicated registers into hex_new_value[i] */
+ if (!bitmap_empty(ctx->predicated_regs, TOTAL_PER_THREAD_REGS)) {
+ int i = find_first_bit(ctx->predicated_regs, TOTAL_PER_THREAD_REGS);
+ while (i < TOTAL_PER_THREAD_REGS) {
+ tcg_gen_mov_tl(hex_new_value[i], hex_gpr[i]);
+ i = find_next_bit(ctx->predicated_regs, TOTAL_PER_THREAD_REGS,
+ i + 1);
+ }
+ }
+
+ /* Preload the predicated HVX registers into future_VRegs and tmp_VRegs */
+ if (!bitmap_empty(ctx->predicated_future_vregs, NUM_VREGS)) {
+ int i = find_first_bit(ctx->predicated_future_vregs, NUM_VREGS);
+ while (i < NUM_VREGS) {
+ const intptr_t VdV_off =
+ ctx_future_vreg_off(ctx, i, 1, true);
+ intptr_t src_off = offsetof(CPUHexagonState, VRegs[i]);
+ tcg_gen_gvec_mov(MO_64, VdV_off,
+ src_off,
+ sizeof(MMVector),
+ sizeof(MMVector));
+ i = find_next_bit(ctx->predicated_future_vregs, NUM_VREGS, i + 1);
+ }
+ }
+ if (!bitmap_empty(ctx->predicated_tmp_vregs, NUM_VREGS)) {
+ int i = find_first_bit(ctx->predicated_tmp_vregs, NUM_VREGS);
+ while (i < NUM_VREGS) {
+ const intptr_t VdV_off =
+ ctx_tmp_vreg_off(ctx, i, 1, true);
+ intptr_t src_off = offsetof(CPUHexagonState, VRegs[i]);
+ tcg_gen_gvec_mov(MO_64, VdV_off,
+ src_off,
+ sizeof(MMVector),
+ sizeof(MMVector));
+ i = find_next_bit(ctx->predicated_tmp_vregs, NUM_VREGS, i + 1);
+ }
}
}
@@ -336,66 +469,6 @@
return false;
}
-/*
- * The LOG_*_WRITE macros mark most of the writes in a packet
- * However, there are some implicit writes marked as attributes
- * of the applicable instructions.
- */
-static void mark_implicit_reg_write(DisasContext *ctx, int attrib, int rnum)
-{
- uint16_t opcode = ctx->insn->opcode;
- if (GET_ATTRIB(opcode, attrib)) {
- /*
- * USR is used to set overflow and FP exceptions,
- * so treat it as conditional
- */
- bool is_predicated = GET_ATTRIB(opcode, A_CONDEXEC) ||
- rnum == HEX_REG_USR;
-
- /* LC0/LC1 is conditionally written by endloop instructions */
- if ((rnum == HEX_REG_LC0 || rnum == HEX_REG_LC1) &&
- (opcode == J2_endloop0 ||
- opcode == J2_endloop1 ||
- opcode == J2_endloop01)) {
- is_predicated = true;
- }
-
- if (is_predicated && !is_preloaded(ctx, rnum)) {
- tcg_gen_mov_tl(hex_new_value[rnum], hex_gpr[rnum]);
- }
-
- ctx_log_reg_write(ctx, rnum);
- }
-}
-
-static void mark_implicit_pred_write(DisasContext *ctx, int attrib, int pnum)
-{
- if (GET_ATTRIB(ctx->insn->opcode, attrib)) {
- ctx_log_pred_write(ctx, pnum);
- }
-}
-
-static void mark_implicit_reg_writes(DisasContext *ctx)
-{
- mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_FP, HEX_REG_FP);
- mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_SP, HEX_REG_SP);
- mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_LR, HEX_REG_LR);
- mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_LC0, HEX_REG_LC0);
- mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_SA0, HEX_REG_SA0);
- mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_LC1, HEX_REG_LC1);
- mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_SA1, HEX_REG_SA1);
- mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_USR, HEX_REG_USR);
- mark_implicit_reg_write(ctx, A_FPOP, HEX_REG_USR);
-}
-
-static void mark_implicit_pred_writes(DisasContext *ctx)
-{
- mark_implicit_pred_write(ctx, A_IMPLICIT_WRITES_P0, 0);
- mark_implicit_pred_write(ctx, A_IMPLICIT_WRITES_P1, 1);
- mark_implicit_pred_write(ctx, A_IMPLICIT_WRITES_P2, 2);
- mark_implicit_pred_write(ctx, A_IMPLICIT_WRITES_P3, 3);
-}
-
static void mark_store_width(DisasContext *ctx)
{
uint16_t opcode = ctx->insn->opcode;
@@ -423,9 +496,7 @@
static void gen_insn(DisasContext *ctx)
{
if (ctx->insn->generate) {
- mark_implicit_reg_writes(ctx);
ctx->insn->generate(ctx);
- mark_implicit_pred_writes(ctx);
mark_store_width(ctx);
} else {
gen_exception_end_tb(ctx, HEX_EXCP_INVALID_OPCODE);
@@ -646,65 +717,31 @@
/*
* for (i = 0; i < ctx->vreg_log_idx; i++) {
* int rnum = ctx->vreg_log[i];
- * if (ctx->vreg_is_predicated[i]) {
- * if (env->VRegs_updated & (1 << rnum)) {
- * env->VRegs[rnum] = env->future_VRegs[rnum];
- * }
- * } else {
- * env->VRegs[rnum] = env->future_VRegs[rnum];
- * }
+ * env->VRegs[rnum] = env->future_VRegs[rnum];
* }
*/
for (i = 0; i < ctx->vreg_log_idx; i++) {
int rnum = ctx->vreg_log[i];
- bool is_predicated = ctx->vreg_is_predicated[i];
intptr_t dstoff = offsetof(CPUHexagonState, VRegs[rnum]);
intptr_t srcoff = ctx_future_vreg_off(ctx, rnum, 1, false);
size_t size = sizeof(MMVector);
- if (is_predicated) {
- TCGv cmp = tcg_temp_new();
- TCGLabel *label_skip = gen_new_label();
-
- tcg_gen_andi_tl(cmp, hex_VRegs_updated, 1 << rnum);
- tcg_gen_brcondi_tl(TCG_COND_EQ, cmp, 0, label_skip);
- tcg_gen_gvec_mov(MO_64, dstoff, srcoff, size, size);
- gen_set_label(label_skip);
- } else {
- tcg_gen_gvec_mov(MO_64, dstoff, srcoff, size, size);
- }
+ tcg_gen_gvec_mov(MO_64, dstoff, srcoff, size, size);
}
/*
* for (i = 0; i < ctx->qreg_log_idx; i++) {
* int rnum = ctx->qreg_log[i];
- * if (ctx->qreg_is_predicated[i]) {
- * if (env->QRegs_updated) & (1 << rnum)) {
- * env->QRegs[rnum] = env->future_QRegs[rnum];
- * }
- * } else {
- * env->QRegs[rnum] = env->future_QRegs[rnum];
- * }
+ * env->QRegs[rnum] = env->future_QRegs[rnum];
* }
*/
for (i = 0; i < ctx->qreg_log_idx; i++) {
int rnum = ctx->qreg_log[i];
- bool is_predicated = ctx->qreg_is_predicated[i];
intptr_t dstoff = offsetof(CPUHexagonState, QRegs[rnum]);
intptr_t srcoff = offsetof(CPUHexagonState, future_QRegs[rnum]);
size_t size = sizeof(MMQReg);
- if (is_predicated) {
- TCGv cmp = tcg_temp_new();
- TCGLabel *label_skip = gen_new_label();
-
- tcg_gen_andi_tl(cmp, hex_QRegs_updated, 1 << rnum);
- tcg_gen_brcondi_tl(TCG_COND_EQ, cmp, 0, label_skip);
- tcg_gen_gvec_mov(MO_64, dstoff, srcoff, size, size);
- gen_set_label(label_skip);
- } else {
- tcg_gen_gvec_mov(MO_64, dstoff, srcoff, size, size);
- }
+ tcg_gen_gvec_mov(MO_64, dstoff, srcoff, size, size);
}
if (pkt_has_hvx_store(ctx->pkt)) {
@@ -775,13 +812,27 @@
TCGv mask_tcgv;
if (has_store_s0) {
- mask |= (1 << 0);
+ mask =
+ FIELD_DP32(mask, PROBE_PKT_SCALAR_HVX_STORES, HAS_ST0, 1);
}
if (has_store_s1) {
- mask |= (1 << 1);
+ mask =
+ FIELD_DP32(mask, PROBE_PKT_SCALAR_HVX_STORES, HAS_ST1, 1);
}
if (has_hvx_store) {
- mask |= (1 << 2);
+ mask =
+ FIELD_DP32(mask, PROBE_PKT_SCALAR_HVX_STORES,
+ HAS_HVX_STORES, 1);
+ }
+ if (has_store_s0 && slot_is_predicated(pkt, 0)) {
+ mask =
+ FIELD_DP32(mask, PROBE_PKT_SCALAR_HVX_STORES,
+ S0_IS_PRED, 1);
+ }
+ if (has_store_s1 && slot_is_predicated(pkt, 1)) {
+ mask =
+ FIELD_DP32(mask, PROBE_PKT_SCALAR_HVX_STORES,
+ S1_IS_PRED, 1);
}
mask_tcgv = tcg_constant_tl(mask);
gen_helper_probe_pkt_scalar_hvx_stores(cpu_env, mask_tcgv, mem_idx);
@@ -791,8 +842,15 @@
* process_store_log will execute the slot 1 store first,
* so we only have to probe the store in slot 0
*/
- TCGv mem_idx = tcg_constant_tl(ctx->mem_idx);
- gen_helper_probe_pkt_scalar_store_s0(cpu_env, mem_idx);
+ int args = 0;
+ args =
+ FIELD_DP32(args, PROBE_PKT_SCALAR_STORE_S0, MMU_IDX, ctx->mem_idx);
+ if (slot_is_predicated(pkt, 0)) {
+ args =
+ FIELD_DP32(args, PROBE_PKT_SCALAR_STORE_S0, IS_PREDICATED, 1);
+ }
+ TCGv args_tcgv = tcg_constant_tl(args);
+ gen_helper_probe_pkt_scalar_store_s0(cpu_env, args_tcgv);
}
process_store_log(ctx);
@@ -1029,10 +1087,6 @@
offsetof(CPUHexagonState, llsc_val), "llsc_val");
hex_llsc_val_i64 = tcg_global_mem_new_i64(cpu_env,
offsetof(CPUHexagonState, llsc_val_i64), "llsc_val_i64");
- hex_VRegs_updated = tcg_global_mem_new(cpu_env,
- offsetof(CPUHexagonState, VRegs_updated), "VRegs_updated");
- hex_QRegs_updated = tcg_global_mem_new(cpu_env,
- offsetof(CPUHexagonState, QRegs_updated), "QRegs_updated");
for (i = 0; i < STORES_MAX; i++) {
snprintf(store_addr_names[i], NAME_LEN, "store_addr_%d", i);
hex_store_addr[i] = tcg_global_mem_new(cpu_env,
diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h
index d971f4f..db832b0 100644
--- a/target/hexagon/translate.h
+++ b/target/hexagon/translate.h
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -38,6 +38,7 @@
int reg_log[REG_WRITES_MAX];
int reg_log_idx;
DECLARE_BITMAP(regs_written, TOTAL_PER_THREAD_REGS);
+ DECLARE_BITMAP(predicated_regs, TOTAL_PER_THREAD_REGS);
int preg_log[PRED_WRITES_MAX];
int preg_log_idx;
DECLARE_BITMAP(pregs_written, NUM_PREGS);
@@ -48,52 +49,54 @@
int tmp_vregs_idx;
int tmp_vregs_num[VECTOR_TEMPS_MAX];
int vreg_log[NUM_VREGS];
- bool vreg_is_predicated[NUM_VREGS];
int vreg_log_idx;
DECLARE_BITMAP(vregs_updated_tmp, NUM_VREGS);
DECLARE_BITMAP(vregs_updated, NUM_VREGS);
DECLARE_BITMAP(vregs_select, NUM_VREGS);
+ DECLARE_BITMAP(predicated_future_vregs, NUM_VREGS);
+ DECLARE_BITMAP(predicated_tmp_vregs, NUM_VREGS);
int qreg_log[NUM_QREGS];
- bool qreg_is_predicated[NUM_QREGS];
int qreg_log_idx;
bool pre_commit;
TCGCond branch_cond;
target_ulong branch_dest;
bool is_tight_loop;
+ bool need_pkt_has_store_s1;
} DisasContext;
-static inline void ctx_log_reg_write(DisasContext *ctx, int rnum)
-{
- if (test_bit(rnum, ctx->regs_written)) {
- HEX_DEBUG_LOG("WARNING: Multiple writes to r%d\n", rnum);
- }
- ctx->reg_log[ctx->reg_log_idx] = rnum;
- ctx->reg_log_idx++;
- set_bit(rnum, ctx->regs_written);
-}
-
-static inline void ctx_log_reg_write_pair(DisasContext *ctx, int rnum)
-{
- ctx_log_reg_write(ctx, rnum);
- ctx_log_reg_write(ctx, rnum + 1);
-}
-
static inline void ctx_log_pred_write(DisasContext *ctx, int pnum)
{
- ctx->preg_log[ctx->preg_log_idx] = pnum;
- ctx->preg_log_idx++;
- set_bit(pnum, ctx->pregs_written);
+ if (!test_bit(pnum, ctx->pregs_written)) {
+ ctx->preg_log[ctx->preg_log_idx] = pnum;
+ ctx->preg_log_idx++;
+ set_bit(pnum, ctx->pregs_written);
+ }
}
-static inline bool is_preloaded(DisasContext *ctx, int num)
+static inline void ctx_log_reg_write(DisasContext *ctx, int rnum,
+ bool is_predicated)
{
- return test_bit(num, ctx->regs_written);
+ if (rnum == HEX_REG_P3_0_ALIASED) {
+ for (int i = 0; i < NUM_PREGS; i++) {
+ ctx_log_pred_write(ctx, i);
+ }
+ } else {
+ if (!test_bit(rnum, ctx->regs_written)) {
+ ctx->reg_log[ctx->reg_log_idx] = rnum;
+ ctx->reg_log_idx++;
+ set_bit(rnum, ctx->regs_written);
+ }
+ if (is_predicated) {
+ set_bit(rnum, ctx->predicated_regs);
+ }
+ }
}
-static inline bool is_vreg_preloaded(DisasContext *ctx, int num)
+static inline void ctx_log_reg_write_pair(DisasContext *ctx, int rnum,
+ bool is_predicated)
{
- return test_bit(num, ctx->vregs_updated) ||
- test_bit(num, ctx->vregs_updated_tmp);
+ ctx_log_reg_write(ctx, rnum, is_predicated);
+ ctx_log_reg_write(ctx, rnum + 1, is_predicated);
}
intptr_t ctx_future_vreg_off(DisasContext *ctx, int regnum,
@@ -106,17 +109,25 @@
bool is_predicated)
{
if (type != EXT_TMP) {
- ctx->vreg_log[ctx->vreg_log_idx] = rnum;
- ctx->vreg_is_predicated[ctx->vreg_log_idx] = is_predicated;
- ctx->vreg_log_idx++;
+ if (!test_bit(rnum, ctx->vregs_updated)) {
+ ctx->vreg_log[ctx->vreg_log_idx] = rnum;
+ ctx->vreg_log_idx++;
+ set_bit(rnum, ctx->vregs_updated);
+ }
set_bit(rnum, ctx->vregs_updated);
+ if (is_predicated) {
+ set_bit(rnum, ctx->predicated_future_vregs);
+ }
}
if (type == EXT_NEW) {
set_bit(rnum, ctx->vregs_select);
}
if (type == EXT_TMP) {
set_bit(rnum, ctx->vregs_updated_tmp);
+ if (is_predicated) {
+ set_bit(rnum, ctx->predicated_tmp_vregs);
+ }
}
}
@@ -129,10 +140,9 @@
}
static inline void ctx_log_qreg_write(DisasContext *ctx,
- int rnum, bool is_predicated)
+ int rnum)
{
ctx->qreg_log[ctx->qreg_log_idx] = rnum;
- ctx->qreg_is_predicated[ctx->qreg_log_idx] = is_predicated;
ctx->qreg_log_idx++;
}
@@ -153,12 +163,20 @@
extern TCGv hex_llsc_addr;
extern TCGv hex_llsc_val;
extern TCGv_i64 hex_llsc_val_i64;
-extern TCGv hex_VRegs_updated;
-extern TCGv hex_QRegs_updated;
extern TCGv hex_vstore_addr[VSTORES_MAX];
extern TCGv hex_vstore_size[VSTORES_MAX];
extern TCGv hex_vstore_pending[VSTORES_MAX];
bool is_gather_store_insn(DisasContext *ctx);
void process_store(DisasContext *ctx, int slot_num);
+
+FIELD(PROBE_PKT_SCALAR_STORE_S0, MMU_IDX, 0, 2)
+FIELD(PROBE_PKT_SCALAR_STORE_S0, IS_PREDICATED, 2, 1)
+
+FIELD(PROBE_PKT_SCALAR_HVX_STORES, HAS_ST0, 0, 1)
+FIELD(PROBE_PKT_SCALAR_HVX_STORES, HAS_ST1, 1, 1)
+FIELD(PROBE_PKT_SCALAR_HVX_STORES, HAS_HVX_STORES, 2, 1)
+FIELD(PROBE_PKT_SCALAR_HVX_STORES, S0_IS_PRED, 3, 1)
+FIELD(PROBE_PKT_SCALAR_HVX_STORES, S1_IS_PRED, 4, 1)
+
#endif
diff --git a/target/hppa/gdbstub.c b/target/hppa/gdbstub.c
index 729c37b..48a5143 100644
--- a/target/hppa/gdbstub.c
+++ b/target/hppa/gdbstub.c
@@ -19,7 +19,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
int hppa_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
{
diff --git a/target/i386/gdbstub.c b/target/i386/gdbstub.c
index 7869712..ebb000d 100644
--- a/target/i386/gdbstub.c
+++ b/target/i386/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "include/gdbstub/helpers.h"
#ifdef TARGET_X86_64
static const int gpr_map[16] = {
diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c
index bad3131..0bb6c60 100644
--- a/target/i386/kvm/xen-emu.c
+++ b/target/i386/kvm/xen-emu.c
@@ -1406,6 +1406,11 @@
return err;
}
+ err = xen_gnttab_reset();
+ if (err) {
+ return err;
+ }
+
err = xen_xenstore_reset();
if (err) {
return err;
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 3d0c0b3..52af816 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -12,7 +12,7 @@
#include "cpu.h"
#include "exec/address-spaces.h"
#include "exec/ioport.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/accel.h"
#include "sysemu/whpx.h"
#include "sysemu/cpus.h"
diff --git a/target/loongarch/gdbstub.c b/target/loongarch/gdbstub.c
index a4d1e28..fa3e034 100644
--- a/target/loongarch/gdbstub.c
+++ b/target/loongarch/gdbstub.c
@@ -10,6 +10,7 @@
#include "cpu.h"
#include "internals.h"
#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
uint64_t read_fcc(CPULoongArchState *env)
{
diff --git a/target/m68k/gdbstub.c b/target/m68k/gdbstub.c
index eb2d030..1e5f033 100644
--- a/target/m68k/gdbstub.c
+++ b/target/m68k/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
int m68k_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
{
diff --git a/target/m68k/helper.c b/target/m68k/helper.c
index 4621cf2..3b3a6ea 100644
--- a/target/m68k/helper.c
+++ b/target/m68k/helper.c
@@ -23,6 +23,7 @@
#include "exec/exec-all.h"
#include "exec/gdbstub.h"
#include "exec/helper-proto.h"
+#include "gdbstub/helpers.h"
#include "fpu/softfloat.h"
#include "qemu/qemu-print.h"
diff --git a/target/m68k/m68k-semi.c b/target/m68k/m68k-semi.c
index 87b1314..88ad9ba 100644
--- a/target/m68k/m68k-semi.c
+++ b/target/m68k/m68k-semi.c
@@ -20,7 +20,8 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
+#include "gdbstub/helpers.h"
#include "semihosting/syscalls.h"
#include "semihosting/softmmu-uaccess.h"
#include "hw/boards.h"
diff --git a/target/microblaze/gdbstub.c b/target/microblaze/gdbstub.c
index 8143fca..29ac6e9 100644
--- a/target/microblaze/gdbstub.c
+++ b/target/microblaze/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
/*
* GDB expects SREGs in the following order:
diff --git a/target/mips/cpu-defs.c.inc b/target/mips/cpu-defs.c.inc
index 480e60a..d45f245 100644
--- a/target/mips/cpu-defs.c.inc
+++ b/target/mips/cpu-defs.c.inc
@@ -332,7 +332,11 @@
(0x1 << CP0C0_AR) | (MMU_TYPE_FMT << CP0C0_MT),
.CP0_Config1 = MIPS_CONFIG1,
.CP0_Config2 = MIPS_CONFIG2,
- .CP0_Config3 = MIPS_CONFIG3 | (0x2 << CP0C3_ISA) | (1 << CP0C3_VInt),
+ .CP0_Config3 = MIPS_CONFIG3 | (0x2 << CP0C3_ISA) | (1 << CP0C3_VInt) |
+ (1 << CP0C3_M),
+ .CP0_Config4 = MIPS_CONFIG4 | (1 << CP0C4_M),
+ .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_NFExists),
+ .CP0_Config7 = 1 << CP0C7_WII,
.CP0_LLAddr_rw_bitmask = 0,
.CP0_LLAddr_shift = 4,
.SYNCI_Step = 32,
@@ -353,7 +357,11 @@
(0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) |
(0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA),
.CP0_Config2 = MIPS_CONFIG2,
- .CP0_Config3 = MIPS_CONFIG3 | (0x2 << CP0C3_ISA) | (0 << CP0C3_VInt),
+ .CP0_Config3 = MIPS_CONFIG3 | (0x2 << CP0C3_ISA) | (0 << CP0C3_VInt) |
+ (1 << CP0C3_M),
+ .CP0_Config4 = MIPS_CONFIG4 | (1 << CP0C4_M),
+ .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_NFExists),
+ .CP0_Config7 = 1 << CP0C7_WII,
.CP0_LLAddr_rw_bitmask = 0,
.CP0_LLAddr_shift = 4,
.SYNCI_Step = 32,
@@ -392,6 +400,7 @@
.CP0_Config5_rw_bitmask = (1 << CP0C5_K) | (1 << CP0C5_CV) |
(1 << CP0C5_MSAEn) | (1 << CP0C5_UFE) |
(1 << CP0C5_FRE) | (1 << CP0C5_UFR),
+ .CP0_Config7 = 1 << CP0C7_WII,
.CP0_LLAddr_rw_bitmask = 0,
.CP0_LLAddr_shift = 0,
.SYNCI_Step = 32,
diff --git a/target/mips/cpu.c b/target/mips/cpu.c
index 05caf54..543da91 100644
--- a/target/mips/cpu.c
+++ b/target/mips/cpu.c
@@ -143,11 +143,13 @@
/*
* Prior to MIPS Release 6 it is implementation dependent if non-enabled
* interrupts wake-up the CPU, however most of the implementations only
- * check for interrupts that can be taken.
+ * check for interrupts that can be taken. For pre-release 6 CPUs,
+ * check for CP0 Config7 'Wait IE ignore' bit.
*/
if ((cs->interrupt_request & CPU_INTERRUPT_HARD) &&
cpu_mips_hw_interrupts_pending(env)) {
if (cpu_mips_hw_interrupts_enabled(env) ||
+ (env->CP0_Config7 & (1 << CP0C7_WII)) ||
(env->insn_flags & ISA_MIPS_R6)) {
has_work = true;
}
diff --git a/target/mips/cpu.h b/target/mips/cpu.h
index caf2b06..142c55a 100644
--- a/target/mips/cpu.h
+++ b/target/mips/cpu.h
@@ -980,6 +980,7 @@
#define CP0C6_DATAPREF 0
int32_t CP0_Config7;
int64_t CP0_Config7_rw_bitmask;
+#define CP0C7_WII 31
#define CP0C7_NAPCGEN 2
#define CP0C7_UNIMUEN 1
#define CP0C7_VFPUCGEN 0
diff --git a/target/mips/gdbstub.c b/target/mips/gdbstub.c
index f1c2a2c..62d7b72 100644
--- a/target/mips/gdbstub.c
+++ b/target/mips/gdbstub.c
@@ -20,7 +20,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "internal.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "fpu_helper.h"
int mips_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
diff --git a/target/mips/sysemu/physaddr.c b/target/mips/sysemu/physaddr.c
index 2970df8..05990aa 100644
--- a/target/mips/sysemu/physaddr.c
+++ b/target/mips/sysemu/physaddr.c
@@ -70,8 +70,7 @@
/* is this AM mapped in current execution mode */
return ((adetlb_mask << am) < 0);
default:
- assert(0);
- return TLBRET_BADADDR;
+ g_assert_not_reached();
};
}
diff --git a/target/mips/tcg/ldst_helper.c b/target/mips/tcg/ldst_helper.c
index d0bd026..c1a8380 100644
--- a/target/mips/tcg/ldst_helper.c
+++ b/target/mips/tcg/ldst_helper.c
@@ -248,14 +248,14 @@
target_ulong i;
for (i = 0; i < base_reglist; i++) {
- cpu_stw_mmuidx_ra(env, addr, env->active_tc.gpr[multiple_regs[i]],
+ cpu_stl_mmuidx_ra(env, addr, env->active_tc.gpr[multiple_regs[i]],
mem_idx, GETPC());
addr += 4;
}
}
if (do_r31) {
- cpu_stw_mmuidx_ra(env, addr, env->active_tc.gpr[31], mem_idx, GETPC());
+ cpu_stl_mmuidx_ra(env, addr, env->active_tc.gpr[31], mem_idx, GETPC());
}
}
diff --git a/target/mips/tcg/msa_helper.c b/target/mips/tcg/msa_helper.c
index 736283e..29b31d7 100644
--- a/target/mips/tcg/msa_helper.c
+++ b/target/mips/tcg/msa_helper.c
@@ -5333,7 +5333,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
msa_move_v(pwd, pwx);
}
@@ -5368,7 +5368,7 @@
} \
break; \
default: \
- assert(0); \
+ g_assert_not_reached(); \
} \
}
@@ -5413,7 +5413,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
}
@@ -5461,7 +5461,7 @@
} \
break; \
default: \
- assert(0); \
+ g_assert_not_reached(); \
} \
}
@@ -5511,7 +5511,7 @@
} \
break; \
default: \
- assert(0); \
+ g_assert_not_reached(); \
} \
}
@@ -5557,7 +5557,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
}
@@ -5632,7 +5632,7 @@
pwd->d[1] = msa_ ## func ## _df(df, pws->d[1], pwt->d[1]); \
break; \
default: \
- assert(0); \
+ g_assert_not_reached(); \
} \
}
@@ -5771,7 +5771,7 @@
pwd->d[1] = msa_ ## func ## _df(df, pwd->d[1], pws->d[1], pwt->d[1]); \
break; \
default: \
- assert(0); \
+ g_assert_not_reached(); \
} \
}
@@ -5811,7 +5811,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
}
@@ -5869,7 +5869,7 @@
MSA_LOOP_D; \
break; \
default: \
- assert(0); \
+ g_assert_not_reached(); \
} \
msa_move_v(pwd, pwx); \
}
@@ -6090,7 +6090,7 @@
pwd->d[n] = (int64_t)pws->d[0];
break;
default:
- assert(0);
+ g_assert_not_reached();
}
}
@@ -6150,7 +6150,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
}
@@ -6565,7 +6565,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, retaddr);
@@ -6596,7 +6596,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, retaddr);
@@ -6625,7 +6625,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, retaddr);
@@ -6654,7 +6654,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, retaddr);
@@ -6683,7 +6683,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, retaddr);
@@ -6712,7 +6712,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, retaddr);
@@ -6741,7 +6741,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, retaddr);
@@ -6770,7 +6770,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, retaddr);
@@ -6799,7 +6799,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, retaddr);
@@ -6828,7 +6828,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, retaddr);
@@ -6857,7 +6857,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, retaddr);
@@ -7107,7 +7107,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -7137,7 +7137,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -7167,7 +7167,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -7198,7 +7198,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -7245,7 +7245,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -7280,7 +7280,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -7317,7 +7317,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -7371,7 +7371,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -7417,7 +7417,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -7526,7 +7526,7 @@
} else {
- assert(0);
+ g_assert_not_reached();
}
@@ -7555,7 +7555,7 @@
FMAXMIN_A(min, max, pwx->d[0], pws->d[0], pwt->d[0], 64, status);
FMAXMIN_A(min, max, pwx->d[1], pws->d[1], pwt->d[1], 64, status);
} else {
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -7628,7 +7628,7 @@
} else {
- assert(0);
+ g_assert_not_reached();
}
@@ -7657,7 +7657,7 @@
FMAXMIN_A(max, min, pwx->d[0], pws->d[0], pwt->d[0], 64, status);
FMAXMIN_A(max, min, pwx->d[1], pws->d[1], pwt->d[1], 64, status);
} else {
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -7681,7 +7681,7 @@
pwd->d[0] = float_class_d(pws->d[0], status);
pwd->d[1] = float_class_d(pws->d[1], status);
} else {
- assert(0);
+ g_assert_not_reached();
}
}
@@ -7723,7 +7723,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -7753,7 +7753,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -7783,7 +7783,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -7832,7 +7832,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -7862,7 +7862,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -7892,7 +7892,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -7946,7 +7946,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -7983,7 +7983,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -8019,7 +8019,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -8046,7 +8046,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
msa_move_v(pwd, pwx);
@@ -8072,7 +8072,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
msa_move_v(pwd, pwx);
@@ -8100,7 +8100,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -8130,7 +8130,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -8166,7 +8166,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
@@ -8196,7 +8196,7 @@
}
break;
default:
- assert(0);
+ g_assert_not_reached();
}
check_msacsr_cause(env, GETPC());
diff --git a/target/mips/tcg/sysemu/mips-semi.c b/target/mips/tcg/sysemu/mips-semi.c
index 85f0567..f3735df 100644
--- a/target/mips/tcg/sysemu/mips-semi.c
+++ b/target/mips/tcg/sysemu/mips-semi.c
@@ -20,7 +20,8 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "qemu/log.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
+#include "gdbstub/helpers.h"
#include "semihosting/softmmu-uaccess.h"
#include "semihosting/semihost.h"
#include "semihosting/console.h"
diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c
index 8cad3d1..24993bc 100644
--- a/target/mips/tcg/translate.c
+++ b/target/mips/tcg/translate.c
@@ -4887,6 +4887,14 @@
break;
case OPC_J:
case OPC_JAL:
+ {
+ /* Jump to immediate */
+ int jal_mask = ctx->hflags & MIPS_HFLAG_M16 ? 0xF8000000
+ : 0xF0000000;
+ btgt = ((ctx->base.pc_next + insn_bytes) & jal_mask)
+ | (uint32_t)offset;
+ break;
+ }
case OPC_JALX:
/* Jump to immediate */
btgt = ((ctx->base.pc_next + insn_bytes) & (int32_t)0xF0000000) |
diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c
index cff3082..bc5cbf8 100644
--- a/target/nios2/cpu.c
+++ b/target/nios2/cpu.c
@@ -23,7 +23,7 @@
#include "qapi/error.h"
#include "cpu.h"
#include "exec/log.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "hw/qdev-properties.h"
static void nios2_cpu_set_pc(CPUState *cs, vaddr value)
diff --git a/target/nios2/nios2-semi.c b/target/nios2/nios2-semi.c
index f76e858..3738774 100644
--- a/target/nios2/nios2-semi.c
+++ b/target/nios2/nios2-semi.c
@@ -23,7 +23,8 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/syscalls.h"
+#include "gdbstub/helpers.h"
#include "semihosting/syscalls.h"
#include "semihosting/softmmu-uaccess.h"
#include "qemu/log.h"
diff --git a/target/openrisc/gdbstub.c b/target/openrisc/gdbstub.c
index 095bf76..d1074a0 100644
--- a/target/openrisc/gdbstub.c
+++ b/target/openrisc/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
int openrisc_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
{
diff --git a/target/openrisc/interrupt.c b/target/openrisc/interrupt.c
index c31c6f1..3887812 100644
--- a/target/openrisc/interrupt.c
+++ b/target/openrisc/interrupt.c
@@ -21,7 +21,7 @@
#include "qemu/log.h"
#include "cpu.h"
#include "exec/exec-all.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#ifndef CONFIG_USER_ONLY
#include "hw/loader.h"
diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c
index 0b8afdb..603c267 100644
--- a/target/openrisc/mmu.c
+++ b/target/openrisc/mmu.c
@@ -22,7 +22,7 @@
#include "qemu/log.h"
#include "cpu.h"
#include "exec/exec-all.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "hw/loader.h"
diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c
index d62ffe8..0ce2e3c 100644
--- a/target/ppc/cpu_init.c
+++ b/target/ppc/cpu_init.c
@@ -20,7 +20,7 @@
#include "qemu/osdep.h"
#include "disas/dis-asm.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "kvm_ppc.h"
#include "sysemu/cpus.h"
#include "sysemu/hw_accel.h"
diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c
index 1a0b9ca..d2bc1d7 100644
--- a/target/ppc/gdbstub.c
+++ b/target/ppc/gdbstub.c
@@ -20,6 +20,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "internal.h"
static int ppc_gdb_register_len_apple(int n)
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index ab56663..d522efc 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -25,6 +25,7 @@
#include "time_helper.h"
#include "qemu/main-loop.h"
#include "exec/exec-all.h"
+#include "exec/tb-flush.h"
#include "sysemu/cpu-timers.h"
#include "qemu/guest-random.h"
#include "qapi/error.h"
diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c
index 6048541..840d1ec 100644
--- a/target/riscv/gdbstub.c
+++ b/target/riscv/gdbstub.c
@@ -18,6 +18,7 @@
#include "qemu/osdep.h"
#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "cpu.h"
struct TypeSize {
diff --git a/target/rx/gdbstub.c b/target/rx/gdbstub.c
index 7eb2059..d7e0e66 100644
--- a/target/rx/gdbstub.c
+++ b/target/rx/gdbstub.c
@@ -17,7 +17,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
int rx_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
{
diff --git a/target/s390x/gdbstub.c b/target/s390x/gdbstub.c
index a5d69d0..0cb6939 100644
--- a/target/s390x/gdbstub.c
+++ b/target/s390x/gdbstub.c
@@ -23,6 +23,7 @@
#include "s390x-internal.h"
#include "exec/exec-all.h"
#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/bitops.h"
#include "sysemu/hw_accel.h"
#include "sysemu/tcg.h"
diff --git a/target/s390x/helper.c b/target/s390x/helper.c
index 473c8e5..2b363aa 100644
--- a/target/s390x/helper.c
+++ b/target/s390x/helper.c
@@ -21,7 +21,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "s390x-internal.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/timer.h"
#include "hw/s390x/ioinst.h"
#include "hw/s390x/pv.h"
diff --git a/target/sh4/gdbstub.c b/target/sh4/gdbstub.c
index 3488f68..d8e199f 100644
--- a/target/sh4/gdbstub.c
+++ b/target/sh4/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
/* Hint: Use "set architecture sh4" in GDB to see fpu registers */
/* FIXME: We should use XML for this. */
diff --git a/target/sparc/gdbstub.c b/target/sparc/gdbstub.c
index 5d1e808..a1c8fdc 100644
--- a/target/sparc/gdbstub.c
+++ b/target/sparc/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#ifdef TARGET_ABI32
#define gdb_get_rega(buf, val) gdb_get_reg32(buf, val)
diff --git a/target/tricore/gdbstub.c b/target/tricore/gdbstub.c
index 3a27a7e..e8f8e5e 100644
--- a/target/tricore/gdbstub.c
+++ b/target/tricore/gdbstub.c
@@ -18,7 +18,7 @@
*/
#include "qemu/osdep.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#define LCX_REGNUM 32
diff --git a/target/xtensa/core-dc232b.c b/target/xtensa/core-dc232b.c
index c982d09..9aba266 100644
--- a/target/xtensa/core-dc232b.c
+++ b/target/xtensa/core-dc232b.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "qemu/timer.h"
diff --git a/target/xtensa/core-dc233c.c b/target/xtensa/core-dc233c.c
index 595ab9a..9b0a625 100644
--- a/target/xtensa/core-dc233c.c
+++ b/target/xtensa/core-dc233c.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-dc233c/core-isa.h"
diff --git a/target/xtensa/core-de212.c b/target/xtensa/core-de212.c
index 50c995b..b08fe22 100644
--- a/target/xtensa/core-de212.c
+++ b/target/xtensa/core-de212.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-de212/core-isa.h"
diff --git a/target/xtensa/core-de233_fpu.c b/target/xtensa/core-de233_fpu.c
index 41af805..8845cdb 100644
--- a/target/xtensa/core-de233_fpu.c
+++ b/target/xtensa/core-de233_fpu.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-de233_fpu/core-isa.h"
diff --git a/target/xtensa/core-dsp3400.c b/target/xtensa/core-dsp3400.c
index 81e425c..c0f94b9 100644
--- a/target/xtensa/core-dsp3400.c
+++ b/target/xtensa/core-dsp3400.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-dsp3400/core-isa.h"
diff --git a/target/xtensa/core-fsf.c b/target/xtensa/core-fsf.c
index 3327c50..310be8d 100644
--- a/target/xtensa/core-fsf.c
+++ b/target/xtensa/core-fsf.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-fsf/core-isa.h"
diff --git a/target/xtensa/core-lx106.c b/target/xtensa/core-lx106.c
index 7a771d0..7f71d08 100644
--- a/target/xtensa/core-lx106.c
+++ b/target/xtensa/core-lx106.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-lx106/core-isa.h"
diff --git a/target/xtensa/core-sample_controller.c b/target/xtensa/core-sample_controller.c
index fd5de55..8867001 100644
--- a/target/xtensa/core-sample_controller.c
+++ b/target/xtensa/core-sample_controller.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-sample_controller/core-isa.h"
diff --git a/target/xtensa/core-test_kc705_be.c b/target/xtensa/core-test_kc705_be.c
index 294c16f..bd082f4 100644
--- a/target/xtensa/core-test_kc705_be.c
+++ b/target/xtensa/core-test_kc705_be.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-test_kc705_be/core-isa.h"
diff --git a/target/xtensa/core-test_mmuhifi_c3.c b/target/xtensa/core-test_mmuhifi_c3.c
index c0e5d32..3090dd0 100644
--- a/target/xtensa/core-test_mmuhifi_c3.c
+++ b/target/xtensa/core-test_mmuhifi_c3.c
@@ -27,7 +27,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-test_mmuhifi_c3/core-isa.h"
diff --git a/target/xtensa/gdbstub.c b/target/xtensa/gdbstub.c
index b669606..4b3bfb7 100644
--- a/target/xtensa/gdbstub.c
+++ b/target/xtensa/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/log.h"
enum {
diff --git a/target/xtensa/helper.c b/target/xtensa/helper.c
index 2aa9777..dbeb97a 100644
--- a/target/xtensa/helper.c
+++ b/target/xtensa/helper.c
@@ -29,7 +29,7 @@
#include "qemu/log.h"
#include "cpu.h"
#include "exec/exec-all.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "exec/helper-proto.h"
#include "qemu/error-report.h"
#include "qemu/qemu-print.h"
diff --git a/target/xtensa/import_core.sh b/target/xtensa/import_core.sh
index b4c1555..17dfec8 100755
--- a/target/xtensa/import_core.sh
+++ b/target/xtensa/import_core.sh
@@ -41,7 +41,7 @@
cat <<EOF > "${TARGET}.c"
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/gdbstub.h"
+#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "core-$NAME/core-isa.h"
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 29a4efb..62eecf2 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -309,10 +309,12 @@
'netdev-socket': files('netdev-socket.c', '../unit/socket-helpers.c'),
}
-gvnc = dependency('gvnc-1.0', required: false)
-if gvnc.found()
- qtests += {'vnc-display-test': [gvnc]}
- qtests_generic += [ 'vnc-display-test' ]
+if vnc.found()
+ gvnc = dependency('gvnc-1.0', required: false)
+ if gvnc.found()
+ qtests += {'vnc-display-test': [gvnc]}
+ qtests_generic += [ 'vnc-display-test' ]
+ endif
endif
if dbus_display
diff --git a/tests/qtest/readconfig-test.c b/tests/qtest/readconfig-test.c
index 9ef8706..2160603 100644
--- a/tests/qtest/readconfig-test.c
+++ b/tests/qtest/readconfig-test.c
@@ -124,13 +124,15 @@
}
#endif
-static void test_object_rng_resp(QObject *res)
+static void test_object_available(QObject *res, const char *name,
+ const char *type)
{
Visitor *v;
g_autoptr(ObjectPropertyInfoList) objs = NULL;
ObjectPropertyInfoList *tmp;
ObjectPropertyInfo *obj;
- bool seen_rng = false;
+ bool object_available = false;
+ g_autofree char *childtype = g_strdup_printf("child<%s>", type);
g_assert(res);
v = qobject_input_visitor_new(res);
@@ -142,16 +144,15 @@
g_assert(tmp->value);
obj = tmp->value;
- if (g_str_equal(obj->name, "rng0") &&
- g_str_equal(obj->type, "child<rng-builtin>")) {
- seen_rng = true;
+ if (g_str_equal(obj->name, name) && g_str_equal(obj->type, childtype)) {
+ object_available = true;
break;
}
tmp = tmp->next;
}
- g_assert(seen_rng);
+ g_assert(object_available);
visit_free(v);
}
@@ -170,7 +171,27 @@
resp = qtest_qmp(qts,
"{ 'execute': 'qom-list',"
" 'arguments': {'path': '/objects' }}");
- test_object_rng_resp(qdict_get(resp, "return"));
+ test_object_available(qdict_get(resp, "return"), "rng0", "rng-builtin");
+ qobject_unref(resp);
+
+ qtest_quit(qts);
+}
+
+static void test_docs_config_ich9(void)
+{
+ QTestState *qts;
+ QDict *resp;
+ QObject *qobj;
+
+ qts = qtest_initf("-nodefaults -readconfig docs/config/ich9-ehci-uhci.cfg");
+
+ resp = qtest_qmp(qts, "{ 'execute': 'qom-list',"
+ " 'arguments': {'path': '/machine/peripheral' }}");
+ qobj = qdict_get(resp, "return");
+ test_object_available(qobj, "ehci", "ich9-usb-ehci1");
+ test_object_available(qobj, "uhci-1", "ich9-usb-uhci1");
+ test_object_available(qobj, "uhci-2", "ich9-usb-uhci2");
+ test_object_available(qobj, "uhci-3", "ich9-usb-uhci3");
qobject_unref(resp);
qtest_quit(qts);
@@ -186,6 +207,7 @@
if (g_str_equal(arch, "i386") ||
g_str_equal(arch, "x86_64")) {
qtest_add_func("readconfig/x86/memdev", test_x86_memdev);
+ qtest_add_func("readconfig/x86/ich9-ehci-uhci", test_docs_config_ich9);
}
#ifdef CONFIG_SPICE
qtest_add_func("readconfig/spice", test_spice);
diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target
index db122ab..9e91a20 100644
--- a/tests/tcg/aarch64/Makefile.target
+++ b/tests/tcg/aarch64/Makefile.target
@@ -81,7 +81,7 @@
TESTS += sha512-vector
-ifneq ($(HAVE_GDB_BIN),)
+ifeq ($(HOST_GDB_SUPPORTS_ARCH),y)
GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py
run-gdbstub-sysregs: sysregs
diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target
index 18e6a59..0d82dfa 100644
--- a/tests/tcg/hexagon/Makefile.target
+++ b/tests/tcg/hexagon/Makefile.target
@@ -1,5 +1,5 @@
##
-## Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
+## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
@@ -45,6 +45,10 @@
HEX_TESTS += overflow
HEX_TESTS += signal_context
HEX_TESTS += reg_mut
+HEX_TESTS += vector_add_int
+HEX_TESTS += scatter_gather
+HEX_TESTS += hvx_misc
+HEX_TESTS += hvx_histogram
HEX_TESTS += test_abs
HEX_TESTS += test_bitcnt
@@ -78,3 +82,10 @@
usr: usr.c
$(CC) $(CFLAGS) -mv67t -O2 -Wno-inline-asm -Wno-expansion-to-defined $< -o $@ $(LDFLAGS)
+scatter_gather: CFLAGS += -mhvx
+vector_add_int: CFLAGS += -mhvx -fvectorize
+hvx_misc: CFLAGS += -mhvx
+hvx_histogram: CFLAGS += -mhvx -Wno-gnu-folding-constant
+
+hvx_histogram: hvx_histogram.c hvx_histogram_row.S
+ $(CC) $(CFLAGS) $(CROSS_CC_GUEST_CFLAGS) $^ -o $@ $(LDFLAGS)
diff --git a/tests/tcg/hexagon/fpstuff.c b/tests/tcg/hexagon/fpstuff.c
index 56bf562..90ce9a6 100644
--- a/tests/tcg/hexagon/fpstuff.c
+++ b/tests/tcg/hexagon/fpstuff.c
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2020-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2020-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -40,6 +40,7 @@
const int SF_small_neg = 0xab98fba8;
const int SF_denorm = 0x00000001;
const int SF_random = 0x346001d6;
+const int SF_neg_zero = 0x80000000;
const long long DF_QNaN = 0x7ff8000000000000ULL;
const long long DF_SNaN = 0x7ff7000000000000ULL;
@@ -536,6 +537,33 @@
check32(result, 0x146001d6);
}
+static void check_sffms(void)
+{
+ int result;
+
+ /* Check that sffms properly deals with -0 */
+ result = SF_neg_zero;
+ asm ("%0 -= sfmpy(%1 , %2)\n\t"
+ : "+r"(result)
+ : "r"(SF_ZERO), "r"(SF_ZERO)
+ : "r12", "r8");
+ check32(result, SF_neg_zero);
+
+ result = SF_ZERO;
+ asm ("%0 -= sfmpy(%1 , %2)\n\t"
+ : "+r"(result)
+ : "r"(SF_neg_zero), "r"(SF_ZERO)
+ : "r12", "r8");
+ check32(result, SF_ZERO);
+
+ result = SF_ZERO;
+ asm ("%0 -= sfmpy(%1 , %2)\n\t"
+ : "+r"(result)
+ : "r"(SF_ZERO), "r"(SF_neg_zero)
+ : "r12", "r8");
+ check32(result, SF_ZERO);
+}
+
static void check_float2int_convs()
{
int res32;
@@ -688,6 +716,7 @@
check_invsqrta();
check_sffixupn();
check_sffixupd();
+ check_sffms();
check_float2int_convs();
puts(err ? "FAIL" : "PASS");
diff --git a/tests/tcg/hexagon/preg_alias.c b/tests/tcg/hexagon/preg_alias.c
index b44a811..8798fbc 100644
--- a/tests/tcg/hexagon/preg_alias.c
+++ b/tests/tcg/hexagon/preg_alias.c
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -65,7 +65,7 @@
: "=r"(pregs->pregs.p0), "=r"(pregs->pregs.p1),
"=r"(pregs->pregs.p2), "=r"(pregs->pregs.p3)
: "r"(cval)
- : "p0", "p1", "p2", "p3");
+ : "c4", "p0", "p1", "p2", "p3");
}
int err;
@@ -92,7 +92,7 @@
: "=r"(pregs->pregs.p0), "=r"(pregs->pregs.p1),
"=r"(pregs->pregs.p2), "=r"(pregs->pregs.p3), "=r"(c5)
: "r"(cval_pair)
- : "p0", "p1", "p2", "p3");
+ : "c4", "c5", "p0", "p1", "p2", "p3");
check(c5, 0xdeadbeef);
}
@@ -117,7 +117,7 @@
"}\n\t"
: "+r"(result)
: "r"(0xffffffff), "r"(0xff00ffff), "r"(0x837ed653)
- : "p0", "p1", "p2", "p3");
+ : "c4", "p0", "p1", "p2", "p3");
check(result, old_val);
/* Test a predicated store */
@@ -129,7 +129,7 @@
"}\n\t"
:
: "r"(0), "r"(0xffffffff), "r"(&result)
- : "p0", "p1", "p2", "p3", "memory");
+ : "c4", "p0", "p1", "p2", "p3", "memory");
check(result, 0x0);
}
diff --git a/tests/tcg/hexagon/scatter_gather.c b/tests/tcg/hexagon/scatter_gather.c
index b93eb18..bf8b5e0 100644
--- a/tests/tcg/hexagon/scatter_gather.c
+++ b/tests/tcg/hexagon/scatter_gather.c
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -40,47 +40,6 @@
typedef long HVX_VectorPred __attribute__((__vector_size__(128)))
__attribute__((aligned(128)));
-#define VSCATTER_16(BASE, RGN, OFF, VALS) \
- __builtin_HEXAGON_V6_vscattermh_128B((int)BASE, RGN, OFF, VALS)
-#define VSCATTER_16_MASKED(MASK, BASE, RGN, OFF, VALS) \
- __builtin_HEXAGON_V6_vscattermhq_128B(MASK, (int)BASE, RGN, OFF, VALS)
-#define VSCATTER_32(BASE, RGN, OFF, VALS) \
- __builtin_HEXAGON_V6_vscattermw_128B((int)BASE, RGN, OFF, VALS)
-#define VSCATTER_32_MASKED(MASK, BASE, RGN, OFF, VALS) \
- __builtin_HEXAGON_V6_vscattermwq_128B(MASK, (int)BASE, RGN, OFF, VALS)
-#define VSCATTER_16_32(BASE, RGN, OFF, VALS) \
- __builtin_HEXAGON_V6_vscattermhw_128B((int)BASE, RGN, OFF, VALS)
-#define VSCATTER_16_32_MASKED(MASK, BASE, RGN, OFF, VALS) \
- __builtin_HEXAGON_V6_vscattermhwq_128B(MASK, (int)BASE, RGN, OFF, VALS)
-#define VSCATTER_16_ACC(BASE, RGN, OFF, VALS) \
- __builtin_HEXAGON_V6_vscattermh_add_128B((int)BASE, RGN, OFF, VALS)
-#define VSCATTER_32_ACC(BASE, RGN, OFF, VALS) \
- __builtin_HEXAGON_V6_vscattermw_add_128B((int)BASE, RGN, OFF, VALS)
-#define VSCATTER_16_32_ACC(BASE, RGN, OFF, VALS) \
- __builtin_HEXAGON_V6_vscattermhw_add_128B((int)BASE, RGN, OFF, VALS)
-
-#define VGATHER_16(DSTADDR, BASE, RGN, OFF) \
- __builtin_HEXAGON_V6_vgathermh_128B(DSTADDR, (int)BASE, RGN, OFF)
-#define VGATHER_16_MASKED(DSTADDR, MASK, BASE, RGN, OFF) \
- __builtin_HEXAGON_V6_vgathermhq_128B(DSTADDR, MASK, (int)BASE, RGN, OFF)
-#define VGATHER_32(DSTADDR, BASE, RGN, OFF) \
- __builtin_HEXAGON_V6_vgathermw_128B(DSTADDR, (int)BASE, RGN, OFF)
-#define VGATHER_32_MASKED(DSTADDR, MASK, BASE, RGN, OFF) \
- __builtin_HEXAGON_V6_vgathermwq_128B(DSTADDR, MASK, (int)BASE, RGN, OFF)
-#define VGATHER_16_32(DSTADDR, BASE, RGN, OFF) \
- __builtin_HEXAGON_V6_vgathermhw_128B(DSTADDR, (int)BASE, RGN, OFF)
-#define VGATHER_16_32_MASKED(DSTADDR, MASK, BASE, RGN, OFF) \
- __builtin_HEXAGON_V6_vgathermhwq_128B(DSTADDR, MASK, (int)BASE, RGN, OFF)
-
-#define VSHUFF_H(V) \
- __builtin_HEXAGON_V6_vshuffh_128B(V)
-#define VSPLAT_H(X) \
- __builtin_HEXAGON_V6_lvsplath_128B(X)
-#define VAND_VAL(PRED, VAL) \
- __builtin_HEXAGON_V6_vandvrt_128B(PRED, VAL)
-#define VDEAL_H(V) \
- __builtin_HEXAGON_V6_vdealh_128B(V)
-
int err;
/* define the number of rows/cols in a square matrix */
@@ -108,22 +67,22 @@
unsigned short vgather16_32_ref[MATRIX_SIZE];
/* declare the arrays of offsets */
-unsigned short half_offsets[MATRIX_SIZE];
-unsigned int word_offsets[MATRIX_SIZE];
+unsigned short half_offsets[MATRIX_SIZE] __attribute__((aligned(128)));
+unsigned int word_offsets[MATRIX_SIZE] __attribute__((aligned(128)));
/* declare the arrays of values */
-unsigned short half_values[MATRIX_SIZE];
-unsigned short half_values_acc[MATRIX_SIZE];
-unsigned short half_values_masked[MATRIX_SIZE];
-unsigned int word_values[MATRIX_SIZE];
-unsigned int word_values_acc[MATRIX_SIZE];
-unsigned int word_values_masked[MATRIX_SIZE];
+unsigned short half_values[MATRIX_SIZE] __attribute__((aligned(128)));
+unsigned short half_values_acc[MATRIX_SIZE] __attribute__((aligned(128)));
+unsigned short half_values_masked[MATRIX_SIZE] __attribute__((aligned(128)));
+unsigned int word_values[MATRIX_SIZE] __attribute__((aligned(128)));
+unsigned int word_values_acc[MATRIX_SIZE] __attribute__((aligned(128)));
+unsigned int word_values_masked[MATRIX_SIZE] __attribute__((aligned(128)));
/* declare the arrays of predicates */
-unsigned short half_predicates[MATRIX_SIZE];
-unsigned int word_predicates[MATRIX_SIZE];
+unsigned short half_predicates[MATRIX_SIZE] __attribute__((aligned(128)));
+unsigned int word_predicates[MATRIX_SIZE] __attribute__((aligned(128)));
-/* make this big enough for all the intrinsics */
+/* make this big enough for all the operations */
const size_t region_len = sizeof(vtcm);
/* optionally add sync instructions */
@@ -261,164 +220,201 @@
}
}
-/* scatter the 16 bit elements using intrinsics */
+/* scatter the 16 bit elements using HVX */
void vector_scatter_16(void)
{
- /* copy the offsets and values to vectors */
- HVX_Vector offsets = *(HVX_Vector *)half_offsets;
- HVX_Vector values = *(HVX_Vector *)half_values;
-
- VSCATTER_16(&vtcm.vscatter16, region_len, offsets, values);
+ asm ("m0 = %1\n\t"
+ "v0 = vmem(%2 + #0)\n\t"
+ "v1 = vmem(%3 + #0)\n\t"
+ "vscatter(%0, m0, v0.h).h = v1\n\t"
+ : : "r"(vtcm.vscatter16), "r"(region_len),
+ "r"(half_offsets), "r"(half_values)
+ : "m0", "v0", "v1", "memory");
sync_scatter(vtcm.vscatter16);
}
-/* scatter-accumulate the 16 bit elements using intrinsics */
+/* scatter-accumulate the 16 bit elements using HVX */
void vector_scatter_16_acc(void)
{
- /* copy the offsets and values to vectors */
- HVX_Vector offsets = *(HVX_Vector *)half_offsets;
- HVX_Vector values = *(HVX_Vector *)half_values_acc;
-
- VSCATTER_16_ACC(&vtcm.vscatter16, region_len, offsets, values);
+ asm ("m0 = %1\n\t"
+ "v0 = vmem(%2 + #0)\n\t"
+ "v1 = vmem(%3 + #0)\n\t"
+ "vscatter(%0, m0, v0.h).h += v1\n\t"
+ : : "r"(vtcm.vscatter16), "r"(region_len),
+ "r"(half_offsets), "r"(half_values_acc)
+ : "m0", "v0", "v1", "memory");
sync_scatter(vtcm.vscatter16);
}
-/* scatter the 16 bit elements using intrinsics */
+/* masked scatter the 16 bit elements using HVX */
void vector_scatter_16_masked(void)
{
- /* copy the offsets and values to vectors */
- HVX_Vector offsets = *(HVX_Vector *)half_offsets;
- HVX_Vector values = *(HVX_Vector *)half_values_masked;
- HVX_Vector pred_reg = *(HVX_Vector *)half_predicates;
- HVX_VectorPred preds = VAND_VAL(pred_reg, ~0);
-
- VSCATTER_16_MASKED(preds, &vtcm.vscatter16, region_len, offsets, values);
+ asm ("r1 = #-1\n\t"
+ "v0 = vmem(%0 + #0)\n\t"
+ "q0 = vand(v0, r1)\n\t"
+ "m0 = %2\n\t"
+ "v0 = vmem(%3 + #0)\n\t"
+ "v1 = vmem(%4 + #0)\n\t"
+ "if (q0) vscatter(%1, m0, v0.h).h = v1\n\t"
+ : : "r"(half_predicates), "r"(vtcm.vscatter16), "r"(region_len),
+ "r"(half_offsets), "r"(half_values_masked)
+ : "r1", "q0", "m0", "q0", "v0", "v1", "memory");
sync_scatter(vtcm.vscatter16);
}
-/* scatter the 32 bit elements using intrinsics */
+/* scatter the 32 bit elements using HVX */
void vector_scatter_32(void)
{
- /* copy the offsets and values to vectors */
- HVX_Vector offsetslo = *(HVX_Vector *)word_offsets;
- HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2];
- HVX_Vector valueslo = *(HVX_Vector *)word_values;
- HVX_Vector valueshi = *(HVX_Vector *)&word_values[MATRIX_SIZE / 2];
+ HVX_Vector *offsetslo = (HVX_Vector *)word_offsets;
+ HVX_Vector *offsetshi = (HVX_Vector *)&word_offsets[MATRIX_SIZE / 2];
+ HVX_Vector *valueslo = (HVX_Vector *)word_values;
+ HVX_Vector *valueshi = (HVX_Vector *)&word_values[MATRIX_SIZE / 2];
- VSCATTER_32(&vtcm.vscatter32, region_len, offsetslo, valueslo);
- VSCATTER_32(&vtcm.vscatter32, region_len, offsetshi, valueshi);
+ asm ("m0 = %1\n\t"
+ "v0 = vmem(%2 + #0)\n\t"
+ "v1 = vmem(%3 + #0)\n\t"
+ "vscatter(%0, m0, v0.w).w = v1\n\t"
+ : : "r"(vtcm.vscatter32), "r"(region_len),
+ "r"(offsetslo), "r"(valueslo)
+ : "m0", "v0", "v1", "memory");
+ asm ("m0 = %1\n\t"
+ "v0 = vmem(%2 + #0)\n\t"
+ "v1 = vmem(%3 + #0)\n\t"
+ "vscatter(%0, m0, v0.w).w = v1\n\t"
+ : : "r"(vtcm.vscatter32), "r"(region_len),
+ "r"(offsetshi), "r"(valueshi)
+ : "m0", "v0", "v1", "memory");
sync_scatter(vtcm.vscatter32);
}
-/* scatter-acc the 32 bit elements using intrinsics */
+/* scatter-accumulate the 32 bit elements using HVX */
void vector_scatter_32_acc(void)
{
- /* copy the offsets and values to vectors */
- HVX_Vector offsetslo = *(HVX_Vector *)word_offsets;
- HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2];
- HVX_Vector valueslo = *(HVX_Vector *)word_values_acc;
- HVX_Vector valueshi = *(HVX_Vector *)&word_values_acc[MATRIX_SIZE / 2];
+ HVX_Vector *offsetslo = (HVX_Vector *)word_offsets;
+ HVX_Vector *offsetshi = (HVX_Vector *)&word_offsets[MATRIX_SIZE / 2];
+ HVX_Vector *valueslo = (HVX_Vector *)word_values_acc;
+ HVX_Vector *valueshi = (HVX_Vector *)&word_values_acc[MATRIX_SIZE / 2];
- VSCATTER_32_ACC(&vtcm.vscatter32, region_len, offsetslo, valueslo);
- VSCATTER_32_ACC(&vtcm.vscatter32, region_len, offsetshi, valueshi);
+ asm ("m0 = %1\n\t"
+ "v0 = vmem(%2 + #0)\n\t"
+ "v1 = vmem(%3 + #0)\n\t"
+ "vscatter(%0, m0, v0.w).w += v1\n\t"
+ : : "r"(vtcm.vscatter32), "r"(region_len),
+ "r"(offsetslo), "r"(valueslo)
+ : "m0", "v0", "v1", "memory");
+ asm ("m0 = %1\n\t"
+ "v0 = vmem(%2 + #0)\n\t"
+ "v1 = vmem(%3 + #0)\n\t"
+ "vscatter(%0, m0, v0.w).w += v1\n\t"
+ : : "r"(vtcm.vscatter32), "r"(region_len),
+ "r"(offsetshi), "r"(valueshi)
+ : "m0", "v0", "v1", "memory");
sync_scatter(vtcm.vscatter32);
}
-/* scatter the 32 bit elements using intrinsics */
+/* masked scatter the 32 bit elements using HVX */
void vector_scatter_32_masked(void)
{
- /* copy the offsets and values to vectors */
- HVX_Vector offsetslo = *(HVX_Vector *)word_offsets;
- HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2];
- HVX_Vector valueslo = *(HVX_Vector *)word_values_masked;
- HVX_Vector valueshi = *(HVX_Vector *)&word_values_masked[MATRIX_SIZE / 2];
- HVX_Vector pred_reglo = *(HVX_Vector *)word_predicates;
- HVX_Vector pred_reghi = *(HVX_Vector *)&word_predicates[MATRIX_SIZE / 2];
- HVX_VectorPred predslo = VAND_VAL(pred_reglo, ~0);
- HVX_VectorPred predshi = VAND_VAL(pred_reghi, ~0);
+ HVX_Vector *offsetslo = (HVX_Vector *)word_offsets;
+ HVX_Vector *offsetshi = (HVX_Vector *)&word_offsets[MATRIX_SIZE / 2];
+ HVX_Vector *valueslo = (HVX_Vector *)word_values_masked;
+ HVX_Vector *valueshi = (HVX_Vector *)&word_values_masked[MATRIX_SIZE / 2];
+ HVX_Vector *predslo = (HVX_Vector *)word_predicates;
+ HVX_Vector *predshi = (HVX_Vector *)&word_predicates[MATRIX_SIZE / 2];
- VSCATTER_32_MASKED(predslo, &vtcm.vscatter32, region_len, offsetslo,
- valueslo);
- VSCATTER_32_MASKED(predshi, &vtcm.vscatter32, region_len, offsetshi,
- valueshi);
+ asm ("r1 = #-1\n\t"
+ "v0 = vmem(%0 + #0)\n\t"
+ "q0 = vand(v0, r1)\n\t"
+ "m0 = %2\n\t"
+ "v0 = vmem(%3 + #0)\n\t"
+ "v1 = vmem(%4 + #0)\n\t"
+ "if (q0) vscatter(%1, m0, v0.w).w = v1\n\t"
+ : : "r"(predslo), "r"(vtcm.vscatter32), "r"(region_len),
+ "r"(offsetslo), "r"(valueslo)
+ : "r1", "q0", "m0", "q0", "v0", "v1", "memory");
+ asm ("r1 = #-1\n\t"
+ "v0 = vmem(%0 + #0)\n\t"
+ "q0 = vand(v0, r1)\n\t"
+ "m0 = %2\n\t"
+ "v0 = vmem(%3 + #0)\n\t"
+ "v1 = vmem(%4 + #0)\n\t"
+ "if (q0) vscatter(%1, m0, v0.w).w = v1\n\t"
+ : : "r"(predshi), "r"(vtcm.vscatter32), "r"(region_len),
+ "r"(offsetshi), "r"(valueshi)
+ : "r1", "q0", "m0", "q0", "v0", "v1", "memory");
- sync_scatter(vtcm.vscatter16);
+ sync_scatter(vtcm.vscatter32);
}
-/* scatter the 16 bit elements with 32 bit offsets using intrinsics */
+/* scatter the 16 bit elements with 32 bit offsets using HVX */
void vector_scatter_16_32(void)
{
- HVX_VectorPair offsets;
- HVX_Vector values;
-
- /* get the word offsets in a vector pair */
- offsets = *(HVX_VectorPair *)word_offsets;
-
- /* these values need to be shuffled for the scatter */
- values = *(HVX_Vector *)half_values;
- values = VSHUFF_H(values);
-
- VSCATTER_16_32(&vtcm.vscatter16_32, region_len, offsets, values);
+ asm ("m0 = %1\n\t"
+ "v0 = vmem(%2 + #0)\n\t"
+ "v1 = vmem(%2 + #1)\n\t"
+ "v2 = vmem(%3 + #0)\n\t"
+ "v2.h = vshuff(v2.h)\n\t" /* shuffle the values for the scatter */
+ "vscatter(%0, m0, v1:0.w).h = v2\n\t"
+ : : "r"(vtcm.vscatter16_32), "r"(region_len),
+ "r"(word_offsets), "r"(half_values)
+ : "m0", "v0", "v1", "v2", "memory");
sync_scatter(vtcm.vscatter16_32);
}
-/* scatter-acc the 16 bit elements with 32 bit offsets using intrinsics */
+/* scatter-accumulate the 16 bit elements with 32 bit offsets using HVX */
void vector_scatter_16_32_acc(void)
{
- HVX_VectorPair offsets;
- HVX_Vector values;
-
- /* get the word offsets in a vector pair */
- offsets = *(HVX_VectorPair *)word_offsets;
-
- /* these values need to be shuffled for the scatter */
- values = *(HVX_Vector *)half_values_acc;
- values = VSHUFF_H(values);
-
- VSCATTER_16_32_ACC(&vtcm.vscatter16_32, region_len, offsets, values);
+ asm ("m0 = %1\n\t"
+ "v0 = vmem(%2 + #0)\n\t"
+ "v1 = vmem(%2 + #1)\n\t"
+ "v2 = vmem(%3 + #0)\n\t" \
+ "v2.h = vshuff(v2.h)\n\t" /* shuffle the values for the scatter */
+ "vscatter(%0, m0, v1:0.w).h += v2\n\t"
+ : : "r"(vtcm.vscatter16_32), "r"(region_len),
+ "r"(word_offsets), "r"(half_values_acc)
+ : "m0", "v0", "v1", "v2", "memory");
sync_scatter(vtcm.vscatter16_32);
}
-/* masked scatter the 16 bit elements with 32 bit offsets using intrinsics */
+/* masked scatter the 16 bit elements with 32 bit offsets using HVX */
void vector_scatter_16_32_masked(void)
{
- HVX_VectorPair offsets;
- HVX_Vector values;
- HVX_Vector pred_reg;
-
- /* get the word offsets in a vector pair */
- offsets = *(HVX_VectorPair *)word_offsets;
-
- /* these values need to be shuffled for the scatter */
- values = *(HVX_Vector *)half_values_masked;
- values = VSHUFF_H(values);
-
- pred_reg = *(HVX_Vector *)half_predicates;
- pred_reg = VSHUFF_H(pred_reg);
- HVX_VectorPred preds = VAND_VAL(pred_reg, ~0);
-
- VSCATTER_16_32_MASKED(preds, &vtcm.vscatter16_32, region_len, offsets,
- values);
+ asm ("r1 = #-1\n\t"
+ "v0 = vmem(%0 + #0)\n\t"
+ "v0.h = vshuff(v0.h)\n\t" /* shuffle the predicates */
+ "q0 = vand(v0, r1)\n\t"
+ "m0 = %2\n\t"
+ "v0 = vmem(%3 + #0)\n\t"
+ "v1 = vmem(%3 + #1)\n\t"
+ "v2 = vmem(%4 + #0)\n\t" \
+ "v2.h = vshuff(v2.h)\n\t" /* shuffle the values for the scatter */
+ "if (q0) vscatter(%1, m0, v1:0.w).h = v2\n\t"
+ : : "r"(half_predicates), "r"(vtcm.vscatter16_32), "r"(region_len),
+ "r"(word_offsets), "r"(half_values_masked)
+ : "r1", "q0", "m0", "v0", "v1", "v2", "memory");
sync_scatter(vtcm.vscatter16_32);
}
-/* gather the elements from the scatter16 buffer */
+/* gather the elements from the scatter16 buffer using HVX */
void vector_gather_16(void)
{
- HVX_Vector *vgather = (HVX_Vector *)&vtcm.vgather16;
- HVX_Vector offsets = *(HVX_Vector *)half_offsets;
+ asm ("m0 = %1\n\t"
+ "v0 = vmem(%2 + #0)\n\t"
+ "{ vtmp.h = vgather(%0, m0, v0.h).h\n\t"
+ " vmem(%3 + #0) = vtmp.new }\n\t"
+ : : "r"(vtcm.vscatter16), "r"(region_len),
+ "r"(half_offsets), "r"(vtcm.vgather16)
+ : "m0", "v0", "memory");
- VGATHER_16(vgather, &vtcm.vscatter16, region_len, offsets);
-
- sync_gather(vgather);
+ sync_gather(vtcm.vgather16);
}
static unsigned short gather_16_masked_init(void)
@@ -427,31 +423,51 @@
return letter | (letter << 8);
}
+/* masked gather the elements from the scatter16 buffer using HVX */
void vector_gather_16_masked(void)
{
- HVX_Vector *vgather = (HVX_Vector *)&vtcm.vgather16;
- HVX_Vector offsets = *(HVX_Vector *)half_offsets;
- HVX_Vector pred_reg = *(HVX_Vector *)half_predicates;
- HVX_VectorPred preds = VAND_VAL(pred_reg, ~0);
+ unsigned short init = gather_16_masked_init();
- *vgather = VSPLAT_H(gather_16_masked_init());
- VGATHER_16_MASKED(vgather, preds, &vtcm.vscatter16, region_len, offsets);
+ asm ("v0.h = vsplat(%5)\n\t"
+ "vmem(%4 + #0) = v0\n\t" /* initialize the write area */
+ "r1 = #-1\n\t"
+ "v0 = vmem(%0 + #0)\n\t"
+ "q0 = vand(v0, r1)\n\t"
+ "m0 = %2\n\t"
+ "v0 = vmem(%3 + #0)\n\t"
+ "{ if (q0) vtmp.h = vgather(%1, m0, v0.h).h\n\t"
+ " vmem(%4 + #0) = vtmp.new }\n\t"
+ : : "r"(half_predicates), "r"(vtcm.vscatter16), "r"(region_len),
+ "r"(half_offsets), "r"(vtcm.vgather16), "r"(init)
+ : "r1", "q0", "m0", "v0", "memory");
- sync_gather(vgather);
+ sync_gather(vtcm.vgather16);
}
-/* gather the elements from the scatter32 buffer */
+/* gather the elements from the scatter32 buffer using HVX */
void vector_gather_32(void)
{
- HVX_Vector *vgatherlo = (HVX_Vector *)&vtcm.vgather32;
- HVX_Vector *vgatherhi =
- (HVX_Vector *)((int)&vtcm.vgather32 + (MATRIX_SIZE * 2));
- HVX_Vector offsetslo = *(HVX_Vector *)word_offsets;
- HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2];
+ HVX_Vector *vgatherlo = (HVX_Vector *)vtcm.vgather32;
+ HVX_Vector *vgatherhi = (HVX_Vector *)&vtcm.vgather32[MATRIX_SIZE / 2];
+ HVX_Vector *offsetslo = (HVX_Vector *)word_offsets;
+ HVX_Vector *offsetshi = (HVX_Vector *)&word_offsets[MATRIX_SIZE / 2];
- VGATHER_32(vgatherlo, &vtcm.vscatter32, region_len, offsetslo);
- VGATHER_32(vgatherhi, &vtcm.vscatter32, region_len, offsetshi);
+ asm ("m0 = %1\n\t"
+ "v0 = vmem(%2 + #0)\n\t"
+ "{ vtmp.w = vgather(%0, m0, v0.w).w\n\t"
+ " vmem(%3 + #0) = vtmp.new }\n\t"
+ : : "r"(vtcm.vscatter32), "r"(region_len),
+ "r"(offsetslo), "r"(vgatherlo)
+ : "m0", "v0", "memory");
+ asm ("m0 = %1\n\t"
+ "v0 = vmem(%2 + #0)\n\t"
+ "{ vtmp.w = vgather(%0, m0, v0.w).w\n\t"
+ " vmem(%3 + #0) = vtmp.new }\n\t"
+ : : "r"(vtcm.vscatter32), "r"(region_len),
+ "r"(offsetshi), "r"(vgatherhi)
+ : "m0", "v0", "memory");
+ sync_gather(vgatherlo);
sync_gather(vgatherhi);
}
@@ -461,79 +477,88 @@
return letter | (letter << 8) | (letter << 16) | (letter << 24);
}
+/* masked gather the elements from the scatter32 buffer using HVX */
void vector_gather_32_masked(void)
{
- HVX_Vector *vgatherlo = (HVX_Vector *)&vtcm.vgather32;
- HVX_Vector *vgatherhi =
- (HVX_Vector *)((int)&vtcm.vgather32 + (MATRIX_SIZE * 2));
- HVX_Vector offsetslo = *(HVX_Vector *)word_offsets;
- HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2];
- HVX_Vector pred_reglo = *(HVX_Vector *)word_predicates;
- HVX_VectorPred predslo = VAND_VAL(pred_reglo, ~0);
- HVX_Vector pred_reghi = *(HVX_Vector *)&word_predicates[MATRIX_SIZE / 2];
- HVX_VectorPred predshi = VAND_VAL(pred_reghi, ~0);
+ unsigned int init = gather_32_masked_init();
+ HVX_Vector *vgatherlo = (HVX_Vector *)vtcm.vgather32;
+ HVX_Vector *vgatherhi = (HVX_Vector *)&vtcm.vgather32[MATRIX_SIZE / 2];
+ HVX_Vector *offsetslo = (HVX_Vector *)word_offsets;
+ HVX_Vector *offsetshi = (HVX_Vector *)&word_offsets[MATRIX_SIZE / 2];
+ HVX_Vector *predslo = (HVX_Vector *)word_predicates;
+ HVX_Vector *predshi = (HVX_Vector *)&word_predicates[MATRIX_SIZE / 2];
- *vgatherlo = VSPLAT_H(gather_32_masked_init());
- *vgatherhi = VSPLAT_H(gather_32_masked_init());
- VGATHER_32_MASKED(vgatherlo, predslo, &vtcm.vscatter32, region_len,
- offsetslo);
- VGATHER_32_MASKED(vgatherhi, predshi, &vtcm.vscatter32, region_len,
- offsetshi);
+ asm ("v0.h = vsplat(%5)\n\t"
+ "vmem(%4 + #0) = v0\n\t" /* initialize the write area */
+ "r1 = #-1\n\t"
+ "v0 = vmem(%0 + #0)\n\t"
+ "q0 = vand(v0, r1)\n\t"
+ "m0 = %2\n\t"
+ "v0 = vmem(%3 + #0)\n\t"
+ "{ if (q0) vtmp.w = vgather(%1, m0, v0.w).w\n\t"
+ " vmem(%4 + #0) = vtmp.new }\n\t"
+ : : "r"(predslo), "r"(vtcm.vscatter32), "r"(region_len),
+ "r"(offsetslo), "r"(vgatherlo), "r"(init)
+ : "r1", "q0", "m0", "v0", "memory");
+ asm ("v0.h = vsplat(%5)\n\t"
+ "vmem(%4 + #0) = v0\n\t" /* initialize the write area */
+ "r1 = #-1\n\t"
+ "v0 = vmem(%0 + #0)\n\t"
+ "q0 = vand(v0, r1)\n\t"
+ "m0 = %2\n\t"
+ "v0 = vmem(%3 + #0)\n\t"
+ "{ if (q0) vtmp.w = vgather(%1, m0, v0.w).w\n\t"
+ " vmem(%4 + #0) = vtmp.new }\n\t"
+ : : "r"(predshi), "r"(vtcm.vscatter32), "r"(region_len),
+ "r"(offsetshi), "r"(vgatherhi), "r"(init)
+ : "r1", "q0", "m0", "v0", "memory");
sync_gather(vgatherlo);
sync_gather(vgatherhi);
}
-/* gather the elements from the scatter16_32 buffer */
+/* gather the elements from the scatter16_32 buffer using HVX */
void vector_gather_16_32(void)
{
- HVX_Vector *vgather;
- HVX_VectorPair offsets;
- HVX_Vector values;
+ asm ("m0 = %1\n\t"
+ "v0 = vmem(%2 + #0)\n\t"
+ "v1 = vmem(%2 + #1)\n\t"
+ "{ vtmp.h = vgather(%0, m0, v1:0.w).h\n\t"
+ " vmem(%3 + #0) = vtmp.new }\n\t"
+ "v0 = vmem(%3 + #0)\n\t"
+ "v0.h = vdeal(v0.h)\n\t" /* deal the elements to get the order back */
+ "vmem(%3 + #0) = v0\n\t"
+ : : "r"(vtcm.vscatter16_32), "r"(region_len),
+ "r"(word_offsets), "r"(vtcm.vgather16_32)
+ : "m0", "v0", "v1", "memory");
- /* get the vtcm address to gather from */
- vgather = (HVX_Vector *)&vtcm.vgather16_32;
-
- /* get the word offsets in a vector pair */
- offsets = *(HVX_VectorPair *)word_offsets;
-
- VGATHER_16_32(vgather, &vtcm.vscatter16_32, region_len, offsets);
-
- /* deal the elements to get the order back */
- values = *(HVX_Vector *)vgather;
- values = VDEAL_H(values);
-
- /* write it back to vtcm address */
- *(HVX_Vector *)vgather = values;
+ sync_gather(vtcm.vgather16_32);
}
+/* masked gather the elements from the scatter16_32 buffer using HVX */
void vector_gather_16_32_masked(void)
{
- HVX_Vector *vgather;
- HVX_VectorPair offsets;
- HVX_Vector pred_reg;
- HVX_VectorPred preds;
- HVX_Vector values;
+ unsigned short init = gather_16_masked_init();
- /* get the vtcm address to gather from */
- vgather = (HVX_Vector *)&vtcm.vgather16_32;
+ asm ("v0.h = vsplat(%5)\n\t"
+ "vmem(%4 + #0) = v0\n\t" /* initialize the write area */
+ "r1 = #-1\n\t"
+ "v0 = vmem(%0 + #0)\n\t"
+ "v0.h = vshuff(v0.h)\n\t" /* shuffle the predicates */
+ "q0 = vand(v0, r1)\n\t"
+ "m0 = %2\n\t"
+ "v0 = vmem(%3 + #0)\n\t"
+ "v1 = vmem(%3 + #1)\n\t"
+ "{ if (q0) vtmp.h = vgather(%1, m0, v1:0.w).h\n\t"
+ " vmem(%4 + #0) = vtmp.new }\n\t"
+ "v0 = vmem(%4 + #0)\n\t"
+ "v0.h = vdeal(v0.h)\n\t" /* deal the elements to get the order back */
+ "vmem(%4 + #0) = v0\n\t"
+ : : "r"(half_predicates), "r"(vtcm.vscatter16_32), "r"(region_len),
+ "r"(word_offsets), "r"(vtcm.vgather16_32), "r"(init)
+ : "r1", "q0", "m0", "v0", "v1", "memory");
- /* get the word offsets in a vector pair */
- offsets = *(HVX_VectorPair *)word_offsets;
- pred_reg = *(HVX_Vector *)half_predicates;
- pred_reg = VSHUFF_H(pred_reg);
- preds = VAND_VAL(pred_reg, ~0);
-
- *vgather = VSPLAT_H(gather_16_masked_init());
- VGATHER_16_32_MASKED(vgather, preds, &vtcm.vscatter16_32, region_len,
- offsets);
-
- /* deal the elements to get the order back */
- values = *(HVX_Vector *)vgather;
- values = VDEAL_H(values);
-
- /* write it back to vtcm address */
- *(HVX_Vector *)vgather = values;
+ sync_gather(vtcm.vgather16_32);
}
static void check_buffer(const char *name, void *c, void *r, size_t size)
@@ -579,6 +604,7 @@
}
}
+/* scatter-accumulate the 16 bit elements using C */
void check_scatter_16_acc()
{
memset(vscatter16_ref, FILL_CHAR,
@@ -589,7 +615,7 @@
SCATTER_BUFFER_SIZE * sizeof(unsigned short));
}
-/* scatter the 16 bit elements using C */
+/* masked scatter the 16 bit elements using C */
void scalar_scatter_16_masked(unsigned short *vscatter16)
{
for (int i = 0; i < MATRIX_SIZE; i++) {
@@ -628,7 +654,7 @@
SCATTER_BUFFER_SIZE * sizeof(unsigned int));
}
-/* scatter the 32 bit elements using C */
+/* scatter-accumulate the 32 bit elements using C */
void scalar_scatter_32_acc(unsigned int *vscatter32)
{
for (int i = 0; i < MATRIX_SIZE; ++i) {
@@ -646,7 +672,7 @@
SCATTER_BUFFER_SIZE * sizeof(unsigned int));
}
-/* scatter the 32 bit elements using C */
+/* masked scatter the 32 bit elements using C */
void scalar_scatter_32_masked(unsigned int *vscatter32)
{
for (int i = 0; i < MATRIX_SIZE; i++) {
@@ -667,7 +693,7 @@
SCATTER_BUFFER_SIZE * sizeof(unsigned int));
}
-/* scatter the 32 bit elements using C */
+/* scatter the 16 bit elements with 32 bit offsets using C */
void scalar_scatter_16_32(unsigned short *vscatter16_32)
{
for (int i = 0; i < MATRIX_SIZE; ++i) {
@@ -684,7 +710,7 @@
SCATTER_BUFFER_SIZE * sizeof(unsigned short));
}
-/* scatter the 32 bit elements using C */
+/* scatter-accumulate the 16 bit elements with 32 bit offsets using C */
void scalar_scatter_16_32_acc(unsigned short *vscatter16_32)
{
for (int i = 0; i < MATRIX_SIZE; ++i) {
@@ -702,6 +728,7 @@
SCATTER_BUFFER_SIZE * sizeof(unsigned short));
}
+/* masked scatter the 16 bit elements with 32 bit offsets using C */
void scalar_scatter_16_32_masked(unsigned short *vscatter16_32)
{
for (int i = 0; i < MATRIX_SIZE; i++) {
@@ -738,6 +765,7 @@
MATRIX_SIZE * sizeof(unsigned short));
}
+/* masked gather the elements from the scatter buffer using C */
void scalar_gather_16_masked(unsigned short *vgather16)
{
for (int i = 0; i < MATRIX_SIZE; ++i) {
@@ -756,7 +784,7 @@
MATRIX_SIZE * sizeof(unsigned short));
}
-/* gather the elements from the scatter buffer using C */
+/* gather the elements from the scatter32 buffer using C */
void scalar_gather_32(unsigned int *vgather32)
{
for (int i = 0; i < MATRIX_SIZE; ++i) {
@@ -772,6 +800,7 @@
MATRIX_SIZE * sizeof(unsigned int));
}
+/* masked gather the elements from the scatter32 buffer using C */
void scalar_gather_32_masked(unsigned int *vgather32)
{
for (int i = 0; i < MATRIX_SIZE; ++i) {
@@ -781,7 +810,6 @@
}
}
-
void check_gather_32_masked(void)
{
memset(vgather32_ref, gather_32_masked_init(),
@@ -791,7 +819,7 @@
vgather32_ref, MATRIX_SIZE * sizeof(unsigned int));
}
-/* gather the elements from the scatter buffer using C */
+/* gather the elements from the scatter16_32 buffer using C */
void scalar_gather_16_32(unsigned short *vgather16_32)
{
for (int i = 0; i < MATRIX_SIZE; ++i) {
@@ -807,6 +835,7 @@
MATRIX_SIZE * sizeof(unsigned short));
}
+/* masked gather the elements from the scatter16_32 buffer using C */
void scalar_gather_16_32_masked(unsigned short *vgather16_32)
{
for (int i = 0; i < MATRIX_SIZE; ++i) {
diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target
index ae8b3d7..373db69 100644
--- a/tests/tcg/multiarch/Makefile.target
+++ b/tests/tcg/multiarch/Makefile.target
@@ -64,6 +64,7 @@
$(call run-test, test-mmap-$*, $(QEMU) -p $* $<, $< ($* byte pages))
ifneq ($(HAVE_GDB_BIN),)
+ifeq ($(HOST_GDB_SUPPORTS_ARCH),y)
GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py
run-gdbstub-sha1: sha1
@@ -89,6 +90,10 @@
else
run-gdbstub-%:
+ $(call skip-test, "gdbstub test $*", "no guest arch support")
+endif
+else
+run-gdbstub-%:
$(call skip-test, "gdbstub test $*", "need working gdb")
endif
EXTRA_RUNS += run-gdbstub-sha1 run-gdbstub-qxfer-auxv-read \
diff --git a/tests/tcg/multiarch/system/Makefile.softmmu-target b/tests/tcg/multiarch/system/Makefile.softmmu-target
index 368b64d..5f432c9 100644
--- a/tests/tcg/multiarch/system/Makefile.softmmu-target
+++ b/tests/tcg/multiarch/system/Makefile.softmmu-target
@@ -15,6 +15,7 @@
MULTIARCH_TESTS = $(patsubst $(MULTIARCH_SYSTEM_SRC)/%.c, %, $(MULTIARCH_TEST_SRCS))
ifneq ($(HAVE_GDB_BIN),)
+ifeq ($(HOST_GDB_SUPPORTS_ARCH),y)
GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py
run-gdbstub-memory: memory
@@ -26,7 +27,10 @@
"-monitor none -display none -chardev file$(COMMA)path=$<.out$(COMMA)id=output $(QEMU_OPTS)" \
--bin $< --test $(MULTIARCH_SRC)/gdbstub/memory.py, \
softmmu gdbstub support)
-
+else
+run-gdbstub-%:
+ $(call skip-test, "gdbstub test $*", "no guest arch support")
+endif
else
run-gdbstub-%:
$(call skip-test, "gdbstub test $*", "need working gdb")
diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target
index 72ad309..b7f576f 100644
--- a/tests/tcg/s390x/Makefile.target
+++ b/tests/tcg/s390x/Makefile.target
@@ -51,7 +51,7 @@
TESTS+=$(Z15_TESTS)
endif
-ifneq ($(HAVE_GDB_BIN),)
+ifeq ($(HOST_GDB_SUPPORTS_ARCH),y)
GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py
run-gdbstub-signals-s390x: signals-s390x
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index 51f453e..d9c0a7e 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -47,6 +47,7 @@
'ptimer-test': ['ptimer-test-stubs.c', meson.project_source_root() / 'hw/core/ptimer.c'],
'test-qapi-util': [],
'test-interval-tree': [],
+ 'test-xs-node': [qom],
}
if have_system or have_tools
diff --git a/tests/unit/test-xs-node.c b/tests/unit/test-xs-node.c
new file mode 100644
index 0000000..b80d10f
--- /dev/null
+++ b/tests/unit/test-xs-node.c
@@ -0,0 +1,871 @@
+/*
+ * QEMU XenStore XsNode testing
+ *
+ * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+
+static int nr_xs_nodes;
+static GList *xs_node_list;
+
+#define XS_NODE_UNIT_TEST
+
+/*
+ * We don't need the core Xen definitions. And we *do* want to be able
+ * to run the unit tests even on architectures that Xen doesn't support
+ * (because life's too short to bother doing otherwise, and test coverage
+ * doesn't hurt).
+ */
+#define __XEN_PUBLIC_XEN_H__
+typedef unsigned long xen_pfn_t;
+
+#include "hw/i386/kvm/xenstore_impl.c"
+
+#define DOMID_QEMU 0
+#define DOMID_GUEST 1
+
+static void dump_ref(const char *name, XsNode *n, int indent)
+{
+ int i;
+
+ if (!indent && name) {
+ printf("%s:\n", name);
+ }
+
+ for (i = 0; i < indent; i++) {
+ printf(" ");
+ }
+
+ printf("->%p(%d, '%s'): '%.*s'%s%s\n", n, n->ref, n->name,
+ (int)(n->content ? n->content->len : strlen("<empty>")),
+ n->content ? (char *)n->content->data : "<empty>",
+ n->modified_in_tx ? " MODIFIED" : "",
+ n->deleted_in_tx ? " DELETED" : "");
+
+ if (n->children) {
+ g_hash_table_foreach(n->children, (void *)dump_ref,
+ GINT_TO_POINTER(indent + 2));
+ }
+}
+
+/* This doesn't happen in qemu but we want to make valgrind happy */
+static void xs_impl_delete(XenstoreImplState *s, bool last)
+{
+ int err;
+
+ xs_impl_reset_watches(s, DOMID_GUEST);
+ g_assert(!s->nr_domu_watches);
+
+ err = xs_impl_rm(s, DOMID_QEMU, XBT_NULL, "/local");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 1);
+
+ g_hash_table_unref(s->watches);
+ g_hash_table_unref(s->transactions);
+ xs_node_unref(s->root);
+ g_free(s);
+
+ if (!last) {
+ return;
+ }
+
+ if (xs_node_list) {
+ GList *l;
+ for (l = xs_node_list; l; l = l->next) {
+ XsNode *n = l->data;
+ printf("Remaining node at %p name %s ref %u\n", n, n->name,
+ n->ref);
+ }
+ }
+ g_assert(!nr_xs_nodes);
+}
+
+struct compare_walk {
+ char path[XENSTORE_ABS_PATH_MAX + 1];
+ XsNode *parent_2;
+ bool compare_ok;
+};
+
+
+static bool compare_perms(GList *p1, GList *p2)
+{
+ while (p1) {
+ if (!p2 || g_strcmp0(p1->data, p2->data)) {
+ return false;
+ }
+ p1 = p1->next;
+ p2 = p2->next;
+ }
+ return (p2 == NULL);
+}
+
+static bool compare_content(GByteArray *c1, GByteArray *c2)
+{
+ size_t len1 = 0, len2 = 0;
+
+ if (c1) {
+ len1 = c1->len;
+ }
+ if (c2) {
+ len2 = c2->len;
+ }
+ if (len1 != len2) {
+ return false;
+ }
+
+ if (!len1) {
+ return true;
+ }
+
+ return !memcmp(c1->data, c2->data, len1);
+}
+
+static void compare_child(gpointer, gpointer, gpointer);
+
+static void compare_nodes(struct compare_walk *cw, XsNode *n1, XsNode *n2)
+{
+ int nr_children1 = 0, nr_children2 = 0;
+
+ if (n1->children) {
+ nr_children1 = g_hash_table_size(n1->children);
+ }
+ if (n2->children) {
+ nr_children2 = g_hash_table_size(n2->children);
+ }
+
+ if (n1->ref != n2->ref ||
+ n1->deleted_in_tx != n2->deleted_in_tx ||
+ n1->modified_in_tx != n2->modified_in_tx ||
+ !compare_perms(n1->perms, n2->perms) ||
+ !compare_content(n1->content, n2->content) ||
+ nr_children1 != nr_children2) {
+ cw->compare_ok = false;
+ printf("Compare failure on '%s'\n", cw->path);
+ }
+
+ if (nr_children1) {
+ XsNode *oldparent = cw->parent_2;
+ cw->parent_2 = n2;
+ g_hash_table_foreach(n1->children, compare_child, cw);
+
+ cw->parent_2 = oldparent;
+ }
+}
+
+static void compare_child(gpointer key, gpointer val, gpointer opaque)
+{
+ struct compare_walk *cw = opaque;
+ char *childname = key;
+ XsNode *child1 = val;
+ XsNode *child2 = g_hash_table_lookup(cw->parent_2->children, childname);
+ int pathlen = strlen(cw->path);
+
+ if (!child2) {
+ cw->compare_ok = false;
+ printf("Child '%s' does not exist under '%s'\n", childname, cw->path);
+ return;
+ }
+
+ strncat(cw->path, "/", sizeof(cw->path) - 1);
+ strncat(cw->path, childname, sizeof(cw->path) - 1);
+
+ compare_nodes(cw, child1, child2);
+ cw->path[pathlen] = '\0';
+}
+
+static bool compare_trees(XsNode *n1, XsNode *n2)
+{
+ struct compare_walk cw;
+
+ cw.path[0] = '\0';
+ cw.parent_2 = n2;
+ cw.compare_ok = true;
+
+ if (!n1 || !n2) {
+ return false;
+ }
+
+ compare_nodes(&cw, n1, n2);
+ return cw.compare_ok;
+}
+
+static void compare_tx(gpointer key, gpointer val, gpointer opaque)
+{
+ XenstoreImplState *s2 = opaque;
+ XsTransaction *t1 = val, *t2;
+ unsigned int tx_id = GPOINTER_TO_INT(key);
+
+ t2 = g_hash_table_lookup(s2->transactions, key);
+ g_assert(t2);
+
+ g_assert(t1->tx_id == tx_id);
+ g_assert(t2->tx_id == tx_id);
+ g_assert(t1->base_tx == t2->base_tx);
+ g_assert(t1->dom_id == t2->dom_id);
+ if (!compare_trees(t1->root, t2->root)) {
+ printf("Comparison failure in TX %u after serdes:\n", tx_id);
+ dump_ref("Original", t1->root, 0);
+ dump_ref("Deserialised", t2->root, 0);
+ g_assert(0);
+ }
+ g_assert(t1->nr_nodes == t2->nr_nodes);
+}
+
+static int write_str(XenstoreImplState *s, unsigned int dom_id,
+ unsigned int tx_id, const char *path,
+ const char *content)
+{
+ GByteArray *d = g_byte_array_new();
+ int err;
+
+ g_byte_array_append(d, (void *)content, strlen(content));
+ err = xs_impl_write(s, dom_id, tx_id, path, d);
+ g_byte_array_unref(d);
+ return err;
+}
+
+static void watch_cb(void *_str, const char *path, const char *token)
+{
+ GString *str = _str;
+
+ g_string_append(str, path);
+ g_string_append(str, token);
+}
+
+static void check_serdes(XenstoreImplState *s)
+{
+ XenstoreImplState *s2 = xs_impl_create(DOMID_GUEST);
+ GByteArray *bytes = xs_impl_serialize(s);
+ int nr_transactions1, nr_transactions2;
+ int ret;
+
+ ret = xs_impl_deserialize(s2, bytes, DOMID_GUEST, watch_cb, NULL);
+ g_assert(!ret);
+
+ g_byte_array_unref(bytes);
+
+ g_assert(s->last_tx == s2->last_tx);
+ g_assert(s->root_tx == s2->root_tx);
+
+ if (!compare_trees(s->root, s2->root)) {
+ printf("Comparison failure in main tree after serdes:\n");
+ dump_ref("Original", s->root, 0);
+ dump_ref("Deserialised", s2->root, 0);
+ g_assert(0);
+ }
+
+ nr_transactions1 = g_hash_table_size(s->transactions);
+ nr_transactions2 = g_hash_table_size(s2->transactions);
+ g_assert(nr_transactions1 == nr_transactions2);
+
+ g_hash_table_foreach(s->transactions, compare_tx, s2);
+
+ g_assert(s->nr_domu_watches == s2->nr_domu_watches);
+ g_assert(s->nr_domu_transactions == s2->nr_domu_transactions);
+ g_assert(s->nr_nodes == s2->nr_nodes);
+ xs_impl_delete(s2, false);
+}
+
+static XenstoreImplState *setup(void)
+{
+ XenstoreImplState *s = xs_impl_create(DOMID_GUEST);
+ char *abspath;
+ GList *perms;
+ int err;
+
+ abspath = g_strdup_printf("/local/domain/%u", DOMID_GUEST);
+
+ err = write_str(s, DOMID_QEMU, XBT_NULL, abspath, "");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 4);
+
+ perms = g_list_append(NULL, g_strdup_printf("n%u", DOMID_QEMU));
+ perms = g_list_append(perms, g_strdup_printf("r%u", DOMID_GUEST));
+
+ err = xs_impl_set_perms(s, DOMID_QEMU, XBT_NULL, abspath, perms);
+ g_assert(!err);
+
+ g_list_free_full(perms, g_free);
+ g_free(abspath);
+
+ abspath = g_strdup_printf("/local/domain/%u/some", DOMID_GUEST);
+
+ err = write_str(s, DOMID_QEMU, XBT_NULL, abspath, "");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 5);
+
+ perms = g_list_append(NULL, g_strdup_printf("n%u", DOMID_GUEST));
+
+ err = xs_impl_set_perms(s, DOMID_QEMU, XBT_NULL, abspath, perms);
+ g_assert(!err);
+
+ g_list_free_full(perms, g_free);
+ g_free(abspath);
+
+ return s;
+}
+
+static void test_xs_node_simple(void)
+{
+ GByteArray *data = g_byte_array_new();
+ XenstoreImplState *s = setup();
+ GString *guest_watches = g_string_new(NULL);
+ GString *qemu_watches = g_string_new(NULL);
+ GList *items = NULL;
+ XsNode *old_root;
+ uint64_t gencnt;
+ int err;
+
+ g_assert(s);
+
+ err = xs_impl_watch(s, DOMID_GUEST, "some", "guestwatch",
+ watch_cb, guest_watches);
+ g_assert(!err);
+ g_assert(guest_watches->len == strlen("someguestwatch"));
+ g_assert(!strcmp(guest_watches->str, "someguestwatch"));
+ g_string_truncate(guest_watches, 0);
+
+ err = xs_impl_watch(s, 0, "/local/domain/1/some", "qemuwatch",
+ watch_cb, qemu_watches);
+ g_assert(!err);
+ g_assert(qemu_watches->len == strlen("/local/domain/1/someqemuwatch"));
+ g_assert(!strcmp(qemu_watches->str, "/local/domain/1/someqemuwatch"));
+ g_string_truncate(qemu_watches, 0);
+
+ /* Read gives ENOENT when it should */
+ err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "foo", data);
+ g_assert(err == ENOENT);
+
+ /* Write works */
+ err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path",
+ "something");
+ g_assert(s->nr_nodes == 7);
+ g_assert(!err);
+ g_assert(!strcmp(guest_watches->str,
+ "some/relative/pathguestwatch"));
+ g_assert(!strcmp(qemu_watches->str,
+ "/local/domain/1/some/relative/pathqemuwatch"));
+
+ g_string_truncate(qemu_watches, 0);
+ g_string_truncate(guest_watches, 0);
+ xs_impl_reset_watches(s, 0);
+
+ /* Read gives back what we wrote */
+ err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative/path", data);
+ g_assert(!err);
+ g_assert(data->len == strlen("something"));
+ g_assert(!memcmp(data->data, "something", data->len));
+
+ /* Even if we use an abolute path */
+ g_byte_array_set_size(data, 0);
+ err = xs_impl_read(s, DOMID_GUEST, XBT_NULL,
+ "/local/domain/1/some/relative/path", data);
+ g_assert(!err);
+ g_assert(data->len == strlen("something"));
+
+ g_assert(!qemu_watches->len);
+ g_assert(!guest_watches->len);
+ /* Keep a copy, to force COW mode */
+ old_root = xs_node_ref(s->root);
+
+ /* Write somewhere we aren't allowed, in COW mode */
+ err = write_str(s, DOMID_GUEST, XBT_NULL, "/local/domain/badplace",
+ "moredata");
+ g_assert(err == EACCES);
+ g_assert(s->nr_nodes == 7);
+
+ /* Write works again */
+ err = write_str(s, DOMID_GUEST, XBT_NULL,
+ "/local/domain/1/some/relative/path2",
+ "something else");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 8);
+ g_assert(!qemu_watches->len);
+ g_assert(!strcmp(guest_watches->str, "some/relative/path2guestwatch"));
+ g_string_truncate(guest_watches, 0);
+
+ /* Overwrite an existing node */
+ err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path",
+ "another thing");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 8);
+ g_assert(!qemu_watches->len);
+ g_assert(!strcmp(guest_watches->str, "some/relative/pathguestwatch"));
+ g_string_truncate(guest_watches, 0);
+
+ /* We can list the two files we wrote */
+ err = xs_impl_directory(s, DOMID_GUEST, XBT_NULL, "some/relative", &gencnt,
+ &items);
+ g_assert(!err);
+ g_assert(items);
+ g_assert(gencnt == 2);
+ g_assert(!strcmp(items->data, "path"));
+ g_assert(items->next);
+ g_assert(!strcmp(items->next->data, "path2"));
+ g_assert(!items->next->next);
+ g_list_free_full(items, g_free);
+
+ err = xs_impl_unwatch(s, DOMID_GUEST, "some", "guestwatch",
+ watch_cb, guest_watches);
+ g_assert(!err);
+
+ err = xs_impl_unwatch(s, DOMID_GUEST, "some", "guestwatch",
+ watch_cb, guest_watches);
+ g_assert(err == ENOENT);
+
+ err = xs_impl_watch(s, DOMID_GUEST, "some/relative/path2", "watchp2",
+ watch_cb, guest_watches);
+ g_assert(!err);
+ g_assert(guest_watches->len == strlen("some/relative/path2watchp2"));
+ g_assert(!strcmp(guest_watches->str, "some/relative/path2watchp2"));
+ g_string_truncate(guest_watches, 0);
+
+ err = xs_impl_watch(s, DOMID_GUEST, "/local/domain/1/some/relative",
+ "watchrel", watch_cb, guest_watches);
+ g_assert(!err);
+ g_assert(guest_watches->len ==
+ strlen("/local/domain/1/some/relativewatchrel"));
+ g_assert(!strcmp(guest_watches->str,
+ "/local/domain/1/some/relativewatchrel"));
+ g_string_truncate(guest_watches, 0);
+
+ /* Write somewhere else which already existed */
+ err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative", "moredata");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 8);
+
+ /* Write somewhere we aren't allowed */
+ err = write_str(s, DOMID_GUEST, XBT_NULL, "/local/domain/badplace",
+ "moredata");
+ g_assert(err == EACCES);
+
+ g_assert(!strcmp(guest_watches->str,
+ "/local/domain/1/some/relativewatchrel"));
+ g_string_truncate(guest_watches, 0);
+
+ g_byte_array_set_size(data, 0);
+ err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data);
+ g_assert(!err);
+ g_assert(data->len == strlen("moredata"));
+ g_assert(!memcmp(data->data, "moredata", data->len));
+
+ /* Overwrite existing data */
+ err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative", "otherdata");
+ g_assert(!err);
+ g_string_truncate(guest_watches, 0);
+
+ g_byte_array_set_size(data, 0);
+ err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data);
+ g_assert(!err);
+ g_assert(data->len == strlen("otherdata"));
+ g_assert(!memcmp(data->data, "otherdata", data->len));
+
+ /* Remove the subtree */
+ err = xs_impl_rm(s, DOMID_GUEST, XBT_NULL, "some/relative");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 5);
+
+ /* Each watch fires with the least specific relevant path */
+ g_assert(strstr(guest_watches->str,
+ "some/relative/path2watchp2"));
+ g_assert(strstr(guest_watches->str,
+ "/local/domain/1/some/relativewatchrel"));
+ g_string_truncate(guest_watches, 0);
+
+ g_byte_array_set_size(data, 0);
+ err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data);
+ g_assert(err == ENOENT);
+ g_byte_array_unref(data);
+
+ xs_impl_reset_watches(s, DOMID_GUEST);
+ g_string_free(qemu_watches, true);
+ g_string_free(guest_watches, true);
+ xs_node_unref(old_root);
+ xs_impl_delete(s, true);
+}
+
+
+static void do_test_xs_node_tx(bool fail, bool commit)
+{
+ XenstoreImplState *s = setup();
+ GString *watches = g_string_new(NULL);
+ GByteArray *data = g_byte_array_new();
+ unsigned int tx_id = XBT_NULL;
+ int err;
+
+ g_assert(s);
+
+ /* Set a watch */
+ err = xs_impl_watch(s, DOMID_GUEST, "some", "watch",
+ watch_cb, watches);
+ g_assert(!err);
+ g_assert(watches->len == strlen("somewatch"));
+ g_assert(!strcmp(watches->str, "somewatch"));
+ g_string_truncate(watches, 0);
+
+ /* Write something */
+ err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path",
+ "something");
+ g_assert(s->nr_nodes == 7);
+ g_assert(!err);
+ g_assert(!strcmp(watches->str,
+ "some/relative/pathwatch"));
+ g_string_truncate(watches, 0);
+
+ /* Create a transaction */
+ err = xs_impl_transaction_start(s, DOMID_GUEST, &tx_id);
+ g_assert(!err);
+
+ if (fail) {
+ /* Write something else in the root */
+ err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path",
+ "another thing");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 7);
+ g_assert(!strcmp(watches->str,
+ "some/relative/pathwatch"));
+ g_string_truncate(watches, 0);
+ }
+
+ g_assert(!watches->len);
+
+ /* Perform a write in the transaction */
+ err = write_str(s, DOMID_GUEST, tx_id, "some/relative/path",
+ "something else");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 7);
+ g_assert(!watches->len);
+
+ err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative/path", data);
+ g_assert(!err);
+ if (fail) {
+ g_assert(data->len == strlen("another thing"));
+ g_assert(!memcmp(data->data, "another thing", data->len));
+ } else {
+ g_assert(data->len == strlen("something"));
+ g_assert(!memcmp(data->data, "something", data->len));
+ }
+ g_byte_array_set_size(data, 0);
+
+ err = xs_impl_read(s, DOMID_GUEST, tx_id, "some/relative/path", data);
+ g_assert(!err);
+ g_assert(data->len == strlen("something else"));
+ g_assert(!memcmp(data->data, "something else", data->len));
+ g_byte_array_set_size(data, 0);
+
+ check_serdes(s);
+
+ /* Attempt to commit the transaction */
+ err = xs_impl_transaction_end(s, DOMID_GUEST, tx_id, commit);
+ if (commit && fail) {
+ g_assert(err == EAGAIN);
+ } else {
+ g_assert(!err);
+ }
+ if (commit && !fail) {
+ g_assert(!strcmp(watches->str,
+ "some/relative/pathwatch"));
+ g_string_truncate(watches, 0);
+ } else {
+ g_assert(!watches->len);
+ }
+ g_assert(s->nr_nodes == 7);
+
+ check_serdes(s);
+
+ err = xs_impl_unwatch(s, DOMID_GUEST, "some", "watch",
+ watch_cb, watches);
+ g_assert(!err);
+
+ err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative/path", data);
+ g_assert(!err);
+ if (fail) {
+ g_assert(data->len == strlen("another thing"));
+ g_assert(!memcmp(data->data, "another thing", data->len));
+ } else if (commit) {
+ g_assert(data->len == strlen("something else"));
+ g_assert(!memcmp(data->data, "something else", data->len));
+ } else {
+ g_assert(data->len == strlen("something"));
+ g_assert(!memcmp(data->data, "something", data->len));
+ }
+ g_byte_array_unref(data);
+ g_string_free(watches, true);
+ xs_impl_delete(s, true);
+}
+
+static void test_xs_node_tx_fail(void)
+{
+ do_test_xs_node_tx(true, true);
+}
+
+static void test_xs_node_tx_abort(void)
+{
+ do_test_xs_node_tx(false, false);
+ do_test_xs_node_tx(true, false);
+}
+static void test_xs_node_tx_succeed(void)
+{
+ do_test_xs_node_tx(false, true);
+}
+
+static void test_xs_node_tx_rm(void)
+{
+ XenstoreImplState *s = setup();
+ GString *watches = g_string_new(NULL);
+ GByteArray *data = g_byte_array_new();
+ unsigned int tx_id = XBT_NULL;
+ int err;
+
+ g_assert(s);
+
+ /* Set a watch */
+ err = xs_impl_watch(s, DOMID_GUEST, "some", "watch",
+ watch_cb, watches);
+ g_assert(!err);
+ g_assert(watches->len == strlen("somewatch"));
+ g_assert(!strcmp(watches->str, "somewatch"));
+ g_string_truncate(watches, 0);
+
+ /* Write something */
+ err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path",
+ "something");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 9);
+ g_assert(!strcmp(watches->str,
+ "some/deep/dark/relative/pathwatch"));
+ g_string_truncate(watches, 0);
+
+ /* Create a transaction */
+ err = xs_impl_transaction_start(s, DOMID_GUEST, &tx_id);
+ g_assert(!err);
+
+ /* Delete the tree in the transaction */
+ err = xs_impl_rm(s, DOMID_GUEST, tx_id, "some/deep/dark");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 9);
+ g_assert(!watches->len);
+
+ err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path",
+ data);
+ g_assert(!err);
+ g_assert(data->len == strlen("something"));
+ g_assert(!memcmp(data->data, "something", data->len));
+ g_byte_array_set_size(data, 0);
+
+ check_serdes(s);
+
+ /* Commit the transaction */
+ err = xs_impl_transaction_end(s, DOMID_GUEST, tx_id, true);
+ g_assert(!err);
+ g_assert(s->nr_nodes == 6);
+
+ g_assert(!strcmp(watches->str, "some/deep/darkwatch"));
+ g_string_truncate(watches, 0);
+
+ /* Now the node is gone */
+ err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path",
+ data);
+ g_assert(err == ENOENT);
+ g_byte_array_unref(data);
+
+ err = xs_impl_unwatch(s, DOMID_GUEST, "some", "watch",
+ watch_cb, watches);
+ g_assert(!err);
+
+ g_string_free(watches, true);
+ xs_impl_delete(s, true);
+}
+
+static void test_xs_node_tx_resurrect(void)
+{
+ XenstoreImplState *s = setup();
+ GString *watches = g_string_new(NULL);
+ GByteArray *data = g_byte_array_new();
+ unsigned int tx_id = XBT_NULL;
+ int err;
+
+ g_assert(s);
+
+ /* Write something */
+ err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path",
+ "something");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 9);
+
+ /* Another node to remain shared */
+ err = write_str(s, DOMID_GUEST, XBT_NULL, "some/place/safe", "keepme");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 11);
+
+ /* This node will be wiped and resurrected */
+ err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark",
+ "foo");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 11);
+
+ /* Set a watch */
+ err = xs_impl_watch(s, DOMID_GUEST, "some", "watch",
+ watch_cb, watches);
+ g_assert(!err);
+ g_assert(watches->len == strlen("somewatch"));
+ g_assert(!strcmp(watches->str, "somewatch"));
+ g_string_truncate(watches, 0);
+
+ /* Create a transaction */
+ err = xs_impl_transaction_start(s, DOMID_GUEST, &tx_id);
+ g_assert(!err);
+
+ /* Delete the tree in the transaction */
+ err = xs_impl_rm(s, DOMID_GUEST, tx_id, "some/deep");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 11);
+ g_assert(!watches->len);
+
+ /* Resurrect part of it */
+ err = write_str(s, DOMID_GUEST, tx_id, "some/deep/dark/different/path",
+ "something");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 11);
+
+ check_serdes(s);
+
+ /* Commit the transaction */
+ err = xs_impl_transaction_end(s, DOMID_GUEST, tx_id, true);
+ g_assert(!err);
+ g_assert(s->nr_nodes == 11);
+
+ check_serdes(s);
+
+ /* lost data */
+ g_assert(strstr(watches->str, "some/deep/dark/different/pathwatch"));
+ /* topmost deleted */
+ g_assert(strstr(watches->str, "some/deep/dark/relativewatch"));
+ /* lost data */
+ g_assert(strstr(watches->str, "some/deep/darkwatch"));
+
+ g_string_truncate(watches, 0);
+
+ /* Now the node is gone */
+ err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path",
+ data);
+ g_assert(err == ENOENT);
+ g_byte_array_unref(data);
+
+ check_serdes(s);
+
+ err = xs_impl_unwatch(s, DOMID_GUEST, "some", "watch",
+ watch_cb, watches);
+ g_assert(!err);
+
+ g_string_free(watches, true);
+ xs_impl_delete(s, true);
+}
+
+static void test_xs_node_tx_resurrect2(void)
+{
+ XenstoreImplState *s = setup();
+ GString *watches = g_string_new(NULL);
+ GByteArray *data = g_byte_array_new();
+ unsigned int tx_id = XBT_NULL;
+ int err;
+
+ g_assert(s);
+
+ /* Write something */
+ err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path",
+ "something");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 9);
+
+ /* Another node to remain shared */
+ err = write_str(s, DOMID_GUEST, XBT_NULL, "some/place/safe", "keepme");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 11);
+
+ /* This node will be wiped and resurrected */
+ err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark",
+ "foo");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 11);
+
+ /* Set a watch */
+ err = xs_impl_watch(s, DOMID_GUEST, "some", "watch",
+ watch_cb, watches);
+ g_assert(!err);
+ g_assert(watches->len == strlen("somewatch"));
+ g_assert(!strcmp(watches->str, "somewatch"));
+ g_string_truncate(watches, 0);
+
+ /* Create a transaction */
+ err = xs_impl_transaction_start(s, DOMID_GUEST, &tx_id);
+ g_assert(!err);
+
+ /* Delete the tree in the transaction */
+ err = xs_impl_rm(s, DOMID_GUEST, tx_id, "some/deep");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 11);
+ g_assert(!watches->len);
+
+ /* Resurrect part of it */
+ err = write_str(s, DOMID_GUEST, tx_id, "some/deep/dark/relative/path",
+ "something");
+ g_assert(!err);
+ g_assert(s->nr_nodes == 11);
+
+ check_serdes(s);
+
+ /* Commit the transaction */
+ err = xs_impl_transaction_end(s, DOMID_GUEST, tx_id, true);
+ g_assert(!err);
+ g_assert(s->nr_nodes == 11);
+
+ check_serdes(s);
+
+ /* lost data */
+ g_assert(strstr(watches->str, "some/deep/dark/relative/pathwatch"));
+ /* lost data */
+ g_assert(strstr(watches->str, "some/deep/darkwatch"));
+
+ g_string_truncate(watches, 0);
+
+ /* Now the node is gone */
+ err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path",
+ data);
+ g_assert(!err);
+ g_assert(data->len == strlen("something"));
+ g_assert(!memcmp(data->data, "something", data->len));
+
+ g_byte_array_unref(data);
+
+ check_serdes(s);
+
+ err = xs_impl_unwatch(s, DOMID_GUEST, "some", "watch",
+ watch_cb, watches);
+ g_assert(!err);
+
+ g_string_free(watches, true);
+ xs_impl_delete(s, true);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ module_call_init(MODULE_INIT_QOM);
+
+ g_test_add_func("/xs_node/simple", test_xs_node_simple);
+ g_test_add_func("/xs_node/tx_abort", test_xs_node_tx_abort);
+ g_test_add_func("/xs_node/tx_fail", test_xs_node_tx_fail);
+ g_test_add_func("/xs_node/tx_succeed", test_xs_node_tx_succeed);
+ g_test_add_func("/xs_node/tx_rm", test_xs_node_tx_rm);
+ g_test_add_func("/xs_node/tx_resurrect", test_xs_node_tx_resurrect);
+ g_test_add_func("/xs_node/tx_resurrect2", test_xs_node_tx_resurrect2);
+
+ return g_test_run();
+}
diff --git a/ui/cocoa.m b/ui/cocoa.m
index 289a2b1..985a0f5 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -1330,10 +1330,15 @@
return NO;
}
-/* Called when QEMU goes into the background */
-- (void) applicationWillResignActive: (NSNotification *)aNotification
+/*
+ * Called when QEMU goes into the background. Note that
+ * [-NSWindowDelegate windowDidResignKey:] is used here instead of
+ * [-NSApplicationDelegate applicationWillResignActive:] because it cannot
+ * detect that the window loses focus when the deck is clicked on macOS 13.2.1.
+ */
+- (void) windowDidResignKey: (NSNotification *)aNotification
{
- COCOA_DEBUG("QemuCocoaAppController: applicationWillResignActive\n");
+ COCOA_DEBUG("%s\n", __func__);
[cocoaView ungrabMouse];
[cocoaView raiseAllKeys];
}
diff --git a/util/async.c b/util/async.c
index 0657b75..21016a1 100644
--- a/util/async.c
+++ b/util/async.c
@@ -74,14 +74,21 @@
unsigned old_flags;
/*
- * The memory barrier implicit in qatomic_fetch_or makes sure that:
- * 1. idle & any writes needed by the callback are done before the
- * locations are read in the aio_bh_poll.
- * 2. ctx is loaded before the callback has a chance to execute and bh
- * could be freed.
+ * Synchronizes with atomic_fetch_and() in aio_bh_dequeue(), ensuring that
+ * insertion starts after BH_PENDING is set.
*/
old_flags = qatomic_fetch_or(&bh->flags, BH_PENDING | new_flags);
+
if (!(old_flags & BH_PENDING)) {
+ /*
+ * At this point the bottom half becomes visible to aio_bh_poll().
+ * This insertion thus synchronizes with QSLIST_MOVE_ATOMIC in
+ * aio_bh_poll(), ensuring that:
+ * 1. any writes needed by the callback are visible from the callback
+ * after aio_bh_dequeue() returns bh.
+ * 2. ctx is loaded before the callback has a chance to execute and bh
+ * could be freed.
+ */
QSLIST_INSERT_HEAD_ATOMIC(&ctx->bh_list, bh, next);
}
@@ -107,11 +114,8 @@
QSLIST_REMOVE_HEAD(head, next);
/*
- * The qatomic_and is paired with aio_bh_enqueue(). The implicit memory
- * barrier ensures that the callback sees all writes done by the scheduling
- * thread. It also ensures that the scheduling thread sees the cleared
- * flag before bh->cb has run, and thus will call aio_notify again if
- * necessary.
+ * Synchronizes with qatomic_fetch_or() in aio_bh_enqueue(), ensuring that
+ * the removal finishes before BH_PENDING is reset.
*/
*flags = qatomic_fetch_and(&bh->flags,
~(BH_PENDING | BH_SCHEDULED | BH_IDLE));
@@ -158,6 +162,7 @@
BHListSlice *s;
int ret = 0;
+ /* Synchronizes with QSLIST_INSERT_HEAD_ATOMIC in aio_bh_enqueue(). */
QSLIST_MOVE_ATOMIC(&slice.bh_list, &ctx->bh_list);
QSIMPLEQ_INSERT_TAIL(&ctx->bh_slice_list, &slice, next);
@@ -448,15 +453,15 @@
void aio_notify(AioContext *ctx)
{
/*
- * Write e.g. bh->flags before writing ctx->notified. Pairs with smp_mb in
- * aio_notify_accept.
+ * Write e.g. ctx->bh_list before writing ctx->notified. Pairs with
+ * smp_mb() in aio_notify_accept().
*/
smp_wmb();
qatomic_set(&ctx->notified, true);
/*
- * Write ctx->notified before reading ctx->notify_me. Pairs
- * with smp_mb in aio_ctx_prepare or aio_poll.
+ * Write ctx->notified (and also ctx->bh_list) before reading ctx->notify_me.
+ * Pairs with smp_mb() in aio_ctx_prepare or aio_poll.
*/
smp_mb();
if (qatomic_read(&ctx->notify_me)) {
@@ -469,8 +474,9 @@
qatomic_set(&ctx->notified, false);
/*
- * Write ctx->notified before reading e.g. bh->flags. Pairs with smp_wmb
- * in aio_notify.
+ * Order reads of ctx->notified (in aio_context_notifier_poll()) and the
+ * above clearing of ctx->notified before reads of e.g. bh->flags. Pairs
+ * with smp_wmb() in aio_notify.
*/
smp_mb();
}
@@ -493,6 +499,11 @@
EventNotifier *e = opaque;
AioContext *ctx = container_of(e, AioContext, notifier);
+ /*
+ * No need for load-acquire because we just want to kick the
+ * event loop. aio_notify_accept() takes care of synchronizing
+ * the event loop with the producers.
+ */
return qatomic_read(&ctx->notified);
}
diff --git a/util/log.c b/util/log.c
index 7837ff9..53b4f6c 100644
--- a/util/log.c
+++ b/util/log.c
@@ -489,7 +489,7 @@
"do not chain compiled TBs so that \"exec\" and \"cpu\" show\n"
"complete traces" },
#ifdef CONFIG_PLUGIN
- { CPU_LOG_PLUGIN, "plugin", "output from TCG plugins\n"},
+ { CPU_LOG_PLUGIN, "plugin", "output from TCG plugins"},
#endif
{ LOG_STRACE, "strace",
"log every user-mode syscall, its input, and its result" },
diff --git a/util/qemu-coroutine-lock.c b/util/qemu-coroutine-lock.c
index 58f3f77..84a50a9 100644
--- a/util/qemu-coroutine-lock.c
+++ b/util/qemu-coroutine-lock.c
@@ -201,10 +201,16 @@
trace_qemu_co_mutex_lock_entry(mutex, self);
push_waiter(mutex, &w);
+ /*
+ * Add waiter before reading mutex->handoff. Pairs with qatomic_mb_set
+ * in qemu_co_mutex_unlock.
+ */
+ smp_mb__after_rmw();
+
/* This is the "Responsibility Hand-Off" protocol; a lock() picks from
* a concurrent unlock() the responsibility of waking somebody up.
*/
- old_handoff = qatomic_mb_read(&mutex->handoff);
+ old_handoff = qatomic_read(&mutex->handoff);
if (old_handoff &&
has_waiters(mutex) &&
qatomic_cmpxchg(&mutex->handoff, old_handoff, 0) == old_handoff) {
@@ -303,6 +309,7 @@
}
our_handoff = mutex->sequence;
+ /* Set handoff before checking for waiters. */
qatomic_mb_set(&mutex->handoff, our_handoff);
if (!has_waiters(mutex)) {
/* The concurrent lock has not added itself yet, so it
diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c
index 93d2505..b2e26e2 100644
--- a/util/qemu-thread-posix.c
+++ b/util/qemu-thread-posix.c
@@ -384,13 +384,21 @@
void qemu_event_set(QemuEvent *ev)
{
- /* qemu_event_set has release semantics, but because it *loads*
+ assert(ev->initialized);
+
+ /*
+ * Pairs with both qemu_event_reset() and qemu_event_wait().
+ *
+ * qemu_event_set has release semantics, but because it *loads*
* ev->value we need a full memory barrier here.
*/
- assert(ev->initialized);
smp_mb();
if (qatomic_read(&ev->value) != EV_SET) {
- if (qatomic_xchg(&ev->value, EV_SET) == EV_BUSY) {
+ int old = qatomic_xchg(&ev->value, EV_SET);
+
+ /* Pairs with memory barrier in kernel futex_wait system call. */
+ smp_mb__after_rmw();
+ if (old == EV_BUSY) {
/* There were waiters, wake them up. */
qemu_futex_wake(ev, INT_MAX);
}
@@ -399,18 +407,19 @@
void qemu_event_reset(QemuEvent *ev)
{
- unsigned value;
-
assert(ev->initialized);
- value = qatomic_read(&ev->value);
- smp_mb_acquire();
- if (value == EV_SET) {
- /*
- * If there was a concurrent reset (or even reset+wait),
- * do nothing. Otherwise change EV_SET->EV_FREE.
- */
- qatomic_or(&ev->value, EV_FREE);
- }
+
+ /*
+ * If there was a concurrent reset (or even reset+wait),
+ * do nothing. Otherwise change EV_SET->EV_FREE.
+ */
+ qatomic_or(&ev->value, EV_FREE);
+
+ /*
+ * Order reset before checking the condition in the caller.
+ * Pairs with the first memory barrier in qemu_event_set().
+ */
+ smp_mb__after_rmw();
}
void qemu_event_wait(QemuEvent *ev)
@@ -418,20 +427,40 @@
unsigned value;
assert(ev->initialized);
- value = qatomic_read(&ev->value);
- smp_mb_acquire();
+
+ /*
+ * qemu_event_wait must synchronize with qemu_event_set even if it does
+ * not go down the slow path, so this load-acquire is needed that
+ * synchronizes with the first memory barrier in qemu_event_set().
+ *
+ * If we do go down the slow path, there is no requirement at all: we
+ * might miss a qemu_event_set() here but ultimately the memory barrier in
+ * qemu_futex_wait() will ensure the check is done correctly.
+ */
+ value = qatomic_load_acquire(&ev->value);
if (value != EV_SET) {
if (value == EV_FREE) {
/*
- * Leave the event reset and tell qemu_event_set that there
- * are waiters. No need to retry, because there cannot be
- * a concurrent busy->free transition. After the CAS, the
- * event will be either set or busy.
+ * Leave the event reset and tell qemu_event_set that there are
+ * waiters. No need to retry, because there cannot be a concurrent
+ * busy->free transition. After the CAS, the event will be either
+ * set or busy.
+ *
+ * This cmpxchg doesn't have particular ordering requirements if it
+ * succeeds (moving the store earlier can only cause qemu_event_set()
+ * to issue _more_ wakeups), the failing case needs acquire semantics
+ * like the load above.
*/
if (qatomic_cmpxchg(&ev->value, EV_FREE, EV_BUSY) == EV_SET) {
return;
}
}
+
+ /*
+ * This is the final check for a concurrent set, so it does need
+ * a smp_mb() pairing with the second barrier of qemu_event_set().
+ * The barrier is inside the FUTEX_WAIT system call.
+ */
qemu_futex_wait(ev, EV_BUSY);
}
}
diff --git a/util/qemu-thread-win32.c b/util/qemu-thread-win32.c
index 69db254..a7fe3cc 100644
--- a/util/qemu-thread-win32.c
+++ b/util/qemu-thread-win32.c
@@ -272,12 +272,20 @@
void qemu_event_set(QemuEvent *ev)
{
assert(ev->initialized);
- /* qemu_event_set has release semantics, but because it *loads*
+
+ /*
+ * Pairs with both qemu_event_reset() and qemu_event_wait().
+ *
+ * qemu_event_set has release semantics, but because it *loads*
* ev->value we need a full memory barrier here.
*/
smp_mb();
if (qatomic_read(&ev->value) != EV_SET) {
- if (qatomic_xchg(&ev->value, EV_SET) == EV_BUSY) {
+ int old = qatomic_xchg(&ev->value, EV_SET);
+
+ /* Pairs with memory barrier after ResetEvent. */
+ smp_mb__after_rmw();
+ if (old == EV_BUSY) {
/* There were waiters, wake them up. */
SetEvent(ev->event);
}
@@ -286,17 +294,19 @@
void qemu_event_reset(QemuEvent *ev)
{
- unsigned value;
-
assert(ev->initialized);
- value = qatomic_read(&ev->value);
- smp_mb_acquire();
- if (value == EV_SET) {
- /* If there was a concurrent reset (or even reset+wait),
- * do nothing. Otherwise change EV_SET->EV_FREE.
- */
- qatomic_or(&ev->value, EV_FREE);
- }
+
+ /*
+ * If there was a concurrent reset (or even reset+wait),
+ * do nothing. Otherwise change EV_SET->EV_FREE.
+ */
+ qatomic_or(&ev->value, EV_FREE);
+
+ /*
+ * Order reset before checking the condition in the caller.
+ * Pairs with the first memory barrier in qemu_event_set().
+ */
+ smp_mb__after_rmw();
}
void qemu_event_wait(QemuEvent *ev)
@@ -304,29 +314,49 @@
unsigned value;
assert(ev->initialized);
- value = qatomic_read(&ev->value);
- smp_mb_acquire();
+
+ /*
+ * qemu_event_wait must synchronize with qemu_event_set even if it does
+ * not go down the slow path, so this load-acquire is needed that
+ * synchronizes with the first memory barrier in qemu_event_set().
+ *
+ * If we do go down the slow path, there is no requirement at all: we
+ * might miss a qemu_event_set() here but ultimately the memory barrier in
+ * qemu_futex_wait() will ensure the check is done correctly.
+ */
+ value = qatomic_load_acquire(&ev->value);
if (value != EV_SET) {
if (value == EV_FREE) {
- /* qemu_event_set is not yet going to call SetEvent, but we are
- * going to do another check for EV_SET below when setting EV_BUSY.
- * At that point it is safe to call WaitForSingleObject.
+ /*
+ * Here the underlying kernel event is reset, but qemu_event_set is
+ * not yet going to call SetEvent. However, there will be another
+ * check for EV_SET below when setting EV_BUSY. At that point it
+ * is safe to call WaitForSingleObject.
*/
ResetEvent(ev->event);
- /* Tell qemu_event_set that there are waiters. No need to retry
- * because there cannot be a concurrent busy->free transition.
- * After the CAS, the event will be either set or busy.
+ /*
+ * It is not clear whether ResetEvent provides this barrier; kernel
+ * APIs (KeResetEvent/KeClearEvent) do not. Better safe than sorry!
+ */
+ smp_mb();
+
+ /*
+ * Leave the event reset and tell qemu_event_set that there are
+ * waiters. No need to retry, because there cannot be a concurrent
+ * busy->free transition. After the CAS, the event will be either
+ * set or busy.
*/
if (qatomic_cmpxchg(&ev->value, EV_FREE, EV_BUSY) == EV_SET) {
- value = EV_SET;
- } else {
- value = EV_BUSY;
+ return;
}
}
- if (value == EV_BUSY) {
- WaitForSingleObject(ev->event, INFINITE);
- }
+
+ /*
+ * ev->value is now EV_BUSY. Since we didn't observe EV_SET,
+ * qemu_event_set() must observe EV_BUSY and call SetEvent().
+ */
+ WaitForSingleObject(ev->event, INFINITE);
}
}