aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--xen/arch/x86/hvm/vmx/realmode.c168
-rw-r--r--xen/arch/x86/x86_emulate.c46
-rw-r--r--xen/include/asm-x86/x86_emulate.h11
3 files changed, 167 insertions, 58 deletions
diff --git a/xen/arch/x86/hvm/vmx/realmode.c b/xen/arch/x86/hvm/vmx/realmode.c
index 9a95878694..1de0b372f6 100644
--- a/xen/arch/x86/hvm/vmx/realmode.c
+++ b/xen/arch/x86/hvm/vmx/realmode.c
@@ -13,6 +13,7 @@
#include <xen/init.h>
#include <xen/lib.h>
#include <xen/sched.h>
+#include <asm/event.h>
#include <asm/hvm/hvm.h>
#include <asm/hvm/support.h>
#include <asm/hvm/vmx/vmx.h>
@@ -23,35 +24,78 @@
struct realmode_emulate_ctxt {
struct x86_emulate_ctxt ctxt;
- /* Cache of up to 31 bytes of instruction. */
- uint8_t insn_buf[31];
- uint8_t insn_buf_bytes;
+ /* Cache of 16 bytes of instruction. */
+ uint8_t insn_buf[16];
unsigned long insn_buf_eip;
struct segment_register seg_reg[10];
};
-static int realmode_translate_linear_addr(
- enum x86_segment seg,
- unsigned long offset,
- unsigned int bytes,
- enum hvm_access_type access_type,
- struct realmode_emulate_ctxt *rm_ctxt,
- unsigned long *paddr)
+static void realmode_deliver_exception(
+ unsigned int vector,
+ unsigned int insn_len,
+ struct realmode_emulate_ctxt *rm_ctxt)
{
- struct segment_register *reg = &rm_ctxt->seg_reg[seg];
- int okay;
+ struct segment_register *idtr = &rm_ctxt->seg_reg[x86_seg_idtr];
+ struct segment_register *csr = &rm_ctxt->seg_reg[x86_seg_cs];
+ struct cpu_user_regs *regs = rm_ctxt->ctxt.regs;
+ uint32_t cs_eip, pstk;
+ uint16_t frame[3];
+ unsigned int last_byte;
+
+ again:
+ last_byte = (vector * 4) + 3;
+ if ( idtr->limit < last_byte )
+ {
+ /* Software interrupt? */
+ if ( insn_len != 0 )
+ {
+ insn_len = 0;
+ vector = TRAP_gp_fault;
+ goto again;
+ }
+
+ /* Exception or hardware interrupt. */
+ switch ( vector )
+ {
+ case TRAP_double_fault:
+ hvm_triple_fault();
+ return;
+ case TRAP_gp_fault:
+ vector = TRAP_double_fault;
+ goto again;
+ default:
+ vector = TRAP_gp_fault;
+ goto again;
+ }
+ }
+
+ (void)hvm_copy_from_guest_phys(&cs_eip, idtr->base + vector * 4, 4);
- okay = hvm_virtual_to_linear_addr(
- seg, reg, offset, bytes, access_type, rm_ctxt->ctxt.addr_size, paddr);
+ frame[0] = regs->eip + insn_len;
+ frame[1] = csr->sel;
+ frame[2] = regs->eflags & ~X86_EFLAGS_RF;
- if ( !okay )
+ if ( rm_ctxt->ctxt.addr_size == 32 )
+ {
+ regs->esp -= 4;
+ pstk = regs->esp;
+ }
+ else
{
- hvm_inject_exception(TRAP_gp_fault, 0, 0);
- return X86EMUL_EXCEPTION;
+ pstk = (uint16_t)(regs->esp - 4);
+ regs->esp &= ~0xffff;
+ regs->esp |= pstk;
}
- return 0;
+ pstk += rm_ctxt->seg_reg[x86_seg_ss].base;
+ (void)hvm_copy_to_guest_phys(pstk, frame, sizeof(frame));
+
+ csr->sel = cs_eip >> 16;
+ csr->base = (uint32_t)csr->sel << 4;
+ regs->eip = (uint16_t)cs_eip;
+ regs->eflags &= ~(X86_EFLAGS_AC | X86_EFLAGS_TF |
+ X86_EFLAGS_AC | X86_EFLAGS_RF);
}
static int
@@ -63,14 +107,7 @@ realmode_read(
enum hvm_access_type access_type,
struct realmode_emulate_ctxt *rm_ctxt)
{
- unsigned long addr;
- int rc;
-
- rc = realmode_translate_linear_addr(
- seg, offset, bytes, access_type, rm_ctxt, &addr);
- if ( rc )
- return rc;
-
+ uint32_t addr = rm_ctxt->seg_reg[seg].base + offset;
*val = 0;
(void)hvm_copy_from_guest_phys(val, addr, bytes);
return X86EMUL_OKAY;
@@ -102,7 +139,7 @@ realmode_emulate_insn_fetch(
unsigned int insn_off = offset - rm_ctxt->insn_buf_eip;
/* Fall back if requested bytes are not in the prefetch cache. */
- if ( unlikely((insn_off + bytes) > rm_ctxt->insn_buf_bytes) )
+ if ( unlikely((insn_off + bytes) > sizeof(rm_ctxt->insn_buf)) )
return realmode_read(
seg, offset, val, bytes,
hvm_access_insn_fetch, rm_ctxt);
@@ -123,14 +160,7 @@ realmode_emulate_write(
{
struct realmode_emulate_ctxt *rm_ctxt =
container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
- unsigned long addr;
- int rc;
-
- rc = realmode_translate_linear_addr(
- seg, offset, bytes, hvm_access_write, rm_ctxt, &addr);
- if ( rc )
- return rc;
-
+ uint32_t addr = rm_ctxt->seg_reg[seg].base + offset;
(void)hvm_copy_to_guest_phys(addr, &val, bytes);
return X86EMUL_OKAY;
}
@@ -258,6 +288,31 @@ static int realmode_write_rflags(
return X86EMUL_OKAY;
}
+static int realmode_inject_hw_exception(
+ uint8_t vector,
+ struct x86_emulate_ctxt *ctxt)
+{
+ struct realmode_emulate_ctxt *rm_ctxt =
+ container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
+
+ realmode_deliver_exception(vector, 0, rm_ctxt);
+
+ return X86EMUL_OKAY;
+}
+
+static int realmode_inject_sw_interrupt(
+ uint8_t vector,
+ uint8_t insn_len,
+ struct x86_emulate_ctxt *ctxt)
+{
+ struct realmode_emulate_ctxt *rm_ctxt =
+ container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
+
+ realmode_deliver_exception(vector, insn_len, rm_ctxt);
+
+ return X86EMUL_OKAY;
+}
+
static struct x86_emulate_ops realmode_emulator_ops = {
.read = realmode_emulate_read,
.insn_fetch = realmode_emulate_insn_fetch,
@@ -268,37 +323,44 @@ static struct x86_emulate_ops realmode_emulator_ops = {
.read_io = realmode_read_io,
.write_io = realmode_write_io,
.read_cr = realmode_read_cr,
- .write_rflags = realmode_write_rflags
+ .write_rflags = realmode_write_rflags,
+ .inject_hw_exception = realmode_inject_hw_exception,
+ .inject_sw_interrupt = realmode_inject_sw_interrupt
};
int vmx_realmode(struct cpu_user_regs *regs)
{
struct vcpu *curr = current;
struct realmode_emulate_ctxt rm_ctxt;
- unsigned long addr;
+ unsigned long intr_info;
int i, rc = 0;
+ rm_ctxt.ctxt.regs = regs;
+
for ( i = 0; i < 10; i++ )
hvm_get_segment_register(curr, i, &rm_ctxt.seg_reg[i]);
- while ( !(curr->arch.hvm_vcpu.guest_cr[0] & X86_CR0_PE) &&
- !softirq_pending(smp_processor_id()) )
+ rm_ctxt.ctxt.addr_size =
+ rm_ctxt.seg_reg[x86_seg_cs].attr.fields.db ? 32 : 16;
+ rm_ctxt.ctxt.sp_size =
+ rm_ctxt.seg_reg[x86_seg_ss].attr.fields.db ? 32 : 16;
+
+ intr_info = __vmread(VM_ENTRY_INTR_INFO);
+ if ( intr_info & INTR_INFO_VALID_MASK )
{
- rm_ctxt.ctxt.regs = regs;
- rm_ctxt.ctxt.addr_size =
- rm_ctxt.seg_reg[x86_seg_cs].attr.fields.db ? 32 : 16;
- rm_ctxt.ctxt.sp_size =
- rm_ctxt.seg_reg[x86_seg_ss].attr.fields.db ? 32 : 16;
+ __vmwrite(VM_ENTRY_INTR_INFO, 0);
+ realmode_deliver_exception((uint8_t)intr_info, 0, &rm_ctxt);
+ }
+ while ( !(curr->arch.hvm_vcpu.guest_cr[0] & X86_CR0_PE) &&
+ !softirq_pending(smp_processor_id()) &&
+ !hvm_local_events_need_delivery(curr) )
+ {
rm_ctxt.insn_buf_eip = regs->eip;
- rm_ctxt.insn_buf_bytes =
- (hvm_virtual_to_linear_addr(
- x86_seg_cs, &rm_ctxt.seg_reg[x86_seg_cs],
- regs->eip, sizeof(rm_ctxt.insn_buf),
- hvm_access_insn_fetch, rm_ctxt.ctxt.addr_size, &addr) &&
- !hvm_copy_from_guest_virt(
- rm_ctxt.insn_buf, addr, sizeof(rm_ctxt.insn_buf)))
- ? sizeof(rm_ctxt.insn_buf) : 0;
+ (void)hvm_copy_from_guest_phys(
+ rm_ctxt.insn_buf,
+ (uint32_t)(rm_ctxt.seg_reg[x86_seg_cs].base + regs->eip),
+ sizeof(rm_ctxt.insn_buf));
rc = x86_emulate(&rm_ctxt.ctxt, &realmode_emulator_ops);
@@ -308,7 +370,7 @@ int vmx_realmode(struct cpu_user_regs *regs)
break;
}
- if ( rc )
+ if ( rc == X86EMUL_UNHANDLEABLE )
{
gdprintk(XENLOG_DEBUG,
"RM %04x:%08lx: %02x %02x %02x %02x %02x %02x\n",
diff --git a/xen/arch/x86/x86_emulate.c b/xen/arch/x86/x86_emulate.c
index 54ea38f581..60b00eebe1 100644
--- a/xen/arch/x86/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate.c
@@ -149,7 +149,7 @@ static uint8_t opcode_table[256] = {
ImplicitOps, ImplicitOps,
0, 0, ByteOp|DstMem|SrcImm|ModRM|Mov, DstMem|SrcImm|ModRM|Mov,
/* 0xC8 - 0xCF */
- 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, ImplicitOps, ImplicitOps, ImplicitOps, 0,
/* 0xD0 - 0xD7 */
ByteOp|DstMem|SrcImplicit|ModRM, DstMem|SrcImplicit|ModRM,
ByteOp|DstMem|SrcImplicit|ModRM, DstMem|SrcImplicit|ModRM,
@@ -163,7 +163,7 @@ static uint8_t opcode_table[256] = {
ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
/* 0xF0 - 0xF7 */
- 0, 0, 0, 0,
+ 0, ImplicitOps, 0, 0,
0, ImplicitOps, ByteOp|DstMem|SrcNone|ModRM, DstMem|SrcNone|ModRM,
/* 0xF8 - 0xFF */
ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
@@ -270,6 +270,7 @@ struct operand {
#define EFLG_OF (1<<11)
#define EFLG_DF (1<<10)
#define EFLG_IF (1<<9)
+#define EFLG_TF (1<<8)
#define EFLG_SF (1<<7)
#define EFLG_ZF (1<<6)
#define EFLG_AF (1<<4)
@@ -278,6 +279,9 @@ struct operand {
/* Exception definitions. */
#define EXC_DE 0
+#define EXC_DB 1
+#define EXC_BP 3
+#define EXC_OF 4
#define EXC_BR 5
#define EXC_UD 6
#define EXC_GP 13
@@ -477,8 +481,13 @@ do { \
if ( rc ) goto done; \
} while (0)
-/* In future we will be able to generate arbitrary exceptions. */
-#define generate_exception_if(p, e) fail_if(p)
+#define generate_exception_if(p, e) \
+({ if ( (p) ) { \
+ fail_if(ops->inject_hw_exception == NULL); \
+ rc = ops->inject_hw_exception(e, ctxt) ? : X86EMUL_EXCEPTION; \
+ goto done; \
+ } \
+})
/* Given byte has even parity (even number of 1s)? */
static int even_parity(uint8_t v)
@@ -1771,7 +1780,11 @@ x86_emulate(
/* Commit shadow register state. */
_regs.eflags &= ~EFLG_RF;
*ctxt->regs = _regs;
- /* FIXME generate_exception_if(_regs.eflags & EFLG_TF, EXC_DB); */
+
+ if ( (_regs.eflags & EFLG_TF) &&
+ (rc == X86EMUL_OKAY) &&
+ (ops->inject_hw_exception != NULL) )
+ rc = ops->inject_hw_exception(EXC_DB, ctxt) ? : X86EMUL_EXCEPTION;
done:
return rc;
@@ -2152,6 +2165,25 @@ x86_emulate(
break;
}
+ case 0xcc: /* int3 */
+ src.val = EXC_BP;
+ goto swint;
+
+ case 0xcd: /* int imm8 */
+ src.val = insn_fetch_type(uint8_t);
+ swint:
+ fail_if(ops->inject_sw_interrupt == NULL);
+ rc = ops->inject_sw_interrupt(src.val, _regs.eip - ctxt->regs->eip,
+ ctxt) ? : X86EMUL_EXCEPTION;
+ goto done;
+
+ case 0xce: /* into */
+ generate_exception_if(mode_64bit(), EXC_UD);
+ if ( !(_regs.eflags & EFLG_OF) )
+ break;
+ src.val = EXC_OF;
+ goto swint;
+
case 0xd4: /* aam */ {
unsigned int base = insn_fetch_type(uint8_t);
uint8_t al = _regs.eax;
@@ -2292,6 +2324,10 @@ x86_emulate(
jmp_rel(insn_fetch_type(int8_t));
break;
+ case 0xf1: /* int1 (icebp) */
+ src.val = EXC_DB;
+ goto swint;
+
case 0xf5: /* cmc */
_regs.eflags ^= EFLG_CF;
break;
diff --git a/xen/include/asm-x86/x86_emulate.h b/xen/include/asm-x86/x86_emulate.h
index b11ec957fd..eab9a513ae 100644
--- a/xen/include/asm-x86/x86_emulate.h
+++ b/xen/include/asm-x86/x86_emulate.h
@@ -274,6 +274,17 @@ struct x86_emulate_ops
/* wbinvd: Write-back and invalidate cache contents. */
int (*wbinvd)(
struct x86_emulate_ctxt *ctxt);
+
+ /* inject_hw_exception */
+ int (*inject_hw_exception)(
+ uint8_t vector,
+ struct x86_emulate_ctxt *ctxt);
+
+ /* inject_sw_interrupt */
+ int (*inject_sw_interrupt)(
+ uint8_t vector,
+ uint8_t insn_len,
+ struct x86_emulate_ctxt *ctxt);
};
struct cpu_user_regs;