aboutsummaryrefslogtreecommitdiff
path: root/target/s390x
diff options
context:
space:
mode:
authorFan Zhang <zhangfan@linux.vnet.ibm.com>2017-02-15 04:47:49 +0100
committerChristian Borntraeger <borntraeger@de.ibm.com>2017-07-14 12:29:49 +0200
commit62deb62d999cf9e2be61272c6b720104f764bd6a (patch)
tree5462d3a6f5935ece77b6a9e560e91c7cad50797c /target/s390x
parentc0a9cd940e6bbad75cef3b5e87b826125702c353 (diff)
s390x/kvm: enable guarded storage
Introduce guarded storage support for KVM guests on s390. We need to enable the capability, extend machine check validity, sigp store-additional-status-at-address, and migration. The feature is fenced for older machine type versions. Signed-off-by: Fan Zhang <zhangfan@linux.vnet.ibm.com> Reviewed-by: Cornelia Huck <cohuck@redhat.com> Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
Diffstat (limited to 'target/s390x')
-rw-r--r--target/s390x/cpu.h7
-rw-r--r--target/s390x/kvm.c71
-rw-r--r--target/s390x/machine.c17
3 files changed, 85 insertions, 10 deletions
diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h
index 8ab75c0d43..7732d01784 100644
--- a/target/s390x/cpu.h
+++ b/target/s390x/cpu.h
@@ -89,6 +89,7 @@ typedef struct CPUS390XState {
CPU_DoubleU vregs[32][2]; /* vector registers */
uint32_t aregs[16]; /* access registers */
uint8_t riccb[64]; /* runtime instrumentation control */
+ uint64_t gscb[4]; /* guarded storage control */
/* Fields up to this point are not cleared by initial CPU reset */
struct {} start_initial_reset_fields;
@@ -1166,6 +1167,7 @@ int kvm_s390_set_mem_limit(KVMState *s, uint64_t new_limit, uint64_t *hw_limit);
void kvm_s390_vcpu_interrupt_pre_save(S390CPU *cpu);
int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu);
int kvm_s390_get_ri(void);
+int kvm_s390_get_gs(void);
void kvm_s390_crypto_reset(void);
#else
static inline void kvm_s390_io_interrupt(uint16_t subchannel_id,
@@ -1220,6 +1222,10 @@ static inline int kvm_s390_get_ri(void)
{
return 0;
}
+static inline int kvm_s390_get_gs(void)
+{
+ return 0;
+}
static inline void kvm_s390_crypto_reset(void)
{
}
@@ -1328,6 +1334,7 @@ static inline bool s390_get_squash_mcss(void)
#define MCIC_VB_CR 0x0000000400000000ULL
#define MCIC_VB_ST 0x0000000100000000ULL
#define MCIC_VB_AR 0x0000000040000000ULL
+#define MCIC_VB_GS 0x0000000008000000ULL
#define MCIC_VB_PR 0x0000000000200000ULL
#define MCIC_VB_FC 0x0000000000100000ULL
#define MCIC_VB_CT 0x0000000000020000ULL
diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c
index c773bd6eac..831492f9a2 100644
--- a/target/s390x/kvm.c
+++ b/target/s390x/kvm.c
@@ -139,6 +139,7 @@ static int cap_async_pf;
static int cap_mem_op;
static int cap_s390_irq;
static int cap_ri;
+static int cap_gs;
static int active_cmma;
@@ -301,6 +302,11 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
cap_ri = 1;
}
}
+ if (gs_allowed()) {
+ if (kvm_vm_enable_cap(s, KVM_CAP_S390_GS, 0) == 0) {
+ cap_gs = 1;
+ }
+ }
/* Try to enable AIS facility */
kvm_vm_enable_cap(s, KVM_CAP_S390_AIS, 0);
@@ -472,6 +478,11 @@ int kvm_arch_put_registers(CPUState *cs, int level)
}
}
+ if (can_sync_regs(cs, KVM_SYNC_GSCB)) {
+ memcpy(cs->kvm_run->s.regs.gscb, env->gscb, 32);
+ cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_GSCB;
+ }
+
/* Finally the prefix */
if (can_sync_regs(cs, KVM_SYNC_PREFIX)) {
cs->kvm_run->s.regs.prefix = env->psa;
@@ -578,6 +589,10 @@ int kvm_arch_get_registers(CPUState *cs)
memcpy(env->riccb, cs->kvm_run->s.regs.riccb, 64);
}
+ if (can_sync_regs(cs, KVM_SYNC_GSCB)) {
+ memcpy(env->gscb, cs->kvm_run->s.regs.gscb, 32);
+ }
+
/* pfault parameters */
if (can_sync_regs(cs, KVM_SYNC_PFAULT)) {
env->pfault_token = cs->kvm_run->s.regs.pft;
@@ -1474,22 +1489,28 @@ static void sigp_stop(CPUState *cs, run_on_cpu_data arg)
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
}
-#define ADTL_SAVE_AREA_SIZE 1024
-static int kvm_s390_store_adtl_status(S390CPU *cpu, hwaddr addr)
+#define ADTL_GS_OFFSET 1024 /* offset of GS data in adtl save area */
+#define ADTL_GS_MIN_SIZE 2048 /* minimal size of adtl save area for GS */
+static int do_store_adtl_status(S390CPU *cpu, hwaddr addr, hwaddr len)
{
+ hwaddr save = len;
void *mem;
- hwaddr len = ADTL_SAVE_AREA_SIZE;
- mem = cpu_physical_memory_map(addr, &len, 1);
+ mem = cpu_physical_memory_map(addr, &save, 1);
if (!mem) {
return -EFAULT;
}
- if (len != ADTL_SAVE_AREA_SIZE) {
+ if (save != len) {
cpu_physical_memory_unmap(mem, len, 1, 0);
return -EFAULT;
}
- memcpy(mem, &cpu->env.vregs, 512);
+ if (s390_has_feat(S390_FEAT_VECTOR)) {
+ memcpy(mem, &cpu->env.vregs, 512);
+ }
+ if (s390_has_feat(S390_FEAT_GUARDED_STORAGE) && len >= ADTL_GS_MIN_SIZE) {
+ memcpy(mem + ADTL_GS_OFFSET, &cpu->env.gscb, 32);
+ }
cpu_physical_memory_unmap(mem, len, 1, len);
@@ -1585,12 +1606,17 @@ static void sigp_store_status_at_address(CPUState *cs, run_on_cpu_data arg)
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)) {
+ if (!s390_has_feat(S390_FEAT_VECTOR) &&
+ !s390_has_feat(S390_FEAT_GUARDED_STORAGE)) {
set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
return;
}
@@ -1601,15 +1627,32 @@ static void sigp_store_adtl_status(CPUState *cs, run_on_cpu_data arg)
return;
}
- /* parameter must be aligned to 1024-byte boundary */
- if (si->param & 0x3ff) {
+ /* 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 (kvm_s390_store_adtl_status(cpu, si->param)) {
+ if (do_store_adtl_status(cpu, addr, len)) {
set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
return;
}
@@ -2188,6 +2231,9 @@ static uint64_t build_channel_report_mcic(void)
if (s390_has_feat(S390_FEAT_VECTOR)) {
mcic |= MCIC_VB_VR;
}
+ if (s390_has_feat(S390_FEAT_GUARDED_STORAGE)) {
+ mcic |= MCIC_VB_GS;
+ }
return mcic;
}
@@ -2253,6 +2299,11 @@ int kvm_s390_get_ri(void)
return cap_ri;
}
+int kvm_s390_get_gs(void)
+{
+ return cap_gs;
+}
+
int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state)
{
struct kvm_mp_state mp_state = {};
diff --git a/target/s390x/machine.c b/target/s390x/machine.c
index 8f908bbe82..2dcadfdd29 100644
--- a/target/s390x/machine.c
+++ b/target/s390x/machine.c
@@ -174,6 +174,22 @@ const VMStateDescription vmstate_exval = {
}
};
+static bool gscb_needed(void *opaque)
+{
+ return kvm_s390_get_gs();
+}
+
+const VMStateDescription vmstate_gscb = {
+ .name = "cpu/gscb",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = gscb_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64_ARRAY(env.gscb, S390CPU, 4),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
const VMStateDescription vmstate_s390_cpu = {
.name = "cpu",
.post_load = cpu_post_load,
@@ -207,6 +223,7 @@ const VMStateDescription vmstate_s390_cpu = {
&vmstate_vregs,
&vmstate_riccb,
&vmstate_exval,
+ &vmstate_gscb,
NULL
},
};