From 3bb4f99d8cec90c3d7731d004333dd03998e2287 Mon Sep 17 00:00:00 2001 From: Keir Fraser Date: Wed, 20 Feb 2008 14:36:45 +0000 Subject: x86 hvm: Replace old MMIO emulator with x86_emulate()-based harness. Re-factor VMX real-mode emulation to use the same harness. Signed-off-by: Keir Fraser --- xen/arch/x86/hvm/io.c | 721 ++------------------------------------------------ 1 file changed, 16 insertions(+), 705 deletions(-) (limited to 'xen/arch/x86/hvm/io.c') diff --git a/xen/arch/x86/hvm/io.c b/xen/arch/x86/hvm/io.c index 178e6891c8..97ebfd0fff 100644 --- a/xen/arch/x86/hvm/io.c +++ b/xen/arch/x86/hvm/io.c @@ -46,379 +46,8 @@ #include #include -#if defined (__i386__) -static void set_reg_value (int size, int index, int seg, struct cpu_user_regs *regs, long value) -{ - switch (size) { - case BYTE: - switch (index) { - case 0: - regs->eax &= 0xFFFFFF00; - regs->eax |= (value & 0xFF); - break; - case 1: - regs->ecx &= 0xFFFFFF00; - regs->ecx |= (value & 0xFF); - break; - case 2: - regs->edx &= 0xFFFFFF00; - regs->edx |= (value & 0xFF); - break; - case 3: - regs->ebx &= 0xFFFFFF00; - regs->ebx |= (value & 0xFF); - break; - case 4: - regs->eax &= 0xFFFF00FF; - regs->eax |= ((value & 0xFF) << 8); - break; - case 5: - regs->ecx &= 0xFFFF00FF; - regs->ecx |= ((value & 0xFF) << 8); - break; - case 6: - regs->edx &= 0xFFFF00FF; - regs->edx |= ((value & 0xFF) << 8); - break; - case 7: - regs->ebx &= 0xFFFF00FF; - regs->ebx |= ((value & 0xFF) << 8); - break; - default: - goto crash; - } - break; - case WORD: - switch (index) { - case 0: - regs->eax &= 0xFFFF0000; - regs->eax |= (value & 0xFFFF); - break; - case 1: - regs->ecx &= 0xFFFF0000; - regs->ecx |= (value & 0xFFFF); - break; - case 2: - regs->edx &= 0xFFFF0000; - regs->edx |= (value & 0xFFFF); - break; - case 3: - regs->ebx &= 0xFFFF0000; - regs->ebx |= (value & 0xFFFF); - break; - case 4: - regs->esp &= 0xFFFF0000; - regs->esp |= (value & 0xFFFF); - break; - case 5: - regs->ebp &= 0xFFFF0000; - regs->ebp |= (value & 0xFFFF); - break; - case 6: - regs->esi &= 0xFFFF0000; - regs->esi |= (value & 0xFFFF); - break; - case 7: - regs->edi &= 0xFFFF0000; - regs->edi |= (value & 0xFFFF); - break; - default: - goto crash; - } - break; - case LONG: - switch (index) { - case 0: - regs->eax = value; - break; - case 1: - regs->ecx = value; - break; - case 2: - regs->edx = value; - break; - case 3: - regs->ebx = value; - break; - case 4: - regs->esp = value; - break; - case 5: - regs->ebp = value; - break; - case 6: - regs->esi = value; - break; - case 7: - regs->edi = value; - break; - default: - goto crash; - } - break; - default: - crash: - gdprintk(XENLOG_ERR, "size:%x, index:%x are invalid!\n", size, index); - domain_crash_synchronous(); - } -} -#else -static inline void __set_reg_value(unsigned long *reg, int size, long value) -{ - switch (size) { - case BYTE_64: - *reg &= ~0xFF; - *reg |= (value & 0xFF); - break; - case WORD: - *reg &= ~0xFFFF; - *reg |= (value & 0xFFFF); - break; - case LONG: - *reg &= ~0xFFFFFFFF; - *reg |= (value & 0xFFFFFFFF); - break; - case QUAD: - *reg = value; - break; - default: - gdprintk(XENLOG_ERR, "size:%x is invalid\n", size); - domain_crash_synchronous(); - } -} - -static void set_reg_value (int size, int index, int seg, struct cpu_user_regs *regs, long value) -{ - if (size == BYTE) { - switch (index) { - case 0: - regs->rax &= ~0xFF; - regs->rax |= (value & 0xFF); - break; - case 1: - regs->rcx &= ~0xFF; - regs->rcx |= (value & 0xFF); - break; - case 2: - regs->rdx &= ~0xFF; - regs->rdx |= (value & 0xFF); - break; - case 3: - regs->rbx &= ~0xFF; - regs->rbx |= (value & 0xFF); - break; - case 4: - regs->rax &= 0xFFFFFFFFFFFF00FF; - regs->rax |= ((value & 0xFF) << 8); - break; - case 5: - regs->rcx &= 0xFFFFFFFFFFFF00FF; - regs->rcx |= ((value & 0xFF) << 8); - break; - case 6: - regs->rdx &= 0xFFFFFFFFFFFF00FF; - regs->rdx |= ((value & 0xFF) << 8); - break; - case 7: - regs->rbx &= 0xFFFFFFFFFFFF00FF; - regs->rbx |= ((value & 0xFF) << 8); - break; - default: - gdprintk(XENLOG_ERR, "size:%x, index:%x are invalid!\n", - size, index); - domain_crash_synchronous(); - break; - } - return; - } - - switch (index) { - case 0: - __set_reg_value(®s->rax, size, value); - break; - case 1: - __set_reg_value(®s->rcx, size, value); - break; - case 2: - __set_reg_value(®s->rdx, size, value); - break; - case 3: - __set_reg_value(®s->rbx, size, value); - break; - case 4: - __set_reg_value(®s->rsp, size, value); - break; - case 5: - __set_reg_value(®s->rbp, size, value); - break; - case 6: - __set_reg_value(®s->rsi, size, value); - break; - case 7: - __set_reg_value(®s->rdi, size, value); - break; - case 8: - __set_reg_value(®s->r8, size, value); - break; - case 9: - __set_reg_value(®s->r9, size, value); - break; - case 10: - __set_reg_value(®s->r10, size, value); - break; - case 11: - __set_reg_value(®s->r11, size, value); - break; - case 12: - __set_reg_value(®s->r12, size, value); - break; - case 13: - __set_reg_value(®s->r13, size, value); - break; - case 14: - __set_reg_value(®s->r14, size, value); - break; - case 15: - __set_reg_value(®s->r15, size, value); - break; - default: - gdprintk(XENLOG_ERR, "Invalid index\n"); - domain_crash_synchronous(); - } - return; -} -#endif - -long get_reg_value(int size, int index, int seg, struct cpu_user_regs *regs); - -static inline void set_eflags_CF(int size, - unsigned int instr, - unsigned long result, - unsigned long src, - unsigned long dst, - struct cpu_user_regs *regs) -{ - unsigned long mask; - - if ( size == BYTE_64 ) - size = BYTE; - ASSERT((size <= sizeof(mask)) && (size > 0)); - - mask = ~0UL >> (8 * (sizeof(mask) - size)); - - if ( instr == INSTR_ADD ) - { - /* CF=1 <==> result is less than the augend and addend) */ - if ( (result & mask) < (dst & mask) ) - { - ASSERT((result & mask) < (src & mask)); - regs->eflags |= X86_EFLAGS_CF; - } - } - else - { - ASSERT( instr == INSTR_CMP || instr == INSTR_SUB ); - if ( (src & mask) > (dst & mask) ) - regs->eflags |= X86_EFLAGS_CF; - } -} - -static inline void set_eflags_OF(int size, - unsigned int instr, - unsigned long result, - unsigned long src, - unsigned long dst, - struct cpu_user_regs *regs) -{ - unsigned long mask; - - if ( size == BYTE_64 ) - size = BYTE; - ASSERT((size <= sizeof(mask)) && (size > 0)); - - mask = 1UL << ((8*size) - 1); - - if ( instr == INSTR_ADD ) - { - if ((src ^ result) & (dst ^ result) & mask); - regs->eflags |= X86_EFLAGS_OF; - } - else - { - ASSERT(instr == INSTR_CMP || instr == INSTR_SUB); - if ((dst ^ src) & (dst ^ result) & mask) - regs->eflags |= X86_EFLAGS_OF; - } -} - -static inline void set_eflags_AF(int size, - unsigned long result, - unsigned long src, - unsigned long dst, - struct cpu_user_regs *regs) -{ - if ((result ^ src ^ dst) & 0x10) - regs->eflags |= X86_EFLAGS_AF; -} - -static inline void set_eflags_ZF(int size, unsigned long result, - struct cpu_user_regs *regs) -{ - unsigned long mask; - - if ( size == BYTE_64 ) - size = BYTE; - ASSERT((size <= sizeof(mask)) && (size > 0)); - - mask = ~0UL >> (8 * (sizeof(mask) - size)); - - if ((result & mask) == 0) - regs->eflags |= X86_EFLAGS_ZF; -} - -static inline void set_eflags_SF(int size, unsigned long result, - struct cpu_user_regs *regs) -{ - unsigned long mask; - - if ( size == BYTE_64 ) - size = BYTE; - ASSERT((size <= sizeof(mask)) && (size > 0)); - - mask = 1UL << ((8*size) - 1); - - if (result & mask) - regs->eflags |= X86_EFLAGS_SF; -} - -static char parity_table[256] = { - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 -}; - -static inline void set_eflags_PF(int size, unsigned long result, - struct cpu_user_regs *regs) -{ - if (parity_table[result & 0xFF]) - regs->eflags |= X86_EFLAGS_PF; -} - -static void hvm_pio_assist(struct cpu_user_regs *regs, ioreq_t *p, - struct hvm_io_op *pio_opp) +static void hvm_pio_assist( + struct cpu_user_regs *regs, ioreq_t *p, struct hvm_io_op *pio_opp) { if ( p->data_is_ptr || (pio_opp->flags & OVERLAP) ) { @@ -472,335 +101,6 @@ static void hvm_pio_assist(struct cpu_user_regs *regs, ioreq_t *p, } } -static void hvm_mmio_assist(struct cpu_user_regs *regs, ioreq_t *p, - struct hvm_io_op *mmio_opp) -{ - int sign = p->df ? -1 : 1; - int size = -1, index = -1; - unsigned long value = 0, result = 0; - unsigned long src, dst; - - src = mmio_opp->operand[0]; - dst = mmio_opp->operand[1]; - size = operand_size(src); - - HVMTRACE_1D(MMIO_ASSIST, current, p->data); - - switch (mmio_opp->instr) { - case INSTR_MOV: - if (dst & REGISTER) { - index = operand_index(dst); - set_reg_value(size, index, 0, regs, p->data); - } - break; - - case INSTR_MOVZX: - if (dst & REGISTER) { - switch (size) { - case BYTE: - p->data &= 0xFFULL; - break; - - case WORD: - p->data &= 0xFFFFULL; - break; - - case LONG: - p->data &= 0xFFFFFFFFULL; - break; - - default: - printk("Impossible source operand size of movzx instr: %d\n", size); - domain_crash_synchronous(); - } - index = operand_index(dst); - set_reg_value(operand_size(dst), index, 0, regs, p->data); - } - break; - - case INSTR_MOVSX: - if (dst & REGISTER) { - switch (size) { - case BYTE: - p->data &= 0xFFULL; - if ( p->data & 0x80ULL ) - p->data |= 0xFFFFFFFFFFFFFF00ULL; - break; - - case WORD: - p->data &= 0xFFFFULL; - if ( p->data & 0x8000ULL ) - p->data |= 0xFFFFFFFFFFFF0000ULL; - break; - - case LONG: - p->data &= 0xFFFFFFFFULL; - if ( p->data & 0x80000000ULL ) - p->data |= 0xFFFFFFFF00000000ULL; - break; - - default: - printk("Impossible source operand size of movsx instr: %d\n", size); - domain_crash_synchronous(); - } - index = operand_index(dst); - set_reg_value(operand_size(dst), index, 0, regs, p->data); - } - break; - - case INSTR_MOVS: - sign = p->df ? -1 : 1; - - if (mmio_opp->flags & REPZ) - regs->ecx -= p->count; - - if ((mmio_opp->flags & OVERLAP) && p->dir == IOREQ_READ) { - unsigned long addr = mmio_opp->addr; - - if (hvm_paging_enabled(current)) - { - int rv = hvm_copy_to_guest_virt(addr, &p->data, p->size); - if ( rv == HVMCOPY_bad_gva_to_gfn ) - return; /* exception already injected */ - } - else - (void)hvm_copy_to_guest_phys(addr, &p->data, p->size); - } - - regs->esi += sign * p->count * p->size; - regs->edi += sign * p->count * p->size; - - break; - - case INSTR_STOS: - sign = p->df ? -1 : 1; - regs->edi += sign * p->count * p->size; - if (mmio_opp->flags & REPZ) - regs->ecx -= p->count; - break; - - case INSTR_LODS: - set_reg_value(size, 0, 0, regs, p->data); - sign = p->df ? -1 : 1; - regs->esi += sign * p->count * p->size; - if (mmio_opp->flags & REPZ) - regs->ecx -= p->count; - break; - - case INSTR_AND: - if (src & REGISTER) { - index = operand_index(src); - value = get_reg_value(size, index, 0, regs); - result = (unsigned long) p->data & value; - } else if (src & IMMEDIATE) { - value = mmio_opp->immediate; - result = (unsigned long) p->data & value; - } else if (src & MEMORY) { - index = operand_index(dst); - value = get_reg_value(size, index, 0, regs); - result = (unsigned long) p->data & value; - set_reg_value(size, index, 0, regs, result); - } - - /* - * The OF and CF flags are cleared; the SF, ZF, and PF - * flags are set according to the result. The state of - * the AF flag is undefined. - */ - regs->eflags &= ~(X86_EFLAGS_CF|X86_EFLAGS_PF| - X86_EFLAGS_ZF|X86_EFLAGS_SF|X86_EFLAGS_OF); - set_eflags_ZF(size, result, regs); - set_eflags_SF(size, result, regs); - set_eflags_PF(size, result, regs); - break; - - case INSTR_ADD: - if (src & REGISTER) { - index = operand_index(src); - value = get_reg_value(size, index, 0, regs); - result = (unsigned long) p->data + value; - } else if (src & IMMEDIATE) { - value = mmio_opp->immediate; - result = (unsigned long) p->data + value; - } else if (src & MEMORY) { - index = operand_index(dst); - value = get_reg_value(size, index, 0, regs); - result = (unsigned long) p->data + value; - set_reg_value(size, index, 0, regs, result); - } - - /* - * The CF, OF, SF, ZF, AF, and PF flags are set according - * to the result - */ - regs->eflags &= ~(X86_EFLAGS_CF|X86_EFLAGS_PF|X86_EFLAGS_AF| - X86_EFLAGS_ZF|X86_EFLAGS_SF|X86_EFLAGS_OF); - set_eflags_CF(size, mmio_opp->instr, result, value, - (unsigned long) p->data, regs); - set_eflags_OF(size, mmio_opp->instr, result, value, - (unsigned long) p->data, regs); - set_eflags_AF(size, result, value, (unsigned long) p->data, regs); - set_eflags_ZF(size, result, regs); - set_eflags_SF(size, result, regs); - set_eflags_PF(size, result, regs); - break; - - case INSTR_OR: - if (src & REGISTER) { - index = operand_index(src); - value = get_reg_value(size, index, 0, regs); - result = (unsigned long) p->data | value; - } else if (src & IMMEDIATE) { - value = mmio_opp->immediate; - result = (unsigned long) p->data | value; - } else if (src & MEMORY) { - index = operand_index(dst); - value = get_reg_value(size, index, 0, regs); - result = (unsigned long) p->data | value; - set_reg_value(size, index, 0, regs, result); - } - - /* - * The OF and CF flags are cleared; the SF, ZF, and PF - * flags are set according to the result. The state of - * the AF flag is undefined. - */ - regs->eflags &= ~(X86_EFLAGS_CF|X86_EFLAGS_PF| - X86_EFLAGS_ZF|X86_EFLAGS_SF|X86_EFLAGS_OF); - set_eflags_ZF(size, result, regs); - set_eflags_SF(size, result, regs); - set_eflags_PF(size, result, regs); - break; - - case INSTR_XOR: - if (src & REGISTER) { - index = operand_index(src); - value = get_reg_value(size, index, 0, regs); - result = (unsigned long) p->data ^ value; - } else if (src & IMMEDIATE) { - value = mmio_opp->immediate; - result = (unsigned long) p->data ^ value; - } else if (src & MEMORY) { - index = operand_index(dst); - value = get_reg_value(size, index, 0, regs); - result = (unsigned long) p->data ^ value; - set_reg_value(size, index, 0, regs, result); - } - - /* - * The OF and CF flags are cleared; the SF, ZF, and PF - * flags are set according to the result. The state of - * the AF flag is undefined. - */ - regs->eflags &= ~(X86_EFLAGS_CF|X86_EFLAGS_PF| - X86_EFLAGS_ZF|X86_EFLAGS_SF|X86_EFLAGS_OF); - set_eflags_ZF(size, result, regs); - set_eflags_SF(size, result, regs); - set_eflags_PF(size, result, regs); - break; - - case INSTR_CMP: - case INSTR_SUB: - if (src & REGISTER) { - index = operand_index(src); - value = get_reg_value(size, index, 0, regs); - result = (unsigned long) p->data - value; - } else if (src & IMMEDIATE) { - value = mmio_opp->immediate; - result = (unsigned long) p->data - value; - } else if (src & MEMORY) { - index = operand_index(dst); - value = get_reg_value(size, index, 0, regs); - result = value - (unsigned long) p->data; - if ( mmio_opp->instr == INSTR_SUB ) - set_reg_value(size, index, 0, regs, result); - } - - /* - * The CF, OF, SF, ZF, AF, and PF flags are set according - * to the result - */ - regs->eflags &= ~(X86_EFLAGS_CF|X86_EFLAGS_PF|X86_EFLAGS_AF| - X86_EFLAGS_ZF|X86_EFLAGS_SF|X86_EFLAGS_OF); - if ( src & (REGISTER | IMMEDIATE) ) - { - set_eflags_CF(size, mmio_opp->instr, result, value, - (unsigned long) p->data, regs); - set_eflags_OF(size, mmio_opp->instr, result, value, - (unsigned long) p->data, regs); - } - else - { - set_eflags_CF(size, mmio_opp->instr, result, - (unsigned long) p->data, value, regs); - set_eflags_OF(size, mmio_opp->instr, result, - (unsigned long) p->data, value, regs); - } - set_eflags_AF(size, result, value, (unsigned long) p->data, regs); - set_eflags_ZF(size, result, regs); - set_eflags_SF(size, result, regs); - set_eflags_PF(size, result, regs); - break; - - case INSTR_TEST: - if (src & REGISTER) { - index = operand_index(src); - value = get_reg_value(size, index, 0, regs); - } else if (src & IMMEDIATE) { - value = mmio_opp->immediate; - } else if (src & MEMORY) { - index = operand_index(dst); - value = get_reg_value(size, index, 0, regs); - } - result = (unsigned long) p->data & value; - - /* - * Sets the SF, ZF, and PF status flags. CF and OF are set to 0 - */ - regs->eflags &= ~(X86_EFLAGS_CF|X86_EFLAGS_PF| - X86_EFLAGS_ZF|X86_EFLAGS_SF|X86_EFLAGS_OF); - set_eflags_ZF(size, result, regs); - set_eflags_SF(size, result, regs); - set_eflags_PF(size, result, regs); - break; - - case INSTR_BT: - if ( src & REGISTER ) - { - index = operand_index(src); - value = get_reg_value(size, index, 0, regs); - } - else if ( src & IMMEDIATE ) - value = mmio_opp->immediate; - if (p->data & (1 << (value & ((1 << 5) - 1)))) - regs->eflags |= X86_EFLAGS_CF; - else - regs->eflags &= ~X86_EFLAGS_CF; - - break; - - case INSTR_XCHG: - if (src & REGISTER) { - index = operand_index(src); - set_reg_value(size, index, 0, regs, p->data); - } else { - index = operand_index(dst); - set_reg_value(size, index, 0, regs, p->data); - } - break; - - case INSTR_PUSH: - mmio_opp->addr += hvm_get_segment_base(current, x86_seg_ss); - { - unsigned long addr = mmio_opp->addr; - int rv = hvm_copy_to_guest_virt(addr, &p->data, size); - if ( rv == HVMCOPY_bad_gva_to_gfn ) - return; /* exception already injected */ - } - break; - } -} - void hvm_io_assist(void) { vcpu_iodata_t *vio; @@ -825,8 +125,18 @@ void hvm_io_assist(void) p->state = STATE_IOREQ_NONE; - if ( v->arch.hvm_vcpu.io_complete && v->arch.hvm_vcpu.io_complete() ) + if ( v->arch.hvm_vcpu.io_in_progress ) + { + v->arch.hvm_vcpu.io_in_progress = 0; + if ( p->dir == IOREQ_READ ) + { + v->arch.hvm_vcpu.io_completed = 1; + v->arch.hvm_vcpu.io_data = p->data; + } + if ( v->arch.hvm_vcpu.mmio_in_progress ) + (void)handle_mmio(); goto out; + } switch ( p->type ) { @@ -836,8 +146,9 @@ void hvm_io_assist(void) hvm_pio_assist(regs, p, io_opp); break; default: - hvm_mmio_assist(regs, p, io_opp); - break; + gdprintk(XENLOG_ERR, "Unexpected HVM iorequest state %d.\n", p->state); + domain_crash(v->domain); + goto out; } /* Copy register changes back into current guest state. */ -- cgit v1.2.3