#include <asm/ptrace.h>
#include "qemu-common.h"
+#include "qemu/error-report.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
#include "hw/s390x/s390-pci-bus.h"
#include "hw/s390x/ipl.h"
#include "hw/s390x/ebcdic.h"
+#include "exec/memattrs.h"
/* #define DEBUG_KVM */
#define PRIV_E3_MPCIFC 0xd0
#define PRIV_E3_STPCIFC 0xd4
+#define DIAG_TIMEREVENT 0x288
#define DIAG_IPL 0x308
#define DIAG_KVM_HYPERCALL 0x500
#define DIAG_KVM_BREAKPOINT 0x501
#define ICPT_CPU_STOP 0x28
#define ICPT_IO 0x40
+#define NR_LOCAL_IRQS 32
+/*
+ * Needs to be big enough to contain max_cpus emergency signals
+ * and in addition NR_LOCAL_IRQS interrupts
+ */
+#define VCPU_IRQ_BUF_SIZE (sizeof(struct kvm_s390_irq) * \
+ (max_cpus + NR_LOCAL_IRQS))
+
static CPUWatchpoint hw_watchpoint;
/*
* We don't use a list because this structure is also used to transmit the
}
kvm_vm_enable_cap(s, KVM_CAP_S390_USER_SIGP, 0);
+ kvm_vm_enable_cap(s, KVM_CAP_S390_VECTOR_REGISTERS, 0);
kvm_vm_enable_cap(s, KVM_CAP_S390_USER_STSI, 0);
return 0;
{
S390CPU *cpu = S390_CPU(cs);
kvm_s390_set_cpu_state(cpu, cpu->env.cpu_state);
+ cpu->irqstate = g_malloc0(VCPU_IRQ_BUF_SIZE);
return 0;
}
}
}
- /* Floating point */
- for (i = 0; i < 16; i++) {
- fpu.fprs[i] = env->fregs[i].ll;
- }
- fpu.fpc = env->fpc;
+ if (can_sync_regs(cs, KVM_SYNC_VRS)) {
+ for (i = 0; i < 32; i++) {
+ cs->kvm_run->s.regs.vrs[i][0] = env->vregs[i][0].ll;
+ cs->kvm_run->s.regs.vrs[i][1] = env->vregs[i][1].ll;
+ }
+ cs->kvm_run->s.regs.fpc = env->fpc;
+ cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_VRS;
+ } else {
+ /* Floating point */
+ for (i = 0; i < 16; i++) {
+ fpu.fprs[i] = get_freg(env, i)->ll;
+ }
+ fpu.fpc = env->fpc;
- r = kvm_vcpu_ioctl(cs, KVM_SET_FPU, &fpu);
- if (r < 0) {
- return r;
+ r = kvm_vcpu_ioctl(cs, KVM_SET_FPU, &fpu);
+ if (r < 0) {
+ return r;
+ }
}
/* Do we need to save more than that? */
}
}
- /* Floating point */
- r = kvm_vcpu_ioctl(cs, KVM_GET_FPU, &fpu);
- if (r < 0) {
- return r;
- }
- for (i = 0; i < 16; i++) {
- env->fregs[i].ll = fpu.fprs[i];
+ /* Floating point and vector registers */
+ if (can_sync_regs(cs, KVM_SYNC_VRS)) {
+ for (i = 0; i < 32; i++) {
+ env->vregs[i][0].ll = cs->kvm_run->s.regs.vrs[i][0];
+ env->vregs[i][1].ll = cs->kvm_run->s.regs.vrs[i][1];
+ }
+ env->fpc = cs->kvm_run->s.regs.fpc;
+ } else {
+ r = kvm_vcpu_ioctl(cs, KVM_GET_FPU, &fpu);
+ if (r < 0) {
+ return r;
+ }
+ for (i = 0; i < 16; i++) {
+ get_freg(env, i)->ll = fpu.fprs[i];
+ }
+ env->fpc = fpu.fpc;
}
- env->fpc = fpu.fpc;
/* The prefix */
if (can_sync_regs(cs, KVM_SYNC_PREFIX)) {
{
}
-void kvm_arch_post_run(CPUState *cpu, struct kvm_run *run)
+MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run)
{
+ return MEMTXATTRS_UNSPECIFIED;
}
int kvm_arch_process_async_events(CPUState *cs)
return ret;
}
+static void kvm_handle_diag_288(S390CPU *cpu, struct kvm_run *run)
+{
+ uint64_t r1, r3;
+ int rc;
+
+ cpu_synchronize_state(CPU(cpu));
+ r1 = (run->s390_sieic.ipa & 0x00f0) >> 4;
+ r3 = run->s390_sieic.ipa & 0x000f;
+ rc = handle_diag_288(&cpu->env, r1, r3);
+ if (rc) {
+ enter_pgmcheck(cpu, PGM_SPECIFICATION);
+ }
+}
+
static void kvm_handle_diag_308(S390CPU *cpu, struct kvm_run *run)
{
uint64_t r1, r3;
*/
func_code = decode_basedisp_rs(&cpu->env, ipb, NULL) & DIAG_KVM_CODE_MASK;
switch (func_code) {
+ case DIAG_TIMEREVENT:
+ kvm_handle_diag_288(cpu, run);
+ break;
case DIAG_IPL:
kvm_handle_diag_308(cpu, run);
break;
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
}
+#define ADTL_SAVE_AREA_SIZE 1024
+static int kvm_s390_store_adtl_status(S390CPU *cpu, hwaddr addr)
+{
+ void *mem;
+ hwaddr len = ADTL_SAVE_AREA_SIZE;
+
+ mem = cpu_physical_memory_map(addr, &len, 1);
+ if (!mem) {
+ return -EFAULT;
+ }
+ if (len != ADTL_SAVE_AREA_SIZE) {
+ cpu_physical_memory_unmap(mem, len, 1, 0);
+ return -EFAULT;
+ }
+
+ memcpy(mem, &cpu->env.vregs, 512);
+
+ cpu_physical_memory_unmap(mem, len, 1, len);
+
+ return 0;
+}
+
#define KVM_S390_STORE_STATUS_DEF_ADDR offsetof(LowCore, floating_pt_save_area)
#define SAVE_AREA_SIZE 512
static int kvm_s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch)
static const uint8_t ar_id = 1;
uint64_t ckc = cpu->env.ckc >> 8;
void *mem;
+ int i;
hwaddr len = SAVE_AREA_SIZE;
mem = cpu_physical_memory_map(addr, &len, 1);
if (store_arch) {
cpu_physical_memory_write(offsetof(LowCore, ar_access_id), &ar_id, 1);
}
- memcpy(mem, &cpu->env.fregs, 128);
+ for (i = 0; i < 16; ++i) {
+ *((uint64 *)mem + i) = get_freg(&cpu->env, i)->ll;
+ }
memcpy(mem + 128, &cpu->env.regs, 128);
memcpy(mem + 256, &cpu->env.psw, 16);
memcpy(mem + 280, &cpu->env.psa, 4);
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
}
+static void sigp_store_adtl_status(void *arg)
+{
+ SigpInfo *si = arg;
+
+ if (!kvm_check_extension(kvm_state, KVM_CAP_S390_VECTOR_REGISTERS)) {
+ set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
+ return;
+ }
+
+ /* cpu has to be stopped */
+ if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) {
+ set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
+ return;
+ }
+
+ /* parameter must be aligned to 1024-byte boundary */
+ if (si->param & 0x3ff) {
+ set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
+ return;
+ }
+
+ cpu_synchronize_state(CPU(si->cpu));
+
+ if (kvm_s390_store_adtl_status(si->cpu, si->param)) {
+ set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
+ return;
+ }
+ si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
+}
+
static void sigp_restart(void *arg)
{
SigpInfo *si = arg;
case SIGP_STORE_STATUS_ADDR:
run_on_cpu(CPU(dst_cpu), sigp_store_status_at_address, &si);
break;
+ case SIGP_STORE_ADTL_STATUS:
+ run_on_cpu(CPU(dst_cpu), sigp_store_adtl_status, &si);
+ break;
case SIGP_SET_PREFIX:
run_on_cpu(CPU(dst_cpu), sigp_set_prefix, &si);
break;
return ret;
}
+void kvm_s390_vcpu_interrupt_pre_save(S390CPU *cpu)
+{
+ struct kvm_s390_irq_state irq_state;
+ CPUState *cs = CPU(cpu);
+ int32_t bytes;
+
+ if (!kvm_check_extension(kvm_state, KVM_CAP_S390_IRQ_STATE)) {
+ return;
+ }
+
+ irq_state.buf = (uint64_t) cpu->irqstate;
+ irq_state.len = VCPU_IRQ_BUF_SIZE;
+
+ bytes = kvm_vcpu_ioctl(cs, KVM_S390_GET_IRQ_STATE, &irq_state);
+ if (bytes < 0) {
+ cpu->irqstate_saved_size = 0;
+ error_report("Migration of interrupt state failed");
+ return;
+ }
+
+ cpu->irqstate_saved_size = bytes;
+}
+
+int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu)
+{
+ CPUState *cs = CPU(cpu);
+ struct kvm_s390_irq_state irq_state;
+ int r;
+
+ if (cpu->irqstate_saved_size == 0) {
+ return 0;
+ }
+
+ if (!kvm_check_extension(kvm_state, KVM_CAP_S390_IRQ_STATE)) {
+ return -ENOSYS;
+ }
+
+ irq_state.buf = (uint64_t) cpu->irqstate;
+ irq_state.len = cpu->irqstate_saved_size;
+
+ r = kvm_vcpu_ioctl(cs, KVM_S390_SET_IRQ_STATE, &irq_state);
+ if (r) {
+ error_report("Setting interrupt state failed %d", r);
+ }
+ return r;
+}
+
int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
uint64_t address, uint32_t data)
{
route->u.adapter.adapter_id = pbdev->routes.adapter.adapter_id;
return 0;
}
+
+int kvm_arch_msi_data_to_gsi(uint32_t data)
+{
+ abort();
+}