diff options
Diffstat (limited to 'arch/s390/kvm/priv.c')
-rw-r--r-- | arch/s390/kvm/priv.c | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index 59200ee275e5..2440602e6df1 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c | |||
@@ -30,6 +30,38 @@ | |||
30 | #include "kvm-s390.h" | 30 | #include "kvm-s390.h" |
31 | #include "trace.h" | 31 | #include "trace.h" |
32 | 32 | ||
33 | /* Handle SCK (SET CLOCK) interception */ | ||
34 | static int handle_set_clock(struct kvm_vcpu *vcpu) | ||
35 | { | ||
36 | struct kvm_vcpu *cpup; | ||
37 | s64 hostclk, val; | ||
38 | u64 op2; | ||
39 | int i; | ||
40 | |||
41 | if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) | ||
42 | return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); | ||
43 | |||
44 | op2 = kvm_s390_get_base_disp_s(vcpu); | ||
45 | if (op2 & 7) /* Operand must be on a doubleword boundary */ | ||
46 | return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); | ||
47 | if (get_guest(vcpu, val, (u64 __user *) op2)) | ||
48 | return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); | ||
49 | |||
50 | if (store_tod_clock(&hostclk)) { | ||
51 | kvm_s390_set_psw_cc(vcpu, 3); | ||
52 | return 0; | ||
53 | } | ||
54 | val = (val - hostclk) & ~0x3fUL; | ||
55 | |||
56 | mutex_lock(&vcpu->kvm->lock); | ||
57 | kvm_for_each_vcpu(i, cpup, vcpu->kvm) | ||
58 | cpup->arch.sie_block->epoch = val; | ||
59 | mutex_unlock(&vcpu->kvm->lock); | ||
60 | |||
61 | kvm_s390_set_psw_cc(vcpu, 0); | ||
62 | return 0; | ||
63 | } | ||
64 | |||
33 | static int handle_set_prefix(struct kvm_vcpu *vcpu) | 65 | static int handle_set_prefix(struct kvm_vcpu *vcpu) |
34 | { | 66 | { |
35 | u64 operand2; | 67 | u64 operand2; |
@@ -128,6 +160,33 @@ static int handle_skey(struct kvm_vcpu *vcpu) | |||
128 | return 0; | 160 | return 0; |
129 | } | 161 | } |
130 | 162 | ||
163 | static int handle_test_block(struct kvm_vcpu *vcpu) | ||
164 | { | ||
165 | unsigned long hva; | ||
166 | gpa_t addr; | ||
167 | int reg2; | ||
168 | |||
169 | if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) | ||
170 | return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); | ||
171 | |||
172 | kvm_s390_get_regs_rre(vcpu, NULL, ®2); | ||
173 | addr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK; | ||
174 | addr = kvm_s390_real_to_abs(vcpu, addr); | ||
175 | |||
176 | hva = gfn_to_hva(vcpu->kvm, gpa_to_gfn(addr)); | ||
177 | if (kvm_is_error_hva(hva)) | ||
178 | return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); | ||
179 | /* | ||
180 | * We don't expect errors on modern systems, and do not care | ||
181 | * about storage keys (yet), so let's just clear the page. | ||
182 | */ | ||
183 | if (clear_user((void __user *)hva, PAGE_SIZE) != 0) | ||
184 | return -EFAULT; | ||
185 | kvm_s390_set_psw_cc(vcpu, 0); | ||
186 | vcpu->run->s.regs.gprs[0] = 0; | ||
187 | return 0; | ||
188 | } | ||
189 | |||
131 | static int handle_tpi(struct kvm_vcpu *vcpu) | 190 | static int handle_tpi(struct kvm_vcpu *vcpu) |
132 | { | 191 | { |
133 | struct kvm_s390_interrupt_info *inti; | 192 | struct kvm_s390_interrupt_info *inti; |
@@ -438,12 +497,14 @@ out_exception: | |||
438 | 497 | ||
439 | static const intercept_handler_t b2_handlers[256] = { | 498 | static const intercept_handler_t b2_handlers[256] = { |
440 | [0x02] = handle_stidp, | 499 | [0x02] = handle_stidp, |
500 | [0x04] = handle_set_clock, | ||
441 | [0x10] = handle_set_prefix, | 501 | [0x10] = handle_set_prefix, |
442 | [0x11] = handle_store_prefix, | 502 | [0x11] = handle_store_prefix, |
443 | [0x12] = handle_store_cpu_address, | 503 | [0x12] = handle_store_cpu_address, |
444 | [0x29] = handle_skey, | 504 | [0x29] = handle_skey, |
445 | [0x2a] = handle_skey, | 505 | [0x2a] = handle_skey, |
446 | [0x2b] = handle_skey, | 506 | [0x2b] = handle_skey, |
507 | [0x2c] = handle_test_block, | ||
447 | [0x30] = handle_io_inst, | 508 | [0x30] = handle_io_inst, |
448 | [0x31] = handle_io_inst, | 509 | [0x31] = handle_io_inst, |
449 | [0x32] = handle_io_inst, | 510 | [0x32] = handle_io_inst, |