* 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----- 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>
This commit is contained in:
commit
3b2fe44bb7
@ -35,9 +35,6 @@ typedef struct AdapterRoutes {
|
|||||||
|
|
||||||
extern const VMStateDescription vmstate_adapter_routes;
|
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"
|
#define TYPE_S390_FLIC_COMMON "s390-flic"
|
||||||
OBJECT_DECLARE_TYPE(S390FLICState, S390FLICStateClass,
|
OBJECT_DECLARE_TYPE(S390FLICState, S390FLICStateClass,
|
||||||
S390_FLIC_COMMON)
|
S390_FLIC_COMMON)
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#ifndef QAPI_CLONE_VISITOR_H
|
#ifndef QAPI_CLONE_VISITOR_H
|
||||||
#define QAPI_CLONE_VISITOR_H
|
#define QAPI_CLONE_VISITOR_H
|
||||||
|
|
||||||
|
#include "qapi/error.h"
|
||||||
#include "qapi/visitor.h"
|
#include "qapi/visitor.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -20,11 +21,8 @@
|
|||||||
*/
|
*/
|
||||||
typedef struct QapiCloneVisitor QapiCloneVisitor;
|
typedef struct QapiCloneVisitor QapiCloneVisitor;
|
||||||
|
|
||||||
void *qapi_clone(const void *src, bool (*visit_type)(Visitor *, const char *,
|
Visitor *qapi_clone_visitor_new(void);
|
||||||
void **, Error **));
|
Visitor *qapi_clone_members_visitor_new(void);
|
||||||
void qapi_clone_members(void *dst, const void *src, size_t sz,
|
|
||||||
bool (*visit_type_members)(Visitor *, void *,
|
|
||||||
Error **));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Deep-clone QAPI object @src of the given @type, and return the result.
|
* Deep-clone QAPI object @src of the given @type, and return the result.
|
||||||
@ -33,9 +31,17 @@ void qapi_clone_members(void *dst, const void *src, size_t sz,
|
|||||||
* QAPI object that references the 'any' type. Safe when @src is NULL.
|
* QAPI object that references the 'any' type. Safe when @src is NULL.
|
||||||
*/
|
*/
|
||||||
#define QAPI_CLONE(type, src) \
|
#define QAPI_CLONE(type, src) \
|
||||||
((type *)qapi_clone(src, \
|
({ \
|
||||||
(bool (*)(Visitor *, const char *, void **, \
|
Visitor *v_; \
|
||||||
Error **))visit_type_ ## type))
|
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.
|
* Copy deep clones of @type members from @src to @dst.
|
||||||
@ -44,8 +50,13 @@ void qapi_clone_members(void *dst, const void *src, size_t sz,
|
|||||||
* QAPI object that references the 'any' type.
|
* QAPI object that references the 'any' type.
|
||||||
*/
|
*/
|
||||||
#define QAPI_CLONE_MEMBERS(type, dst, src) \
|
#define QAPI_CLONE_MEMBERS(type, dst, src) \
|
||||||
qapi_clone_members(dst, src, sizeof(type), \
|
({ \
|
||||||
(bool (*)(Visitor *, void *, \
|
Visitor *v_; \
|
||||||
Error **))visit_type_ ## type ## _members)
|
\
|
||||||
|
v_ = qapi_clone_members_visitor_new(); \
|
||||||
|
*(type *)(dst) = *(src); \
|
||||||
|
visit_type_ ## type ## _members(v_, (type *)(dst), &error_abort); \
|
||||||
|
visit_free(v_); \
|
||||||
|
})
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -43,6 +43,21 @@ qemu_null_lockable(void *x)
|
|||||||
return NULL;
|
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, compound literals have the lifetime of an automatic variable.
|
||||||
* In C++ it would be different, but then C++ wouldn't need QemuLockable
|
* In C++ it would be different, but then C++ wouldn't need QemuLockable
|
||||||
@ -50,8 +65,8 @@ qemu_null_lockable(void *x)
|
|||||||
*/
|
*/
|
||||||
#define QML_OBJ_(x, name) (&(QemuLockable) { \
|
#define QML_OBJ_(x, name) (&(QemuLockable) { \
|
||||||
.object = (x), \
|
.object = (x), \
|
||||||
.lock = (QemuLockUnlockFunc *) qemu_ ## name ## _lock, \
|
.lock = qemu_lockable_ ## name ## _lock, \
|
||||||
.unlock = (QemuLockUnlockFunc *) qemu_ ## name ## _unlock \
|
.unlock = qemu_lockable_ ## name ## _unlock \
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -149,7 +149,7 @@ static void qapi_clone_free(Visitor *v)
|
|||||||
g_free(v);
|
g_free(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Visitor *qapi_clone_visitor_new(void)
|
Visitor *qapi_clone_visitor_new(void)
|
||||||
{
|
{
|
||||||
QapiCloneVisitor *v;
|
QapiCloneVisitor *v;
|
||||||
|
|
||||||
@ -174,31 +174,9 @@ static Visitor *qapi_clone_visitor_new(void)
|
|||||||
return &v->visitor;
|
return &v->visitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *qapi_clone(const void *src, bool (*visit_type)(Visitor *, const char *,
|
Visitor *qapi_clone_members_visitor_new(void)
|
||||||
void **, Error **))
|
|
||||||
{
|
{
|
||||||
Visitor *v;
|
Visitor *v = qapi_clone_visitor_new();
|
||||||
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);
|
|
||||||
to_qcv(v)->depth++;
|
to_qcv(v)->depth++;
|
||||||
visit_type_members(v, dst, &error_abort);
|
return v;
|
||||||
visit_free(v);
|
|
||||||
}
|
}
|
||||||
|
@ -154,9 +154,9 @@ static xkb_mod_mask_t get_mod(struct xkb_keymap *map, const char *name)
|
|||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct xkb_context *ctx;
|
static struct xkb_context *ctx;
|
||||||
struct xkb_keymap *map;
|
static struct xkb_keymap *map;
|
||||||
struct xkb_state *state;
|
static struct xkb_state *state;
|
||||||
xkb_mod_index_t mod, mods;
|
xkb_mod_index_t mod, mods;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
@ -234,8 +234,6 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
state = xkb_state_new(map);
|
state = xkb_state_new(map);
|
||||||
xkb_keymap_key_for_each(map, walk_map, state);
|
xkb_keymap_key_for_each(map, walk_map, state);
|
||||||
xkb_state_unref(state);
|
|
||||||
state = NULL;
|
|
||||||
|
|
||||||
/* add quirks */
|
/* add quirks */
|
||||||
fprintf(outfile,
|
fprintf(outfile,
|
||||||
|
@ -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/bin
|
||||||
rm -rf $DEST_DIR/qemu-bundle/opt/qemu-oss-fuzz/libexec
|
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}')
|
targets=$(./qemu-fuzz-i386 | grep generic-fuzz | awk '$1 ~ /\*/ {print $2}')
|
||||||
base_copy="$DEST_DIR/qemu-fuzz-i386-target-$(echo "$targets" | head -n 1)"
|
base_copy="$DEST_DIR/qemu-fuzz-i386-target-$(echo "$targets" | head -n 1)"
|
||||||
|
|
||||||
|
@ -112,6 +112,7 @@ for arch in $ARCHLIST; do
|
|||||||
arch_var=ARCH
|
arch_var=ARCH
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
rm -rf "$hdrdir"
|
||||||
make -C "$linux" O="$blddir" INSTALL_HDR_PATH="$hdrdir" $arch_var=$arch headers_install
|
make -C "$linux" O="$blddir" INSTALL_HDR_PATH="$hdrdir" $arch_var=$arch headers_install
|
||||||
|
|
||||||
rm -rf "$output/linux-headers/asm-$arch"
|
rm -rf "$output/linux-headers/asm-$arch"
|
||||||
@ -158,7 +159,7 @@ for arch in $ARCHLIST; do
|
|||||||
cp_portable "$hdrdir/bootparam.h" \
|
cp_portable "$hdrdir/bootparam.h" \
|
||||||
"$output/include/standard-headers/asm-$arch"
|
"$output/include/standard-headers/asm-$arch"
|
||||||
cp_portable "$hdrdir/include/asm/setup_data.h" \
|
cp_portable "$hdrdir/include/asm/setup_data.h" \
|
||||||
"$output/standard-headers/asm-x86"
|
"$output/include/standard-headers/asm-x86"
|
||||||
fi
|
fi
|
||||||
if [ $arch = riscv ]; then
|
if [ $arch = riscv ]; then
|
||||||
cp "$hdrdir/include/asm/ptrace.h" "$output/linux-headers/asm-riscv/"
|
cp "$hdrdir/include/asm/ptrace.h" "$output/linux-headers/asm-riscv/"
|
||||||
|
@ -324,6 +324,42 @@ static void s390_cpu_reset_full(DeviceState *dev)
|
|||||||
#ifdef CONFIG_TCG
|
#ifdef CONFIG_TCG
|
||||||
#include "hw/core/tcg-cpu-ops.h"
|
#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 = {
|
static const TCGCPUOps s390_tcg_ops = {
|
||||||
.initialize = s390x_translate_init,
|
.initialize = s390x_translate_init,
|
||||||
.restore_state_to_opc = s390x_restore_state_to_opc,
|
.restore_state_to_opc = s390x_restore_state_to_opc,
|
||||||
|
@ -343,18 +343,31 @@ extern const VMStateDescription vmstate_s390_cpu;
|
|||||||
/* tb flags */
|
/* tb flags */
|
||||||
|
|
||||||
#define FLAG_MASK_PSW_SHIFT 31
|
#define FLAG_MASK_PSW_SHIFT 31
|
||||||
#define FLAG_MASK_PER (PSW_MASK_PER >> FLAG_MASK_PSW_SHIFT)
|
#define FLAG_MASK_32 0x00000001u
|
||||||
#define FLAG_MASK_DAT (PSW_MASK_DAT >> FLAG_MASK_PSW_SHIFT)
|
#define FLAG_MASK_64 0x00000002u
|
||||||
#define FLAG_MASK_PSTATE (PSW_MASK_PSTATE >> FLAG_MASK_PSW_SHIFT)
|
#define FLAG_MASK_AFP 0x00000004u
|
||||||
#define FLAG_MASK_ASC (PSW_MASK_ASC >> FLAG_MASK_PSW_SHIFT)
|
#define FLAG_MASK_VECTOR 0x00000008u
|
||||||
#define FLAG_MASK_64 (PSW_MASK_64 >> FLAG_MASK_PSW_SHIFT)
|
#define FLAG_MASK_ASC 0x00018000u
|
||||||
#define FLAG_MASK_32 (PSW_MASK_32 >> FLAG_MASK_PSW_SHIFT)
|
#define FLAG_MASK_PSTATE 0x00020000u
|
||||||
#define FLAG_MASK_PSW (FLAG_MASK_PER | FLAG_MASK_DAT | FLAG_MASK_PSTATE \
|
#define FLAG_MASK_PER_IFETCH_NULLIFY 0x01000000u
|
||||||
| FLAG_MASK_ASC | FLAG_MASK_64 | FLAG_MASK_32)
|
#define FLAG_MASK_DAT 0x08000000u
|
||||||
|
#define FLAG_MASK_PER_STORE_REAL 0x20000000u
|
||||||
|
#define FLAG_MASK_PER_IFETCH 0x40000000u
|
||||||
|
#define FLAG_MASK_PER_BRANCH 0x80000000u
|
||||||
|
|
||||||
/* we'll use some unused PSW positions to store CR flags in tb flags */
|
QEMU_BUILD_BUG_ON(FLAG_MASK_32 != PSW_MASK_32 >> FLAG_MASK_PSW_SHIFT);
|
||||||
#define FLAG_MASK_AFP (PSW_MASK_UNUSED_2 >> FLAG_MASK_PSW_SHIFT)
|
QEMU_BUILD_BUG_ON(FLAG_MASK_64 != PSW_MASK_64 >> FLAG_MASK_PSW_SHIFT);
|
||||||
#define FLAG_MASK_VECTOR (PSW_MASK_UNUSED_3 >> 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 */
|
/* Control register 0 bits */
|
||||||
#define CR0_LOWPROT 0x0000000010000000ULL
|
#define CR0_LOWPROT 0x0000000010000000ULL
|
||||||
@ -413,27 +426,8 @@ static inline int s390x_env_mmu_index(CPUS390XState *env, bool ifetch)
|
|||||||
|
|
||||||
#include "tcg/tcg_s390x.h"
|
#include "tcg/tcg_s390x.h"
|
||||||
|
|
||||||
static inline void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc,
|
void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc,
|
||||||
uint64_t *cs_base, uint32_t *flags)
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* CONFIG_TCG */
|
#endif /* CONFIG_TCG */
|
||||||
|
|
||||||
@ -441,10 +435,19 @@ static inline void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc,
|
|||||||
#define PER_CR9_EVENT_BRANCH 0x80000000
|
#define PER_CR9_EVENT_BRANCH 0x80000000
|
||||||
#define PER_CR9_EVENT_IFETCH 0x40000000
|
#define PER_CR9_EVENT_IFETCH 0x40000000
|
||||||
#define PER_CR9_EVENT_STORE 0x20000000
|
#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_STORE_REAL 0x08000000
|
||||||
#define PER_CR9_EVENT_NULLIFICATION 0x01000000
|
#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_BRANCH_ADDRESS 0x00800000
|
||||||
#define PER_CR9_CONTROL_ALTERATION 0x00200000
|
#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 */
|
/* PER bits from the PER CODE/ATMID/AI in lowcore */
|
||||||
#define PER_CODE_EVENT_BRANCH 0x8000
|
#define PER_CODE_EVENT_BRANCH 0x8000
|
||||||
|
@ -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(ptlb, TCG_CALL_NO_RWG, void, env)
|
||||||
DEF_HELPER_FLAGS_1(purge, 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_3(lra, i64, env, i64, i64)
|
||||||
DEF_HELPER_1(per_check_exception, void, env)
|
DEF_HELPER_FLAGS_1(per_check_exception, TCG_CALL_NO_WG, void, env)
|
||||||
DEF_HELPER_FLAGS_3(per_branch, TCG_CALL_NO_RWG, void, env, i64, i64)
|
DEF_HELPER_FLAGS_3(per_branch, TCG_CALL_NO_WG, void, env, i64, i32)
|
||||||
DEF_HELPER_FLAGS_2(per_ifetch, TCG_CALL_NO_RWG, void, env, i64)
|
DEF_HELPER_FLAGS_2(per_ifetch, TCG_CALL_NO_WG, void, env, i32)
|
||||||
DEF_HELPER_FLAGS_1(per_store_real, TCG_CALL_NO_RWG, void, env)
|
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_FLAGS_1(stfl, TCG_CALL_NO_RWG, void, env)
|
||||||
|
|
||||||
DEF_HELPER_2(xsch, void, env, i64)
|
DEF_HELPER_2(xsch, void, env, i64)
|
||||||
|
@ -209,7 +209,7 @@ static void do_program_interrupt(CPUS390XState *env)
|
|||||||
|
|
||||||
switch (env->int_pgm_code) {
|
switch (env->int_pgm_code) {
|
||||||
case PGM_PER:
|
case PGM_PER:
|
||||||
advance = !(env->per_perc_atmid & PER_CODE_EVENT_NULLIFICATION);
|
/* advance already handled */
|
||||||
break;
|
break;
|
||||||
case PGM_ASCE_TYPE:
|
case PGM_ASCE_TYPE:
|
||||||
case PGM_REG_FIRST_TRANS:
|
case PGM_REG_FIRST_TRANS:
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "s390x-internal.h"
|
#include "s390x-internal.h"
|
||||||
#include "qemu/host-utils.h"
|
#include "qemu/host-utils.h"
|
||||||
@ -590,10 +591,24 @@ void HELPER(chsc)(CPUS390XState *env, uint64_t inst)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#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)
|
void HELPER(per_check_exception)(CPUS390XState *env)
|
||||||
{
|
{
|
||||||
if (env->per_perc_atmid) {
|
/* psw_addr, per_address and int_pgm_ilen are already set. */
|
||||||
tcg_s390_program_interrupt(env, PGM_PER, GETPC());
|
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)
|
||||||
if (!(env->cregs[9] & PER_CR9_CONTROL_BRANCH_ADDRESS)
|
&& !get_per_in_range(env, dest)) {
|
||||||
|| get_per_in_range(env, to)) {
|
return;
|
||||||
env->per_address = from;
|
|
||||||
env->per_perc_atmid = PER_CODE_EVENT_BRANCH | get_per_atmid(env);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HELPER(per_ifetch)(CPUS390XState *env, uint64_t addr)
|
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, uint32_t ilen)
|
||||||
{
|
{
|
||||||
if ((env->cregs[9] & PER_CR9_EVENT_IFETCH) && get_per_in_range(env, addr)) {
|
if (get_per_in_range(env, env->psw.addr)) {
|
||||||
env->per_address = addr;
|
env->per_address = env->psw.addr;
|
||||||
|
env->int_pgm_ilen = ilen;
|
||||||
env->per_perc_atmid = PER_CODE_EVENT_IFETCH | get_per_atmid(env);
|
env->per_perc_atmid = PER_CODE_EVENT_IFETCH | get_per_atmid(env);
|
||||||
|
|
||||||
/* If the instruction has to be nullified, trigger the
|
/* If the instruction has to be nullified, trigger the
|
||||||
exception immediately. */
|
exception immediately. */
|
||||||
if (env->cregs[9] & PER_CR9_EVENT_NULLIFICATION) {
|
if (env->cregs[9] & PER_CR9_EVENT_IFETCH_NULLIFICATION) {
|
||||||
CPUState *cs = env_cpu(env);
|
|
||||||
|
|
||||||
env->per_perc_atmid |= PER_CODE_EVENT_NULLIFICATION;
|
env->per_perc_atmid |= PER_CODE_EVENT_NULLIFICATION;
|
||||||
env->int_pgm_code = PGM_PER;
|
qemu_log_mask(CPU_LOG_INT, "PER interrupt before 0x%" PRIx64 "\n",
|
||||||
env->int_pgm_ilen = get_ilen(cpu_ldub_code(env, addr));
|
env->per_address);
|
||||||
|
per_raise_exception(env);
|
||||||
cs->exception_index = EXCP_PGM;
|
|
||||||
cpu_loop_exit(cs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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. */
|
/* PSW is saved just before calling the helper. */
|
||||||
env->per_address = env->psw.addr;
|
env->per_address = env->psw.addr;
|
||||||
|
env->int_pgm_ilen = ilen;
|
||||||
env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env);
|
env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env);
|
||||||
}
|
per_raise_exception_log(env);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -341,33 +341,11 @@ static void update_psw_addr(DisasContext *s)
|
|||||||
tcg_gen_movi_i64(psw_addr, s->base.pc_next);
|
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
|
#ifndef CONFIG_USER_ONLY
|
||||||
tcg_gen_movi_i64(gbea, s->base.pc_next);
|
if (s->base.tb->flags & FLAG_MASK_PER_BRANCH) {
|
||||||
|
gen_helper_per_branch(tcg_env, dest, tcg_constant_i32(s->ilen));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -656,9 +634,6 @@ static void gen_op_calc_cc(DisasContext *s)
|
|||||||
|
|
||||||
static bool use_goto_tb(DisasContext *s, uint64_t dest)
|
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);
|
return translator_use_goto_tb(&s->base, dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1100,144 +1075,105 @@ struct DisasInsn {
|
|||||||
|
|
||||||
static DisasJumpType help_goto_direct(DisasContext *s, uint64_t dest)
|
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) {
|
if (dest == s->pc_tmp) {
|
||||||
per_branch(s, true);
|
|
||||||
return DISAS_NEXT;
|
return DISAS_NEXT;
|
||||||
}
|
}
|
||||||
if (use_goto_tb(s, dest)) {
|
if (use_goto_tb(s, dest)) {
|
||||||
update_cc_op(s);
|
|
||||||
per_breaking_event(s);
|
|
||||||
tcg_gen_goto_tb(0);
|
tcg_gen_goto_tb(0);
|
||||||
tcg_gen_movi_i64(psw_addr, dest);
|
tcg_gen_movi_i64(psw_addr, dest);
|
||||||
tcg_gen_exit_tb(s->base.tb, 0);
|
tcg_gen_exit_tb(s->base.tb, 0);
|
||||||
return DISAS_NORETURN;
|
return DISAS_NORETURN;
|
||||||
} else {
|
} else {
|
||||||
tcg_gen_movi_i64(psw_addr, dest);
|
tcg_gen_movi_i64(psw_addr, dest);
|
||||||
per_branch(s, false);
|
return DISAS_PC_CC_UPDATED;
|
||||||
return DISAS_PC_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,
|
static DisasJumpType help_branch(DisasContext *s, DisasCompare *c,
|
||||||
bool is_imm, int imm, TCGv_i64 cdest)
|
bool is_imm, int imm, TCGv_i64 cdest)
|
||||||
{
|
{
|
||||||
DisasJumpType ret;
|
|
||||||
uint64_t dest = s->base.pc_next + (int64_t)imm * 2;
|
uint64_t dest = s->base.pc_next + (int64_t)imm * 2;
|
||||||
TCGLabel *lab;
|
TCGLabel *lab;
|
||||||
|
|
||||||
/* Take care of the special cases first. */
|
/* Take care of the special cases first. */
|
||||||
if (c->cond == TCG_COND_NEVER) {
|
if (c->cond == TCG_COND_NEVER) {
|
||||||
ret = DISAS_NEXT;
|
return DISAS_NEXT;
|
||||||
goto egress;
|
|
||||||
}
|
}
|
||||||
if (is_imm) {
|
if (is_imm) {
|
||||||
if (dest == s->pc_tmp) {
|
/*
|
||||||
/* Branch to next. */
|
* Do not optimize a conditional branch if PER enabled, because we
|
||||||
per_branch(s, true);
|
* still need a conditional call to helper_per_branch.
|
||||||
ret = DISAS_NEXT;
|
*/
|
||||||
goto egress;
|
if (c->cond == TCG_COND_ALWAYS
|
||||||
}
|
|| (dest == s->pc_tmp &&
|
||||||
if (c->cond == TCG_COND_ALWAYS) {
|
!(s->base.tb->flags & FLAG_MASK_PER_BRANCH))) {
|
||||||
ret = help_goto_direct(s, dest);
|
return help_goto_direct(s, dest);
|
||||||
goto egress;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!cdest) {
|
if (!cdest) {
|
||||||
/* E.g. bcr %r0 -> no branch. */
|
/* E.g. bcr %r0 -> no branch. */
|
||||||
ret = DISAS_NEXT;
|
return DISAS_NEXT;
|
||||||
goto egress;
|
|
||||||
}
|
}
|
||||||
if (c->cond == TCG_COND_ALWAYS) {
|
if (c->cond == TCG_COND_ALWAYS) {
|
||||||
tcg_gen_mov_i64(psw_addr, cdest);
|
return help_goto_indirect(s, cdest);
|
||||||
per_branch(s, false);
|
|
||||||
ret = DISAS_PC_UPDATED;
|
|
||||||
goto egress;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
update_cc_op(s);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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();
|
lab = gen_new_label();
|
||||||
if (c->is_64) {
|
if (c->is_64) {
|
||||||
tcg_gen_brcond_i64(c->cond, c->u.s64.a, c->u.s64.b, lab);
|
tcg_gen_brcond_i64(tcg_invert_cond(c->cond),
|
||||||
|
c->u.s64.a, c->u.s64.b, lab);
|
||||||
} else {
|
} else {
|
||||||
tcg_gen_brcond_i32(c->cond, c->u.s32.a, c->u.s32.b, lab);
|
tcg_gen_brcond_i32(tcg_invert_cond(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. */
|
/* Branch taken. */
|
||||||
gen_set_label(lab);
|
|
||||||
per_breaking_event(s);
|
per_breaking_event(s);
|
||||||
tcg_gen_goto_tb(1);
|
if (is_imm) {
|
||||||
tcg_gen_movi_i64(psw_addr, dest);
|
tcg_gen_movi_i64(psw_addr, dest);
|
||||||
tcg_gen_exit_tb(s->base.tb, 1);
|
|
||||||
|
|
||||||
ret = DISAS_NORETURN;
|
|
||||||
} else {
|
} 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);
|
tcg_gen_mov_i64(psw_addr, cdest);
|
||||||
}
|
}
|
||||||
|
per_branch(s, psw_addr);
|
||||||
|
|
||||||
lab = gen_new_label();
|
if (is_imm && use_goto_tb(s, dest)) {
|
||||||
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. */
|
|
||||||
update_cc_op(s);
|
|
||||||
tcg_gen_goto_tb(0);
|
tcg_gen_goto_tb(0);
|
||||||
tcg_gen_movi_i64(psw_addr, s->pc_tmp);
|
|
||||||
tcg_gen_exit_tb(s->base.tb, 0);
|
tcg_gen_exit_tb(s->base.tb, 0);
|
||||||
|
} else {
|
||||||
|
tcg_gen_lookup_and_goto_ptr();
|
||||||
|
}
|
||||||
|
|
||||||
gen_set_label(lab);
|
gen_set_label(lab);
|
||||||
if (is_imm) {
|
|
||||||
tcg_gen_movi_i64(psw_addr, dest);
|
|
||||||
}
|
|
||||||
per_breaking_event(s);
|
|
||||||
ret = DISAS_PC_UPDATED;
|
|
||||||
}
|
|
||||||
} 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. */
|
|
||||||
|
|
||||||
TCGv_i64 next = tcg_constant_i64(s->pc_tmp);
|
/* Branch not taken. */
|
||||||
if (is_imm) {
|
tcg_gen_movi_i64(psw_addr, s->pc_tmp);
|
||||||
cdest = tcg_constant_i64(dest);
|
if (use_goto_tb(s, s->pc_tmp)) {
|
||||||
|
tcg_gen_goto_tb(1);
|
||||||
|
tcg_gen_exit_tb(s->base.tb, 1);
|
||||||
|
return DISAS_NORETURN;
|
||||||
}
|
}
|
||||||
|
return DISAS_PC_CC_UPDATED;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = DISAS_PC_UPDATED;
|
|
||||||
}
|
|
||||||
|
|
||||||
egress:
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ====================================================================== */
|
/* ====================================================================== */
|
||||||
@ -1463,9 +1399,7 @@ static DisasJumpType op_bas(DisasContext *s, DisasOps *o)
|
|||||||
{
|
{
|
||||||
pc_to_link_info(o->out, s, s->pc_tmp);
|
pc_to_link_info(o->out, s, s->pc_tmp);
|
||||||
if (o->in2) {
|
if (o->in2) {
|
||||||
tcg_gen_mov_i64(psw_addr, o->in2);
|
return help_goto_indirect(s, o->in2);
|
||||||
per_branch(s, false);
|
|
||||||
return DISAS_PC_UPDATED;
|
|
||||||
} else {
|
} else {
|
||||||
return DISAS_NEXT;
|
return DISAS_NEXT;
|
||||||
}
|
}
|
||||||
@ -1495,9 +1429,7 @@ static DisasJumpType op_bal(DisasContext *s, DisasOps *o)
|
|||||||
{
|
{
|
||||||
save_link_info(s, o);
|
save_link_info(s, o);
|
||||||
if (o->in2) {
|
if (o->in2) {
|
||||||
tcg_gen_mov_i64(psw_addr, o->in2);
|
return help_goto_indirect(s, o->in2);
|
||||||
per_branch(s, false);
|
|
||||||
return DISAS_PC_UPDATED;
|
|
||||||
} else {
|
} else {
|
||||||
return DISAS_NEXT;
|
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);
|
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);
|
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;
|
return DISAS_NEXT;
|
||||||
}
|
}
|
||||||
@ -6323,9 +6257,9 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
if (s->base.tb->flags & FLAG_MASK_PER) {
|
if (s->base.tb->flags & FLAG_MASK_PER_IFETCH) {
|
||||||
TCGv_i64 addr = tcg_constant_i64(s->base.pc_next);
|
/* With ifetch set, psw_addr and cc_op are always up-to-date. */
|
||||||
gen_helper_per_ifetch(tcg_env, addr);
|
gen_helper_per_ifetch(tcg_env, tcg_constant_i32(s->ilen));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -6407,15 +6341,16 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s)
|
|||||||
}
|
}
|
||||||
if (insn->help_op) {
|
if (insn->help_op) {
|
||||||
ret = insn->help_op(s, &o);
|
ret = insn->help_op(s, &o);
|
||||||
|
if (ret == DISAS_NORETURN) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ret != DISAS_NORETURN) {
|
|
||||||
if (insn->help_wout) {
|
if (insn->help_wout) {
|
||||||
insn->help_wout(s, &o);
|
insn->help_wout(s, &o);
|
||||||
}
|
}
|
||||||
if (insn->help_cout) {
|
if (insn->help_cout) {
|
||||||
insn->help_cout(s, &o);
|
insn->help_cout(s, &o);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* io should be the last instruction in tb when icount is enabled */
|
/* io should be the last instruction in tb when icount is enabled */
|
||||||
if (unlikely(icount && ret == DISAS_NEXT)) {
|
if (unlikely(icount && ret == DISAS_NEXT)) {
|
||||||
@ -6423,13 +6358,18 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
if (s->base.tb->flags & FLAG_MASK_PER) {
|
if (s->base.tb->flags & FLAG_MASK_PER_IFETCH) {
|
||||||
/* An exception might be triggered, save PSW if not already done. */
|
switch (ret) {
|
||||||
if (ret == DISAS_NEXT || ret == DISAS_TOO_MANY) {
|
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);
|
tcg_gen_movi_i64(psw_addr, s->pc_tmp);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
update_cc_op(s);
|
||||||
/* Call the helper to check for a possible PER exception. */
|
|
||||||
gen_helper_per_check_exception(tcg_env);
|
gen_helper_per_check_exception(tcg_env);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -6452,7 +6392,7 @@ static void s390x_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
|
|||||||
|
|
||||||
dc->cc_op = CC_OP_DYNAMIC;
|
dc->cc_op = CC_OP_DYNAMIC;
|
||||||
dc->ex_value = dc->base.tb->cs_base;
|
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)
|
static void s390x_tr_tb_start(DisasContextBase *db, CPUState *cs)
|
||||||
|
@ -150,7 +150,8 @@ const generic_fuzz_config predefined_configs[] = {
|
|||||||
"-chardev null,id=cd0 -chardev null,id=cd1 "
|
"-chardev null,id=cd0 -chardev null,id=cd1 "
|
||||||
"-device usb-braille,chardev=cd0 -device usb-ccid -device usb-ccid "
|
"-device usb-braille,chardev=cd0 -device usb-ccid -device usb-ccid "
|
||||||
"-device usb-kbd -device usb-mouse -device usb-serial,chardev=cd1 "
|
"-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*",
|
.objects = "*usb* *uhci* *xhci*",
|
||||||
},{
|
},{
|
||||||
.name = "pc-i440fx",
|
.name = "pc-i440fx",
|
||||||
|
@ -25,6 +25,7 @@ ASM_TESTS = \
|
|||||||
lpswe-early \
|
lpswe-early \
|
||||||
lra \
|
lra \
|
||||||
mc \
|
mc \
|
||||||
|
per \
|
||||||
precise-smc-softmmu \
|
precise-smc-softmmu \
|
||||||
ssm-early \
|
ssm-early \
|
||||||
stosm-early \
|
stosm-early \
|
||||||
|
82
tests/tcg/s390x/per.S
Normal file
82
tests/tcg/s390x/per.S
Normal file
@ -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
|
Loading…
x
Reference in New Issue
Block a user