Patchwork x86/HVM: correct repeat count update in linear->phys translation

login
register
mail settings
Submitter Jan Beulich
Date Sept. 7, 2017, 10:41 a.m.
Message ID <59B13E9102000078001783E2@prv-mh.provo.novell.com>
Download mbox | patch
Permalink /patch/333587/
State New
Headers show

Comments

Jan Beulich - Sept. 7, 2017, 10:41 a.m.
For the insn emulator's fallback logic in REP MOVS/STOS/INS/OUTS
handling to work correctly, *reps must not be set to zero when
returning X86EMUL_UNHANDLEABLE.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Paul Durrant - Sept. 7, 2017, 10:50 a.m.
> -----Original Message-----
> From: Jan Beulich [mailto:JBeulich@suse.com]
> Sent: 07 September 2017 11:42
> To: xen-devel <xen-devel@lists.xenproject.org>
> Cc: Andrew Cooper <Andrew.Cooper3@citrix.com>; Paul Durrant
> <Paul.Durrant@citrix.com>
> Subject: [PATCH] x86/HVM: correct repeat count update in linear->phys
> translation
> 
> For the insn emulator's fallback logic in REP MOVS/STOS/INS/OUTS
> handling to work correctly, *reps must not be set to zero when
> returning X86EMUL_UNHANDLEABLE.

Does it really need to be zero when returning X86EMUL_EXCEPTION?

  Paul

> 
> Signed-off-by: Jan Beulich <jbeulich@suse.com>
> 
> --- a/xen/arch/x86/hvm/emulate.c
> +++ b/xen/arch/x86/hvm/emulate.c
> @@ -566,15 +566,16 @@ static int hvmemul_linear_to_phys(
>              if ( pfec & (PFEC_page_paged | PFEC_page_shared) )
>                  return X86EMUL_RETRY;
>              done /= bytes_per_rep;
> -            *reps = done;
>              if ( done == 0 )
>              {
>                  ASSERT(!reverse);
>                  if ( npfn != gfn_x(INVALID_GFN) )
>                      return X86EMUL_UNHANDLEABLE;
> +                *reps = 0;
>                  x86_emul_pagefault(pfec, addr & PAGE_MASK, &hvmemul_ctxt-
> >ctxt);
>                  return X86EMUL_EXCEPTION;
>              }
> +            *reps = done;
>              break;
>          }
> 
> 
>
Andrew Cooper - Sept. 7, 2017, 11:15 a.m.
On 07/09/17 11:41, Jan Beulich wrote:
> For the insn emulator's fallback logic in REP MOVS/STOS/INS/OUTS
> handling to work correctly, *reps must not be set to zero when
> returning X86EMUL_UNHANDLEABLE.
>
> Signed-off-by: Jan Beulich <jbeulich@suse.com>

Why is this?  In the case that X86EMUL_UNHANDLEABLE is returned, the
emulator appears to override nr_reps to 1.

I'm clearly missing something.

>
> --- a/xen/arch/x86/hvm/emulate.c
> +++ b/xen/arch/x86/hvm/emulate.c
> @@ -566,15 +566,16 @@ static int hvmemul_linear_to_phys(
>              if ( pfec & (PFEC_page_paged | PFEC_page_shared) )
>                  return X86EMUL_RETRY;
>              done /= bytes_per_rep;
> -            *reps = done;
>              if ( done == 0 )
>              {
>                  ASSERT(!reverse);
>                  if ( npfn != gfn_x(INVALID_GFN) )
>                      return X86EMUL_UNHANDLEABLE;
> +                *reps = 0;
>                  x86_emul_pagefault(pfec, addr & PAGE_MASK, &hvmemul_ctxt->ctxt);

Independently to the issue at hand, this looks suspicious for the
reverse direction.

Hardware will issue a walk for the first byte of access, and optionally
a second at the start of the subsequent page for a straddled access. 
For the reverse case, this looks like it will truncate down to the start
of the lower linear address, which I bet isn't how hardware actually
behaves.

(In some copious free time, I should really put together a discontinuous
rep insn XTF test).

~Andrew

>                  return X86EMUL_EXCEPTION;
>              }
> +            *reps = done;
>              break;
>          }
>  
>
>
>
Jan Beulich - Sept. 7, 2017, 11:20 a.m.
>>> On 07.09.17 at 12:50, <Paul.Durrant@citrix.com> wrote:
>>  -----Original Message-----
>> From: Jan Beulich [mailto:JBeulich@suse.com]
>> Sent: 07 September 2017 11:42
>> To: xen-devel <xen-devel@lists.xenproject.org>
>> Cc: Andrew Cooper <Andrew.Cooper3@citrix.com>; Paul Durrant
>> <Paul.Durrant@citrix.com>
>> Subject: [PATCH] x86/HVM: correct repeat count update in linear->phys
>> translation
>> 
>> For the insn emulator's fallback logic in REP MOVS/STOS/INS/OUTS
>> handling to work correctly, *reps must not be set to zero when
>> returning X86EMUL_UNHANDLEABLE.
> 
> Does it really need to be zero when returning X86EMUL_EXCEPTION?

Yes. Since an exception may occur on other than the first
iteration, in that case the number of successfully done one
needs to be handed back. See also the other #PF invocation
a few lines up.

Jan
Jan Beulich - Sept. 7, 2017, 11:24 a.m.
>>> On 07.09.17 at 13:15, <andrew.cooper3@citrix.com> wrote:
> On 07/09/17 11:41, Jan Beulich wrote:
>> For the insn emulator's fallback logic in REP MOVS/STOS/INS/OUTS
>> handling to work correctly, *reps must not be set to zero when
>> returning X86EMUL_UNHANDLEABLE.
>>
>> Signed-off-by: Jan Beulich <jbeulich@suse.com>
> 
> Why is this?  In the case that X86EMUL_UNHANDLEABLE is returned, the
> emulator appears to override nr_reps to 1.

Where did you see that? What we have is

        if ( (nr_reps > 1 || rc == X86EMUL_UNHANDLEABLE) && ops->rep_ins )
            rc = ops->rep_ins(port, dst.mem.seg, dst.mem.off, dst.bytes,
                              &nr_reps, ctxt);
        if ( nr_reps >= 1 && rc == X86EMUL_UNHANDLEABLE )
        {
            fail_if(!ops->read_io || !ops->write);
            if ( (rc = ops->read_io(port, dst.bytes, &dst.val, ctxt)) != 0 )
                goto done;
            nr_reps = 0;
        }

>> --- a/xen/arch/x86/hvm/emulate.c
>> +++ b/xen/arch/x86/hvm/emulate.c
>> @@ -566,15 +566,16 @@ static int hvmemul_linear_to_phys(
>>              if ( pfec & (PFEC_page_paged | PFEC_page_shared) )
>>                  return X86EMUL_RETRY;
>>              done /= bytes_per_rep;
>> -            *reps = done;
>>              if ( done == 0 )
>>              {
>>                  ASSERT(!reverse);
>>                  if ( npfn != gfn_x(INVALID_GFN) )
>>                      return X86EMUL_UNHANDLEABLE;
>> +                *reps = 0;
>>                  x86_emul_pagefault(pfec, addr & PAGE_MASK, 
> &hvmemul_ctxt->ctxt);
> 
> Independently to the issue at hand, this looks suspicious for the
> reverse direction.
> 
> Hardware will issue a walk for the first byte of access, and optionally
> a second at the start of the subsequent page for a straddled access. 
> For the reverse case, this looks like it will truncate down to the start
> of the lower linear address, which I bet isn't how hardware actually
> behaves.

Good point. Since I've played with this just now anyway, let me
see if I can get this corrected in another patch.

Jan
Jan Beulich - Sept. 7, 2017, 11:27 a.m.
>>> On 07.09.17 at 13:15, <andrew.cooper3@citrix.com> wrote:
> On 07/09/17 11:41, Jan Beulich wrote:
>> --- a/xen/arch/x86/hvm/emulate.c
>> +++ b/xen/arch/x86/hvm/emulate.c
>> @@ -566,15 +566,16 @@ static int hvmemul_linear_to_phys(
>>              if ( pfec & (PFEC_page_paged | PFEC_page_shared) )
>>                  return X86EMUL_RETRY;
>>              done /= bytes_per_rep;
>> -            *reps = done;
>>              if ( done == 0 )
>>              {
>>                  ASSERT(!reverse);
>>                  if ( npfn != gfn_x(INVALID_GFN) )
>>                      return X86EMUL_UNHANDLEABLE;
>> +                *reps = 0;
>>                  x86_emul_pagefault(pfec, addr & PAGE_MASK, 
> &hvmemul_ctxt->ctxt);
> 
> Independently to the issue at hand, this looks suspicious for the
> reverse direction.
> 
> Hardware will issue a walk for the first byte of access, and optionally
> a second at the start of the subsequent page for a straddled access. 
> For the reverse case, this looks like it will truncate down to the start
> of the lower linear address, which I bet isn't how hardware actually
> behaves.

Actually - no, this looks all fine. Note the "ASSERT(!reverse)" in
context above. Reverse page straddling accesses are being
handled by a single forward iteration several lines up from here.

Jan
Andrew Cooper - Sept. 7, 2017, 11:35 a.m.
On 07/09/17 12:24, Jan Beulich wrote:
>>>> On 07.09.17 at 13:15, <andrew.cooper3@citrix.com> wrote:
>> On 07/09/17 11:41, Jan Beulich wrote:
>>> For the insn emulator's fallback logic in REP MOVS/STOS/INS/OUTS
>>> handling to work correctly, *reps must not be set to zero when
>>> returning X86EMUL_UNHANDLEABLE.
>>>
>>> Signed-off-by: Jan Beulich <jbeulich@suse.com>
>> Why is this?  In the case that X86EMUL_UNHANDLEABLE is returned, the
>> emulator appears to override nr_reps to 1.
> Where did you see that? What we have is
>
>         if ( (nr_reps > 1 || rc == X86EMUL_UNHANDLEABLE) && ops->rep_ins )
>             rc = ops->rep_ins(port, dst.mem.seg, dst.mem.off, dst.bytes,
>                               &nr_reps, ctxt);
>         if ( nr_reps >= 1 && rc == X86EMUL_UNHANDLEABLE )
>         {
>             fail_if(!ops->read_io || !ops->write);
>             if ( (rc = ops->read_io(port, dst.bytes, &dst.val, ctxt)) != 0 )
>                 goto done;
>             nr_reps = 0;
>         }

Ah - the INS/OUTS handing is different to the MOVS/STOS, where the
MOVS/STOS does cope fine with reps being zero.

With a suitable adjustment to the commit message, Reviewed-by: Andrew
Cooper <andrew.cooper3@citrix.com>

>
>>> --- a/xen/arch/x86/hvm/emulate.c
>>> +++ b/xen/arch/x86/hvm/emulate.c
>>> @@ -566,15 +566,16 @@ static int hvmemul_linear_to_phys(
>>>              if ( pfec & (PFEC_page_paged | PFEC_page_shared) )
>>>                  return X86EMUL_RETRY;
>>>              done /= bytes_per_rep;
>>> -            *reps = done;
>>>              if ( done == 0 )
>>>              {
>>>                  ASSERT(!reverse);
>>>                  if ( npfn != gfn_x(INVALID_GFN) )
>>>                      return X86EMUL_UNHANDLEABLE;
>>> +                *reps = 0;
>>>                  x86_emul_pagefault(pfec, addr & PAGE_MASK, 
>> &hvmemul_ctxt->ctxt);
>>
>> Independently to the issue at hand, this looks suspicious for the
>> reverse direction.
>>
>> Hardware will issue a walk for the first byte of access, and optionally
>> a second at the start of the subsequent page for a straddled access. 
>> For the reverse case, this looks like it will truncate down to the start
>> of the lower linear address, which I bet isn't how hardware actually
>> behaves.
> Good point. Since I've played with this just now anyway, let me
> see if I can get this corrected in another patch.

(Merging the other thread to avoid unnecessary emails)

> Actually - no, this looks all fine. Note the "ASSERT(!reverse)" in
> context above. Reverse page straddling accesses are being
> handled by a single forward iteration several lines up from here.

Good point.  I've got a patch which cleans up the pagefault handling a
little, which I will rebase over this patch.

~Andrew
Jan Beulich - Sept. 7, 2017, 11:41 a.m.
>>> On 07.09.17 at 13:35, <andrew.cooper3@citrix.com> wrote:
> On 07/09/17 12:24, Jan Beulich wrote:
>>>>> On 07.09.17 at 13:15, <andrew.cooper3@citrix.com> wrote:
>>> On 07/09/17 11:41, Jan Beulich wrote:
>>>> For the insn emulator's fallback logic in REP MOVS/STOS/INS/OUTS
>>>> handling to work correctly, *reps must not be set to zero when
>>>> returning X86EMUL_UNHANDLEABLE.
>>>>
>>>> Signed-off-by: Jan Beulich <jbeulich@suse.com>
>>> Why is this?  In the case that X86EMUL_UNHANDLEABLE is returned, the
>>> emulator appears to override nr_reps to 1.
>> Where did you see that? What we have is
>>
>>         if ( (nr_reps > 1 || rc == X86EMUL_UNHANDLEABLE) && ops->rep_ins )
>>             rc = ops->rep_ins(port, dst.mem.seg, dst.mem.off, dst.bytes,
>>                               &nr_reps, ctxt);
>>         if ( nr_reps >= 1 && rc == X86EMUL_UNHANDLEABLE )
>>         {
>>             fail_if(!ops->read_io || !ops->write);
>>             if ( (rc = ops->read_io(port, dst.bytes, &dst.val, ctxt)) != 0 )
>>                 goto done;
>>             nr_reps = 0;
>>         }
> 
> Ah - the INS/OUTS handing is different to the MOVS/STOS, where the
> MOVS/STOS does cope fine with reps being zero.

Oh, right.

> With a suitable adjustment to the commit message, Reviewed-by: Andrew
> Cooper <andrew.cooper3@citrix.com>

I'd just dropped the MOVS/STOS from the text, but left it unchanged
otherwise.

Jan
Paul Durrant - Sept. 8, 2017, 12:31 p.m.
> -----Original Message-----
> From: Jan Beulich [mailto:JBeulich@suse.com]
> Sent: 07 September 2017 11:42
> To: xen-devel <xen-devel@lists.xenproject.org>
> Cc: Andrew Cooper <Andrew.Cooper3@citrix.com>; Paul Durrant
> <Paul.Durrant@citrix.com>
> Subject: [PATCH] x86/HVM: correct repeat count update in linear->phys
> translation
> 
> For the insn emulator's fallback logic in REP MOVS/STOS/INS/OUTS
> handling to work correctly, *reps must not be set to zero when
> returning X86EMUL_UNHANDLEABLE.
> 
> Signed-off-by: Jan Beulich <jbeulich@suse.com>
>

Acked-by: Paul Durrant <paul.durrant@citrix.com>

 
> --- a/xen/arch/x86/hvm/emulate.c
> +++ b/xen/arch/x86/hvm/emulate.c
> @@ -566,15 +566,16 @@ static int hvmemul_linear_to_phys(
>              if ( pfec & (PFEC_page_paged | PFEC_page_shared) )
>                  return X86EMUL_RETRY;
>              done /= bytes_per_rep;
> -            *reps = done;
>              if ( done == 0 )
>              {
>                  ASSERT(!reverse);
>                  if ( npfn != gfn_x(INVALID_GFN) )
>                      return X86EMUL_UNHANDLEABLE;
> +                *reps = 0;
>                  x86_emul_pagefault(pfec, addr & PAGE_MASK, &hvmemul_ctxt-
> >ctxt);
>                  return X86EMUL_EXCEPTION;
>              }
> +            *reps = done;
>              break;
>          }
> 
> 
>

Patch

--- a/xen/arch/x86/hvm/emulate.c
+++ b/xen/arch/x86/hvm/emulate.c
@@ -566,15 +566,16 @@  static int hvmemul_linear_to_phys(
             if ( pfec & (PFEC_page_paged | PFEC_page_shared) )
                 return X86EMUL_RETRY;
             done /= bytes_per_rep;
-            *reps = done;
             if ( done == 0 )
             {
                 ASSERT(!reverse);
                 if ( npfn != gfn_x(INVALID_GFN) )
                     return X86EMUL_UNHANDLEABLE;
+                *reps = 0;
                 x86_emul_pagefault(pfec, addr & PAGE_MASK, &hvmemul_ctxt->ctxt);
                 return X86EMUL_EXCEPTION;
             }
+            *reps = done;
             break;
         }