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 | |
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')
-rw-r--r-- | drivers/kvm/kvm_main.c | 3 | ||||
-rw-r--r-- | drivers/kvm/svm.c | 149 | ||||
-rw-r--r-- | drivers/kvm/vmx.c | 76 | ||||
-rw-r--r-- | drivers/kvm/x86_emulate.c | 49 |
4 files changed, 69 insertions, 208 deletions
diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c index 62adaeedfdb0..661d065fd866 100644 --- a/drivers/kvm/kvm_main.c +++ b/drivers/kvm/kvm_main.c | |||
@@ -1221,7 +1221,10 @@ int emulate_instruction(struct kvm_vcpu *vcpu, | |||
1221 | emulate_ctxt.fs_base = get_segment_base(vcpu, VCPU_SREG_FS); | 1221 | emulate_ctxt.fs_base = get_segment_base(vcpu, VCPU_SREG_FS); |
1222 | 1222 | ||
1223 | vcpu->mmio_is_write = 0; | 1223 | vcpu->mmio_is_write = 0; |
1224 | vcpu->pio.string = 0; | ||
1224 | r = x86_emulate_memop(&emulate_ctxt, &emulate_ops); | 1225 | r = x86_emulate_memop(&emulate_ctxt, &emulate_ops); |
1226 | if (vcpu->pio.string) | ||
1227 | return EMULATE_DO_MMIO; | ||
1225 | 1228 | ||
1226 | if ((r || vcpu->mmio_is_write) && run) { | 1229 | if ((r || vcpu->mmio_is_write) && run) { |
1227 | run->exit_reason = KVM_EXIT_MMIO; | 1230 | run->exit_reason = KVM_EXIT_MMIO; |
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) |
diff --git a/drivers/kvm/vmx.c b/drivers/kvm/vmx.c index 30c627d3b21d..044722bc1a7f 100644 --- a/drivers/kvm/vmx.c +++ b/drivers/kvm/vmx.c | |||
@@ -1763,82 +1763,30 @@ static int handle_triple_fault(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | |||
1763 | return 0; | 1763 | return 0; |
1764 | } | 1764 | } |
1765 | 1765 | ||
1766 | static int get_io_count(struct kvm_vcpu *vcpu, unsigned long *count) | ||
1767 | { | ||
1768 | u64 inst; | ||
1769 | gva_t rip; | ||
1770 | int countr_size; | ||
1771 | int i; | ||
1772 | |||
1773 | if ((vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_VM)) { | ||
1774 | countr_size = 2; | ||
1775 | } else { | ||
1776 | u32 cs_ar = vmcs_read32(GUEST_CS_AR_BYTES); | ||
1777 | |||
1778 | countr_size = (cs_ar & AR_L_MASK) ? 8: | ||
1779 | (cs_ar & AR_DB_MASK) ? 4: 2; | ||
1780 | } | ||
1781 | |||
1782 | rip = vmcs_readl(GUEST_RIP); | ||
1783 | if (countr_size != 8) | ||
1784 | rip += vmcs_readl(GUEST_CS_BASE); | ||
1785 | |||
1786 | if (emulator_read_std(rip, &inst, sizeof(inst), vcpu) != | ||
1787 | X86EMUL_CONTINUE) | ||
1788 | return 0; | ||
1789 | |||
1790 | for (i = 0; i < sizeof(inst); i++) { | ||
1791 | switch (((u8*)&inst)[i]) { | ||
1792 | case 0xf0: | ||
1793 | case 0xf2: | ||
1794 | case 0xf3: | ||
1795 | case 0x2e: | ||
1796 | case 0x36: | ||
1797 | case 0x3e: | ||
1798 | case 0x26: | ||
1799 | case 0x64: | ||
1800 | case 0x65: | ||
1801 | case 0x66: | ||
1802 | break; | ||
1803 | case 0x67: | ||
1804 | countr_size = (countr_size == 2) ? 4: (countr_size >> 1); | ||
1805 | default: | ||
1806 | goto done; | ||
1807 | } | ||
1808 | } | ||
1809 | return 0; | ||
1810 | done: | ||
1811 | countr_size *= 8; | ||
1812 | *count = vcpu->regs[VCPU_REGS_RCX] & (~0ULL >> (64 - countr_size)); | ||
1813 | //printk("cx: %lx\n", vcpu->regs[VCPU_REGS_RCX]); | ||
1814 | return 1; | ||
1815 | } | ||
1816 | |||
1817 | static int handle_io(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | 1766 | static int handle_io(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) |
1818 | { | 1767 | { |
1819 | u64 exit_qualification; | 1768 | u64 exit_qualification; |
1820 | int size, down, in, string, rep; | 1769 | int size, down, in, string, rep; |
1821 | unsigned port; | 1770 | unsigned port; |
1822 | unsigned long count; | ||
1823 | gva_t address; | ||
1824 | 1771 | ||
1825 | ++vcpu->stat.io_exits; | 1772 | ++vcpu->stat.io_exits; |
1826 | exit_qualification = vmcs_read64(EXIT_QUALIFICATION); | 1773 | exit_qualification = vmcs_read64(EXIT_QUALIFICATION); |
1827 | in = (exit_qualification & 8) != 0; | ||
1828 | size = (exit_qualification & 7) + 1; | ||
1829 | string = (exit_qualification & 16) != 0; | 1774 | string = (exit_qualification & 16) != 0; |
1775 | |||
1776 | if (string) { | ||
1777 | if (emulate_instruction(vcpu, kvm_run, 0, 0) == EMULATE_DO_MMIO) | ||
1778 | return 0; | ||
1779 | return 1; | ||
1780 | } | ||
1781 | |||
1782 | size = (exit_qualification & 7) + 1; | ||
1783 | in = (exit_qualification & 8) != 0; | ||
1830 | down = (vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_DF) != 0; | 1784 | down = (vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_DF) != 0; |
1831 | count = 1; | ||
1832 | rep = (exit_qualification & 32) != 0; | 1785 | rep = (exit_qualification & 32) != 0; |
1833 | port = exit_qualification >> 16; | 1786 | port = exit_qualification >> 16; |
1834 | address = 0; | 1787 | |
1835 | if (string) { | 1788 | return kvm_setup_pio(vcpu, kvm_run, in, size, 1, 0, down, |
1836 | if (rep && !get_io_count(vcpu, &count)) | 1789 | 0, rep, port); |
1837 | return 1; | ||
1838 | address = vmcs_readl(GUEST_LINEAR_ADDRESS); | ||
1839 | } | ||
1840 | return kvm_setup_pio(vcpu, kvm_run, in, size, count, string, down, | ||
1841 | address, rep, port); | ||
1842 | } | 1790 | } |
1843 | 1791 | ||
1844 | static void | 1792 | static void |
diff --git a/drivers/kvm/x86_emulate.c b/drivers/kvm/x86_emulate.c index 44eb28d31499..d553719fc4cb 100644 --- a/drivers/kvm/x86_emulate.c +++ b/drivers/kvm/x86_emulate.c | |||
@@ -103,9 +103,12 @@ static u8 opcode_table[256] = { | |||
103 | /* 0x58 - 0x5F */ | 103 | /* 0x58 - 0x5F */ |
104 | ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps, | 104 | ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps, |
105 | ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps, | 105 | ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps, |
106 | /* 0x60 - 0x6F */ | 106 | /* 0x60 - 0x6B */ |
107 | 0, 0, 0, DstReg | SrcMem32 | ModRM | Mov /* movsxd (x86/64) */ , | 107 | 0, 0, 0, DstReg | SrcMem32 | ModRM | Mov /* movsxd (x86/64) */ , |
108 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | 108 | 0, 0, 0, 0, 0, 0, 0, 0, |
109 | /* 0x6C - 0x6F */ | ||
110 | SrcNone | ByteOp | ImplicitOps, SrcNone | ImplicitOps, /* insb, insw/insd */ | ||
111 | SrcNone | ByteOp | ImplicitOps, SrcNone | ImplicitOps, /* outsb, outsw/outsd */ | ||
109 | /* 0x70 - 0x7F */ | 112 | /* 0x70 - 0x7F */ |
110 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | 113 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
111 | /* 0x80 - 0x87 */ | 114 | /* 0x80 - 0x87 */ |
@@ -428,10 +431,11 @@ struct operand { | |||
428 | }) | 431 | }) |
429 | 432 | ||
430 | /* Access/update address held in a register, based on addressing mode. */ | 433 | /* Access/update address held in a register, based on addressing mode. */ |
434 | #define address_mask(reg) \ | ||
435 | ((ad_bytes == sizeof(unsigned long)) ? \ | ||
436 | (reg) : ((reg) & ((1UL << (ad_bytes << 3)) - 1))) | ||
431 | #define register_address(base, reg) \ | 437 | #define register_address(base, reg) \ |
432 | ((base) + ((ad_bytes == sizeof(unsigned long)) ? (reg) : \ | 438 | ((base) + address_mask(reg)) |
433 | ((reg) & ((1UL << (ad_bytes << 3)) - 1)))) | ||
434 | |||
435 | #define register_address_increment(reg, inc) \ | 439 | #define register_address_increment(reg, inc) \ |
436 | do { \ | 440 | do { \ |
437 | /* signed type ensures sign extension to long */ \ | 441 | /* signed type ensures sign extension to long */ \ |
@@ -1116,6 +1120,41 @@ done: | |||
1116 | special_insn: | 1120 | special_insn: |
1117 | if (twobyte) | 1121 | if (twobyte) |
1118 | goto twobyte_special_insn; | 1122 | goto twobyte_special_insn; |
1123 | switch(b) { | ||
1124 | case 0x6c: /* insb */ | ||
1125 | case 0x6d: /* insw/insd */ | ||
1126 | if (kvm_setup_pio(ctxt->vcpu, NULL, | ||
1127 | 1, /* in */ | ||
1128 | (d & ByteOp) ? 1 : op_bytes, /* size */ | ||
1129 | rep_prefix ? | ||
1130 | address_mask(_regs[VCPU_REGS_RCX]) : 1, /* count */ | ||
1131 | 1, /* strings */ | ||
1132 | (_eflags & EFLG_DF), /* down */ | ||
1133 | register_address(ctxt->es_base, | ||
1134 | _regs[VCPU_REGS_RDI]), /* address */ | ||
1135 | rep_prefix, | ||
1136 | _regs[VCPU_REGS_RDX] /* port */ | ||
1137 | ) == 0) | ||
1138 | return -1; | ||
1139 | return 0; | ||
1140 | case 0x6e: /* outsb */ | ||
1141 | case 0x6f: /* outsw/outsd */ | ||
1142 | if (kvm_setup_pio(ctxt->vcpu, NULL, | ||
1143 | 0, /* in */ | ||
1144 | (d & ByteOp) ? 1 : op_bytes, /* size */ | ||
1145 | rep_prefix ? | ||
1146 | address_mask(_regs[VCPU_REGS_RCX]) : 1, /* count */ | ||
1147 | 1, /* strings */ | ||
1148 | (_eflags & EFLG_DF), /* down */ | ||
1149 | register_address(override_base ? | ||
1150 | *override_base : ctxt->ds_base, | ||
1151 | _regs[VCPU_REGS_RSI]), /* address */ | ||
1152 | rep_prefix, | ||
1153 | _regs[VCPU_REGS_RDX] /* port */ | ||
1154 | ) == 0) | ||
1155 | return -1; | ||
1156 | return 0; | ||
1157 | } | ||
1119 | if (rep_prefix) { | 1158 | if (rep_prefix) { |
1120 | if (_regs[VCPU_REGS_RCX] == 0) { | 1159 | if (_regs[VCPU_REGS_RCX] == 0) { |
1121 | ctxt->vcpu->rip = _eip; | 1160 | ctxt->vcpu->rip = _eip; |