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@xxxxxxxxxx> --- src/cpu/cpu_map.xml | 1 - src/cpu/cpu_x86.c | 278 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 269 insertions(+), 10 deletions(-) diff --git a/src/cpu/cpu_map.xml b/src/cpu/cpu_map.xml index 1ddc55f..14c0e0f 100644 --- a/src/cpu/cpu_map.xml +++ b/src/cpu/cpu_map.xml @@ -289,7 +289,6 @@ </feature> <!-- cpuid function 0x7 ecx 0x0 features --> - <!-- We support only ecx 0x0 now as it's done by a workaround --> <feature name='fsgsbase'> <cpuid eax_in='0x00000007' ebx='0x00000001'/> </feature> diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c index ae809de..6823a27 100644 --- a/src/cpu/cpu_x86.c +++ b/src/cpu/cpu_x86.c @@ -259,7 +259,7 @@ virCPUx86CPUIDSorter(const void *a, const void *b) } -/* skips all zero CPUID leafs */ +/* skips all zero CPUID leaves */ static virCPUx86CPUID * x86DataCpuidNext(virCPUx86DataIteratorPtr iterator) { @@ -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;" @@ -1893,21 +1891,283 @@ cpuidCall(virCPUx86CPUID *cpuid) } +/* Leaf 0x4 + * + * 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 + * + * 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 + * + * 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. + */ +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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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; +} + + static int cpuidSet(uint32_t base, virCPUx86Data *data) { + int rc; uint32_t max; - uint32_t i; + uint32_t leaf; virCPUx86CPUID cpuid = { .eax_in = base }; cpuidCall(&cpuid); max = cpuid.eax; - for (i = base; i <= max; i++) { - cpuid.eax_in = i; + for (leaf = base; leaf <= max; leaf++) { + cpuid.eax_in = leaf; cpuid.ecx_in = 0; cpuidCall(&cpuid); - if (virCPUx86DataAddCPUID(data, &cpuid) < 0) + + /* Handle CPUID leaves that depend on previously queried bits or + * which provide additional sub leaves for ecx_in > 0 + */ + if (leaf == 0x4) + rc = cpuidSetLeaf4(data, &cpuid); + else if (leaf == 0x7) + rc = cpuidSetLeaf7(data, &cpuid); + else if (leaf == 0xb) + rc = cpuidSetLeafB(data, &cpuid); + else if (leaf == 0xd) + rc = cpuidSetLeafD(data, &cpuid); + else if (leaf == 0xf) + rc = cpuidSetLeafResID(data, &cpuid, cpuid.edx); + else if (leaf == 0x10) + rc = cpuidSetLeafResID(data, &cpuid, cpuid.ebx); + else if (leaf == 0x12) + rc = cpuidSetLeaf12(data, &cpuid); + else if (leaf == 0x14) + rc = cpuidSetLeaf14(data, &cpuid); + else if (leaf == 0x17) + rc = cpuidSetLeaf17(data, &cpuid); + else + rc = virCPUx86DataAddCPUID(data, &cpuid); + + if (rc < 0) return -1; } -- 2.8.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list