Commit b31605c1 authored by Dominik Dingel's avatar Dominik Dingel Committed by Christian Borntraeger

KVM: s390: make cmma usage conditionally

When userspace reset the guest without notifying kvm, the CMMA state
of the pages might be unused, resulting in guest data corruption.
To avoid this, CMMA must be enabled only if userspace understands
the implications.

CMMA must be enabled before vCPU creation. It can't be switched off
once enabled.  All subsequently created vCPUs will be enabled for
CMMA according to the CMMA state of the VM.
Signed-off-by: default avatarDominik Dingel <dingel@linux.vnet.ibm.com>
Signed-off-by: default avatarChristian Borntraeger <borntraeger@de.ibm.com>
[remove now unnecessary calls to page_table_reset_pgste]
parent f2061656
...@@ -288,6 +288,7 @@ struct kvm_arch{ ...@@ -288,6 +288,7 @@ struct kvm_arch{
struct gmap *gmap; struct gmap *gmap;
int css_support; int css_support;
int use_irqchip; int use_irqchip;
int use_cmma;
struct s390_io_adapter *adapters[MAX_S390_IO_ADAPTERS]; struct s390_io_adapter *adapters[MAX_S390_IO_ADAPTERS];
}; };
......
...@@ -167,17 +167,11 @@ static int __diag_ipl_functions(struct kvm_vcpu *vcpu) ...@@ -167,17 +167,11 @@ static int __diag_ipl_functions(struct kvm_vcpu *vcpu)
VCPU_EVENT(vcpu, 5, "diag ipl functions, subcode %lx", subcode); VCPU_EVENT(vcpu, 5, "diag ipl functions, subcode %lx", subcode);
switch (subcode) { switch (subcode) {
case 0:
case 1:
page_table_reset_pgste(current->mm, 0, TASK_SIZE, false);
return -EOPNOTSUPP;
case 3: case 3:
vcpu->run->s390_reset_flags = KVM_S390_RESET_CLEAR; vcpu->run->s390_reset_flags = KVM_S390_RESET_CLEAR;
page_table_reset_pgste(current->mm, 0, TASK_SIZE, false);
break; break;
case 4: case 4:
vcpu->run->s390_reset_flags = 0; vcpu->run->s390_reset_flags = 0;
page_table_reset_pgste(current->mm, 0, TASK_SIZE, false);
break; break;
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
......
...@@ -437,9 +437,8 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) ...@@ -437,9 +437,8 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
if (kvm_is_ucontrol(vcpu->kvm)) if (kvm_is_ucontrol(vcpu->kvm))
gmap_free(vcpu->arch.gmap); gmap_free(vcpu->arch.gmap);
if (vcpu->arch.sie_block->cbrlo) if (kvm_s390_cmma_enabled(vcpu->kvm))
__free_page(__pfn_to_page( kvm_s390_vcpu_unsetup_cmma(vcpu);
vcpu->arch.sie_block->cbrlo >> PAGE_SHIFT));
free_page((unsigned long)(vcpu->arch.sie_block)); free_page((unsigned long)(vcpu->arch.sie_block));
kvm_vcpu_uninit(vcpu); kvm_vcpu_uninit(vcpu);
...@@ -553,9 +552,26 @@ int kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu) ...@@ -553,9 +552,26 @@ int kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
return 0; return 0;
} }
void kvm_s390_vcpu_unsetup_cmma(struct kvm_vcpu *vcpu)
{
free_page(vcpu->arch.sie_block->cbrlo);
vcpu->arch.sie_block->cbrlo = 0;
}
int kvm_s390_vcpu_setup_cmma(struct kvm_vcpu *vcpu)
{
vcpu->arch.sie_block->cbrlo = get_zeroed_page(GFP_KERNEL);
if (!vcpu->arch.sie_block->cbrlo)
return -ENOMEM;
vcpu->arch.sie_block->ecb2 |= 0x80;
vcpu->arch.sie_block->ecb2 &= ~0x08;
return 0;
}
int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu) int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
{ {
struct page *cbrl; int rc = 0;
atomic_set(&vcpu->arch.sie_block->cpuflags, CPUSTAT_ZARCH | atomic_set(&vcpu->arch.sie_block->cpuflags, CPUSTAT_ZARCH |
CPUSTAT_SM | CPUSTAT_SM |
...@@ -569,13 +585,10 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu) ...@@ -569,13 +585,10 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
vcpu->arch.sie_block->eca = 0xC1002001U; vcpu->arch.sie_block->eca = 0xC1002001U;
vcpu->arch.sie_block->fac = (int) (long) vfacilities; vcpu->arch.sie_block->fac = (int) (long) vfacilities;
vcpu->arch.sie_block->ictl |= ICTL_ISKE | ICTL_SSKE | ICTL_RRBE; vcpu->arch.sie_block->ictl |= ICTL_ISKE | ICTL_SSKE | ICTL_RRBE;
if (kvm_enabled_cmma()) { if (kvm_s390_cmma_enabled(vcpu->kvm)) {
cbrl = alloc_page(GFP_KERNEL | __GFP_ZERO); rc = kvm_s390_vcpu_setup_cmma(vcpu);
if (cbrl) { if (rc)
vcpu->arch.sie_block->ecb2 |= 0x80; return rc;
vcpu->arch.sie_block->ecb2 &= ~0x08;
vcpu->arch.sie_block->cbrlo = page_to_phys(cbrl);
}
} }
hrtimer_init(&vcpu->arch.ckc_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); hrtimer_init(&vcpu->arch.ckc_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
tasklet_init(&vcpu->arch.tasklet, kvm_s390_tasklet, tasklet_init(&vcpu->arch.tasklet, kvm_s390_tasklet,
...@@ -583,7 +596,7 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu) ...@@ -583,7 +596,7 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
vcpu->arch.ckc_timer.function = kvm_s390_idle_wakeup; vcpu->arch.ckc_timer.function = kvm_s390_idle_wakeup;
get_cpu_id(&vcpu->arch.cpu_id); get_cpu_id(&vcpu->arch.cpu_id);
vcpu->arch.cpu_id.version = 0xff; vcpu->arch.cpu_id.version = 0xff;
return 0; return rc;
} }
struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
...@@ -890,6 +903,18 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, ...@@ -890,6 +903,18 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
return -EINVAL; /* not implemented yet */ return -EINVAL; /* not implemented yet */
} }
bool kvm_s390_cmma_enabled(struct kvm *kvm)
{
if (!MACHINE_IS_LPAR)
return false;
/* only enable for z10 and later */
if (!MACHINE_HAS_EDAT1)
return false;
if (!kvm->arch.use_cmma)
return false;
return true;
}
static int kvm_s390_handle_requests(struct kvm_vcpu *vcpu) static int kvm_s390_handle_requests(struct kvm_vcpu *vcpu)
{ {
/* /*
...@@ -1072,16 +1097,6 @@ static int vcpu_post_run(struct kvm_vcpu *vcpu, int exit_reason) ...@@ -1072,16 +1097,6 @@ static int vcpu_post_run(struct kvm_vcpu *vcpu, int exit_reason)
return rc; return rc;
} }
bool kvm_enabled_cmma(void)
{
if (!MACHINE_IS_LPAR)
return false;
/* only enable for z10 and later */
if (!MACHINE_HAS_EDAT1)
return false;
return true;
}
static int __vcpu_run(struct kvm_vcpu *vcpu) static int __vcpu_run(struct kvm_vcpu *vcpu)
{ {
int rc, exit_reason; int rc, exit_reason;
......
...@@ -158,8 +158,11 @@ void s390_vcpu_block(struct kvm_vcpu *vcpu); ...@@ -158,8 +158,11 @@ void s390_vcpu_block(struct kvm_vcpu *vcpu);
void s390_vcpu_unblock(struct kvm_vcpu *vcpu); void s390_vcpu_unblock(struct kvm_vcpu *vcpu);
void exit_sie(struct kvm_vcpu *vcpu); void exit_sie(struct kvm_vcpu *vcpu);
void exit_sie_sync(struct kvm_vcpu *vcpu); void exit_sie_sync(struct kvm_vcpu *vcpu);
/* are we going to support cmma? */ int kvm_s390_vcpu_setup_cmma(struct kvm_vcpu *vcpu);
bool kvm_enabled_cmma(void); void kvm_s390_vcpu_unsetup_cmma(struct kvm_vcpu *vcpu);
/* is cmma enabled */
bool kvm_s390_cmma_enabled(struct kvm *kvm);
/* implemented in diag.c */ /* implemented in diag.c */
int kvm_s390_handle_diag(struct kvm_vcpu *vcpu); int kvm_s390_handle_diag(struct kvm_vcpu *vcpu);
......
...@@ -656,7 +656,7 @@ static int handle_essa(struct kvm_vcpu *vcpu) ...@@ -656,7 +656,7 @@ static int handle_essa(struct kvm_vcpu *vcpu)
VCPU_EVENT(vcpu, 5, "cmma release %d pages", entries); VCPU_EVENT(vcpu, 5, "cmma release %d pages", entries);
gmap = vcpu->arch.gmap; gmap = vcpu->arch.gmap;
vcpu->stat.instruction_essa++; vcpu->stat.instruction_essa++;
if (!kvm_enabled_cmma() || !vcpu->arch.sie_block->cbrlo) if (!kvm_s390_cmma_enabled(vcpu->kvm))
return kvm_s390_inject_program_int(vcpu, PGM_OPERATION); return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment