aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/kvm/svm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/kvm/svm.c')
-rw-r--r--drivers/kvm/svm.c149
1 files changed, 10 insertions, 139 deletions
diff --git a/drivers/kvm/svm.c b/drivers/kvm/svm.c
index 436bdff9b0bf..a83ff01bb014 100644
--- a/drivers/kvm/svm.c
+++ b/drivers/kvm/svm.c
@@ -98,20 +98,6 @@ static inline u32 svm_has(u32 feat)
98 return svm_features & feat; 98 return svm_features & feat;
99} 99}
100 100
101static unsigned get_addr_size(struct vcpu_svm *svm)
102{
103 struct vmcb_save_area *sa = &svm->vmcb->save;
104 u16 cs_attrib;
105
106 if (!(sa->cr0 & X86_CR0_PE) || (sa->rflags & X86_EFLAGS_VM))
107 return 2;
108
109 cs_attrib = sa->cs.attrib;
110
111 return (cs_attrib & SVM_SELECTOR_L_MASK) ? 8 :
112 (cs_attrib & SVM_SELECTOR_DB_MASK) ? 4 : 2;
113}
114
115static inline u8 pop_irq(struct kvm_vcpu *vcpu) 101static inline u8 pop_irq(struct kvm_vcpu *vcpu)
116{ 102{
117 int word_index = __ffs(vcpu->irq_summary); 103 int word_index = __ffs(vcpu->irq_summary);
@@ -995,147 +981,32 @@ static int shutdown_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
995 return 0; 981 return 0;
996} 982}
997 983
998static int io_get_override(struct vcpu_svm *svm,
999 struct vmcb_seg **seg,
1000 int *addr_override)
1001{
1002 u8 inst[MAX_INST_SIZE];
1003 unsigned ins_length;
1004 gva_t rip;
1005 int i;
1006
1007 rip = svm->vmcb->save.rip;
1008 ins_length = svm->next_rip - rip;
1009 rip += svm->vmcb->save.cs.base;
1010
1011 if (ins_length > MAX_INST_SIZE)
1012 printk(KERN_DEBUG
1013 "%s: inst length err, cs base 0x%llx rip 0x%llx "
1014 "next rip 0x%llx ins_length %u\n",
1015 __FUNCTION__,
1016 svm->vmcb->save.cs.base,
1017 svm->vmcb->save.rip,
1018 svm->vmcb->control.exit_info_2,
1019 ins_length);
1020
1021 if (emulator_read_std(rip, inst, ins_length, &svm->vcpu)
1022 != X86EMUL_CONTINUE)
1023 /* #PF */
1024 return 0;
1025
1026 *addr_override = 0;
1027 *seg = NULL;
1028 for (i = 0; i < ins_length; i++)
1029 switch (inst[i]) {
1030 case 0xf0:
1031 case 0xf2:
1032 case 0xf3:
1033 case 0x66:
1034 continue;
1035 case 0x67:
1036 *addr_override = 1;
1037 continue;
1038 case 0x2e:
1039 *seg = &svm->vmcb->save.cs;
1040 continue;
1041 case 0x36:
1042 *seg = &svm->vmcb->save.ss;
1043 continue;
1044 case 0x3e:
1045 *seg = &svm->vmcb->save.ds;
1046 continue;
1047 case 0x26:
1048 *seg = &svm->vmcb->save.es;
1049 continue;
1050 case 0x64:
1051 *seg = &svm->vmcb->save.fs;
1052 continue;
1053 case 0x65:
1054 *seg = &svm->vmcb->save.gs;
1055 continue;
1056 default:
1057 return 1;
1058 }
1059 printk(KERN_DEBUG "%s: unexpected\n", __FUNCTION__);
1060 return 0;
1061}
1062
1063static unsigned long io_address(struct vcpu_svm *svm, int ins, gva_t *address)
1064{
1065 unsigned long addr_mask;
1066 unsigned long *reg;
1067 struct vmcb_seg *seg;
1068 int addr_override;
1069 struct vmcb_save_area *save_area = &svm->vmcb->save;
1070 u16 cs_attrib = save_area->cs.attrib;
1071 unsigned addr_size = get_addr_size(svm);
1072
1073 if (!io_get_override(svm, &seg, &addr_override))
1074 return 0;
1075
1076 if (addr_override)
1077 addr_size = (addr_size == 2) ? 4: (addr_size >> 1);
1078
1079 if (ins) {
1080 reg = &svm->vcpu.regs[VCPU_REGS_RDI];
1081 seg = &svm->vmcb->save.es;
1082 } else {
1083 reg = &svm->vcpu.regs[VCPU_REGS_RSI];
1084 seg = (seg) ? seg : &svm->vmcb->save.ds;
1085 }
1086
1087 addr_mask = ~0ULL >> (64 - (addr_size * 8));
1088
1089 if ((cs_attrib & SVM_SELECTOR_L_MASK) &&
1090 !(svm->vmcb->save.rflags & X86_EFLAGS_VM)) {
1091 *address = (*reg & addr_mask);
1092 return addr_mask;
1093 }
1094
1095 if (!(seg->attrib & SVM_SELECTOR_P_SHIFT)) {
1096 svm_inject_gp(&svm->vcpu, 0);
1097 return 0;
1098 }
1099
1100 *address = (*reg & addr_mask) + seg->base;
1101 return addr_mask;
1102}
1103
1104static int io_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run) 984static int io_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
1105{ 985{
1106 u32 io_info = svm->vmcb->control.exit_info_1; //address size bug? 986 u32 io_info = svm->vmcb->control.exit_info_1; //address size bug?
1107 int size, down, in, string, rep; 987 int size, down, in, string, rep;
1108 unsigned port; 988 unsigned port;
1109 unsigned long count;
1110 gva_t address = 0;
1111 989
1112 ++svm->vcpu.stat.io_exits; 990 ++svm->vcpu.stat.io_exits;
1113 991
1114 svm->next_rip = svm->vmcb->control.exit_info_2; 992 svm->next_rip = svm->vmcb->control.exit_info_2;
1115 993
994 string = (io_info & SVM_IOIO_STR_MASK) != 0;
995
996 if (string) {
997 if (emulate_instruction(&svm->vcpu, kvm_run, 0, 0) == EMULATE_DO_MMIO)
998 return 0;
999 return 1;
1000 }
1001
1116 in = (io_info & SVM_IOIO_TYPE_MASK) != 0; 1002 in = (io_info & SVM_IOIO_TYPE_MASK) != 0;
1117 port = io_info >> 16; 1003 port = io_info >> 16;
1118 size = (io_info & SVM_IOIO_SIZE_MASK) >> SVM_IOIO_SIZE_SHIFT; 1004 size = (io_info & SVM_IOIO_SIZE_MASK) >> SVM_IOIO_SIZE_SHIFT;
1119 string = (io_info & SVM_IOIO_STR_MASK) != 0;
1120 rep = (io_info & SVM_IOIO_REP_MASK) != 0; 1005 rep = (io_info & SVM_IOIO_REP_MASK) != 0;
1121 count = 1;
1122 down = (svm->vmcb->save.rflags & X86_EFLAGS_DF) != 0; 1006 down = (svm->vmcb->save.rflags & X86_EFLAGS_DF) != 0;
1123 1007
1124 if (string) { 1008 return kvm_setup_pio(&svm->vcpu, kvm_run, in, size, 1, 0,
1125 unsigned addr_mask; 1009 down, 0, rep, port);
1126
1127 addr_mask = io_address(svm, in, &address);
1128 if (!addr_mask) {
1129 printk(KERN_DEBUG "%s: get io address failed\n",
1130 __FUNCTION__);
1131 return 1;
1132 }
1133
1134 if (rep)
1135 count = svm->vcpu.regs[VCPU_REGS_RCX] & addr_mask;
1136 }
1137 return kvm_setup_pio(&svm->vcpu, kvm_run, in, size, count, string,
1138 down, address, rep, port);
1139} 1010}
1140 1011
1141static int nop_on_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run) 1012static int nop_on_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)