aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLaurent Vivier <Laurent.Vivier@bull.net>2007-08-05 03:36:40 -0400
committerAvi Kivity <avi@qumranet.com>2007-10-13 04:18:23 -0400
commite70669abd4e60dfea3ac1639848e20e2b8dd1255 (patch)
tree4625f787efdf5f586514cd6306e045a97edb490e /drivers
parent9fdaaac38e8c8a63c6383b807b91fea2d51da95d (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.c3
-rw-r--r--drivers/kvm/svm.c149
-rw-r--r--drivers/kvm/vmx.c76
-rw-r--r--drivers/kvm/x86_emulate.c49
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
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)
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
1766static 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;
1810done:
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
1817static int handle_io(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) 1766static 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
1844static void 1792static 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:
1116special_insn: 1120special_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;