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