diff options
author | Richard Henderson <richard.henderson@linaro.org> | 2024-05-29 08:38:20 -0700 |
---|---|---|
committer | Richard Henderson <richard.henderson@linaro.org> | 2024-05-29 08:38:20 -0700 |
commit | 3b2fe44bb7f605f179e5e7feb2c13c2eb3abbb80 (patch) | |
tree | 3889669db11710754fde6e50bce2b4aca196e355 | |
parent | 79d7475f39f1b0f05fcb159f5cdcbf162340dc7e (diff) | |
parent | b04091393e6a71065aee6c91b2566f2dec95a4c9 (diff) |
Merge tag 'pull-request-2024-05-29' of https://gitlab.com/thuth/qemu into staging
* Fix and improve PER emulation on s390x
* Fix problems of the build-oss-fuzz CI job
* Fix broken update-linux-headers.sh script
* Fixes for compiling with -fsanitize=undefined on latest Clang versions
# -----BEGIN PGP SIGNATURE-----
#
# iQJFBAABCAAvFiEEJ7iIR+7gJQEY8+q5LtnXdP5wLbUFAmZXCNURHHRodXRoQHJl
# ZGhhdC5jb20ACgkQLtnXdP5wLbU0SxAAnN1i7v/RPfxm1xNQurs+Wl+rS2gJyvGK
# IJbEBAYufSQyY4yYrmZrmgNsa3CenPQpV7zWDvUV8BW8R3er8ZGLHmJ3cXQDaN5n
# JiLy9rvEBmAVb0LLaQX1GY94jdPRV2mRS9Q7Rxa2XDhn0w+sRy/wNFYEO2nghPjs
# zmhbDZrKm8os6imyp0DmDNWi8wLJJzpz8YsKlX60rPEFIynaNdp1ZuB6cXx+9pXH
# KXqiY8k/3WCYVs60xB9TfXh2o/Vb29WWaD5IyobZzGEq9pFyQzQf3aqhrv/heRfS
# B9537otkU9RIRf09p9f9/78JYHynb3SclM8UXHIGhYQl2S1C9T9gRePO9R+Rigq4
# 51UdsNvZV9WoacVk+L3c2MgIDAXsDOhTSpGKxgWZKgvxhczhr/iOEmWI+oyag7oD
# JZfHzwgdwFywumgMrLUrvf6274cyoDNIjpSFnfw0h2Ynp3qkpyigVw5gtP5sfQgD
# p/CoVUSRHxsajYQP3UmI70gG1fFbSz2ZWdnG+lC7kkCrD/xD4xLGP9DYK82d1/YS
# PmBaVoBttylOtr/S/I8KgJSmaQG0V/Sui7/5iyouZ26VFqakPnNzbxSDlJOEZ7k7
# GigybdjLSy6OWg0IfTOpuxsB3Cw/P2VZrNoO9xUmrjXpdBA/8BCkhmTNYu3QRvS1
# Mwgdyxqdy8I=
# =2/Y3
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 29 May 2024 03:52:05 AM PDT
# gpg: using RSA key 27B88847EEE0250118F3EAB92ED9D774FE702DB5
# gpg: issuer "thuth@redhat.com"
# gpg: Good signature from "Thomas Huth <th.huth@gmx.de>" [full]
# gpg: aka "Thomas Huth <thuth@redhat.com>" [full]
# gpg: aka "Thomas Huth <th.huth@posteo.de>" [unknown]
# gpg: aka "Thomas Huth <huth@tuxfamily.org>" [full]
* tag 'pull-request-2024-05-29' of https://gitlab.com/thuth/qemu: (22 commits)
qapi: Do not cast function pointers
lockable: Do not cast function pointers
qemu-keymap: Make references to allocations static
scripts/update-linux-headers.sh: Fix the path of setup_data.h
scripts/update-linux-headers.sh: Remove temporary directory inbetween
hw/s390x: Remove unused macro VMSTATE_ADAPTER_ROUTES
fuzz: disable leak-detection for oss-fuzz builds
fuzz: specify audiodev for usb-audio
tests/tcg/s390x: Add per.S
target/s390x: Adjust check of noreturn in translate_one
target/s390x: Simplify per_ifetch, per_check_exception
target/s390x: Fix helper_per_ifetch flags
target/s390x: Raise exception from per_store_real
target/s390x: Raise exception from helper_per_branch
target/s390x: Split per_breaking_event from per_branch_*
target/s390x: Simplify help_branch
target/s390x: Introduce help_goto_indirect
target/s390x: Disable conditional branch-to-next for PER
target/s390x: Record separate PER bits in TB flags
target/s390x: Update CR9 bits
...
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
-rw-r--r-- | include/hw/s390x/s390_flic.h | 3 | ||||
-rw-r--r-- | include/qapi/clone-visitor.h | 37 | ||||
-rw-r--r-- | include/qemu/lockable.h | 23 | ||||
-rw-r--r-- | qapi/qapi-clone-visitor.c | 30 | ||||
-rw-r--r-- | qemu-keymap.c | 8 | ||||
-rwxr-xr-x | scripts/oss-fuzz/build.sh | 1 | ||||
-rwxr-xr-x | scripts/update-linux-headers.sh | 3 | ||||
-rw-r--r-- | target/s390x/cpu.c | 36 | ||||
-rw-r--r-- | target/s390x/cpu.h | 85 | ||||
-rw-r--r-- | target/s390x/helper.h | 8 | ||||
-rw-r--r-- | target/s390x/tcg/excp_helper.c | 2 | ||||
-rw-r--r-- | target/s390x/tcg/misc_helper.c | 68 | ||||
-rw-r--r-- | target/s390x/tcg/translate.c | 242 | ||||
-rw-r--r-- | tests/qtest/fuzz/generic_fuzz_configs.h | 3 | ||||
-rw-r--r-- | tests/tcg/s390x/Makefile.softmmu-target | 1 | ||||
-rw-r--r-- | tests/tcg/s390x/per.S | 82 |
16 files changed, 355 insertions, 277 deletions
diff --git a/include/hw/s390x/s390_flic.h b/include/hw/s390x/s390_flic.h index bcb081def5..382d9833f1 100644 --- a/include/hw/s390x/s390_flic.h +++ b/include/hw/s390x/s390_flic.h @@ -35,9 +35,6 @@ typedef struct AdapterRoutes { extern const VMStateDescription vmstate_adapter_routes; -#define VMSTATE_ADAPTER_ROUTES(_f, _s) \ - VMSTATE_STRUCT(_f, _s, 1, vmstate_adapter_routes, AdapterRoutes) - #define TYPE_S390_FLIC_COMMON "s390-flic" OBJECT_DECLARE_TYPE(S390FLICState, S390FLICStateClass, S390_FLIC_COMMON) diff --git a/include/qapi/clone-visitor.h b/include/qapi/clone-visitor.h index adf9a788e2..ebc182b034 100644 --- a/include/qapi/clone-visitor.h +++ b/include/qapi/clone-visitor.h @@ -11,6 +11,7 @@ #ifndef QAPI_CLONE_VISITOR_H #define QAPI_CLONE_VISITOR_H +#include "qapi/error.h" #include "qapi/visitor.h" /* @@ -20,11 +21,8 @@ */ typedef struct QapiCloneVisitor QapiCloneVisitor; -void *qapi_clone(const void *src, bool (*visit_type)(Visitor *, const char *, - void **, Error **)); -void qapi_clone_members(void *dst, const void *src, size_t sz, - bool (*visit_type_members)(Visitor *, void *, - Error **)); +Visitor *qapi_clone_visitor_new(void); +Visitor *qapi_clone_members_visitor_new(void); /* * Deep-clone QAPI object @src of the given @type, and return the result. @@ -32,10 +30,18 @@ void qapi_clone_members(void *dst, const void *src, size_t sz, * Not usable on QAPI scalars (integers, strings, enums), nor on a * QAPI object that references the 'any' type. Safe when @src is NULL. */ -#define QAPI_CLONE(type, src) \ - ((type *)qapi_clone(src, \ - (bool (*)(Visitor *, const char *, void **, \ - Error **))visit_type_ ## type)) +#define QAPI_CLONE(type, src) \ + ({ \ + Visitor *v_; \ + type *dst_ = (type *) (src); /* Cast away const */ \ + \ + if (dst_) { \ + v_ = qapi_clone_visitor_new(); \ + visit_type_ ## type(v_, NULL, &dst_, &error_abort); \ + visit_free(v_); \ + } \ + dst_; \ + }) /* * Copy deep clones of @type members from @src to @dst. @@ -43,9 +49,14 @@ void qapi_clone_members(void *dst, const void *src, size_t sz, * Not usable on QAPI scalars (integers, strings, enums), nor on a * QAPI object that references the 'any' type. */ -#define QAPI_CLONE_MEMBERS(type, dst, src) \ - qapi_clone_members(dst, src, sizeof(type), \ - (bool (*)(Visitor *, void *, \ - Error **))visit_type_ ## type ## _members) +#define QAPI_CLONE_MEMBERS(type, dst, src) \ + ({ \ + Visitor *v_; \ + \ + v_ = qapi_clone_members_visitor_new(); \ + *(type *)(dst) = *(src); \ + visit_type_ ## type ## _members(v_, (type *)(dst), &error_abort); \ + visit_free(v_); \ + }) #endif diff --git a/include/qemu/lockable.h b/include/qemu/lockable.h index 62110d2eb7..66713bd429 100644 --- a/include/qemu/lockable.h +++ b/include/qemu/lockable.h @@ -43,15 +43,30 @@ qemu_null_lockable(void *x) return NULL; } +#define QML_FUNC_(name) \ + static inline void qemu_lockable_ ## name ## _lock(void *x) \ + { \ + qemu_ ## name ## _lock(x); \ + } \ + static inline void qemu_lockable_ ## name ## _unlock(void *x) \ + { \ + qemu_ ## name ## _unlock(x); \ + } + +QML_FUNC_(mutex) +QML_FUNC_(rec_mutex) +QML_FUNC_(co_mutex) +QML_FUNC_(spin) + /* * In C, compound literals have the lifetime of an automatic variable. * In C++ it would be different, but then C++ wouldn't need QemuLockable * either... */ -#define QML_OBJ_(x, name) (&(QemuLockable) { \ - .object = (x), \ - .lock = (QemuLockUnlockFunc *) qemu_ ## name ## _lock, \ - .unlock = (QemuLockUnlockFunc *) qemu_ ## name ## _unlock \ +#define QML_OBJ_(x, name) (&(QemuLockable) { \ + .object = (x), \ + .lock = qemu_lockable_ ## name ## _lock, \ + .unlock = qemu_lockable_ ## name ## _unlock \ }) /** diff --git a/qapi/qapi-clone-visitor.c b/qapi/qapi-clone-visitor.c index c45c5caa3b..bbf953698f 100644 --- a/qapi/qapi-clone-visitor.c +++ b/qapi/qapi-clone-visitor.c @@ -149,7 +149,7 @@ static void qapi_clone_free(Visitor *v) g_free(v); } -static Visitor *qapi_clone_visitor_new(void) +Visitor *qapi_clone_visitor_new(void) { QapiCloneVisitor *v; @@ -174,31 +174,9 @@ static Visitor *qapi_clone_visitor_new(void) return &v->visitor; } -void *qapi_clone(const void *src, bool (*visit_type)(Visitor *, const char *, - void **, Error **)) +Visitor *qapi_clone_members_visitor_new(void) { - Visitor *v; - void *dst = (void *) src; /* Cast away const */ - - if (!src) { - return NULL; - } - - v = qapi_clone_visitor_new(); - visit_type(v, NULL, &dst, &error_abort); - visit_free(v); - return dst; -} - -void qapi_clone_members(void *dst, const void *src, size_t sz, - bool (*visit_type_members)(Visitor *, void *, - Error **)) -{ - Visitor *v; - - v = qapi_clone_visitor_new(); - memcpy(dst, src, sz); + Visitor *v = qapi_clone_visitor_new(); to_qcv(v)->depth++; - visit_type_members(v, dst, &error_abort); - visit_free(v); + return v; } diff --git a/qemu-keymap.c b/qemu-keymap.c index 8c80f7a4ed..701e4332af 100644 --- a/qemu-keymap.c +++ b/qemu-keymap.c @@ -154,9 +154,9 @@ static xkb_mod_mask_t get_mod(struct xkb_keymap *map, const char *name) int main(int argc, char *argv[]) { - struct xkb_context *ctx; - struct xkb_keymap *map; - struct xkb_state *state; + static struct xkb_context *ctx; + static struct xkb_keymap *map; + static struct xkb_state *state; xkb_mod_index_t mod, mods; int rc; @@ -234,8 +234,6 @@ int main(int argc, char *argv[]) state = xkb_state_new(map); xkb_keymap_key_for_each(map, walk_map, state); - xkb_state_unref(state); - state = NULL; /* add quirks */ fprintf(outfile, diff --git a/scripts/oss-fuzz/build.sh b/scripts/oss-fuzz/build.sh index 5238f83343..7398298173 100755 --- a/scripts/oss-fuzz/build.sh +++ b/scripts/oss-fuzz/build.sh @@ -92,6 +92,7 @@ make install DESTDIR=$DEST_DIR/qemu-bundle rm -rf $DEST_DIR/qemu-bundle/opt/qemu-oss-fuzz/bin rm -rf $DEST_DIR/qemu-bundle/opt/qemu-oss-fuzz/libexec +export ASAN_OPTIONS=detect_leaks=0 targets=$(./qemu-fuzz-i386 | grep generic-fuzz | awk '$1 ~ /\*/ {print $2}') base_copy="$DEST_DIR/qemu-fuzz-i386-target-$(echo "$targets" | head -n 1)" diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 8963c39189..23afe8c08a 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -112,6 +112,7 @@ for arch in $ARCHLIST; do arch_var=ARCH fi + rm -rf "$hdrdir" make -C "$linux" O="$blddir" INSTALL_HDR_PATH="$hdrdir" $arch_var=$arch headers_install rm -rf "$output/linux-headers/asm-$arch" @@ -158,7 +159,7 @@ for arch in $ARCHLIST; do cp_portable "$hdrdir/bootparam.h" \ "$output/include/standard-headers/asm-$arch" cp_portable "$hdrdir/include/asm/setup_data.h" \ - "$output/standard-headers/asm-x86" + "$output/include/standard-headers/asm-x86" fi if [ $arch = riscv ]; then cp "$hdrdir/include/asm/ptrace.h" "$output/linux-headers/asm-riscv/" diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index f7194534ae..2bbeaca36e 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -324,6 +324,42 @@ static void s390_cpu_reset_full(DeviceState *dev) #ifdef CONFIG_TCG #include "hw/core/tcg-cpu-ops.h" +void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags) +{ + uint32_t flags; + + if (env->psw.addr & 1) { + /* + * Instructions must be at even addresses. + * This needs to be checked before address translation. + */ + env->int_pgm_ilen = 2; /* see s390_cpu_tlb_fill() */ + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, 0); + } + + *pc = env->psw.addr; + *cs_base = env->ex_value; + + flags = (env->psw.mask >> FLAG_MASK_PSW_SHIFT) & FLAG_MASK_PSW; + if (env->psw.mask & PSW_MASK_PER) { + flags |= env->cregs[9] & (FLAG_MASK_PER_BRANCH | + FLAG_MASK_PER_IFETCH | + FLAG_MASK_PER_IFETCH_NULLIFY); + if ((env->cregs[9] & PER_CR9_EVENT_STORE) && + (env->cregs[9] & PER_CR9_EVENT_STORE_REAL)) { + flags |= FLAG_MASK_PER_STORE_REAL; + } + } + if (env->cregs[0] & CR0_AFP) { + flags |= FLAG_MASK_AFP; + } + if (env->cregs[0] & CR0_VECTOR) { + flags |= FLAG_MASK_VECTOR; + } + *pflags = flags; +} + static const TCGCPUOps s390_tcg_ops = { .initialize = s390x_translate_init, .restore_state_to_opc = s390x_restore_state_to_opc, diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 414680eed1..d6b75ad0e0 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -342,19 +342,32 @@ extern const VMStateDescription vmstate_s390_cpu; /* tb flags */ -#define FLAG_MASK_PSW_SHIFT 31 -#define FLAG_MASK_PER (PSW_MASK_PER >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_DAT (PSW_MASK_DAT >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_PSTATE (PSW_MASK_PSTATE >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_ASC (PSW_MASK_ASC >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_64 (PSW_MASK_64 >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_32 (PSW_MASK_32 >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_PSW (FLAG_MASK_PER | FLAG_MASK_DAT | FLAG_MASK_PSTATE \ - | FLAG_MASK_ASC | FLAG_MASK_64 | FLAG_MASK_32) - -/* we'll use some unused PSW positions to store CR flags in tb flags */ -#define FLAG_MASK_AFP (PSW_MASK_UNUSED_2 >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_VECTOR (PSW_MASK_UNUSED_3 >> FLAG_MASK_PSW_SHIFT) +#define FLAG_MASK_PSW_SHIFT 31 +#define FLAG_MASK_32 0x00000001u +#define FLAG_MASK_64 0x00000002u +#define FLAG_MASK_AFP 0x00000004u +#define FLAG_MASK_VECTOR 0x00000008u +#define FLAG_MASK_ASC 0x00018000u +#define FLAG_MASK_PSTATE 0x00020000u +#define FLAG_MASK_PER_IFETCH_NULLIFY 0x01000000u +#define FLAG_MASK_DAT 0x08000000u +#define FLAG_MASK_PER_STORE_REAL 0x20000000u +#define FLAG_MASK_PER_IFETCH 0x40000000u +#define FLAG_MASK_PER_BRANCH 0x80000000u + +QEMU_BUILD_BUG_ON(FLAG_MASK_32 != PSW_MASK_32 >> FLAG_MASK_PSW_SHIFT); +QEMU_BUILD_BUG_ON(FLAG_MASK_64 != PSW_MASK_64 >> FLAG_MASK_PSW_SHIFT); +QEMU_BUILD_BUG_ON(FLAG_MASK_ASC != PSW_MASK_ASC >> FLAG_MASK_PSW_SHIFT); +QEMU_BUILD_BUG_ON(FLAG_MASK_PSTATE != PSW_MASK_PSTATE >> FLAG_MASK_PSW_SHIFT); +QEMU_BUILD_BUG_ON(FLAG_MASK_DAT != PSW_MASK_DAT >> FLAG_MASK_PSW_SHIFT); + +#define FLAG_MASK_PSW (FLAG_MASK_DAT | FLAG_MASK_PSTATE | \ + FLAG_MASK_ASC | FLAG_MASK_64 | FLAG_MASK_32) +#define FLAG_MASK_CR9 (FLAG_MASK_PER_BRANCH | FLAG_MASK_PER_IFETCH) +#define FLAG_MASK_PER (FLAG_MASK_PER_BRANCH | \ + FLAG_MASK_PER_IFETCH | \ + FLAG_MASK_PER_IFETCH_NULLIFY | \ + FLAG_MASK_PER_STORE_REAL) /* Control register 0 bits */ #define CR0_LOWPROT 0x0000000010000000ULL @@ -413,38 +426,28 @@ static inline int s390x_env_mmu_index(CPUS390XState *env, bool ifetch) #include "tcg/tcg_s390x.h" -static inline void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - if (env->psw.addr & 1) { - /* - * Instructions must be at even addresses. - * This needs to be checked before address translation. - */ - env->int_pgm_ilen = 2; /* see s390_cpu_tlb_fill() */ - tcg_s390_program_interrupt(env, PGM_SPECIFICATION, 0); - } - *pc = env->psw.addr; - *cs_base = env->ex_value; - *flags = (env->psw.mask >> FLAG_MASK_PSW_SHIFT) & FLAG_MASK_PSW; - if (env->cregs[0] & CR0_AFP) { - *flags |= FLAG_MASK_AFP; - } - if (env->cregs[0] & CR0_VECTOR) { - *flags |= FLAG_MASK_VECTOR; - } -} +void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags); #endif /* CONFIG_TCG */ /* PER bits from control register 9 */ -#define PER_CR9_EVENT_BRANCH 0x80000000 -#define PER_CR9_EVENT_IFETCH 0x40000000 -#define PER_CR9_EVENT_STORE 0x20000000 -#define PER_CR9_EVENT_STORE_REAL 0x08000000 -#define PER_CR9_EVENT_NULLIFICATION 0x01000000 -#define PER_CR9_CONTROL_BRANCH_ADDRESS 0x00800000 -#define PER_CR9_CONTROL_ALTERATION 0x00200000 +#define PER_CR9_EVENT_BRANCH 0x80000000 +#define PER_CR9_EVENT_IFETCH 0x40000000 +#define PER_CR9_EVENT_STORE 0x20000000 +#define PER_CR9_EVENT_STORAGE_KEY_ALTERATION 0x10000000 +#define PER_CR9_EVENT_STORE_REAL 0x08000000 +#define PER_CR9_EVENT_ZERO_ADDRESS_DETECTION 0x04000000 +#define PER_CR9_EVENT_TRANSACTION_END 0x02000000 +#define PER_CR9_EVENT_IFETCH_NULLIFICATION 0x01000000 +#define PER_CR9_CONTROL_BRANCH_ADDRESS 0x00800000 +#define PER_CR9_CONTROL_TRANSACTION_SUPRESS 0x00400000 +#define PER_CR9_CONTROL_STORAGE_ALTERATION 0x00200000 + +QEMU_BUILD_BUG_ON(FLAG_MASK_PER_BRANCH != PER_CR9_EVENT_BRANCH); +QEMU_BUILD_BUG_ON(FLAG_MASK_PER_IFETCH != PER_CR9_EVENT_IFETCH); +QEMU_BUILD_BUG_ON(FLAG_MASK_PER_IFETCH_NULLIFY != + PER_CR9_EVENT_IFETCH_NULLIFICATION); /* PER bits from the PER CODE/ATMID/AI in lowcore */ #define PER_CODE_EVENT_BRANCH 0x8000 diff --git a/target/s390x/helper.h b/target/s390x/helper.h index cc1c20e9e3..1a8a76abb9 100644 --- a/target/s390x/helper.h +++ b/target/s390x/helper.h @@ -359,10 +359,10 @@ 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_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) -DEF_HELPER_FLAGS_1(per_store_real, TCG_CALL_NO_RWG, void, env) +DEF_HELPER_FLAGS_1(per_check_exception, TCG_CALL_NO_WG, void, env) +DEF_HELPER_FLAGS_3(per_branch, TCG_CALL_NO_WG, void, env, i64, i32) +DEF_HELPER_FLAGS_2(per_ifetch, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_FLAGS_2(per_store_real, TCG_CALL_NO_WG, noreturn, env, i32) DEF_HELPER_FLAGS_1(stfl, TCG_CALL_NO_RWG, void, env) DEF_HELPER_2(xsch, void, env, i64) diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c index f1c33f7967..4c0b692c9e 100644 --- a/target/s390x/tcg/excp_helper.c +++ b/target/s390x/tcg/excp_helper.c @@ -209,7 +209,7 @@ static void do_program_interrupt(CPUS390XState *env) switch (env->int_pgm_code) { case PGM_PER: - advance = !(env->per_perc_atmid & PER_CODE_EVENT_NULLIFICATION); + /* advance already handled */ break; case PGM_ASCE_TYPE: case PGM_REG_FIRST_TRANS: diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index 8764846ce8..303f86d363 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" +#include "qemu/log.h" #include "cpu.h" #include "s390x-internal.h" #include "qemu/host-utils.h" @@ -590,10 +591,24 @@ void HELPER(chsc)(CPUS390XState *env, uint64_t inst) #endif #ifndef CONFIG_USER_ONLY +static G_NORETURN void per_raise_exception(CPUS390XState *env) +{ + trigger_pgm_exception(env, PGM_PER); + cpu_loop_exit(env_cpu(env)); +} + +static G_NORETURN void per_raise_exception_log(CPUS390XState *env) +{ + qemu_log_mask(CPU_LOG_INT, "PER interrupt after 0x%" PRIx64 "\n", + env->per_address); + per_raise_exception(env); +} + void HELPER(per_check_exception)(CPUS390XState *env) { - if (env->per_perc_atmid) { - tcg_s390_program_interrupt(env, PGM_PER, GETPC()); + /* psw_addr, per_address and int_pgm_ilen are already set. */ + if (unlikely(env->per_perc_atmid)) { + per_raise_exception_log(env); } } @@ -608,46 +623,45 @@ static inline bool get_per_in_range(CPUS390XState *env, uint64_t addr) } } -void HELPER(per_branch)(CPUS390XState *env, uint64_t from, uint64_t to) +void HELPER(per_branch)(CPUS390XState *env, uint64_t dest, uint32_t ilen) { - if ((env->cregs[9] & PER_CR9_EVENT_BRANCH)) { - if (!(env->cregs[9] & PER_CR9_CONTROL_BRANCH_ADDRESS) - || get_per_in_range(env, to)) { - env->per_address = from; - env->per_perc_atmid = PER_CODE_EVENT_BRANCH | get_per_atmid(env); - } + if ((env->cregs[9] & PER_CR9_CONTROL_BRANCH_ADDRESS) + && !get_per_in_range(env, dest)) { + return; } + + env->psw.addr = dest; + env->int_pgm_ilen = ilen; + env->per_address = env->gbea; + env->per_perc_atmid = PER_CODE_EVENT_BRANCH | get_per_atmid(env); + per_raise_exception_log(env); } -void HELPER(per_ifetch)(CPUS390XState *env, uint64_t addr) +void HELPER(per_ifetch)(CPUS390XState *env, uint32_t ilen) { - if ((env->cregs[9] & PER_CR9_EVENT_IFETCH) && get_per_in_range(env, addr)) { - env->per_address = addr; + if (get_per_in_range(env, env->psw.addr)) { + env->per_address = env->psw.addr; + env->int_pgm_ilen = ilen; env->per_perc_atmid = PER_CODE_EVENT_IFETCH | get_per_atmid(env); /* If the instruction has to be nullified, trigger the exception immediately. */ - if (env->cregs[9] & PER_CR9_EVENT_NULLIFICATION) { - CPUState *cs = env_cpu(env); - + if (env->cregs[9] & PER_CR9_EVENT_IFETCH_NULLIFICATION) { env->per_perc_atmid |= PER_CODE_EVENT_NULLIFICATION; - env->int_pgm_code = PGM_PER; - env->int_pgm_ilen = get_ilen(cpu_ldub_code(env, addr)); - - cs->exception_index = EXCP_PGM; - cpu_loop_exit(cs); + qemu_log_mask(CPU_LOG_INT, "PER interrupt before 0x%" PRIx64 "\n", + env->per_address); + per_raise_exception(env); } } } -void HELPER(per_store_real)(CPUS390XState *env) +void HELPER(per_store_real)(CPUS390XState *env, uint32_t ilen) { - if ((env->cregs[9] & PER_CR9_EVENT_STORE) && - (env->cregs[9] & PER_CR9_EVENT_STORE_REAL)) { - /* PSW is saved just before calling the helper. */ - env->per_address = env->psw.addr; - env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env); - } + /* PSW is saved just before calling the helper. */ + env->per_address = env->psw.addr; + env->int_pgm_ilen = ilen; + env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env); + per_raise_exception_log(env); } #endif diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index ebd96abe6c..c81e035dea 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -341,33 +341,11 @@ static void update_psw_addr(DisasContext *s) tcg_gen_movi_i64(psw_addr, s->base.pc_next); } -static void per_branch(DisasContext *s, bool to_next) +static void per_branch(DisasContext *s, TCGv_i64 dest) { #ifndef CONFIG_USER_ONLY - tcg_gen_movi_i64(gbea, s->base.pc_next); - - if (s->base.tb->flags & FLAG_MASK_PER) { - TCGv_i64 next_pc = to_next ? tcg_constant_i64(s->pc_tmp) : psw_addr; - gen_helper_per_branch(tcg_env, gbea, next_pc); - } -#endif -} - -static void per_branch_cond(DisasContext *s, TCGCond cond, - TCGv_i64 arg1, TCGv_i64 arg2) -{ -#ifndef CONFIG_USER_ONLY - if (s->base.tb->flags & FLAG_MASK_PER) { - TCGLabel *lab = gen_new_label(); - tcg_gen_brcond_i64(tcg_invert_cond(cond), arg1, arg2, lab); - - tcg_gen_movi_i64(gbea, s->base.pc_next); - gen_helper_per_branch(tcg_env, gbea, psw_addr); - - gen_set_label(lab); - } else { - TCGv_i64 pc = tcg_constant_i64(s->base.pc_next); - tcg_gen_movcond_i64(cond, gbea, arg1, arg2, gbea, pc); + if (s->base.tb->flags & FLAG_MASK_PER_BRANCH) { + gen_helper_per_branch(tcg_env, dest, tcg_constant_i32(s->ilen)); } #endif } @@ -656,9 +634,6 @@ static void gen_op_calc_cc(DisasContext *s) static bool use_goto_tb(DisasContext *s, uint64_t dest) { - if (unlikely(s->base.tb->flags & FLAG_MASK_PER)) { - return false; - } return translator_use_goto_tb(&s->base, dest); } @@ -1100,144 +1075,105 @@ struct DisasInsn { static DisasJumpType help_goto_direct(DisasContext *s, uint64_t dest) { + update_cc_op(s); + per_breaking_event(s); + per_branch(s, tcg_constant_i64(dest)); + if (dest == s->pc_tmp) { - per_branch(s, true); return DISAS_NEXT; } if (use_goto_tb(s, dest)) { - update_cc_op(s); - per_breaking_event(s); tcg_gen_goto_tb(0); tcg_gen_movi_i64(psw_addr, dest); tcg_gen_exit_tb(s->base.tb, 0); return DISAS_NORETURN; } else { tcg_gen_movi_i64(psw_addr, dest); - per_branch(s, false); - return DISAS_PC_UPDATED; + return DISAS_PC_CC_UPDATED; } } +static DisasJumpType help_goto_indirect(DisasContext *s, TCGv_i64 dest) +{ + update_cc_op(s); + per_breaking_event(s); + tcg_gen_mov_i64(psw_addr, dest); + per_branch(s, psw_addr); + return DISAS_PC_CC_UPDATED; +} + static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, bool is_imm, int imm, TCGv_i64 cdest) { - DisasJumpType ret; uint64_t dest = s->base.pc_next + (int64_t)imm * 2; TCGLabel *lab; /* Take care of the special cases first. */ if (c->cond == TCG_COND_NEVER) { - ret = DISAS_NEXT; - goto egress; + return DISAS_NEXT; } if (is_imm) { - if (dest == s->pc_tmp) { - /* Branch to next. */ - per_branch(s, true); - ret = DISAS_NEXT; - goto egress; - } - if (c->cond == TCG_COND_ALWAYS) { - ret = help_goto_direct(s, dest); - goto egress; + /* + * Do not optimize a conditional branch if PER enabled, because we + * still need a conditional call to helper_per_branch. + */ + if (c->cond == TCG_COND_ALWAYS + || (dest == s->pc_tmp && + !(s->base.tb->flags & FLAG_MASK_PER_BRANCH))) { + return help_goto_direct(s, dest); } } else { if (!cdest) { /* E.g. bcr %r0 -> no branch. */ - ret = DISAS_NEXT; - goto egress; + return DISAS_NEXT; } if (c->cond == TCG_COND_ALWAYS) { - tcg_gen_mov_i64(psw_addr, cdest); - per_branch(s, false); - ret = DISAS_PC_UPDATED; - goto egress; + return help_goto_indirect(s, cdest); } } - if (use_goto_tb(s, s->pc_tmp)) { - if (is_imm && use_goto_tb(s, dest)) { - /* Both exits can use goto_tb. */ - update_cc_op(s); - - lab = gen_new_label(); - if (c->is_64) { - tcg_gen_brcond_i64(c->cond, c->u.s64.a, c->u.s64.b, lab); - } else { - tcg_gen_brcond_i32(c->cond, c->u.s32.a, c->u.s32.b, lab); - } - - /* Branch not taken. */ - tcg_gen_goto_tb(0); - tcg_gen_movi_i64(psw_addr, s->pc_tmp); - tcg_gen_exit_tb(s->base.tb, 0); - - /* Branch taken. */ - gen_set_label(lab); - per_breaking_event(s); - tcg_gen_goto_tb(1); - tcg_gen_movi_i64(psw_addr, dest); - tcg_gen_exit_tb(s->base.tb, 1); - - ret = DISAS_NORETURN; - } else { - /* Fallthru can use goto_tb, but taken branch cannot. */ - /* Store taken branch destination before the brcond. This - avoids having to allocate a new local temp to hold it. - We'll overwrite this in the not taken case anyway. */ - if (!is_imm) { - tcg_gen_mov_i64(psw_addr, cdest); - } - - lab = gen_new_label(); - if (c->is_64) { - tcg_gen_brcond_i64(c->cond, c->u.s64.a, c->u.s64.b, lab); - } else { - tcg_gen_brcond_i32(c->cond, c->u.s32.a, c->u.s32.b, lab); - } + update_cc_op(s); - /* Branch not taken. */ - update_cc_op(s); - tcg_gen_goto_tb(0); - tcg_gen_movi_i64(psw_addr, s->pc_tmp); - tcg_gen_exit_tb(s->base.tb, 0); + /* + * Ensure the taken branch is fall-through of the tcg branch. + * This keeps @cdest usage within the extended basic block, + * which avoids an otherwise unnecessary spill to the stack. + */ + lab = gen_new_label(); + if (c->is_64) { + tcg_gen_brcond_i64(tcg_invert_cond(c->cond), + c->u.s64.a, c->u.s64.b, lab); + } else { + tcg_gen_brcond_i32(tcg_invert_cond(c->cond), + c->u.s32.a, c->u.s32.b, lab); + } - gen_set_label(lab); - if (is_imm) { - tcg_gen_movi_i64(psw_addr, dest); - } - per_breaking_event(s); - ret = DISAS_PC_UPDATED; - } + /* Branch taken. */ + per_breaking_event(s); + if (is_imm) { + tcg_gen_movi_i64(psw_addr, dest); } else { - /* Fallthru cannot use goto_tb. This by itself is vanishingly rare. - Most commonly we're single-stepping or some other condition that - disables all use of goto_tb. Just update the PC and exit. */ + tcg_gen_mov_i64(psw_addr, cdest); + } + per_branch(s, psw_addr); - TCGv_i64 next = tcg_constant_i64(s->pc_tmp); - if (is_imm) { - cdest = tcg_constant_i64(dest); - } + if (is_imm && use_goto_tb(s, dest)) { + tcg_gen_goto_tb(0); + tcg_gen_exit_tb(s->base.tb, 0); + } else { + tcg_gen_lookup_and_goto_ptr(); + } - if (c->is_64) { - tcg_gen_movcond_i64(c->cond, psw_addr, c->u.s64.a, c->u.s64.b, - cdest, next); - per_branch_cond(s, c->cond, c->u.s64.a, c->u.s64.b); - } else { - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 z = tcg_constant_i64(0); - tcg_gen_setcond_i32(c->cond, t0, c->u.s32.a, c->u.s32.b); - tcg_gen_extu_i32_i64(t1, t0); - tcg_gen_movcond_i64(TCG_COND_NE, psw_addr, t1, z, cdest, next); - per_branch_cond(s, TCG_COND_NE, t1, z); - } + gen_set_label(lab); - ret = DISAS_PC_UPDATED; + /* Branch not taken. */ + tcg_gen_movi_i64(psw_addr, s->pc_tmp); + if (use_goto_tb(s, s->pc_tmp)) { + tcg_gen_goto_tb(1); + tcg_gen_exit_tb(s->base.tb, 1); + return DISAS_NORETURN; } - - egress: - return ret; + return DISAS_PC_CC_UPDATED; } /* ====================================================================== */ @@ -1463,9 +1399,7 @@ static DisasJumpType op_bas(DisasContext *s, DisasOps *o) { pc_to_link_info(o->out, s, s->pc_tmp); if (o->in2) { - tcg_gen_mov_i64(psw_addr, o->in2); - per_branch(s, false); - return DISAS_PC_UPDATED; + return help_goto_indirect(s, o->in2); } else { return DISAS_NEXT; } @@ -1495,9 +1429,7 @@ static DisasJumpType op_bal(DisasContext *s, DisasOps *o) { save_link_info(s, o); if (o->in2) { - tcg_gen_mov_i64(psw_addr, o->in2); - per_branch(s, false); - return DISAS_PC_UPDATED; + return help_goto_indirect(s, o->in2); } else { return DISAS_NEXT; } @@ -4409,9 +4341,11 @@ static DisasJumpType op_stura(DisasContext *s, DisasOps *o) { tcg_gen_qemu_st_tl(o->in1, o->in2, MMU_REAL_IDX, s->insn->data); - if (s->base.tb->flags & FLAG_MASK_PER) { + if (s->base.tb->flags & FLAG_MASK_PER_STORE_REAL) { + update_cc_op(s); update_psw_addr(s); - gen_helper_per_store_real(tcg_env); + gen_helper_per_store_real(tcg_env, tcg_constant_i32(s->ilen)); + return DISAS_NORETURN; } return DISAS_NEXT; } @@ -6323,9 +6257,9 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) } #ifndef CONFIG_USER_ONLY - if (s->base.tb->flags & FLAG_MASK_PER) { - TCGv_i64 addr = tcg_constant_i64(s->base.pc_next); - gen_helper_per_ifetch(tcg_env, addr); + if (s->base.tb->flags & FLAG_MASK_PER_IFETCH) { + /* With ifetch set, psw_addr and cc_op are always up-to-date. */ + gen_helper_per_ifetch(tcg_env, tcg_constant_i32(s->ilen)); } #endif @@ -6407,15 +6341,16 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) } if (insn->help_op) { ret = insn->help_op(s, &o); - } - if (ret != DISAS_NORETURN) { - if (insn->help_wout) { - insn->help_wout(s, &o); - } - if (insn->help_cout) { - insn->help_cout(s, &o); + if (ret == DISAS_NORETURN) { + goto out; } } + if (insn->help_wout) { + insn->help_wout(s, &o); + } + if (insn->help_cout) { + insn->help_cout(s, &o); + } /* io should be the last instruction in tb when icount is enabled */ if (unlikely(icount && ret == DISAS_NEXT)) { @@ -6423,13 +6358,18 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) } #ifndef CONFIG_USER_ONLY - if (s->base.tb->flags & FLAG_MASK_PER) { - /* An exception might be triggered, save PSW if not already done. */ - if (ret == DISAS_NEXT || ret == DISAS_TOO_MANY) { + if (s->base.tb->flags & FLAG_MASK_PER_IFETCH) { + switch (ret) { + case DISAS_TOO_MANY: + s->base.is_jmp = DISAS_PC_CC_UPDATED; + /* fall through */ + case DISAS_NEXT: tcg_gen_movi_i64(psw_addr, s->pc_tmp); + break; + default: + break; } - - /* Call the helper to check for a possible PER exception. */ + update_cc_op(s); gen_helper_per_check_exception(tcg_env); } #endif @@ -6452,7 +6392,7 @@ static void s390x_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->cc_op = CC_OP_DYNAMIC; dc->ex_value = dc->base.tb->cs_base; - dc->exit_to_mainloop = (dc->base.tb->flags & FLAG_MASK_PER) || dc->ex_value; + dc->exit_to_mainloop = dc->ex_value; } static void s390x_tr_tb_start(DisasContextBase *db, CPUState *cs) diff --git a/tests/qtest/fuzz/generic_fuzz_configs.h b/tests/qtest/fuzz/generic_fuzz_configs.h index 4d7c8ca4ec..ef0ad95712 100644 --- a/tests/qtest/fuzz/generic_fuzz_configs.h +++ b/tests/qtest/fuzz/generic_fuzz_configs.h @@ -150,7 +150,8 @@ const generic_fuzz_config predefined_configs[] = { "-chardev null,id=cd0 -chardev null,id=cd1 " "-device usb-braille,chardev=cd0 -device usb-ccid -device usb-ccid " "-device usb-kbd -device usb-mouse -device usb-serial,chardev=cd1 " - "-device usb-tablet -device usb-wacom-tablet -device usb-audio", + "-device usb-tablet -device usb-wacom-tablet " + "-device usb-audio,audiodev=snd0 -audiodev none,id=snd0", .objects = "*usb* *uhci* *xhci*", },{ .name = "pc-i440fx", diff --git a/tests/tcg/s390x/Makefile.softmmu-target b/tests/tcg/s390x/Makefile.softmmu-target index 1a1f088b28..80159cccf5 100644 --- a/tests/tcg/s390x/Makefile.softmmu-target +++ b/tests/tcg/s390x/Makefile.softmmu-target @@ -25,6 +25,7 @@ ASM_TESTS = \ lpswe-early \ lra \ mc \ + per \ precise-smc-softmmu \ ssm-early \ stosm-early \ diff --git a/tests/tcg/s390x/per.S b/tests/tcg/s390x/per.S new file mode 100644 index 0000000000..79e704a6ff --- /dev/null +++ b/tests/tcg/s390x/per.S @@ -0,0 +1,82 @@ + .org 0x8d +ilc: + .org 0x8e +program_interruption_code: + .org 0x96 +per_code: + .org 0x98 +per_address: + .org 0x150 +program_old_psw: + .org 0x1d0 +program_new_psw: + .quad 0, pgm_handler + + .org 0x200 /* exit lowcore */ + +per_on_psw: + .quad 0x4000000000000000, start_per +per_on_regs: + .quad 0x80000000, 0, -1 /* successful-branching everywhere */ +per_off_regs: + .quad 0, 0 ,0 +success_psw: + .quad 0x2000000000000, 0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000, 0 /* disabled wait */ + + .org 0x2000 /* exit lowcore pages */ + + .globl _start +_start: + lpswe per_on_psw +start_per: + lctlg %c9, %c11, per_on_regs + +/* Test unconditional relative branch. */ + larl %r0, j1 + larl %r1, d1 + lhi %r2, 0 +j1: j d1 + lpswe failure_psw +d1: + +/* Test unconditional indirect branch. */ + larl %r0, j2 + larl %r1, d2 +j2: br %r1 + lpswe failure_psw +d2: + +/* Test conditional relative branch. */ + larl %r0, j3 + larl %r1, d3 + clr %r1, %r2 /* d3 != 0 */ +j3: jne d3 + lpswe failure_psw +d3: + +/* Test conditional register branch. */ + larl %r0, j4 + larl %r1, d4 + clr %r1, %r2 /* d4 != 0 */ +j4: bner %r1 + lpswe failure_psw +d4: + +/* Success! */ + nop + lpswe success_psw + +pgm_handler: + chhsi program_interruption_code, 0x80 /* PER event? */ + jne fail + cli per_code, 0x80 /* successful-branching event? */ + jne fail + clg %r0, per_address /* per_address == jump insn? */ + jne fail + clg %r1, program_old_psw+8 /* psw.addr updated to dest? */ + jne fail + lpswe program_old_psw +fail: + lpswe failure_psw |