aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/kvm/kvm.h9
-rw-r--r--drivers/kvm/kvm_main.c160
-rw-r--r--drivers/kvm/vmx.c43
3 files changed, 158 insertions, 54 deletions
diff --git a/drivers/kvm/kvm.h b/drivers/kvm/kvm.h
index 80cfb99fffe0..1965438f18cc 100644
--- a/drivers/kvm/kvm.h
+++ b/drivers/kvm/kvm.h
@@ -559,6 +559,15 @@ extern hpa_t bad_page_address;
559 559
560gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn); 560gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn);
561struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn); 561struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn);
562int kvm_read_guest_page(struct kvm *kvm, gfn_t gfn, void *data, int offset,
563 int len);
564int kvm_read_guest(struct kvm *kvm, gpa_t gpa, void *data, unsigned long len);
565int kvm_write_guest_page(struct kvm *kvm, gfn_t gfn, const void *data,
566 int offset, int len);
567int kvm_write_guest(struct kvm *kvm, gpa_t gpa, const void *data,
568 unsigned long len);
569int kvm_clear_guest_page(struct kvm *kvm, gfn_t gfn, int offset, int len);
570int kvm_clear_guest(struct kvm *kvm, gpa_t gpa, unsigned long len);
562struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn); 571struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn);
563void mark_page_dirty(struct kvm *kvm, gfn_t gfn); 572void mark_page_dirty(struct kvm *kvm, gfn_t gfn);
564 573
diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c
index 9510e2276ca3..cac328b8421c 100644
--- a/drivers/kvm/kvm_main.c
+++ b/drivers/kvm/kvm_main.c
@@ -400,22 +400,16 @@ static int load_pdptrs(struct kvm_vcpu *vcpu, unsigned long cr3)
400 gfn_t pdpt_gfn = cr3 >> PAGE_SHIFT; 400 gfn_t pdpt_gfn = cr3 >> PAGE_SHIFT;
401 unsigned offset = ((cr3 & (PAGE_SIZE-1)) >> 5) << 2; 401 unsigned offset = ((cr3 & (PAGE_SIZE-1)) >> 5) << 2;
402 int i; 402 int i;
403 u64 *pdpt;
404 int ret; 403 int ret;
405 struct page *page;
406 u64 pdpte[ARRAY_SIZE(vcpu->pdptrs)]; 404 u64 pdpte[ARRAY_SIZE(vcpu->pdptrs)];
407 405
408 mutex_lock(&vcpu->kvm->lock); 406 mutex_lock(&vcpu->kvm->lock);
409 page = gfn_to_page(vcpu->kvm, pdpt_gfn); 407 ret = kvm_read_guest_page(vcpu->kvm, pdpt_gfn, pdpte,
410 if (!page) { 408 offset * sizeof(u64), sizeof(pdpte));
409 if (ret < 0) {
411 ret = 0; 410 ret = 0;
412 goto out; 411 goto out;
413 } 412 }
414
415 pdpt = kmap_atomic(page, KM_USER0);
416 memcpy(pdpte, pdpt+offset, sizeof(pdpte));
417 kunmap_atomic(pdpt, KM_USER0);
418
419 for (i = 0; i < ARRAY_SIZE(pdpte); ++i) { 413 for (i = 0; i < ARRAY_SIZE(pdpte); ++i) {
420 if ((pdpte[i] & 1) && (pdpte[i] & 0xfffffff0000001e6ull)) { 414 if ((pdpte[i] & 1) && (pdpte[i] & 0xfffffff0000001e6ull)) {
421 ret = 0; 415 ret = 0;
@@ -962,6 +956,127 @@ struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn)
962} 956}
963EXPORT_SYMBOL_GPL(gfn_to_page); 957EXPORT_SYMBOL_GPL(gfn_to_page);
964 958
959static int next_segment(unsigned long len, int offset)
960{
961 if (len > PAGE_SIZE - offset)
962 return PAGE_SIZE - offset;
963 else
964 return len;
965}
966
967int kvm_read_guest_page(struct kvm *kvm, gfn_t gfn, void *data, int offset,
968 int len)
969{
970 void *page_virt;
971 struct page *page;
972
973 page = gfn_to_page(kvm, gfn);
974 if (!page)
975 return -EFAULT;
976 page_virt = kmap_atomic(page, KM_USER0);
977
978 memcpy(data, page_virt + offset, len);
979
980 kunmap_atomic(page_virt, KM_USER0);
981 return 0;
982}
983EXPORT_SYMBOL_GPL(kvm_read_guest_page);
984
985int kvm_read_guest(struct kvm *kvm, gpa_t gpa, void *data, unsigned long len)
986{
987 gfn_t gfn = gpa >> PAGE_SHIFT;
988 int seg;
989 int offset = offset_in_page(gpa);
990 int ret;
991
992 while ((seg = next_segment(len, offset)) != 0) {
993 ret = kvm_read_guest_page(kvm, gfn, data, offset, seg);
994 if (ret < 0)
995 return ret;
996 offset = 0;
997 len -= seg;
998 data += seg;
999 ++gfn;
1000 }
1001 return 0;
1002}
1003EXPORT_SYMBOL_GPL(kvm_read_guest);
1004
1005int kvm_write_guest_page(struct kvm *kvm, gfn_t gfn, const void *data,
1006 int offset, int len)
1007{
1008 void *page_virt;
1009 struct page *page;
1010
1011 page = gfn_to_page(kvm, gfn);
1012 if (!page)
1013 return -EFAULT;
1014 page_virt = kmap_atomic(page, KM_USER0);
1015
1016 memcpy(page_virt + offset, data, len);
1017
1018 kunmap_atomic(page_virt, KM_USER0);
1019 mark_page_dirty(kvm, gfn);
1020 return 0;
1021}
1022EXPORT_SYMBOL_GPL(kvm_write_guest_page);
1023
1024int kvm_write_guest(struct kvm *kvm, gpa_t gpa, const void *data,
1025 unsigned long len)
1026{
1027 gfn_t gfn = gpa >> PAGE_SHIFT;
1028 int seg;
1029 int offset = offset_in_page(gpa);
1030 int ret;
1031
1032 while ((seg = next_segment(len, offset)) != 0) {
1033 ret = kvm_write_guest_page(kvm, gfn, data, offset, seg);
1034 if (ret < 0)
1035 return ret;
1036 offset = 0;
1037 len -= seg;
1038 data += seg;
1039 ++gfn;
1040 }
1041 return 0;
1042}
1043
1044int kvm_clear_guest_page(struct kvm *kvm, gfn_t gfn, int offset, int len)
1045{
1046 void *page_virt;
1047 struct page *page;
1048
1049 page = gfn_to_page(kvm, gfn);
1050 if (!page)
1051 return -EFAULT;
1052 page_virt = kmap_atomic(page, KM_USER0);
1053
1054 memset(page_virt + offset, 0, len);
1055
1056 kunmap_atomic(page_virt, KM_USER0);
1057 return 0;
1058}
1059EXPORT_SYMBOL_GPL(kvm_clear_guest_page);
1060
1061int kvm_clear_guest(struct kvm *kvm, gpa_t gpa, unsigned long len)
1062{
1063 gfn_t gfn = gpa >> PAGE_SHIFT;
1064 int seg;
1065 int offset = offset_in_page(gpa);
1066 int ret;
1067
1068 while ((seg = next_segment(len, offset)) != 0) {
1069 ret = kvm_clear_guest_page(kvm, gfn, offset, seg);
1070 if (ret < 0)
1071 return ret;
1072 offset = 0;
1073 len -= seg;
1074 ++gfn;
1075 }
1076 return 0;
1077}
1078EXPORT_SYMBOL_GPL(kvm_clear_guest);
1079
965/* WARNING: Does not work on aliased pages. */ 1080/* WARNING: Does not work on aliased pages. */
966void mark_page_dirty(struct kvm *kvm, gfn_t gfn) 1081void mark_page_dirty(struct kvm *kvm, gfn_t gfn)
967{ 1082{
@@ -988,21 +1103,13 @@ int emulator_read_std(unsigned long addr,
988 gpa_t gpa = vcpu->mmu.gva_to_gpa(vcpu, addr); 1103 gpa_t gpa = vcpu->mmu.gva_to_gpa(vcpu, addr);
989 unsigned offset = addr & (PAGE_SIZE-1); 1104 unsigned offset = addr & (PAGE_SIZE-1);
990 unsigned tocopy = min(bytes, (unsigned)PAGE_SIZE - offset); 1105 unsigned tocopy = min(bytes, (unsigned)PAGE_SIZE - offset);
991 unsigned long pfn; 1106 int ret;
992 struct page *page;
993 void *page_virt;
994 1107
995 if (gpa == UNMAPPED_GVA) 1108 if (gpa == UNMAPPED_GVA)
996 return X86EMUL_PROPAGATE_FAULT; 1109 return X86EMUL_PROPAGATE_FAULT;
997 pfn = gpa >> PAGE_SHIFT; 1110 ret = kvm_read_guest(vcpu->kvm, gpa, data, tocopy);
998 page = gfn_to_page(vcpu->kvm, pfn); 1111 if (ret < 0)
999 if (!page)
1000 return X86EMUL_UNHANDLEABLE; 1112 return X86EMUL_UNHANDLEABLE;
1001 page_virt = kmap_atomic(page, KM_USER0);
1002
1003 memcpy(data, page_virt + offset, tocopy);
1004
1005 kunmap_atomic(page_virt, KM_USER0);
1006 1113
1007 bytes -= tocopy; 1114 bytes -= tocopy;
1008 data += tocopy; 1115 data += tocopy;
@@ -1095,19 +1202,12 @@ static int emulator_read_emulated(unsigned long addr,
1095static int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa, 1202static int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa,
1096 const void *val, int bytes) 1203 const void *val, int bytes)
1097{ 1204{
1098 struct page *page; 1205 int ret;
1099 void *virt;
1100 1206
1101 if (((gpa + bytes - 1) >> PAGE_SHIFT) != (gpa >> PAGE_SHIFT)) 1207 ret = kvm_write_guest(vcpu->kvm, gpa, val, bytes);
1102 return 0; 1208 if (ret < 0)
1103 page = gfn_to_page(vcpu->kvm, gpa >> PAGE_SHIFT);
1104 if (!page)
1105 return 0; 1209 return 0;
1106 mark_page_dirty(vcpu->kvm, gpa >> PAGE_SHIFT);
1107 virt = kmap_atomic(page, KM_USER0);
1108 kvm_mmu_pte_write(vcpu, gpa, val, bytes); 1210 kvm_mmu_pte_write(vcpu, gpa, val, bytes);
1109 memcpy(virt + offset_in_page(gpa), val, bytes);
1110 kunmap_atomic(virt, KM_USER0);
1111 return 1; 1211 return 1;
1112} 1212}
1113 1213
diff --git a/drivers/kvm/vmx.c b/drivers/kvm/vmx.c
index 439873a25cca..894fd45ecc98 100644
--- a/drivers/kvm/vmx.c
+++ b/drivers/kvm/vmx.c
@@ -1387,33 +1387,28 @@ static void vmx_set_gdt(struct kvm_vcpu *vcpu, struct descriptor_table *dt)
1387 1387
1388static int init_rmode_tss(struct kvm* kvm) 1388static int init_rmode_tss(struct kvm* kvm)
1389{ 1389{
1390 struct page *p1, *p2, *p3;
1391 gfn_t fn = rmode_tss_base(kvm) >> PAGE_SHIFT; 1390 gfn_t fn = rmode_tss_base(kvm) >> PAGE_SHIFT;
1392 char *page; 1391 u16 data = 0;
1393 1392 int r;
1394 p1 = gfn_to_page(kvm, fn++);
1395 p2 = gfn_to_page(kvm, fn++);
1396 p3 = gfn_to_page(kvm, fn);
1397 1393
1398 if (!p1 || !p2 || !p3) { 1394 r = kvm_clear_guest_page(kvm, fn, 0, PAGE_SIZE);
1399 kvm_printf(kvm,"%s: gfn_to_page failed\n", __FUNCTION__); 1395 if (r < 0)
1396 return 0;
1397 data = TSS_BASE_SIZE + TSS_REDIRECTION_SIZE;
1398 r = kvm_write_guest_page(kvm, fn++, &data, 0x66, sizeof(u16));
1399 if (r < 0)
1400 return 0;
1401 r = kvm_clear_guest_page(kvm, fn++, 0, PAGE_SIZE);
1402 if (r < 0)
1403 return 0;
1404 r = kvm_clear_guest_page(kvm, fn, 0, PAGE_SIZE);
1405 if (r < 0)
1406 return 0;
1407 data = ~0;
1408 r = kvm_write_guest_page(kvm, fn, &data, RMODE_TSS_SIZE - 2 * PAGE_SIZE - 1,
1409 sizeof(u8));
1410 if (r < 0)
1400 return 0; 1411 return 0;
1401 }
1402
1403 page = kmap_atomic(p1, KM_USER0);
1404 clear_page(page);
1405 *(u16*)(page + 0x66) = TSS_BASE_SIZE + TSS_REDIRECTION_SIZE;
1406 kunmap_atomic(page, KM_USER0);
1407
1408 page = kmap_atomic(p2, KM_USER0);
1409 clear_page(page);
1410 kunmap_atomic(page, KM_USER0);
1411
1412 page = kmap_atomic(p3, KM_USER0);
1413 clear_page(page);
1414 *(page + RMODE_TSS_SIZE - 2 * PAGE_SIZE - 1) = ~0;
1415 kunmap_atomic(page, KM_USER0);
1416
1417 return 1; 1412 return 1;
1418} 1413}
1419 1414