qemu/target/s390x/interrupt.c
<<
>>
Prefs
   1/*
   2 * QEMU S/390 Interrupt support
   3 *
   4 * Copyright IBM Corp. 2012, 2014
   5 *
   6 * This work is licensed under the terms of the GNU GPL, version 2 or (at your
   7 * option) any later version.  See the COPYING file in the top-level directory.
   8 */
   9
  10#include "qemu/osdep.h"
  11#include "qemu/log.h"
  12#include "cpu.h"
  13#include "kvm_s390x.h"
  14#include "internal.h"
  15#include "exec/exec-all.h"
  16#include "sysemu/kvm.h"
  17#include "hw/s390x/ioinst.h"
  18#include "tcg_s390x.h"
  19#if !defined(CONFIG_USER_ONLY)
  20#include "hw/s390x/s390_flic.h"
  21#endif
  22
  23/* Ensure to exit the TB after this call! */
  24void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen)
  25{
  26    CPUState *cs = CPU(s390_env_get_cpu(env));
  27
  28    cs->exception_index = EXCP_PGM;
  29    env->int_pgm_code = code;
  30    env->int_pgm_ilen = ilen;
  31}
  32
  33void s390_program_interrupt(CPUS390XState *env, uint32_t code, int ilen,
  34                            uintptr_t ra)
  35{
  36    S390CPU *cpu = s390_env_get_cpu(env);
  37
  38    if (kvm_enabled()) {
  39        kvm_s390_program_interrupt(cpu, code);
  40    } else if (tcg_enabled()) {
  41        tcg_s390_program_interrupt(env, code, ilen, ra);
  42    } else {
  43        g_assert_not_reached();
  44    }
  45}
  46
  47#if !defined(CONFIG_USER_ONLY)
  48void cpu_inject_clock_comparator(S390CPU *cpu)
  49{
  50    CPUS390XState *env = &cpu->env;
  51
  52    env->pending_int |= INTERRUPT_EXT_CLOCK_COMPARATOR;
  53    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
  54}
  55
  56void cpu_inject_cpu_timer(S390CPU *cpu)
  57{
  58    CPUS390XState *env = &cpu->env;
  59
  60    env->pending_int |= INTERRUPT_EXT_CPU_TIMER;
  61    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
  62}
  63
  64void cpu_inject_emergency_signal(S390CPU *cpu, uint16_t src_cpu_addr)
  65{
  66    CPUS390XState *env = &cpu->env;
  67
  68    g_assert(src_cpu_addr < S390_MAX_CPUS);
  69    set_bit(src_cpu_addr, env->emergency_signals);
  70
  71    env->pending_int |= INTERRUPT_EMERGENCY_SIGNAL;
  72    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
  73}
  74
  75int cpu_inject_external_call(S390CPU *cpu, uint16_t src_cpu_addr)
  76{
  77    CPUS390XState *env = &cpu->env;
  78
  79    g_assert(src_cpu_addr < S390_MAX_CPUS);
  80    if (env->pending_int & INTERRUPT_EXTERNAL_CALL) {
  81        return -EBUSY;
  82    }
  83    env->external_call_addr = src_cpu_addr;
  84
  85    env->pending_int |= INTERRUPT_EXTERNAL_CALL;
  86    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
  87    return 0;
  88}
  89
  90void cpu_inject_restart(S390CPU *cpu)
  91{
  92    CPUS390XState *env = &cpu->env;
  93
  94    if (kvm_enabled()) {
  95        kvm_s390_restart_interrupt(cpu);
  96        return;
  97    }
  98
  99    env->pending_int |= INTERRUPT_RESTART;
 100    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
 101}
 102
 103void cpu_inject_stop(S390CPU *cpu)
 104{
 105    CPUS390XState *env = &cpu->env;
 106
 107    if (kvm_enabled()) {
 108        kvm_s390_stop_interrupt(cpu);
 109        return;
 110    }
 111
 112    env->pending_int |= INTERRUPT_STOP;
 113    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
 114}
 115
 116/*
 117 * All of the following interrupts are floating, i.e. not per-vcpu.
 118 * We just need a dummy cpustate in order to be able to inject in the
 119 * non-kvm case.
 120 */
 121void s390_sclp_extint(uint32_t parm)
 122{
 123    S390FLICState *fs = s390_get_flic();
 124    S390FLICStateClass *fsc = s390_get_flic_class(fs);
 125
 126    fsc->inject_service(fs, parm);
 127}
 128
 129void s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr,
 130                       uint32_t io_int_parm, uint32_t io_int_word)
 131{
 132    S390FLICState *fs = s390_get_flic();
 133    S390FLICStateClass *fsc = s390_get_flic_class(fs);
 134
 135    fsc->inject_io(fs, subchannel_id, subchannel_nr, io_int_parm, io_int_word);
 136}
 137
 138void s390_crw_mchk(void)
 139{
 140    S390FLICState *fs = s390_get_flic();
 141    S390FLICStateClass *fsc = s390_get_flic_class(fs);
 142
 143    fsc->inject_crw_mchk(fs);
 144}
 145
 146bool s390_cpu_has_mcck_int(S390CPU *cpu)
 147{
 148    QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic());
 149    CPUS390XState *env = &cpu->env;
 150
 151    if (!(env->psw.mask & PSW_MASK_MCHECK)) {
 152        return false;
 153    }
 154
 155    /* for now we only support channel report machine checks (floating) */
 156    if (qemu_s390_flic_has_crw_mchk(flic) &&
 157        (env->cregs[14] & CR14_CHANNEL_REPORT_SC)) {
 158        return true;
 159    }
 160
 161    return false;
 162}
 163
 164bool s390_cpu_has_ext_int(S390CPU *cpu)
 165{
 166    QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic());
 167    CPUS390XState *env = &cpu->env;
 168
 169    if (!(env->psw.mask & PSW_MASK_EXT)) {
 170        return false;
 171    }
 172
 173    if ((env->pending_int & INTERRUPT_EMERGENCY_SIGNAL) &&
 174        (env->cregs[0] & CR0_EMERGENCY_SIGNAL_SC)) {
 175        return true;
 176    }
 177
 178    if ((env->pending_int & INTERRUPT_EXTERNAL_CALL) &&
 179        (env->cregs[0] & CR0_EXTERNAL_CALL_SC)) {
 180        return true;
 181    }
 182
 183    if ((env->pending_int & INTERRUPT_EXTERNAL_CALL) &&
 184        (env->cregs[0] & CR0_EXTERNAL_CALL_SC)) {
 185        return true;
 186    }
 187
 188    if ((env->pending_int & INTERRUPT_EXT_CLOCK_COMPARATOR) &&
 189        (env->cregs[0] & CR0_CKC_SC)) {
 190        return true;
 191    }
 192
 193    if ((env->pending_int & INTERRUPT_EXT_CPU_TIMER) &&
 194        (env->cregs[0] & CR0_CPU_TIMER_SC)) {
 195        return true;
 196    }
 197
 198    if (qemu_s390_flic_has_service(flic) &&
 199        (env->cregs[0] & CR0_SERVICE_SC)) {
 200        return true;
 201    }
 202
 203    return false;
 204}
 205
 206bool s390_cpu_has_io_int(S390CPU *cpu)
 207{
 208    QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic());
 209    CPUS390XState *env = &cpu->env;
 210
 211    if (!(env->psw.mask & PSW_MASK_IO)) {
 212        return false;
 213    }
 214
 215    return qemu_s390_flic_has_io(flic, env->cregs[6]);
 216}
 217
 218bool s390_cpu_has_restart_int(S390CPU *cpu)
 219{
 220    CPUS390XState *env = &cpu->env;
 221
 222    return env->pending_int & INTERRUPT_RESTART;
 223}
 224
 225bool s390_cpu_has_stop_int(S390CPU *cpu)
 226{
 227    CPUS390XState *env = &cpu->env;
 228
 229    return env->pending_int & INTERRUPT_STOP;
 230}
 231#endif
 232
 233bool s390_cpu_has_int(S390CPU *cpu)
 234{
 235#ifndef CONFIG_USER_ONLY
 236    if (!tcg_enabled()) {
 237        return false;
 238    }
 239    return s390_cpu_has_mcck_int(cpu) ||
 240           s390_cpu_has_ext_int(cpu) ||
 241           s390_cpu_has_io_int(cpu) ||
 242           s390_cpu_has_restart_int(cpu) ||
 243           s390_cpu_has_stop_int(cpu);
 244#else
 245    return false;
 246#endif
 247}
 248