x86_emulate.c
来自「xen虚拟机源代码安装包」· C语言 代码 · 共 1,781 行 · 第 1/5 页
C
1,781 行
: "=a" (m[0]), "=d" (m[1]), "=q" (rc) : "0" (m[0]), "1" (m[1]), "2" (0) ); return rc;}/* * Signed multiplication with double-word result. * IN: Multiplicand=m[0], Multiplier=m[1] * OUT: Return CF/OF (overflow status); Result=m[1]:m[0] */static int imul_dbl(unsigned long m[2]){ int rc; asm ( "imul %4; seto %b2" : "=a" (m[0]), "=d" (m[1]), "=q" (rc) : "0" (m[0]), "1" (m[1]), "2" (0) ); return rc;}/* * Unsigned division of double-word dividend. * IN: Dividend=u[1]:u[0], Divisor=v * OUT: Return 1: #DE * Return 0: Quotient=u[0], Remainder=u[1] */static int div_dbl(unsigned long u[2], unsigned long v){ if ( (v == 0) || (u[1] >= v) ) return 1; asm ( "div %4" : "=a" (u[0]), "=d" (u[1]) : "0" (u[0]), "1" (u[1]), "r" (v) ); return 0;}/* * Signed division of double-word dividend. * IN: Dividend=u[1]:u[0], Divisor=v * OUT: Return 1: #DE * Return 0: Quotient=u[0], Remainder=u[1] * NB. We don't use idiv directly as it's moderately hard to work out * ahead of time whether it will #DE, which we cannot allow to happen. */static int idiv_dbl(unsigned long u[2], unsigned long v){ int negu = (long)u[1] < 0, negv = (long)v < 0; /* u = abs(u) */ if ( negu ) { u[1] = ~u[1]; if ( (u[0] = -u[0]) == 0 ) u[1]++; } /* abs(u) / abs(v) */ if ( div_dbl(u, negv ? -v : v) ) return 1; /* Remainder has same sign as dividend. It cannot overflow. */ if ( negu ) u[1] = -u[1]; /* Quotient is overflowed if sign bit is set. */ if ( negu ^ negv ) { if ( (long)u[0] >= 0 ) u[0] = -u[0]; else if ( (u[0] << 1) != 0 ) /* == 0x80...0 is okay */ return 1; } else if ( (long)u[0] < 0 ) return 1; return 0;}static inttest_cc( unsigned int condition, unsigned int flags){ int rc = 0; switch ( (condition & 15) >> 1 ) { case 0: /* o */ rc |= (flags & EFLG_OF); break; case 1: /* b/c/nae */ rc |= (flags & EFLG_CF); break; case 2: /* z/e */ rc |= (flags & EFLG_ZF); break; case 3: /* be/na */ rc |= (flags & (EFLG_CF|EFLG_ZF)); break; case 4: /* s */ rc |= (flags & EFLG_SF); break; case 5: /* p/pe */ rc |= (flags & EFLG_PF); break; case 7: /* le/ng */ rc |= (flags & EFLG_ZF); /* fall through */ case 6: /* l/nge */ rc |= (!(flags & EFLG_SF) != !(flags & EFLG_OF)); break; } /* Odd condition identifiers (lsb == 1) have inverted sense. */ return (!!rc ^ (condition & 1));}static intget_cpl( struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops){ struct segment_register reg; if ( ctxt->regs->eflags & EFLG_VM ) return 3; if ( (ops->read_segment == NULL) || ops->read_segment(x86_seg_ss, ®, ctxt) ) return -1; return reg.attr.fields.dpl;}static int_mode_iopl( struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops){ int cpl = get_cpl(ctxt, ops); if ( cpl == -1 ) return -1; return (cpl <= ((ctxt->regs->eflags >> 12) & 3));}#define mode_ring0() ({ \ int _cpl = get_cpl(ctxt, ops); \ fail_if(_cpl < 0); \ (_cpl == 0); \})#define mode_iopl() ({ \ int _iopl = _mode_iopl(ctxt, ops); \ fail_if(_iopl < 0); \ _iopl; \})static int ioport_access_check( unsigned int first_port, unsigned int bytes, struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops){ unsigned long iobmp; struct segment_register tr; int rc = X86EMUL_OKAY; if ( !(ctxt->regs->eflags & EFLG_VM) && mode_iopl() ) return X86EMUL_OKAY; fail_if(ops->read_segment == NULL); if ( (rc = ops->read_segment(x86_seg_tr, &tr, ctxt)) != 0 ) return rc; /* Ensure that the TSS is valid and has an io-bitmap-offset field. */ if ( !tr.attr.fields.p || ((tr.attr.fields.type & 0xd) != 0x9) || (tr.limit < 0x67) ) goto raise_exception; if ( (rc = read_ulong(x86_seg_none, tr.base + 0x66, &iobmp, 2, ctxt, ops)) ) return rc; /* Ensure TSS includes two bytes including byte containing first port. */ iobmp += first_port / 8; if ( tr.limit <= iobmp ) goto raise_exception; if ( (rc = read_ulong(x86_seg_none, tr.base + iobmp, &iobmp, 2, ctxt, ops)) ) return rc; if ( (iobmp & (((1<<bytes)-1) << (first_port&7))) != 0 ) goto raise_exception; done: return rc; raise_exception: fail_if(ops->inject_hw_exception == NULL); return ops->inject_hw_exception(EXC_GP, 0, ctxt) ? : X86EMUL_EXCEPTION;}static intin_realmode( struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops){ unsigned long cr0; int rc; if ( ops->read_cr == NULL ) return 0; rc = ops->read_cr(0, &cr0, ctxt); return (!rc && !(cr0 & CR0_PE));}static intin_protmode( struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops){ return !(in_realmode(ctxt, ops) || (ctxt->regs->eflags & EFLG_VM));}static intrealmode_load_seg( enum x86_segment seg, uint16_t sel, struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops){ struct segment_register reg; int rc; if ( (rc = ops->read_segment(seg, ®, ctxt)) != 0 ) return rc; reg.sel = sel; reg.base = (uint32_t)sel << 4; return ops->write_segment(seg, ®, ctxt);}static intprotmode_load_seg( enum x86_segment seg, uint16_t sel, struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops){ struct segment_register desctab, ss, segr; struct { uint32_t a, b; } desc; unsigned long val; uint8_t dpl, rpl, cpl; uint32_t new_desc_b; 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_ss, &ss, 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 = read_ulong(x86_seg_none, desctab.base + (sel & 0xfff8), &val, 4, ctxt, ops)) ) return rc; desc.a = val; if ( (rc = read_ulong(x86_seg_none, desctab.base + (sel & 0xfff8) + 4, &val, 4, ctxt, ops)) ) 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 = ss.attr.fields.dpl; 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)) != (6u<<9)) && (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)) != (6u<<9)) && ((dpl < cpl) || (dpl < rpl)) ) goto raise_exn; break; } /* Ensure Accessed flag is set. */ new_desc_b = desc.b | 0x100; rc = ((desc.b & 0x100) ? X86EMUL_OKAY : ops->cmpxchg( x86_seg_none, desctab.base + (sel & 0xfff8) + 4, &desc.b, &new_desc_b, 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);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?