interrupt.c 5.9 KB
Newer Older
1 2 3
/*
 * QEMU S/390 Interrupt support
 *
4
 * Copyright IBM Corp. 2012, 2014
5 6 7 8 9
 *
 * This work is licensed under the terms of the GNU GPL, version 2 or (at your
 * option) any later version.  See the COPYING file in the top-level directory.
 */

P
Peter Maydell 已提交
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 18
#include "hw/s390x/ioinst.h"

19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
/* Ensure to exit the TB after this call! */
void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen)
{
    CPUState *cs = CPU(s390_env_get_cpu(env));

    cs->exception_index = EXCP_PGM;
    env->int_pgm_code = code;
    env->int_pgm_ilen = ilen;
}

static void tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code,
                                       int ilen)
{
#ifdef CONFIG_TCG
    trigger_pgm_exception(env, code, ilen);
    cpu_loop_exit(CPU(s390_env_get_cpu(env)));
#else
    g_assert_not_reached();
#endif
}

void program_interrupt(CPUS390XState *env, uint32_t code, int ilen)
{
    S390CPU *cpu = s390_env_get_cpu(env);

    qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n",
                  env->psw.addr);

    if (kvm_enabled()) {
        kvm_s390_program_interrupt(cpu, code);
    } else if (tcg_enabled()) {
        tcg_s390_program_interrupt(env, code, ilen);
    } else {
        g_assert_not_reached();
    }
}

56
#if !defined(CONFIG_USER_ONLY)
57
static void cpu_inject_service(S390CPU *cpu, uint32_t param)
58 59 60
{
    CPUS390XState *env = &cpu->env;

61 62
    /* multiplexing is good enough for sclp - kvm does it internally as well*/
    env->service_param |= param;
63

64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
    env->pending_int |= INTERRUPT_EXT_SERVICE;
    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
}

void cpu_inject_clock_comparator(S390CPU *cpu)
{
    CPUS390XState *env = &cpu->env;

    env->pending_int |= INTERRUPT_EXT_CLOCK_COMPARATOR;
    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
}

void cpu_inject_cpu_timer(S390CPU *cpu)
{
    CPUS390XState *env = &cpu->env;

    env->pending_int |= INTERRUPT_EXT_CPU_TIMER;
81 82 83
    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
}

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
void cpu_inject_emergency_signal(S390CPU *cpu, uint16_t src_cpu_addr)
{
    CPUS390XState *env = &cpu->env;

    g_assert(src_cpu_addr < S390_MAX_CPUS);
    set_bit(src_cpu_addr, env->emergency_signals);

    env->pending_int |= INTERRUPT_EMERGENCY_SIGNAL;
    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
}

int cpu_inject_external_call(S390CPU *cpu, uint16_t src_cpu_addr)
{
    CPUS390XState *env = &cpu->env;

    g_assert(src_cpu_addr < S390_MAX_CPUS);
    if (env->pending_int & INTERRUPT_EXTERNAL_CALL) {
        return -EBUSY;
    }
    env->external_call_addr = src_cpu_addr;

    env->pending_int |= INTERRUPT_EXTERNAL_CALL;
    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
    return 0;
}

110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
static void cpu_inject_io(S390CPU *cpu, uint16_t subchannel_id,
                          uint16_t subchannel_number,
                          uint32_t io_int_parm, uint32_t io_int_word)
{
    CPUS390XState *env = &cpu->env;
    int isc = IO_INT_WORD_ISC(io_int_word);

    if (env->io_index[isc] == MAX_IO_QUEUE - 1) {
        /* ugh - can't queue anymore. Let's drop. */
        return;
    }

    env->io_index[isc]++;
    assert(env->io_index[isc] < MAX_IO_QUEUE);

    env->io_queue[env->io_index[isc]][isc].id = subchannel_id;
    env->io_queue[env->io_index[isc]][isc].nr = subchannel_number;
    env->io_queue[env->io_index[isc]][isc].parm = io_int_parm;
    env->io_queue[env->io_index[isc]][isc].word = io_int_word;

    env->pending_int |= INTERRUPT_IO;
    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
}

static void cpu_inject_crw_mchk(S390CPU *cpu)
{
    CPUS390XState *env = &cpu->env;

    if (env->mchk_index == MAX_MCHK_QUEUE - 1) {
        /* ugh - can't queue anymore. Let's drop. */
        return;
    }

    env->mchk_index++;
    assert(env->mchk_index < MAX_MCHK_QUEUE);

    env->mchk_queue[env->mchk_index].type = 1;

    env->pending_int |= INTERRUPT_MCHK;
    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
}
151

152 153
/*
 * All of the following interrupts are floating, i.e. not per-vcpu.
154 155
 * We just need a dummy cpustate in order to be able to inject in the
 * non-kvm case.
156
 */
157 158 159
void s390_sclp_extint(uint32_t parm)
{
    if (kvm_enabled()) {
160
        kvm_s390_service_interrupt(parm);
161
    } else {
162 163
        S390CPU *dummy_cpu = s390_cpu_addr2state(0);

164
        cpu_inject_service(dummy_cpu, parm);
165 166
    }
}
167

168 169
void s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr,
                       uint32_t io_int_parm, uint32_t io_int_word)
170 171
{
    if (kvm_enabled()) {
172
        kvm_s390_io_interrupt(subchannel_id, subchannel_nr, io_int_parm,
173 174
                              io_int_word);
    } else {
175 176 177
        S390CPU *dummy_cpu = s390_cpu_addr2state(0);

        cpu_inject_io(dummy_cpu, subchannel_id, subchannel_nr, io_int_parm,
178 179 180 181
                      io_int_word);
    }
}

182
void s390_crw_mchk(void)
183 184
{
    if (kvm_enabled()) {
185
        kvm_s390_crw_mchk();
186
    } else {
187 188 189
        S390CPU *dummy_cpu = s390_cpu_addr2state(0);

        cpu_inject_crw_mchk(dummy_cpu);
190 191 192
    }
}

193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
bool s390_cpu_has_mcck_int(S390CPU *cpu)
{
    CPUS390XState *env = &cpu->env;

    if (!(env->psw.mask & PSW_MASK_MCHECK)) {
        return false;
    }

    return env->pending_int & INTERRUPT_MCHK;
}

bool s390_cpu_has_ext_int(S390CPU *cpu)
{
    CPUS390XState *env = &cpu->env;

    if (!(env->psw.mask & PSW_MASK_EXT)) {
        return false;
    }

    return env->pending_int & INTERRUPT_EXT;
}

bool s390_cpu_has_io_int(S390CPU *cpu)
{
    CPUS390XState *env = &cpu->env;

    if (!(env->psw.mask & PSW_MASK_IO)) {
        return false;
    }

    return env->pending_int & INTERRUPT_IO;
}
225
#endif
226 227 228 229 230 231 232 233 234 235 236 237 238 239

bool s390_cpu_has_int(S390CPU *cpu)
{
#ifndef CONFIG_USER_ONLY
    if (!tcg_enabled()) {
        return false;
    }
    return s390_cpu_has_mcck_int(cpu) ||
           s390_cpu_has_ext_int(cpu) ||
           s390_cpu_has_io_int(cpu);
#else
    return false;
#endif
}