X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/a195fdd028370faa54ba3d627f1add8401ac5193..28f3331887f9ae1fc19d2b9d7914047483442270:/target-s390x/misc_helper.c diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c index e1007fa35b..86da1947b9 100644 --- a/target-s390x/misc_helper.c +++ b/target-s390x/misc_helper.c @@ -18,20 +18,22 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" #include "cpu.h" #include "exec/memory.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" -#include #include "sysemu/kvm.h" #include "qemu/timer.h" #include "exec/address-spaces.h" #ifdef CONFIG_KVM #include #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" @@ -61,7 +63,7 @@ void QEMU_NORETURN runtime_exception(CPUS390XState *env, int excp, /* 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; + env->psw.addr += t; cpu_loop_exit(cs); } @@ -126,8 +128,9 @@ static int modified_clear_reset(S390CPU *cpu) CPU_FOREACH(t) { run_on_cpu(t, s390_do_cpu_full_reset, t); } - cmma_reset(cpu); - io_subsystem_reset(); + s390_cmma_reset(); + subsystem_reset(); + s390_crypto_reset(); scc->load_normal(CPU(cpu)); cpu_synchronize_all_post_reset(); resume_all_vcpus(); @@ -144,8 +147,8 @@ static int load_normal_reset(S390CPU *cpu) CPU_FOREACH(t) { run_on_cpu(t, s390_do_cpu_reset, t); } - cmma_reset(cpu); - io_subsystem_reset(); + s390_cmma_reset(); + subsystem_reset(); scc->initial_cpu_reset(CPU(cpu)); scc->load_normal(CPU(cpu)); cpu_synchronize_all_post_reset(); @@ -153,6 +156,34 @@ static int load_normal_reset(S390CPU *cpu) 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 @@ -176,9 +207,21 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3) 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)) { @@ -190,13 +233,23 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3) 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 { + 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: @@ -211,8 +264,7 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3) } iplb = s390_ipl_get_iplb(); if (iplb) { - cpu_physical_memory_write(addr, iplb, - sizeof(struct IplParameterBlock)); + 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; @@ -225,9 +277,7 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3) } #endif -/* DIAG */ -uint64_t HELPER(diag)(CPUS390XState *env, uint32_t num, uint64_t mem, - uint64_t code) +void HELPER(diag)(CPUS390XState *env, uint32_t r1, uint32_t r3, uint32_t num) { uint64_t r; @@ -242,6 +292,7 @@ uint64_t HELPER(diag)(CPUS390XState *env, uint32_t num, uint64_t mem, break; case 0x308: /* ipl */ + handle_diag_308(env, r1, r3); r = 0; break; default: @@ -252,8 +303,6 @@ uint64_t HELPER(diag)(CPUS390XState *env, uint32_t num, uint64_t mem, if (r) { program_interrupt(env, PGM_OPERATION, ILEN_LATER_INC); } - - return r; } /* Set Prefix */ @@ -263,12 +312,13 @@ void HELPER(spx)(CPUS390XState *env, uint64_t a1) uint32_t prefix = a1 & 0x7fffe000; env->psa = prefix; - qemu_log("prefix: %#x\n", prefix); + HELPER_LOG("prefix: %#x\n", prefix); tlb_flush_page(cs, 0); tlb_flush_page(cs, TARGET_PAGE_SIZE); } -static inline uint64_t clock_value(CPUS390XState *env) +/* Store Clock */ +uint64_t HELPER(stck)(CPUS390XState *env) { uint64_t time; @@ -278,12 +328,6 @@ static inline uint64_t clock_value(CPUS390XState *env) return time; } -/* Store Clock */ -uint64_t HELPER(stck)(CPUS390XState *env) -{ - return clock_value(env); -} - /* Set Clock Comparator */ void HELPER(sckc)(CPUS390XState *env, uint64_t time) { @@ -291,19 +335,21 @@ void HELPER(sckc)(CPUS390XState *env, uint64_t time) return; } - /* difference between now and then */ - time -= clock_value(env); + env->ckc = time; + + /* difference between origins */ + time -= env->tod_offset; + /* nanoseconds */ - time = (time * 125) >> 9; + time = tod2time(time); - timer_mod(env->tod_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + time); + timer_mod(env->tod_timer, env->tod_basetime + time); } /* Store Clock Comparator */ uint64_t HELPER(stckc)(CPUS390XState *env) { - /* XXX implement */ - return 0; + return env->ckc; } /* Set CPU Timer */ @@ -314,16 +360,17 @@ void HELPER(spt)(CPUS390XState *env, uint64_t time) } /* nanoseconds */ - time = (time * 125) >> 9; + time = tod2time(time); - timer_mod(env->cpu_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + time); + env->cputm = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + time; + + timer_mod(env->cpu_timer, env->cputm); } /* Store CPU Timer */ uint64_t HELPER(stpt)(CPUS390XState *env) { - /* XXX implement */ - return 0; + return time2tod(env->cputm - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); } /* Store System Information */ @@ -496,3 +543,111 @@ uint32_t HELPER(sigp)(CPUS390XState *env, uint64_t order_code, uint32_t r1, return cc; } #endif + +#ifndef CONFIG_USER_ONLY +void HELPER(xsch)(CPUS390XState *env, uint64_t r1) +{ + S390CPU *cpu = s390_env_get_cpu(env); + ioinst_handle_xsch(cpu, r1); +} + +void HELPER(csch)(CPUS390XState *env, uint64_t r1) +{ + S390CPU *cpu = s390_env_get_cpu(env); + ioinst_handle_csch(cpu, r1); +} + +void HELPER(hsch)(CPUS390XState *env, uint64_t r1) +{ + S390CPU *cpu = s390_env_get_cpu(env); + ioinst_handle_hsch(cpu, r1); +} + +void HELPER(msch)(CPUS390XState *env, uint64_t r1, uint64_t inst) +{ + S390CPU *cpu = s390_env_get_cpu(env); + ioinst_handle_msch(cpu, r1, inst >> 16); +} + +void HELPER(rchp)(CPUS390XState *env, uint64_t r1) +{ + S390CPU *cpu = s390_env_get_cpu(env); + ioinst_handle_rchp(cpu, r1); +} + +void HELPER(rsch)(CPUS390XState *env, uint64_t r1) +{ + S390CPU *cpu = s390_env_get_cpu(env); + ioinst_handle_rsch(cpu, r1); +} + +void HELPER(ssch)(CPUS390XState *env, uint64_t r1, uint64_t inst) +{ + S390CPU *cpu = s390_env_get_cpu(env); + ioinst_handle_ssch(cpu, r1, inst >> 16); +} + +void HELPER(stsch)(CPUS390XState *env, uint64_t r1, uint64_t inst) +{ + S390CPU *cpu = s390_env_get_cpu(env); + ioinst_handle_stsch(cpu, r1, inst >> 16); +} + +void HELPER(tsch)(CPUS390XState *env, uint64_t r1, uint64_t inst) +{ + S390CPU *cpu = s390_env_get_cpu(env); + ioinst_handle_tsch(cpu, r1, inst >> 16); +} + +void HELPER(chsc)(CPUS390XState *env, uint64_t inst) +{ + S390CPU *cpu = s390_env_get_cpu(env); + ioinst_handle_chsc(cpu, inst >> 16); +} +#endif + +#ifndef CONFIG_USER_ONLY +void HELPER(per_check_exception)(CPUS390XState *env) +{ + CPUState *cs = CPU(s390_env_get_cpu(env)); + + if (env->per_perc_atmid) { + env->int_pgm_code = PGM_PER; + env->int_pgm_ilen = get_ilen(cpu_ldub_code(env, env->per_address)); + + cs->exception_index = EXCP_PGM; + cpu_loop_exit(cs); + } +} + +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_CONTROL_BRANCH_ADDRESS) + || get_per_in_range(env, to)) { + env->per_address = from; + env->per_perc_atmid = PER_CODE_EVENT_BRANCH | get_per_atmid(env); + } + } +} + +void HELPER(per_ifetch)(CPUS390XState *env, uint64_t addr) +{ + if ((env->cregs[9] & PER_CR9_EVENT_IFETCH) && get_per_in_range(env, addr)) { + env->per_address = addr; + env->per_perc_atmid = PER_CODE_EVENT_IFETCH | get_per_atmid(env); + + /* If the instruction has to be nullified, trigger the + exception immediately. */ + if (env->cregs[9] & PER_CR9_EVENT_NULLIFICATION) { + CPUState *cs = CPU(s390_env_get_cpu(env)); + + env->int_pgm_code = PGM_PER; + env->int_pgm_ilen = get_ilen(cpu_ldub_code(env, addr)); + + cs->exception_index = EXCP_PGM; + cpu_loop_exit(cs); + } + } +} +#endif