Skip to content

Commit 19e1227

Browse files
frankjaaborntraeger
authored andcommitted
KVM: S390: protvirt: Introduce instruction data area bounce buffer
Now that we can't access guest memory anymore, we have a dedicated satellite block that's a bounce buffer for instruction data. We re-use the memop interface to copy the instruction data to / from userspace. This lets us re-use a lot of QEMU code which used that interface to make logical guest memory accesses which are not possible anymore in protected mode anyway. Signed-off-by: Janosch Frank <[email protected]> Reviewed-by: Thomas Huth <[email protected]> Reviewed-by: David Hildenbrand <[email protected]> [[email protected]: patch merging, splitting, fixing] Signed-off-by: Christian Borntraeger <[email protected]>
1 parent c8aac23 commit 19e1227

File tree

4 files changed

+91
-11
lines changed

4 files changed

+91
-11
lines changed

arch/s390/include/asm/kvm_host.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ struct mcck_volatile_info {
127127
#define CR14_INITIAL_MASK (CR14_UNUSED_32 | CR14_UNUSED_33 | \
128128
CR14_EXTERNAL_DAMAGE_SUBMASK)
129129

130+
#define SIDAD_SIZE_MASK 0xff
131+
#define sida_origin(sie_block) \
132+
((sie_block)->sidad & PAGE_MASK)
133+
#define sida_size(sie_block) \
134+
((((sie_block)->sidad & SIDAD_SIZE_MASK) + 1) * PAGE_SIZE)
135+
130136
#define CPUSTAT_STOPPED 0x80000000
131137
#define CPUSTAT_WAIT 0x10000000
132138
#define CPUSTAT_ECALL_PEND 0x08000000
@@ -315,7 +321,10 @@ struct kvm_s390_sie_block {
315321
#define CRYCB_FORMAT2 0x00000003
316322
__u32 crycbd; /* 0x00fc */
317323
__u64 gcr[16]; /* 0x0100 */
318-
__u64 gbea; /* 0x0180 */
324+
union {
325+
__u64 gbea; /* 0x0180 */
326+
__u64 sidad;
327+
};
319328
__u8 reserved188[8]; /* 0x0188 */
320329
__u64 sdnxo; /* 0x0190 */
321330
__u8 reserved198[8]; /* 0x0198 */

arch/s390/kvm/kvm-s390.c

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4495,12 +4495,40 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
44954495
return r;
44964496
}
44974497

4498+
static long kvm_s390_guest_sida_op(struct kvm_vcpu *vcpu,
4499+
struct kvm_s390_mem_op *mop)
4500+
{
4501+
void __user *uaddr = (void __user *)mop->buf;
4502+
int r = 0;
4503+
4504+
if (mop->flags || !mop->size)
4505+
return -EINVAL;
4506+
if (mop->size + mop->sida_offset < mop->size)
4507+
return -EINVAL;
4508+
if (mop->size + mop->sida_offset > sida_size(vcpu->arch.sie_block))
4509+
return -E2BIG;
4510+
4511+
switch (mop->op) {
4512+
case KVM_S390_MEMOP_SIDA_READ:
4513+
if (copy_to_user(uaddr, (void *)(sida_origin(vcpu->arch.sie_block) +
4514+
mop->sida_offset), mop->size))
4515+
r = -EFAULT;
4516+
4517+
break;
4518+
case KVM_S390_MEMOP_SIDA_WRITE:
4519+
if (copy_from_user((void *)(sida_origin(vcpu->arch.sie_block) +
4520+
mop->sida_offset), uaddr, mop->size))
4521+
r = -EFAULT;
4522+
break;
4523+
}
4524+
return r;
4525+
}
44984526
static long kvm_s390_guest_mem_op(struct kvm_vcpu *vcpu,
44994527
struct kvm_s390_mem_op *mop)
45004528
{
45014529
void __user *uaddr = (void __user *)mop->buf;
45024530
void *tmpbuf = NULL;
4503-
int r, srcu_idx;
4531+
int r = 0;
45044532
const u64 supported_flags = KVM_S390_MEMOP_F_INJECT_EXCEPTION
45054533
| KVM_S390_MEMOP_F_CHECK_ONLY;
45064534

@@ -4510,14 +4538,15 @@ static long kvm_s390_guest_mem_op(struct kvm_vcpu *vcpu,
45104538
if (mop->size > MEM_OP_MAX_SIZE)
45114539
return -E2BIG;
45124540

4541+
if (kvm_s390_pv_cpu_is_protected(vcpu))
4542+
return -EINVAL;
4543+
45134544
if (!(mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY)) {
45144545
tmpbuf = vmalloc(mop->size);
45154546
if (!tmpbuf)
45164547
return -ENOMEM;
45174548
}
45184549

4519-
srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
4520-
45214550
switch (mop->op) {
45224551
case KVM_S390_MEMOP_LOGICAL_READ:
45234552
if (mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY) {
@@ -4543,19 +4572,40 @@ static long kvm_s390_guest_mem_op(struct kvm_vcpu *vcpu,
45434572
}
45444573
r = write_guest(vcpu, mop->gaddr, mop->ar, tmpbuf, mop->size);
45454574
break;
4546-
default:
4547-
r = -EINVAL;
45484575
}
45494576

4550-
srcu_read_unlock(&vcpu->kvm->srcu, srcu_idx);
4551-
45524577
if (r > 0 && (mop->flags & KVM_S390_MEMOP_F_INJECT_EXCEPTION) != 0)
45534578
kvm_s390_inject_prog_irq(vcpu, &vcpu->arch.pgm);
45544579

45554580
vfree(tmpbuf);
45564581
return r;
45574582
}
45584583

4584+
static long kvm_s390_guest_memsida_op(struct kvm_vcpu *vcpu,
4585+
struct kvm_s390_mem_op *mop)
4586+
{
4587+
int r, srcu_idx;
4588+
4589+
srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
4590+
4591+
switch (mop->op) {
4592+
case KVM_S390_MEMOP_LOGICAL_READ:
4593+
case KVM_S390_MEMOP_LOGICAL_WRITE:
4594+
r = kvm_s390_guest_mem_op(vcpu, mop);
4595+
break;
4596+
case KVM_S390_MEMOP_SIDA_READ:
4597+
case KVM_S390_MEMOP_SIDA_WRITE:
4598+
/* we are locked against sida going away by the vcpu->mutex */
4599+
r = kvm_s390_guest_sida_op(vcpu, mop);
4600+
break;
4601+
default:
4602+
r = -EINVAL;
4603+
}
4604+
4605+
srcu_read_unlock(&vcpu->kvm->srcu, srcu_idx);
4606+
return r;
4607+
}
4608+
45594609
long kvm_arch_vcpu_async_ioctl(struct file *filp,
45604610
unsigned int ioctl, unsigned long arg)
45614611
{
@@ -4686,7 +4736,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
46864736
struct kvm_s390_mem_op mem_op;
46874737

46884738
if (copy_from_user(&mem_op, argp, sizeof(mem_op)) == 0)
4689-
r = kvm_s390_guest_mem_op(vcpu, &mem_op);
4739+
r = kvm_s390_guest_memsida_op(vcpu, &mem_op);
46904740
else
46914741
r = -EFAULT;
46924742
break;

arch/s390/kvm/pv.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,18 @@ int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc)
3333
if (!cc)
3434
free_pages(vcpu->arch.pv.stor_base,
3535
get_order(uv_info.guest_cpu_stor_len));
36+
37+
free_page(sida_origin(vcpu->arch.sie_block));
3638
vcpu->arch.sie_block->pv_handle_cpu = 0;
3739
vcpu->arch.sie_block->pv_handle_config = 0;
3840
memset(&vcpu->arch.pv, 0, sizeof(vcpu->arch.pv));
3941
vcpu->arch.sie_block->sdf = 0;
42+
/*
43+
* The sidad field (for sdf == 2) is now the gbea field (for sdf == 0).
44+
* Use the reset value of gbea to avoid leaking the kernel pointer of
45+
* the just freed sida.
46+
*/
47+
vcpu->arch.sie_block->gbea = 1;
4048
kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu);
4149

4250
return cc ? EIO : 0;
@@ -64,6 +72,14 @@ int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc)
6472
uvcb.state_origin = (u64)vcpu->arch.sie_block;
6573
uvcb.stor_origin = (u64)vcpu->arch.pv.stor_base;
6674

75+
/* Alloc Secure Instruction Data Area Designation */
76+
vcpu->arch.sie_block->sidad = __get_free_page(GFP_KERNEL | __GFP_ZERO);
77+
if (!vcpu->arch.sie_block->sidad) {
78+
free_pages(vcpu->arch.pv.stor_base,
79+
get_order(uv_info.guest_cpu_stor_len));
80+
return -ENOMEM;
81+
}
82+
6783
cc = uv_call(0, (u64)&uvcb);
6884
*rc = uvcb.header.rc;
6985
*rrc = uvcb.header.rrc;

include/uapi/linux/kvm.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,12 +474,17 @@ struct kvm_s390_mem_op {
474474
__u32 size; /* amount of bytes */
475475
__u32 op; /* type of operation */
476476
__u64 buf; /* buffer in userspace */
477-
__u8 ar; /* the access register number */
478-
__u8 reserved[31]; /* should be set to 0 */
477+
union {
478+
__u8 ar; /* the access register number */
479+
__u32 sida_offset; /* offset into the sida */
480+
__u8 reserved[32]; /* should be set to 0 */
481+
};
479482
};
480483
/* types for kvm_s390_mem_op->op */
481484
#define KVM_S390_MEMOP_LOGICAL_READ 0
482485
#define KVM_S390_MEMOP_LOGICAL_WRITE 1
486+
#define KVM_S390_MEMOP_SIDA_READ 2
487+
#define KVM_S390_MEMOP_SIDA_WRITE 3
483488
/* flags for kvm_s390_mem_op->flags */
484489
#define KVM_S390_MEMOP_F_CHECK_ONLY (1ULL << 0)
485490
#define KVM_S390_MEMOP_F_INJECT_EXCEPTION (1ULL << 1)

0 commit comments

Comments
 (0)