diff options
Diffstat (limited to 'drivers/kvm/mmu.c')
| -rw-r--r-- | drivers/kvm/mmu.c | 53 |
1 files changed, 38 insertions, 15 deletions
diff --git a/drivers/kvm/mmu.c b/drivers/kvm/mmu.c index a1a93368f314..cab26f301eab 100644 --- a/drivers/kvm/mmu.c +++ b/drivers/kvm/mmu.c | |||
| @@ -131,7 +131,7 @@ static int dbg = 1; | |||
| 131 | (((address) >> PT32_LEVEL_SHIFT(level)) & ((1 << PT32_LEVEL_BITS) - 1)) | 131 | (((address) >> PT32_LEVEL_SHIFT(level)) & ((1 << PT32_LEVEL_BITS) - 1)) |
| 132 | 132 | ||
| 133 | 133 | ||
| 134 | #define PT64_BASE_ADDR_MASK (((1ULL << 52) - 1) & PAGE_MASK) | 134 | #define PT64_BASE_ADDR_MASK (((1ULL << 52) - 1) & ~(u64)(PAGE_SIZE-1)) |
| 135 | #define PT64_DIR_BASE_ADDR_MASK \ | 135 | #define PT64_DIR_BASE_ADDR_MASK \ |
| 136 | (PT64_BASE_ADDR_MASK & ~((1ULL << (PAGE_SHIFT + PT64_LEVEL_BITS)) - 1)) | 136 | (PT64_BASE_ADDR_MASK & ~((1ULL << (PAGE_SHIFT + PT64_LEVEL_BITS)) - 1)) |
| 137 | 137 | ||
| @@ -406,8 +406,8 @@ static void rmap_write_protect(struct kvm_vcpu *vcpu, u64 gfn) | |||
| 406 | spte = desc->shadow_ptes[0]; | 406 | spte = desc->shadow_ptes[0]; |
| 407 | } | 407 | } |
| 408 | BUG_ON(!spte); | 408 | BUG_ON(!spte); |
| 409 | BUG_ON((*spte & PT64_BASE_ADDR_MASK) != | 409 | BUG_ON((*spte & PT64_BASE_ADDR_MASK) >> PAGE_SHIFT |
| 410 | page_to_pfn(page) << PAGE_SHIFT); | 410 | != page_to_pfn(page)); |
| 411 | BUG_ON(!(*spte & PT_PRESENT_MASK)); | 411 | BUG_ON(!(*spte & PT_PRESENT_MASK)); |
| 412 | BUG_ON(!(*spte & PT_WRITABLE_MASK)); | 412 | BUG_ON(!(*spte & PT_WRITABLE_MASK)); |
| 413 | rmap_printk("rmap_write_protect: spte %p %llx\n", spte, *spte); | 413 | rmap_printk("rmap_write_protect: spte %p %llx\n", spte, *spte); |
| @@ -1093,22 +1093,40 @@ out: | |||
| 1093 | return r; | 1093 | return r; |
| 1094 | } | 1094 | } |
| 1095 | 1095 | ||
| 1096 | static void mmu_pre_write_zap_pte(struct kvm_vcpu *vcpu, | ||
| 1097 | struct kvm_mmu_page *page, | ||
| 1098 | u64 *spte) | ||
| 1099 | { | ||
| 1100 | u64 pte; | ||
| 1101 | struct kvm_mmu_page *child; | ||
| 1102 | |||
| 1103 | pte = *spte; | ||
| 1104 | if (is_present_pte(pte)) { | ||
| 1105 | if (page->role.level == PT_PAGE_TABLE_LEVEL) | ||
| 1106 | rmap_remove(vcpu, spte); | ||
| 1107 | else { | ||
| 1108 | child = page_header(pte & PT64_BASE_ADDR_MASK); | ||
| 1109 | mmu_page_remove_parent_pte(vcpu, child, spte); | ||
| 1110 | } | ||
| 1111 | } | ||
| 1112 | *spte = 0; | ||
| 1113 | } | ||
| 1114 | |||
| 1096 | void kvm_mmu_pre_write(struct kvm_vcpu *vcpu, gpa_t gpa, int bytes) | 1115 | void kvm_mmu_pre_write(struct kvm_vcpu *vcpu, gpa_t gpa, int bytes) |
| 1097 | { | 1116 | { |
| 1098 | gfn_t gfn = gpa >> PAGE_SHIFT; | 1117 | gfn_t gfn = gpa >> PAGE_SHIFT; |
| 1099 | struct kvm_mmu_page *page; | 1118 | struct kvm_mmu_page *page; |
| 1100 | struct kvm_mmu_page *child; | ||
| 1101 | struct hlist_node *node, *n; | 1119 | struct hlist_node *node, *n; |
| 1102 | struct hlist_head *bucket; | 1120 | struct hlist_head *bucket; |
| 1103 | unsigned index; | 1121 | unsigned index; |
| 1104 | u64 *spte; | 1122 | u64 *spte; |
| 1105 | u64 pte; | ||
| 1106 | unsigned offset = offset_in_page(gpa); | 1123 | unsigned offset = offset_in_page(gpa); |
| 1107 | unsigned pte_size; | 1124 | unsigned pte_size; |
| 1108 | unsigned page_offset; | 1125 | unsigned page_offset; |
| 1109 | unsigned misaligned; | 1126 | unsigned misaligned; |
| 1110 | int level; | 1127 | int level; |
| 1111 | int flooded = 0; | 1128 | int flooded = 0; |
| 1129 | int npte; | ||
| 1112 | 1130 | ||
| 1113 | pgprintk("%s: gpa %llx bytes %d\n", __FUNCTION__, gpa, bytes); | 1131 | pgprintk("%s: gpa %llx bytes %d\n", __FUNCTION__, gpa, bytes); |
| 1114 | if (gfn == vcpu->last_pt_write_gfn) { | 1132 | if (gfn == vcpu->last_pt_write_gfn) { |
| @@ -1144,22 +1162,27 @@ void kvm_mmu_pre_write(struct kvm_vcpu *vcpu, gpa_t gpa, int bytes) | |||
| 1144 | } | 1162 | } |
| 1145 | page_offset = offset; | 1163 | page_offset = offset; |
| 1146 | level = page->role.level; | 1164 | level = page->role.level; |
| 1165 | npte = 1; | ||
| 1147 | if (page->role.glevels == PT32_ROOT_LEVEL) { | 1166 | if (page->role.glevels == PT32_ROOT_LEVEL) { |
| 1148 | page_offset <<= 1; /* 32->64 */ | 1167 | page_offset <<= 1; /* 32->64 */ |
| 1168 | /* | ||
| 1169 | * A 32-bit pde maps 4MB while the shadow pdes map | ||
| 1170 | * only 2MB. So we need to double the offset again | ||
| 1171 | * and zap two pdes instead of one. | ||
| 1172 | */ | ||
| 1173 | if (level == PT32_ROOT_LEVEL) { | ||
| 1174 | page_offset &= ~7; /* kill rounding error */ | ||
| 1175 | page_offset <<= 1; | ||
| 1176 | npte = 2; | ||
| 1177 | } | ||
| 1149 | page_offset &= ~PAGE_MASK; | 1178 | page_offset &= ~PAGE_MASK; |
| 1150 | } | 1179 | } |
| 1151 | spte = __va(page->page_hpa); | 1180 | spte = __va(page->page_hpa); |
| 1152 | spte += page_offset / sizeof(*spte); | 1181 | spte += page_offset / sizeof(*spte); |
| 1153 | pte = *spte; | 1182 | while (npte--) { |
| 1154 | if (is_present_pte(pte)) { | 1183 | mmu_pre_write_zap_pte(vcpu, page, spte); |
| 1155 | if (level == PT_PAGE_TABLE_LEVEL) | 1184 | ++spte; |
| 1156 | rmap_remove(vcpu, spte); | ||
| 1157 | else { | ||
| 1158 | child = page_header(pte & PT64_BASE_ADDR_MASK); | ||
| 1159 | mmu_page_remove_parent_pte(vcpu, child, spte); | ||
| 1160 | } | ||
| 1161 | } | 1185 | } |
| 1162 | *spte = 0; | ||
| 1163 | } | 1186 | } |
| 1164 | } | 1187 | } |
| 1165 | 1188 | ||
