s390: Wire up channel I/O in kvm.
Trigger the code for our virtual css in case of instruction intercepts for I/O instructions. Handle the tsch exit for the subchannel-related part of tsch. Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com> Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
parent
df1fe5bb49
commit
09b9987829
@ -1058,6 +1058,13 @@ void QEMU_NORETURN runtime_exception(CPUS390XState *env, int excp,
|
||||
|
||||
#include <sysemu/kvm.h>
|
||||
|
||||
#ifdef CONFIG_KVM
|
||||
void kvm_s390_io_interrupt(S390CPU *cpu, uint16_t subchannel_id,
|
||||
uint16_t subchannel_nr, uint32_t io_int_parm,
|
||||
uint32_t io_int_word);
|
||||
void kvm_s390_crw_mchk(S390CPU *cpu);
|
||||
void kvm_s390_enable_css_support(S390CPU *cpu);
|
||||
#else
|
||||
static inline void kvm_s390_io_interrupt(S390CPU *cpu,
|
||||
uint16_t subchannel_id,
|
||||
uint16_t subchannel_nr,
|
||||
@ -1068,6 +1075,10 @@ static inline void kvm_s390_io_interrupt(S390CPU *cpu,
|
||||
static inline void kvm_s390_crw_mchk(S390CPU *cpu)
|
||||
{
|
||||
}
|
||||
static inline void kvm_s390_enable_css_support(S390CPU *cpu)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void s390_io_interrupt(S390CPU *cpu,
|
||||
uint16_t subchannel_id,
|
||||
|
@ -47,9 +47,29 @@
|
||||
|
||||
#define IPA0_DIAG 0x8300
|
||||
#define IPA0_SIGP 0xae00
|
||||
#define IPA0_PRIV 0xb200
|
||||
#define IPA0_B2 0xb200
|
||||
#define IPA0_B9 0xb900
|
||||
#define IPA0_EB 0xeb00
|
||||
|
||||
#define PRIV_SCLP_CALL 0x20
|
||||
#define PRIV_CSCH 0x30
|
||||
#define PRIV_HSCH 0x31
|
||||
#define PRIV_MSCH 0x32
|
||||
#define PRIV_SSCH 0x33
|
||||
#define PRIV_STSCH 0x34
|
||||
#define PRIV_TSCH 0x35
|
||||
#define PRIV_TPI 0x36
|
||||
#define PRIV_SAL 0x37
|
||||
#define PRIV_RSCH 0x38
|
||||
#define PRIV_STCRW 0x39
|
||||
#define PRIV_STCPS 0x3a
|
||||
#define PRIV_RCHP 0x3b
|
||||
#define PRIV_SCHM 0x3c
|
||||
#define PRIV_CHSC 0x5f
|
||||
#define PRIV_SIGA 0x74
|
||||
#define PRIV_XSCH 0x76
|
||||
#define PRIV_SQBS 0x8a
|
||||
#define PRIV_EQBS 0x9c
|
||||
#define DIAG_KVM_HYPERCALL 0x500
|
||||
#define DIAG_KVM_BREAKPOINT 0x501
|
||||
|
||||
@ -380,10 +400,123 @@ static int kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_priv(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1)
|
||||
static int kvm_handle_css_inst(S390CPU *cpu, struct kvm_run *run,
|
||||
uint8_t ipa0, uint8_t ipa1, uint8_t ipb)
|
||||
{
|
||||
int r = 0;
|
||||
int no_cc = 0;
|
||||
CPUS390XState *env = &cpu->env;
|
||||
|
||||
if (ipa0 != 0xb2) {
|
||||
/* Not handled for now. */
|
||||
return -1;
|
||||
}
|
||||
cpu_synchronize_state(env);
|
||||
switch (ipa1) {
|
||||
case PRIV_XSCH:
|
||||
r = ioinst_handle_xsch(env, env->regs[1]);
|
||||
break;
|
||||
case PRIV_CSCH:
|
||||
r = ioinst_handle_csch(env, env->regs[1]);
|
||||
break;
|
||||
case PRIV_HSCH:
|
||||
r = ioinst_handle_hsch(env, env->regs[1]);
|
||||
break;
|
||||
case PRIV_MSCH:
|
||||
r = ioinst_handle_msch(env, env->regs[1], run->s390_sieic.ipb);
|
||||
break;
|
||||
case PRIV_SSCH:
|
||||
r = ioinst_handle_ssch(env, env->regs[1], run->s390_sieic.ipb);
|
||||
break;
|
||||
case PRIV_STCRW:
|
||||
r = ioinst_handle_stcrw(env, run->s390_sieic.ipb);
|
||||
break;
|
||||
case PRIV_STSCH:
|
||||
r = ioinst_handle_stsch(env, env->regs[1], run->s390_sieic.ipb);
|
||||
break;
|
||||
case PRIV_TSCH:
|
||||
/* We should only get tsch via KVM_EXIT_S390_TSCH. */
|
||||
fprintf(stderr, "Spurious tsch intercept\n");
|
||||
break;
|
||||
case PRIV_CHSC:
|
||||
r = ioinst_handle_chsc(env, run->s390_sieic.ipb);
|
||||
break;
|
||||
case PRIV_TPI:
|
||||
/* This should have been handled by kvm already. */
|
||||
fprintf(stderr, "Spurious tpi intercept\n");
|
||||
break;
|
||||
case PRIV_SCHM:
|
||||
no_cc = 1;
|
||||
r = ioinst_handle_schm(env, env->regs[1], env->regs[2],
|
||||
run->s390_sieic.ipb);
|
||||
break;
|
||||
case PRIV_RSCH:
|
||||
r = ioinst_handle_rsch(env, env->regs[1]);
|
||||
break;
|
||||
case PRIV_RCHP:
|
||||
r = ioinst_handle_rchp(env, env->regs[1]);
|
||||
break;
|
||||
case PRIV_STCPS:
|
||||
/* We do not provide this instruction, it is suppressed. */
|
||||
no_cc = 1;
|
||||
r = 0;
|
||||
break;
|
||||
case PRIV_SAL:
|
||||
no_cc = 1;
|
||||
r = ioinst_handle_sal(env, env->regs[1]);
|
||||
break;
|
||||
default:
|
||||
r = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (r >= 0) {
|
||||
if (!no_cc) {
|
||||
setcc(cpu, r);
|
||||
}
|
||||
r = 0;
|
||||
} else if (r < -1) {
|
||||
r = 0;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static int is_ioinst(uint8_t ipa0, uint8_t ipa1, uint8_t ipb)
|
||||
{
|
||||
int ret = 0;
|
||||
uint16_t ipa = (ipa0 << 8) | ipa1;
|
||||
|
||||
switch (ipa) {
|
||||
case IPA0_B2 | PRIV_CSCH:
|
||||
case IPA0_B2 | PRIV_HSCH:
|
||||
case IPA0_B2 | PRIV_MSCH:
|
||||
case IPA0_B2 | PRIV_SSCH:
|
||||
case IPA0_B2 | PRIV_STSCH:
|
||||
case IPA0_B2 | PRIV_TPI:
|
||||
case IPA0_B2 | PRIV_SAL:
|
||||
case IPA0_B2 | PRIV_RSCH:
|
||||
case IPA0_B2 | PRIV_STCRW:
|
||||
case IPA0_B2 | PRIV_STCPS:
|
||||
case IPA0_B2 | PRIV_RCHP:
|
||||
case IPA0_B2 | PRIV_SCHM:
|
||||
case IPA0_B2 | PRIV_CHSC:
|
||||
case IPA0_B2 | PRIV_SIGA:
|
||||
case IPA0_B2 | PRIV_XSCH:
|
||||
case IPA0_B9 | PRIV_EQBS:
|
||||
case IPA0_EB | PRIV_SQBS:
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int handle_priv(S390CPU *cpu, struct kvm_run *run,
|
||||
uint8_t ipa0, uint8_t ipa1)
|
||||
{
|
||||
int r = 0;
|
||||
uint16_t ipbh0 = (run->s390_sieic.ipb & 0xffff0000) >> 16;
|
||||
uint8_t ipb = run->s390_sieic.ipb & 0xff;
|
||||
|
||||
dprintf("KVM: PRIV: %d\n", ipa1);
|
||||
switch (ipa1) {
|
||||
@ -391,8 +524,16 @@ static int handle_priv(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1)
|
||||
r = kvm_sclp_service_call(cpu, run, ipbh0);
|
||||
break;
|
||||
default:
|
||||
dprintf("KVM: unknown PRIV: 0x%x\n", ipa1);
|
||||
r = -1;
|
||||
if (is_ioinst(ipa0, ipa1, ipb)) {
|
||||
r = kvm_handle_css_inst(cpu, run, ipa0, ipa1, ipb);
|
||||
if (r == -1) {
|
||||
setcc(cpu, 3);
|
||||
r = 0;
|
||||
}
|
||||
} else {
|
||||
dprintf("KVM: unknown PRIV: 0x%x\n", ipa1);
|
||||
r = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -533,15 +674,17 @@ static int handle_instruction(S390CPU *cpu, struct kvm_run *run)
|
||||
|
||||
dprintf("handle_instruction 0x%x 0x%x\n", run->s390_sieic.ipa, run->s390_sieic.ipb);
|
||||
switch (ipa0) {
|
||||
case IPA0_PRIV:
|
||||
r = handle_priv(cpu, run, ipa1);
|
||||
break;
|
||||
case IPA0_DIAG:
|
||||
r = handle_diag(env, run, ipb_code);
|
||||
break;
|
||||
case IPA0_SIGP:
|
||||
r = handle_sigp(cpu, run, ipa1);
|
||||
break;
|
||||
case IPA0_B2:
|
||||
case IPA0_B9:
|
||||
case IPA0_EB:
|
||||
r = handle_priv(cpu, run, ipa0 >> 8, ipa1);
|
||||
break;
|
||||
case IPA0_DIAG:
|
||||
r = handle_diag(env, run, ipb_code);
|
||||
break;
|
||||
case IPA0_SIGP:
|
||||
r = handle_sigp(cpu, run, ipa1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
@ -600,6 +743,43 @@ static int handle_intercept(S390CPU *cpu)
|
||||
return r;
|
||||
}
|
||||
|
||||
static int handle_tsch(S390CPU *cpu)
|
||||
{
|
||||
CPUS390XState *env = &cpu->env;
|
||||
CPUState *cs = CPU(cpu);
|
||||
struct kvm_run *run = cs->kvm_run;
|
||||
int ret;
|
||||
|
||||
cpu_synchronize_state(env);
|
||||
ret = ioinst_handle_tsch(env, env->regs[1], run->s390_tsch.ipb);
|
||||
if (ret >= 0) {
|
||||
/* Success; set condition code. */
|
||||
setcc(cpu, ret);
|
||||
ret = 0;
|
||||
} else if (ret < -1) {
|
||||
/*
|
||||
* Failure.
|
||||
* If an I/O interrupt had been dequeued, we have to reinject it.
|
||||
*/
|
||||
if (run->s390_tsch.dequeued) {
|
||||
uint16_t subchannel_id = run->s390_tsch.subchannel_id;
|
||||
uint16_t subchannel_nr = run->s390_tsch.subchannel_nr;
|
||||
uint32_t io_int_parm = run->s390_tsch.io_int_parm;
|
||||
uint32_t io_int_word = run->s390_tsch.io_int_word;
|
||||
uint32_t type = ((subchannel_id & 0xff00) << 24) |
|
||||
((subchannel_id & 0x00060) << 22) | (subchannel_nr << 16);
|
||||
|
||||
kvm_s390_interrupt_internal(cpu, type,
|
||||
((uint32_t)subchannel_id << 16)
|
||||
| subchannel_nr,
|
||||
((uint64_t)io_int_parm << 32)
|
||||
| io_int_word, 1);
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
@ -612,6 +792,9 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
|
||||
case KVM_EXIT_S390_RESET:
|
||||
qemu_system_reset_request();
|
||||
break;
|
||||
case KVM_EXIT_S390_TSCH:
|
||||
ret = handle_tsch(cpu);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown KVM exit: %d\n", run->exit_reason);
|
||||
break;
|
||||
@ -637,3 +820,33 @@ int kvm_arch_on_sigbus(int code, void *addr)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void kvm_s390_io_interrupt(S390CPU *cpu, uint16_t subchannel_id,
|
||||
uint16_t subchannel_nr, uint32_t io_int_parm,
|
||||
uint32_t io_int_word)
|
||||
{
|
||||
uint32_t type;
|
||||
|
||||
type = ((subchannel_id & 0xff00) << 24) |
|
||||
((subchannel_id & 0x00060) << 22) | (subchannel_nr << 16);
|
||||
kvm_s390_interrupt_internal(cpu, type,
|
||||
((uint32_t)subchannel_id << 16) | subchannel_nr,
|
||||
((uint64_t)io_int_parm << 32) | io_int_word, 1);
|
||||
}
|
||||
|
||||
void kvm_s390_crw_mchk(S390CPU *cpu)
|
||||
{
|
||||
kvm_s390_interrupt_internal(cpu, KVM_S390_MCHK, 1 << 28,
|
||||
0x00400f1d40330000, 1);
|
||||
}
|
||||
|
||||
void kvm_s390_enable_css_support(S390CPU *cpu)
|
||||
{
|
||||
struct kvm_enable_cap cap = {};
|
||||
int r;
|
||||
|
||||
/* Activate host kernel channel subsystem support. */
|
||||
cap.cap = KVM_CAP_S390_CSS_SUPPORT;
|
||||
r = kvm_vcpu_ioctl(CPU(cpu), KVM_ENABLE_CAP, &cap);
|
||||
assert(r == 0);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user