[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH 2/2] x86emul: support LAR/LSL/VERR/VERW
On 08/12/16 11:52, Jan Beulich wrote: > --- a/xen/arch/x86/x86_emulate/x86_emulate.c > +++ b/xen/arch/x86/x86_emulate/x86_emulate.c > @@ -182,7 +182,7 @@ static const opcode_desc_t opcode_table[ > > static const opcode_desc_t twobyte_table[256] = { > /* 0x00 - 0x07 */ > - ModRM, ImplicitOps|ModRM, ModRM, ModRM, > + ModRM, ImplicitOps|ModRM, DstReg|SrcMem16|ModRM, DstReg|SrcMem16|ModRM, > 0, ImplicitOps, ImplicitOps, ImplicitOps, > /* 0x08 - 0x0F */ > ImplicitOps, ImplicitOps, 0, ImplicitOps, > @@ -1384,7 +1384,7 @@ protmode_load_seg( > } A number of the following changes were very confusing to follow until I realised that you are introducing a meaning, where protmode_load_seg(x86_seg_none, ...) means "please do all the checks, but don't commit any state or raise any exceptions". It would be helpful to point this out in the commit message and in a comment at the head of protmode_load_seg(). > > /* System segment descriptors must reside in the GDT. */ > - if ( !is_x86_user_segment(seg) && (sel & 4) ) > + if ( is_x86_system_segment(seg) && (sel & 4) ) > goto raise_exn; > > switch ( rc = ops->read(sel_seg, sel & 0xfff8, &desc, sizeof(desc), > ctxt) ) > @@ -1401,14 +1401,11 @@ protmode_load_seg( > return rc; > } > > - if ( !is_x86_user_segment(seg) ) > - { > - /* System segments must have S flag == 0. */ > - if ( desc.b & (1u << 12) ) > - goto raise_exn; > - } > + /* System segments must have S flag == 0. */ > + if ( is_x86_system_segment(seg) && (desc.b & (1u << 12)) ) > + goto raise_exn; > /* User segments must have S flag == 1. */ > - else if ( !(desc.b & (1u << 12)) ) > + if ( is_x86_user_segment(seg) && !(desc.b & (1u << 12)) ) > goto raise_exn; This might be clearer as (and is definitely shorter as) /* Check .S is correct for the type of segment. */ if ( seg != x86_seg_none && is_x86_user_segment(seg) != (desc.b & (1u << 12)) ) goto raise_exn; > > dpl = (desc.b >> 13) & 3; > @@ -1470,10 +1467,17 @@ protmode_load_seg( > ((dpl < cpl) || (dpl < rpl)) ) > goto raise_exn; > break; > + case x86_seg_none: > + /* Non-conforming segment: check DPL against RPL and CPL. */ > + if ( ((desc.b & (0x1c << 8)) != (0x1c << 8)) && > + ((dpl < cpl) || (dpl < rpl)) ) > + return X86EMUL_EXCEPTION; I realise it is no functional change, but it would be cleaner to have this as a goto raise_exn, to avoid having one spurious early-exit in a fucntion which otherwise always goes to raise_exn or done. > + a_flag = 0; > + break; > } > > /* Segment present in memory? */ > - if ( !(desc.b & (1 << 15)) ) > + if ( !(desc.b & (1 << 15)) && seg != x86_seg_none ) What is the purpose of this change, given that the raise_exn case is always conditional on seg != x86_seg_none ? > { > fault_type = seg != x86_seg_ss ? EXC_NP : EXC_SS; > goto raise_exn; > @@ -1481,7 +1485,7 @@ protmode_load_seg( > > if ( !is_x86_user_segment(seg) ) > { > - int lm = in_longmode(ctxt, ops); > + int lm = (desc.b & (1u << 12)) ? 0 : in_longmode(ctxt, ops); > > if ( lm < 0 ) > return X86EMUL_UNHANDLEABLE; > @@ -1501,7 +1505,8 @@ protmode_load_seg( > return rc; > } > if ( (desc_hi.b & 0x00001f00) || > - !is_canonical_address((uint64_t)desc_hi.a << 32) ) > + (seg != x86_seg_none && > + !is_canonical_address((uint64_t)desc_hi.a << 32)) ) > goto raise_exn; > } > } > @@ -1544,7 +1549,8 @@ protmode_load_seg( > return X86EMUL_OKAY; > > raise_exn: > - generate_exception(fault_type, sel & 0xfffc); > + generate_exception_if(seg != x86_seg_none, fault_type, sel & 0xfffc); > + rc = X86EMUL_EXCEPTION; > done: > return rc; > } > @@ -4424,6 +4430,28 @@ x86_emulate( > if ( (rc = load_seg(seg, src.val, 0, NULL, ctxt, ops)) != 0 ) > goto done; > break; > + case 4: /* verr / verw */ This looks wrong, but I see it isn't actually. Whether this patch or a subsequent one, it would be clearer to alter the switch statement not to mask off the bottom bit, and have individual case labels for the instructions. > + _regs.eflags &= ~EFLG_ZF; > + switch ( rc = protmode_load_seg(x86_seg_none, src.val, false, > + &sreg, ctxt, ops) ) > + { > + case X86EMUL_OKAY: > + if ( sreg.attr.fields.s && > + ((modrm_reg & 1) ? ((sreg.attr.fields.type & 0xa) == > 0x2) > + : ((sreg.attr.fields.type & 0xa) != > 0x8)) ) > + _regs.eflags |= EFLG_ZF; > + break; > + case X86EMUL_EXCEPTION: Could we please annotate the areas where we selectively passing exceptions? I can see this pattern getting confusing if we don't. Something like: /* #PF needs to escape. #GP should have been squashed already. */ > + if ( ctxt->event_pending ) > + { > + default: > + goto done; > + } > + /* Instead of the exception, ZF remains cleared. */ > + rc = X86EMUL_OKAY; > + break; > + } > + break; > default: > generate_exception_if(true, EXC_UD); > break; > @@ -4621,6 +4649,96 @@ x86_emulate( > break; > } > > + case X86EMUL_OPC(0x0f, 0x02): /* lar */ > + generate_exception_if(!in_protmode(ctxt, ops), EXC_UD); As a tangential point, the distinction between the various in_*() predicates is sufficiently subtle that I keep on having to look it up to check. What do you think about replacing the current predicates with a single mode_is() predicate which takes an or-able set of REAL|VM86|PROT|LONG flags ? > + _regs.eflags &= ~EFLG_ZF; > + switch ( rc = protmode_load_seg(x86_seg_none, src.val, false, &sreg, > + ctxt, ops) ) > + { > + case X86EMUL_OKAY: > + if ( !sreg.attr.fields.s ) > + { > + switch ( sreg.attr.fields.type ) > + { > + case 0x01: /* available 16-bit TSS */ > + case 0x03: /* busy 16-bit TSS */ > + case 0x04: /* 16-bit call gate */ > + case 0x05: /* 16/32-bit task gate */ > + if ( in_longmode(ctxt, ops) ) > + break; > + /* fall through */ > + case 0x02: /* LDT */ According to the Intel manual, LDT is valid in protected mode, but not valid in long mode. This appears to be a functional difference from AMD, who permit querying LDT in long mode. I haven't checked what real hardware behaviour is. Given that Intel documents LDT as valid for LSL, I wonder whether this is a documentation error. > + case 0x09: /* available 32/64-bit TSS */ > + case 0x0b: /* busy 32/64-bit TSS */ > + case 0x0c: /* 32/64-bit call gate */ > + _regs.eflags |= EFLG_ZF; > + break; > + } > + } > + else > + _regs.eflags |= EFLG_ZF; > + break; > + case X86EMUL_EXCEPTION: > + if ( ctxt->event_pending ) > + { > + default: > + goto done; > + } > + /* Instead of the exception, ZF remains cleared. */ > + rc = X86EMUL_OKAY; > + break; > + } > + if ( _regs.eflags & EFLG_ZF ) > + dst.val = ((sreg.attr.bytes & 0xff) << 8) | > + ((sreg.limit >> (sreg.attr.fields.g ? 12 : 0)) & > + 0xf0000) | > + ((sreg.attr.bytes & 0xf00) << 12); Is this correct? The AMD manuals says for 16bit, attr & 0xff00, and for 32 or 64bit, attr & 0xffff00. The Intel manual describes this in a far more complicated way, but still looks compatible. ~Andrew _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx https://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |