diff options
author | David Hildenbrand <david@redhat.com> | 2018-01-29 13:56:14 +0100 |
---|---|---|
committer | Cornelia Huck <cohuck@redhat.com> | 2018-02-09 09:37:13 +0100 |
commit | 6a253de39557433e19878c7e78aa5cc65b763813 (patch) | |
tree | 046548f6bc63b8b8f68be3ca79de77efb9848017 /target/s390x/misc_helper.c | |
parent | b194e44785269c8884f17eac40aa80bada4c746a (diff) |
s390x/tcg: implement TEST PENDING INTERRUPTION
Use s390_cpu_virt_mem_write() so we can actually revert what we did
(re-inject the dequeued IO interrupt).
Signed-off-by: David Hildenbrand <david@redhat.com>
Message-Id: <20180129125623.21729-10-david@redhat.com>
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
Diffstat (limited to 'target/s390x/misc_helper.c')
-rw-r--r-- | target/s390x/misc_helper.c | 54 |
1 files changed, 54 insertions, 0 deletions
diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c index 86da6aab7e..6ee7e8a64a 100644 --- a/target/s390x/misc_helper.c +++ b/target/s390x/misc_helper.c @@ -36,6 +36,7 @@ #include "hw/s390x/ebcdic.h" #include "hw/s390x/s390-virtio-hcall.h" #include "hw/s390x/sclp.h" +#include "hw/s390x/s390_flic.h" #endif /* #define DEBUG_HELPER */ @@ -429,6 +430,59 @@ void HELPER(stsch)(CPUS390XState *env, uint64_t r1, uint64_t inst) qemu_mutex_unlock_iothread(); } +uint32_t HELPER(tpi)(CPUS390XState *env, uint64_t addr) +{ + const uintptr_t ra = GETPC(); + S390CPU *cpu = s390_env_get_cpu(env); + QEMUS390FLICState *flic = QEMU_S390_FLIC(s390_get_flic()); + QEMUS390FlicIO *io = NULL; + LowCore *lowcore; + + if (addr & 0x3) { + s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + } + + qemu_mutex_lock_iothread(); + io = qemu_s390_flic_dequeue_io(flic, env->cregs[6]); + if (!io) { + qemu_mutex_unlock_iothread(); + return 0; + } + + if (addr) { + struct { + uint16_t id; + uint16_t nr; + uint32_t parm; + } intc = { + .id = cpu_to_be16(io->id), + .nr = cpu_to_be16(io->nr), + .parm = cpu_to_be32(io->parm), + }; + + if (s390_cpu_virt_mem_write(cpu, addr, 0, &intc, sizeof(intc))) { + /* writing failed, reinject and properly clean up */ + s390_io_interrupt(io->id, io->nr, io->parm, io->word); + qemu_mutex_unlock_iothread(); + g_free(io); + s390_cpu_virt_mem_handle_exc(cpu, ra); + return 0; + } + } else { + /* no protection applies */ + lowcore = cpu_map_lowcore(env); + lowcore->subchannel_id = cpu_to_be16(io->id); + lowcore->subchannel_nr = cpu_to_be16(io->nr); + lowcore->io_int_parm = cpu_to_be32(io->parm); + lowcore->io_int_word = cpu_to_be32(io->word); + cpu_unmap_lowcore(lowcore); + } + + g_free(io); + qemu_mutex_unlock_iothread(); + return 1; +} + void HELPER(tsch)(CPUS390XState *env, uint64_t r1, uint64_t inst) { S390CPU *cpu = s390_env_get_cpu(env); |