Re: [Xen-devel] [PATCH] x86emul: suppress memory writes after faulting FPU insns

2017-01-13 Thread Jan Beulich
>>> On 12.01.17 at 17:43,  wrote:
> On 12/01/17 16:12, Jan Beulich wrote:
> On 12.01.17 at 16:04,  wrote:
>>> On 12/01/17 14:02, Jan Beulich wrote:
 Furthermore I think we have another issue with writes: If the write
 faults, the FSW (or MXCSR, albeit there only for instructions we don't
 emulate yet) register may have been updated already, so we'd need to
 undo that update.
>>> Do you mean restore the value before we sample it, or before the guest
>>> gets to see it?
>> Read it, run the stub, call ->write(), and upon failure restore the
>> value read in the first step.
>>
>>> (I can't see what the problem is here.)
>> The stub execution may modify FSW/MXCSR, if the operation causes
>> an exception to be latched (for MXCSR this would need to be a
>> masked exception), but if ->write() fails architecturally the update to
>> FSW/MXCSR should not be committed.
> 
> Ok - I see now.  Yes - this is ugly corner case.  Short of doing a
> pre-emptive fpu save before emulation, I don't see an alternative.  This
> at least makes us no worse than taking a context switch.

And apparently one which even hardware gets wrong: While FPU
insns (I've tried just one) look to work as expected, VCVTPS2PH
updates MXCSR even when raising #PF.

Jan


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


Re: [Xen-devel] [PATCH] x86emul: suppress memory writes after faulting FPU insns

2017-01-13 Thread Jan Beulich
>>> On 12.01.17 at 17:43,  wrote:
> On 12/01/17 16:12, Jan Beulich wrote:
> On 12.01.17 at 16:04,  wrote:
>>> On 12/01/17 14:02, Jan Beulich wrote:
 Furthermore I think we have another issue with writes: If the write
 faults, the FSW (or MXCSR, albeit there only for instructions we don't
 emulate yet) register may have been updated already, so we'd need to
 undo that update.
>>> Do you mean restore the value before we sample it, or before the guest
>>> gets to see it?
>> Read it, run the stub, call ->write(), and upon failure restore the
>> value read in the first step.
>>
>>> (I can't see what the problem is here.)
>> The stub execution may modify FSW/MXCSR, if the operation causes
>> an exception to be latched (for MXCSR this would need to be a
>> masked exception), but if ->write() fails architecturally the update to
>> FSW/MXCSR should not be committed.
> 
> Ok - I see now.  Yes - this is ugly corner case.  Short of doing a
> pre-emptive fpu save before emulation, I don't see an alternative.  This
> at least makes us no worse than taking a context switch.

Not exactly - we don't need a full save as long as insns writing to
memory don't alter other registers (that'll become an issue with
vscatter). It would be sufficient to store FSW up front, leaving most
of the additional overhead to just the failed-write path.

Jan


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


Re: [Xen-devel] [PATCH] x86emul: suppress memory writes after faulting FPU insns

2017-01-12 Thread Andrew Cooper
On 12/01/17 16:12, Jan Beulich wrote:
 On 12.01.17 at 16:04,  wrote:
>> On 12/01/17 14:02, Jan Beulich wrote:
>>> Furthermore I think we have another issue with writes: If the write
>>> faults, the FSW (or MXCSR, albeit there only for instructions we don't
>>> emulate yet) register may have been updated already, so we'd need to
>>> undo that update.
>> Do you mean restore the value before we sample it, or before the guest
>> gets to see it?
> Read it, run the stub, call ->write(), and upon failure restore the
> value read in the first step.
>
>> (I can't see what the problem is here.)
> The stub execution may modify FSW/MXCSR, if the operation causes
> an exception to be latched (for MXCSR this would need to be a
> masked exception), but if ->write() fails architecturally the update to
> FSW/MXCSR should not be committed.

Ok - I see now.  Yes - this is ugly corner case.  Short of doing a
pre-emptive fpu save before emulation, I don't see an alternative.  This
at least makes us no worse than taking a context switch.

>
>>> --- a/xen/arch/x86/x86_emulate/x86_emulate.c
>>> +++ b/xen/arch/x86/x86_emulate/x86_emulate.c
>>> @@ -3723,6 +3735,8 @@ x86_emulate(
>>>  default:
>>>  generate_exception(EXC_UD);
>>>  }
>>> +if ( dst.type == OP_MEM && dst.bytes == 4 && 
>>> !fpu_check_write() )
>>> +dst.type = OP_NONE;
>> This dst.bytes check is rather suspicious, as the size of the operand
>> has nothing to do with whether the write should be surpressed.
>>
>> I presume you actually mean (modrm_reg & 7) < 6 to exclude fnstenv and
>> fnstcw from triggering the fpu_check_write() logic?
> I had it this way first, and then thought it's better the way it is now:
> The cases we want to exclude are the non-register-data stores,
> and in both groups all register stores are respectively uniform in size.
> Plus this way the conditional is slightly shorter (i.e. doesn't require
> splitting across lines). Yet if you strongly prefer the other variant, I
> can of course switch back. Just let me know.

As with everything here, clarity of code is the most important.

I'd prefer the modrm_reg check over dst.bytes, although would settle for
a comment describing the situations when we shouldn't suppress a write
despite an exception occuring.

~Andrew

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


Re: [Xen-devel] [PATCH] x86emul: suppress memory writes after faulting FPU insns

2017-01-12 Thread Jan Beulich
>>> On 12.01.17 at 16:04,  wrote:
> On 12/01/17 14:02, Jan Beulich wrote:
>> Furthermore I think we have another issue with writes: If the write
>> faults, the FSW (or MXCSR, albeit there only for instructions we don't
>> emulate yet) register may have been updated already, so we'd need to
>> undo that update.
> 
> Do you mean restore the value before we sample it, or before the guest
> gets to see it?

Read it, run the stub, call ->write(), and upon failure restore the
value read in the first step.

> (I can't see what the problem is here.)

The stub execution may modify FSW/MXCSR, if the operation causes
an exception to be latched (for MXCSR this would need to be a
masked exception), but if ->write() fails architecturally the update to
FSW/MXCSR should not be committed.

>> --- a/xen/arch/x86/x86_emulate/x86_emulate.c
>> +++ b/xen/arch/x86/x86_emulate/x86_emulate.c
>> @@ -3723,6 +3735,8 @@ x86_emulate(
>>  default:
>>  generate_exception(EXC_UD);
>>  }
>> +if ( dst.type == OP_MEM && dst.bytes == 4 && !fpu_check_write() 
>> )
>> +dst.type = OP_NONE;
> 
> This dst.bytes check is rather suspicious, as the size of the operand
> has nothing to do with whether the write should be surpressed.
> 
> I presume you actually mean (modrm_reg & 7) < 6 to exclude fnstenv and
> fnstcw from triggering the fpu_check_write() logic?

I had it this way first, and then thought it's better the way it is now:
The cases we want to exclude are the non-register-data stores,
and in both groups all register stores are respectively uniform in size.
Plus this way the conditional is slightly shorter (i.e. doesn't require
splitting across lines). Yet if you strongly prefer the other variant, I
can of course switch back. Just let me know.

Jan


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


Re: [Xen-devel] [PATCH] x86emul: suppress memory writes after faulting FPU insns

2017-01-12 Thread Andrew Cooper
On 12/01/17 14:02, Jan Beulich wrote:
> FPU insns writing to memory must not touch memory if they latch #MF (to
> be delivered on the next waiting FPU insn). Note that inspecting FSW.ES
> needs to be avoided for all FNST* insns, as they don't raise exceptions
> themselves, but may instead be invoked with the bit already set.
>
> Signed-off-by: Jan Beulich 
> ---
> While #MF and memory access faults are all listed in the same priority
> group, it is not entirely clear how FPU insns reading memory operate
> when an exception to be delivered doesn't depend on the memory operand
> (which would namely be FPU register stack overflows). It is therefore
> possible that memory reads would need to be suppressed in some
> situations, too. Of course this only matters for reads which have side
> effects. Otoh SNaN operand detection and stack overflow/underflow are
> listed as having the same priority, so the memory read may well be
> performed unconditionally.

This can be checked by using pagetable access bits, although I can't see
any sensible case where one would point the x87 FPU at MMIO (at all, let
alone) with read side effects.

I think it is reasonable to leave reads as they currently are.

> Furthermore I think we have another issue with writes: If the write
> faults, the FSW (or MXCSR, albeit there only for instructions we don't
> emulate yet) register may have been updated already, so we'd need to
> undo that update.

Do you mean restore the value before we sample it, or before the guest
gets to see it?

(I can't see what the problem is here.)

>  For MXCSR that will be possible by saving the initial
> value and re-loading it in case ->write() fails (and iirc there's
> exactly one affected insn - VCVTPS2PH). There's no suitable way to load
> FSW, though - all existing mechanisms have further effects we don't
> really want (albeit arguably the side effects of going through a
> {F,}XSAVE/{F,}XRSTOR cycle could occur at any time as a scheduling side
> effect).
>
> --- a/xen/arch/x86/x86_emulate/x86_emulate.c
> +++ b/xen/arch/x86/x86_emulate/x86_emulate.c
> @@ -3723,6 +3735,8 @@ x86_emulate(
>  default:
>  generate_exception(EXC_UD);
>  }
> +if ( dst.type == OP_MEM && dst.bytes == 4 && !fpu_check_write() )
> +dst.type = OP_NONE;

This dst.bytes check is rather suspicious, as the size of the operand
has nothing to do with whether the write should be surpressed.

I presume you actually mean (modrm_reg & 7) < 6 to exclude fnstenv and
fnstcw from triggering the fpu_check_write() logic?

>  }
>  put_fpu(&fic);
>  break;
> @@ -3946,6 +3963,8 @@ x86_emulate(
>  default:
>  generate_exception(EXC_UD);
>  }
> +if ( dst.type == OP_MEM && dst.bytes == 8 && !fpu_check_write() )
> +dst.type = OP_NONE;

Same again here.

Otherwise, it looks fine.

~Andrew

>  }
>  put_fpu(&fic);
>  break;

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


[Xen-devel] [PATCH] x86emul: suppress memory writes after faulting FPU insns

2017-01-12 Thread Jan Beulich
FPU insns writing to memory must not touch memory if they latch #MF (to
be delivered on the next waiting FPU insn). Note that inspecting FSW.ES
needs to be avoided for all FNST* insns, as they don't raise exceptions
themselves, but may instead be invoked with the bit already set.

Signed-off-by: Jan Beulich 
---
While #MF and memory access faults are all listed in the same priority
group, it is not entirely clear how FPU insns reading memory operate
when an exception to be delivered doesn't depend on the memory operand
(which would namely be FPU register stack overflows). It is therefore
possible that memory reads would need to be suppressed in some
situations, too. Of course this only matters for reads which have side
effects. Otoh SNaN operand detection and stack overflow/underflow are
listed as having the same priority, so the memory read may well be
performed unconditionally.

Furthermore I think we have another issue with writes: If the write
faults, the FSW (or MXCSR, albeit there only for instructions we don't
emulate yet) register may have been updated already, so we'd need to
undo that update. For MXCSR that will be possible by saving the initial
value and re-loading it in case ->write() fails (and iirc there's
exactly one affected insn - VCVTPS2PH). There's no suitable way to load
FSW, though - all existing mechanisms have further effects we don't
really want (albeit arguably the side effects of going through a
{F,}XSAVE/{F,}XRSTOR cycle could occur at any time as a scheduling side
effect).

--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -457,6 +457,9 @@ typedef union {
 #define EFLG_MBS  (1<<1)
 #define EFLG_CF   (1<<0)
 
+/* Floating point status word definitions. */
+#define FSW_ES(1U << 7)
+
 /* MXCSR bit definitions. */
 #define MXCSR_MM  (1U << 17)
 
@@ -868,6 +871,15 @@ do {
   (_fic)->exn_raised);  \
 } while (0)
 
+static inline bool fpu_check_write(void)
+{
+uint16_t fsw;
+
+asm ( "fnstsw %0" : "=am" (fsw) );
+
+return !(fsw & FSW_ES);
+}
+
 #define emulate_fpu_insn(_op)   \
 asm volatile (  \
 "movb $2f-1f,%0 \n" \
@@ -3723,6 +3735,8 @@ x86_emulate(
 default:
 generate_exception(EXC_UD);
 }
+if ( dst.type == OP_MEM && dst.bytes == 4 && !fpu_check_write() )
+dst.type = OP_NONE;
 }
 put_fpu(&fic);
 break;
@@ -3835,7 +3849,8 @@ x86_emulate(
 case 7: /* fstp m80fp */
 fail_if(!ops->write);
 emulate_fpu_insn_memdst("fstpt", *mmvalp);
-if ( (rc = ops->write(ea.mem.seg, ea.mem.off, mmvalp,
+if ( fpu_check_write() &&
+ (rc = ops->write(ea.mem.seg, ea.mem.off, mmvalp,
   10, ctxt)) != X86EMUL_OKAY )
 goto done;
 dst.type = OP_NONE;
@@ -3843,6 +3858,8 @@ x86_emulate(
 default:
 generate_exception(EXC_UD);
 }
+if ( dst.type == OP_MEM && !fpu_check_write() )
+dst.type = OP_NONE;
 }
 put_fpu(&fic);
 break;
@@ -3946,6 +3963,8 @@ x86_emulate(
 default:
 generate_exception(EXC_UD);
 }
+if ( dst.type == OP_MEM && dst.bytes == 8 && !fpu_check_write() )
+dst.type = OP_NONE;
 }
 put_fpu(&fic);
 break;
@@ -4063,7 +4082,8 @@ x86_emulate(
 case 6: /* fbstp packed bcd */
 fail_if(!ops->write);
 emulate_fpu_insn_memdst("fbstp", *mmvalp);
-if ( (rc = ops->write(ea.mem.seg, ea.mem.off, mmvalp,
+if ( fpu_check_write() &&
+ (rc = ops->write(ea.mem.seg, ea.mem.off, mmvalp,
   10, ctxt)) != X86EMUL_OKAY )
 goto done;
 dst.type = OP_NONE;
@@ -4073,6 +4093,8 @@ x86_emulate(
 dst.bytes = 8;
 break;
 }
+if ( dst.type == OP_MEM && !fpu_check_write() )
+dst.type = OP_NONE;
 }
 put_fpu(&fic);
 break;


x86emul: suppress memory writes after faulting FPU insns

FPU insns writing to memory must not touch memory if they latch #MF (to
be delivered on the next waiting FPU insn). Note that inspecting FSW.ES
needs to be avoided for all FNST* insns, as they don't raise exceptions
themselves, but may instead be invoked with the bit already set.

Signed-off-by: Jan Beulich 
---
While #MF and memory access faults are all listed in the same priority
group, it is not entirely clear how FPU insns reading memory operate
when an exception to be delivered doesn't depend on the memory operand
(which would namely be FPU register stack overflows). It