diff options
author | Keir Fraser <keir.fraser@citrix.com> | 2008-02-05 15:45:10 +0000 |
---|---|---|
committer | Keir Fraser <keir.fraser@citrix.com> | 2008-02-05 15:45:10 +0000 |
commit | 1118c5715f51800714f1ff1b62fc6382e1486f05 (patch) | |
tree | 4f2fd8aa1ca57d2c5733da62812ab6e42e53876f /xen/arch/x86/x86_emulate.c | |
parent | ae9093a33f56a3f3140aa96c04acc0049228afc3 (diff) | |
download | xen-1118c5715f51800714f1ff1b62fc6382e1486f05.tar.gz xen-1118c5715f51800714f1ff1b62fc6382e1486f05.tar.bz2 xen-1118c5715f51800714f1ff1b62fc6382e1486f05.zip |
vmx realmode: Emulate protected-mode transition while CS and SS have
bad selector values (bottom two bits non-zero).
Allows opensuse 10.3 install CD to boot. Unfortunately SUSE Linux 10.1
install CD still fails to work...
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
Diffstat (limited to 'xen/arch/x86/x86_emulate.c')
-rw-r--r-- | xen/arch/x86/x86_emulate.c | 167 |
1 files changed, 154 insertions, 13 deletions
diff --git a/xen/arch/x86/x86_emulate.c b/xen/arch/x86/x86_emulate.c index e559e3c91a..bf867a7b56 100644 --- a/xen/arch/x86/x86_emulate.c +++ b/xen/arch/x86/x86_emulate.c @@ -303,7 +303,11 @@ struct operand { #define EXC_OF 4 #define EXC_BR 5 #define EXC_UD 6 +#define EXC_TS 10 +#define EXC_NP 11 +#define EXC_SS 12 #define EXC_GP 13 +#define EXC_PF 14 /* * Instruction emulation: @@ -500,12 +504,12 @@ do { \ if ( rc ) goto done; \ } while (0) -#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; \ - } \ +#define generate_exception_if(p, e) \ +({ if ( (p) ) { \ + fail_if(ops->inject_hw_exception == NULL); \ + rc = ops->inject_hw_exception(e, 0, ctxt) ? : X86EMUL_EXCEPTION; \ + goto done; \ + } \ }) /* @@ -774,7 +778,7 @@ in_realmode( } static int -load_seg( +realmode_load_seg( enum x86_segment seg, uint16_t sel, struct x86_emulate_ctxt *ctxt, @@ -783,11 +787,6 @@ load_seg( struct segment_register reg; int rc; - if ( !in_realmode(ctxt, ops) || - (ops->read_segment == NULL) || - (ops->write_segment == NULL) ) - return X86EMUL_UNHANDLEABLE; - if ( (rc = ops->read_segment(seg, ®, ctxt)) != 0 ) return rc; @@ -797,6 +796,148 @@ load_seg( return ops->write_segment(seg, ®, ctxt); } +static int +protmode_load_seg( + enum x86_segment seg, + uint16_t sel, + struct x86_emulate_ctxt *ctxt, + struct x86_emulate_ops *ops) +{ + struct segment_register desctab, cs, segr; + struct { uint32_t a, b; } desc; + unsigned long val; + uint8_t dpl, rpl, cpl; + int rc, fault_type = EXC_TS; + + /* NULL selector? */ + if ( (sel & 0xfffc) == 0 ) + { + if ( (seg == x86_seg_cs) || (seg == x86_seg_ss) ) + goto raise_exn; + memset(&segr, 0, sizeof(segr)); + return ops->write_segment(seg, &segr, ctxt); + } + + /* LDT descriptor must be in the GDT. */ + if ( (seg == x86_seg_ldtr) && (sel & 4) ) + goto raise_exn; + + if ( (rc = ops->read_segment(x86_seg_cs, &cs, ctxt)) || + (rc = ops->read_segment((sel & 4) ? x86_seg_ldtr : x86_seg_gdtr, + &desctab, ctxt)) ) + return rc; + + /* Check against descriptor table limit. */ + if ( ((sel & 0xfff8) + 7) > desctab.limit ) + goto raise_exn; + + do { + if ( (rc = ops->read(x86_seg_none, desctab.base + (sel & 0xfff8), + &val, 4, ctxt)) ) + return rc; + desc.a = val; + if ( (rc = ops->read(x86_seg_none, desctab.base + (sel & 0xfff8) + 4, + &val, 4, ctxt)) ) + return rc; + desc.b = val; + + /* Segment present in memory? */ + if ( !(desc.b & (1u<<15)) ) + { + fault_type = EXC_NP; + goto raise_exn; + } + + /* LDT descriptor is a system segment. All others are code/data. */ + if ( (desc.b & (1u<<12)) == ((seg == x86_seg_ldtr) << 12) ) + goto raise_exn; + + dpl = (desc.b >> 13) & 3; + rpl = sel & 3; + cpl = cs.sel & 3; + + switch ( seg ) + { + case x86_seg_cs: + /* Code segment? */ + if ( !(desc.b & (1u<<11)) ) + goto raise_exn; + /* Non-conforming segment: check DPL against RPL. */ + if ( ((desc.b & (6u<<9)) != 6) && (dpl != rpl) ) + goto raise_exn; + break; + case x86_seg_ss: + /* Writable data segment? */ + if ( (desc.b & (5u<<9)) != (1u<<9) ) + goto raise_exn; + if ( (dpl != cpl) || (dpl != rpl) ) + goto raise_exn; + break; + case x86_seg_ldtr: + /* LDT system segment? */ + if ( (desc.b & (15u<<8)) != (2u<<8) ) + goto raise_exn; + goto skip_accessed_flag; + default: + /* Readable code or data segment? */ + if ( (desc.b & (5u<<9)) == (4u<<9) ) + goto raise_exn; + /* Non-conforming segment: check DPL against RPL and CPL. */ + if ( ((desc.b & (6u<<9)) != 6) && ((dpl < cpl) || (dpl < rpl)) ) + goto raise_exn; + break; + } + + /* Ensure Accessed flag is set. */ + rc = ((desc.b & 0x100) ? X86EMUL_OKAY : + ops->cmpxchg( + x86_seg_none, desctab.base + (sel & 0xfff8) + 4, desc.b, + desc.b | 0x100, 4, ctxt)); + } while ( rc == X86EMUL_CMPXCHG_FAILED ); + + if ( rc ) + return rc; + + /* Force the Accessed flag in our local copy. */ + desc.b |= 0x100; + + skip_accessed_flag: + segr.base = (((desc.b << 0) & 0xff000000u) | + ((desc.b << 16) & 0x00ff0000u) | + ((desc.a >> 16) & 0x0000ffffu)); + segr.attr.bytes = (((desc.b >> 8) & 0x00ffu) | + ((desc.b >> 12) & 0x0f00u)); + segr.limit = (desc.b & 0x000f0000u) | (desc.a & 0x0000ffffu); + if ( segr.attr.fields.g ) + segr.limit = (segr.limit << 12) | 0xfffu; + segr.sel = sel; + return ops->write_segment(seg, &segr, ctxt); + + raise_exn: + if ( ops->inject_hw_exception == NULL ) + return X86EMUL_UNHANDLEABLE; + if ( (rc = ops->inject_hw_exception(fault_type, sel & 0xfffc, ctxt)) ) + return rc; + return X86EMUL_EXCEPTION; +} + +static int +load_seg( + enum x86_segment seg, + uint16_t sel, + struct x86_emulate_ctxt *ctxt, + struct x86_emulate_ops *ops) +{ + if ( (ops->read_segment == NULL) || + (ops->write_segment == NULL) ) + return X86EMUL_UNHANDLEABLE; + + if ( in_realmode(ctxt, ops) ) + return realmode_load_seg(seg, sel, ctxt, ops); + + return protmode_load_seg(seg, sel, ctxt, ops); +} + void * decode_register( uint8_t modrm_reg, struct cpu_user_regs *regs, int highbyte_regs) @@ -1858,7 +1999,7 @@ x86_emulate( if ( (_regs.eflags & EFLG_TF) && (rc == X86EMUL_OKAY) && (ops->inject_hw_exception != NULL) ) - rc = ops->inject_hw_exception(EXC_DB, ctxt) ? : X86EMUL_EXCEPTION; + rc = ops->inject_hw_exception(EXC_DB, 0, ctxt) ? : X86EMUL_EXCEPTION; done: return rc; |