aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390
diff options
context:
space:
mode:
authorChristian Borntraeger <borntraeger@de.ibm.com>2013-05-17 08:41:36 -0400
committerGleb Natapov <gleb@redhat.com>2013-05-21 04:55:24 -0400
commit2c70fe4416d5f6d092b20ebf7d7654835e09c109 (patch)
tree077a740810c4ed97629efa5e6fc598823f2af503 /arch/s390
parent49b99e1e0dedbd6cc93b2d2776b60fb7151ff3d7 (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.h1
-rw-r--r--arch/s390/kvm/intercept.c39
-rw-r--r--arch/s390/kvm/kvm-s390.c49
-rw-r--r--arch/s390/kvm/kvm-s390.h1
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
175static int handle_validity(struct kvm_vcpu *vcpu) 175static 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
213out:
214 if (rc)
215 VCPU_EVENT(vcpu, 2, "unhandled validity intercept code %d",
216 viwhy);
217 return rc;
218} 183}
219 184
220static int handle_instruction(struct kvm_vcpu *vcpu) 185static 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
86static unsigned long long *facilities; 86static unsigned long long *facilities;
87static struct gmap_notifier gmap_notifier;
87 88
88/* Section: not file related */ 89/* Section: not file related */
89int kvm_arch_hardware_enable(void *garbage) 90int kvm_arch_hardware_enable(void *garbage)
@@ -96,13 +97,18 @@ void kvm_arch_hardware_disable(void *garbage)
96{ 97{
97} 98}
98 99
100static void kvm_gmap_notifier(struct gmap *gmap, unsigned long address);
101
99int kvm_arch_hardware_setup(void) 102int 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
104void kvm_arch_hardware_unsetup(void) 109void kvm_arch_hardware_unsetup(void)
105{ 110{
111 gmap_unregister_ipte_notifier(&gmap_notifier);
106} 112}
107 113
108void kvm_arch_check_processor_compat(void *rtn) 114void 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
493static 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
485int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) 509int 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
661static 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
637static int __vcpu_run(struct kvm_vcpu *vcpu) 682static 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
68static inline u64 kvm_s390_get_base_disp_s(struct kvm_vcpu *vcpu) 69static inline u64 kvm_s390_get_base_disp_s(struct kvm_vcpu *vcpu)