#include "qemu/osdep.h"
#include "qemu/main-loop.h"
#include "cpu.h"
+#include "internal.h"
#include "exec/memory.h"
#include "qemu/host-utils.h"
#include "exec/helper-proto.h"
-#include "sysemu/kvm.h"
#include "qemu/timer.h"
-#include "qemu/main-loop.h"
#include "exec/address-spaces.h"
-#ifdef CONFIG_KVM
-#include <linux/kvm.h>
-#endif
#include "exec/exec-all.h"
#include "exec/cpu_ldst.h"
#if !defined(CONFIG_USER_ONLY)
-#include "hw/watchdog/wdt_diag288.h"
#include "sysemu/cpus.h"
#include "sysemu/sysemu.h"
#include "hw/s390x/ebcdic.h"
-#include "hw/s390x/ipl.h"
#endif
/* #define DEBUG_HELPER */
uintptr_t retaddr)
{
CPUState *cs = CPU(s390_env_get_cpu(env));
- int t;
cs->exception_index = EXCP_PGM;
env->int_pgm_code = excp;
+ env->int_pgm_ilen = ILEN_AUTO;
/* Use the (ultimate) callers address to find the insn that trapped. */
cpu_restore_state(cs, retaddr);
- /* Advance past the insn. */
- t = cpu_ldub_code(env, env->psw.addr);
- env->int_pgm_ilen = t = get_ilen(t);
- env->psw.addr += t;
-
cpu_loop_exit(cs);
}
#ifndef CONFIG_USER_ONLY
-void program_interrupt(CPUS390XState *env, uint32_t code, int ilen)
-{
- S390CPU *cpu = s390_env_get_cpu(env);
-
- qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n",
- env->psw.addr);
-
- if (kvm_enabled()) {
-#ifdef CONFIG_KVM
- struct kvm_s390_irq irq = {
- .type = KVM_S390_PROGRAM_INT,
- .u.pgm.code = code,
- };
-
- kvm_s390_vcpu_interrupt(cpu, &irq);
-#endif
- } else {
- CPUState *cs = CPU(cpu);
-
- env->int_pgm_code = code;
- env->int_pgm_ilen = ilen;
- cs->exception_index = EXCP_PGM;
- cpu_loop_exit(cs);
- }
-}
-
/* SCLP service call */
uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2)
{
return r;
}
-#ifndef CONFIG_USER_ONLY
-static int modified_clear_reset(S390CPU *cpu)
-{
- S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
- CPUState *t;
-
- pause_all_vcpus();
- cpu_synchronize_all_states();
- CPU_FOREACH(t) {
- run_on_cpu(t, s390_do_cpu_full_reset, RUN_ON_CPU_NULL);
- }
- s390_cmma_reset();
- subsystem_reset();
- s390_crypto_reset();
- scc->load_normal(CPU(cpu));
- cpu_synchronize_all_post_reset();
- resume_all_vcpus();
- return 0;
-}
-
-static int load_normal_reset(S390CPU *cpu)
-{
- S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
- CPUState *t;
-
- pause_all_vcpus();
- cpu_synchronize_all_states();
- CPU_FOREACH(t) {
- run_on_cpu(t, s390_do_cpu_reset, RUN_ON_CPU_NULL);
- }
- s390_cmma_reset();
- subsystem_reset();
- scc->initial_cpu_reset(CPU(cpu));
- scc->load_normal(CPU(cpu));
- cpu_synchronize_all_post_reset();
- resume_all_vcpus();
- return 0;
-}
-
-int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3)
-{
- uint64_t func = env->regs[r1];
- uint64_t timeout = env->regs[r1 + 1];
- uint64_t action = env->regs[r3];
- Object *obj;
- DIAG288State *diag288;
- DIAG288Class *diag288_class;
-
- if (r1 % 2 || action != 0) {
- return -1;
- }
-
- /* Timeout must be more than 15 seconds except for timer deletion */
- if (func != WDT_DIAG288_CANCEL && timeout < 15) {
- return -1;
- }
-
- obj = object_resolve_path_type("", TYPE_WDT_DIAG288, NULL);
- if (!obj) {
- return -1;
- }
-
- diag288 = DIAG288(obj);
- diag288_class = DIAG288_GET_CLASS(diag288);
- return diag288_class->handle_timer(diag288, func, timeout);
-}
-
-#define DIAG_308_RC_OK 0x0001
-#define DIAG_308_RC_NO_CONF 0x0102
-#define DIAG_308_RC_INVALID 0x0402
-
-void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3)
-{
- uint64_t addr = env->regs[r1];
- uint64_t subcode = env->regs[r3];
- IplParameterBlock *iplb;
-
- 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) {
- case 0:
- modified_clear_reset(s390_env_get_cpu(env));
- if (tcg_enabled()) {
- cpu_loop_exit(CPU(s390_env_get_cpu(env)));
- }
- break;
- case 1:
- load_normal_reset(s390_env_get_cpu(env));
- if (tcg_enabled()) {
- cpu_loop_exit(CPU(s390_env_get_cpu(env)));
- }
- break;
- case 3:
- s390_reipl_request();
- if (tcg_enabled()) {
- cpu_loop_exit(CPU(s390_env_get_cpu(env)));
- }
- break;
- case 5:
- if ((r1 & 1) || (addr & 0x0fffULL)) {
- program_interrupt(env, PGM_SPECIFICATION, ILEN_LATER_INC);
- return;
- }
- 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(IplParameterBlock));
- cpu_physical_memory_read(addr, iplb, sizeof(iplb->len));
- if (!iplb_valid_len(iplb)) {
- env->regs[r1 + 1] = DIAG_308_RC_INVALID;
- goto out;
- }
-
- cpu_physical_memory_read(addr, iplb, be32_to_cpu(iplb->len));
-
- if (!iplb_valid_ccw(iplb) && !iplb_valid_fcp(iplb)) {
- env->regs[r1 + 1] = DIAG_308_RC_INVALID;
- goto out;
- }
-
- s390_ipl_update_diag308(iplb);
- env->regs[r1 + 1] = DIAG_308_RC_OK;
-out:
- g_free(iplb);
- return;
- case 6:
- if ((r1 & 1) || (addr & 0x0fffULL)) {
- program_interrupt(env, PGM_SPECIFICATION, ILEN_LATER_INC);
- return;
- }
- 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, be32_to_cpu(iplb->len));
- env->regs[r1 + 1] = DIAG_308_RC_OK;
- } else {
- env->regs[r1 + 1] = DIAG_308_RC_NO_CONF;
- }
- return;
- default:
- hw_error("Unhandled diag308 subcode %" PRIx64, subcode);
- break;
- }
-}
-#endif
-
void HELPER(diag)(CPUS390XState *env, uint32_t r1, uint32_t r3, uint32_t num)
{
uint64_t r;
handle_diag_308(env, r1, r3);
r = 0;
break;
+ case 0x288:
+ /* time bomb (watchdog) */
+ r = handle_diag_288(env, r1, r3);
+ break;
default:
r = -1;
break;
}
if (r) {
- program_interrupt(env, PGM_OPERATION, ILEN_LATER_INC);
+ program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO);
}
}
uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0,
uint64_t r0, uint64_t r1)
{
+ S390CPU *cpu = s390_env_get_cpu(env);
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);
+ program_interrupt(env, PGM_SPECIFICATION, 4);
}
sel1 = r0 & STSI_R0_SEL1_MASK;
if ((sel1 == 1) && (sel2 == 1)) {
/* Basic Machine Configuration */
struct sysib_111 sysib;
+ char type[5] = {};
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 */
+ /* same as machine type number in STORE CPU ID, but in EBCDIC */
+ snprintf(type, ARRAY_SIZE(type), "%X", cpu->model->def->type);
+ ebcdic_put(sysib.type, type, 4);
+ /* model number (not stored in STORE CPU ID for z/Architecure) */
ebcdic_put(sysib.model, "QEMU ", 16);
ebcdic_put(sysib.sequence, "QEMU ", 16);
ebcdic_put(sysib.plant, "QEMU", 4);
}
}
+/* Check if an address is within the PER starting address and the PER
+ ending address. The address range might loop. */
+static inline bool get_per_in_range(CPUS390XState *env, uint64_t addr)
+{
+ if (env->cregs[10] <= env->cregs[11]) {
+ return env->cregs[10] <= addr && addr <= env->cregs[11];
+ } else {
+ return env->cregs[10] <= addr || addr <= env->cregs[11];
+ }
+}
+
void HELPER(per_branch)(CPUS390XState *env, uint64_t from, uint64_t to)
{
if ((env->cregs[9] & PER_CR9_EVENT_BRANCH)) {
if (env->cregs[9] & PER_CR9_EVENT_NULLIFICATION) {
CPUState *cs = CPU(s390_env_get_cpu(env));
+ env->per_perc_atmid |= PER_CODE_EVENT_NULLIFICATION;
env->int_pgm_code = PGM_PER;
env->int_pgm_ilen = get_ilen(cpu_ldub_code(env, addr));