[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 at 17:24, <andrew.cooper3@xxxxxxxxxx> wrote:
> On 08/12/16 11:52, Jan Beulich wrote:
>> @@ -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;

I had it that way first, and then thought the opposite (the explicit
two if()-s being more clear). I'd prefer to keep it as is, but if you
feel strongly about the other variant being better, I can change it.

>> @@ -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.

That's a matter of taste I think - to me it seems better to make
immediately obvious that no exception will be raised in this case
(which "goto raise_exn" would suggest).

>>      /* 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 ?

Well - we don't want to reach raise_exn in this case, i.e. we
want to take the implicit "else" path. After all a clear P bit does
not result in the instructions to fail.

>> @@ -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.

Again a case where I think the masking off of the low bit is the
better approach. I wouldn't object to a patch altering it, but I'm
not convinced enough to put one together myself. And no, I
don't think this should be done in this patch.

>> +            _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. */

Hmm, we're not selectively passing exceptions here; we pass any
which have got raised, and #GP/#SS/#NP aren't among them. So
I think the comment may rather want to be "Only #PF can come
here", which then we could as well ASSERT() ...

>> +                if ( ctxt->event_pending )
>> +                {

... here (and that would then serve as a de-facto comment).
What do you think?

>> @@ -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 ?

And you mean x86_decode() to set them once at its beginning?
That would make e.g. read_msr() a required hook, which I don't
think is a good idea (the more that x86_decode(), which in
particular gets passed almost no hooks at all from
x86_decode_insn(), doesn't need to know whether we're
emulating long mode).

If anything this would therefore need to be another input coming
from the original callers (complementing addr_size and sp_size).

>> +        _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.

I did check all this on hardware, so yes, looks to be a doc 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.

Well - as for all operations truncation to operand size will occur
during writeback of dst.val to the destination register.

Jan

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.