qemu/target/s390x/diag.c
<<
>>
Prefs
   1/*
   2 * S390x DIAG instruction helper functions
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License as published by
   6 * the Free Software Foundation; either version 2 of the License, or
   7 * (at your option) any later version.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 */
  14
  15#include "qemu/osdep.h"
  16#include "cpu.h"
  17#include "internal.h"
  18#include "exec/address-spaces.h"
  19#include "exec/exec-all.h"
  20#include "hw/watchdog/wdt_diag288.h"
  21#include "sysemu/cpus.h"
  22#include "hw/s390x/ipl.h"
  23#include "hw/s390x/s390-virtio-ccw.h"
  24
  25static int modified_clear_reset(S390CPU *cpu)
  26{
  27    S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
  28    CPUState *t;
  29
  30    pause_all_vcpus();
  31    cpu_synchronize_all_states();
  32    CPU_FOREACH(t) {
  33        run_on_cpu(t, s390_do_cpu_full_reset, RUN_ON_CPU_NULL);
  34    }
  35    s390_cmma_reset();
  36    subsystem_reset();
  37    s390_crypto_reset();
  38    scc->load_normal(CPU(cpu));
  39    cpu_synchronize_all_post_reset();
  40    resume_all_vcpus();
  41    return 0;
  42}
  43
  44static inline void s390_do_cpu_reset(CPUState *cs, run_on_cpu_data arg)
  45{
  46    S390CPUClass *scc = S390_CPU_GET_CLASS(cs);
  47
  48    scc->cpu_reset(cs);
  49}
  50
  51static int load_normal_reset(S390CPU *cpu)
  52{
  53    S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
  54    CPUState *t;
  55
  56    pause_all_vcpus();
  57    cpu_synchronize_all_states();
  58    CPU_FOREACH(t) {
  59        run_on_cpu(t, s390_do_cpu_reset, RUN_ON_CPU_NULL);
  60    }
  61    s390_cmma_reset();
  62    subsystem_reset();
  63    scc->initial_cpu_reset(CPU(cpu));
  64    scc->load_normal(CPU(cpu));
  65    cpu_synchronize_all_post_reset();
  66    resume_all_vcpus();
  67    return 0;
  68}
  69
  70int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3)
  71{
  72    uint64_t func = env->regs[r1];
  73    uint64_t timeout = env->regs[r1 + 1];
  74    uint64_t action = env->regs[r3];
  75    Object *obj;
  76    DIAG288State *diag288;
  77    DIAG288Class *diag288_class;
  78
  79    if (r1 % 2 || action != 0) {
  80        return -1;
  81    }
  82
  83    /* Timeout must be more than 15 seconds except for timer deletion */
  84    if (func != WDT_DIAG288_CANCEL && timeout < 15) {
  85        return -1;
  86    }
  87
  88    obj = object_resolve_path_type("", TYPE_WDT_DIAG288, NULL);
  89    if (!obj) {
  90        return -1;
  91    }
  92
  93    diag288 = DIAG288(obj);
  94    diag288_class = DIAG288_GET_CLASS(diag288);
  95    return diag288_class->handle_timer(diag288, func, timeout);
  96}
  97
  98#define DIAG_308_RC_OK              0x0001
  99#define DIAG_308_RC_NO_CONF         0x0102
 100#define DIAG_308_RC_INVALID         0x0402
 101
 102void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
 103{
 104    uint64_t addr =  env->regs[r1];
 105    uint64_t subcode = env->regs[r3];
 106    IplParameterBlock *iplb;
 107
 108    if (env->psw.mask & PSW_MASK_PSTATE) {
 109        s390_program_interrupt(env, PGM_PRIVILEGED, ILEN_AUTO, ra);
 110        return;
 111    }
 112
 113    if ((subcode & ~0x0ffffULL) || (subcode > 6)) {
 114        s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra);
 115        return;
 116    }
 117
 118    switch (subcode) {
 119    case 0:
 120        modified_clear_reset(s390_env_get_cpu(env));
 121        if (tcg_enabled()) {
 122            cpu_loop_exit(CPU(s390_env_get_cpu(env)));
 123        }
 124        break;
 125    case 1:
 126        load_normal_reset(s390_env_get_cpu(env));
 127        if (tcg_enabled()) {
 128            cpu_loop_exit(CPU(s390_env_get_cpu(env)));
 129        }
 130        break;
 131    case 3:
 132        s390_reipl_request();
 133        if (tcg_enabled()) {
 134            cpu_loop_exit(CPU(s390_env_get_cpu(env)));
 135        }
 136        break;
 137    case 5:
 138        if ((r1 & 1) || (addr & 0x0fffULL)) {
 139            s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra);
 140            return;
 141        }
 142        if (!address_space_access_valid(&address_space_memory, addr,
 143                                        sizeof(IplParameterBlock), false)) {
 144            s390_program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO, ra);
 145            return;
 146        }
 147        iplb = g_new0(IplParameterBlock, 1);
 148        cpu_physical_memory_read(addr, iplb, sizeof(iplb->len));
 149        if (!iplb_valid_len(iplb)) {
 150            env->regs[r1 + 1] = DIAG_308_RC_INVALID;
 151            goto out;
 152        }
 153
 154        cpu_physical_memory_read(addr, iplb, be32_to_cpu(iplb->len));
 155
 156        if (!iplb_valid_ccw(iplb) && !iplb_valid_fcp(iplb)) {
 157            env->regs[r1 + 1] = DIAG_308_RC_INVALID;
 158            goto out;
 159        }
 160
 161        s390_ipl_update_diag308(iplb);
 162        env->regs[r1 + 1] = DIAG_308_RC_OK;
 163out:
 164        g_free(iplb);
 165        return;
 166    case 6:
 167        if ((r1 & 1) || (addr & 0x0fffULL)) {
 168            s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra);
 169            return;
 170        }
 171        if (!address_space_access_valid(&address_space_memory, addr,
 172                                        sizeof(IplParameterBlock), true)) {
 173            s390_program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO, ra);
 174            return;
 175        }
 176        iplb = s390_ipl_get_iplb();
 177        if (iplb) {
 178            cpu_physical_memory_write(addr, iplb, be32_to_cpu(iplb->len));
 179            env->regs[r1 + 1] = DIAG_308_RC_OK;
 180        } else {
 181            env->regs[r1 + 1] = DIAG_308_RC_NO_CONF;
 182        }
 183        return;
 184    default:
 185        hw_error("Unhandled diag308 subcode %" PRIx64, subcode);
 186        break;
 187    }
 188}
 189