Merge tag 'pull-riscv-to-apply-20230710-1' of https://github.com/alistair23/qemu into staging
Third RISC-V PR for 8.1
* Use xl instead of mxl for disassemble
* Factor out extension tests to cpu_cfg.h
* disas/riscv: Add vendor extension support
* disas/riscv: Add support for XVentanaCondOps
* disas/riscv: Add support for XThead* instructions
* Fix mstatus related problems
* Fix veyron-v1 CPU properties
* Fix the xlen for data address when MPRV=1
* opensbi: Upgrade from v1.2 to v1.3
* Enable 32-bit Spike OpenSBI boot testing
* Support the watchdog timer of HiFive 1 rev b
* Only build qemu-system-riscv$$ on rv$$ host
* Add RVV registers to log
* Restrict ACLINT to TCG
* Add syscall riscv_hwprobe
* Add support for BF16 extensions
* KVM_RISCV_SET_TIMER macro is not configured correctly
* Generate devicetree only after machine initialization is complete
* virt: Convert fdt_load_addr to uint64_t
* KVM: fixes and enhancements
* Add support for the Zfa extension
# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEaukCtqfKh31tZZKWr3yVEwxTgBMFAmSr+ekACgkQr3yVEwxT
# gBMMGg//ZCcyH3KXB49c2KUIFO6FKYUxN9uC3giZCtuGyEH8T2yDgZVVXnxwU+Ij
# +3Ej6T/ZdWMpePC9qf+xKzHWZk7Qc8Tcg+JgQbga573894yZInRwYl8HsSlEKA+Z
# vlqSBPxTlp9rlDwGP/LjGljyIFqL4konk9zi3FL4ZXTF1iHUGrh/953Y3wIreEfl
# KX5UznnWcgy2BqQT1vihMbM8qCVK6iryH+QZ6LiAsPMSX1rIzk8ectQryILzoIYh
# bMiwCLVMyr4ZrUXjmGTF+7/WcOWwhhyfpdstf2iotKALelZtVHit0wHcty2GYQde
# nvN83jJWu04DGXkPBUsqCUQXczGo1QHjJUH3RIRJzfOby/lGt4pSzHAfKA+iNUht
# ikM3SdBsXMO+ogjTtTcCMb7/m2vsMoQP60VRts9Mh3YVD0cgr7RqpqRoEMugVYnr
# ca8Vijf71mB+y+pq477eV1Q8BoKpr8xa1OlFkNKPC17uMD7HoDMI44QgFOgtYp10
# TMsqqyB75q6PZhSEwm63xbmH0Zpo8kSqT/E3MTtGTyPeuL8TNNNSkCmFaGYmRrbI
# XEp7vG2RaDJOvDomS3nUhA5ruc8SaXd0q25q2gLYQfCsehfFqZAwuNB5xf1zS0M0
# ov1/gwaqU93t6nLbo2cCbb0plkIFKwwJ9KKjD06wJ4KPe0TGFzk=
# =3XFD
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon 10 Jul 2023 01:30:33 PM BST
# gpg: using RSA key 6AE902B6A7CA877D6D659296AF7C95130C538013
# gpg: Good signature from "Alistair Francis <alistair@alistair23.me>" [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: 6AE9 02B6 A7CA 877D 6D65 9296 AF7C 9513 0C53 8013
* tag 'pull-riscv-to-apply-20230710-1' of https://github.com/alistair23/qemu: (54 commits)
riscv: Add support for the Zfa extension
target/riscv/kvm.c: read/write (cbom|cboz)_blocksize in KVM
target/riscv/kvm.c: add kvmconfig_get_cfg_addr() helper
target/riscv: update multi-letter extension KVM properties
target/riscv/cpu.c: create KVM mock properties
target/riscv/cpu.c: remove priv_ver check from riscv_isa_string_ext()
target/riscv/cpu.c: add satp_mode properties earlier
target/riscv/kvm.c: add multi-letter extension KVM properties
target/riscv/kvm.c: update KVM MISA bits
target/riscv: add KVM specific MISA properties
target/riscv/cpu: add misa_ext_info_arr[]
target/riscv/kvm.c: init 'misa_ext_mask' with scratch CPU
target/riscv: handle mvendorid/marchid/mimpid for KVM CPUs
target/riscv: read marchid/mimpid in kvm_riscv_init_machine_ids()
target/riscv: use KVM scratch CPUs to init KVM properties
target/riscv/cpu.c: restrict 'marchid' value
target/riscv/cpu.c: restrict 'mimpid' value
target/riscv/cpu.c: restrict 'mvendorid' value
hw/riscv/virt.c: skip 'mmu-type' FDT if satp mode not set
target/riscv: skip features setup for KVM CPUs
...
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
diff --git a/MAINTAINERS b/MAINTAINERS
index 1817cfc..43bd9af 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -452,8 +452,6 @@
F: target/s390x/kvm/
F: target/s390x/machine.c
F: target/s390x/sigp.c
-F: hw/s390x/pv.c
-F: include/hw/s390x/pv.h
F: gdb-xml/s390*.xml
T: git https://github.com/borntraeger/qemu.git s390-next
L: qemu-s390x@nongnu.org
diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst
index ddc1e48..02ea5a8 100644
--- a/docs/about/deprecated.rst
+++ b/docs/about/deprecated.rst
@@ -116,6 +116,11 @@
Use ``-run-with async-teardown=on`` instead.
+``-chroot`` (since 8.1)
+'''''''''''''''''''''''
+
+Use ``-run-with chroot=dir`` instead.
+
``-singlestep`` (since 8.1)
'''''''''''''''''''''''''''
diff --git a/docs/devel/style.rst b/docs/devel/style.rst
index aa5e083..3cfcdeb 100644
--- a/docs/devel/style.rst
+++ b/docs/devel/style.rst
@@ -567,7 +567,8 @@
.. code-block:: c
- int somefunc(void) {
+ int somefunc(void)
+ {
int ret = -1;
char *foo = g_strdup_printf("foo%", "wibble");
GList *bar = .....
@@ -588,7 +589,8 @@
.. code-block:: c
- int somefunc(void) {
+ int somefunc(void)
+ {
g_autofree char *foo = g_strdup_printf("foo%", "wibble");
g_autoptr (GList) bar = .....
@@ -613,7 +615,8 @@
.. code-block:: c
- char *somefunc(void) {
+ char *somefunc(void)
+ {
g_autofree char *foo = g_strdup_printf("foo%", "wibble");
g_autoptr (GList) bar = .....
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 8612684..515dcf5 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -26,7 +26,7 @@
#include "hw/s390x/vfio-ccw.h"
#include "hw/s390x/css.h"
#include "hw/s390x/ebcdic.h"
-#include "hw/s390x/pv.h"
+#include "target/s390x/kvm/pv.h"
#include "hw/scsi/scsi.h"
#include "hw/virtio/virtio-net.h"
#include "ipl.h"
diff --git a/hw/s390x/meson.build b/hw/s390x/meson.build
index f291016..6fd0968 100644
--- a/hw/s390x/meson.build
+++ b/hw/s390x/meson.build
@@ -22,7 +22,6 @@
'tod-kvm.c',
's390-skeys-kvm.c',
's390-stattrib-kvm.c',
- 'pv.c',
's390-pci-kvm.c',
))
s390x_ss.add(when: 'CONFIG_TCG', if_true: files(
diff --git a/hw/s390x/s390-pci-kvm.c b/hw/s390x/s390-pci-kvm.c
index 9134fe1..ff41e41 100644
--- a/hw/s390x/s390-pci-kvm.c
+++ b/hw/s390x/s390-pci-kvm.c
@@ -14,7 +14,7 @@
#include <linux/kvm.h>
#include "kvm/kvm_s390x.h"
-#include "hw/s390x/pv.h"
+#include "target/s390x/kvm/pv.h"
#include "hw/s390x/s390-pci-bus.h"
#include "hw/s390x/s390-pci-kvm.h"
#include "hw/s390x/s390-pci-inst.h"
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 2dece8e..4516d73 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -42,7 +42,7 @@
#include "hw/s390x/tod.h"
#include "sysemu/sysemu.h"
#include "sysemu/cpus.h"
-#include "hw/s390x/pv.h"
+#include "target/s390x/kvm/pv.h"
#include "migration/blocker.h"
#include "qapi/visitor.h"
diff --git a/hw/s390x/tod-kvm.c b/hw/s390x/tod-kvm.c
index e2202da..9588b90 100644
--- a/hw/s390x/tod-kvm.c
+++ b/hw/s390x/tod-kvm.c
@@ -13,7 +13,7 @@
#include "qemu/module.h"
#include "sysemu/runstate.h"
#include "hw/s390x/tod.h"
-#include "hw/s390x/pv.h"
+#include "target/s390x/kvm/pv.h"
#include "kvm/kvm_s390x.h"
static void kvm_s390_get_tod_raw(S390TOD *tod, Error **errp)
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 9a2ec56..d3d1352 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1635,7 +1635,9 @@
static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
{
regs->psw.addr = infop->entry;
- regs->psw.mask = PSW_MASK_64 | PSW_MASK_32;
+ regs->psw.mask = PSW_MASK_DAT | PSW_MASK_IO | PSW_MASK_EXT | \
+ PSW_MASK_MCHECK | PSW_MASK_PSTATE | PSW_MASK_64 | \
+ PSW_MASK_32;
regs->gprs[15] = infop->start_stack;
}
diff --git a/meson.build b/meson.build
index 1ed47c7..5fcdb37 100644
--- a/meson.build
+++ b/meson.build
@@ -20,7 +20,7 @@
cc = meson.get_compiler('c')
all_languages = ['c']
-if add_languages('cpp', required: false, native: false)
+if targetos == 'windows' and add_languages('cpp', required: false, native: false)
all_languages += ['cpp']
cxx = meson.get_compiler('cpp')
endif
diff --git a/os-posix.c b/os-posix.c
index 90ea717..cfcb965 100644
--- a/os-posix.c
+++ b/os-posix.c
@@ -38,6 +38,7 @@
#include "qemu/cutils.h"
#include "qemu/config-file.h"
#include "qemu/option.h"
+#include "qemu/module.h"
#ifdef CONFIG_LINUX
#include <sys/prctl.h>
@@ -148,6 +149,7 @@
}
break;
case QEMU_OPTION_chroot:
+ warn_report("option is deprecated, use '-run-with chroot=...' instead");
chroot_dir = optarg;
break;
case QEMU_OPTION_daemonize:
@@ -158,18 +160,25 @@
case QEMU_OPTION_asyncteardown:
init_async_teardown();
break;
+#endif
case QEMU_OPTION_run_with: {
+ const char *str;
QemuOpts *opts = qemu_opts_parse_noisily(qemu_find_opts("run-with"),
optarg, false);
if (!opts) {
exit(1);
}
+#if defined(CONFIG_LINUX)
if (qemu_opt_get_bool(opts, "async-teardown", false)) {
init_async_teardown();
}
+#endif
+ str = qemu_opt_get(opts, "chroot");
+ if (str) {
+ chroot_dir = str;
+ }
break;
}
-#endif
default:
return -1;
}
@@ -348,3 +357,27 @@
return -ENOSYS;
#endif
}
+
+static QemuOptsList qemu_run_with_opts = {
+ .name = "run-with",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_run_with_opts.head),
+ .desc = {
+#if defined(CONFIG_LINUX)
+ {
+ .name = "async-teardown",
+ .type = QEMU_OPT_BOOL,
+ },
+#endif
+ {
+ .name = "chroot",
+ .type = QEMU_OPT_STRING,
+ },
+ { /* end of list */ }
+ },
+};
+
+static void register_runwith(void)
+{
+ qemu_add_opts(&qemu_run_with_opts);
+}
+opts_init(register_runwith);
diff --git a/qemu-options.hx b/qemu-options.hx
index 9608750..f8f384e 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4677,11 +4677,12 @@
#ifndef _WIN32
DEF("chroot", HAS_ARG, QEMU_OPTION_chroot, \
- "-chroot dir chroot to dir just before starting the VM\n",
+ "-chroot dir chroot to dir just before starting the VM (deprecated)\n",
QEMU_ARCH_ALL)
#endif
SRST
``-chroot dir``
+ Deprecated, use '-run-with chroot=...' instead.
Immediately before starting guest execution, chroot to the specified
directory. Especially useful in combination with -runas.
ERST
@@ -4868,13 +4869,16 @@
This option is deprecated and should no longer be used. The new option
``-run-with async-teardown=on`` is a replacement.
ERST
+#endif
+#ifdef CONFIG_POSIX
DEF("run-with", HAS_ARG, QEMU_OPTION_run_with,
- "-run-with async-teardown[=on|off]\n"
- " misc QEMU process lifecycle options\n"
- " async-teardown=on enables asynchronous teardown\n",
+ "-run-with [async-teardown=on|off][,chroot=dir]\n"
+ " Set miscellaneous QEMU process lifecycle options:\n"
+ " async-teardown=on enables asynchronous teardown (Linux only)\n"
+ " chroot=dir chroot to dir just before starting the VM\n",
QEMU_ARCH_ALL)
SRST
-``-run-with``
+``-run-with [async-teardown=on|off][,chroot=dir]``
Set QEMU process lifecycle options.
``async-teardown=on`` enables asynchronous teardown. A new process called
@@ -4887,6 +4891,10 @@
performed correctly. This only works if the cleanup process is not
forcefully killed with SIGKILL before the main QEMU process has
terminated completely.
+
+ ``chroot=dir`` can be used for doing a chroot to the specified directory
+ immediately before starting the guest execution. This is especially useful
+ in combination with -runas.
ERST
#endif
diff --git a/target/s390x/arch_dump.c b/target/s390x/arch_dump.c
index cb98f48..51a2116 100644
--- a/target/s390x/arch_dump.c
+++ b/target/s390x/arch_dump.c
@@ -17,8 +17,8 @@
#include "s390x-internal.h"
#include "elf.h"
#include "sysemu/dump.h"
-#include "hw/s390x/pv.h"
#include "kvm/kvm_s390x.h"
+#include "target/s390x/kvm/pv.h"
struct S390xUserRegsStruct {
uint64_t psw[2];
diff --git a/target/s390x/cpu-sysemu.c b/target/s390x/cpu-sysemu.c
index 97d6c76..8112561 100644
--- a/target/s390x/cpu-sysemu.c
+++ b/target/s390x/cpu-sysemu.c
@@ -33,7 +33,7 @@
#include "qapi/qapi-visit-run-state.h"
#include "sysemu/hw_accel.h"
-#include "hw/s390x/pv.h"
+#include "target/s390x/kvm/pv.h"
#include "hw/boards.h"
#include "sysemu/sysemu.h"
#include "sysemu/tcg.h"
diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c
index 2e4e11d..ebb155c 100644
--- a/target/s390x/cpu_features.c
+++ b/target/s390x/cpu_features.c
@@ -15,7 +15,7 @@
#include "qemu/module.h"
#include "cpu_features.h"
#ifndef CONFIG_USER_ONLY
-#include "hw/s390x/pv.h"
+#include "target/s390x/kvm/pv.h"
#endif
#define DEF_FEAT(_FEAT, _NAME, _TYPE, _BIT, _DESC) \
diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
index ae8880e..42b52af 100644
--- a/target/s390x/cpu_models.c
+++ b/target/s390x/cpu_models.c
@@ -24,7 +24,7 @@
#include "qemu/qemu-print.h"
#ifndef CONFIG_USER_ONLY
#include "sysemu/sysemu.h"
-#include "hw/s390x/pv.h"
+#include "target/s390x/kvm/pv.h"
#endif
#define CPUDEF_INIT(_type, _gen, _ec_ga, _mha_pow, _hmfai, _name, _desc) \
diff --git a/target/s390x/diag.c b/target/s390x/diag.c
index e5f0df1..8ce18e0 100644
--- a/target/s390x/diag.c
+++ b/target/s390x/diag.c
@@ -19,9 +19,9 @@
#include "sysemu/cpus.h"
#include "hw/s390x/ipl.h"
#include "hw/s390x/s390-virtio-ccw.h"
-#include "hw/s390x/pv.h"
#include "sysemu/kvm.h"
#include "kvm/kvm_s390x.h"
+#include "target/s390x/kvm/pv.h"
#include "qemu/error-report.h"
diff --git a/target/s390x/helper.c b/target/s390x/helper.c
index 2b363aa..d76c063 100644
--- a/target/s390x/helper.c
+++ b/target/s390x/helper.c
@@ -24,7 +24,7 @@
#include "gdbstub/helpers.h"
#include "qemu/timer.h"
#include "hw/s390x/ioinst.h"
-#include "hw/s390x/pv.h"
+#include "target/s390x/kvm/pv.h"
#include "sysemu/hw_accel.h"
#include "sysemu/runstate.h"
diff --git a/target/s390x/helper.h b/target/s390x/helper.h
index 6bc01df..0510257 100644
--- a/target/s390x/helper.h
+++ b/target/s390x/helper.h
@@ -355,7 +355,7 @@
DEF_HELPER_FLAGS_4(ipte, TCG_CALL_NO_RWG, void, env, i64, i64, i32)
DEF_HELPER_FLAGS_1(ptlb, TCG_CALL_NO_RWG, void, env)
DEF_HELPER_FLAGS_1(purge, TCG_CALL_NO_RWG, void, env)
-DEF_HELPER_2(lra, i64, env, i64)
+DEF_HELPER_3(lra, i64, env, i64, i64)
DEF_HELPER_1(per_check_exception, void, env)
DEF_HELPER_FLAGS_3(per_branch, TCG_CALL_NO_RWG, void, env, i64, i64)
DEF_HELPER_FLAGS_2(per_ifetch, TCG_CALL_NO_RWG, void, env, i64)
diff --git a/target/s390x/ioinst.c b/target/s390x/ioinst.c
index 053aaab..bbe45a4 100644
--- a/target/s390x/ioinst.c
+++ b/target/s390x/ioinst.c
@@ -16,7 +16,7 @@
#include "hw/s390x/ioinst.h"
#include "trace.h"
#include "hw/s390x/s390-pci-bus.h"
-#include "hw/s390x/pv.h"
+#include "target/s390x/kvm/pv.h"
/* All I/O instructions but chsc use the s format */
static uint64_t get_address_from_regs(CPUS390XState *env, uint32_t ipb,
diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c
index 3ac7ec9..a9e5880 100644
--- a/target/s390x/kvm/kvm.c
+++ b/target/s390x/kvm/kvm.c
@@ -50,7 +50,7 @@
#include "exec/memattrs.h"
#include "hw/s390x/s390-virtio-ccw.h"
#include "hw/s390x/s390-virtio-hcall.h"
-#include "hw/s390x/pv.h"
+#include "target/s390x/kvm/pv.h"
#ifndef DEBUG_KVM
#define DEBUG_KVM 0
diff --git a/target/s390x/kvm/meson.build b/target/s390x/kvm/meson.build
index 37253f7..d6aca59 100644
--- a/target/s390x/kvm/meson.build
+++ b/target/s390x/kvm/meson.build
@@ -1,5 +1,6 @@
s390x_ss.add(when: 'CONFIG_KVM', if_true: files(
+ 'pv.c',
'kvm.c'
), if_false: files(
'stubs.c'
diff --git a/hw/s390x/pv.c b/target/s390x/kvm/pv.c
similarity index 99%
rename from hw/s390x/pv.c
rename to target/s390x/kvm/pv.c
index b63f378..6a69be7 100644
--- a/hw/s390x/pv.c
+++ b/target/s390x/kvm/pv.c
@@ -21,9 +21,9 @@
#include "qom/object_interfaces.h"
#include "exec/confidential-guest-support.h"
#include "hw/s390x/ipl.h"
-#include "hw/s390x/pv.h"
#include "hw/s390x/sclp.h"
#include "target/s390x/kvm/kvm_s390x.h"
+#include "target/s390x/kvm/pv.h"
static bool info_valid;
static struct kvm_s390_pv_info_vm info_vm;
diff --git a/include/hw/s390x/pv.h b/target/s390x/kvm/pv.h
similarity index 100%
rename from include/hw/s390x/pv.h
rename to target/s390x/kvm/pv.h
diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c
index b04b57c..fbb2f1b 100644
--- a/target/s390x/mmu_helper.c
+++ b/target/s390x/mmu_helper.c
@@ -417,7 +417,7 @@
vaddr &= TARGET_PAGE_MASK;
- if (!(env->psw.mask & PSW_MASK_DAT)) {
+ if (rw != MMU_S390_LRA && !(env->psw.mask & PSW_MASK_DAT)) {
*raddr = vaddr;
goto nodat;
}
diff --git a/target/s390x/tcg/fpu_helper.c b/target/s390x/tcg/fpu_helper.c
index 57e5829..4b7fa58 100644
--- a/target/s390x/tcg/fpu_helper.c
+++ b/target/s390x/tcg/fpu_helper.c
@@ -306,8 +306,9 @@
/* 64/32-bit FP multiplication */
uint64_t HELPER(mdeb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
{
+ float64 f1_64 = float32_to_float64(f1, &env->fpu_status);
float64 ret = float32_to_float64(f2, &env->fpu_status);
- ret = float64_mul(f1, ret, &env->fpu_status);
+ ret = float64_mul(f1_64, ret, &env->fpu_status);
handle_exceptions(env, false, GETPC());
return ret;
}
diff --git a/target/s390x/tcg/insn-data.h.inc b/target/s390x/tcg/insn-data.h.inc
index 0a45dbb..457ed25 100644
--- a/target/s390x/tcg/insn-data.h.inc
+++ b/target/s390x/tcg/insn-data.h.inc
@@ -667,11 +667,11 @@
F(0xb317, MEEBR, RRE, Z, e1, e2, new, e1, meeb, 0, IF_BFP)
F(0xb31c, MDBR, RRE, Z, f1, f2, new, f1, mdb, 0, IF_BFP)
F(0xb34c, MXBR, RRE, Z, x1, x2, new_x, x1, mxb, 0, IF_BFP)
- F(0xb30c, MDEBR, RRE, Z, f1, e2, new, f1, mdeb, 0, IF_BFP)
+ F(0xb30c, MDEBR, RRE, Z, e1, e2, new, f1, mdeb, 0, IF_BFP)
F(0xb307, MXDBR, RRE, Z, f1, f2, new_x, x1, mxdb, 0, IF_BFP)
F(0xed17, MEEB, RXE, Z, e1, m2_32u, new, e1, meeb, 0, IF_BFP)
F(0xed1c, MDB, RXE, Z, f1, m2_64, new, f1, mdb, 0, IF_BFP)
- F(0xed0c, MDEB, RXE, Z, f1, m2_32u, new, f1, mdeb, 0, IF_BFP)
+ F(0xed0c, MDEB, RXE, Z, e1, m2_32u, new, f1, mdeb, 0, IF_BFP)
F(0xed07, MXDB, RXE, Z, f1, m2_64, new_x, x1, mxdb, 0, IF_BFP)
/* MULTIPLY HALFWORD */
C(0x4c00, MH, RX_a, Z, r1_o, m2_16s, new, r1_32, mul, 0)
diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c
index d02ec86..f417fb1 100644
--- a/target/s390x/tcg/mem_helper.c
+++ b/target/s390x/tcg/mem_helper.c
@@ -514,6 +514,7 @@
int32_t i;
/* MVCRL always copies one more byte than specified - maximum is 256 */
+ l &= 0xff;
l++;
access_prepare(&srca, env, src, l, MMU_DATA_LOAD, mmu_idx, ra);
@@ -2355,7 +2356,7 @@
}
/* load real address */
-uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
+uint64_t HELPER(lra)(CPUS390XState *env, uint64_t r1, uint64_t addr)
{
uint64_t asc = env->psw.mask & PSW_MASK_ASC;
uint64_t ret, tec;
@@ -2369,7 +2370,7 @@
exc = mmu_translate(env, addr, MMU_S390_LRA, asc, &ret, &flags, &tec);
if (exc) {
cc = 3;
- ret = exc | 0x80000000;
+ ret = (r1 & 0xFFFFFFFF00000000ULL) | exc | 0x80000000;
} else {
cc = 0;
ret |= addr & ~TARGET_PAGE_MASK;
diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c
index a6ee2d4..6661b27 100644
--- a/target/s390x/tcg/translate.c
+++ b/target/s390x/tcg/translate.c
@@ -2383,10 +2383,14 @@
int r1 = get_field(s, r1);
int r2 = get_field(s, r2);
TCGv_i64 t = tcg_temp_new_i64();
+ TCGv_i64 t_cc = tcg_temp_new_i64();
/* Note the "subsequently" in the PoO, which implies a defined result
if r1 == r2. Thus we cannot defer these writes to an output hook. */
+ gen_op_calc_cc(s);
+ tcg_gen_extu_i32_i64(t_cc, cc_op);
tcg_gen_shri_i64(t, psw_mask, 32);
+ tcg_gen_deposit_i64(t, t, t_cc, 12, 2);
store_reg32_i64(r1, t);
if (r2 != 0) {
store_reg32_i64(r2, psw_mask);
@@ -2928,7 +2932,7 @@
static DisasJumpType op_lra(DisasContext *s, DisasOps *o)
{
- gen_helper_lra(o->out, cpu_env, o->in2);
+ gen_helper_lra(o->out, cpu_env, o->out, o->in2);
set_cc_static(s);
return DISAS_NEXT;
}
@@ -5790,7 +5794,7 @@
disas_jdest(s, i2, is_imm, imm, ri2);
if (is_imm) {
- ri2 = tcg_constant_i64(s->base.pc_next + imm * 2);
+ ri2 = tcg_constant_i64(s->base.pc_next + (int64_t)imm * 2);
}
return ri2;
diff --git a/tests/qtest/libqos/libqos.c b/tests/qtest/libqos/libqos.c
index 5ffda08..5c0fa1f 100644
--- a/tests/qtest/libqos/libqos.c
+++ b/tests/qtest/libqos/libqos.c
@@ -137,56 +137,9 @@
migrate_allocator(&from->alloc, &to->alloc);
}
-bool have_qemu_img(void)
-{
- char *rpath;
- const char *path = getenv("QTEST_QEMU_IMG");
- if (!path) {
- return false;
- }
-
- rpath = realpath(path, NULL);
- if (!rpath) {
- return false;
- } else {
- free(rpath);
- return true;
- }
-}
-
-void mkimg(const char *file, const char *fmt, unsigned size_mb)
-{
- gchar *cli;
- bool ret;
- int rc;
- GError *err = NULL;
- char *qemu_img_path;
- gchar *out, *out2;
- char *qemu_img_abs_path;
-
- qemu_img_path = getenv("QTEST_QEMU_IMG");
- g_assert(qemu_img_path);
- qemu_img_abs_path = realpath(qemu_img_path, NULL);
- g_assert(qemu_img_abs_path);
-
- cli = g_strdup_printf("%s create -f %s %s %uM", qemu_img_abs_path,
- fmt, file, size_mb);
- ret = g_spawn_command_line_sync(cli, &out, &out2, &rc, &err);
- if (err || !g_spawn_check_exit_status(rc, &err)) {
- fprintf(stderr, "%s\n", err->message);
- g_error_free(err);
- }
- g_assert(ret && !err);
-
- g_free(out);
- g_free(out2);
- g_free(cli);
- free(qemu_img_abs_path);
-}
-
void mkqcow2(const char *file, unsigned size_mb)
{
- return mkimg(file, "qcow2", size_mb);
+ g_assert_true(mkimg(file, "qcow2", size_mb));
}
void prepare_blkdebug_script(const char *debug_fn, const char *event)
diff --git a/tests/qtest/libqos/libqos.h b/tests/qtest/libqos/libqos.h
index 12d05b2..c04950e 100644
--- a/tests/qtest/libqos/libqos.h
+++ b/tests/qtest/libqos/libqos.h
@@ -27,8 +27,6 @@
G_GNUC_PRINTF(2, 3);
void qtest_common_shutdown(QOSState *qs);
void qtest_shutdown(QOSState *qs);
-bool have_qemu_img(void);
-void mkimg(const char *file, const char *fmt, unsigned size_mb);
void mkqcow2(const char *file, unsigned size_mb);
void migrate(QOSState *from, QOSState *to, const char *uri);
void prepare_blkdebug_script(const char *debug_fn, const char *event);
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index 79152f0..c22dfc3 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -1742,3 +1742,55 @@
return b;
}
+
+bool have_qemu_img(void)
+{
+ char *rpath;
+ const char *path = getenv("QTEST_QEMU_IMG");
+ if (!path) {
+ return false;
+ }
+
+ rpath = realpath(path, NULL);
+ if (!rpath) {
+ return false;
+ } else {
+ free(rpath);
+ return true;
+ }
+}
+
+bool mkimg(const char *file, const char *fmt, unsigned size_mb)
+{
+ gchar *cli;
+ bool ret;
+ int rc;
+ GError *err = NULL;
+ char *qemu_img_path;
+ gchar *out, *out2;
+ char *qemu_img_abs_path;
+
+ qemu_img_path = getenv("QTEST_QEMU_IMG");
+ if (!qemu_img_path) {
+ return false;
+ }
+ qemu_img_abs_path = realpath(qemu_img_path, NULL);
+ if (!qemu_img_abs_path) {
+ return false;
+ }
+
+ cli = g_strdup_printf("%s create -f %s %s %uM", qemu_img_abs_path,
+ fmt, file, size_mb);
+ ret = g_spawn_command_line_sync(cli, &out, &out2, &rc, &err);
+ if (err || !g_spawn_check_exit_status(rc, &err)) {
+ fprintf(stderr, "%s\n", err->message);
+ g_error_free(err);
+ }
+
+ g_free(out);
+ g_free(out2);
+ g_free(cli);
+ free(qemu_img_abs_path);
+
+ return ret && !err;
+}
diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h
index 913acc3..3a71bc4 100644
--- a/tests/qtest/libqtest.h
+++ b/tests/qtest/libqtest.h
@@ -994,4 +994,24 @@
*/
pid_t qtest_pid(QTestState *s);
+/**
+ * have_qemu_img:
+ *
+ * Returns: true if "qemu-img" is available.
+ */
+bool have_qemu_img(void);
+
+/**
+ * mkimg:
+ * @file: File name of the image that should be created
+ * @fmt: Format, e.g. "qcow2" or "raw"
+ * @size_mb: Size of the image in megabytes
+ *
+ * Create a disk image with qemu-img. Note that the QTEST_QEMU_IMG
+ * environment variable must point to the qemu-img file.
+ *
+ * Returns: true if the image has been created successfully.
+ */
+bool mkimg(const char *file, const char *fmt, unsigned size_mb);
+
#endif
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index b9cc194..efa8c72 100644
--- a/tests/qtest/migration-test.c
+++ b/tests/qtest/migration-test.c
@@ -47,6 +47,20 @@
static bool got_dst_resume;
/*
+ * An initial 3 MB offset is used as that corresponds
+ * to ~1 sec of data transfer with our bandwidth setting.
+ */
+#define MAGIC_OFFSET_BASE (3 * 1024 * 1024)
+/*
+ * A further 1k is added to ensure we're not a multiple
+ * of TEST_MEM_PAGE_SIZE, thus avoid clash with writes
+ * from the migration guest workload.
+ */
+#define MAGIC_OFFSET_SHUFFLE 1024
+#define MAGIC_OFFSET (MAGIC_OFFSET_BASE + MAGIC_OFFSET_SHUFFLE)
+#define MAGIC_MARKER 0xFEED12345678CAFEULL
+
+/*
* Dirtylimit stop working if dirty page rate error
* value less than DIRTYLIMIT_TOLERANCE_RANGE
*/
@@ -445,6 +459,91 @@
migrate_set_parameter_int(who, "downtime-limit", 30 * 1000);
}
+/*
+ * Our goal is to ensure that we run a single full migration
+ * iteration, and also dirty memory, ensuring that at least
+ * one further iteration is required.
+ *
+ * We can't directly synchronize with the start of a migration
+ * so we have to apply some tricks monitoring memory that is
+ * transferred.
+ *
+ * Initially we set the migration bandwidth to an insanely
+ * low value, with tiny max downtime too. This basically
+ * guarantees migration will never complete.
+ *
+ * This will result in a test that is unacceptably slow though,
+ * so we can't let the entire migration pass run at this speed.
+ * Our intent is to let it run just long enough that we can
+ * prove data prior to the marker has been transferred *AND*
+ * also prove this transferred data is dirty again.
+ *
+ * Before migration starts, we write a 64-bit magic marker
+ * into a fixed location in the src VM RAM.
+ *
+ * Then watch dst memory until the marker appears. This is
+ * proof that start_address -> MAGIC_OFFSET_BASE has been
+ * transferred.
+ *
+ * Finally we go back to the source and read a byte just
+ * before the marker untill we see it flip in value. This
+ * is proof that start_address -> MAGIC_OFFSET_BASE
+ * is now dirty again.
+ *
+ * IOW, we're guaranteed at least a 2nd migration pass
+ * at this point.
+ *
+ * We can now let migration run at full speed to finish
+ * the test
+ */
+static void migrate_prepare_for_dirty_mem(QTestState *from)
+{
+ /*
+ * The guest workflow iterates from start_address to
+ * end_address, writing 1 byte every TEST_MEM_PAGE_SIZE
+ * bytes.
+ *
+ * IOW, if we write to mem at a point which is NOT
+ * a multiple of TEST_MEM_PAGE_SIZE, our write won't
+ * conflict with the migration workflow.
+ *
+ * We put in a marker here, that we'll use to determine
+ * when the data has been transferred to the dst.
+ */
+ qtest_writeq(from, start_address + MAGIC_OFFSET, MAGIC_MARKER);
+}
+
+static void migrate_wait_for_dirty_mem(QTestState *from,
+ QTestState *to)
+{
+ uint64_t watch_address = start_address + MAGIC_OFFSET_BASE;
+ uint64_t marker_address = start_address + MAGIC_OFFSET;
+ uint8_t watch_byte;
+
+ /*
+ * Wait for the MAGIC_MARKER to get transferred, as an
+ * indicator that a migration pass has made some known
+ * amount of progress.
+ */
+ do {
+ usleep(1000 * 10);
+ } while (qtest_readq(to, marker_address) != MAGIC_MARKER);
+
+ /*
+ * Now ensure that already transferred bytes are
+ * dirty again from the guest workload. Note the
+ * guest byte value will wrap around and by chance
+ * match the original watch_byte. This is harmless
+ * as we'll eventually see a different value if we
+ * keep watching
+ */
+ watch_byte = qtest_readb(from, watch_address);
+ do {
+ usleep(1000 * 10);
+ } while (qtest_readb(from, watch_address) == watch_byte);
+}
+
+
static void migrate_pause(QTestState *who)
{
qtest_qmp_assert_success(who, "{ 'execute': 'migrate-pause' }");
@@ -577,7 +676,10 @@
MIG_TEST_FAIL_DEST_QUIT_ERR,
} result;
- /* Optional: set number of migration passes to wait for, if live==true */
+ /*
+ * Optional: set number of migration passes to wait for, if live==true.
+ * If zero, then merely wait for a few MB of dirty data
+ */
unsigned int iterations;
/*
@@ -1165,12 +1267,14 @@
migrate_ensure_non_converge(from);
+ migrate_prepare_for_dirty_mem(from);
+
/* Wait for the first serial output from the source */
wait_for_serial("src_serial");
migrate_qmp(from, uri, "{}");
- wait_for_migration_pass(from);
+ migrate_wait_for_dirty_mem(from, to);
*from_ptr = from;
*to_ptr = to;
@@ -1405,14 +1509,8 @@
}
if (args->live) {
- /*
- * Testing live migration, we want to ensure that some
- * memory is re-dirtied after being transferred, so that
- * we exercise logic for dirty page handling. We achieve
- * this with a ridiculosly low bandwidth that guarantees
- * non-convergance.
- */
migrate_ensure_non_converge(from);
+ migrate_prepare_for_dirty_mem(from);
} else {
/*
* Testing non-live migration, we allow it to run at
@@ -1447,13 +1545,16 @@
}
} else {
if (args->live) {
- if (args->iterations) {
- while (args->iterations--) {
- wait_for_migration_pass(from);
- }
- } else {
+ /*
+ * For initial iteration(s) we must do a full pass,
+ * but for the final iteration, we need only wait
+ * for some dirty mem before switching to converge
+ */
+ while (args->iterations > 1) {
wait_for_migration_pass(from);
+ args->iterations--;
}
+ migrate_wait_for_dirty_mem(from, to);
migrate_ensure_converge(from);
@@ -1586,6 +1687,9 @@
return;
}
+ migrate_ensure_non_converge(from);
+ migrate_prepare_for_dirty_mem(from);
+
migrate_set_capability(from, "x-ignore-shared", true);
migrate_set_capability(to, "x-ignore-shared", true);
@@ -1594,7 +1698,7 @@
migrate_qmp(from, uri, "{}");
- wait_for_migration_pass(from);
+ migrate_wait_for_dirty_mem(from, to);
if (!got_src_stop) {
qtest_qmp_eventwait(from, "STOP");
@@ -2325,6 +2429,7 @@
}
migrate_ensure_non_converge(from);
+ migrate_prepare_for_dirty_mem(from);
migrate_set_parameter_int(from, "multifd-channels", 16);
migrate_set_parameter_int(to, "multifd-channels", 16);
@@ -2343,7 +2448,7 @@
migrate_qmp(from, uri, "{}");
- wait_for_migration_pass(from);
+ migrate_wait_for_dirty_mem(from, to);
migrate_cancel(from);
@@ -2372,11 +2477,13 @@
wait_for_migration_status(from, "cancelled", NULL);
- migrate_ensure_converge(from);
+ migrate_ensure_non_converge(from);
migrate_qmp(from, uri, "{}");
- wait_for_migration_pass(from);
+ migrate_wait_for_dirty_mem(from, to);
+
+ migrate_ensure_converge(from);
if (!got_src_stop) {
qtest_qmp_eventwait(from, "STOP");
diff --git a/tests/qtest/readconfig-test.c b/tests/qtest/readconfig-test.c
index ac72424..760f974 100644
--- a/tests/qtest/readconfig-test.c
+++ b/tests/qtest/readconfig-test.c
@@ -48,7 +48,7 @@
return qts;
}
-static void test_x86_memdev_resp(QObject *res)
+static void test_x86_memdev_resp(QObject *res, const char *mem_id, int size)
{
Visitor *v;
g_autoptr(MemdevList) memdevs = NULL;
@@ -63,8 +63,8 @@
g_assert(!memdevs->next);
memdev = memdevs->value;
- g_assert_cmpstr(memdev->id, ==, "ram");
- g_assert_cmpint(memdev->size, ==, 200 * MiB);
+ g_assert_cmpstr(memdev->id, ==, mem_id);
+ g_assert_cmpint(memdev->size, ==, size * MiB);
visit_free(v);
}
@@ -80,7 +80,7 @@
qts = qtest_init_with_config(cfgdata);
/* Test valid command */
resp = qtest_qmp(qts, "{ 'execute': 'query-memdev' }");
- test_x86_memdev_resp(qdict_get(resp, "return"));
+ test_x86_memdev_resp(qdict_get(resp, "return"), "ram", 200);
qobject_unref(resp);
qtest_quit(qts);
@@ -197,6 +197,189 @@
qtest_quit(qts);
}
+#if defined(CONFIG_POSIX) && defined(CONFIG_SLIRP)
+
+static char *make_temp_img(const char *template, const char *format, int size)
+{
+ GError *error = NULL;
+ char *temp_name;
+ int fd;
+
+ /* Create a temporary image names */
+ fd = g_file_open_tmp(template, &temp_name, &error);
+ if (fd == -1) {
+ fprintf(stderr, "unable to create file: %s\n", error->message);
+ g_error_free(error);
+ return NULL;
+ }
+ close(fd);
+
+ if (!mkimg(temp_name, format, size)) {
+ fprintf(stderr, "qemu-img failed to create %s\n", temp_name);
+ g_free(temp_name);
+ return NULL;
+ }
+
+ return temp_name;
+}
+
+struct device {
+ const char *name;
+ const char *type;
+};
+
+static void test_docs_q35(const char *input_file, struct device *devices)
+{
+ QTestState *qts;
+ QDict *resp;
+ QObject *qobj;
+ int ret, i;
+ g_autofree char *cfg_file = NULL, *sedcmd = NULL;
+ g_autofree char *hd_file = NULL, *cd_file = NULL;
+
+ /* Check that all the devices are available in the QEMU binary */
+ for (i = 0; devices[i].name; i++) {
+ if (!qtest_has_device(devices[i].type)) {
+ g_test_skip("one of the required devices is not available");
+ return;
+ }
+ }
+
+ hd_file = make_temp_img("qtest_disk_XXXXXX.qcow2", "qcow2", 1);
+ cd_file = make_temp_img("qtest_cdrom_XXXXXX.iso", "raw", 1);
+ if (!hd_file || !cd_file) {
+ g_test_skip("could not create disk images");
+ goto cleanup;
+ }
+
+ /* Create a temporary config file where we replace the disk image names */
+ ret = g_file_open_tmp("q35-emulated-XXXXXX.cfg", &cfg_file, NULL);
+ if (ret == -1) {
+ g_test_skip("could not create temporary config file");
+ goto cleanup;
+ }
+ close(ret);
+
+ sedcmd = g_strdup_printf("sed -e 's,guest.qcow2,%s,' -e 's,install.iso,%s,'"
+ " %s %s > '%s'",
+ hd_file, cd_file,
+ !qtest_has_accel("kvm") ? "-e '/accel/d'" : "",
+ input_file, cfg_file);
+ ret = system(sedcmd);
+ if (ret) {
+ g_test_skip("could not modify temporary config file");
+ goto cleanup;
+ }
+
+ qts = qtest_initf("-machine none -nodefaults -readconfig %s", cfg_file);
+
+ /* Check memory size */
+ resp = qtest_qmp(qts, "{ 'execute': 'query-memdev' }");
+ test_x86_memdev_resp(qdict_get(resp, "return"), "pc.ram", 1024);
+ qobject_unref(resp);
+
+ resp = qtest_qmp(qts, "{ 'execute': 'qom-list',"
+ " 'arguments': {'path': '/machine/peripheral' }}");
+ qobj = qdict_get(resp, "return");
+
+ /* Check that all the devices have been created */
+ for (i = 0; devices[i].name; i++) {
+ test_object_available(qobj, devices[i].name, devices[i].type);
+ }
+
+ qobject_unref(resp);
+
+ qtest_quit(qts);
+
+cleanup:
+ if (hd_file) {
+ unlink(hd_file);
+ }
+ if (cd_file) {
+ unlink(cd_file);
+ }
+ if (cfg_file) {
+ unlink(cfg_file);
+ }
+}
+
+static void test_docs_q35_emulated(void)
+{
+ struct device devices[] = {
+ { "ich9-pcie-port-1", "ioh3420" },
+ { "ich9-pcie-port-2", "ioh3420" },
+ { "ich9-pcie-port-3", "ioh3420" },
+ { "ich9-pcie-port-4", "ioh3420" },
+ { "ich9-pci-bridge", "i82801b11-bridge" },
+ { "ich9-ehci-1", "ich9-usb-ehci1" },
+ { "ich9-ehci-2", "ich9-usb-ehci2" },
+ { "ich9-uhci-1", "ich9-usb-uhci1" },
+ { "ich9-uhci-2", "ich9-usb-uhci2" },
+ { "ich9-uhci-3", "ich9-usb-uhci3" },
+ { "ich9-uhci-4", "ich9-usb-uhci4" },
+ { "ich9-uhci-5", "ich9-usb-uhci5" },
+ { "ich9-uhci-6", "ich9-usb-uhci6" },
+ { "sata-disk", "ide-hd" },
+ { "sata-optical-disk", "ide-cd" },
+ { "net", "e1000" },
+ { "video", "VGA" },
+ { "ich9-hda-audio", "ich9-intel-hda" },
+ { "ich9-hda-duplex", "hda-duplex" },
+ { NULL, NULL }
+ };
+
+ test_docs_q35("docs/config/q35-emulated.cfg", devices);
+}
+
+static void test_docs_q35_virtio_graphical(void)
+{
+ struct device devices[] = {
+ { "pcie.1", "pcie-root-port" },
+ { "pcie.2", "pcie-root-port" },
+ { "pcie.3", "pcie-root-port" },
+ { "pcie.4", "pcie-root-port" },
+ { "pcie.5", "pcie-root-port" },
+ { "pcie.6", "pcie-root-port" },
+ { "pcie.7", "pcie-root-port" },
+ { "pcie.8", "pcie-root-port" },
+ { "scsi", "virtio-scsi-pci" },
+ { "scsi-disk", "scsi-hd" },
+ { "scsi-optical-disk", "scsi-cd" },
+ { "net", "virtio-net-pci" },
+ { "usb", "nec-usb-xhci" },
+ { "tablet", "usb-tablet" },
+ { "video", "qxl-vga" },
+ { "sound", "ich9-intel-hda" },
+ { "duplex", "hda-duplex" },
+ { NULL, NULL }
+ };
+
+ test_docs_q35("docs/config/q35-virtio-graphical.cfg", devices);
+}
+
+static void test_docs_q35_virtio_serial(void)
+{
+ struct device devices[] = {
+ { "pcie.1", "pcie-root-port" },
+ { "pcie.2", "pcie-root-port" },
+ { "pcie.3", "pcie-root-port" },
+ { "pcie.4", "pcie-root-port" },
+ { "pcie.5", "pcie-root-port" },
+ { "pcie.6", "pcie-root-port" },
+ { "pcie.7", "pcie-root-port" },
+ { "pcie.8", "pcie-root-port" },
+ { "scsi", "virtio-scsi-pci" },
+ { "scsi-disk", "scsi-hd" },
+ { "scsi-optical-disk", "scsi-cd" },
+ { "net", "virtio-net-pci" },
+ { NULL, NULL }
+ };
+
+ test_docs_q35("docs/config/q35-virtio-serial.cfg", devices);
+}
+
+#endif /* CONFIG_LINUX */
+
int main(int argc, char *argv[])
{
const char *arch;
@@ -211,6 +394,19 @@
qtest_has_device("ich9-usb-uhci1")) {
qtest_add_func("readconfig/x86/ich9-ehci-uhci", test_docs_config_ich9);
}
+#if defined(CONFIG_POSIX) && defined(CONFIG_SLIRP)
+ qtest_add_func("readconfig/x86/q35-emulated", test_docs_q35_emulated);
+ qtest_add_func("readconfig/x86/q35-virtio-graphical",
+ test_docs_q35_virtio_graphical);
+ if (g_test_slow()) {
+ /*
+ * q35-virtio-serial.cfg is a subset of q35-virtio-graphical.cfg,
+ * so we can skip the test in quick mode
+ */
+ qtest_add_func("readconfig/x86/q35-virtio-serial",
+ test_docs_q35_virtio_serial);
+ }
+#endif
}
#if defined(CONFIG_SPICE) && !defined(__FreeBSD__)
qtest_add_func("readconfig/spice", test_spice);
diff --git a/tests/tcg/s390x/Makefile.softmmu-target b/tests/tcg/s390x/Makefile.softmmu-target
index 44dfd71..242c7b0 100644
--- a/tests/tcg/s390x/Makefile.softmmu-target
+++ b/tests/tcg/s390x/Makefile.softmmu-target
@@ -20,6 +20,7 @@
sam \
lpsw \
lpswe-early \
+ lra \
ssm-early \
stosm-early \
unaligned-lowcore
diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target
index 85abfbb..19fbbc6 100644
--- a/tests/tcg/s390x/Makefile.target
+++ b/tests/tcg/s390x/Makefile.target
@@ -36,6 +36,9 @@
TESTS+=ex-relative-long
TESTS+=ex-branch
TESTS+=mxdb
+TESTS+=epsw
+TESTS+=larl
+TESTS+=mdeb
cdsg: CFLAGS+=-pthread
cdsg: LDFLAGS+=-pthread
diff --git a/tests/tcg/s390x/epsw.c b/tests/tcg/s390x/epsw.c
new file mode 100644
index 0000000..affb1a5e
--- /dev/null
+++ b/tests/tcg/s390x/epsw.c
@@ -0,0 +1,23 @@
+/*
+ * Test the EPSW instruction.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include <assert.h>
+#include <stdlib.h>
+
+int main(void)
+{
+ unsigned long r1 = 0x1234567887654321UL, r2 = 0x8765432112345678UL;
+
+ asm("cr %[r1],%[r2]\n" /* cc = 1 */
+ "epsw %[r1],%[r2]"
+ : [r1] "+r" (r1), [r2] "+r" (r2) : : "cc");
+
+ /* Do not check the R and RI bits. */
+ r1 &= ~0x40000008UL;
+ assert(r1 == 0x1234567807051001UL);
+ assert(r2 == 0x8765432180000000UL);
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/tcg/s390x/gdbstub/test-svc.py b/tests/tcg/s390x/gdbstub/test-svc.py
index 7851ca7..18fad3f 100644
--- a/tests/tcg/s390x/gdbstub/test-svc.py
+++ b/tests/tcg/s390x/gdbstub/test-svc.py
@@ -25,7 +25,7 @@
gdb.execute("si")
report("larl\t" in gdb.execute("x/i $pc", False, True), "insn #2")
gdb.execute("si")
- report("lghi\t" in gdb.execute("x/i $pc", False, True), "insn #3")
+ report("lgrl\t" in gdb.execute("x/i $pc", False, True), "insn #3")
gdb.execute("si")
report("svc\t" in gdb.execute("x/i $pc", False, True), "insn #4")
gdb.execute("si")
diff --git a/tests/tcg/s390x/hello-s390x-asm.S b/tests/tcg/s390x/hello-s390x-asm.S
index 2e9faa1..4dbda12 100644
--- a/tests/tcg/s390x/hello-s390x-asm.S
+++ b/tests/tcg/s390x/hello-s390x-asm.S
@@ -8,7 +8,7 @@
/* puts("Hello, World!"); */
lghi %r2,1
larl %r3,foo
-lghi %r4,foo_end-foo
+lgrl %r4,foo_len
svc 4
/* exit(0); */
@@ -18,3 +18,5 @@
.align 2
foo: .asciz "Hello, World!\n"
foo_end:
+.align 8
+foo_len: .quad foo_end-foo
diff --git a/tests/tcg/s390x/larl.c b/tests/tcg/s390x/larl.c
new file mode 100644
index 0000000..7c95f89
--- /dev/null
+++ b/tests/tcg/s390x/larl.c
@@ -0,0 +1,21 @@
+/*
+ * Test the LARL instruction.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include <stdlib.h>
+
+int main(void)
+{
+ long algfi = (long)main;
+ long larl;
+
+ /*
+ * The compiler may emit larl for the C addition, so compute the expected
+ * value using algfi.
+ */
+ asm("algfi %[r],0xd0000000" : [r] "+r" (algfi) : : "cc");
+ asm("larl %[r],main+0xd0000000" : [r] "=r" (larl));
+
+ return algfi == larl ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/tests/tcg/s390x/lra.S b/tests/tcg/s390x/lra.S
new file mode 100644
index 0000000..79ab86f
--- /dev/null
+++ b/tests/tcg/s390x/lra.S
@@ -0,0 +1,19 @@
+ .org 0x200 /* lowcore padding */
+ .globl _start
+_start:
+ lgrl %r1,initial_r1
+ lra %r1,0(%r1)
+ cgrl %r1,expected_r1
+ jne 1f
+ lpswe success_psw
+1:
+ lpswe failure_psw
+ .align 8
+initial_r1:
+ .quad 0x8765432112345678
+expected_r1:
+ .quad 0x8765432180000038 /* ASCE type exception */
+success_psw:
+ .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */
+failure_psw:
+ .quad 0x2000000000000,0 /* disabled wait */
diff --git a/tests/tcg/s390x/mdeb.c b/tests/tcg/s390x/mdeb.c
new file mode 100644
index 0000000..4897d28
--- /dev/null
+++ b/tests/tcg/s390x/mdeb.c
@@ -0,0 +1,30 @@
+/*
+ * Test the MDEB and MDEBR instructions.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include <assert.h>
+#include <stdlib.h>
+
+int main(void)
+{
+ union {
+ float f[2];
+ double d;
+ } a;
+ float b;
+
+ a.f[0] = 1.2345;
+ a.f[1] = 999;
+ b = 6.789;
+ asm("mdeb %[a],%[b]" : [a] "+f" (a.d) : [b] "R" (b));
+ assert(a.d > 8.38 && a.d < 8.39);
+
+ a.f[0] = 1.2345;
+ a.f[1] = 999;
+ b = 6.789;
+ asm("mdebr %[a],%[b]" : [a] "+f" (a.d) : [b] "f" (b));
+ assert(a.d > 8.38 && a.d < 8.39);
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/tcg/s390x/mie3-mvcrl.c b/tests/tcg/s390x/mie3-mvcrl.c
index 93c7b0a..6d3d049 100644
--- a/tests/tcg/s390x/mie3-mvcrl.c
+++ b/tests/tcg/s390x/mie3-mvcrl.c
@@ -1,29 +1,55 @@
+#include <stdbool.h>
#include <stdint.h>
+#include <stdlib.h>
#include <string.h>
-
-static inline void mvcrl_8(const char *dst, const char *src)
+static void mvcrl(const char *dst, const char *src, size_t len)
{
+ register long r0 asm("r0") = len;
+
asm volatile (
- "llill %%r0, 8\n"
".insn sse, 0xE50A00000000, 0(%[dst]), 0(%[src])"
- : : [dst] "d" (dst), [src] "d" (src)
- : "r0", "memory");
+ : : [dst] "d" (dst), [src] "d" (src), "r" (r0)
+ : "memory");
}
-
-int main(int argc, char *argv[])
+static bool test(void)
{
const char *alpha = "abcdefghijklmnop";
/* array missing 'i' */
- char tstr[17] = "abcdefghjklmnop\0" ;
+ char tstr[17] = "abcdefghjklmnop\0";
/* mvcrl reference use: 'open a hole in an array' */
- mvcrl_8(tstr + 9, tstr + 8);
+ mvcrl(tstr + 9, tstr + 8, 8);
/* place missing 'i' */
tstr[8] = 'i';
- return strncmp(alpha, tstr, 16ul);
+ return strncmp(alpha, tstr, 16ul) == 0;
+}
+
+static bool test_bad_r0(void)
+{
+ char src[256] = { 0 };
+
+ /*
+ * PoP says: Bits 32-55 of general register 0 should contain zeros;
+ * otherwise, the program may not operate compatibly in the future.
+ *
+ * Try it anyway in order to check whether this would crash QEMU itself.
+ */
+ mvcrl(src, src, (size_t)-1);
+
+ return true;
+}
+
+int main(void)
+{
+ bool ok = true;
+
+ ok &= test();
+ ok &= test_bad_r0();
+
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/util/async-teardown.c b/util/async-teardown.c
index 3ab19c8..62cdeb0 100644
--- a/util/async-teardown.c
+++ b/util/async-teardown.c
@@ -12,9 +12,6 @@
*/
#include "qemu/osdep.h"
-#include "qemu/config-file.h"
-#include "qemu/option.h"
-#include "qemu/module.h"
#include <dirent.h>
#include <sys/prctl.h>
#include <sched.h>
@@ -147,21 +144,3 @@
clone(async_teardown_fn, new_stack_for_clone(), CLONE_VM, NULL);
sigprocmask(SIG_SETMASK, &old_signals, NULL);
}
-
-static QemuOptsList qemu_run_with_opts = {
- .name = "run-with",
- .head = QTAILQ_HEAD_INITIALIZER(qemu_run_with_opts.head),
- .desc = {
- {
- .name = "async-teardown",
- .type = QEMU_OPT_BOOL,
- },
- { /* end of list */ }
- },
-};
-
-static void register_teardown(void)
-{
- qemu_add_opts(&qemu_run_with_opts);
-}
-opts_init(register_teardown);