diff mbox

[22/27] x86/cpuid: Perform max_leaf calculations in guest_cpuid()

Message ID 1483533584-8015-23-git-send-email-andrew.cooper3@citrix.com (mailing list archive)
State New, archived
Headers show

Commit Message

Andrew Cooper Jan. 4, 2017, 12:39 p.m. UTC
Clamp the toolstack-providied max_leaf values in recalculate_cpuid_policy(),
causing the per-domain policy to have guest-accurate data.

Have guest_cpuid() exit early if a requested leaf is out of range, rather than
falling into the legacy path.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
CC: Jan Beulich <JBeulich@suse.com>
---
 xen/arch/x86/cpuid.c        | 36 ++++++++++++++++++++++++++++++++++++
 xen/arch/x86/hvm/hvm.c      | 21 ---------------------
 xen/arch/x86/traps.c        | 23 -----------------------
 xen/include/asm-x86/cpuid.h |  1 +
 4 files changed, 37 insertions(+), 44 deletions(-)

Comments

Jan Beulich Jan. 5, 2017, 1:51 p.m. UTC | #1
>>> On 04.01.17 at 13:39, <andrew.cooper3@citrix.com> wrote:
> @@ -306,6 +310,9 @@ void recalculate_cpuid_policy(struct domain *d)
>      if ( !d->disable_migrate && !d->arch.vtsc )
>          __clear_bit(X86_FEATURE_ITSC, fs);
>  
> +    if ( p->basic.max_leaf < 0xd )

XSTATE_CPUID

> @@ -333,21 +340,50 @@ void guest_cpuid(const struct vcpu *v, unsigned int leaf,
>                   unsigned int subleaf, struct cpuid_leaf *res)
>  {
>      const struct domain *d = v->domain;
> +    const struct cpuid_policy *p = d->arch.cpuid;
>  
>      *res = EMPTY_LEAF;
>  
>      /*
>       * First pass:
>       * - Dispatch the virtualised leaves to their respective handlers.
> +     * - Perform max_leaf/subleaf calculations, maybe returning early.
>       */
>      switch ( leaf )
>      {
> +    case 0x0 ... 0x6:
> +    case 0x8 ... 0xc:
> +#if 0 /* For when CPUID_GUEST_NR_BASIC isn't 0xd */
> +    case 0xe ... CPUID_GUEST_NR_BASIC - 1:
> +#endif

Perhaps have a BUILD_BUG_ON() in an #else here?

> +        if ( leaf > p->basic.max_leaf )
> +            return;
> +        break;
> +
> +    case 0x7:
> +        if ( subleaf > p->feat.max_subleaf )
> +            return;
> +        break;
> +
> +    case 0xd:

XSTATE_CPUID again, which raises the question whether switch()
really is the best way to deal with things here.

> --- a/xen/arch/x86/hvm/hvm.c
> +++ b/xen/arch/x86/hvm/hvm.c
> @@ -3305,27 +3305,6 @@ void hvm_cpuid(unsigned int input, unsigned int *eax, unsigned int *ebx,
>      if ( !edx )
>          edx = &dummy;
>  
> -    if ( input & 0x7fffffff )
> -    {
> -        /*
> -         * Requests outside the supported leaf ranges return zero on AMD
> -         * and the highest basic leaf output on Intel. Uniformly follow
> -         * the AMD model as the more sane one.
> -         */

I think this comment would better be moved instead of deleted.

Jan
Andrew Cooper Jan. 5, 2017, 2:28 p.m. UTC | #2
On 05/01/17 13:51, Jan Beulich wrote:
>
>> @@ -333,21 +340,50 @@ void guest_cpuid(const struct vcpu *v, unsigned int leaf,
>>                   unsigned int subleaf, struct cpuid_leaf *res)
>>  {
>>      const struct domain *d = v->domain;
>> +    const struct cpuid_policy *p = d->arch.cpuid;
>>  
>>      *res = EMPTY_LEAF;
>>  
>>      /*
>>       * First pass:
>>       * - Dispatch the virtualised leaves to their respective handlers.
>> +     * - Perform max_leaf/subleaf calculations, maybe returning early.
>>       */
>>      switch ( leaf )
>>      {
>> +    case 0x0 ... 0x6:
>> +    case 0x8 ... 0xc:
>> +#if 0 /* For when CPUID_GUEST_NR_BASIC isn't 0xd */
>> +    case 0xe ... CPUID_GUEST_NR_BASIC - 1:
>> +#endif
> Perhaps have a BUILD_BUG_ON() in an #else here?

The presence of this was to be a reminder to whomever tries upping
max_leaf beyond 0xd.  Then again, there is a reasonable chance it will
be me.

I am half tempted to leave it out.

>
>> +        if ( leaf > p->basic.max_leaf )
>> +            return;
>> +        break;
>> +
>> +    case 0x7:
>> +        if ( subleaf > p->feat.max_subleaf )
>> +            return;
>> +        break;
>> +
>> +    case 0xd:
> XSTATE_CPUID again,

I considered this, but having a mix of named an numbered leaves is worse
than having them uniformly numbered, especially when visually checking
the conditions around the #if 0 case above.

I had considered making a cpuid-index.h for leaf names, but most leaves
are more commonly referred to by number than name, so I am really not
sure if that would be helpful or hindering in the long run.

> which raises the question whether switch() really is the best way to deal with things here.

What else would you suggest?  One way or another (better shown in the
context of the following patch), we need one block per union{} to apply
max_leaf calculations and read the base data from p->$FOO.raw[$IDX].

>
>> --- a/xen/arch/x86/hvm/hvm.c
>> +++ b/xen/arch/x86/hvm/hvm.c
>> @@ -3305,27 +3305,6 @@ void hvm_cpuid(unsigned int input, unsigned int *eax, unsigned int *ebx,
>>      if ( !edx )
>>          edx = &dummy;
>>  
>> -    if ( input & 0x7fffffff )
>> -    {
>> -        /*
>> -         * Requests outside the supported leaf ranges return zero on AMD
>> -         * and the highest basic leaf output on Intel. Uniformly follow
>> -         * the AMD model as the more sane one.
>> -         */
> I think this comment would better be moved instead of deleted.

Where would you like it?  It doesn't have an easy logical place to live
in guest_cpuid().  The best I can think of is probably as an extension
of the "First Pass" comment.

~Andrew
Jan Beulich Jan. 5, 2017, 2:52 p.m. UTC | #3
>>> On 05.01.17 at 15:28, <andrew.cooper3@citrix.com> wrote:
> On 05/01/17 13:51, Jan Beulich wrote:
>>> @@ -333,21 +340,50 @@ void guest_cpuid(const struct vcpu *v, unsigned int leaf,
>>>                   unsigned int subleaf, struct cpuid_leaf *res)
>>>  {
>>>      const struct domain *d = v->domain;
>>> +    const struct cpuid_policy *p = d->arch.cpuid;
>>>  
>>>      *res = EMPTY_LEAF;
>>>  
>>>      /*
>>>       * First pass:
>>>       * - Dispatch the virtualised leaves to their respective handlers.
>>> +     * - Perform max_leaf/subleaf calculations, maybe returning early.
>>>       */
>>>      switch ( leaf )
>>>      {
>>> +    case 0x0 ... 0x6:
>>> +    case 0x8 ... 0xc:
>>> +#if 0 /* For when CPUID_GUEST_NR_BASIC isn't 0xd */
>>> +    case 0xe ... CPUID_GUEST_NR_BASIC - 1:
>>> +#endif
>> Perhaps have a BUILD_BUG_ON() in an #else here?
> 
> The presence of this was to be a reminder to whomever tries upping
> max_leaf beyond 0xd.  Then again, there is a reasonable chance it will
> be me.

Well, that's why the recommendation to add a BUILD_BUG_ON() -
that's a reminder to that "whoever".

>>> +        if ( leaf > p->basic.max_leaf )
>>> +            return;
>>> +        break;
>>> +
>>> +    case 0x7:
>>> +        if ( subleaf > p->feat.max_subleaf )
>>> +            return;
>>> +        break;
>>> +
>>> +    case 0xd:
>> XSTATE_CPUID again,
> 
> I considered this, but having a mix of named an numbered leaves is worse
> than having them uniformly numbered, especially when visually checking
> the conditions around the #if 0 case above.
> 
> I had considered making a cpuid-index.h for leaf names, but most leaves
> are more commonly referred to by number than name, so I am really not
> sure if that would be helpful or hindering in the long run.
> 
>> which raises the question whether switch() really is the best way to deal 
> with things here.
> 
> What else would you suggest?  One way or another (better shown in the
> context of the following patch), we need one block per union{} to apply
> max_leaf calculations and read the base data from p->$FOO.raw[$IDX].

Actually, perhaps a mixture: Inside the default case have

    if ( leaf == 0x7 )
    {
        if ( subleaf > p->feat.max_subleaf )
            return;
    }
    else if ( leaf == 0xd)
    {
        if ( subleaf > ARRAY_SIZE(p->xstate.raw) )
            return;
    }
    if ( leaf > p->basic.max_leaf )
        return;

Which (by making the last one if rather than else-if) also fixes an
issue I've spotted only now: So far you exclude leaves 7 and 0xd
from the basic.max_leaf checking. (And this way that check could
also go first.)

>>> --- a/xen/arch/x86/hvm/hvm.c
>>> +++ b/xen/arch/x86/hvm/hvm.c
>>> @@ -3305,27 +3305,6 @@ void hvm_cpuid(unsigned int input, unsigned int *eax, unsigned int *ebx,
>>>      if ( !edx )
>>>          edx = &dummy;
>>>  
>>> -    if ( input & 0x7fffffff )
>>> -    {
>>> -        /*
>>> -         * Requests outside the supported leaf ranges return zero on AMD
>>> -         * and the highest basic leaf output on Intel. Uniformly follow
>>> -         * the AMD model as the more sane one.
>>> -         */
>> I think this comment would better be moved instead of deleted.
> 
> Where would you like it?  It doesn't have an easy logical place to live
> in guest_cpuid().  The best I can think of is probably as an extension
> of the "First Pass" comment.

Right there, yes, as an extension to the line you're already adding.

Jan
Andrew Cooper Jan. 5, 2017, 3:02 p.m. UTC | #4
On 05/01/17 14:52, Jan Beulich wrote:
>>>> On 05.01.17 at 15:28, <andrew.cooper3@citrix.com> wrote:
>> On 05/01/17 13:51, Jan Beulich wrote:
>>>> @@ -333,21 +340,50 @@ void guest_cpuid(const struct vcpu *v, unsigned int leaf,
>>>>                   unsigned int subleaf, struct cpuid_leaf *res)
>>>>  {
>>>>      const struct domain *d = v->domain;
>>>> +    const struct cpuid_policy *p = d->arch.cpuid;
>>>>  
>>>>      *res = EMPTY_LEAF;
>>>>  
>>>>      /*
>>>>       * First pass:
>>>>       * - Dispatch the virtualised leaves to their respective handlers.
>>>> +     * - Perform max_leaf/subleaf calculations, maybe returning early.
>>>>       */
>>>>      switch ( leaf )
>>>>      {
>>>> +    case 0x0 ... 0x6:
>>>> +    case 0x8 ... 0xc:
>>>> +#if 0 /* For when CPUID_GUEST_NR_BASIC isn't 0xd */
>>>> +    case 0xe ... CPUID_GUEST_NR_BASIC - 1:
>>>> +#endif
>>> Perhaps have a BUILD_BUG_ON() in an #else here?
>> The presence of this was to be a reminder to whomever tries upping
>> max_leaf beyond 0xd.  Then again, there is a reasonable chance it will
>> be me.
> Well, that's why the recommendation to add a BUILD_BUG_ON() -
> that's a reminder to that "whoever".

Ok.

>
>>>> +        if ( leaf > p->basic.max_leaf )
>>>> +            return;
>>>> +        break;
>>>> +
>>>> +    case 0x7:
>>>> +        if ( subleaf > p->feat.max_subleaf )
>>>> +            return;
>>>> +        break;
>>>> +
>>>> +    case 0xd:
>>> XSTATE_CPUID again,
>> I considered this, but having a mix of named an numbered leaves is worse
>> than having them uniformly numbered, especially when visually checking
>> the conditions around the #if 0 case above.
>>
>> I had considered making a cpuid-index.h for leaf names, but most leaves
>> are more commonly referred to by number than name, so I am really not
>> sure if that would be helpful or hindering in the long run.
>>
>>> which raises the question whether switch() really is the best way to deal 
>> with things here.
>>
>> What else would you suggest?  One way or another (better shown in the
>> context of the following patch), we need one block per union{} to apply
>> max_leaf calculations and read the base data from p->$FOO.raw[$IDX].
> Actually, perhaps a mixture: Inside the default case have
>
>     if ( leaf == 0x7 )
>     {
>         if ( subleaf > p->feat.max_subleaf )
>             return;
>     }
>     else if ( leaf == 0xd)
>     {
>         if ( subleaf > ARRAY_SIZE(p->xstate.raw) )
>             return;
>     }
>     if ( leaf > p->basic.max_leaf )
>         return;
>
> Which (by making the last one if rather than else-if) also fixes an
> issue I've spotted only now: So far you exclude leaves 7 and 0xd
> from the basic.max_leaf checking. (And this way that check could
> also go first.)

Very good point, although I still think I'd still prefer a logic block
in this form inside a case 0 ... 0x3fffffff to avoid potential leakage
if other logic changes.

~Andrew
Jan Beulich Jan. 5, 2017, 3:39 p.m. UTC | #5
>>> On 05.01.17 at 16:02, <andrew.cooper3@citrix.com> wrote:
> On 05/01/17 14:52, Jan Beulich wrote:
>>>>> On 05.01.17 at 15:28, <andrew.cooper3@citrix.com> wrote:
>>> What else would you suggest?  One way or another (better shown in the
>>> context of the following patch), we need one block per union{} to apply
>>> max_leaf calculations and read the base data from p->$FOO.raw[$IDX].
>> Actually, perhaps a mixture: Inside the default case have
>>
>>     if ( leaf == 0x7 )
>>     {
>>         if ( subleaf > p->feat.max_subleaf )
>>             return;
>>     }
>>     else if ( leaf == 0xd)
>>     {
>>         if ( subleaf > ARRAY_SIZE(p->xstate.raw) )
>>             return;
>>     }
>>     if ( leaf > p->basic.max_leaf )
>>         return;
>>
>> Which (by making the last one if rather than else-if) also fixes an
>> issue I've spotted only now: So far you exclude leaves 7 and 0xd
>> from the basic.max_leaf checking. (And this way that check could
>> also go first.)
> 
> Very good point, although I still think I'd still prefer a logic block
> in this form inside a case 0 ... 0x3fffffff to avoid potential leakage
> if other logic changes.

Well, that's certainly fine with me.

Jan
diff mbox

Patch

diff --git a/xen/arch/x86/cpuid.c b/xen/arch/x86/cpuid.c
index 01bb906..d140482 100644
--- a/xen/arch/x86/cpuid.c
+++ b/xen/arch/x86/cpuid.c
@@ -279,6 +279,10 @@  void recalculate_cpuid_policy(struct domain *d)
     uint32_t fs[FSCAPINTS], max_fs[FSCAPINTS];
     unsigned int i;
 
+    p->basic.max_leaf   = min(p->basic.max_leaf,   max->basic.max_leaf);
+    p->feat.max_subleaf = min(p->feat.max_subleaf, max->feat.max_subleaf);
+    p->extd.max_leaf    = min(p->extd.max_leaf,    max->extd.max_leaf);
+
     cpuid_policy_to_featureset(p, fs);
     cpuid_policy_to_featureset(max, max_fs);
 
@@ -306,6 +310,9 @@  void recalculate_cpuid_policy(struct domain *d)
     if ( !d->disable_migrate && !d->arch.vtsc )
         __clear_bit(X86_FEATURE_ITSC, fs);
 
+    if ( p->basic.max_leaf < 0xd )
+        __clear_bit(X86_FEATURE_XSAVE, fs);
+
     /* Fold host's FDP_EXCP_ONLY and NO_FPU_SEL into guest's view. */
     fs[FEATURESET_7b0] &= ~special_features[FEATURESET_7b0];
     fs[FEATURESET_7b0] |= (host_policy.feat._7b0 &
@@ -333,21 +340,50 @@  void guest_cpuid(const struct vcpu *v, unsigned int leaf,
                  unsigned int subleaf, struct cpuid_leaf *res)
 {
     const struct domain *d = v->domain;
+    const struct cpuid_policy *p = d->arch.cpuid;
 
     *res = EMPTY_LEAF;
 
     /*
      * First pass:
      * - Dispatch the virtualised leaves to their respective handlers.
+     * - Perform max_leaf/subleaf calculations, maybe returning early.
      */
     switch ( leaf )
     {
+    case 0x0 ... 0x6:
+    case 0x8 ... 0xc:
+#if 0 /* For when CPUID_GUEST_NR_BASIC isn't 0xd */
+    case 0xe ... CPUID_GUEST_NR_BASIC - 1:
+#endif
+        if ( leaf > p->basic.max_leaf )
+            return;
+        break;
+
+    case 0x7:
+        if ( subleaf > p->feat.max_subleaf )
+            return;
+        break;
+
+    case 0xd:
+        if ( subleaf > ARRAY_SIZE(p->xstate.raw) )
+            return;
+        break;
+
     case 0x40000000 ... 0x400000ff:
         if ( is_viridian_domain(d) )
             return cpuid_viridian_leaves(v, leaf, subleaf, res);
         /* Fallthrough. */
     case 0x40000100 ... 0x4fffffff:
         return cpuid_hypervisor_leaves(v, leaf, subleaf, res);
+
+    case 0x80000000 ... 0x80000000 + CPUID_GUEST_NR_EXTD - 1:
+        if ( leaf > p->extd.max_leaf )
+            return;
+        break;
+
+    default:
+        return;
     }
 
     /* {pv,hvm}_cpuid() have this expectation. */
diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index 7cda53f..1dd92e3 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -3305,27 +3305,6 @@  void hvm_cpuid(unsigned int input, unsigned int *eax, unsigned int *ebx,
     if ( !edx )
         edx = &dummy;
 
-    if ( input & 0x7fffffff )
-    {
-        /*
-         * Requests outside the supported leaf ranges return zero on AMD
-         * and the highest basic leaf output on Intel. Uniformly follow
-         * the AMD model as the more sane one.
-         */
-        unsigned int limit;
-
-        domain_cpuid(d, (input >> 16) != 0x8000 ? 0 : 0x80000000, 0,
-                     &limit, &dummy, &dummy, &dummy);
-        if ( input > limit )
-        {
-            *eax = 0;
-            *ebx = 0;
-            *ecx = 0;
-            *edx = 0;
-            return;
-        }
-    }
-
     domain_cpuid(d, input, count, eax, ebx, ecx, edx);
 
     switch ( input )
diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c
index 1c384cf..aed96c3 100644
--- a/xen/arch/x86/traps.c
+++ b/xen/arch/x86/traps.c
@@ -1032,29 +1032,6 @@  void pv_cpuid(struct cpu_user_regs *regs)
     subleaf = c = regs->_ecx;
     d = regs->_edx;
 
-    if ( leaf & 0x7fffffff )
-    {
-        /*
-         * Requests outside the supported leaf ranges return zero on AMD
-         * and the highest basic leaf output on Intel. Uniformly follow
-         * the AMD model as the more sane one.
-         */
-        unsigned int limit = (leaf >> 16) != 0x8000 ? 0 : 0x80000000, dummy;
-
-        if ( !is_control_domain(currd) && !is_hardware_domain(currd) )
-            domain_cpuid(currd, limit, 0, &limit, &dummy, &dummy, &dummy);
-        else
-            limit = cpuid_eax(limit);
-        if ( leaf > limit )
-        {
-            regs->rax = 0;
-            regs->rbx = 0;
-            regs->rcx = 0;
-            regs->rdx = 0;
-            return;
-        }
-    }
-
     if ( !is_control_domain(currd) && !is_hardware_domain(currd) )
         domain_cpuid(currd, leaf, subleaf, &a, &b, &c, &d);
     else
diff --git a/xen/include/asm-x86/cpuid.h b/xen/include/asm-x86/cpuid.h
index c621a6f..7363263 100644
--- a/xen/include/asm-x86/cpuid.h
+++ b/xen/include/asm-x86/cpuid.h
@@ -87,6 +87,7 @@  struct cpuid_policy
      * Per-domain objects:
      *
      * - Guest accurate:
+     *   - max_{,sub}leaf
      *   - All FEATURESET_* words
      *
      * Everything else should be considered inaccurate, and not necesserily 0.