Files
unicorn/qemu/target/s390x/sigp.c

469 lines
14 KiB
C

/*
* s390x SIGP instruction handling
*
* Copyright (c) 2009 Alexander Graf <agraf@suse.de>
* Copyright IBM Corp. 2012
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "cpu.h"
#include "internal.h"
//#include "exec/address-spaces.h"
#include "exec/exec-all.h"
#include "sysemu/tcg.h"
typedef struct SigpInfo {
uint64_t param;
int cc;
uint64_t *status_reg;
} SigpInfo;
// static void set_sigp_status(SigpInfo *si, uint64_t status)
// {
// *si->status_reg &= 0xffffffff00000000ULL;
// *si->status_reg |= status;
// si->cc = SIGP_CC_STATUS_STORED;
// }
// static void sigp_sense(S390CPU *dst_cpu, SigpInfo *si)
// {
// uint8_t state = s390_cpu_get_state(dst_cpu);
// bool ext_call = dst_cpu->env.pending_int & INTERRUPT_EXTERNAL_CALL;
// uint64_t status = 0;
// /* sensing without locks is racy, but it's the same for real hw */
// if (state != S390_CPU_STATE_STOPPED && !ext_call) {
// si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
// } else {
// if (ext_call) {
// status |= SIGP_STAT_EXT_CALL_PENDING;
// }
// if (state == S390_CPU_STATE_STOPPED) {
// status |= SIGP_STAT_STOPPED;
// }
// set_sigp_status(si, status);
// }
// }
// static void sigp_external_call(S390CPU *src_cpu, S390CPU *dst_cpu, SigpInfo *si)
// {
// int ret;
// ret = cpu_inject_external_call(dst_cpu, src_cpu->env.core_id);
// if (!ret) {
// si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
// } else {
// set_sigp_status(si, SIGP_STAT_EXT_CALL_PENDING);
// }
// }
// static void sigp_emergency(S390CPU *src_cpu, S390CPU *dst_cpu, SigpInfo *si)
// {
// cpu_inject_emergency_signal(dst_cpu, src_cpu->env.core_id);
// si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
// }
#if 0
static void sigp_start(CPUState *cs, run_on_cpu_data arg)
{
S390CPU *cpu = S390_CPU(cs);
SigpInfo *si = arg.host_ptr;
if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
return;
}
s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu);
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
}
static void sigp_stop(CPUState *cs, run_on_cpu_data arg)
{
S390CPU *cpu = S390_CPU(cs);
SigpInfo *si = arg.host_ptr;
if (s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING) {
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
return;
}
/* disabled wait - sleeping in user space */
if (cs->halted) {
s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
} else {
/* execute the stop function */
cpu->env.sigp_order = SIGP_STOP;
cpu_inject_stop(cpu);
}
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
}
static void sigp_stop_and_store_status(CPUState *cs, run_on_cpu_data arg)
{
S390CPU *cpu = S390_CPU(cs);
SigpInfo *si = arg.host_ptr;
/* disabled wait - sleeping in user space */
if (s390_cpu_get_state(cpu) == S390_CPU_STATE_OPERATING && cs->halted) {
s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
}
switch (s390_cpu_get_state(cpu)) {
case S390_CPU_STATE_OPERATING:
cpu->env.sigp_order = SIGP_STOP_STORE_STATUS;
cpu_inject_stop(cpu);
/* store will be performed in do_stop_interrup() */
break;
case S390_CPU_STATE_STOPPED:
/* already stopped, just store the status */
// cpu_synchronize_state(cs);
s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true);
break;
}
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
}
static void sigp_store_status_at_address(CPUState *cs, run_on_cpu_data arg)
{
S390CPU *cpu = S390_CPU(cs);
SigpInfo *si = arg.host_ptr;
uint32_t address = si->param & 0x7ffffe00u;
/* cpu has to be stopped */
if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
return;
}
// cpu_synchronize_state(cs);
if (s390_store_status(cpu, address, false)) {
set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
return;
}
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
}
#define ADTL_SAVE_LC_MASK 0xfUL
static void sigp_store_adtl_status(CPUState *cs, run_on_cpu_data arg)
{
S390CPU *cpu = S390_CPU(cs);
SigpInfo *si = arg.host_ptr;
uint8_t lc = si->param & ADTL_SAVE_LC_MASK;
hwaddr addr = si->param & ~ADTL_SAVE_LC_MASK;
hwaddr len = 1UL << (lc ? lc : 10);
if (!s390_has_feat(S390_FEAT_VECTOR) &&
!s390_has_feat(S390_FEAT_GUARDED_STORAGE)) {
set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
return;
}
/* cpu has to be stopped */
if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
return;
}
/* address must be aligned to length */
if (addr & (len - 1)) {
set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
return;
}
/* no GS: only lc == 0 is valid */
if (!s390_has_feat(S390_FEAT_GUARDED_STORAGE) &&
lc != 0) {
set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
return;
}
/* GS: 0, 10, 11, 12 are valid */
if (s390_has_feat(S390_FEAT_GUARDED_STORAGE) &&
lc != 0 &&
lc != 10 &&
lc != 11 &&
lc != 12) {
set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
return;
}
// cpu_synchronize_state(cs);
if (s390_store_adtl_status(cpu, addr, len)) {
set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
return;
}
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
}
static void sigp_restart(CPUState *cs, run_on_cpu_data arg)
{
S390CPU *cpu = S390_CPU(cs);
SigpInfo *si = arg.host_ptr;
switch (s390_cpu_get_state(cpu)) {
case S390_CPU_STATE_STOPPED:
/* the restart irq has to be delivered prior to any other pending irq */
// cpu_synchronize_state(cs);
/*
* Set OPERATING (and unhalting) before loading the restart PSW.
* load_psw() will then properly halt the CPU again if necessary (TCG).
*/
s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu);
do_restart_interrupt(&cpu->env);
break;
case S390_CPU_STATE_OPERATING:
cpu_inject_restart(cpu);
break;
}
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
}
static void sigp_initial_cpu_reset(CPUState *cs, run_on_cpu_data arg)
{
S390CPU *cpu = S390_CPU(cs);
S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
SigpInfo *si = arg.host_ptr;
// cpu_synchronize_state(cs);
scc->reset(cs, S390_CPU_RESET_INITIAL);
// cpu_synchronize_post_reset(cs);
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
}
static void sigp_cpu_reset(CPUState *cs, run_on_cpu_data arg)
{
S390CPU *cpu = S390_CPU(cs);
S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
SigpInfo *si = arg.host_ptr;
// cpu_synchronize_state(cs);
scc->reset(cs, S390_CPU_RESET_NORMAL);
// cpu_synchronize_post_reset(cs);
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
}
static void sigp_set_prefix(CPUState *cs, run_on_cpu_data arg)
{
S390CPU *cpu = S390_CPU(cs);
CPUS390XState *env = &cpu->env;
SigpInfo *si = arg.host_ptr;
uint32_t addr = si->param & 0x7fffe000u;
// cpu_synchronize_state(cs);
if (!address_space_access_valid(env_cpu(env)->as, addr,
sizeof(struct LowCore), false,
MEMTXATTRS_UNSPECIFIED)) {
set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
return;
}
/* cpu has to be stopped */
if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
return;
}
cpu->env.psa = addr;
tlb_flush(cs);
// cpu_synchronize_post_init(cs);
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
}
#endif
// static void sigp_cond_emergency(S390CPU *src_cpu, S390CPU *dst_cpu,
// SigpInfo *si)
// {
// const uint64_t psw_int_mask = PSW_MASK_IO | PSW_MASK_EXT;
// uint16_t p_asn, s_asn, asn;
// uint64_t psw_addr, psw_mask;
// bool idle;
// /* this looks racy, but these values are only used when STOPPED */
// idle = CPU(dst_cpu)->halted;
// psw_addr = dst_cpu->env.psw.addr;
// psw_mask = dst_cpu->env.psw.mask;
// asn = si->param;
// p_asn = dst_cpu->env.cregs[4] & 0xffff; /* Primary ASN */
// s_asn = dst_cpu->env.cregs[3] & 0xffff; /* Secondary ASN */
// if (s390_cpu_get_state(dst_cpu) != S390_CPU_STATE_STOPPED ||
// (psw_mask & psw_int_mask) != psw_int_mask ||
// (idle && psw_addr != 0) ||
// (!idle && (asn == p_asn || asn == s_asn))) {
// cpu_inject_emergency_signal(dst_cpu, src_cpu->env.core_id);
// } else {
// set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
// }
// si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
// }
// static void sigp_sense_running(S390CPU *dst_cpu, SigpInfo *si)
// {
// /* sensing without locks is racy, but it's the same for real hw */
// //if (!s390_has_feat(S390_FEAT_SENSE_RUNNING_STATUS)) {
// // set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
// // return;
// //}
// /* If halted (which includes also STOPPED), it is not running */
// if (CPU(dst_cpu)->halted) {
// set_sigp_status(si, SIGP_STAT_NOT_RUNNING);
// } else {
// si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
// }
// }
// static int handle_sigp_single_dst(S390CPU *cpu, S390CPU *dst_cpu, uint8_t order,
// uint64_t param, uint64_t *status_reg)
// {
// SigpInfo si = {
// .param = param,
// .status_reg = status_reg,
// };
// /* cpu available? */
// if (dst_cpu == NULL) {
// return SIGP_CC_NOT_OPERATIONAL;
// }
// /* only resets can break pending orders */
// if (dst_cpu->env.sigp_order != 0 &&
// order != SIGP_CPU_RESET &&
// order != SIGP_INITIAL_CPU_RESET) {
// return SIGP_CC_BUSY;
// }
// switch (order) {
// case SIGP_SENSE:
// sigp_sense(dst_cpu, &si);
// break;
// case SIGP_EXTERNAL_CALL:
// sigp_external_call(cpu, dst_cpu, &si);
// break;
// case SIGP_EMERGENCY:
// sigp_emergency(cpu, dst_cpu, &si);
// break;
// case SIGP_START:
// //run_on_cpu(CPU(dst_cpu), sigp_start, RUN_ON_CPU_HOST_PTR(&si));
// break;
// case SIGP_STOP:
// //run_on_cpu(CPU(dst_cpu), sigp_stop, RUN_ON_CPU_HOST_PTR(&si));
// break;
// case SIGP_RESTART:
// //run_on_cpu(CPU(dst_cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si));
// break;
// case SIGP_STOP_STORE_STATUS:
// //run_on_cpu(CPU(dst_cpu), sigp_stop_and_store_status, RUN_ON_CPU_HOST_PTR(&si));
// break;
// case SIGP_STORE_STATUS_ADDR:
// //run_on_cpu(CPU(dst_cpu), sigp_store_status_at_address, RUN_ON_CPU_HOST_PTR(&si));
// break;
// case SIGP_STORE_ADTL_STATUS:
// //run_on_cpu(CPU(dst_cpu), sigp_store_adtl_status, RUN_ON_CPU_HOST_PTR(&si));
// break;
// case SIGP_SET_PREFIX:
// //run_on_cpu(CPU(dst_cpu), sigp_set_prefix, RUN_ON_CPU_HOST_PTR(&si));
// break;
// case SIGP_INITIAL_CPU_RESET:
// //run_on_cpu(CPU(dst_cpu), sigp_initial_cpu_reset, RUN_ON_CPU_HOST_PTR(&si));
// break;
// case SIGP_CPU_RESET:
// //run_on_cpu(CPU(dst_cpu), sigp_cpu_reset, RUN_ON_CPU_HOST_PTR(&si));
// break;
// case SIGP_COND_EMERGENCY:
// sigp_cond_emergency(cpu, dst_cpu, &si);
// break;
// case SIGP_SENSE_RUNNING:
// sigp_sense_running(dst_cpu, &si);
// break;
// default:
// set_sigp_status(&si, SIGP_STAT_INVALID_ORDER);
// }
// return si.cc;
// }
// static int sigp_set_architecture(S390CPU *cpu, uint32_t param,
// uint64_t *status_reg)
// {
// bool all_stopped = true;
// #if 0
// CPU_FOREACH(cur_cs) {
// cur_cpu = S390_CPU(cur_cs);
// if (cur_cpu == cpu) {
// continue;
// }
// if (s390_cpu_get_state(cur_cpu) != S390_CPU_STATE_STOPPED) {
// all_stopped = false;
// }
// }
// #endif
// all_stopped = false;
// *status_reg &= 0xffffffff00000000ULL;
// /* Reject set arch order, with czam we're always in z/Arch mode. */
// *status_reg |= (all_stopped ? SIGP_STAT_INVALID_PARAMETER :
// SIGP_STAT_INCORRECT_STATE);
// return SIGP_CC_STATUS_STORED;
// }
#if 0
int handle_sigp(CPUS390XState *env, uint8_t order, uint64_t r1, uint64_t r3)
{
uint64_t *status_reg = &env->regs[r1];
uint64_t param = (r1 % 2) ? env->regs[r1] : env->regs[r1 + 1];
S390CPU *cpu = env_archcpu(env);
S390CPU *dst_cpu = NULL;
int ret;
switch (order) {
case SIGP_SET_ARCH:
ret = sigp_set_architecture(cpu, param, status_reg);
break;
default:
/* all other sigp orders target a single vcpu */
dst_cpu = s390_cpu_addr2state(env->regs[r3]);
ret = handle_sigp_single_dst(cpu, dst_cpu, order, param, status_reg);
}
return ret;
}
#endif
int s390_cpu_restart(S390CPU *cpu)
{
//SigpInfo si = {};
//run_on_cpu(CPU(cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si));
return 0;
}
void do_stop_interrupt(CPUS390XState *env)
{
S390CPU *cpu = env_archcpu(env);
if (s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu) == 0) {
// qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
}
if (cpu->env.sigp_order == SIGP_STOP_STORE_STATUS) {
s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true);
}
env->sigp_order = 0;
env->pending_int &= ~INTERRUPT_STOP;
}
void s390_init_sigp(void)
{
}