helper.c 15.6 KB
Newer Older
A
Alexander Graf 已提交
1 2 3 4
/*
 *  S/390 helpers
 *
 *  Copyright (c) 2009 Ulrich Hecht
5
 *  Copyright (c) 2011 Alexander Graf
A
Alexander Graf 已提交
6 7 8 9 10 11 12 13 14 15 16 17
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * 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/>.
A
Alexander Graf 已提交
19 20 21
 */

#include "cpu.h"
22
#include "exec/gdbstub.h"
23
#include "qemu/timer.h"
P
Paolo Bonzini 已提交
24
#include "exec/cpu_ldst.h"
25
#ifndef CONFIG_USER_ONLY
26
#include "sysemu/sysemu.h"
27
#endif
A
Alexander Graf 已提交
28

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
//#define DEBUG_S390
//#define DEBUG_S390_STDOUT

#ifdef DEBUG_S390
#ifdef DEBUG_S390_STDOUT
#define DPRINTF(fmt, ...) \
    do { fprintf(stderr, fmt, ## __VA_ARGS__); \
         qemu_log(fmt, ##__VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...) \
    do { qemu_log(fmt, ## __VA_ARGS__); } while (0)
#endif
#else
#define DPRINTF(fmt, ...) \
    do { } while (0)
#endif


#ifndef CONFIG_USER_ONLY
A
Andreas Färber 已提交
48
void s390x_tod_timer(void *opaque)
49
{
50 51
    S390CPU *cpu = opaque;
    CPUS390XState *env = &cpu->env;
52 53

    env->pending_int |= INTERRUPT_TOD;
54
    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
55 56
}

A
Andreas Färber 已提交
57
void s390x_cpu_timer(void *opaque)
58
{
59 60
    S390CPU *cpu = opaque;
    CPUS390XState *env = &cpu->env;
61 62

    env->pending_int |= INTERRUPT_CPUTIMER;
63
    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
64 65
}
#endif
66

67
S390CPU *cpu_s390x_init(const char *cpu_model)
A
Alexander Graf 已提交
68
{
A
Andreas Färber 已提交
69
    S390CPU *cpu;
A
Alexander Graf 已提交
70

A
Andreas Färber 已提交
71
    cpu = S390_CPU(object_new(TYPE_S390_CPU));
72 73 74

    object_property_set_bool(OBJECT(cpu), true, "realized", NULL);

75
    return cpu;
A
Alexander Graf 已提交
76 77
}

78 79
#if defined(CONFIG_USER_ONLY)

80
void s390_cpu_do_interrupt(CPUState *cs)
81
{
82
    cs->exception_index = -1;
83 84
}

85 86
int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr address,
                              int rw, int mmu_idx)
87
{
88 89
    S390CPU *cpu = S390_CPU(cs);

90
    cs->exception_index = EXCP_PGM;
91
    cpu->env.int_pgm_code = PGM_ADDRESSING;
92 93
    /* On real machines this value is dropped into LowMem.  Since this
       is userland, simply put this someplace that cpu_loop can find it.  */
94
    cpu->env.__excp_addr = address;
95 96 97
    return 1;
}

A
Andreas Färber 已提交
98
#else /* !CONFIG_USER_ONLY */
99 100

/* Ensure to exit the TB after this call! */
101
void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen)
102
{
103 104 105
    CPUState *cs = CPU(s390_env_get_cpu(env));

    cs->exception_index = EXCP_PGM;
106
    env->int_pgm_code = code;
107
    env->int_pgm_ilen = ilen;
108 109
}

110 111
int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr,
                              int rw, int mmu_idx)
112
{
113 114
    S390CPU *cpu = S390_CPU(cs);
    CPUS390XState *env = &cpu->env;
115 116
    uint64_t asc = env->psw.mask & PSW_MASK_ASC;
    target_ulong vaddr, raddr;
117 118
    int prot;

119
    DPRINTF("%s: address 0x%" VADDR_PRIx " rw %d mmu_idx %d\n",
A
Andreas Färber 已提交
120
            __func__, orig_vaddr, rw, mmu_idx);
121

B
Blue Swirl 已提交
122 123
    orig_vaddr &= TARGET_PAGE_MASK;
    vaddr = orig_vaddr;
124 125 126 127 128 129 130 131 132 133

    /* 31-Bit mode */
    if (!(env->psw.mask & PSW_MASK_64)) {
        vaddr &= 0x7fffffff;
    }

    if (mmu_translate(env, vaddr, rw, asc, &raddr, &prot)) {
        /* Translation ended in exception */
        return 1;
    }
134

135 136
    /* check out of RAM access */
    if (raddr > (ram_size + virtio_size)) {
137 138
        DPRINTF("%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__,
                (uint64_t)raddr, (uint64_t)ram_size);
139
        trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_LATER);
140 141
        return 1;
    }
142

143 144
    qemu_log_mask(CPU_LOG_MMU, "%s: set tlb %" PRIx64 " -> %" PRIx64 " (%x)\n",
            __func__, (uint64_t)vaddr, (uint64_t)raddr, prot);
145

146
    tlb_set_page(cs, orig_vaddr, raddr, prot,
P
Paul Brook 已提交
147
                 mmu_idx, TARGET_PAGE_SIZE);
148

P
Paul Brook 已提交
149
    return 0;
150
}
151

152
hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr)
153
{
154 155
    S390CPU *cpu = S390_CPU(cs);
    CPUS390XState *env = &cpu->env;
156 157
    target_ulong raddr;
    int prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
158
    int old_exc = cs->exception_index;
159 160 161 162 163 164 165 166
    uint64_t asc = env->psw.mask & PSW_MASK_ASC;

    /* 31-Bit mode */
    if (!(env->psw.mask & PSW_MASK_64)) {
        vaddr &= 0x7fffffff;
    }

    mmu_translate(env, vaddr, 2, asc, &raddr, &prot);
167
    cs->exception_index = old_exc;
168 169 170 171

    return raddr;
}

172 173 174 175 176 177 178 179 180 181 182 183
hwaddr s390_cpu_get_phys_addr_debug(CPUState *cs, vaddr vaddr)
{
    hwaddr phys_addr;
    target_ulong page;

    page = vaddr & TARGET_PAGE_MASK;
    phys_addr = cpu_get_phys_page_debug(cs, page);
    phys_addr += (vaddr & ~TARGET_PAGE_MASK);

    return phys_addr;
}

184
void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr)
185
{
186 187 188 189
    env->psw.addr = addr;
    env->psw.mask = mask;
    env->cc_op = (mask >> 44) & 3;

190
    if (mask & PSW_MASK_WAIT) {
191
        S390CPU *cpu = s390_env_get_cpu(env);
192
        if (s390_cpu_halt(cpu) == 0) {
193
#ifndef CONFIG_USER_ONLY
194
            qemu_system_shutdown_request();
195
#endif
196 197 198 199
        }
    }
}

200
static uint64_t get_psw_mask(CPUS390XState *env)
201
{
202
    uint64_t r;
203 204 205

    env->cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst, env->cc_vr);

206 207
    r = env->psw.mask;
    r &= ~PSW_MASK_CC;
208
    assert(!(env->cc_op & ~3));
209
    r |= (uint64_t)env->cc_op << 44;
210 211 212 213

    return r;
}

C
Cornelia Huck 已提交
214 215
static LowCore *cpu_map_lowcore(CPUS390XState *env)
{
216
    S390CPU *cpu = s390_env_get_cpu(env);
C
Cornelia Huck 已提交
217 218 219 220 221 222
    LowCore *lowcore;
    hwaddr len = sizeof(LowCore);

    lowcore = cpu_physical_memory_map(env->psa, &len, 1);

    if (len < sizeof(LowCore)) {
223
        cpu_abort(CPU(cpu), "Could not map lowcore\n");
C
Cornelia Huck 已提交
224 225 226 227 228 229 230 231 232 233
    }

    return lowcore;
}

static void cpu_unmap_lowcore(LowCore *lowcore)
{
    cpu_physical_memory_unmap(lowcore, sizeof(LowCore), 1, sizeof(LowCore));
}

234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
void *s390_cpu_physical_memory_map(CPUS390XState *env, hwaddr addr, hwaddr *len,
                                   int is_write)
{
    hwaddr start = addr;

    /* Mind the prefix area. */
    if (addr < 8192) {
        /* Map the lowcore. */
        start += env->psa;
        *len = MIN(*len, 8192 - addr);
    } else if ((addr >= env->psa) && (addr < env->psa + 8192)) {
        /* Map the 0 page. */
        start -= env->psa;
        *len = MIN(*len, 8192 - start);
    }

    return cpu_physical_memory_map(start, len, is_write);
}

void s390_cpu_physical_memory_unmap(CPUS390XState *env, void *addr, hwaddr len,
                                    int is_write)
{
    cpu_physical_memory_unmap(addr, len, is_write, len);
}

259
static void do_svc_interrupt(CPUS390XState *env)
260 261 262 263
{
    uint64_t mask, addr;
    LowCore *lowcore;

C
Cornelia Huck 已提交
264
    lowcore = cpu_map_lowcore(env);
265 266

    lowcore->svc_code = cpu_to_be16(env->int_svc_code);
267
    lowcore->svc_ilen = cpu_to_be16(env->int_svc_ilen);
268
    lowcore->svc_old_psw.mask = cpu_to_be64(get_psw_mask(env));
269
    lowcore->svc_old_psw.addr = cpu_to_be64(env->psw.addr + env->int_svc_ilen);
270 271 272
    mask = be64_to_cpu(lowcore->svc_new_psw.mask);
    addr = be64_to_cpu(lowcore->svc_new_psw.addr);

C
Cornelia Huck 已提交
273
    cpu_unmap_lowcore(lowcore);
274 275 276 277

    load_psw(env, mask, addr);
}

278
static void do_program_interrupt(CPUS390XState *env)
279 280 281
{
    uint64_t mask, addr;
    LowCore *lowcore;
282
    int ilen = env->int_pgm_ilen;
283

284 285 286
    switch (ilen) {
    case ILEN_LATER:
        ilen = get_ilen(cpu_ldub_code(env, env->psw.addr));
287
        break;
288 289 290
    case ILEN_LATER_INC:
        ilen = get_ilen(cpu_ldub_code(env, env->psw.addr));
        env->psw.addr += ilen;
291
        break;
292 293
    default:
        assert(ilen == 2 || ilen == 4 || ilen == 6);
294 295
    }

296 297
    qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilen=%d\n",
                  __func__, env->int_pgm_code, ilen);
298

C
Cornelia Huck 已提交
299
    lowcore = cpu_map_lowcore(env);
300

301
    lowcore->pgm_ilen = cpu_to_be16(ilen);
302 303 304 305 306 307
    lowcore->pgm_code = cpu_to_be16(env->int_pgm_code);
    lowcore->program_old_psw.mask = cpu_to_be64(get_psw_mask(env));
    lowcore->program_old_psw.addr = cpu_to_be64(env->psw.addr);
    mask = be64_to_cpu(lowcore->program_new_psw.mask);
    addr = be64_to_cpu(lowcore->program_new_psw.addr);

C
Cornelia Huck 已提交
308
    cpu_unmap_lowcore(lowcore);
309

B
Blue Swirl 已提交
310
    DPRINTF("%s: %x %x %" PRIx64 " %" PRIx64 "\n", __func__,
311
            env->int_pgm_code, ilen, env->psw.mask,
312 313 314 315 316 317 318
            env->psw.addr);

    load_psw(env, mask, addr);
}

#define VIRTIO_SUBCODE_64 0x0D00

319
static void do_ext_interrupt(CPUS390XState *env)
320
{
321
    S390CPU *cpu = s390_env_get_cpu(env);
322 323 324 325 326
    uint64_t mask, addr;
    LowCore *lowcore;
    ExtQueue *q;

    if (!(env->psw.mask & PSW_MASK_EXT)) {
327
        cpu_abort(CPU(cpu), "Ext int w/o ext mask\n");
328 329
    }

330
    if (env->ext_index < 0 || env->ext_index >= MAX_EXT_QUEUE) {
331
        cpu_abort(CPU(cpu), "Ext queue overrun: %d\n", env->ext_index);
332 333 334
    }

    q = &env->ext_queue[env->ext_index];
C
Cornelia Huck 已提交
335
    lowcore = cpu_map_lowcore(env);
336 337 338 339 340 341 342 343 344 345

    lowcore->ext_int_code = cpu_to_be16(q->code);
    lowcore->ext_params = cpu_to_be32(q->param);
    lowcore->ext_params2 = cpu_to_be64(q->param64);
    lowcore->external_old_psw.mask = cpu_to_be64(get_psw_mask(env));
    lowcore->external_old_psw.addr = cpu_to_be64(env->psw.addr);
    lowcore->cpu_addr = cpu_to_be16(env->cpu_num | VIRTIO_SUBCODE_64);
    mask = be64_to_cpu(lowcore->external_new_psw.mask);
    addr = be64_to_cpu(lowcore->external_new_psw.addr);

C
Cornelia Huck 已提交
346
    cpu_unmap_lowcore(lowcore);
347 348 349 350 351 352

    env->ext_index--;
    if (env->ext_index == -1) {
        env->pending_int &= ~INTERRUPT_EXT;
    }

B
Blue Swirl 已提交
353
    DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
354 355 356 357
            env->psw.mask, env->psw.addr);

    load_psw(env, mask, addr);
}
358

359 360
static void do_io_interrupt(CPUS390XState *env)
{
361
    S390CPU *cpu = s390_env_get_cpu(env);
362 363 364 365 366 367 368
    LowCore *lowcore;
    IOIntQueue *q;
    uint8_t isc;
    int disable = 1;
    int found = 0;

    if (!(env->psw.mask & PSW_MASK_IO)) {
369
        cpu_abort(CPU(cpu), "I/O int w/o I/O mask\n");
370 371 372
    }

    for (isc = 0; isc < ARRAY_SIZE(env->io_index); isc++) {
C
Cornelia Huck 已提交
373 374
        uint64_t isc_bits;

375 376 377
        if (env->io_index[isc] < 0) {
            continue;
        }
378
        if (env->io_index[isc] >= MAX_IO_QUEUE) {
379
            cpu_abort(CPU(cpu), "I/O queue overrun for isc %d: %d\n",
380 381 382 383
                      isc, env->io_index[isc]);
        }

        q = &env->io_queue[env->io_index[isc]][isc];
C
Cornelia Huck 已提交
384 385
        isc_bits = ISC_TO_ISC_BITS(IO_INT_WORD_ISC(q->word));
        if (!(env->cregs[6] & isc_bits)) {
386 387 388
            disable = 0;
            continue;
        }
389 390
        if (!found) {
            uint64_t mask, addr;
391

392 393
            found = 1;
            lowcore = cpu_map_lowcore(env);
394

395 396 397 398 399 400 401 402
            lowcore->subchannel_id = cpu_to_be16(q->id);
            lowcore->subchannel_nr = cpu_to_be16(q->nr);
            lowcore->io_int_parm = cpu_to_be32(q->parm);
            lowcore->io_int_word = cpu_to_be32(q->word);
            lowcore->io_old_psw.mask = cpu_to_be64(get_psw_mask(env));
            lowcore->io_old_psw.addr = cpu_to_be64(env->psw.addr);
            mask = be64_to_cpu(lowcore->io_new_psw.mask);
            addr = be64_to_cpu(lowcore->io_new_psw.addr);
403

404 405 406 407 408 409 410 411
            cpu_unmap_lowcore(lowcore);

            env->io_index[isc]--;

            DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
                    env->psw.mask, env->psw.addr);
            load_psw(env, mask, addr);
        }
412
        if (env->io_index[isc] >= 0) {
413 414
            disable = 0;
        }
415
        continue;
416 417 418 419 420 421 422 423 424 425
    }

    if (disable) {
        env->pending_int &= ~INTERRUPT_IO;
    }

}

static void do_mchk_interrupt(CPUS390XState *env)
{
426
    S390CPU *cpu = s390_env_get_cpu(env);
427 428 429 430 431 432
    uint64_t mask, addr;
    LowCore *lowcore;
    MchkQueue *q;
    int i;

    if (!(env->psw.mask & PSW_MASK_MCHECK)) {
433
        cpu_abort(CPU(cpu), "Machine check w/o mchk mask\n");
434 435
    }

436
    if (env->mchk_index < 0 || env->mchk_index >= MAX_MCHK_QUEUE) {
437
        cpu_abort(CPU(cpu), "Mchk queue overrun: %d\n", env->mchk_index);
438 439 440 441 442 443
    }

    q = &env->mchk_queue[env->mchk_index];

    if (q->type != 1) {
        /* Don't know how to handle this... */
444
        cpu_abort(CPU(cpu), "Unknown machine check type %d\n", q->type);
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
    }
    if (!(env->cregs[14] & (1 << 28))) {
        /* CRW machine checks disabled */
        return;
    }

    lowcore = cpu_map_lowcore(env);

    for (i = 0; i < 16; i++) {
        lowcore->floating_pt_save_area[i] = cpu_to_be64(env->fregs[i].ll);
        lowcore->gpregs_save_area[i] = cpu_to_be64(env->regs[i]);
        lowcore->access_regs_save_area[i] = cpu_to_be32(env->aregs[i]);
        lowcore->cregs_save_area[i] = cpu_to_be64(env->cregs[i]);
    }
    lowcore->prefixreg_save_area = cpu_to_be32(env->psa);
    lowcore->fpt_creg_save_area = cpu_to_be32(env->fpc);
    lowcore->tod_progreg_save_area = cpu_to_be32(env->todpr);
    lowcore->cpu_timer_save_area[0] = cpu_to_be32(env->cputm >> 32);
    lowcore->cpu_timer_save_area[1] = cpu_to_be32((uint32_t)env->cputm);
    lowcore->clock_comp_save_area[0] = cpu_to_be32(env->ckc >> 32);
    lowcore->clock_comp_save_area[1] = cpu_to_be32((uint32_t)env->ckc);

    lowcore->mcck_interruption_code[0] = cpu_to_be32(0x00400f1d);
    lowcore->mcck_interruption_code[1] = cpu_to_be32(0x40330000);
    lowcore->mcck_old_psw.mask = cpu_to_be64(get_psw_mask(env));
    lowcore->mcck_old_psw.addr = cpu_to_be64(env->psw.addr);
    mask = be64_to_cpu(lowcore->mcck_new_psw.mask);
    addr = be64_to_cpu(lowcore->mcck_new_psw.addr);

    cpu_unmap_lowcore(lowcore);

    env->mchk_index--;
    if (env->mchk_index == -1) {
        env->pending_int &= ~INTERRUPT_MCHK;
    }

    DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
            env->psw.mask, env->psw.addr);

    load_psw(env, mask, addr);
}

487
void s390_cpu_do_interrupt(CPUState *cs)
488
{
489 490
    S390CPU *cpu = S390_CPU(cs);
    CPUS390XState *env = &cpu->env;
491

492
    qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n",
493
                  __func__, cs->exception_index, env->psw.addr);
494

495
    s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
496 497
    /* handle machine checks */
    if ((env->psw.mask & PSW_MASK_MCHECK) &&
498
        (cs->exception_index == -1)) {
499
        if (env->pending_int & INTERRUPT_MCHK) {
500
            cs->exception_index = EXCP_MCHK;
501 502
        }
    }
503 504
    /* handle external interrupts */
    if ((env->psw.mask & PSW_MASK_EXT) &&
505
        cs->exception_index == -1) {
506 507
        if (env->pending_int & INTERRUPT_EXT) {
            /* code is already in env */
508
            cs->exception_index = EXCP_EXT;
509
        } else if (env->pending_int & INTERRUPT_TOD) {
510
            cpu_inject_ext(cpu, 0x1004, 0, 0);
511
            cs->exception_index = EXCP_EXT;
512 513 514
            env->pending_int &= ~INTERRUPT_EXT;
            env->pending_int &= ~INTERRUPT_TOD;
        } else if (env->pending_int & INTERRUPT_CPUTIMER) {
515
            cpu_inject_ext(cpu, 0x1005, 0, 0);
516
            cs->exception_index = EXCP_EXT;
517 518 519 520
            env->pending_int &= ~INTERRUPT_EXT;
            env->pending_int &= ~INTERRUPT_TOD;
        }
    }
521 522
    /* handle I/O interrupts */
    if ((env->psw.mask & PSW_MASK_IO) &&
523
        (cs->exception_index == -1)) {
524
        if (env->pending_int & INTERRUPT_IO) {
525
            cs->exception_index = EXCP_IO;
526 527
        }
    }
528

529
    switch (cs->exception_index) {
530 531 532 533 534 535 536 537 538
    case EXCP_PGM:
        do_program_interrupt(env);
        break;
    case EXCP_SVC:
        do_svc_interrupt(env);
        break;
    case EXCP_EXT:
        do_ext_interrupt(env);
        break;
539 540 541 542 543 544
    case EXCP_IO:
        do_io_interrupt(env);
        break;
    case EXCP_MCHK:
        do_mchk_interrupt(env);
        break;
545
    }
546
    cs->exception_index = -1;
547 548

    if (!env->pending_int) {
549
        cs->interrupt_request &= ~CPU_INTERRUPT_HARD;
550
    }
551
}
552

553 554 555 556 557 558 559 560 561 562 563 564 565
bool s390_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
{
    if (interrupt_request & CPU_INTERRUPT_HARD) {
        S390CPU *cpu = S390_CPU(cs);
        CPUS390XState *env = &cpu->env;

        if (env->psw.mask & PSW_MASK_EXT) {
            s390_cpu_do_interrupt(cs);
            return true;
        }
    }
    return false;
}
566
#endif /* CONFIG_USER_ONLY */