@@ -268,6 +268,116 @@ static void encode_cache_cpuid4(CPUCacheInfo *cache,
(cache->complex_indexing ? CACHE_COMPLEX_IDX : 0);
}
+static uint32_t num_cpus_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)
+{
+ static DECLARE_BITMAP(topo_bitmap, CPU_TOPO_LEVEL_MAX);
+ X86CPU *cpu = env_archcpu(env);
+ unsigned long level, next_level;
+ uint32_t num_cpus_next_level, offset_next_level;
+
+ /*
+ * Initialize the bitmap to decide which levels should be
+ * encoded in 0x1f.
+ */
+ if (!count) {
+ /* SMT and core levels are exposed in 0x1f leaf by default. */
+ set_bit(CPU_TOPO_LEVEL_SMT, topo_bitmap);
+ set_bit(CPU_TOPO_LEVEL_CORE, topo_bitmap);
+
+ if (env->nr_dies > 1) {
+ set_bit(CPU_TOPO_LEVEL_DIE, topo_bitmap);
+ }
+ }
+
+ *ecx = count & 0xff;
+ *edx = cpu->apic_id;
+
+ level = find_first_bit(topo_bitmap, CPU_TOPO_LEVEL_MAX);
+ if (level == CPU_TOPO_LEVEL_MAX) {
+ num_cpus_next_level = 0;
+ offset_next_level = 0;
+
+ /* Encode CPU_TOPO_LEVEL_INVALID into the last subleaf of 0x1f. */
+ level = CPU_TOPO_LEVEL_INVALID;
+ } else {
+ next_level = find_next_bit(topo_bitmap, CPU_TOPO_LEVEL_MAX, level + 1);
+ if (next_level == CPU_TOPO_LEVEL_MAX) {
+ next_level = CPU_TOPO_LEVEL_PACKAGE;
+ }
+
+ num_cpus_next_level = num_cpus_by_topo_level(topo_info, next_level);
+ offset_next_level = apicid_offset_by_topo_level(topo_info, next_level);
+ }
+
+ *eax = offset_next_level;
+ *ebx = num_cpus_next_level;
+ *ecx |= cpuid1f_topo_type(level) << 8;
+
+ assert(!(*eax & ~0x1f));
+ *ebx &= 0xffff; /* The count doesn't need to be reliable. */
+ if (level != CPU_TOPO_LEVEL_MAX) {
+ clear_bit(level, topo_bitmap);
+ }
+}
+
/* Encode cache info for CPUID[0x80000005].ECX or CPUID[0x80000005].EDX */
static uint32_t encode_cache_cpuid80000005(CPUCacheInfo *cache)
{
@@ -6280,31 +6390,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 = cpus_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 */
@@ -1008,6 +1008,21 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w,
#define CPUID_MWAIT_IBE (1U << 1) /* Interrupts can exit capability */
#define CPUID_MWAIT_EMX (1U << 0) /* enumeration supported */
+/*
+ * CPUTopoLevel is the general i386 topology hierarchical representation,
+ * ordered by increasing hierarchical relationship.
+ * Its enumeration value is not bound to the type value of Intel (CPUID[0x1F])
+ * or AMD (CPUID[0x80000026]).
+ */
+enum CPUTopoLevel {
+ CPU_TOPO_LEVEL_INVALID,
+ CPU_TOPO_LEVEL_SMT,
+ CPU_TOPO_LEVEL_CORE,
+ CPU_TOPO_LEVEL_DIE,
+ CPU_TOPO_LEVEL_PACKAGE,
+ CPU_TOPO_LEVEL_MAX,
+};
+
/* CPUID[0xB].ECX level types */
#define CPUID_B_ECX_TOPO_LEVEL_INVALID 0
#define CPUID_B_ECX_TOPO_LEVEL_SMT 1