*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "cpu.h"
#include "internal.h"
#include "sysemu/hw_accel.h"
#include "exec/address-spaces.h"
+#include "exec/exec-all.h"
#include "sysemu/sysemu.h"
+#include "sysemu/tcg.h"
#include "trace.h"
+#include "qapi/qapi-types-machine.h"
QemuMutex qemu_sigp_mutex;
}
/* sensing without locks is racy, but it's the same for real hw */
- if (state != CPU_STATE_STOPPED && !ext_call) {
+ 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 == CPU_STATE_STOPPED) {
+ 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;
+
+ if (!tcg_enabled()) {
+ /* handled in KVM */
+ set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
+ return;
+ }
+
+ 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)
+{
+ if (!tcg_enabled()) {
+ /* handled in KVM */
+ set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
+ return;
+ }
+
+ cpu_inject_emergency_signal(dst_cpu, src_cpu->env.core_id);
+ si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
+}
+
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) != CPU_STATE_STOPPED) {
+ if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
return;
}
- s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
+ s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu);
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
}
S390CPU *cpu = S390_CPU(cs);
SigpInfo *si = arg.host_ptr;
- if (s390_cpu_get_state(cpu) != CPU_STATE_OPERATING) {
+ 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(CPU_STATE_STOPPED, cpu);
+ s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
} else {
/* execute the stop function */
cpu->env.sigp_order = SIGP_STOP;
SigpInfo *si = arg.host_ptr;
/* disabled wait - sleeping in user space */
- if (s390_cpu_get_state(cpu) == CPU_STATE_OPERATING && cs->halted) {
- s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
+ 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 CPU_STATE_OPERATING:
+ 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 CPU_STATE_STOPPED:
+ 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);
uint32_t address = si->param & 0x7ffffe00u;
/* cpu has to be stopped */
- if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
+ if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
return;
}
}
/* cpu has to be stopped */
- if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
+ if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
return;
}
SigpInfo *si = arg.host_ptr;
switch (s390_cpu_get_state(cpu)) {
- case CPU_STATE_STOPPED:
+ 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);
- s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
break;
- case CPU_STATE_OPERATING:
+ case S390_CPU_STATE_OPERATING:
cpu_inject_restart(cpu);
break;
}
cpu_synchronize_state(cs);
if (!address_space_access_valid(&address_space_memory, addr,
- sizeof(struct LowCore), false)) {
+ 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) != CPU_STATE_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;
}
+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;
+
+ if (!tcg_enabled()) {
+ /* handled in KVM */
+ set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
+ return;
+ }
+
+ /* 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)
{
if (!tcg_enabled()) {
}
}
-static int handle_sigp_single_dst(S390CPU *dst_cpu, uint8_t order,
+static int handle_sigp_single_dst(S390CPU *cpu, S390CPU *dst_cpu, uint8_t order,
uint64_t param, uint64_t *status_reg)
{
SigpInfo si = {
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_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;
if (cur_cpu == cpu) {
continue;
}
- if (s390_cpu_get_state(cur_cpu) != CPU_STATE_STOPPED) {
+ if (s390_cpu_get_state(cur_cpu) != S390_CPU_STATE_STOPPED) {
all_stopped = false;
}
}
{
uint64_t *status_reg = &env->regs[r1];
uint64_t param = (r1 % 2) ? env->regs[r1] : env->regs[r1 + 1];
- S390CPU *cpu = s390_env_get_cpu(env);
+ S390CPU *cpu = env_archcpu(env);
S390CPU *dst_cpu = NULL;
int ret;
default:
/* all other sigp orders target a single vcpu */
dst_cpu = s390_cpu_addr2state(env->regs[r3]);
- ret = handle_sigp_single_dst(dst_cpu, order, param, status_reg);
+ ret = handle_sigp_single_dst(cpu, dst_cpu, order, param, status_reg);
}
qemu_mutex_unlock(&qemu_sigp_mutex);
{
SigpInfo si = {};
- if (tcg_enabled()) {
- /* FIXME TCG */
- return -ENOSYS;
- }
-
run_on_cpu(CPU(cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si));
return 0;
}
void do_stop_interrupt(CPUS390XState *env)
{
- S390CPU *cpu = s390_env_get_cpu(env);
+ S390CPU *cpu = env_archcpu(env);
- if (s390_cpu_set_state(CPU_STATE_STOPPED, cpu) == 0) {
+ 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)