Skip to content

Commit 9abc2a0

Browse files
David Hildenbrandborntraeger
authored andcommitted
KVM: s390: fix memory overwrites when vx is disabled
The kernel now always uses vector registers when available, however KVM has special logic if support is really enabled for a guest. If support is disabled, guest_fpregs.fregs will only contain memory for the fpu. The kernel, however, will store vector registers into that area, resulting in crazy memory overwrites. Simply extending that area is not enough, because the format of the registers also changes. We would have to do additional conversions, making the code even more complex. Therefore let's directly use one place for the vector/fpu registers + fpc (in kvm_run). We just have to convert the data properly when accessing it. This makes current code much easier. Please note that vector/fpu registers are now always stored to vcpu->run->s.regs.vrs. Although this data is visible to QEMU and used for migration, we only guarantee valid values to user space when KVM_SYNC_VRS is set. As that is only the case when we have vector register support, we are on the safe side. Fixes: b5510d9 ("s390/fpu: always enable the vector facility if it is available") Cc: [email protected] # v4.4 d9a3a09 s390/kvm: remove dependency on struct save_area definition Signed-off-by: David Hildenbrand <[email protected]> Signed-off-by: Christian Borntraeger <[email protected]> [adopt to d9a3a09]
1 parent 14b0b4a commit 9abc2a0

File tree

2 files changed

+43
-84
lines changed

2 files changed

+43
-84
lines changed

arch/s390/include/asm/kvm_host.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,6 @@ struct kvm_vcpu_arch {
546546
struct kvm_s390_sie_block *sie_block;
547547
unsigned int host_acrs[NUM_ACRS];
548548
struct fpu host_fpregs;
549-
struct fpu guest_fpregs;
550549
struct kvm_s390_local_interrupt local_int;
551550
struct hrtimer ckc_timer;
552551
struct kvm_s390_pgm_info pgm;

arch/s390/kvm/kvm-s390.c

Lines changed: 43 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,7 +1244,6 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
12441244

12451245
if (vcpu->kvm->arch.use_cmma)
12461246
kvm_s390_vcpu_unsetup_cmma(vcpu);
1247-
kfree(vcpu->arch.guest_fpregs.fprs);
12481247
free_page((unsigned long)(vcpu->arch.sie_block));
12491248

12501249
kvm_vcpu_uninit(vcpu);
@@ -1424,44 +1423,18 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
14241423
return 0;
14251424
}
14261425

1427-
/*
1428-
* Backs up the current FP/VX register save area on a particular
1429-
* destination. Used to switch between different register save
1430-
* areas.
1431-
*/
1432-
static inline void save_fpu_to(struct fpu *dst)
1433-
{
1434-
dst->fpc = current->thread.fpu.fpc;
1435-
dst->regs = current->thread.fpu.regs;
1436-
}
1437-
1438-
/*
1439-
* Switches the FP/VX register save area from which to lazy
1440-
* restore register contents.
1441-
*/
1442-
static inline void load_fpu_from(struct fpu *from)
1443-
{
1444-
current->thread.fpu.fpc = from->fpc;
1445-
current->thread.fpu.regs = from->regs;
1446-
}
1447-
14481426
void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
14491427
{
14501428
/* Save host register state */
14511429
save_fpu_regs();
1452-
save_fpu_to(&vcpu->arch.host_fpregs);
1453-
1454-
if (test_kvm_facility(vcpu->kvm, 129)) {
1455-
current->thread.fpu.fpc = vcpu->run->s.regs.fpc;
1456-
/*
1457-
* Use the register save area in the SIE-control block
1458-
* for register restore and save in kvm_arch_vcpu_put()
1459-
*/
1460-
current->thread.fpu.vxrs =
1461-
(__vector128 *)&vcpu->run->s.regs.vrs;
1462-
} else
1463-
load_fpu_from(&vcpu->arch.guest_fpregs);
1430+
vcpu->arch.host_fpregs.fpc = current->thread.fpu.fpc;
1431+
vcpu->arch.host_fpregs.regs = current->thread.fpu.regs;
14641432

1433+
/* Depending on MACHINE_HAS_VX, data stored to vrs either
1434+
* has vector register or floating point register format.
1435+
*/
1436+
current->thread.fpu.regs = vcpu->run->s.regs.vrs;
1437+
current->thread.fpu.fpc = vcpu->run->s.regs.fpc;
14651438
if (test_fp_ctl(current->thread.fpu.fpc))
14661439
/* User space provided an invalid FPC, let's clear it */
14671440
current->thread.fpu.fpc = 0;
@@ -1477,19 +1450,13 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
14771450
atomic_andnot(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags);
14781451
gmap_disable(vcpu->arch.gmap);
14791452

1453+
/* Save guest register state */
14801454
save_fpu_regs();
1455+
vcpu->run->s.regs.fpc = current->thread.fpu.fpc;
14811456

1482-
if (test_kvm_facility(vcpu->kvm, 129))
1483-
/*
1484-
* kvm_arch_vcpu_load() set up the register save area to
1485-
* the &vcpu->run->s.regs.vrs and, thus, the vector registers
1486-
* are already saved. Only the floating-point control must be
1487-
* copied.
1488-
*/
1489-
vcpu->run->s.regs.fpc = current->thread.fpu.fpc;
1490-
else
1491-
save_fpu_to(&vcpu->arch.guest_fpregs);
1492-
load_fpu_from(&vcpu->arch.host_fpregs);
1457+
/* Restore host register state */
1458+
current->thread.fpu.fpc = vcpu->arch.host_fpregs.fpc;
1459+
current->thread.fpu.regs = vcpu->arch.host_fpregs.regs;
14931460

14941461
save_access_regs(vcpu->run->s.regs.acrs);
14951462
restore_access_regs(vcpu->arch.host_acrs);
@@ -1507,8 +1474,9 @@ static void kvm_s390_vcpu_initial_reset(struct kvm_vcpu *vcpu)
15071474
memset(vcpu->arch.sie_block->gcr, 0, 16 * sizeof(__u64));
15081475
vcpu->arch.sie_block->gcr[0] = 0xE0UL;
15091476
vcpu->arch.sie_block->gcr[14] = 0xC2000000UL;
1510-
vcpu->arch.guest_fpregs.fpc = 0;
1511-
asm volatile("lfpc %0" : : "Q" (vcpu->arch.guest_fpregs.fpc));
1477+
/* make sure the new fpc will be lazily loaded */
1478+
save_fpu_regs();
1479+
current->thread.fpu.fpc = 0;
15121480
vcpu->arch.sie_block->gbea = 1;
15131481
vcpu->arch.sie_block->pp = 0;
15141482
vcpu->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID;
@@ -1649,27 +1617,14 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
16491617
vcpu->arch.local_int.wq = &vcpu->wq;
16501618
vcpu->arch.local_int.cpuflags = &vcpu->arch.sie_block->cpuflags;
16511619

1652-
/*
1653-
* Allocate a save area for floating-point registers. If the vector
1654-
* extension is available, register contents are saved in the SIE
1655-
* control block. The allocated save area is still required in
1656-
* particular places, for example, in kvm_s390_vcpu_store_status().
1657-
*/
1658-
vcpu->arch.guest_fpregs.fprs = kzalloc(sizeof(freg_t) * __NUM_FPRS,
1659-
GFP_KERNEL);
1660-
if (!vcpu->arch.guest_fpregs.fprs)
1661-
goto out_free_sie_block;
1662-
16631620
rc = kvm_vcpu_init(vcpu, kvm, id);
16641621
if (rc)
1665-
goto out_free_fprs;
1622+
goto out_free_sie_block;
16661623
VM_EVENT(kvm, 3, "create cpu %d at 0x%pK, sie block at 0x%pK", id, vcpu,
16671624
vcpu->arch.sie_block);
16681625
trace_kvm_s390_create_vcpu(id, vcpu, vcpu->arch.sie_block);
16691626

16701627
return vcpu;
1671-
out_free_fprs:
1672-
kfree(vcpu->arch.guest_fpregs.fprs);
16731628
out_free_sie_block:
16741629
free_page((unsigned long)(vcpu->arch.sie_block));
16751630
out_free_cpu:
@@ -1882,19 +1837,27 @@ int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
18821837

18831838
int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
18841839
{
1840+
/* make sure the new values will be lazily loaded */
1841+
save_fpu_regs();
18851842
if (test_fp_ctl(fpu->fpc))
18861843
return -EINVAL;
1887-
memcpy(vcpu->arch.guest_fpregs.fprs, &fpu->fprs, sizeof(fpu->fprs));
1888-
vcpu->arch.guest_fpregs.fpc = fpu->fpc;
1889-
save_fpu_regs();
1890-
load_fpu_from(&vcpu->arch.guest_fpregs);
1844+
current->thread.fpu.fpc = fpu->fpc;
1845+
if (MACHINE_HAS_VX)
1846+
convert_fp_to_vx(current->thread.fpu.vxrs, (freg_t *)fpu->fprs);
1847+
else
1848+
memcpy(current->thread.fpu.fprs, &fpu->fprs, sizeof(fpu->fprs));
18911849
return 0;
18921850
}
18931851

18941852
int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
18951853
{
1896-
memcpy(&fpu->fprs, vcpu->arch.guest_fpregs.fprs, sizeof(fpu->fprs));
1897-
fpu->fpc = vcpu->arch.guest_fpregs.fpc;
1854+
/* make sure we have the latest values */
1855+
save_fpu_regs();
1856+
if (MACHINE_HAS_VX)
1857+
convert_vx_to_fp((freg_t *)fpu->fprs, current->thread.fpu.vxrs);
1858+
else
1859+
memcpy(fpu->fprs, current->thread.fpu.fprs, sizeof(fpu->fprs));
1860+
fpu->fpc = current->thread.fpu.fpc;
18981861
return 0;
18991862
}
19001863

@@ -2399,6 +2362,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
23992362
int kvm_s390_store_status_unloaded(struct kvm_vcpu *vcpu, unsigned long gpa)
24002363
{
24012364
unsigned char archmode = 1;
2365+
freg_t fprs[NUM_FPRS];
24022366
unsigned int px;
24032367
u64 clkcomp;
24042368
int rc;
@@ -2414,16 +2378,24 @@ int kvm_s390_store_status_unloaded(struct kvm_vcpu *vcpu, unsigned long gpa)
24142378
gpa = px;
24152379
} else
24162380
gpa -= __LC_FPREGS_SAVE_AREA;
2417-
rc = write_guest_abs(vcpu, gpa + __LC_FPREGS_SAVE_AREA,
2418-
vcpu->arch.guest_fpregs.fprs, 128);
2381+
2382+
/* manually convert vector registers if necessary */
2383+
if (MACHINE_HAS_VX) {
2384+
convert_vx_to_fp(fprs, current->thread.fpu.vxrs);
2385+
rc = write_guest_abs(vcpu, gpa + __LC_FPREGS_SAVE_AREA,
2386+
fprs, 128);
2387+
} else {
2388+
rc = write_guest_abs(vcpu, gpa + __LC_FPREGS_SAVE_AREA,
2389+
vcpu->run->s.regs.vrs, 128);
2390+
}
24192391
rc |= write_guest_abs(vcpu, gpa + __LC_GPREGS_SAVE_AREA,
24202392
vcpu->run->s.regs.gprs, 128);
24212393
rc |= write_guest_abs(vcpu, gpa + __LC_PSW_SAVE_AREA,
24222394
&vcpu->arch.sie_block->gpsw, 16);
24232395
rc |= write_guest_abs(vcpu, gpa + __LC_PREFIX_SAVE_AREA,
24242396
&px, 4);
24252397
rc |= write_guest_abs(vcpu, gpa + __LC_FP_CREG_SAVE_AREA,
2426-
&vcpu->arch.guest_fpregs.fpc, 4);
2398+
&vcpu->run->s.regs.fpc, 4);
24272399
rc |= write_guest_abs(vcpu, gpa + __LC_TOD_PROGREG_SAVE_AREA,
24282400
&vcpu->arch.sie_block->todpr, 4);
24292401
rc |= write_guest_abs(vcpu, gpa + __LC_CPU_TIMER_SAVE_AREA,
@@ -2446,19 +2418,7 @@ int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr)
24462418
* it into the save area
24472419
*/
24482420
save_fpu_regs();
2449-
if (test_kvm_facility(vcpu->kvm, 129)) {
2450-
/*
2451-
* If the vector extension is available, the vector registers
2452-
* which overlaps with floating-point registers are saved in
2453-
* the SIE-control block. Hence, extract the floating-point
2454-
* registers and the FPC value and store them in the
2455-
* guest_fpregs structure.
2456-
*/
2457-
vcpu->arch.guest_fpregs.fpc = current->thread.fpu.fpc;
2458-
convert_vx_to_fp(vcpu->arch.guest_fpregs.fprs,
2459-
current->thread.fpu.vxrs);
2460-
} else
2461-
save_fpu_to(&vcpu->arch.guest_fpregs);
2421+
vcpu->run->s.regs.fpc = current->thread.fpu.fpc;
24622422
save_access_regs(vcpu->run->s.regs.acrs);
24632423

24642424
return kvm_s390_store_status_unloaded(vcpu, addr);

0 commit comments

Comments
 (0)