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 "s390x-internal.h"
  18#include "hw/watchdog/wdt_diag288.h"
  19#include "sysemu/cpus.h"
  20#include "hw/s390x/ipl.h"
  21#include "hw/s390x/s390-virtio-ccw.h"
  22#include "hw/s390x/pv.h"
  23#include "sysemu/kvm.h"
  24#include "kvm/kvm_s390x.h"
  25
  26int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3)
  27{
  28    uint64_t func = env->regs[r1];
  29    uint64_t timeout = env->regs[r1 + 1];
  30    uint64_t action = env->regs[r3];
  31    Object *obj;
  32    DIAG288State *diag288;
  33    DIAG288Class *diag288_class;
  34
  35    if (r1 % 2 || action != 0) {
  36        return -1;
  37    }
  38
  39    /* Timeout must be more than 15 seconds except for timer deletion */
  40    if (func != WDT_DIAG288_CANCEL && timeout < 15) {
  41        return -1;
  42    }
  43
  44    obj = object_resolve_path_type("", TYPE_WDT_DIAG288, NULL);
  45    if (!obj) {
  46        return -1;
  47    }
  48
  49    diag288 = DIAG288(obj);
  50    diag288_class = DIAG288_GET_CLASS(diag288);
  51    return diag288_class->handle_timer(diag288, func, timeout);
  52}
  53
  54static int diag308_parm_check(CPUS390XState *env, uint64_t r1, uint64_t addr,
  55                              uintptr_t ra, bool write)
  56{
  57    /* Handled by the Ultravisor */
  58    if (s390_is_pv()) {
  59        return 0;
  60    }
  61    if ((r1 & 1) || (addr & ~TARGET_PAGE_MASK)) {
  62        s390_program_interrupt(env, PGM_SPECIFICATION, ra);
  63        return -1;
  64    }
  65    if (!address_space_access_valid(&address_space_memory, addr,
  66                                    sizeof(IplParameterBlock), write,
  67                                    MEMTXATTRS_UNSPECIFIED)) {
  68        s390_program_interrupt(env, PGM_ADDRESSING, ra);
  69        return -1;
  70    }
  71    return 0;
  72}
  73
  74void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
  75{
  76    bool valid;
  77    CPUState *cs = env_cpu(env);
  78    S390CPU *cpu = S390_CPU(cs);
  79    uint64_t addr =  env->regs[r1];
  80    uint64_t subcode = env->regs[r3];
  81    IplParameterBlock *iplb;
  82
  83    if (env->psw.mask & PSW_MASK_PSTATE) {
  84        s390_program_interrupt(env, PGM_PRIVILEGED, ra);
  85        return;
  86    }
  87
  88    if (subcode & ~0x0ffffULL) {
  89        s390_program_interrupt(env, PGM_SPECIFICATION, ra);
  90        return;
  91    }
  92
  93    if (subcode >= DIAG308_PV_SET && !s390_has_feat(S390_FEAT_UNPACK)) {
  94        s390_program_interrupt(env, PGM_SPECIFICATION, ra);
  95        return;
  96    }
  97
  98    switch (subcode) {
  99    case DIAG308_RESET_MOD_CLR:
 100        s390_ipl_reset_request(cs, S390_RESET_MODIFIED_CLEAR);
 101        break;
 102    case DIAG308_RESET_LOAD_NORM:
 103        s390_ipl_reset_request(cs, S390_RESET_LOAD_NORMAL);
 104        break;
 105    case DIAG308_LOAD_CLEAR:
 106        /* Well we still lack the clearing bit... */
 107        s390_ipl_reset_request(cs, S390_RESET_REIPL);
 108        break;
 109    case DIAG308_SET:
 110    case DIAG308_PV_SET:
 111        if (diag308_parm_check(env, r1, addr, ra, false)) {
 112            return;
 113        }
 114        iplb = g_new0(IplParameterBlock, 1);
 115        if (!s390_is_pv()) {
 116            cpu_physical_memory_read(addr, iplb, sizeof(iplb->len));
 117        } else {
 118            s390_cpu_pv_mem_read(cpu, 0, iplb, sizeof(iplb->len));
 119        }
 120
 121        if (!iplb_valid_len(iplb)) {
 122            env->regs[r1 + 1] = DIAG_308_RC_INVALID;
 123            goto out;
 124        }
 125
 126        if (!s390_is_pv()) {
 127            cpu_physical_memory_read(addr, iplb, be32_to_cpu(iplb->len));
 128        } else {
 129            s390_cpu_pv_mem_read(cpu, 0, iplb, be32_to_cpu(iplb->len));
 130        }
 131
 132        valid = subcode == DIAG308_PV_SET ? iplb_valid_pv(iplb) : iplb_valid(iplb);
 133        if (!valid) {
 134            env->regs[r1 + 1] = DIAG_308_RC_INVALID;
 135            goto out;
 136        }
 137
 138        s390_ipl_update_diag308(iplb);
 139        env->regs[r1 + 1] = DIAG_308_RC_OK;
 140out:
 141        g_free(iplb);
 142        return;
 143    case DIAG308_STORE:
 144    case DIAG308_PV_STORE:
 145        if (diag308_parm_check(env, r1, addr, ra, true)) {
 146            return;
 147        }
 148        if (subcode == DIAG308_PV_STORE) {
 149            iplb = s390_ipl_get_iplb_pv();
 150        } else {
 151            iplb = s390_ipl_get_iplb();
 152        }
 153        if (!iplb) {
 154            env->regs[r1 + 1] = DIAG_308_RC_NO_CONF;
 155            return;
 156        }
 157
 158        if (!s390_is_pv()) {
 159            cpu_physical_memory_write(addr, iplb, be32_to_cpu(iplb->len));
 160        } else {
 161            s390_cpu_pv_mem_write(cpu, 0, iplb, be32_to_cpu(iplb->len));
 162        }
 163        env->regs[r1 + 1] = DIAG_308_RC_OK;
 164        return;
 165    case DIAG308_PV_START:
 166        iplb = s390_ipl_get_iplb_pv();
 167        if (!iplb) {
 168            env->regs[r1 + 1] = DIAG_308_RC_NO_PV_CONF;
 169            return;
 170        }
 171
 172        if (kvm_enabled() && kvm_s390_get_hpage_1m()) {
 173            error_report("Protected VMs can currently not be backed with "
 174                         "huge pages");
 175            env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV;
 176            return;
 177        }
 178
 179        s390_ipl_reset_request(cs, S390_RESET_PV);
 180        break;
 181    default:
 182        s390_program_interrupt(env, PGM_SPECIFICATION, ra);
 183        break;
 184    }
 185}
 186