diff options
author | Laurent Vivier <Laurent.Vivier@bull.net> | 2007-08-05 03:36:40 -0400 |
---|---|---|
committer | Avi Kivity <avi@qumranet.com> | 2007-10-13 04:18:23 -0400 |
commit | e70669abd4e60dfea3ac1639848e20e2b8dd1255 (patch) | |
tree | 4625f787efdf5f586514cd6306e045a97edb490e /drivers/kvm/svm.c | |
parent | 9fdaaac38e8c8a63c6383b807b91fea2d51da95d (diff) |
KVM: Cleanup string I/O instruction emulation
Both vmx and svm decode the I/O instructions, and both botch the job,
requiring the instruction prefixes to be fetched in order to completely
decode the instruction.
So, if we see a string I/O instruction, use the x86 emulator to decode it,
as it already has all the prefix decoding machinery.
This patch defines ins/outs opcodes in x86_emulate.c and calls
emulate_instruction() from io_interception() (svm.c) and from handle_io()
(vmx.c). It removes all vmx/svm prefix instruction decoders
(get_addr_size(), io_get_override(), io_address(), get_io_count())
Signed-off-by: Laurent Vivier <Laurent.Vivier@bull.net>
Signed-off-by: Avi Kivity <avi@qumranet.com>
Diffstat (limited to 'drivers/kvm/svm.c')
-rw-r--r-- | drivers/kvm/svm.c | 149 |
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 | ||
101 | static 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 | |||
115 | static inline u8 pop_irq(struct kvm_vcpu *vcpu) | 101 | static 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 | ||
998 | static 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 | |||
1063 | static 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 | |||
1104 | static int io_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run) | 984 | static 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 | ||
1141 | static int nop_on_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run) | 1012 | static int nop_on_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run) |