aboutsummaryrefslogtreecommitdiffstats
path: root/xen/arch/x86/domctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'xen/arch/x86/domctl.c')
-rw-r--r--xen/arch/x86/domctl.c51
1 files changed, 30 insertions, 21 deletions
diff --git a/xen/arch/x86/domctl.c b/xen/arch/x86/domctl.c
index f1146cdbd0..aa368a6d7c 100644
--- a/xen/arch/x86/domctl.c
+++ b/xen/arch/x86/domctl.c
@@ -1374,11 +1374,8 @@ long arch_do_domctl(
struct domain *d;
struct vcpu *v;
uint32_t offset = 0;
- uint64_t _xfeature_mask = 0;
- uint64_t _xcr0, _xcr0_accum;
- void *receive_buf = NULL, *_xsave_area;
-#define PV_XSAVE_SIZE (2 * sizeof(uint64_t) + xsave_cntxt_size)
+#define PV_XSAVE_SIZE(xcr0) (2 * sizeof(uint64_t) + xstate_ctxt_size(xcr0))
evc = &domctl->u.vcpuextstate;
@@ -1399,15 +1396,16 @@ long arch_do_domctl(
if ( domctl->cmd == XEN_DOMCTL_getvcpuextstate )
{
+ unsigned int size = PV_XSAVE_SIZE(v->arch.xcr0_accum);
+
if ( !evc->size && !evc->xfeature_mask )
{
evc->xfeature_mask = xfeature_mask;
- evc->size = PV_XSAVE_SIZE;
+ evc->size = size;
ret = 0;
goto vcpuextstate_out;
}
- if ( evc->size != PV_XSAVE_SIZE ||
- evc->xfeature_mask != xfeature_mask )
+ if ( evc->size != size || evc->xfeature_mask != xfeature_mask )
{
ret = -EINVAL;
goto vcpuextstate_out;
@@ -1430,7 +1428,7 @@ long arch_do_domctl(
offset += sizeof(v->arch.xcr0_accum);
if ( copy_to_guest_offset(domctl->u.vcpuextstate.buffer,
offset, (void *)v->arch.xsave_area,
- xsave_cntxt_size) )
+ size - 2 * sizeof(uint64_t)) )
{
ret = -EFAULT;
goto vcpuextstate_out;
@@ -1438,13 +1436,14 @@ long arch_do_domctl(
}
else
{
- ret = -EINVAL;
+ void *receive_buf;
+ uint64_t _xcr0, _xcr0_accum;
+ const struct xsave_struct *_xsave_area;
- _xfeature_mask = evc->xfeature_mask;
- /* xsave context must be restored on compatible target CPUs */
- if ( (_xfeature_mask & xfeature_mask) != _xfeature_mask )
- goto vcpuextstate_out;
- if ( evc->size > PV_XSAVE_SIZE || evc->size < 2 * sizeof(uint64_t) )
+ ret = -EINVAL;
+ if ( evc->size < 2 * sizeof(uint64_t) ||
+ evc->size > 2 * sizeof(uint64_t) +
+ xstate_ctxt_size(xfeature_mask) )
goto vcpuextstate_out;
receive_buf = xmalloc_bytes(evc->size);
@@ -1465,20 +1464,30 @@ long arch_do_domctl(
_xcr0_accum = *(uint64_t *)(receive_buf + sizeof(uint64_t));
_xsave_area = receive_buf + 2 * sizeof(uint64_t);
- if ( !(_xcr0 & XSTATE_FP) || _xcr0 & ~xfeature_mask )
+ if ( _xcr0_accum )
{
- xfree(receive_buf);
- goto vcpuextstate_out;
+ if ( evc->size >= 2 * sizeof(uint64_t) + XSTATE_AREA_MIN_SIZE )
+ ret = validate_xstate(_xcr0, _xcr0_accum,
+ _xsave_area->xsave_hdr.xstate_bv,
+ evc->xfeature_mask);
}
- if ( (_xcr0 & _xcr0_accum) != _xcr0 )
+ else if ( !_xcr0 )
+ ret = 0;
+ if ( ret )
{
xfree(receive_buf);
goto vcpuextstate_out;
}
- v->arch.xcr0 = _xcr0;
- v->arch.xcr0_accum = _xcr0_accum;
- memcpy(v->arch.xsave_area, _xsave_area, evc->size - 2 * sizeof(uint64_t) );
+ if ( evc->size <= PV_XSAVE_SIZE(_xcr0_accum) )
+ {
+ v->arch.xcr0 = _xcr0;
+ v->arch.xcr0_accum = _xcr0_accum;
+ memcpy(v->arch.xsave_area, _xsave_area,
+ evc->size - 2 * sizeof(uint64_t));
+ }
+ else
+ ret = -EINVAL;
xfree(receive_buf);
}