@@ -269,6 +269,115 @@ static void encode_cache_cpuid4(CPUCacheInfo *cache,
(cache->complex_indexing ? CACHE_COMPLEX_IDX : 0);
}
+static uint32_t num_threads_by_topo_level(X86CPUTopoInfo *topo_info,
+ enum CPUTopoLevel topo_level)
+{
+ switch (topo_level) {
+ case CPU_TOPO_LEVEL_SMT:
+ return 1;
+ case CPU_TOPO_LEVEL_CORE:
+ return topo_info->threads_per_core;
+ case CPU_TOPO_LEVEL_DIE:
+ return topo_info->threads_per_core * topo_info->cores_per_die;
+ case CPU_TOPO_LEVEL_PACKAGE:
+ return topo_info->threads_per_core * topo_info->cores_per_die *
+ topo_info->dies_per_pkg;
+ default:
+ g_assert_not_reached();
+ }
+ return 0;
+}
+
+static uint32_t apicid_offset_by_topo_level(X86CPUTopoInfo *topo_info,
+ enum CPUTopoLevel topo_level)
+{
+ switch (topo_level) {
+ case CPU_TOPO_LEVEL_SMT:
+ return 0;
+ case CPU_TOPO_LEVEL_CORE:
+ return apicid_core_offset(topo_info);
+ case CPU_TOPO_LEVEL_DIE:
+ return apicid_die_offset(topo_info);
+ case CPU_TOPO_LEVEL_PACKAGE:
+ return apicid_pkg_offset(topo_info);
+ default:
+ g_assert_not_reached();
+ }
+ return 0;
+}
+
+static uint32_t cpuid1f_topo_type(enum CPUTopoLevel topo_level)
+{
+ switch (topo_level) {
+ case CPU_TOPO_LEVEL_INVALID:
+ return CPUID_1F_ECX_TOPO_LEVEL_INVALID;
+ case CPU_TOPO_LEVEL_SMT:
+ return CPUID_1F_ECX_TOPO_LEVEL_SMT;
+ case CPU_TOPO_LEVEL_CORE:
+ return CPUID_1F_ECX_TOPO_LEVEL_CORE;
+ case CPU_TOPO_LEVEL_DIE:
+ return CPUID_1F_ECX_TOPO_LEVEL_DIE;
+ default:
+ /* Other types are not supported in QEMU. */
+ g_assert_not_reached();
+ }
+ return 0;
+}
+
+static void encode_topo_cpuid1f(CPUX86State *env, uint32_t count,
+ X86CPUTopoInfo *topo_info,
+ uint32_t *eax, uint32_t *ebx,
+ uint32_t *ecx, uint32_t *edx)
+{
+ X86CPU *cpu = env_archcpu(env);
+ unsigned long level, next_level;
+ uint32_t num_threads_next_level, offset_next_level;
+
+ assert(count + 1 < CPU_TOPO_LEVEL_MAX);
+
+ /*
+ * Find the No.(count + 1) topology level in avail_cpu_topo bitmap.
+ * The search starts from bit 1 (CPU_TOPO_LEVEL_INVALID + 1).
+ */
+ level = CPU_TOPO_LEVEL_INVALID;
+ for (int i = 0; i <= count; i++) {
+ level = find_next_bit(env->avail_cpu_topo,
+ CPU_TOPO_LEVEL_PACKAGE,
+ level + 1);
+
+ /*
+ * CPUID[0x1f] doesn't explicitly encode the package level,
+ * and it just encodes the invalid level (all fields are 0)
+ * into the last subleaf of 0x1f.
+ */
+ if (level == CPU_TOPO_LEVEL_PACKAGE) {
+ level = CPU_TOPO_LEVEL_INVALID;
+ break;
+ }
+ }
+
+ if (level == CPU_TOPO_LEVEL_INVALID) {
+ num_threads_next_level = 0;
+ offset_next_level = 0;
+ } else {
+ next_level = find_next_bit(env->avail_cpu_topo,
+ CPU_TOPO_LEVEL_PACKAGE,
+ level + 1);
+ num_threads_next_level = num_threads_by_topo_level(topo_info,
+ next_level);
+ offset_next_level = apicid_offset_by_topo_level(topo_info,
+ next_level);
+ }
+
+ *eax = offset_next_level;
+ /* The count (bits 15-00) doesn't need to be reliable. */
+ *ebx = num_threads_next_level & 0xffff;
+ *ecx = (count & 0xff) | (cpuid1f_topo_type(level) << 8);
+ *edx = cpu->apic_id;
+
+ assert(!(*eax & ~0x1f));
+}
+
/* Encode cache info for CPUID[0x80000005].ECX or CPUID[0x80000005].EDX */
static uint32_t encode_cache_cpuid80000005(CPUCacheInfo *cache)
{
@@ -6431,31 +6540,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
break;
}
- *ecx = count & 0xff;
- *edx = cpu->apic_id;
- switch (count) {
- case 0:
- *eax = apicid_core_offset(&topo_info);
- *ebx = topo_info.threads_per_core;
- *ecx |= CPUID_1F_ECX_TOPO_LEVEL_SMT << 8;
- break;
- case 1:
- *eax = apicid_die_offset(&topo_info);
- *ebx = topo_info.cores_per_die * topo_info.threads_per_core;
- *ecx |= CPUID_1F_ECX_TOPO_LEVEL_CORE << 8;
- break;
- case 2:
- *eax = apicid_pkg_offset(&topo_info);
- *ebx = threads_per_pkg;
- *ecx |= CPUID_1F_ECX_TOPO_LEVEL_DIE << 8;
- break;
- default:
- *eax = 0;
- *ebx = 0;
- *ecx |= CPUID_1F_ECX_TOPO_LEVEL_INVALID << 8;
- }
- assert(!(*eax & ~0x1f));
- *ebx &= 0xffff; /* The count doesn't need to be reliable. */
+ encode_topo_cpuid1f(env, count, &topo_info, eax, ebx, ecx, edx);
break;
case 0xD: {
/* Processor Extended State */