qemu/target-s390x/misc_helper.c
<<
>>
Prefs
   1/*
   2 *  S/390 misc helper routines
   3 *
   4 *  Copyright (c) 2009 Ulrich Hecht
   5 *  Copyright (c) 2009 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 "memory.h"
  23#include "host-utils.h"
  24#include "helper.h"
  25#include <string.h>
  26#include "kvm.h"
  27#include "qemu-timer.h"
  28#ifdef CONFIG_KVM
  29#include <linux/kvm.h>
  30#endif
  31
  32#if !defined(CONFIG_USER_ONLY)
  33#include "softmmu_exec.h"
  34#include "sysemu.h"
  35#endif
  36
  37/* #define DEBUG_HELPER */
  38#ifdef DEBUG_HELPER
  39#define HELPER_LOG(x...) qemu_log(x)
  40#else
  41#define HELPER_LOG(x...)
  42#endif
  43
  44/* raise an exception */
  45void HELPER(exception)(CPUS390XState *env, uint32_t excp)
  46{
  47    HELPER_LOG("%s: exception %d\n", __func__, excp);
  48    env->exception_index = excp;
  49    cpu_loop_exit(env);
  50}
  51
  52#ifndef CONFIG_USER_ONLY
  53void program_interrupt(CPUS390XState *env, uint32_t code, int ilc)
  54{
  55    qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n",
  56                  env->psw.addr);
  57
  58    if (kvm_enabled()) {
  59#ifdef CONFIG_KVM
  60        kvm_s390_interrupt(env, KVM_S390_PROGRAM_INT, code);
  61#endif
  62    } else {
  63        env->int_pgm_code = code;
  64        env->int_pgm_ilc = ilc;
  65        env->exception_index = EXCP_PGM;
  66        cpu_loop_exit(env);
  67    }
  68}
  69
  70/* SCLP service call */
  71uint32_t HELPER(servc)(CPUS390XState *env, uint32_t r1, uint64_t r2)
  72{
  73    int r;
  74
  75    r = sclp_service_call(r1, r2);
  76    if (r < 0) {
  77        program_interrupt(env, -r, 4);
  78        return 0;
  79    }
  80    return r;
  81}
  82
  83/* DIAG */
  84uint64_t HELPER(diag)(CPUS390XState *env, uint32_t num, uint64_t mem,
  85                      uint64_t code)
  86{
  87    uint64_t r;
  88
  89    switch (num) {
  90    case 0x500:
  91        /* KVM hypercall */
  92        r = s390_virtio_hypercall(env, mem, code);
  93        break;
  94    case 0x44:
  95        /* yield */
  96        r = 0;
  97        break;
  98    case 0x308:
  99        /* ipl */
 100        r = 0;
 101        break;
 102    default:
 103        r = -1;
 104        break;
 105    }
 106
 107    if (r) {
 108        program_interrupt(env, PGM_OPERATION, ILC_LATER_INC);
 109    }
 110
 111    return r;
 112}
 113
 114/* Store CPU ID */
 115void HELPER(stidp)(CPUS390XState *env, uint64_t a1)
 116{
 117    cpu_stq_data(env, a1, env->cpu_num);
 118}
 119
 120/* Set Prefix */
 121void HELPER(spx)(CPUS390XState *env, uint64_t a1)
 122{
 123    uint32_t prefix;
 124
 125    prefix = cpu_ldl_data(env, a1);
 126    env->psa = prefix & 0xfffff000;
 127    qemu_log("prefix: %#x\n", prefix);
 128    tlb_flush_page(env, 0);
 129    tlb_flush_page(env, TARGET_PAGE_SIZE);
 130}
 131
 132/* Set Clock */
 133uint32_t HELPER(sck)(uint64_t a1)
 134{
 135    /* XXX not implemented - is it necessary? */
 136
 137    return 0;
 138}
 139
 140static inline uint64_t clock_value(CPUS390XState *env)
 141{
 142    uint64_t time;
 143
 144    time = env->tod_offset +
 145        time2tod(qemu_get_clock_ns(vm_clock) - env->tod_basetime);
 146
 147    return time;
 148}
 149
 150/* Store Clock */
 151uint32_t HELPER(stck)(CPUS390XState *env, uint64_t a1)
 152{
 153    cpu_stq_data(env, a1, clock_value(env));
 154
 155    return 0;
 156}
 157
 158/* Store Clock Extended */
 159uint32_t HELPER(stcke)(CPUS390XState *env, uint64_t a1)
 160{
 161    cpu_stb_data(env, a1, 0);
 162    /* basically the same value as stck */
 163    cpu_stq_data(env, a1 + 1, clock_value(env) | env->cpu_num);
 164    /* more fine grained than stck */
 165    cpu_stq_data(env, a1 + 9, 0);
 166    /* XXX programmable fields */
 167    cpu_stw_data(env, a1 + 17, 0);
 168
 169    return 0;
 170}
 171
 172/* Set Clock Comparator */
 173void HELPER(sckc)(CPUS390XState *env, uint64_t a1)
 174{
 175    uint64_t time = cpu_ldq_data(env, a1);
 176
 177    if (time == -1ULL) {
 178        return;
 179    }
 180
 181    /* difference between now and then */
 182    time -= clock_value(env);
 183    /* nanoseconds */
 184    time = (time * 125) >> 9;
 185
 186    qemu_mod_timer(env->tod_timer, qemu_get_clock_ns(vm_clock) + time);
 187}
 188
 189/* Store Clock Comparator */
 190void HELPER(stckc)(CPUS390XState *env, uint64_t a1)
 191{
 192    /* XXX implement */
 193    cpu_stq_data(env, a1, 0);
 194}
 195
 196/* Set CPU Timer */
 197void HELPER(spt)(CPUS390XState *env, uint64_t a1)
 198{
 199    uint64_t time = cpu_ldq_data(env, a1);
 200
 201    if (time == -1ULL) {
 202        return;
 203    }
 204
 205    /* nanoseconds */
 206    time = (time * 125) >> 9;
 207
 208    qemu_mod_timer(env->cpu_timer, qemu_get_clock_ns(vm_clock) + time);
 209}
 210
 211/* Store CPU Timer */
 212void HELPER(stpt)(CPUS390XState *env, uint64_t a1)
 213{
 214    /* XXX implement */
 215    cpu_stq_data(env, a1, 0);
 216}
 217
 218/* Store System Information */
 219uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0, uint32_t r0,
 220                      uint32_t r1)
 221{
 222    int cc = 0;
 223    int sel1, sel2;
 224
 225    if ((r0 & STSI_LEVEL_MASK) <= STSI_LEVEL_3 &&
 226        ((r0 & STSI_R0_RESERVED_MASK) || (r1 & STSI_R1_RESERVED_MASK))) {
 227        /* valid function code, invalid reserved bits */
 228        program_interrupt(env, PGM_SPECIFICATION, 2);
 229    }
 230
 231    sel1 = r0 & STSI_R0_SEL1_MASK;
 232    sel2 = r1 & STSI_R1_SEL2_MASK;
 233
 234    /* XXX: spec exception if sysib is not 4k-aligned */
 235
 236    switch (r0 & STSI_LEVEL_MASK) {
 237    case STSI_LEVEL_1:
 238        if ((sel1 == 1) && (sel2 == 1)) {
 239            /* Basic Machine Configuration */
 240            struct sysib_111 sysib;
 241
 242            memset(&sysib, 0, sizeof(sysib));
 243            ebcdic_put(sysib.manuf, "QEMU            ", 16);
 244            /* same as machine type number in STORE CPU ID */
 245            ebcdic_put(sysib.type, "QEMU", 4);
 246            /* same as model number in STORE CPU ID */
 247            ebcdic_put(sysib.model, "QEMU            ", 16);
 248            ebcdic_put(sysib.sequence, "QEMU            ", 16);
 249            ebcdic_put(sysib.plant, "QEMU", 4);
 250            cpu_physical_memory_rw(a0, (uint8_t *)&sysib, sizeof(sysib), 1);
 251        } else if ((sel1 == 2) && (sel2 == 1)) {
 252            /* Basic Machine CPU */
 253            struct sysib_121 sysib;
 254
 255            memset(&sysib, 0, sizeof(sysib));
 256            /* XXX make different for different CPUs? */
 257            ebcdic_put(sysib.sequence, "QEMUQEMUQEMUQEMU", 16);
 258            ebcdic_put(sysib.plant, "QEMU", 4);
 259            stw_p(&sysib.cpu_addr, env->cpu_num);
 260            cpu_physical_memory_rw(a0, (uint8_t *)&sysib, sizeof(sysib), 1);
 261        } else if ((sel1 == 2) && (sel2 == 2)) {
 262            /* Basic Machine CPUs */
 263            struct sysib_122 sysib;
 264
 265            memset(&sysib, 0, sizeof(sysib));
 266            stl_p(&sysib.capability, 0x443afc29);
 267            /* XXX change when SMP comes */
 268            stw_p(&sysib.total_cpus, 1);
 269            stw_p(&sysib.active_cpus, 1);
 270            stw_p(&sysib.standby_cpus, 0);
 271            stw_p(&sysib.reserved_cpus, 0);
 272            cpu_physical_memory_rw(a0, (uint8_t *)&sysib, sizeof(sysib), 1);
 273        } else {
 274            cc = 3;
 275        }
 276        break;
 277    case STSI_LEVEL_2:
 278        {
 279            if ((sel1 == 2) && (sel2 == 1)) {
 280                /* LPAR CPU */
 281                struct sysib_221 sysib;
 282
 283                memset(&sysib, 0, sizeof(sysib));
 284                /* XXX make different for different CPUs? */
 285                ebcdic_put(sysib.sequence, "QEMUQEMUQEMUQEMU", 16);
 286                ebcdic_put(sysib.plant, "QEMU", 4);
 287                stw_p(&sysib.cpu_addr, env->cpu_num);
 288                stw_p(&sysib.cpu_id, 0);
 289                cpu_physical_memory_rw(a0, (uint8_t *)&sysib, sizeof(sysib), 1);
 290            } else if ((sel1 == 2) && (sel2 == 2)) {
 291                /* LPAR CPUs */
 292                struct sysib_222 sysib;
 293
 294                memset(&sysib, 0, sizeof(sysib));
 295                stw_p(&sysib.lpar_num, 0);
 296                sysib.lcpuc = 0;
 297                /* XXX change when SMP comes */
 298                stw_p(&sysib.total_cpus, 1);
 299                stw_p(&sysib.conf_cpus, 1);
 300                stw_p(&sysib.standby_cpus, 0);
 301                stw_p(&sysib.reserved_cpus, 0);
 302                ebcdic_put(sysib.name, "QEMU    ", 8);
 303                stl_p(&sysib.caf, 1000);
 304                stw_p(&sysib.dedicated_cpus, 0);
 305                stw_p(&sysib.shared_cpus, 0);
 306                cpu_physical_memory_rw(a0, (uint8_t *)&sysib, sizeof(sysib), 1);
 307            } else {
 308                cc = 3;
 309            }
 310            break;
 311        }
 312    case STSI_LEVEL_3:
 313        {
 314            if ((sel1 == 2) && (sel2 == 2)) {
 315                /* VM CPUs */
 316                struct sysib_322 sysib;
 317
 318                memset(&sysib, 0, sizeof(sysib));
 319                sysib.count = 1;
 320                /* XXX change when SMP comes */
 321                stw_p(&sysib.vm[0].total_cpus, 1);
 322                stw_p(&sysib.vm[0].conf_cpus, 1);
 323                stw_p(&sysib.vm[0].standby_cpus, 0);
 324                stw_p(&sysib.vm[0].reserved_cpus, 0);
 325                ebcdic_put(sysib.vm[0].name, "KVMguest", 8);
 326                stl_p(&sysib.vm[0].caf, 1000);
 327                ebcdic_put(sysib.vm[0].cpi, "KVM/Linux       ", 16);
 328                cpu_physical_memory_rw(a0, (uint8_t *)&sysib, sizeof(sysib), 1);
 329            } else {
 330                cc = 3;
 331            }
 332            break;
 333        }
 334    case STSI_LEVEL_CURRENT:
 335        env->regs[0] = STSI_LEVEL_3;
 336        break;
 337    default:
 338        cc = 3;
 339        break;
 340    }
 341
 342    return cc;
 343}
 344
 345uint32_t HELPER(sigp)(CPUS390XState *env, uint64_t order_code, uint32_t r1,
 346                      uint64_t cpu_addr)
 347{
 348    int cc = 0;
 349
 350    HELPER_LOG("%s: %016" PRIx64 " %08x %016" PRIx64 "\n",
 351               __func__, order_code, r1, cpu_addr);
 352
 353    /* Remember: Use "R1 or R1 + 1, whichever is the odd-numbered register"
 354       as parameter (input). Status (output) is always R1. */
 355
 356    switch (order_code) {
 357    case SIGP_SET_ARCH:
 358        /* switch arch */
 359        break;
 360    case SIGP_SENSE:
 361        /* enumerate CPU status */
 362        if (cpu_addr) {
 363            /* XXX implement when SMP comes */
 364            return 3;
 365        }
 366        env->regs[r1] &= 0xffffffff00000000ULL;
 367        cc = 1;
 368        break;
 369#if !defined(CONFIG_USER_ONLY)
 370    case SIGP_RESTART:
 371        qemu_system_reset_request();
 372        cpu_loop_exit(env);
 373        break;
 374    case SIGP_STOP:
 375        qemu_system_shutdown_request();
 376        cpu_loop_exit(env);
 377        break;
 378#endif
 379    default:
 380        /* unknown sigp */
 381        fprintf(stderr, "XXX unknown sigp: 0x%" PRIx64 "\n", order_code);
 382        cc = 3;
 383    }
 384
 385    return cc;
 386}
 387#endif
 388