diff options
author | David Hildenbrand <dahi@linux.vnet.ibm.com> | 2016-05-10 03:50:21 -0400 |
---|---|---|
committer | Christian Borntraeger <borntraeger@de.ibm.com> | 2016-06-10 06:07:31 -0400 |
commit | a7e19ab55ffdd82f1a8d12694b9a0c0beeef534c (patch) | |
tree | 77c52e6a73a3a871caaba0a0e7b7eda3789fa53a /arch/s390/kvm | |
parent | 11ddcd41bce5c2394b0390584236afdd13656998 (diff) |
KVM: s390: handle missing storage-key facility
Without the storage-key facility, SIE won't interpret SSKE, ISKE and
RRBE for us. So let's add proper interception handlers that will be called
if lazy sske cannot be enabled.
Reviewed-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: David Hildenbrand <dahi@linux.vnet.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
Diffstat (limited to 'arch/s390/kvm')
-rw-r--r-- | arch/s390/kvm/priv.c | 150 |
1 files changed, 142 insertions, 8 deletions
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index 6745c2a602c3..3db3be139992 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <asm/io.h> | 27 | #include <asm/io.h> |
28 | #include <asm/ptrace.h> | 28 | #include <asm/ptrace.h> |
29 | #include <asm/compat.h> | 29 | #include <asm/compat.h> |
30 | #include <asm/sclp.h> | ||
30 | #include "gaccess.h" | 31 | #include "gaccess.h" |
31 | #include "kvm-s390.h" | 32 | #include "kvm-s390.h" |
32 | #include "trace.h" | 33 | #include "trace.h" |
@@ -164,8 +165,7 @@ static int __skey_check_enable(struct kvm_vcpu *vcpu) | |||
164 | return rc; | 165 | return rc; |
165 | } | 166 | } |
166 | 167 | ||
167 | 168 | static int try_handle_skey(struct kvm_vcpu *vcpu) | |
168 | static int handle_skey(struct kvm_vcpu *vcpu) | ||
169 | { | 169 | { |
170 | int rc; | 170 | int rc; |
171 | 171 | ||
@@ -173,12 +173,146 @@ static int handle_skey(struct kvm_vcpu *vcpu) | |||
173 | rc = __skey_check_enable(vcpu); | 173 | rc = __skey_check_enable(vcpu); |
174 | if (rc) | 174 | if (rc) |
175 | return rc; | 175 | return rc; |
176 | 176 | if (sclp.has_skey) { | |
177 | /* with storage-key facility, SIE interprets it for us */ | ||
178 | kvm_s390_retry_instr(vcpu); | ||
179 | VCPU_EVENT(vcpu, 4, "%s", "retrying storage key operation"); | ||
180 | return -EAGAIN; | ||
181 | } | ||
177 | if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) | 182 | if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) |
178 | return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); | 183 | return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); |
184 | return 0; | ||
185 | } | ||
179 | 186 | ||
180 | kvm_s390_retry_instr(vcpu); | 187 | static int handle_iske(struct kvm_vcpu *vcpu) |
181 | VCPU_EVENT(vcpu, 4, "%s", "retrying storage key operation"); | 188 | { |
189 | unsigned long addr; | ||
190 | unsigned char key; | ||
191 | int reg1, reg2; | ||
192 | int rc; | ||
193 | |||
194 | rc = try_handle_skey(vcpu); | ||
195 | if (rc) | ||
196 | return rc != -EAGAIN ? rc : 0; | ||
197 | |||
198 | kvm_s390_get_regs_rre(vcpu, ®1, ®2); | ||
199 | |||
200 | addr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK; | ||
201 | addr = kvm_s390_logical_to_effective(vcpu, addr); | ||
202 | addr = kvm_s390_real_to_abs(vcpu, addr); | ||
203 | addr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(addr)); | ||
204 | if (kvm_is_error_hva(addr)) | ||
205 | return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); | ||
206 | |||
207 | down_read(¤t->mm->mmap_sem); | ||
208 | rc = get_guest_storage_key(current->mm, addr, &key); | ||
209 | up_read(¤t->mm->mmap_sem); | ||
210 | if (rc) | ||
211 | return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); | ||
212 | vcpu->run->s.regs.gprs[reg1] &= ~0xff; | ||
213 | vcpu->run->s.regs.gprs[reg1] |= key; | ||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | static int handle_rrbe(struct kvm_vcpu *vcpu) | ||
218 | { | ||
219 | unsigned long addr; | ||
220 | int reg1, reg2; | ||
221 | int rc; | ||
222 | |||
223 | rc = try_handle_skey(vcpu); | ||
224 | if (rc) | ||
225 | return rc != -EAGAIN ? rc : 0; | ||
226 | |||
227 | kvm_s390_get_regs_rre(vcpu, ®1, ®2); | ||
228 | |||
229 | addr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK; | ||
230 | addr = kvm_s390_logical_to_effective(vcpu, addr); | ||
231 | addr = kvm_s390_real_to_abs(vcpu, addr); | ||
232 | addr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(addr)); | ||
233 | if (kvm_is_error_hva(addr)) | ||
234 | return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); | ||
235 | |||
236 | down_read(¤t->mm->mmap_sem); | ||
237 | rc = reset_guest_reference_bit(current->mm, addr); | ||
238 | up_read(¤t->mm->mmap_sem); | ||
239 | if (rc < 0) | ||
240 | return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); | ||
241 | |||
242 | kvm_s390_set_psw_cc(vcpu, rc); | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | #define SSKE_NQ 0x8 | ||
247 | #define SSKE_MR 0x4 | ||
248 | #define SSKE_MC 0x2 | ||
249 | #define SSKE_MB 0x1 | ||
250 | static int handle_sske(struct kvm_vcpu *vcpu) | ||
251 | { | ||
252 | unsigned char m3 = vcpu->arch.sie_block->ipb >> 28; | ||
253 | unsigned long start, end; | ||
254 | unsigned char key, oldkey; | ||
255 | int reg1, reg2; | ||
256 | int rc; | ||
257 | |||
258 | rc = try_handle_skey(vcpu); | ||
259 | if (rc) | ||
260 | return rc != -EAGAIN ? rc : 0; | ||
261 | |||
262 | if (!test_kvm_facility(vcpu->kvm, 8)) | ||
263 | m3 &= ~SSKE_MB; | ||
264 | if (!test_kvm_facility(vcpu->kvm, 10)) | ||
265 | m3 &= ~(SSKE_MC | SSKE_MR); | ||
266 | if (!test_kvm_facility(vcpu->kvm, 14)) | ||
267 | m3 &= ~SSKE_NQ; | ||
268 | |||
269 | kvm_s390_get_regs_rre(vcpu, ®1, ®2); | ||
270 | |||
271 | key = vcpu->run->s.regs.gprs[reg1] & 0xfe; | ||
272 | start = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK; | ||
273 | start = kvm_s390_logical_to_effective(vcpu, start); | ||
274 | if (m3 & SSKE_MB) { | ||
275 | /* start already designates an absolute address */ | ||
276 | end = (start + (1UL << 20)) & ~((1UL << 20) - 1); | ||
277 | } else { | ||
278 | start = kvm_s390_real_to_abs(vcpu, start); | ||
279 | end = start + PAGE_SIZE; | ||
280 | } | ||
281 | |||
282 | while (start != end) { | ||
283 | unsigned long addr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(start)); | ||
284 | |||
285 | if (kvm_is_error_hva(addr)) | ||
286 | return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); | ||
287 | |||
288 | down_read(¤t->mm->mmap_sem); | ||
289 | rc = cond_set_guest_storage_key(current->mm, addr, key, &oldkey, | ||
290 | m3 & SSKE_NQ, m3 & SSKE_MR, | ||
291 | m3 & SSKE_MC); | ||
292 | up_read(¤t->mm->mmap_sem); | ||
293 | if (rc < 0) | ||
294 | return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); | ||
295 | start += PAGE_SIZE; | ||
296 | }; | ||
297 | |||
298 | if (m3 & (SSKE_MC | SSKE_MR)) { | ||
299 | if (m3 & SSKE_MB) { | ||
300 | /* skey in reg1 is unpredictable */ | ||
301 | kvm_s390_set_psw_cc(vcpu, 3); | ||
302 | } else { | ||
303 | kvm_s390_set_psw_cc(vcpu, rc); | ||
304 | vcpu->run->s.regs.gprs[reg1] &= ~0xff00UL; | ||
305 | vcpu->run->s.regs.gprs[reg1] |= (u64) oldkey << 8; | ||
306 | } | ||
307 | } | ||
308 | if (m3 & SSKE_MB) { | ||
309 | if (psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_AMODE_64BIT) | ||
310 | vcpu->run->s.regs.gprs[reg2] &= ~PAGE_MASK; | ||
311 | else | ||
312 | vcpu->run->s.regs.gprs[reg2] &= ~0xfffff000UL; | ||
313 | end = kvm_s390_logical_to_effective(vcpu, end); | ||
314 | vcpu->run->s.regs.gprs[reg2] |= end; | ||
315 | } | ||
182 | return 0; | 316 | return 0; |
183 | } | 317 | } |
184 | 318 | ||
@@ -586,9 +720,9 @@ static const intercept_handler_t b2_handlers[256] = { | |||
586 | [0x11] = handle_store_prefix, | 720 | [0x11] = handle_store_prefix, |
587 | [0x12] = handle_store_cpu_address, | 721 | [0x12] = handle_store_cpu_address, |
588 | [0x21] = handle_ipte_interlock, | 722 | [0x21] = handle_ipte_interlock, |
589 | [0x29] = handle_skey, | 723 | [0x29] = handle_iske, |
590 | [0x2a] = handle_skey, | 724 | [0x2a] = handle_rrbe, |
591 | [0x2b] = handle_skey, | 725 | [0x2b] = handle_sske, |
592 | [0x2c] = handle_test_block, | 726 | [0x2c] = handle_test_block, |
593 | [0x30] = handle_io_inst, | 727 | [0x30] = handle_io_inst, |
594 | [0x31] = handle_io_inst, | 728 | [0x31] = handle_io_inst, |