Skip to content

Commit bd096f6

Browse files
committed
KVM: s390: Add skey emulation fault handling
When doing skey emulation for huge guests, we now need to fault in pmds, as we don't have PGSTES anymore to store them when we do not have valid table entries. Signed-off-by: Janosch Frank <frankja@linux.ibm.com>
1 parent 637ff9e commit bd096f6

File tree

2 files changed

+83
-37
lines changed

2 files changed

+83
-37
lines changed

arch/s390/kvm/kvm-s390.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,6 +1551,7 @@ static long kvm_s390_set_skeys(struct kvm *kvm, struct kvm_s390_skeys *args)
15511551
uint8_t *keys;
15521552
uint64_t hva;
15531553
int srcu_idx, i, r = 0;
1554+
bool unlocked;
15541555

15551556
if (args->flags != 0)
15561557
return -EINVAL;
@@ -1575,9 +1576,11 @@ static long kvm_s390_set_skeys(struct kvm *kvm, struct kvm_s390_skeys *args)
15751576
if (r)
15761577
goto out;
15771578

1579+
i = 0;
15781580
down_read(&current->mm->mmap_sem);
15791581
srcu_idx = srcu_read_lock(&kvm->srcu);
1580-
for (i = 0; i < args->count; i++) {
1582+
while (i < args->count) {
1583+
unlocked = false;
15811584
hva = gfn_to_hva(kvm, args->start_gfn + i);
15821585
if (kvm_is_error_hva(hva)) {
15831586
r = -EFAULT;
@@ -1591,8 +1594,14 @@ static long kvm_s390_set_skeys(struct kvm *kvm, struct kvm_s390_skeys *args)
15911594
}
15921595

15931596
r = set_guest_storage_key(current->mm, hva, keys[i], 0);
1594-
if (r)
1595-
break;
1597+
if (r) {
1598+
r = fixup_user_fault(current, current->mm, hva,
1599+
FAULT_FLAG_WRITE, &unlocked);
1600+
if (r)
1601+
break;
1602+
}
1603+
if (!r)
1604+
i++;
15961605
}
15971606
srcu_read_unlock(&kvm->srcu, srcu_idx);
15981607
up_read(&current->mm->mmap_sem);

arch/s390/kvm/priv.c

Lines changed: 71 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -246,9 +246,10 @@ static int try_handle_skey(struct kvm_vcpu *vcpu)
246246

247247
static int handle_iske(struct kvm_vcpu *vcpu)
248248
{
249-
unsigned long addr;
249+
unsigned long gaddr, vmaddr;
250250
unsigned char key;
251251
int reg1, reg2;
252+
bool unlocked;
252253
int rc;
253254

254255
vcpu->stat.instruction_iske++;
@@ -262,27 +263,38 @@ static int handle_iske(struct kvm_vcpu *vcpu)
262263

263264
kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
264265

265-
addr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
266-
addr = kvm_s390_logical_to_effective(vcpu, addr);
267-
addr = kvm_s390_real_to_abs(vcpu, addr);
268-
addr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(addr));
269-
if (kvm_is_error_hva(addr))
266+
gaddr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
267+
gaddr = kvm_s390_logical_to_effective(vcpu, gaddr);
268+
gaddr = kvm_s390_real_to_abs(vcpu, gaddr);
269+
vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(gaddr));
270+
if (kvm_is_error_hva(vmaddr))
270271
return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
271-
272+
retry:
273+
unlocked = false;
272274
down_read(&current->mm->mmap_sem);
273-
rc = get_guest_storage_key(current->mm, addr, &key);
274-
up_read(&current->mm->mmap_sem);
275+
rc = get_guest_storage_key(current->mm, vmaddr, &key);
276+
277+
if (rc) {
278+
rc = fixup_user_fault(current, current->mm, vmaddr,
279+
FAULT_FLAG_WRITE, &unlocked);
280+
if (!rc) {
281+
up_read(&current->mm->mmap_sem);
282+
goto retry;
283+
}
284+
}
275285
if (rc)
276286
return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
287+
up_read(&current->mm->mmap_sem);
277288
vcpu->run->s.regs.gprs[reg1] &= ~0xff;
278289
vcpu->run->s.regs.gprs[reg1] |= key;
279290
return 0;
280291
}
281292

282293
static int handle_rrbe(struct kvm_vcpu *vcpu)
283294
{
284-
unsigned long addr;
295+
unsigned long vmaddr, gaddr;
285296
int reg1, reg2;
297+
bool unlocked;
286298
int rc;
287299

288300
vcpu->stat.instruction_rrbe++;
@@ -296,19 +308,27 @@ static int handle_rrbe(struct kvm_vcpu *vcpu)
296308

297309
kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
298310

299-
addr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
300-
addr = kvm_s390_logical_to_effective(vcpu, addr);
301-
addr = kvm_s390_real_to_abs(vcpu, addr);
302-
addr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(addr));
303-
if (kvm_is_error_hva(addr))
311+
gaddr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
312+
gaddr = kvm_s390_logical_to_effective(vcpu, gaddr);
313+
gaddr = kvm_s390_real_to_abs(vcpu, gaddr);
314+
vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(gaddr));
315+
if (kvm_is_error_hva(vmaddr))
304316
return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
305-
317+
retry:
318+
unlocked = false;
306319
down_read(&current->mm->mmap_sem);
307-
rc = reset_guest_reference_bit(current->mm, addr);
308-
up_read(&current->mm->mmap_sem);
320+
rc = reset_guest_reference_bit(current->mm, vmaddr);
321+
if (rc < 0) {
322+
rc = fixup_user_fault(current, current->mm, vmaddr,
323+
FAULT_FLAG_WRITE, &unlocked);
324+
if (!rc) {
325+
up_read(&current->mm->mmap_sem);
326+
goto retry;
327+
}
328+
}
309329
if (rc < 0)
310330
return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
311-
331+
up_read(&current->mm->mmap_sem);
312332
kvm_s390_set_psw_cc(vcpu, rc);
313333
return 0;
314334
}
@@ -323,6 +343,7 @@ static int handle_sske(struct kvm_vcpu *vcpu)
323343
unsigned long start, end;
324344
unsigned char key, oldkey;
325345
int reg1, reg2;
346+
bool unlocked;
326347
int rc;
327348

328349
vcpu->stat.instruction_sske++;
@@ -355,19 +376,28 @@ static int handle_sske(struct kvm_vcpu *vcpu)
355376
}
356377

357378
while (start != end) {
358-
unsigned long addr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(start));
379+
unsigned long vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(start));
380+
unlocked = false;
359381

360-
if (kvm_is_error_hva(addr))
382+
if (kvm_is_error_hva(vmaddr))
361383
return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
362384

363385
down_read(&current->mm->mmap_sem);
364-
rc = cond_set_guest_storage_key(current->mm, addr, key, &oldkey,
386+
rc = cond_set_guest_storage_key(current->mm, vmaddr, key, &oldkey,
365387
m3 & SSKE_NQ, m3 & SSKE_MR,
366388
m3 & SSKE_MC);
367-
up_read(&current->mm->mmap_sem);
368-
if (rc < 0)
389+
390+
if (rc < 0) {
391+
rc = fixup_user_fault(current, current->mm, vmaddr,
392+
FAULT_FLAG_WRITE, &unlocked);
393+
rc = !rc ? -EAGAIN : rc;
394+
}
395+
if (rc == -EFAULT)
369396
return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
370-
start += PAGE_SIZE;
397+
398+
up_read(&current->mm->mmap_sem);
399+
if (rc >= 0)
400+
start += PAGE_SIZE;
371401
}
372402

373403
if (m3 & (SSKE_MC | SSKE_MR)) {
@@ -948,15 +978,16 @@ static int handle_pfmf(struct kvm_vcpu *vcpu)
948978
}
949979

950980
while (start != end) {
951-
unsigned long useraddr;
981+
unsigned long vmaddr;
982+
bool unlocked = false;
952983

953984
/* Translate guest address to host address */
954-
useraddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(start));
955-
if (kvm_is_error_hva(useraddr))
985+
vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(start));
986+
if (kvm_is_error_hva(vmaddr))
956987
return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
957988

958989
if (vcpu->run->s.regs.gprs[reg1] & PFMF_CF) {
959-
if (clear_user((void __user *)useraddr, PAGE_SIZE))
990+
if (clear_user((void __user *)vmaddr, PAGE_SIZE))
960991
return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
961992
}
962993

@@ -966,14 +997,20 @@ static int handle_pfmf(struct kvm_vcpu *vcpu)
966997
if (rc)
967998
return rc;
968999
down_read(&current->mm->mmap_sem);
969-
rc = cond_set_guest_storage_key(current->mm, useraddr,
1000+
rc = cond_set_guest_storage_key(current->mm, vmaddr,
9701001
key, NULL, nq, mr, mc);
971-
up_read(&current->mm->mmap_sem);
972-
if (rc < 0)
1002+
if (rc < 0) {
1003+
rc = fixup_user_fault(current, current->mm, vmaddr,
1004+
FAULT_FLAG_WRITE, &unlocked);
1005+
rc = !rc ? -EAGAIN : rc;
1006+
}
1007+
if (rc == -EFAULT)
9731008
return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
974-
}
9751009

976-
start += PAGE_SIZE;
1010+
up_read(&current->mm->mmap_sem);
1011+
if (rc >= 0)
1012+
start += PAGE_SIZE;
1013+
}
9771014
}
9781015
if (vcpu->run->s.regs.gprs[reg1] & PFMF_FSC) {
9791016
if (psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_BITS_AMODE_64BIT) {

0 commit comments

Comments
 (0)