|
22 | 22 | #include "dat.h" |
23 | 23 | #include "gmap.h" |
24 | 24 | #include "kvm-s390.h" |
| 25 | +#include "faultin.h" |
25 | 26 |
|
26 | 27 | static inline bool kvm_s390_is_in_sie(struct kvm_vcpu *vcpu) |
27 | 28 | { |
@@ -1091,10 +1092,79 @@ static struct gmap *gmap_find_shadow(struct gmap *parent, union asce asce, int e |
1091 | 1092 | return NULL; |
1092 | 1093 | } |
1093 | 1094 |
|
| 1095 | +#define CRST_TABLE_PAGES (_CRST_TABLE_SIZE / PAGE_SIZE) |
| 1096 | +struct gmap_protect_asce_top_level { |
| 1097 | + unsigned long seq; |
| 1098 | + struct guest_fault f[CRST_TABLE_PAGES]; |
| 1099 | +}; |
| 1100 | + |
| 1101 | +static inline int __gmap_protect_asce_top_level(struct kvm_s390_mmu_cache *mc, struct gmap *sg, |
| 1102 | + struct gmap_protect_asce_top_level *context) |
| 1103 | +{ |
| 1104 | + int rc, i; |
| 1105 | + |
| 1106 | + guard(write_lock)(&sg->kvm->mmu_lock); |
| 1107 | + |
| 1108 | + if (kvm_s390_array_needs_retry_safe(sg->kvm, context->seq, context->f)) |
| 1109 | + return -EAGAIN; |
| 1110 | + |
| 1111 | + scoped_guard(spinlock, &sg->parent->children_lock) { |
| 1112 | + for (i = 0; i < CRST_TABLE_PAGES; i++) { |
| 1113 | + if (!context->f[i].valid) |
| 1114 | + continue; |
| 1115 | + rc = gmap_protect_rmap(mc, sg, context->f[i].gfn, 0, context->f[i].pfn, |
| 1116 | + TABLE_TYPE_REGION1 + 1, context->f[i].writable); |
| 1117 | + if (rc) |
| 1118 | + return rc; |
| 1119 | + } |
| 1120 | + gmap_add_child(sg->parent, sg); |
| 1121 | + } |
| 1122 | + |
| 1123 | + kvm_s390_release_faultin_array(sg->kvm, context->f, false); |
| 1124 | + return 0; |
| 1125 | +} |
| 1126 | + |
| 1127 | +static inline int _gmap_protect_asce_top_level(struct kvm_s390_mmu_cache *mc, struct gmap *sg, |
| 1128 | + struct gmap_protect_asce_top_level *context) |
| 1129 | +{ |
| 1130 | + int rc; |
| 1131 | + |
| 1132 | + if (kvm_s390_array_needs_retry_unsafe(sg->kvm, context->seq, context->f)) |
| 1133 | + return -EAGAIN; |
| 1134 | + do { |
| 1135 | + rc = kvm_s390_mmu_cache_topup(mc); |
| 1136 | + if (rc) |
| 1137 | + return rc; |
| 1138 | + rc = radix_tree_preload(GFP_KERNEL); |
| 1139 | + if (rc) |
| 1140 | + return rc; |
| 1141 | + rc = __gmap_protect_asce_top_level(mc, sg, context); |
| 1142 | + radix_tree_preload_end(); |
| 1143 | + } while (rc == -ENOMEM); |
| 1144 | + |
| 1145 | + return rc; |
| 1146 | +} |
| 1147 | + |
1094 | 1148 | static int gmap_protect_asce_top_level(struct kvm_s390_mmu_cache *mc, struct gmap *sg) |
1095 | 1149 | { |
1096 | | - KVM_BUG_ON(1, sg->kvm); |
1097 | | - return -EINVAL; |
| 1150 | + struct gmap_protect_asce_top_level context = {}; |
| 1151 | + union asce asce = sg->guest_asce; |
| 1152 | + int rc; |
| 1153 | + |
| 1154 | + KVM_BUG_ON(!is_shadow(sg), sg->kvm); |
| 1155 | + |
| 1156 | + context.seq = sg->kvm->mmu_invalidate_seq; |
| 1157 | + /* Pairs with the smp_wmb() in kvm_mmu_invalidate_end(). */ |
| 1158 | + smp_rmb(); |
| 1159 | + |
| 1160 | + rc = kvm_s390_get_guest_pages(sg->kvm, context.f, asce.rsto, asce.dt + 1, false); |
| 1161 | + if (rc > 0) |
| 1162 | + rc = -EFAULT; |
| 1163 | + if (!rc) |
| 1164 | + rc = _gmap_protect_asce_top_level(mc, sg, &context); |
| 1165 | + if (rc) |
| 1166 | + kvm_s390_release_faultin_array(sg->kvm, context.f, true); |
| 1167 | + return rc; |
1098 | 1168 | } |
1099 | 1169 |
|
1100 | 1170 | /** |
|
0 commit comments