qemu/target-s390x/helper.c
<<
>>
Prefs
   1/*
   2 *  S/390 helpers
   3 *
   4 *  Copyright (c) 2009 Ulrich Hecht
   5 *  Copyright (c) 2011 Alexander Graf
   6 *
   7 * This library is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU Lesser General Public
   9 * License as published by the Free Software Foundation; either
  10 * version 2 of the License, or (at your option) any later version.
  11 *
  12 * This library is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15 * Lesser General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU Lesser General Public
  18 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  19 */
  20
  21#include "cpu.h"
  22#include "exec/gdbstub.h"
  23#include "qemu/timer.h"
  24#ifndef CONFIG_USER_ONLY
  25#include "sysemu/sysemu.h"
  26#endif
  27
  28//#define DEBUG_S390
  29//#define DEBUG_S390_PTE
  30//#define DEBUG_S390_STDOUT
  31
  32#ifdef DEBUG_S390
  33#ifdef DEBUG_S390_STDOUT
  34#define DPRINTF(fmt, ...) \
  35    do { fprintf(stderr, fmt, ## __VA_ARGS__); \
  36         qemu_log(fmt, ##__VA_ARGS__); } while (0)
  37#else
  38#define DPRINTF(fmt, ...) \
  39    do { qemu_log(fmt, ## __VA_ARGS__); } while (0)
  40#endif
  41#else
  42#define DPRINTF(fmt, ...) \
  43    do { } while (0)
  44#endif
  45
  46#ifdef DEBUG_S390_PTE
  47#define PTE_DPRINTF DPRINTF
  48#else
  49#define PTE_DPRINTF(fmt, ...) \
  50    do { } while (0)
  51#endif
  52
  53#ifndef CONFIG_USER_ONLY
  54void s390x_tod_timer(void *opaque)
  55{
  56    S390CPU *cpu = opaque;
  57    CPUS390XState *env = &cpu->env;
  58
  59    env->pending_int |= INTERRUPT_TOD;
  60    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
  61}
  62
  63void s390x_cpu_timer(void *opaque)
  64{
  65    S390CPU *cpu = opaque;
  66    CPUS390XState *env = &cpu->env;
  67
  68    env->pending_int |= INTERRUPT_CPUTIMER;
  69    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
  70}
  71#endif
  72
  73S390CPU *cpu_s390x_init(const char *cpu_model)
  74{
  75    S390CPU *cpu;
  76    CPUS390XState *env;
  77
  78    cpu = S390_CPU(object_new(TYPE_S390_CPU));
  79    env = &cpu->env;
  80    env->cpu_model_str = cpu_model;
  81
  82    object_property_set_bool(OBJECT(cpu), true, "realized", NULL);
  83
  84    return cpu;
  85}
  86
  87#if defined(CONFIG_USER_ONLY)
  88
  89void s390_cpu_do_interrupt(CPUState *cs)
  90{
  91    S390CPU *cpu = S390_CPU(cs);
  92    CPUS390XState *env = &cpu->env;
  93
  94    env->exception_index = -1;
  95}
  96
  97int cpu_s390x_handle_mmu_fault(CPUS390XState *env, target_ulong address,
  98                               int rw, int mmu_idx)
  99{
 100    env->exception_index = EXCP_PGM;
 101    env->int_pgm_code = PGM_ADDRESSING;
 102    /* On real machines this value is dropped into LowMem.  Since this
 103       is userland, simply put this someplace that cpu_loop can find it.  */
 104    env->__excp_addr = address;
 105    return 1;
 106}
 107
 108#else /* !CONFIG_USER_ONLY */
 109
 110/* Ensure to exit the TB after this call! */
 111static void trigger_pgm_exception(CPUS390XState *env, uint32_t code,
 112                                  uint32_t ilen)
 113{
 114    env->exception_index = EXCP_PGM;
 115    env->int_pgm_code = code;
 116    env->int_pgm_ilen = ilen;
 117}
 118
 119static int trans_bits(CPUS390XState *env, uint64_t mode)
 120{
 121    int bits = 0;
 122
 123    switch (mode) {
 124    case PSW_ASC_PRIMARY:
 125        bits = 1;
 126        break;
 127    case PSW_ASC_SECONDARY:
 128        bits = 2;
 129        break;
 130    case PSW_ASC_HOME:
 131        bits = 3;
 132        break;
 133    default:
 134        cpu_abort(env, "unknown asc mode\n");
 135        break;
 136    }
 137
 138    return bits;
 139}
 140
 141static void trigger_prot_fault(CPUS390XState *env, target_ulong vaddr,
 142                               uint64_t mode)
 143{
 144    int ilen = ILEN_LATER_INC;
 145    int bits = trans_bits(env, mode) | 4;
 146
 147    DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits);
 148
 149    stq_phys(env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits);
 150    trigger_pgm_exception(env, PGM_PROTECTION, ilen);
 151}
 152
 153static void trigger_page_fault(CPUS390XState *env, target_ulong vaddr,
 154                               uint32_t type, uint64_t asc, int rw)
 155{
 156    int ilen = ILEN_LATER;
 157    int bits = trans_bits(env, asc);
 158
 159    /* Code accesses have an undefined ilc.  */
 160    if (rw == 2) {
 161        ilen = 2;
 162    }
 163
 164    DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits);
 165
 166    stq_phys(env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits);
 167    trigger_pgm_exception(env, type, ilen);
 168}
 169
 170static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr,
 171                              uint64_t asc, uint64_t asce, int level,
 172                              target_ulong *raddr, int *flags, int rw)
 173{
 174    uint64_t offs = 0;
 175    uint64_t origin;
 176    uint64_t new_asce;
 177
 178    PTE_DPRINTF("%s: 0x%" PRIx64 "\n", __func__, asce);
 179
 180    if (((level != _ASCE_TYPE_SEGMENT) && (asce & _REGION_ENTRY_INV)) ||
 181        ((level == _ASCE_TYPE_SEGMENT) && (asce & _SEGMENT_ENTRY_INV))) {
 182        /* XXX different regions have different faults */
 183        DPRINTF("%s: invalid region\n", __func__);
 184        trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw);
 185        return -1;
 186    }
 187
 188    if ((level <= _ASCE_TYPE_MASK) && ((asce & _ASCE_TYPE_MASK) != level)) {
 189        trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw);
 190        return -1;
 191    }
 192
 193    if (asce & _ASCE_REAL_SPACE) {
 194        /* direct mapping */
 195
 196        *raddr = vaddr;
 197        return 0;
 198    }
 199
 200    origin = asce & _ASCE_ORIGIN;
 201
 202    switch (level) {
 203    case _ASCE_TYPE_REGION1 + 4:
 204        offs = (vaddr >> 50) & 0x3ff8;
 205        break;
 206    case _ASCE_TYPE_REGION1:
 207        offs = (vaddr >> 39) & 0x3ff8;
 208        break;
 209    case _ASCE_TYPE_REGION2:
 210        offs = (vaddr >> 28) & 0x3ff8;
 211        break;
 212    case _ASCE_TYPE_REGION3:
 213        offs = (vaddr >> 17) & 0x3ff8;
 214        break;
 215    case _ASCE_TYPE_SEGMENT:
 216        offs = (vaddr >> 9) & 0x07f8;
 217        origin = asce & _SEGMENT_ENTRY_ORIGIN;
 218        break;
 219    }
 220
 221    /* XXX region protection flags */
 222    /* *flags &= ~PAGE_WRITE */
 223
 224    new_asce = ldq_phys(origin + offs);
 225    PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n",
 226                __func__, origin, offs, new_asce);
 227
 228    if (level != _ASCE_TYPE_SEGMENT) {
 229        /* yet another region */
 230        return mmu_translate_asce(env, vaddr, asc, new_asce, level - 4, raddr,
 231                                  flags, rw);
 232    }
 233
 234    /* PTE */
 235    if (new_asce & _PAGE_INVALID) {
 236        DPRINTF("%s: PTE=0x%" PRIx64 " invalid\n", __func__, new_asce);
 237        trigger_page_fault(env, vaddr, PGM_PAGE_TRANS, asc, rw);
 238        return -1;
 239    }
 240
 241    if (new_asce & _PAGE_RO) {
 242        *flags &= ~PAGE_WRITE;
 243    }
 244
 245    *raddr = new_asce & _ASCE_ORIGIN;
 246
 247    PTE_DPRINTF("%s: PTE=0x%" PRIx64 "\n", __func__, new_asce);
 248
 249    return 0;
 250}
 251
 252static int mmu_translate_asc(CPUS390XState *env, target_ulong vaddr,
 253                             uint64_t asc, target_ulong *raddr, int *flags,
 254                             int rw)
 255{
 256    uint64_t asce = 0;
 257    int level, new_level;
 258    int r;
 259
 260    switch (asc) {
 261    case PSW_ASC_PRIMARY:
 262        PTE_DPRINTF("%s: asc=primary\n", __func__);
 263        asce = env->cregs[1];
 264        break;
 265    case PSW_ASC_SECONDARY:
 266        PTE_DPRINTF("%s: asc=secondary\n", __func__);
 267        asce = env->cregs[7];
 268        break;
 269    case PSW_ASC_HOME:
 270        PTE_DPRINTF("%s: asc=home\n", __func__);
 271        asce = env->cregs[13];
 272        break;
 273    }
 274
 275    switch (asce & _ASCE_TYPE_MASK) {
 276    case _ASCE_TYPE_REGION1:
 277        break;
 278    case _ASCE_TYPE_REGION2:
 279        if (vaddr & 0xffe0000000000000ULL) {
 280            DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
 281                    " 0xffe0000000000000ULL\n", __func__, vaddr);
 282            trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw);
 283            return -1;
 284        }
 285        break;
 286    case _ASCE_TYPE_REGION3:
 287        if (vaddr & 0xfffffc0000000000ULL) {
 288            DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
 289                    " 0xfffffc0000000000ULL\n", __func__, vaddr);
 290            trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw);
 291            return -1;
 292        }
 293        break;
 294    case _ASCE_TYPE_SEGMENT:
 295        if (vaddr & 0xffffffff80000000ULL) {
 296            DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
 297                    " 0xffffffff80000000ULL\n", __func__, vaddr);
 298            trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw);
 299            return -1;
 300        }
 301        break;
 302    }
 303
 304    /* fake level above current */
 305    level = asce & _ASCE_TYPE_MASK;
 306    new_level = level + 4;
 307    asce = (asce & ~_ASCE_TYPE_MASK) | (new_level & _ASCE_TYPE_MASK);
 308
 309    r = mmu_translate_asce(env, vaddr, asc, asce, new_level, raddr, flags, rw);
 310
 311    if ((rw == 1) && !(*flags & PAGE_WRITE)) {
 312        trigger_prot_fault(env, vaddr, asc);
 313        return -1;
 314    }
 315
 316    return r;
 317}
 318
 319int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
 320                  target_ulong *raddr, int *flags)
 321{
 322    int r = -1;
 323    uint8_t *sk;
 324
 325    *flags = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
 326    vaddr &= TARGET_PAGE_MASK;
 327
 328    if (!(env->psw.mask & PSW_MASK_DAT)) {
 329        *raddr = vaddr;
 330        r = 0;
 331        goto out;
 332    }
 333
 334    switch (asc) {
 335    case PSW_ASC_PRIMARY:
 336    case PSW_ASC_HOME:
 337        r = mmu_translate_asc(env, vaddr, asc, raddr, flags, rw);
 338        break;
 339    case PSW_ASC_SECONDARY:
 340        /*
 341         * Instruction: Primary
 342         * Data: Secondary
 343         */
 344        if (rw == 2) {
 345            r = mmu_translate_asc(env, vaddr, PSW_ASC_PRIMARY, raddr, flags,
 346                                  rw);
 347            *flags &= ~(PAGE_READ | PAGE_WRITE);
 348        } else {
 349            r = mmu_translate_asc(env, vaddr, PSW_ASC_SECONDARY, raddr, flags,
 350                                  rw);
 351            *flags &= ~(PAGE_EXEC);
 352        }
 353        break;
 354    case PSW_ASC_ACCREG:
 355    default:
 356        hw_error("guest switched to unknown asc mode\n");
 357        break;
 358    }
 359
 360 out:
 361    /* Convert real address -> absolute address */
 362    if (*raddr < 0x2000) {
 363        *raddr = *raddr + env->psa;
 364    }
 365
 366    if (*raddr <= ram_size) {
 367        sk = &env->storage_keys[*raddr / TARGET_PAGE_SIZE];
 368        if (*flags & PAGE_READ) {
 369            *sk |= SK_R;
 370        }
 371
 372        if (*flags & PAGE_WRITE) {
 373            *sk |= SK_C;
 374        }
 375    }
 376
 377    return r;
 378}
 379
 380int cpu_s390x_handle_mmu_fault(CPUS390XState *env, target_ulong orig_vaddr,
 381                               int rw, int mmu_idx)
 382{
 383    uint64_t asc = env->psw.mask & PSW_MASK_ASC;
 384    target_ulong vaddr, raddr;
 385    int prot;
 386
 387    DPRINTF("%s: address 0x%" PRIx64 " rw %d mmu_idx %d\n",
 388            __func__, orig_vaddr, rw, mmu_idx);
 389
 390    orig_vaddr &= TARGET_PAGE_MASK;
 391    vaddr = orig_vaddr;
 392
 393    /* 31-Bit mode */
 394    if (!(env->psw.mask & PSW_MASK_64)) {
 395        vaddr &= 0x7fffffff;
 396    }
 397
 398    if (mmu_translate(env, vaddr, rw, asc, &raddr, &prot)) {
 399        /* Translation ended in exception */
 400        return 1;
 401    }
 402
 403    /* check out of RAM access */
 404    if (raddr > (ram_size + virtio_size)) {
 405        DPRINTF("%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__,
 406                (uint64_t)raddr, (uint64_t)ram_size);
 407        trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_LATER);
 408        return 1;
 409    }
 410
 411    DPRINTF("%s: set tlb %" PRIx64 " -> %" PRIx64 " (%x)\n", __func__,
 412            (uint64_t)vaddr, (uint64_t)raddr, prot);
 413
 414    tlb_set_page(env, orig_vaddr, raddr, prot,
 415                 mmu_idx, TARGET_PAGE_SIZE);
 416
 417    return 0;
 418}
 419
 420hwaddr cpu_get_phys_page_debug(CPUS390XState *env,
 421                                           target_ulong vaddr)
 422{
 423    target_ulong raddr;
 424    int prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
 425    int old_exc = env->exception_index;
 426    uint64_t asc = env->psw.mask & PSW_MASK_ASC;
 427
 428    /* 31-Bit mode */
 429    if (!(env->psw.mask & PSW_MASK_64)) {
 430        vaddr &= 0x7fffffff;
 431    }
 432
 433    mmu_translate(env, vaddr, 2, asc, &raddr, &prot);
 434    env->exception_index = old_exc;
 435
 436    return raddr;
 437}
 438
 439void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr)
 440{
 441    if (mask & PSW_MASK_WAIT) {
 442        S390CPU *cpu = s390_env_get_cpu(env);
 443        CPUState *cs = CPU(cpu);
 444        if (!(mask & (PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK))) {
 445            if (s390_del_running_cpu(cpu) == 0) {
 446#ifndef CONFIG_USER_ONLY
 447                qemu_system_shutdown_request();
 448#endif
 449            }
 450        }
 451        cs->halted = 1;
 452        env->exception_index = EXCP_HLT;
 453    }
 454
 455    env->psw.addr = addr;
 456    env->psw.mask = mask;
 457    env->cc_op = (mask >> 44) & 3;
 458}
 459
 460static uint64_t get_psw_mask(CPUS390XState *env)
 461{
 462    uint64_t r;
 463
 464    env->cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst, env->cc_vr);
 465
 466    r = env->psw.mask;
 467    r &= ~PSW_MASK_CC;
 468    assert(!(env->cc_op & ~3));
 469    r |= (uint64_t)env->cc_op << 44;
 470
 471    return r;
 472}
 473
 474static LowCore *cpu_map_lowcore(CPUS390XState *env)
 475{
 476    LowCore *lowcore;
 477    hwaddr len = sizeof(LowCore);
 478
 479    lowcore = cpu_physical_memory_map(env->psa, &len, 1);
 480
 481    if (len < sizeof(LowCore)) {
 482        cpu_abort(env, "Could not map lowcore\n");
 483    }
 484
 485    return lowcore;
 486}
 487
 488static void cpu_unmap_lowcore(LowCore *lowcore)
 489{
 490    cpu_physical_memory_unmap(lowcore, sizeof(LowCore), 1, sizeof(LowCore));
 491}
 492
 493void *s390_cpu_physical_memory_map(CPUS390XState *env, hwaddr addr, hwaddr *len,
 494                                   int is_write)
 495{
 496    hwaddr start = addr;
 497
 498    /* Mind the prefix area. */
 499    if (addr < 8192) {
 500        /* Map the lowcore. */
 501        start += env->psa;
 502        *len = MIN(*len, 8192 - addr);
 503    } else if ((addr >= env->psa) && (addr < env->psa + 8192)) {
 504        /* Map the 0 page. */
 505        start -= env->psa;
 506        *len = MIN(*len, 8192 - start);
 507    }
 508
 509    return cpu_physical_memory_map(start, len, is_write);
 510}
 511
 512void s390_cpu_physical_memory_unmap(CPUS390XState *env, void *addr, hwaddr len,
 513                                    int is_write)
 514{
 515    cpu_physical_memory_unmap(addr, len, is_write, len);
 516}
 517
 518static void do_svc_interrupt(CPUS390XState *env)
 519{
 520    uint64_t mask, addr;
 521    LowCore *lowcore;
 522
 523    lowcore = cpu_map_lowcore(env);
 524
 525    lowcore->svc_code = cpu_to_be16(env->int_svc_code);
 526    lowcore->svc_ilen = cpu_to_be16(env->int_svc_ilen);
 527    lowcore->svc_old_psw.mask = cpu_to_be64(get_psw_mask(env));
 528    lowcore->svc_old_psw.addr = cpu_to_be64(env->psw.addr + env->int_svc_ilen);
 529    mask = be64_to_cpu(lowcore->svc_new_psw.mask);
 530    addr = be64_to_cpu(lowcore->svc_new_psw.addr);
 531
 532    cpu_unmap_lowcore(lowcore);
 533
 534    load_psw(env, mask, addr);
 535}
 536
 537static void do_program_interrupt(CPUS390XState *env)
 538{
 539    uint64_t mask, addr;
 540    LowCore *lowcore;
 541    int ilen = env->int_pgm_ilen;
 542
 543    switch (ilen) {
 544    case ILEN_LATER:
 545        ilen = get_ilen(cpu_ldub_code(env, env->psw.addr));
 546        break;
 547    case ILEN_LATER_INC:
 548        ilen = get_ilen(cpu_ldub_code(env, env->psw.addr));
 549        env->psw.addr += ilen;
 550        break;
 551    default:
 552        assert(ilen == 2 || ilen == 4 || ilen == 6);
 553    }
 554
 555    qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilen=%d\n",
 556                  __func__, env->int_pgm_code, ilen);
 557
 558    lowcore = cpu_map_lowcore(env);
 559
 560    lowcore->pgm_ilen = cpu_to_be16(ilen);
 561    lowcore->pgm_code = cpu_to_be16(env->int_pgm_code);
 562    lowcore->program_old_psw.mask = cpu_to_be64(get_psw_mask(env));
 563    lowcore->program_old_psw.addr = cpu_to_be64(env->psw.addr);
 564    mask = be64_to_cpu(lowcore->program_new_psw.mask);
 565    addr = be64_to_cpu(lowcore->program_new_psw.addr);
 566
 567    cpu_unmap_lowcore(lowcore);
 568
 569    DPRINTF("%s: %x %x %" PRIx64 " %" PRIx64 "\n", __func__,
 570            env->int_pgm_code, ilen, env->psw.mask,
 571            env->psw.addr);
 572
 573    load_psw(env, mask, addr);
 574}
 575
 576#define VIRTIO_SUBCODE_64 0x0D00
 577
 578static void do_ext_interrupt(CPUS390XState *env)
 579{
 580    uint64_t mask, addr;
 581    LowCore *lowcore;
 582    ExtQueue *q;
 583
 584    if (!(env->psw.mask & PSW_MASK_EXT)) {
 585        cpu_abort(env, "Ext int w/o ext mask\n");
 586    }
 587
 588    if (env->ext_index < 0 || env->ext_index > MAX_EXT_QUEUE) {
 589        cpu_abort(env, "Ext queue overrun: %d\n", env->ext_index);
 590    }
 591
 592    q = &env->ext_queue[env->ext_index];
 593    lowcore = cpu_map_lowcore(env);
 594
 595    lowcore->ext_int_code = cpu_to_be16(q->code);
 596    lowcore->ext_params = cpu_to_be32(q->param);
 597    lowcore->ext_params2 = cpu_to_be64(q->param64);
 598    lowcore->external_old_psw.mask = cpu_to_be64(get_psw_mask(env));
 599    lowcore->external_old_psw.addr = cpu_to_be64(env->psw.addr);
 600    lowcore->cpu_addr = cpu_to_be16(env->cpu_num | VIRTIO_SUBCODE_64);
 601    mask = be64_to_cpu(lowcore->external_new_psw.mask);
 602    addr = be64_to_cpu(lowcore->external_new_psw.addr);
 603
 604    cpu_unmap_lowcore(lowcore);
 605
 606    env->ext_index--;
 607    if (env->ext_index == -1) {
 608        env->pending_int &= ~INTERRUPT_EXT;
 609    }
 610
 611    DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
 612            env->psw.mask, env->psw.addr);
 613
 614    load_psw(env, mask, addr);
 615}
 616
 617static void do_io_interrupt(CPUS390XState *env)
 618{
 619    LowCore *lowcore;
 620    IOIntQueue *q;
 621    uint8_t isc;
 622    int disable = 1;
 623    int found = 0;
 624
 625    if (!(env->psw.mask & PSW_MASK_IO)) {
 626        cpu_abort(env, "I/O int w/o I/O mask\n");
 627    }
 628
 629    for (isc = 0; isc < ARRAY_SIZE(env->io_index); isc++) {
 630        uint64_t isc_bits;
 631
 632        if (env->io_index[isc] < 0) {
 633            continue;
 634        }
 635        if (env->io_index[isc] > MAX_IO_QUEUE) {
 636            cpu_abort(env, "I/O queue overrun for isc %d: %d\n",
 637                      isc, env->io_index[isc]);
 638        }
 639
 640        q = &env->io_queue[env->io_index[isc]][isc];
 641        isc_bits = ISC_TO_ISC_BITS(IO_INT_WORD_ISC(q->word));
 642        if (!(env->cregs[6] & isc_bits)) {
 643            disable = 0;
 644            continue;
 645        }
 646        if (!found) {
 647            uint64_t mask, addr;
 648
 649            found = 1;
 650            lowcore = cpu_map_lowcore(env);
 651
 652            lowcore->subchannel_id = cpu_to_be16(q->id);
 653            lowcore->subchannel_nr = cpu_to_be16(q->nr);
 654            lowcore->io_int_parm = cpu_to_be32(q->parm);
 655            lowcore->io_int_word = cpu_to_be32(q->word);
 656            lowcore->io_old_psw.mask = cpu_to_be64(get_psw_mask(env));
 657            lowcore->io_old_psw.addr = cpu_to_be64(env->psw.addr);
 658            mask = be64_to_cpu(lowcore->io_new_psw.mask);
 659            addr = be64_to_cpu(lowcore->io_new_psw.addr);
 660
 661            cpu_unmap_lowcore(lowcore);
 662
 663            env->io_index[isc]--;
 664
 665            DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
 666                    env->psw.mask, env->psw.addr);
 667            load_psw(env, mask, addr);
 668        }
 669        if (env->io_index[isc] >= 0) {
 670            disable = 0;
 671        }
 672        continue;
 673    }
 674
 675    if (disable) {
 676        env->pending_int &= ~INTERRUPT_IO;
 677    }
 678
 679}
 680
 681static void do_mchk_interrupt(CPUS390XState *env)
 682{
 683    uint64_t mask, addr;
 684    LowCore *lowcore;
 685    MchkQueue *q;
 686    int i;
 687
 688    if (!(env->psw.mask & PSW_MASK_MCHECK)) {
 689        cpu_abort(env, "Machine check w/o mchk mask\n");
 690    }
 691
 692    if (env->mchk_index < 0 || env->mchk_index > MAX_MCHK_QUEUE) {
 693        cpu_abort(env, "Mchk queue overrun: %d\n", env->mchk_index);
 694    }
 695
 696    q = &env->mchk_queue[env->mchk_index];
 697
 698    if (q->type != 1) {
 699        /* Don't know how to handle this... */
 700        cpu_abort(env, "Unknown machine check type %d\n", q->type);
 701    }
 702    if (!(env->cregs[14] & (1 << 28))) {
 703        /* CRW machine checks disabled */
 704        return;
 705    }
 706
 707    lowcore = cpu_map_lowcore(env);
 708
 709    for (i = 0; i < 16; i++) {
 710        lowcore->floating_pt_save_area[i] = cpu_to_be64(env->fregs[i].ll);
 711        lowcore->gpregs_save_area[i] = cpu_to_be64(env->regs[i]);
 712        lowcore->access_regs_save_area[i] = cpu_to_be32(env->aregs[i]);
 713        lowcore->cregs_save_area[i] = cpu_to_be64(env->cregs[i]);
 714    }
 715    lowcore->prefixreg_save_area = cpu_to_be32(env->psa);
 716    lowcore->fpt_creg_save_area = cpu_to_be32(env->fpc);
 717    lowcore->tod_progreg_save_area = cpu_to_be32(env->todpr);
 718    lowcore->cpu_timer_save_area[0] = cpu_to_be32(env->cputm >> 32);
 719    lowcore->cpu_timer_save_area[1] = cpu_to_be32((uint32_t)env->cputm);
 720    lowcore->clock_comp_save_area[0] = cpu_to_be32(env->ckc >> 32);
 721    lowcore->clock_comp_save_area[1] = cpu_to_be32((uint32_t)env->ckc);
 722
 723    lowcore->mcck_interruption_code[0] = cpu_to_be32(0x00400f1d);
 724    lowcore->mcck_interruption_code[1] = cpu_to_be32(0x40330000);
 725    lowcore->mcck_old_psw.mask = cpu_to_be64(get_psw_mask(env));
 726    lowcore->mcck_old_psw.addr = cpu_to_be64(env->psw.addr);
 727    mask = be64_to_cpu(lowcore->mcck_new_psw.mask);
 728    addr = be64_to_cpu(lowcore->mcck_new_psw.addr);
 729
 730    cpu_unmap_lowcore(lowcore);
 731
 732    env->mchk_index--;
 733    if (env->mchk_index == -1) {
 734        env->pending_int &= ~INTERRUPT_MCHK;
 735    }
 736
 737    DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
 738            env->psw.mask, env->psw.addr);
 739
 740    load_psw(env, mask, addr);
 741}
 742
 743void s390_cpu_do_interrupt(CPUState *cs)
 744{
 745    S390CPU *cpu = S390_CPU(cs);
 746    CPUS390XState *env = &cpu->env;
 747
 748    qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n",
 749                  __func__, env->exception_index, env->psw.addr);
 750
 751    s390_add_running_cpu(cpu);
 752    /* handle machine checks */
 753    if ((env->psw.mask & PSW_MASK_MCHECK) &&
 754        (env->exception_index == -1)) {
 755        if (env->pending_int & INTERRUPT_MCHK) {
 756            env->exception_index = EXCP_MCHK;
 757        }
 758    }
 759    /* handle external interrupts */
 760    if ((env->psw.mask & PSW_MASK_EXT) &&
 761        env->exception_index == -1) {
 762        if (env->pending_int & INTERRUPT_EXT) {
 763            /* code is already in env */
 764            env->exception_index = EXCP_EXT;
 765        } else if (env->pending_int & INTERRUPT_TOD) {
 766            cpu_inject_ext(cpu, 0x1004, 0, 0);
 767            env->exception_index = EXCP_EXT;
 768            env->pending_int &= ~INTERRUPT_EXT;
 769            env->pending_int &= ~INTERRUPT_TOD;
 770        } else if (env->pending_int & INTERRUPT_CPUTIMER) {
 771            cpu_inject_ext(cpu, 0x1005, 0, 0);
 772            env->exception_index = EXCP_EXT;
 773            env->pending_int &= ~INTERRUPT_EXT;
 774            env->pending_int &= ~INTERRUPT_TOD;
 775        }
 776    }
 777    /* handle I/O interrupts */
 778    if ((env->psw.mask & PSW_MASK_IO) &&
 779        (env->exception_index == -1)) {
 780        if (env->pending_int & INTERRUPT_IO) {
 781            env->exception_index = EXCP_IO;
 782        }
 783    }
 784
 785    switch (env->exception_index) {
 786    case EXCP_PGM:
 787        do_program_interrupt(env);
 788        break;
 789    case EXCP_SVC:
 790        do_svc_interrupt(env);
 791        break;
 792    case EXCP_EXT:
 793        do_ext_interrupt(env);
 794        break;
 795    case EXCP_IO:
 796        do_io_interrupt(env);
 797        break;
 798    case EXCP_MCHK:
 799        do_mchk_interrupt(env);
 800        break;
 801    }
 802    env->exception_index = -1;
 803
 804    if (!env->pending_int) {
 805        cs->interrupt_request &= ~CPU_INTERRUPT_HARD;
 806    }
 807}
 808
 809#endif /* CONFIG_USER_ONLY */
 810