[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

 


Rackspace

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