diff options
author | Christian Borntraeger <borntraeger@de.ibm.com> | 2013-05-17 08:41:36 -0400 |
---|---|---|
committer | Gleb Natapov <gleb@redhat.com> | 2013-05-21 04:55:24 -0400 |
commit | 2c70fe4416d5f6d092b20ebf7d7654835e09c109 (patch) | |
tree | 077a740810c4ed97629efa5e6fc598823f2af503 /arch/s390 | |
parent | 49b99e1e0dedbd6cc93b2d2776b60fb7151ff3d7 (diff) |
s390/kvm: Kick guests out of sie if prefix page host pte is touched
The guest prefix pages must be mapped writeable all the time
while SIE is running, otherwise the guest might see random
behaviour. (pinned at the pte level) Turns out that mlocking is
not enough, the page table entry (not the page) might change or
become r/o. This patch uses the gmap notifiers to kick guest
cpus out of SIE.
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
Acked-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Gleb Natapov <gleb@redhat.com>
Diffstat (limited to 'arch/s390')
-rw-r--r-- | arch/s390/include/asm/pgtable.h | 1 | ||||
-rw-r--r-- | arch/s390/kvm/intercept.c | 39 | ||||
-rw-r--r-- | arch/s390/kvm/kvm-s390.c | 49 | ||||
-rw-r--r-- | arch/s390/kvm/kvm-s390.h | 1 |
4 files changed, 53 insertions, 37 deletions
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 1fc68d97be9d..1d0ad7d2d29a 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h | |||
@@ -739,6 +739,7 @@ struct gmap { | |||
739 | struct mm_struct *mm; | 739 | struct mm_struct *mm; |
740 | unsigned long *table; | 740 | unsigned long *table; |
741 | unsigned long asce; | 741 | unsigned long asce; |
742 | void *private; | ||
742 | struct list_head crst_list; | 743 | struct list_head crst_list; |
743 | }; | 744 | }; |
744 | 745 | ||
diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c index b7d1b2edeeb3..f0b8be0cc08d 100644 --- a/arch/s390/kvm/intercept.c +++ b/arch/s390/kvm/intercept.c | |||
@@ -174,47 +174,12 @@ static int handle_stop(struct kvm_vcpu *vcpu) | |||
174 | 174 | ||
175 | static int handle_validity(struct kvm_vcpu *vcpu) | 175 | static int handle_validity(struct kvm_vcpu *vcpu) |
176 | { | 176 | { |
177 | unsigned long vmaddr; | ||
178 | int viwhy = vcpu->arch.sie_block->ipb >> 16; | 177 | int viwhy = vcpu->arch.sie_block->ipb >> 16; |
179 | int rc; | ||
180 | 178 | ||
181 | vcpu->stat.exit_validity++; | 179 | vcpu->stat.exit_validity++; |
182 | trace_kvm_s390_intercept_validity(vcpu, viwhy); | 180 | trace_kvm_s390_intercept_validity(vcpu, viwhy); |
183 | if (viwhy == 0x37) { | 181 | WARN_ONCE(true, "kvm: unhandled validity intercept 0x%x\n", viwhy); |
184 | vmaddr = gmap_fault(vcpu->arch.sie_block->prefix, | 182 | return -EOPNOTSUPP; |
185 | vcpu->arch.gmap); | ||
186 | if (IS_ERR_VALUE(vmaddr)) { | ||
187 | rc = -EOPNOTSUPP; | ||
188 | goto out; | ||
189 | } | ||
190 | rc = fault_in_pages_writeable((char __user *) vmaddr, | ||
191 | PAGE_SIZE); | ||
192 | if (rc) { | ||
193 | /* user will receive sigsegv, exit to user */ | ||
194 | rc = -EOPNOTSUPP; | ||
195 | goto out; | ||
196 | } | ||
197 | vmaddr = gmap_fault(vcpu->arch.sie_block->prefix + PAGE_SIZE, | ||
198 | vcpu->arch.gmap); | ||
199 | if (IS_ERR_VALUE(vmaddr)) { | ||
200 | rc = -EOPNOTSUPP; | ||
201 | goto out; | ||
202 | } | ||
203 | rc = fault_in_pages_writeable((char __user *) vmaddr, | ||
204 | PAGE_SIZE); | ||
205 | if (rc) { | ||
206 | /* user will receive sigsegv, exit to user */ | ||
207 | rc = -EOPNOTSUPP; | ||
208 | goto out; | ||
209 | } | ||
210 | } else | ||
211 | rc = -EOPNOTSUPP; | ||
212 | |||
213 | out: | ||
214 | if (rc) | ||
215 | VCPU_EVENT(vcpu, 2, "unhandled validity intercept code %d", | ||
216 | viwhy); | ||
217 | return rc; | ||
218 | } | 183 | } |
219 | 184 | ||
220 | static int handle_instruction(struct kvm_vcpu *vcpu) | 185 | static int handle_instruction(struct kvm_vcpu *vcpu) |
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index ef4ef21f2c73..08227c1e816f 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c | |||
@@ -84,6 +84,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = { | |||
84 | }; | 84 | }; |
85 | 85 | ||
86 | static unsigned long long *facilities; | 86 | static unsigned long long *facilities; |
87 | static struct gmap_notifier gmap_notifier; | ||
87 | 88 | ||
88 | /* Section: not file related */ | 89 | /* Section: not file related */ |
89 | int kvm_arch_hardware_enable(void *garbage) | 90 | int kvm_arch_hardware_enable(void *garbage) |
@@ -96,13 +97,18 @@ void kvm_arch_hardware_disable(void *garbage) | |||
96 | { | 97 | { |
97 | } | 98 | } |
98 | 99 | ||
100 | static void kvm_gmap_notifier(struct gmap *gmap, unsigned long address); | ||
101 | |||
99 | int kvm_arch_hardware_setup(void) | 102 | int kvm_arch_hardware_setup(void) |
100 | { | 103 | { |
104 | gmap_notifier.notifier_call = kvm_gmap_notifier; | ||
105 | gmap_register_ipte_notifier(&gmap_notifier); | ||
101 | return 0; | 106 | return 0; |
102 | } | 107 | } |
103 | 108 | ||
104 | void kvm_arch_hardware_unsetup(void) | 109 | void kvm_arch_hardware_unsetup(void) |
105 | { | 110 | { |
111 | gmap_unregister_ipte_notifier(&gmap_notifier); | ||
106 | } | 112 | } |
107 | 113 | ||
108 | void kvm_arch_check_processor_compat(void *rtn) | 114 | void kvm_arch_check_processor_compat(void *rtn) |
@@ -239,6 +245,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) | |||
239 | kvm->arch.gmap = gmap_alloc(current->mm); | 245 | kvm->arch.gmap = gmap_alloc(current->mm); |
240 | if (!kvm->arch.gmap) | 246 | if (!kvm->arch.gmap) |
241 | goto out_nogmap; | 247 | goto out_nogmap; |
248 | kvm->arch.gmap->private = kvm; | ||
242 | } | 249 | } |
243 | 250 | ||
244 | kvm->arch.css_support = 0; | 251 | kvm->arch.css_support = 0; |
@@ -309,6 +316,7 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) | |||
309 | vcpu->arch.gmap = gmap_alloc(current->mm); | 316 | vcpu->arch.gmap = gmap_alloc(current->mm); |
310 | if (!vcpu->arch.gmap) | 317 | if (!vcpu->arch.gmap) |
311 | return -ENOMEM; | 318 | return -ENOMEM; |
319 | vcpu->arch.gmap->private = vcpu->kvm; | ||
312 | return 0; | 320 | return 0; |
313 | } | 321 | } |
314 | 322 | ||
@@ -482,6 +490,22 @@ void exit_sie_sync(struct kvm_vcpu *vcpu) | |||
482 | exit_sie(vcpu); | 490 | exit_sie(vcpu); |
483 | } | 491 | } |
484 | 492 | ||
493 | static void kvm_gmap_notifier(struct gmap *gmap, unsigned long address) | ||
494 | { | ||
495 | int i; | ||
496 | struct kvm *kvm = gmap->private; | ||
497 | struct kvm_vcpu *vcpu; | ||
498 | |||
499 | kvm_for_each_vcpu(i, vcpu, kvm) { | ||
500 | /* match against both prefix pages */ | ||
501 | if (vcpu->arch.sie_block->prefix == (address & ~0x1000UL)) { | ||
502 | VCPU_EVENT(vcpu, 2, "gmap notifier for %lx", address); | ||
503 | kvm_make_request(KVM_REQ_MMU_RELOAD, vcpu); | ||
504 | exit_sie_sync(vcpu); | ||
505 | } | ||
506 | } | ||
507 | } | ||
508 | |||
485 | int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) | 509 | int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) |
486 | { | 510 | { |
487 | /* kvm common code refers to this, but never calls it */ | 511 | /* kvm common code refers to this, but never calls it */ |
@@ -634,6 +658,27 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, | |||
634 | return -EINVAL; /* not implemented yet */ | 658 | return -EINVAL; /* not implemented yet */ |
635 | } | 659 | } |
636 | 660 | ||
661 | static int kvm_s390_handle_requests(struct kvm_vcpu *vcpu) | ||
662 | { | ||
663 | /* | ||
664 | * We use MMU_RELOAD just to re-arm the ipte notifier for the | ||
665 | * guest prefix page. gmap_ipte_notify will wait on the ptl lock. | ||
666 | * This ensures that the ipte instruction for this request has | ||
667 | * already finished. We might race against a second unmapper that | ||
668 | * wants to set the blocking bit. Lets just retry the request loop. | ||
669 | */ | ||
670 | while (kvm_check_request(KVM_REQ_MMU_RELOAD, vcpu)) { | ||
671 | int rc; | ||
672 | rc = gmap_ipte_notify(vcpu->arch.gmap, | ||
673 | vcpu->arch.sie_block->prefix, | ||
674 | PAGE_SIZE * 2); | ||
675 | if (rc) | ||
676 | return rc; | ||
677 | s390_vcpu_unblock(vcpu); | ||
678 | } | ||
679 | return 0; | ||
680 | } | ||
681 | |||
637 | static int __vcpu_run(struct kvm_vcpu *vcpu) | 682 | static int __vcpu_run(struct kvm_vcpu *vcpu) |
638 | { | 683 | { |
639 | int rc; | 684 | int rc; |
@@ -649,6 +694,10 @@ static int __vcpu_run(struct kvm_vcpu *vcpu) | |||
649 | if (!kvm_is_ucontrol(vcpu->kvm)) | 694 | if (!kvm_is_ucontrol(vcpu->kvm)) |
650 | kvm_s390_deliver_pending_interrupts(vcpu); | 695 | kvm_s390_deliver_pending_interrupts(vcpu); |
651 | 696 | ||
697 | rc = kvm_s390_handle_requests(vcpu); | ||
698 | if (rc) | ||
699 | return rc; | ||
700 | |||
652 | vcpu->arch.sie_block->icptcode = 0; | 701 | vcpu->arch.sie_block->icptcode = 0; |
653 | preempt_disable(); | 702 | preempt_disable(); |
654 | kvm_guest_enter(); | 703 | kvm_guest_enter(); |
diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index 7a8abfd26a0f..269b523d0f6e 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h | |||
@@ -63,6 +63,7 @@ static inline void kvm_s390_set_prefix(struct kvm_vcpu *vcpu, u32 prefix) | |||
63 | { | 63 | { |
64 | vcpu->arch.sie_block->prefix = prefix & 0x7fffe000u; | 64 | vcpu->arch.sie_block->prefix = prefix & 0x7fffe000u; |
65 | vcpu->arch.sie_block->ihcpu = 0xffff; | 65 | vcpu->arch.sie_block->ihcpu = 0xffff; |
66 | kvm_make_request(KVM_REQ_MMU_RELOAD, vcpu); | ||
66 | } | 67 | } |
67 | 68 | ||
68 | static inline u64 kvm_s390_get_base_disp_s(struct kvm_vcpu *vcpu) | 69 | static inline u64 kvm_s390_get_base_disp_s(struct kvm_vcpu *vcpu) |