[libvirt] [PATCH 03/10] cpu_x86: Add full support for ecx_in CPUID parameter

Peter Krempa pkrempa at redhat.com
Thu Jun 9 06:15:12 UTC 2016


On Wed, Jun 08, 2016 at 14:41:31 +0200, Jiri Denemark wrote:
> This patch makes our CPUID handling code up-to-date with the current
> specification found in
> 
> Intel® 64 and IA-32 Architectures Developer's Manual: Vol. 2A
> http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html
> 
> Signed-off-by: Jiri Denemark <jdenemar at redhat.com>
> ---
>  src/cpu/cpu_map.xml |   1 -
>  src/cpu/cpu_x86.c   | 278 ++++++++++++++++++++++++++++++++++++++++++++++++++--
>  2 files changed, 269 insertions(+), 10 deletions(-)
> 

[]

> @@ -1862,8 +1862,7 @@ cpuidCall(virCPUx86CPUID *cpuid)
>  {
>  # if __x86_64__
>      asm("xor %%ebx, %%ebx;" /* clear the other registers as some cpuid */
> -        "xor %%ecx, %%ecx;" /* functions may use them as additional */
> -        "xor %%edx, %%edx;" /* arguments */
> +        "xor %%edx, %%edx;" /* functions may use them as additional arguments */
>          "cpuid;"
>          : "=a" (cpuid->eax),
>            "=b" (cpuid->ebx),
> @@ -1877,8 +1876,7 @@ cpuidCall(virCPUx86CPUID *cpuid)
>       */
>      asm("push %%ebx;"
>          "xor %%ebx, %%ebx;" /* clear the other registers as some cpuid */
> -        "xor %%ecx, %%ecx;" /* functions may use them as additional */
> -        "xor %%edx, %%edx;" /* arguments */
> +        "xor %%edx, %%edx;" /* functions may use them as additional arguments */
>          "cpuid;"
>          "mov %%ebx, %1;"
>          "pop %%ebx;"

As said. This belongs to previous patch.

> @@ -1893,21 +1891,283 @@ cpuidCall(virCPUx86CPUID *cpuid)
>  }
>  
>  
> +/* Leaf 0x4

Deterministic cache parameters.

This leaf is borderline useful for CPU identification.

> + *
> + * Sub leaf n+1 is invalid if eax[4:0] in sub leaf n equals 0.
> + */
> +static int
> +cpuidSetLeaf4(virCPUx86Data *data,
> +              virCPUx86CPUID *subLeaf0)
> +{
> +    virCPUx86CPUID cpuid = *subLeaf0;
> +
> +    if (virCPUx86DataAddCPUID(data, subLeaf0) < 0)
> +        return -1;
> +
> +    while (cpuid.eax & 0x1f) {
> +        cpuid.ecx_in++;
> +        cpuidCall(&cpuid);
> +        if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
> +            return -1;
> +    }
> +    return 0;
> +}
> +
> +
> +/* Leaf 0x7

Structured extended feature flags enumeration. Very useful. Although it
looks like all the data is currently reported with ECX == 0.

> + *
> + * Sub leaf n is invalid if n > eax in sub leaf 0.
> + */
> +static int
> +cpuidSetLeaf7(virCPUx86Data *data,
> +              virCPUx86CPUID *subLeaf0)
> +{
> +    virCPUx86CPUID cpuid = { .eax_in = 0x7 };
> +    uint32_t sub;
> +
> +    if (virCPUx86DataAddCPUID(data, subLeaf0) < 0)
> +        return -1;
> +
> +    for (sub = 1; sub <= subLeaf0->eax; sub++) {
> +        cpuid.ecx_in = sub;
> +        cpuidCall(&cpuid);
> +        if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
> +            return -1;
> +    }
> +    return 0;
> +}
> +
> +
> +/* Leaf 0xb

Extended topology enumeration

> + *
> + * Sub leaf n is invalid if it returns 0 in ecx[15:8].
> + * Sub leaf n+1 is invalid if sub leaf n is invalid.
> + * Some output values do not depend on ecx, thus sub leaf 0 provides
> + * meaningful data even if it was (theoretically) considered invalid.

Mostly EDX which reports APIC ID of the current processor which is
volatile anyways. Not sure whether it's worth collecting this leaf at
all since the returned data depend very specifically on the cpu where
it's executed. Also as we don't call it for all cpus it might be
pre-empted to a different CPU while enumerating.

> + */
> +static int
> +cpuidSetLeafB(virCPUx86Data *data,
> +              virCPUx86CPUID *subLeaf0)
> +{
> +    virCPUx86CPUID cpuid = *subLeaf0;
> +
> +    while (cpuid.ecx & 0xff00) {
> +        if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
> +            return -1;
> +        cpuid.ecx_in++;
> +        cpuidCall(&cpuid);
> +    }
> +    return 0;
> +}
> +
> +
> +/* Leaf 0xd

processor extended state enumeration

> + *
> + * Sub leaves 0 and 1 are valid.
> + * Sub leaf n (2 <= n < 32) is invalid if eax[n] from sub leaf 0 is not set
> + * and ecx[n] from sub leaf 1 is not set.
> + * Sub leaf n (32 <= n < 64) is invalid if edx[n-32] from sub leaf 0 is not set
> + * and edx[n-32] from sub leaf 1 is not set.
> + */
> +static int
> +cpuidSetLeafD(virCPUx86Data *data,
> +              virCPUx86CPUID *subLeaf0)
> +{
> +    virCPUx86CPUID cpuid = { .eax_in = 0xd };
> +    virCPUx86CPUID sub0;
> +    virCPUx86CPUID sub1;
> +    uint32_t sub;
> +
> +    if (virCPUx86DataAddCPUID(data, subLeaf0) < 0)
> +        return -1;
> +
> +    cpuid.ecx_in = 1;
> +    cpuidCall(&cpuid);
> +    if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
> +        return -1;
> +
> +    sub0 = *subLeaf0;
> +    sub1 = cpuid;
> +    for (sub = 2; sub < 64; sub++) {
> +        if (sub < 32 &&
> +            !(sub0.eax & (1 << sub)) &&
> +            !(sub1.ecx & (1 << sub)))
> +            continue;
> +        if (sub >= 32 &&
> +            !(sub0.edx & (1 << (sub - 32))) &&
> +            !(sub1.edx & (1 << (sub - 32))))
> +            continue;
> +
> +        cpuid.ecx_in = sub;
> +        cpuidCall(&cpuid);
> +        if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
> +            return -1;
> +    }
> +    return 0;
> +}
> +
> +
> +/* Leaf 0xf, 0x10

0x0f - L3 cached RDT monitoring capability enumeration
0x10 - RDT allocation enumeration

> + *
> + * res reports valid resource identification (ResID) starting at bit 1.
> + * Values associated with each valid ResID are reported by ResID sub leaf.
> + *
> + * 0xf:  Sub leaf n is valid if edx[n] (= res[ResID]) from sub leaf 0 is set.
> + * 0x10: Sub leaf n is valid if ebx[n] (= res[ResID]) from sub leaf 0 is set.
> + */
> +static int
> +cpuidSetLeafResID(virCPUx86Data *data,
> +                  virCPUx86CPUID *subLeaf0,
> +                  uint32_t res)
> +{
> +    virCPUx86CPUID cpuid = { .eax_in = subLeaf0->eax_in };
> +    uint32_t sub;
> +
> +    if (virCPUx86DataAddCPUID(data, subLeaf0) < 0)
> +        return -1;
> +
> +    for (sub = 1; sub < 32; sub++) {
> +        if (!(res & (1 << sub)))
> +            continue;
> +        cpuid.ecx_in = sub;
> +        cpuidCall(&cpuid);
> +        if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
> +            return -1;
> +    }
> +    return 0;
> +}
> +
> +
> +/* Leaf 0x12

SGX Capability enumeration

> + *
> + * Sub leaves 0 and 1 is supported if ebx[2] from leaf 0x7 (SGX) is set.
> + * Sub leaves n >= 2 are valid as long as eax[3:0] != 0.
> + */
> +static int
> +cpuidSetLeaf12(virCPUx86Data *data,
> +               virCPUx86CPUID *subLeaf0)
> +{
> +    virCPUx86CPUID cpuid = { .eax_in = 0x7 };
> +    virCPUx86CPUID *cpuid7;
> +
> +    if (!(cpuid7 = x86DataCpuid(data, &cpuid)) ||
> +        !(cpuid7->ebx & (1 << 2)))
> +        return 0;
> +
> +    if (virCPUx86DataAddCPUID(data, subLeaf0) < 0)
> +        return -1;
> +
> +    cpuid.eax_in = 0x12;
> +    cpuid.ecx_in = 1;
> +    cpuidCall(&cpuid);
> +    if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
> +        return -1;
> +
> +    cpuid.ecx_in = 2;
> +    cpuidCall(&cpuid);
> +    while (cpuid.eax & 0xf) {
> +        if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
> +            return -1;
> +        cpuid.ecx_in++;
> +        cpuidCall(&cpuid);
> +    }
> +    return 0;
> +}
> +
> +
> +/* Leaf 0x14

Processor trace enumeration

This function is the same as for 0x07
> + *
> + * Sub leaf 0 reports the maximum supported sub leaf in eax.
> + */
> +static int
> +cpuidSetLeaf14(virCPUx86Data *data,
> +               virCPUx86CPUID *subLeaf0)
> +{
> +    virCPUx86CPUID cpuid = { .eax_in = 0x14 };
> +    uint32_t sub;
> +
> +    if (virCPUx86DataAddCPUID(data, subLeaf0) < 0)
> +        return -1;
> +
> +    for (sub = 1; sub <= subLeaf0->eax; sub++) {
> +        cpuid.ecx_in = sub;
> +        cpuidCall(&cpuid);
> +        if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
> +            return -1;
> +    }
> +    return 0;
> +}
> +
> +
> +/* Leaf 0x17

SOC Vendor

> + *
> + * Sub leaf 0 is valid if eax >= 3.
> + * Sub leaf 0 reports the maximum supported sub leaf in eax.
> + */
> +static int
> +cpuidSetLeaf17(virCPUx86Data *data,
> +               virCPUx86CPUID *subLeaf0)
> +{
> +    virCPUx86CPUID cpuid = { .eax_in = 0x17 };
> +    uint32_t sub;
> +
> +    if (subLeaf0->eax < 3)
> +        return 0;
> +
> +    if (virCPUx86DataAddCPUID(data, subLeaf0) < 0)
> +        return -1;
> +
> +    for (sub = 1; sub <= subLeaf0->eax; sub++) {
> +        cpuid.ecx_in = sub;
> +        cpuidCall(&cpuid);
> +        if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
> +            return -1;
> +    }
> +    return 0;
> +}

Most of the data collected in other leaves is not very useful for cpu
identification for our purposes but that's true also for the main leaves
(ECX=0).

ACK. I'd appreciate if you added names for the leaves for future
reference (not everyone enjoys reading through the x86_64 reference)
but it's not strictly required.




More information about the libvir-list mailing list