misc_helper.c 13.6 KB
Newer Older
A
Alexander Graf 已提交
1
/*
2
 *  S/390 misc helper routines
A
Alexander Graf 已提交
3
 *
A
Alexander Graf 已提交
4
 *  Copyright (c) 2009 Ulrich Hecht
A
Alexander Graf 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17
 *  Copyright (c) 2009 Alexander Graf
 *
 * 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
 */

B
Blue Swirl 已提交
21
#include "cpu.h"
22
#include "exec/memory.h"
23
#include "qemu/host-utils.h"
24
#include "exec/helper-proto.h"
A
Alexander Graf 已提交
25
#include <string.h>
26
#include "sysemu/kvm.h"
27
#include "qemu/timer.h"
28
#include "exec/address-spaces.h"
29 30 31
#ifdef CONFIG_KVM
#include <linux/kvm.h>
#endif
P
Paolo Bonzini 已提交
32
#include "exec/cpu_ldst.h"
A
Alexander Graf 已提交
33

B
Blue Swirl 已提交
34
#if !defined(CONFIG_USER_ONLY)
35
#include "sysemu/cpus.h"
36
#include "sysemu/sysemu.h"
37
#include "hw/s390x/ebcdic.h"
38
#include "hw/s390x/ipl.h"
A
Alexander Graf 已提交
39
#endif
40

A
Alexander Graf 已提交
41 42 43 44 45 46 47
/* #define DEBUG_HELPER */
#ifdef DEBUG_HELPER
#define HELPER_LOG(x...) qemu_log(x)
#else
#define HELPER_LOG(x...)
#endif

48 49 50 51
/* Raise an exception dynamically from a helper function.  */
void QEMU_NORETURN runtime_exception(CPUS390XState *env, int excp,
                                     uintptr_t retaddr)
{
52
    CPUState *cs = CPU(s390_env_get_cpu(env));
53 54
    int t;

55
    cs->exception_index = EXCP_PGM;
56 57 58
    env->int_pgm_code = excp;

    /* Use the (ultimate) callers address to find the insn that trapped.  */
59
    cpu_restore_state(cs, retaddr);
60 61 62 63 64 65

    /* Advance past the insn.  */
    t = cpu_ldub_code(env, env->psw.addr);
    env->int_pgm_ilen = t = get_ilen(t);
    env->psw.addr += 2 * t;

66
    cpu_loop_exit(cs);
67 68
}

69
/* Raise an exception statically from a TB.  */
70
void HELPER(exception)(CPUS390XState *env, uint32_t excp)
A
Alexander Graf 已提交
71
{
72 73
    CPUState *cs = CPU(s390_env_get_cpu(env));

B
Blue Swirl 已提交
74
    HELPER_LOG("%s: exception %d\n", __func__, excp);
75
    cs->exception_index = excp;
76
    cpu_loop_exit(cs);
A
Alexander Graf 已提交
77 78 79
}

#ifndef CONFIG_USER_ONLY
80

81
void program_interrupt(CPUS390XState *env, uint32_t code, int ilen)
A
Alexander Graf 已提交
82
{
83 84
    S390CPU *cpu = s390_env_get_cpu(env);

85 86
    qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n",
                  env->psw.addr);
A
Alexander Graf 已提交
87 88

    if (kvm_enabled()) {
89
#ifdef CONFIG_KVM
90 91 92 93 94 95
        struct kvm_s390_irq irq = {
            .type = KVM_S390_PROGRAM_INT,
            .u.pgm.code = code,
        };

        kvm_s390_vcpu_interrupt(cpu, &irq);
96
#endif
A
Alexander Graf 已提交
97
    } else {
98 99
        CPUState *cs = CPU(cpu);

A
Alexander Graf 已提交
100
        env->int_pgm_code = code;
101
        env->int_pgm_ilen = ilen;
102
        cs->exception_index = EXCP_PGM;
103
        cpu_loop_exit(cs);
A
Alexander Graf 已提交
104 105 106 107
    }
}

/* SCLP service call */
R
Richard Henderson 已提交
108
uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2)
A
Alexander Graf 已提交
109
{
110
    int r = sclp_service_call(env, r1, r2);
111 112 113 114 115
    if (r < 0) {
        program_interrupt(env, -r, 4);
        return 0;
    }
    return r;
A
Alexander Graf 已提交
116 117
}

118
#ifndef CONFIG_USER_ONLY
119 120 121
static int modified_clear_reset(S390CPU *cpu)
{
    S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
122
    CPUState *t;
123 124 125

    pause_all_vcpus();
    cpu_synchronize_all_states();
126 127 128
    CPU_FOREACH(t) {
        run_on_cpu(t, s390_do_cpu_full_reset, t);
    }
129
    cmma_reset(cpu);
130 131 132 133 134 135 136
    io_subsystem_reset();
    scc->load_normal(CPU(cpu));
    cpu_synchronize_all_post_reset();
    resume_all_vcpus();
    return 0;
}

137 138 139
static int load_normal_reset(S390CPU *cpu)
{
    S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
140
    CPUState *t;
141 142 143

    pause_all_vcpus();
    cpu_synchronize_all_states();
144 145 146
    CPU_FOREACH(t) {
        run_on_cpu(t, s390_do_cpu_reset, t);
    }
147
    cmma_reset(cpu);
148 149 150 151 152 153 154 155
    io_subsystem_reset();
    scc->initial_cpu_reset(CPU(cpu));
    scc->load_normal(CPU(cpu));
    cpu_synchronize_all_post_reset();
    resume_all_vcpus();
    return 0;
}

156
#define DIAG_308_RC_OK              0x0001
157 158
#define DIAG_308_RC_NO_CONF         0x0102
#define DIAG_308_RC_INVALID         0x0402
159

160 161 162 163
void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3)
{
    uint64_t addr =  env->regs[r1];
    uint64_t subcode = env->regs[r3];
164
    IplParameterBlock *iplb;
165 166 167 168 169 170 171 172 173 174 175 176

    if (env->psw.mask & PSW_MASK_PSTATE) {
        program_interrupt(env, PGM_PRIVILEGED, ILEN_LATER_INC);
        return;
    }

    if ((subcode & ~0x0ffffULL) || (subcode > 6)) {
        program_interrupt(env, PGM_SPECIFICATION, ILEN_LATER_INC);
        return;
    }

    switch (subcode) {
177 178 179
    case 0:
        modified_clear_reset(s390_env_get_cpu(env));
        break;
180 181 182
    case 1:
        load_normal_reset(s390_env_get_cpu(env));
        break;
183 184 185 186 187
    case 5:
        if ((r1 & 1) || (addr & 0x0fffULL)) {
            program_interrupt(env, PGM_SPECIFICATION, ILEN_LATER_INC);
            return;
        }
188 189 190 191 192 193 194 195 196 197 198 199 200
        if (!address_space_access_valid(&address_space_memory, addr,
                                        sizeof(IplParameterBlock), false)) {
            program_interrupt(env, PGM_ADDRESSING, ILEN_LATER_INC);
            return;
        }
        iplb = g_malloc0(sizeof(struct IplParameterBlock));
        cpu_physical_memory_read(addr, iplb, sizeof(struct IplParameterBlock));
        if (!s390_ipl_update_diag308(iplb)) {
            env->regs[r1 + 1] = DIAG_308_RC_OK;
        } else {
            env->regs[r1 + 1] = DIAG_308_RC_INVALID;
        }
        g_free(iplb);
201 202 203 204 205 206
        return;
    case 6:
        if ((r1 & 1) || (addr & 0x0fffULL)) {
            program_interrupt(env, PGM_SPECIFICATION, ILEN_LATER_INC);
            return;
        }
207 208 209 210 211 212 213 214 215 216 217 218 219
        if (!address_space_access_valid(&address_space_memory, addr,
                                        sizeof(IplParameterBlock), true)) {
            program_interrupt(env, PGM_ADDRESSING, ILEN_LATER_INC);
            return;
        }
        iplb = s390_ipl_get_iplb();
        if (iplb) {
            cpu_physical_memory_write(addr, iplb,
                                      sizeof(struct IplParameterBlock));
            env->regs[r1 + 1] = DIAG_308_RC_OK;
        } else {
            env->regs[r1 + 1] = DIAG_308_RC_NO_CONF;
        }
220 221 222 223 224 225 226 227
        return;
    default:
        hw_error("Unhandled diag308 subcode %" PRIx64, subcode);
        break;
    }
}
#endif

A
Alexander Graf 已提交
228
/* DIAG */
229 230
uint64_t HELPER(diag)(CPUS390XState *env, uint32_t num, uint64_t mem,
                      uint64_t code)
A
Alexander Graf 已提交
231 232 233 234 235 236
{
    uint64_t r;

    switch (num) {
    case 0x500:
        /* KVM hypercall */
237
        r = s390_virtio_hypercall(env);
A
Alexander Graf 已提交
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
        break;
    case 0x44:
        /* yield */
        r = 0;
        break;
    case 0x308:
        /* ipl */
        r = 0;
        break;
    default:
        r = -1;
        break;
    }

    if (r) {
253
        program_interrupt(env, PGM_OPERATION, ILEN_LATER_INC);
A
Alexander Graf 已提交
254 255 256 257 258 259
    }

    return r;
}

/* Set Prefix */
260
void HELPER(spx)(CPUS390XState *env, uint64_t a1)
A
Alexander Graf 已提交
261
{
262
    CPUState *cs = CPU(s390_env_get_cpu(env));
263
    uint32_t prefix = a1 & 0x7fffe000;
264

265
    env->psa = prefix;
A
Alexander Graf 已提交
266
    qemu_log("prefix: %#x\n", prefix);
267 268
    tlb_flush_page(cs, 0);
    tlb_flush_page(cs, TARGET_PAGE_SIZE);
A
Alexander Graf 已提交
269 270
}

271 272
/* Store Clock */
uint64_t HELPER(stck)(CPUS390XState *env)
A
Alexander Graf 已提交
273 274 275 276
{
    uint64_t time;

    time = env->tod_offset +
277
        time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - env->tod_basetime);
A
Alexander Graf 已提交
278 279 280 281 282

    return time;
}

/* Set Clock Comparator */
283
void HELPER(sckc)(CPUS390XState *env, uint64_t time)
A
Alexander Graf 已提交
284 285 286 287 288
{
    if (time == -1ULL) {
        return;
    }

289 290 291
    /* difference between origins */
    time -= env->tod_offset;

A
Alexander Graf 已提交
292
    /* nanoseconds */
293
    time = tod2time(time);
A
Alexander Graf 已提交
294

295
    timer_mod(env->tod_timer, env->tod_basetime + time);
A
Alexander Graf 已提交
296 297 298
}

/* Store Clock Comparator */
299
uint64_t HELPER(stckc)(CPUS390XState *env)
A
Alexander Graf 已提交
300 301
{
    /* XXX implement */
302
    return 0;
A
Alexander Graf 已提交
303 304 305
}

/* Set CPU Timer */
306
void HELPER(spt)(CPUS390XState *env, uint64_t time)
A
Alexander Graf 已提交
307 308 309 310 311 312
{
    if (time == -1ULL) {
        return;
    }

    /* nanoseconds */
313
    time = tod2time(time);
A
Alexander Graf 已提交
314

315
    timer_mod(env->cpu_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + time);
A
Alexander Graf 已提交
316 317 318
}

/* Store CPU Timer */
319
uint64_t HELPER(stpt)(CPUS390XState *env)
A
Alexander Graf 已提交
320 321
{
    /* XXX implement */
322
    return 0;
A
Alexander Graf 已提交
323 324 325
}

/* Store System Information */
R
Richard Henderson 已提交
326 327
uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0,
                      uint64_t r0, uint64_t r1)
A
Alexander Graf 已提交
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
{
    int cc = 0;
    int sel1, sel2;

    if ((r0 & STSI_LEVEL_MASK) <= STSI_LEVEL_3 &&
        ((r0 & STSI_R0_RESERVED_MASK) || (r1 & STSI_R1_RESERVED_MASK))) {
        /* valid function code, invalid reserved bits */
        program_interrupt(env, PGM_SPECIFICATION, 2);
    }

    sel1 = r0 & STSI_R0_SEL1_MASK;
    sel2 = r1 & STSI_R1_SEL2_MASK;

    /* XXX: spec exception if sysib is not 4k-aligned */

    switch (r0 & STSI_LEVEL_MASK) {
    case STSI_LEVEL_1:
        if ((sel1 == 1) && (sel2 == 1)) {
            /* Basic Machine Configuration */
            struct sysib_111 sysib;

            memset(&sysib, 0, sizeof(sysib));
            ebcdic_put(sysib.manuf, "QEMU            ", 16);
            /* same as machine type number in STORE CPU ID */
            ebcdic_put(sysib.type, "QEMU", 4);
            /* same as model number in STORE CPU ID */
            ebcdic_put(sysib.model, "QEMU            ", 16);
            ebcdic_put(sysib.sequence, "QEMU            ", 16);
            ebcdic_put(sysib.plant, "QEMU", 4);
357
            cpu_physical_memory_write(a0, &sysib, sizeof(sysib));
A
Alexander Graf 已提交
358 359 360 361 362 363 364 365 366
        } else if ((sel1 == 2) && (sel2 == 1)) {
            /* Basic Machine CPU */
            struct sysib_121 sysib;

            memset(&sysib, 0, sizeof(sysib));
            /* XXX make different for different CPUs? */
            ebcdic_put(sysib.sequence, "QEMUQEMUQEMUQEMU", 16);
            ebcdic_put(sysib.plant, "QEMU", 4);
            stw_p(&sysib.cpu_addr, env->cpu_num);
367
            cpu_physical_memory_write(a0, &sysib, sizeof(sysib));
A
Alexander Graf 已提交
368 369 370 371 372 373 374 375 376 377 378
        } else if ((sel1 == 2) && (sel2 == 2)) {
            /* Basic Machine CPUs */
            struct sysib_122 sysib;

            memset(&sysib, 0, sizeof(sysib));
            stl_p(&sysib.capability, 0x443afc29);
            /* XXX change when SMP comes */
            stw_p(&sysib.total_cpus, 1);
            stw_p(&sysib.active_cpus, 1);
            stw_p(&sysib.standby_cpus, 0);
            stw_p(&sysib.reserved_cpus, 0);
379
            cpu_physical_memory_write(a0, &sysib, sizeof(sysib));
A
Alexander Graf 已提交
380 381 382 383 384
        } else {
            cc = 3;
        }
        break;
    case STSI_LEVEL_2:
B
Blue Swirl 已提交
385 386 387 388 389 390 391 392 393 394 395
        {
            if ((sel1 == 2) && (sel2 == 1)) {
                /* LPAR CPU */
                struct sysib_221 sysib;

                memset(&sysib, 0, sizeof(sysib));
                /* XXX make different for different CPUs? */
                ebcdic_put(sysib.sequence, "QEMUQEMUQEMUQEMU", 16);
                ebcdic_put(sysib.plant, "QEMU", 4);
                stw_p(&sysib.cpu_addr, env->cpu_num);
                stw_p(&sysib.cpu_id, 0);
396
                cpu_physical_memory_write(a0, &sysib, sizeof(sysib));
B
Blue Swirl 已提交
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
            } else if ((sel1 == 2) && (sel2 == 2)) {
                /* LPAR CPUs */
                struct sysib_222 sysib;

                memset(&sysib, 0, sizeof(sysib));
                stw_p(&sysib.lpar_num, 0);
                sysib.lcpuc = 0;
                /* XXX change when SMP comes */
                stw_p(&sysib.total_cpus, 1);
                stw_p(&sysib.conf_cpus, 1);
                stw_p(&sysib.standby_cpus, 0);
                stw_p(&sysib.reserved_cpus, 0);
                ebcdic_put(sysib.name, "QEMU    ", 8);
                stl_p(&sysib.caf, 1000);
                stw_p(&sysib.dedicated_cpus, 0);
                stw_p(&sysib.shared_cpus, 0);
413
                cpu_physical_memory_write(a0, &sysib, sizeof(sysib));
B
Blue Swirl 已提交
414 415 416 417
            } else {
                cc = 3;
            }
            break;
A
Alexander Graf 已提交
418 419
        }
    case STSI_LEVEL_3:
B
Blue Swirl 已提交
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
        {
            if ((sel1 == 2) && (sel2 == 2)) {
                /* VM CPUs */
                struct sysib_322 sysib;

                memset(&sysib, 0, sizeof(sysib));
                sysib.count = 1;
                /* XXX change when SMP comes */
                stw_p(&sysib.vm[0].total_cpus, 1);
                stw_p(&sysib.vm[0].conf_cpus, 1);
                stw_p(&sysib.vm[0].standby_cpus, 0);
                stw_p(&sysib.vm[0].reserved_cpus, 0);
                ebcdic_put(sysib.vm[0].name, "KVMguest", 8);
                stl_p(&sysib.vm[0].caf, 1000);
                ebcdic_put(sysib.vm[0].cpi, "KVM/Linux       ", 16);
435
                cpu_physical_memory_write(a0, &sysib, sizeof(sysib));
B
Blue Swirl 已提交
436 437 438 439
            } else {
                cc = 3;
            }
            break;
A
Alexander Graf 已提交
440 441 442 443 444 445 446 447 448 449 450 451
        }
    case STSI_LEVEL_CURRENT:
        env->regs[0] = STSI_LEVEL_3;
        break;
    default:
        cc = 3;
        break;
    }

    return cc;
}

452 453
uint32_t HELPER(sigp)(CPUS390XState *env, uint64_t order_code, uint32_t r1,
                      uint64_t cpu_addr)
A
Alexander Graf 已提交
454
{
455
    int cc = SIGP_CC_ORDER_CODE_ACCEPTED;
A
Alexander Graf 已提交
456 457

    HELPER_LOG("%s: %016" PRIx64 " %08x %016" PRIx64 "\n",
B
Blue Swirl 已提交
458
               __func__, order_code, r1, cpu_addr);
A
Alexander Graf 已提交
459

B
Blue Swirl 已提交
460
    /* Remember: Use "R1 or R1 + 1, whichever is the odd-numbered register"
A
Alexander Graf 已提交
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
       as parameter (input). Status (output) is always R1. */

    switch (order_code) {
    case SIGP_SET_ARCH:
        /* switch arch */
        break;
    case SIGP_SENSE:
        /* enumerate CPU status */
        if (cpu_addr) {
            /* XXX implement when SMP comes */
            return 3;
        }
        env->regs[r1] &= 0xffffffff00000000ULL;
        cc = 1;
        break;
B
Blue Swirl 已提交
476
#if !defined(CONFIG_USER_ONLY)
477 478
    case SIGP_RESTART:
        qemu_system_reset_request();
479
        cpu_loop_exit(CPU(s390_env_get_cpu(env)));
480 481 482
        break;
    case SIGP_STOP:
        qemu_system_shutdown_request();
483
        cpu_loop_exit(CPU(s390_env_get_cpu(env)));
484 485
        break;
#endif
A
Alexander Graf 已提交
486 487 488
    default:
        /* unknown sigp */
        fprintf(stderr, "XXX unknown sigp: 0x%" PRIx64 "\n", order_code);
489
        cc = SIGP_CC_NOT_OPERATIONAL;
A
Alexander Graf 已提交
490 491 492 493 494
    }

    return cc;
}
#endif