diff mbox

[for-4.7,3/4] x86/hvm: Correct the emulated interaction of invlpg with segments

Message ID 1462799742-15507-4-git-send-email-andrew.cooper3@citrix.com (mailing list archive)
State New, archived
Headers show

Commit Message

Andrew Cooper May 9, 2016, 1:15 p.m. UTC
The `invlpg` instruction is documented to take a memory address, and is not
documented to suffer faults from segmentation violations.

Experimentally, and subsequently confirmed by both Intel and AMD, the
instruction does take into account segment bases, but will happily invalidate
a TLB entry for a mapping beyond the segment limit.

The emulation logic will currently raise #GP/#SS faults for segment limit
violations, or non-canonical addresses, which doesn't match hardware's
behaviour.  Instead, squash exceptions generated by
hvmemul_virtual_to_linear() and proceed with invalidation.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
CC: Jan Beulich <JBeulich@suse.com>
CC: Paul Durrant <paul.durrant@citrix.com>
CC: Wei Liu <wei.liu2@citrix.com>
---
 xen/arch/x86/hvm/emulate.c | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

Comments

Jan Beulich May 9, 2016, 1:42 p.m. UTC | #1
>>> On 09.05.16 at 15:15, <andrew.cooper3@citrix.com> wrote:
> The `invlpg` instruction is documented to take a memory address, and is not
> documented to suffer faults from segmentation violations.
> 
> Experimentally, and subsequently confirmed by both Intel and AMD, the
> instruction does take into account segment bases, but will happily invalidate
> a TLB entry for a mapping beyond the segment limit.

How about non-canonical addresses? If those don't #GP (which is
what I assume), is such an INVLPG a NOP, or does it invalidate
something (e.g. the translation for the address truncated to 48
bits)? In that latter case we might need to also make our code
behave that way...

> The emulation logic will currently raise #GP/#SS faults for segment limit
> violations, or non-canonical addresses, which doesn't match hardware's
> behaviour.  Instead, squash exceptions generated by
> hvmemul_virtual_to_linear() and proceed with invalidation.
> 
> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>

Reviewed-by: Jan Beulich <jbeulich@suse.com>

albeit I think ...

> --- a/xen/arch/x86/hvm/emulate.c
> +++ b/xen/arch/x86/hvm/emulate.c
> @@ -1608,7 +1608,22 @@ static int hvmemul_invlpg(
>      rc = hvmemul_virtual_to_linear(
>          seg, offset, 1, &reps, hvm_access_none, hvmemul_ctxt, &addr);
>  
> -    if ( rc == X86EMUL_OKAY )
> +    if ( rc == X86EMUL_EXCEPTION )
> +    {
> +        /*
> +         * `invlpg` takes segment bases into account, but is not subject to
> +         * faults from segment type/limit checks, and is specified as a NOP
> +         * when issued on non-canonical addresses.
> +         *
> +         * hvmemul_virtual_to_linear() raises exceptions for type/limit
> +         * violations, so squash them.
> +         */
> +        hvmemul_ctxt->exn_pending = 0;
> +        hvmemul_ctxt->trap = (struct hvm_trap){};

... this does more work than is really needed.

Jan
Andrew Cooper May 9, 2016, 1:47 p.m. UTC | #2
On 09/05/16 14:42, Jan Beulich wrote:
>>>> On 09.05.16 at 15:15, <andrew.cooper3@citrix.com> wrote:
>> The `invlpg` instruction is documented to take a memory address, and is not
>> documented to suffer faults from segmentation violations.
>>
>> Experimentally, and subsequently confirmed by both Intel and AMD, the
>> instruction does take into account segment bases, but will happily invalidate
>> a TLB entry for a mapping beyond the segment limit.
> How about non-canonical addresses? If those don't #GP (which is
> what I assume), is such an INVLPG a NOP

They are explicitly documented by both Intel and AMD as being a NOP when
passed a non-canonical address.  Experimentation confirms this.

(I am just putting the finishing touches on an XTF test for all of this).

> , or does it invalidate
> something (e.g. the translation for the address truncated to 48
> bits)? In that latter case we might need to also make our code
> behave that way...
>
>> The emulation logic will currently raise #GP/#SS faults for segment limit
>> violations, or non-canonical addresses, which doesn't match hardware's
>> behaviour.  Instead, squash exceptions generated by
>> hvmemul_virtual_to_linear() and proceed with invalidation.
>>
>> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
> Reviewed-by: Jan Beulich <jbeulich@suse.com>
>
> albeit I think ...
>
>> --- a/xen/arch/x86/hvm/emulate.c
>> +++ b/xen/arch/x86/hvm/emulate.c
>> @@ -1608,7 +1608,22 @@ static int hvmemul_invlpg(
>>      rc = hvmemul_virtual_to_linear(
>>          seg, offset, 1, &reps, hvm_access_none, hvmemul_ctxt, &addr);
>>  
>> -    if ( rc == X86EMUL_OKAY )
>> +    if ( rc == X86EMUL_EXCEPTION )
>> +    {
>> +        /*
>> +         * `invlpg` takes segment bases into account, but is not subject to
>> +         * faults from segment type/limit checks, and is specified as a NOP
>> +         * when issued on non-canonical addresses.
>> +         *
>> +         * hvmemul_virtual_to_linear() raises exceptions for type/limit
>> +         * violations, so squash them.
>> +         */
>> +        hvmemul_ctxt->exn_pending = 0;
>> +        hvmemul_ctxt->trap = (struct hvm_trap){};
> ... this does more work than is really needed.

For sanity sake, I would prefer not to leave stale information in the
emulation context.  This path is definitely not a hotpath.

~Andrew
Paul Durrant May 9, 2016, 1:48 p.m. UTC | #3
> -----Original Message-----
> From: Andrew Cooper [mailto:andrew.cooper3@citrix.com]
> Sent: 09 May 2016 14:16
> To: Xen-devel
> Cc: Andrew Cooper; Jan Beulich; Paul Durrant; Wei Liu
> Subject: [PATCH for-4.7 3/4] x86/hvm: Correct the emulated interaction of
> invlpg with segments
> 
> The `invlpg` instruction is documented to take a memory address, and is not
> documented to suffer faults from segmentation violations.
> 
> Experimentally, and subsequently confirmed by both Intel and AMD, the
> instruction does take into account segment bases, but will happily invalidate
> a TLB entry for a mapping beyond the segment limit.
> 
> The emulation logic will currently raise #GP/#SS faults for segment limit
> violations, or non-canonical addresses, which doesn't match hardware's
> behaviour.  Instead, squash exceptions generated by
> hvmemul_virtual_to_linear() and proceed with invalidation.
> 
> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>

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

> ---
> CC: Jan Beulich <JBeulich@suse.com>
> CC: Paul Durrant <paul.durrant@citrix.com>
> CC: Wei Liu <wei.liu2@citrix.com>
> ---
>  xen/arch/x86/hvm/emulate.c | 17 ++++++++++++++++-
>  1 file changed, 16 insertions(+), 1 deletion(-)
> 
> diff --git a/xen/arch/x86/hvm/emulate.c b/xen/arch/x86/hvm/emulate.c
> index ee5cf1f..e6316be 100644
> --- a/xen/arch/x86/hvm/emulate.c
> +++ b/xen/arch/x86/hvm/emulate.c
> @@ -1608,7 +1608,22 @@ static int hvmemul_invlpg(
>      rc = hvmemul_virtual_to_linear(
>          seg, offset, 1, &reps, hvm_access_none, hvmemul_ctxt, &addr);
> 
> -    if ( rc == X86EMUL_OKAY )
> +    if ( rc == X86EMUL_EXCEPTION )
> +    {
> +        /*
> +         * `invlpg` takes segment bases into account, but is not subject to
> +         * faults from segment type/limit checks, and is specified as a NOP
> +         * when issued on non-canonical addresses.
> +         *
> +         * hvmemul_virtual_to_linear() raises exceptions for type/limit
> +         * violations, so squash them.
> +         */
> +        hvmemul_ctxt->exn_pending = 0;
> +        hvmemul_ctxt->trap = (struct hvm_trap){};
> +        rc = X86EMUL_OKAY;
> +    }
> +
> +    if ( rc == X86EMUL_OKAY && is_canonical_address(addr) )
>          hvm_funcs.invlpg_intercept(addr);
> 
>      return rc;
> --
> 2.1.4
diff mbox

Patch

diff --git a/xen/arch/x86/hvm/emulate.c b/xen/arch/x86/hvm/emulate.c
index ee5cf1f..e6316be 100644
--- a/xen/arch/x86/hvm/emulate.c
+++ b/xen/arch/x86/hvm/emulate.c
@@ -1608,7 +1608,22 @@  static int hvmemul_invlpg(
     rc = hvmemul_virtual_to_linear(
         seg, offset, 1, &reps, hvm_access_none, hvmemul_ctxt, &addr);
 
-    if ( rc == X86EMUL_OKAY )
+    if ( rc == X86EMUL_EXCEPTION )
+    {
+        /*
+         * `invlpg` takes segment bases into account, but is not subject to
+         * faults from segment type/limit checks, and is specified as a NOP
+         * when issued on non-canonical addresses.
+         *
+         * hvmemul_virtual_to_linear() raises exceptions for type/limit
+         * violations, so squash them.
+         */
+        hvmemul_ctxt->exn_pending = 0;
+        hvmemul_ctxt->trap = (struct hvm_trap){};
+        rc = X86EMUL_OKAY;
+    }
+
+    if ( rc == X86EMUL_OKAY && is_canonical_address(addr) )
         hvm_funcs.invlpg_intercept(addr);
 
     return rc;