# HG changeset patch
# User kaf24@xxxxxxxxxxxxxxxxxxxx
# Node ID 632ad28f2fd7a6602b08a9d054dc1b44efaf93f3
# Parent 81ab21f76a6f11a7264d242f039c6c7b25cda650
SVM patch to cleanup IOIO handling, do not use "real" mode but rather
the correct "bitness".
Signed-off-by: Tom Woller <thomas.woller@xxxxxxx>
Signed-off-by: Mats Petersson <mats.petersson@xxxxxxx>
---
xen/arch/x86/hvm/svm/svm.c | 193 +++++++++++++++++++++++++++------------------
1 files changed, 120 insertions(+), 73 deletions(-)
diff -r 81ab21f76a6f -r 632ad28f2fd7 xen/arch/x86/hvm/svm/svm.c
--- a/xen/arch/x86/hvm/svm/svm.c Wed May 17 23:47:19 2006 +0100
+++ b/xen/arch/x86/hvm/svm/svm.c Wed May 17 23:50:23 2006 +0100
@@ -1118,19 +1118,17 @@ static void svm_dr_access (struct vcpu *
}
-static unsigned int check_for_null_selector(struct vmcb_struct *vmcb,
- unsigned int dir, unsigned long *base, unsigned int real)
-
+static void svm_get_prefix_info(struct vmcb_struct *vmcb,
+ unsigned int dir, segment_selector_t **seg, unsigned int *asize)
{
unsigned char inst[MAX_INST_LEN];
- segment_selector_t seg;
int i;
memset(inst, 0, MAX_INST_LEN);
if (inst_copy_from_guest(inst, svm_rip2pointer(vmcb), sizeof(inst))
!= MAX_INST_LEN)
{
- printk("check_for_null_selector: get guest instruction failed\n");
+ printk("%s: get guest instruction failed\n", __func__);
domain_crash_synchronous();
}
@@ -1142,7 +1140,6 @@ static unsigned int check_for_null_selec
case 0xf2: /* REPNZ */
case 0xf0: /* LOCK */
case 0x66: /* data32 */
- case 0x67: /* addr32 */
#if __x86_64__
/* REX prefixes */
case 0x40:
@@ -1164,89 +1161,133 @@ static unsigned int check_for_null_selec
case 0x4f:
#endif
continue;
+ case 0x67: /* addr32 */
+ *asize ^= 48; /* Switch 16/32 bits */
+ continue;
case 0x2e: /* CS */
- seg = vmcb->cs;
+ *seg = &vmcb->cs;
+ continue;
+ case 0x36: /* SS */
+ *seg = &vmcb->ss;
+ continue;
+ case 0x26: /* ES */
+ *seg = &vmcb->es;
+ continue;
+ case 0x64: /* FS */
+ *seg = &vmcb->fs;
+ continue;
+ case 0x65: /* GS */
+ *seg = &vmcb->gs;
+ continue;
+ case 0x3e: /* DS */
+ *seg = &vmcb->ds;
+ continue;
+ default:
break;
- case 0x36: /* SS */
- seg = vmcb->ss;
- break;
- case 0x26: /* ES */
- seg = vmcb->es;
- break;
- case 0x64: /* FS */
- seg = vmcb->fs;
- break;
- case 0x65: /* GS */
- seg = vmcb->gs;
- break;
- case 0x3e: /* DS */
- /* FALLTHROUGH */
- seg = vmcb->ds;
- break;
- default:
- if (dir == IOREQ_READ) /* IN/INS instruction? */
- seg = vmcb->es;
- else
- seg = vmcb->ds;
- }
-
- if (base)
- *base = seg.base;
-
- return seg.attributes.fields.p;
- }
-
- ASSERT(0);
- return 0;
+ }
+ return;
+ }
}
/* Get the address of INS/OUTS instruction */
-static inline unsigned long svm_get_io_address(struct vmcb_struct *vmcb,
- struct cpu_user_regs *regs, unsigned int dir, unsigned int real)
-{
- unsigned long addr = 0;
- unsigned long base = 0;
-
- check_for_null_selector(vmcb, dir, &base, real);
+static inline int svm_get_io_address(struct vmcb_struct *vmcb,
+ struct cpu_user_regs *regs, unsigned int dir,
+ unsigned long *count, unsigned long *addr)
+{
+ unsigned long reg;
+ unsigned int asize = 0;
+ unsigned int isize;
+ int long_mode;
+ ioio_info_t info;
+ segment_selector_t *seg = NULL;
+
+ info.bytes = vmcb->exitinfo1;
+
+ /* If we're in long mode, we shouldn't check the segment presence and
limit */
+ long_mode = vmcb->cs.attributes.fields.l && vmcb->efer & EFER_LMA;
+
+ /* d field of cs.attributes is 1 for 32-bit, 0 for 16 or 64 bit.
+ * l field combined with EFER_LMA -> longmode says whether it's 16 or 64
bit.
+ */
+ asize = (long_mode)?64:((vmcb->cs.attributes.fields.db)?32:16);
+
+
+ /* The ins/outs instructions are single byte, so if we have got more
+ * than one byte (+ maybe rep-prefix), we have some prefix so we need
+ * to figure out what it is...
+ */
+ isize = vmcb->exitinfo2 - vmcb->rip;
+
+ if (info.fields.rep)
+ isize --;
+
+ if (isize > 1)
+ {
+ svm_get_prefix_info(vmcb, dir, &seg, &asize);
+ }
+
+ ASSERT(dir == IOREQ_READ || dir == IOREQ_WRITE);
if (dir == IOREQ_WRITE)
{
- if (real)
- addr = (regs->esi & 0xFFFF) + base;
- else
- addr = regs->esi + base;
+ reg = regs->esi;
+ if (!seg) /* If no prefix, used DS. */
+ seg = &vmcb->ds;
}
else
{
- if (real)
- addr = (regs->edi & 0xFFFF) + base;
- else
- addr = regs->edi + base;
- }
-
- return addr;
+ reg = regs->edi;
+ seg = &vmcb->es; /* Note: This is ALWAYS ES. */
+ }
+
+ /* If the segment isn't present, give GP fault! */
+ if (!long_mode && !seg->attributes.fields.p)
+ {
+ svm_inject_exception(vmcb, TRAP_gp_fault, 1, seg->sel);
+ return 0;
+ }
+
+ if (asize == 16)
+ {
+ *addr = (reg & 0xFFFF);
+ *count = regs->ecx & 0xffff;
+ }
+ else
+ {
+ *addr = reg;
+ *count = regs->ecx;
+ }
+
+ if (!long_mode) {
+ if (*addr > seg->limit)
+ {
+ svm_inject_exception(vmcb, TRAP_gp_fault, 1, seg->sel);
+ return 0;
+ }
+ else
+ {
+ *addr += seg->base;
+ }
+ }
+
+
+ return 1;
}
static void svm_io_instruction(struct vcpu *v, struct cpu_user_regs *regs)
{
struct mmio_op *mmio_opp;
- unsigned long eip, cs, eflags, cr0;
- unsigned long port;
- unsigned int real, size, dir;
+ unsigned int port;
+ unsigned int size, dir;
ioio_info_t info;
-
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
ASSERT(vmcb);
mmio_opp = ¤t->arch.hvm_vcpu.mmio_op;
mmio_opp->instr = INSTR_PIO;
mmio_opp->flags = 0;
-
- eip = vmcb->rip;
- cs = vmcb->cs.sel;
- eflags = vmcb->rflags;
info.bytes = vmcb->exitinfo1;
@@ -1259,27 +1300,33 @@ static void svm_io_instruction(struct vc
else
size = 1;
- cr0 = vmcb->cr0;
- real = (eflags & X86_EFLAGS_VM) || !(cr0 & X86_CR0_PE);
-
HVM_DBG_LOG(DBG_LEVEL_IO,
- "svm_io_instruction: port 0x%lx real %d, eip=%lx:%lx, "
+ "svm_io_instruction: port 0x%x eip=%lx:%lx, "
"exit_qualification = %lx",
- (unsigned long) port, real, cs, eip, (unsigned
long)info.bytes);
+ port, vmcb->cs.sel, vmcb->rip, (unsigned long)info.bytes);
/* string instruction */
if (info.fields.str)
{
- unsigned long addr, count = 1;
+ unsigned long addr, count;
int sign = regs->eflags & EF_DF ? -1 : 1;
- /* Need the original rip, here. */
- addr = svm_get_io_address(vmcb, regs, dir, real);
+ if (!svm_get_io_address(vmcb, regs, dir, &count, &addr))
+ {
+ /* We failed to get a valid address, so don't do the IO operation
-
+ * it would just get worse if we do! Hopefully the guest is handing
+ * gp-faults...
+ */
+ return;
+ }
/* "rep" prefix */
if (info.fields.rep)
{
mmio_opp->flags |= REPZ;
- count = real ? regs->ecx & 0xFFFF : regs->ecx;
+ }
+ else
+ {
+ count = 1;
}
/*
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|