* hw/i386/pc_sysfw: Alias rather than copy isa-bios region

* target/i386: add control bits support for LAM
 * target/i386: tweaks to new translator
 * target/i386: add support for LAM in CPUID enumeration
 * hw/i386/pc: Support smp.modules for x86 PC machine
 * target-i386: hyper-v: Correct kvm_hv_handle_exit return value
 -----BEGIN PGP SIGNATURE-----
 
 iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmZOMlAUHHBib256aW5p
 QHJlZGhhdC5jb20ACgkQv/vSX3jHroNTSwf8DOPgipepNcsxUQoV9nOBfNXqEWa6
 DilQGwuu/3eMSPITUCGKVrtLR5azwCwvNfYYErVBPVIhjImnk3XHwfKpH1csadgq
 7Np8WGjAyKEIP/yC/K1VwsanFHv3hmC6jfcO3ZnsnlmbHsRINbvU9uMlFuiQkKJG
 lP/dSUcTVhwLT6eFr9DVDUnq4Nh7j3saY85pZUoDclobpeRLaEAYrawha1/0uQpc
 g7MZYsxT3sg9PIHlM+flpRvJNPz/ZDBdj4raN1xo4q0ET0KRLni6oEOVs5GpTY1R
 t4O8a/IYkxeI15K9U7i0HwYI2wVwKZbHgp9XPMYVZFJdKBGT8bnF56pV9A==
 =lp7q
 -----END PGP SIGNATURE-----

Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging

* hw/i386/pc_sysfw: Alias rather than copy isa-bios region
* target/i386: add control bits support for LAM
* target/i386: tweaks to new translator
* target/i386: add support for LAM in CPUID enumeration
* hw/i386/pc: Support smp.modules for x86 PC machine
* target-i386: hyper-v: Correct kvm_hv_handle_exit return value

# -----BEGIN PGP SIGNATURE-----
#
# iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmZOMlAUHHBib256aW5p
# QHJlZGhhdC5jb20ACgkQv/vSX3jHroNTSwf8DOPgipepNcsxUQoV9nOBfNXqEWa6
# DilQGwuu/3eMSPITUCGKVrtLR5azwCwvNfYYErVBPVIhjImnk3XHwfKpH1csadgq
# 7Np8WGjAyKEIP/yC/K1VwsanFHv3hmC6jfcO3ZnsnlmbHsRINbvU9uMlFuiQkKJG
# lP/dSUcTVhwLT6eFr9DVDUnq4Nh7j3saY85pZUoDclobpeRLaEAYrawha1/0uQpc
# g7MZYsxT3sg9PIHlM+flpRvJNPz/ZDBdj4raN1xo4q0ET0KRLni6oEOVs5GpTY1R
# t4O8a/IYkxeI15K9U7i0HwYI2wVwKZbHgp9XPMYVZFJdKBGT8bnF56pV9A==
# =lp7q
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 22 May 2024 10:58:40 AM PDT
# gpg:                using RSA key F13338574B662389866C7682BFFBD25F78C7AE83
# gpg:                issuer "pbonzini@redhat.com"
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full]
# gpg:                 aka "Paolo Bonzini <pbonzini@redhat.com>" [full]

* tag 'for-upstream' of https://gitlab.com/bonzini/qemu: (23 commits)
  target-i386: hyper-v: Correct kvm_hv_handle_exit return value
  i386/cpu: Use CPUCacheInfo.share_level to encode CPUID[0x8000001D].EAX[bits 25:14]
  i386/cpu: Use CPUCacheInfo.share_level to encode CPUID[4]
  i386: Add cache topology info in CPUCacheInfo
  hw/i386/pc: Support smp.modules for x86 PC machine
  tests: Add test case of APIC ID for module level parsing
  i386/cpu: Introduce module-id to X86CPU
  i386: Support module_id in X86CPUTopoIDs
  i386: Expose module level in CPUID[0x1F]
  i386: Support modules_per_die in X86CPUTopoInfo
  i386: Introduce module level cpu topology to CPUX86State
  i386/cpu: Decouple CPUID[0x1F] subleaf with specific topology level
  i386: Split topology types of CPUID[0x1F] from the definitions of CPUID[0xB]
  i386/cpu: Introduce bitmap to cache available CPU topology levels
  i386/cpu: Consolidate the use of topo_info in cpu_x86_cpuid()
  i386/cpu: Use APIC ID info get NumSharingCache for CPUID[0x8000001D].EAX[bits 25:14]
  i386/cpu: Use APIC ID info to encode cache topo in CPUID[4]
  i386/cpu: Fix i/d-cache topology to core level for Intel CPU
  target/i386: add control bits support for LAM
  target/i386: add support for LAM in CPUID enumeration
  ...

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2024-05-23 08:14:02 -07:00
commit 7b68a5fe2f
19 changed files with 491 additions and 150 deletions

View File

@ -79,6 +79,7 @@
{ "athlon-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, },
GlobalProperty pc_compat_9_0[] = {
{ TYPE_X86_CPU, "x-l1-cache-per-thread", "false" },
{ TYPE_X86_CPU, "guest-phys-bits", "0" },
{ "sev-guest", "legacy-vm-type", "true" },
{ TYPE_X86_CPU, "legacy-multi-node", "on" },
@ -1816,6 +1817,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
pcmc->has_reserved_memory = true;
pcmc->enforce_aligned_dimm = true;
pcmc->enforce_amd_1tb_hole = true;
pcmc->isa_bios_alias = true;
/* BIOS ACPI tables: 128K. Other BIOS datastructures: less than 4K reported
* to be used at the moment, 32K should be enough for a while. */
pcmc->acpi_data_size = 0x20000 + 0x8000;
@ -1841,6 +1843,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE;
mc->nvdimm_supported = true;
mc->smp_props.dies_supported = true;
mc->smp_props.modules_supported = true;
mc->default_ram_id = "pc.ram";
pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_AUTO;

View File

@ -526,12 +526,15 @@ DEFINE_I440FX_MACHINE(v9_1, "pc-i440fx-9.1", NULL,
static void pc_i440fx_9_0_machine_options(MachineClass *m)
{
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_i440fx_9_1_machine_options(m);
m->alias = NULL;
m->is_default = false;
compat_props_add(m->compat_props, hw_compat_9_0, hw_compat_9_0_len);
compat_props_add(m->compat_props, pc_compat_9_0, pc_compat_9_0_len);
pcmc->isa_bios_alias = false;
}
DEFINE_I440FX_MACHINE(v9_0, "pc-i440fx-9.0", NULL,

View File

@ -378,10 +378,12 @@ DEFINE_Q35_MACHINE(v9_1, "pc-q35-9.1", NULL,
static void pc_q35_9_0_machine_options(MachineClass *m)
{
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_q35_9_1_machine_options(m);
m->alias = NULL;
compat_props_add(m->compat_props, hw_compat_9_0, hw_compat_9_0_len);
compat_props_add(m->compat_props, pc_compat_9_0, pc_compat_9_0_len);
pcmc->isa_bios_alias = false;
}
DEFINE_Q35_MACHINE(v9_0, "pc-q35-9.0", NULL,

View File

@ -135,6 +135,7 @@ static void pc_system_flash_map(PCMachineState *pcms,
MemoryRegion *rom_memory)
{
X86MachineState *x86ms = X86_MACHINE(pcms);
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
hwaddr total_size = 0;
int i;
BlockBackend *blk;
@ -184,7 +185,12 @@ static void pc_system_flash_map(PCMachineState *pcms,
if (i == 0) {
flash_mem = pflash_cfi01_get_memory(system_flash);
pc_isa_bios_init(&x86ms->isa_bios, rom_memory, flash_mem);
if (pcmc->isa_bios_alias) {
x86_isa_bios_init(&x86ms->isa_bios, rom_memory, flash_mem,
true);
} else {
pc_isa_bios_init(&x86ms->isa_bios, rom_memory, flash_mem);
}
/* Encrypt the pflash boot ROM */
if (sev_enabled()) {

View File

@ -271,16 +271,21 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev,
init_topo_info(&topo_info, x86ms);
env->nr_dies = ms->smp.dies;
if (ms->smp.modules > 1) {
env->nr_modules = ms->smp.modules;
set_bit(CPU_TOPO_LEVEL_MODULE, env->avail_cpu_topo);
}
if (ms->smp.dies > 1) {
env->nr_dies = ms->smp.dies;
set_bit(CPU_TOPO_LEVEL_DIE, env->avail_cpu_topo);
}
/*
* If APIC ID is not set,
* set it based on socket/die/core/thread properties.
* set it based on socket/die/module/core/thread properties.
*/
if (cpu->apic_id == UNASSIGNED_APIC_ID) {
int max_socket = (ms->smp.max_cpus - 1) /
smp_threads / smp_cores / ms->smp.dies;
/*
* die-id was optional in QEMU 4.0 and older, so keep it optional
* if there's only one die per socket.
@ -289,12 +294,20 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev,
cpu->die_id = 0;
}
/*
* module-id was optional in QEMU 9.0 and older, so keep it optional
* if there's only one module per die.
*/
if (cpu->module_id < 0 && ms->smp.modules == 1) {
cpu->module_id = 0;
}
if (cpu->socket_id < 0) {
error_setg(errp, "CPU socket-id is not set");
return;
} else if (cpu->socket_id > max_socket) {
} else if (cpu->socket_id > ms->smp.sockets - 1) {
error_setg(errp, "Invalid CPU socket-id: %u must be in range 0:%u",
cpu->socket_id, max_socket);
cpu->socket_id, ms->smp.sockets - 1);
return;
}
if (cpu->die_id < 0) {
@ -305,6 +318,14 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev,
cpu->die_id, ms->smp.dies - 1);
return;
}
if (cpu->module_id < 0) {
error_setg(errp, "CPU module-id is not set");
return;
} else if (cpu->module_id > ms->smp.modules - 1) {
error_setg(errp, "Invalid CPU module-id: %u must be in range 0:%u",
cpu->module_id, ms->smp.modules - 1);
return;
}
if (cpu->core_id < 0) {
error_setg(errp, "CPU core-id is not set");
return;
@ -324,6 +345,7 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev,
topo_ids.pkg_id = cpu->socket_id;
topo_ids.die_id = cpu->die_id;
topo_ids.module_id = cpu->module_id;
topo_ids.core_id = cpu->core_id;
topo_ids.smt_id = cpu->thread_id;
cpu->apic_id = x86_apicid_from_topo_ids(&topo_info, &topo_ids);
@ -332,11 +354,13 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev,
cpu_slot = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx);
if (!cpu_slot) {
x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids);
error_setg(errp,
"Invalid CPU [socket: %u, die: %u, core: %u, thread: %u] with"
" APIC ID %" PRIu32 ", valid index range 0:%d",
topo_ids.pkg_id, topo_ids.die_id, topo_ids.core_id, topo_ids.smt_id,
cpu->apic_id, ms->possible_cpus->len - 1);
"Invalid CPU [socket: %u, die: %u, module: %u, core: %u, thread: %u]"
" with APIC ID %" PRIu32 ", valid index range 0:%d",
topo_ids.pkg_id, topo_ids.die_id, topo_ids.module_id,
topo_ids.core_id, topo_ids.smt_id, cpu->apic_id,
ms->possible_cpus->len - 1);
return;
}
@ -368,6 +392,14 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev,
}
cpu->die_id = topo_ids.die_id;
if (cpu->module_id != -1 && cpu->module_id != topo_ids.module_id) {
error_setg(errp, "property module-id: %u doesn't match set apic-id:"
" 0x%x (module-id: %u)", cpu->module_id, cpu->apic_id,
topo_ids.module_id);
return;
}
cpu->module_id = topo_ids.module_id;
if (cpu->core_id != -1 && cpu->core_id != topo_ids.core_id) {
error_setg(errp, "property core-id: %u doesn't match set apic-id:"
" 0x%x (core-id: %u)", cpu->core_id, cpu->apic_id,

View File

@ -45,7 +45,14 @@ void init_topo_info(X86CPUTopoInfo *topo_info,
MachineState *ms = MACHINE(x86ms);
topo_info->dies_per_pkg = ms->smp.dies;
topo_info->cores_per_die = ms->smp.cores;
/*
* Though smp.modules means the number of modules in one cluster,
* i386 doesn't support cluster level so that the smp.clusters
* always defaults to 1, therefore using smp.modules directly is
* fine here.
*/
topo_info->modules_per_die = ms->smp.modules;
topo_info->cores_per_module = ms->smp.cores;
topo_info->threads_per_core = ms->smp.threads;
}
@ -128,6 +135,10 @@ static const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms)
ms->possible_cpus->cpus[i].props.has_die_id = true;
ms->possible_cpus->cpus[i].props.die_id = topo_ids.die_id;
}
if (ms->smp.modules > 1) {
ms->possible_cpus->cpus[i].props.has_module_id = true;
ms->possible_cpus->cpus[i].props.module_id = topo_ids.module_id;
}
ms->possible_cpus->cpus[i].props.has_core_id = true;
ms->possible_cpus->cpus[i].props.core_id = topo_ids.core_id;
ms->possible_cpus->cpus[i].props.has_thread_id = true;

View File

@ -119,6 +119,7 @@ struct PCMachineClass {
bool enforce_aligned_dimm;
bool broken_reserved_end;
bool enforce_amd_1tb_hole;
bool isa_bios_alias;
/* generate legacy CPU hotplug AML */
bool legacy_cpu_hotplug;

View File

@ -50,16 +50,34 @@ typedef uint32_t apic_id_t;
typedef struct X86CPUTopoIDs {
unsigned pkg_id;
unsigned die_id;
unsigned module_id;
unsigned core_id;
unsigned smt_id;
} X86CPUTopoIDs;
typedef struct X86CPUTopoInfo {
unsigned dies_per_pkg;
unsigned cores_per_die;
unsigned modules_per_die;
unsigned cores_per_module;
unsigned threads_per_core;
} X86CPUTopoInfo;
/*
* 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_MODULE,
CPU_TOPO_LEVEL_DIE,
CPU_TOPO_LEVEL_PACKAGE,
CPU_TOPO_LEVEL_MAX,
};
/* Return the bit width needed for 'count' IDs */
static unsigned apicid_bitwidth_for_count(unsigned count)
{
@ -77,7 +95,13 @@ static inline unsigned apicid_smt_width(X86CPUTopoInfo *topo_info)
/* Bit width of the Core_ID field */
static inline unsigned apicid_core_width(X86CPUTopoInfo *topo_info)
{
return apicid_bitwidth_for_count(topo_info->cores_per_die);
return apicid_bitwidth_for_count(topo_info->cores_per_module);
}
/* Bit width of the Module_ID field */
static inline unsigned apicid_module_width(X86CPUTopoInfo *topo_info)
{
return apicid_bitwidth_for_count(topo_info->modules_per_die);
}
/* Bit width of the Die_ID field */
@ -92,10 +116,16 @@ static inline unsigned apicid_core_offset(X86CPUTopoInfo *topo_info)
return apicid_smt_width(topo_info);
}
/* Bit offset of the Module_ID field */
static inline unsigned apicid_module_offset(X86CPUTopoInfo *topo_info)
{
return apicid_core_offset(topo_info) + apicid_core_width(topo_info);
}
/* Bit offset of the Die_ID field */
static inline unsigned apicid_die_offset(X86CPUTopoInfo *topo_info)
{
return apicid_core_offset(topo_info) + apicid_core_width(topo_info);
return apicid_module_offset(topo_info) + apicid_module_width(topo_info);
}
/* Bit offset of the Pkg_ID (socket ID) field */
@ -114,6 +144,7 @@ static inline apic_id_t x86_apicid_from_topo_ids(X86CPUTopoInfo *topo_info,
{
return (topo_ids->pkg_id << apicid_pkg_offset(topo_info)) |
(topo_ids->die_id << apicid_die_offset(topo_info)) |
(topo_ids->module_id << apicid_module_offset(topo_info)) |
(topo_ids->core_id << apicid_core_offset(topo_info)) |
topo_ids->smt_id;
}
@ -127,11 +158,16 @@ static inline void x86_topo_ids_from_idx(X86CPUTopoInfo *topo_info,
X86CPUTopoIDs *topo_ids)
{
unsigned nr_dies = topo_info->dies_per_pkg;
unsigned nr_cores = topo_info->cores_per_die;
unsigned nr_modules = topo_info->modules_per_die;
unsigned nr_cores = topo_info->cores_per_module;
unsigned nr_threads = topo_info->threads_per_core;
topo_ids->pkg_id = cpu_index / (nr_dies * nr_cores * nr_threads);
topo_ids->die_id = cpu_index / (nr_cores * nr_threads) % nr_dies;
topo_ids->pkg_id = cpu_index / (nr_dies * nr_modules *
nr_cores * nr_threads);
topo_ids->die_id = cpu_index / (nr_modules * nr_cores *
nr_threads) % nr_dies;
topo_ids->module_id = cpu_index / (nr_cores * nr_threads) %
nr_modules;
topo_ids->core_id = cpu_index / nr_threads % nr_cores;
topo_ids->smt_id = cpu_index % nr_threads;
}
@ -149,6 +185,9 @@ static inline void x86_topo_ids_from_apicid(apic_id_t apicid,
topo_ids->core_id =
(apicid >> apicid_core_offset(topo_info)) &
~(0xFFFFFFFFUL << apicid_core_width(topo_info));
topo_ids->module_id =
(apicid >> apicid_module_offset(topo_info)) &
~(0xFFFFFFFFUL << apicid_module_width(topo_info));
topo_ids->die_id =
(apicid >> apicid_die_offset(topo_info)) &
~(0xFFFFFFFFUL << apicid_die_width(topo_info));
@ -168,4 +207,13 @@ static inline apic_id_t x86_apicid_from_cpu_idx(X86CPUTopoInfo *topo_info,
return x86_apicid_from_topo_ids(topo_info, &topo_ids);
}
/*
* Check whether there's extended topology level (module or die)?
*/
static inline bool x86_has_extended_topo(unsigned long *topo_bitmap)
{
return test_bit(CPU_TOPO_LEVEL_MODULE, topo_bitmap) ||
test_bit(CPU_TOPO_LEVEL_DIE, topo_bitmap);
}
#endif /* HW_I386_TOPOLOGY_H */

View File

@ -281,7 +281,8 @@ ERST
DEF("smp", HAS_ARG, QEMU_OPTION_smp,
"-smp [[cpus=]n][,maxcpus=maxcpus][,drawers=drawers][,books=books][,sockets=sockets]\n"
" [,dies=dies][,clusters=clusters][,cores=cores][,threads=threads]\n"
" [,dies=dies][,clusters=clusters][,modules=modules][,cores=cores]\n"
" [,threads=threads]\n"
" set the number of initial CPUs to 'n' [default=1]\n"
" maxcpus= maximum number of total CPUs, including\n"
" offline CPUs for hotplug, etc\n"
@ -290,7 +291,8 @@ DEF("smp", HAS_ARG, QEMU_OPTION_smp,
" sockets= number of sockets in one book\n"
" dies= number of dies in one socket\n"
" clusters= number of clusters in one die\n"
" cores= number of cores in one cluster\n"
" modules= number of modules in one cluster\n"
" cores= number of cores in one module\n"
" threads= number of threads in one core\n"
"Note: Different machines may have different subsets of the CPU topology\n"
" parameters supported, so the actual meaning of the supported parameters\n"
@ -306,7 +308,7 @@ DEF("smp", HAS_ARG, QEMU_OPTION_smp,
" must be set as 1 in the purpose of correct parsing.\n",
QEMU_ARCH_ALL)
SRST
``-smp [[cpus=]n][,maxcpus=maxcpus][,sockets=sockets][,dies=dies][,clusters=clusters][,cores=cores][,threads=threads]``
``-smp [[cpus=]n][,maxcpus=maxcpus][,drawers=drawers][,books=books][,sockets=sockets][,dies=dies][,clusters=clusters][,modules=modules][,cores=cores][,threads=threads]``
Simulate a SMP system with '\ ``n``\ ' CPUs initially present on
the machine type board. On boards supporting CPU hotplug, the optional
'\ ``maxcpus``\ ' parameter can be set to enable further CPUs to be
@ -345,14 +347,14 @@ SRST
-smp 8,sockets=2,cores=2,threads=2,maxcpus=8
The following sub-option defines a CPU topology hierarchy (2 sockets
totally on the machine, 2 dies per socket, 2 cores per die, 2 threads
per core) for PC machines which support sockets/dies/cores/threads.
Some members of the option can be omitted but their values will be
automatically computed:
totally on the machine, 2 dies per socket, 2 modules per die, 2 cores per
module, 2 threads per core) for PC machines which support sockets/dies
/modules/cores/threads. Some members of the option can be omitted but
their values will be automatically computed:
::
-smp 16,sockets=2,dies=2,cores=2,threads=2,maxcpus=16
-smp 32,sockets=2,dies=2,modules=2,cores=2,threads=2,maxcpus=32
The following sub-option defines a CPU topology hierarchy (2 sockets
totally on the machine, 2 clusters per socket, 2 cores per cluster,

View File

@ -235,22 +235,53 @@ static uint8_t cpuid2_cache_descriptor(CPUCacheInfo *cache)
((t) == UNIFIED_CACHE) ? CACHE_TYPE_UNIFIED : \
0 /* Invalid value */)
static uint32_t max_thread_ids_for_cache(X86CPUTopoInfo *topo_info,
enum CPUTopoLevel share_level)
{
uint32_t num_ids = 0;
switch (share_level) {
case CPU_TOPO_LEVEL_CORE:
num_ids = 1 << apicid_core_offset(topo_info);
break;
case CPU_TOPO_LEVEL_DIE:
num_ids = 1 << apicid_die_offset(topo_info);
break;
case CPU_TOPO_LEVEL_PACKAGE:
num_ids = 1 << apicid_pkg_offset(topo_info);
break;
default:
/*
* Currently there is no use case for SMT and MODULE, so use
* assert directly to facilitate debugging.
*/
g_assert_not_reached();
}
return num_ids - 1;
}
static uint32_t max_core_ids_in_package(X86CPUTopoInfo *topo_info)
{
uint32_t num_cores = 1 << (apicid_pkg_offset(topo_info) -
apicid_core_offset(topo_info));
return num_cores - 1;
}
/* Encode cache info for CPUID[4] */
static void encode_cache_cpuid4(CPUCacheInfo *cache,
int num_apic_ids, int num_cores,
X86CPUTopoInfo *topo_info,
uint32_t *eax, uint32_t *ebx,
uint32_t *ecx, uint32_t *edx)
{
assert(cache->size == cache->line_size * cache->associativity *
cache->partitions * cache->sets);
assert(num_apic_ids > 0);
*eax = CACHE_TYPE(cache->type) |
CACHE_LEVEL(cache->level) |
(cache->self_init ? CACHE_SELF_INIT_LEVEL : 0) |
((num_cores - 1) << 26) |
((num_apic_ids - 1) << 14);
(max_core_ids_in_package(topo_info) << 26) |
(max_thread_ids_for_cache(topo_info, cache->share_level) << 14);
assert(cache->line_size > 0);
assert(cache->partitions > 0);
@ -269,6 +300,122 @@ 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_MODULE:
return topo_info->threads_per_core * topo_info->cores_per_module;
case CPU_TOPO_LEVEL_DIE:
return topo_info->threads_per_core * topo_info->cores_per_module *
topo_info->modules_per_die;
case CPU_TOPO_LEVEL_PACKAGE:
return topo_info->threads_per_core * topo_info->cores_per_module *
topo_info->modules_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_MODULE:
return apicid_module_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_MODULE:
return CPUID_1F_ECX_TOPO_LEVEL_MODULE;
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)
{
@ -331,20 +478,12 @@ static void encode_cache_cpuid8000001d(CPUCacheInfo *cache,
uint32_t *eax, uint32_t *ebx,
uint32_t *ecx, uint32_t *edx)
{
uint32_t l3_threads;
assert(cache->size == cache->line_size * cache->associativity *
cache->partitions * cache->sets);
*eax = CACHE_TYPE(cache->type) | CACHE_LEVEL(cache->level) |
(cache->self_init ? CACHE_SELF_INIT_LEVEL : 0);
/* L3 is shared among multiple cores */
if (cache->level == 3) {
l3_threads = topo_info->cores_per_die * topo_info->threads_per_core;
*eax |= (l3_threads - 1) << 14;
} else {
*eax |= ((topo_info->threads_per_core - 1) << 14);
}
*eax |= max_thread_ids_for_cache(topo_info, cache->share_level) << 14;
assert(cache->line_size > 0);
assert(cache->partitions > 0);
@ -436,6 +575,7 @@ static CPUCacheInfo legacy_l1d_cache = {
.sets = 64,
.partitions = 1,
.no_invd_sharing = true,
.share_level = CPU_TOPO_LEVEL_CORE,
};
/*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */
@ -450,6 +590,7 @@ static CPUCacheInfo legacy_l1d_cache_amd = {
.partitions = 1,
.lines_per_tag = 1,
.no_invd_sharing = true,
.share_level = CPU_TOPO_LEVEL_CORE,
};
/* L1 instruction cache: */
@ -463,6 +604,7 @@ static CPUCacheInfo legacy_l1i_cache = {
.sets = 64,
.partitions = 1,
.no_invd_sharing = true,
.share_level = CPU_TOPO_LEVEL_CORE,
};
/*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */
@ -477,6 +619,7 @@ static CPUCacheInfo legacy_l1i_cache_amd = {
.partitions = 1,
.lines_per_tag = 1,
.no_invd_sharing = true,
.share_level = CPU_TOPO_LEVEL_CORE,
};
/* Level 2 unified cache: */
@ -490,6 +633,7 @@ static CPUCacheInfo legacy_l2_cache = {
.sets = 4096,
.partitions = 1,
.no_invd_sharing = true,
.share_level = CPU_TOPO_LEVEL_CORE,
};
/*FIXME: CPUID leaf 2 descriptor is inconsistent with CPUID leaf 4 */
@ -499,6 +643,7 @@ static CPUCacheInfo legacy_l2_cache_cpuid2 = {
.size = 2 * MiB,
.line_size = 64,
.associativity = 8,
.share_level = CPU_TOPO_LEVEL_INVALID,
};
@ -512,6 +657,7 @@ static CPUCacheInfo legacy_l2_cache_amd = {
.associativity = 16,
.sets = 512,
.partitions = 1,
.share_level = CPU_TOPO_LEVEL_CORE,
};
/* Level 3 unified cache: */
@ -527,6 +673,7 @@ static CPUCacheInfo legacy_l3_cache = {
.self_init = true,
.inclusive = true,
.complex_indexing = true,
.share_level = CPU_TOPO_LEVEL_DIE,
};
/* TLB definitions: */
@ -969,7 +1116,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
"fsrc", NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, "amx-fp16", NULL, "avx-ifma",
NULL, NULL, NULL, NULL,
NULL, NULL, "lam", NULL,
NULL, NULL, NULL, NULL,
},
.cpuid = {
@ -1825,6 +1972,7 @@ static const CPUCaches epyc_cache_info = {
.lines_per_tag = 1,
.self_init = 1,
.no_invd_sharing = true,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l1i_cache = &(CPUCacheInfo) {
.type = INSTRUCTION_CACHE,
@ -1837,6 +1985,7 @@ static const CPUCaches epyc_cache_info = {
.lines_per_tag = 1,
.self_init = 1,
.no_invd_sharing = true,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l2_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
@ -1847,6 +1996,7 @@ static const CPUCaches epyc_cache_info = {
.partitions = 1,
.sets = 1024,
.lines_per_tag = 1,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l3_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
@ -1860,6 +2010,7 @@ static const CPUCaches epyc_cache_info = {
.self_init = true,
.inclusive = true,
.complex_indexing = true,
.share_level = CPU_TOPO_LEVEL_DIE,
},
};
@ -1875,6 +2026,7 @@ static CPUCaches epyc_v4_cache_info = {
.lines_per_tag = 1,
.self_init = 1,
.no_invd_sharing = true,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l1i_cache = &(CPUCacheInfo) {
.type = INSTRUCTION_CACHE,
@ -1887,6 +2039,7 @@ static CPUCaches epyc_v4_cache_info = {
.lines_per_tag = 1,
.self_init = 1,
.no_invd_sharing = true,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l2_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
@ -1897,6 +2050,7 @@ static CPUCaches epyc_v4_cache_info = {
.partitions = 1,
.sets = 1024,
.lines_per_tag = 1,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l3_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
@ -1910,6 +2064,7 @@ static CPUCaches epyc_v4_cache_info = {
.self_init = true,
.inclusive = true,
.complex_indexing = false,
.share_level = CPU_TOPO_LEVEL_DIE,
},
};
@ -1925,6 +2080,7 @@ static const CPUCaches epyc_rome_cache_info = {
.lines_per_tag = 1,
.self_init = 1,
.no_invd_sharing = true,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l1i_cache = &(CPUCacheInfo) {
.type = INSTRUCTION_CACHE,
@ -1937,6 +2093,7 @@ static const CPUCaches epyc_rome_cache_info = {
.lines_per_tag = 1,
.self_init = 1,
.no_invd_sharing = true,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l2_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
@ -1947,6 +2104,7 @@ static const CPUCaches epyc_rome_cache_info = {
.partitions = 1,
.sets = 1024,
.lines_per_tag = 1,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l3_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
@ -1960,6 +2118,7 @@ static const CPUCaches epyc_rome_cache_info = {
.self_init = true,
.inclusive = true,
.complex_indexing = true,
.share_level = CPU_TOPO_LEVEL_DIE,
},
};
@ -1975,6 +2134,7 @@ static const CPUCaches epyc_rome_v3_cache_info = {
.lines_per_tag = 1,
.self_init = 1,
.no_invd_sharing = true,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l1i_cache = &(CPUCacheInfo) {
.type = INSTRUCTION_CACHE,
@ -1987,6 +2147,7 @@ static const CPUCaches epyc_rome_v3_cache_info = {
.lines_per_tag = 1,
.self_init = 1,
.no_invd_sharing = true,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l2_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
@ -1997,6 +2158,7 @@ static const CPUCaches epyc_rome_v3_cache_info = {
.partitions = 1,
.sets = 1024,
.lines_per_tag = 1,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l3_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
@ -2010,6 +2172,7 @@ static const CPUCaches epyc_rome_v3_cache_info = {
.self_init = true,
.inclusive = true,
.complex_indexing = false,
.share_level = CPU_TOPO_LEVEL_DIE,
},
};
@ -2025,6 +2188,7 @@ static const CPUCaches epyc_milan_cache_info = {
.lines_per_tag = 1,
.self_init = 1,
.no_invd_sharing = true,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l1i_cache = &(CPUCacheInfo) {
.type = INSTRUCTION_CACHE,
@ -2037,6 +2201,7 @@ static const CPUCaches epyc_milan_cache_info = {
.lines_per_tag = 1,
.self_init = 1,
.no_invd_sharing = true,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l2_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
@ -2047,6 +2212,7 @@ static const CPUCaches epyc_milan_cache_info = {
.partitions = 1,
.sets = 1024,
.lines_per_tag = 1,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l3_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
@ -2060,6 +2226,7 @@ static const CPUCaches epyc_milan_cache_info = {
.self_init = true,
.inclusive = true,
.complex_indexing = true,
.share_level = CPU_TOPO_LEVEL_DIE,
},
};
@ -2075,6 +2242,7 @@ static const CPUCaches epyc_milan_v2_cache_info = {
.lines_per_tag = 1,
.self_init = 1,
.no_invd_sharing = true,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l1i_cache = &(CPUCacheInfo) {
.type = INSTRUCTION_CACHE,
@ -2087,6 +2255,7 @@ static const CPUCaches epyc_milan_v2_cache_info = {
.lines_per_tag = 1,
.self_init = 1,
.no_invd_sharing = true,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l2_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
@ -2097,6 +2266,7 @@ static const CPUCaches epyc_milan_v2_cache_info = {
.partitions = 1,
.sets = 1024,
.lines_per_tag = 1,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l3_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
@ -2110,6 +2280,7 @@ static const CPUCaches epyc_milan_v2_cache_info = {
.self_init = true,
.inclusive = true,
.complex_indexing = false,
.share_level = CPU_TOPO_LEVEL_DIE,
},
};
@ -2125,6 +2296,7 @@ static const CPUCaches epyc_genoa_cache_info = {
.lines_per_tag = 1,
.self_init = 1,
.no_invd_sharing = true,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l1i_cache = &(CPUCacheInfo) {
.type = INSTRUCTION_CACHE,
@ -2137,6 +2309,7 @@ static const CPUCaches epyc_genoa_cache_info = {
.lines_per_tag = 1,
.self_init = 1,
.no_invd_sharing = true,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l2_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
@ -2147,6 +2320,7 @@ static const CPUCaches epyc_genoa_cache_info = {
.partitions = 1,
.sets = 2048,
.lines_per_tag = 1,
.share_level = CPU_TOPO_LEVEL_CORE,
},
.l3_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
@ -2160,6 +2334,7 @@ static const CPUCaches epyc_genoa_cache_info = {
.self_init = true,
.inclusive = true,
.complex_indexing = false,
.share_level = CPU_TOPO_LEVEL_DIE,
},
};
@ -6162,15 +6337,21 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
{
X86CPU *cpu = env_archcpu(env);
CPUState *cs = env_cpu(env);
uint32_t die_offset;
uint32_t limit;
uint32_t signature[3];
X86CPUTopoInfo topo_info;
uint32_t cores_per_pkg;
uint32_t threads_per_pkg;
topo_info.dies_per_pkg = env->nr_dies;
topo_info.cores_per_die = cs->nr_cores / env->nr_dies;
topo_info.modules_per_die = env->nr_modules;
topo_info.cores_per_module = cs->nr_cores / env->nr_dies / env->nr_modules;
topo_info.threads_per_core = cs->nr_threads;
cores_per_pkg = topo_info.cores_per_module * topo_info.modules_per_die *
topo_info.dies_per_pkg;
threads_per_pkg = cores_per_pkg * topo_info.threads_per_core;
/* Calculate & apply limits for different index ranges */
if (index >= 0xC0000000) {
limit = env->cpuid_xlevel2;
@ -6206,8 +6387,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
*ecx |= CPUID_EXT_OSXSAVE;
}
*edx = env->features[FEAT_1_EDX];
if (cs->nr_cores * cs->nr_threads > 1) {
*ebx |= (cs->nr_cores * cs->nr_threads) << 16;
if (threads_per_pkg > 1) {
*ebx |= threads_per_pkg << 16;
*edx |= CPUID_HT;
}
if (!cpu->enable_pmu) {
@ -6244,41 +6425,50 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
*/
if (*eax & 31) {
int host_vcpus_per_cache = 1 + ((*eax & 0x3FFC000) >> 14);
int vcpus_per_socket = cs->nr_cores * cs->nr_threads;
if (cs->nr_cores > 1) {
if (cores_per_pkg > 1) {
*eax &= ~0xFC000000;
*eax |= (pow2ceil(cs->nr_cores) - 1) << 26;
*eax |= max_core_ids_in_package(&topo_info) << 26;
}
if (host_vcpus_per_cache > vcpus_per_socket) {
if (host_vcpus_per_cache > threads_per_pkg) {
*eax &= ~0x3FFC000;
*eax |= (pow2ceil(vcpus_per_socket) - 1) << 14;
/* Share the cache at package level. */
*eax |= max_thread_ids_for_cache(&topo_info,
CPU_TOPO_LEVEL_PACKAGE) << 14;
}
}
} else if (cpu->vendor_cpuid_only && IS_AMD_CPU(env)) {
*eax = *ebx = *ecx = *edx = 0;
} else {
*eax = 0;
switch (count) {
case 0: /* L1 dcache info */
encode_cache_cpuid4(env->cache_info_cpuid4.l1d_cache,
1, cs->nr_cores,
&topo_info,
eax, ebx, ecx, edx);
if (!cpu->l1_cache_per_core) {
*eax &= ~MAKE_64BIT_MASK(14, 12);
}
break;
case 1: /* L1 icache info */
encode_cache_cpuid4(env->cache_info_cpuid4.l1i_cache,
1, cs->nr_cores,
&topo_info,
eax, ebx, ecx, edx);
if (!cpu->l1_cache_per_core) {
*eax &= ~MAKE_64BIT_MASK(14, 12);
}
break;
case 2: /* L2 cache info */
encode_cache_cpuid4(env->cache_info_cpuid4.l2_cache,
cs->nr_threads, cs->nr_cores,
&topo_info,
eax, ebx, ecx, edx);
break;
case 3: /* L3 cache info */
die_offset = apicid_die_offset(&topo_info);
if (cpu->enable_l3_cache) {
encode_cache_cpuid4(env->cache_info_cpuid4.l3_cache,
(1 << die_offset), cs->nr_cores,
&topo_info,
eax, ebx, ecx, edx);
break;
}
@ -6381,18 +6571,18 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
switch (count) {
case 0:
*eax = apicid_core_offset(&topo_info);
*ebx = cs->nr_threads;
*ecx |= CPUID_TOPOLOGY_LEVEL_SMT;
*ebx = topo_info.threads_per_core;
*ecx |= CPUID_B_ECX_TOPO_LEVEL_SMT << 8;
break;
case 1:
*eax = apicid_pkg_offset(&topo_info);
*ebx = cs->nr_cores * cs->nr_threads;
*ecx |= CPUID_TOPOLOGY_LEVEL_CORE;
*ebx = threads_per_pkg;
*ecx |= CPUID_B_ECX_TOPO_LEVEL_CORE << 8;
break;
default:
*eax = 0;
*ebx = 0;
*ecx |= CPUID_TOPOLOGY_LEVEL_INVALID;
*ecx |= CPUID_B_ECX_TOPO_LEVEL_INVALID << 8;
}
assert(!(*eax & ~0x1f));
@ -6406,36 +6596,12 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
break;
case 0x1F:
/* V2 Extended Topology Enumeration Leaf */
if (env->nr_dies < 2) {
if (!x86_has_extended_topo(env->avail_cpu_topo)) {
*eax = *ebx = *ecx = *edx = 0;
break;
}
*ecx = count & 0xff;
*edx = cpu->apic_id;
switch (count) {
case 0:
*eax = apicid_core_offset(&topo_info);
*ebx = cs->nr_threads;
*ecx |= CPUID_TOPOLOGY_LEVEL_SMT;
break;
case 1:
*eax = apicid_die_offset(&topo_info);
*ebx = topo_info.cores_per_die * topo_info.threads_per_core;
*ecx |= CPUID_TOPOLOGY_LEVEL_CORE;
break;
case 2:
*eax = apicid_pkg_offset(&topo_info);
*ebx = cs->nr_cores * cs->nr_threads;
*ecx |= CPUID_TOPOLOGY_LEVEL_DIE;
break;
default:
*eax = 0;
*ebx = 0;
*ecx |= CPUID_TOPOLOGY_LEVEL_INVALID;
}
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 */
@ -6654,7 +6820,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
* discards multiple thread information if it is set.
* So don't set it here for Intel to make Linux guests happy.
*/
if (cs->nr_cores * cs->nr_threads > 1) {
if (threads_per_pkg > 1) {
if (env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1 ||
env->cpuid_vendor2 != CPUID_VENDOR_INTEL_2 ||
env->cpuid_vendor3 != CPUID_VENDOR_INTEL_3) {
@ -6721,7 +6887,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
*eax |= (cpu->guest_phys_bits << 16);
}
*ebx = env->features[FEAT_8000_0008_EBX];
if (cs->nr_cores * cs->nr_threads > 1) {
if (threads_per_pkg > 1) {
/*
* Bits 15:12 is "The number of bits in the initial
* Core::X86::Apic::ApicId[ApicId] value that indicate
@ -6729,7 +6895,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
* Bits 7:0 is "The number of threads in the package is NC+1"
*/
*ecx = (apicid_pkg_offset(&topo_info) << 12) |
((cs->nr_cores * cs->nr_threads) - 1);
(threads_per_pkg - 1);
} else {
*ecx = 0;
}
@ -7239,7 +7405,7 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp)
* cpu->vendor_cpuid_only has been unset for compatibility with older
* machine types.
*/
if ((env->nr_dies > 1) &&
if (x86_has_extended_topo(env->avail_cpu_topo) &&
(IS_INTEL_CPU(env) || !cpu->vendor_cpuid_only)) {
x86_cpu_adjust_level(cpu, &env->cpuid_min_level, 0x1F);
}
@ -7762,13 +7928,26 @@ static void x86_cpu_post_initfn(Object *obj)
accel_cpu_instance_init(CPU(obj));
}
static void x86_cpu_init_default_topo(X86CPU *cpu)
{
CPUX86State *env = &cpu->env;
env->nr_modules = 1;
env->nr_dies = 1;
/* SMT, core and package levels are set by default. */
set_bit(CPU_TOPO_LEVEL_SMT, env->avail_cpu_topo);
set_bit(CPU_TOPO_LEVEL_CORE, env->avail_cpu_topo);
set_bit(CPU_TOPO_LEVEL_PACKAGE, env->avail_cpu_topo);
}
static void x86_cpu_initfn(Object *obj)
{
X86CPU *cpu = X86_CPU(obj);
X86CPUClass *xcc = X86_CPU_GET_CLASS(obj);
CPUX86State *env = &cpu->env;
env->nr_dies = 1;
x86_cpu_init_default_topo(cpu);
object_property_add(obj, "feature-words", "X86CPUFeatureWordInfo",
x86_cpu_get_feature_words,
@ -7975,12 +8154,14 @@ static Property x86_cpu_properties[] = {
DEFINE_PROP_UINT32("apic-id", X86CPU, apic_id, 0),
DEFINE_PROP_INT32("thread-id", X86CPU, thread_id, 0),
DEFINE_PROP_INT32("core-id", X86CPU, core_id, 0),
DEFINE_PROP_INT32("module-id", X86CPU, module_id, 0),
DEFINE_PROP_INT32("die-id", X86CPU, die_id, 0),
DEFINE_PROP_INT32("socket-id", X86CPU, socket_id, 0),
#else
DEFINE_PROP_UINT32("apic-id", X86CPU, apic_id, UNASSIGNED_APIC_ID),
DEFINE_PROP_INT32("thread-id", X86CPU, thread_id, -1),
DEFINE_PROP_INT32("core-id", X86CPU, core_id, -1),
DEFINE_PROP_INT32("module-id", X86CPU, module_id, -1),
DEFINE_PROP_INT32("die-id", X86CPU, die_id, -1),
DEFINE_PROP_INT32("socket-id", X86CPU, socket_id, -1),
#endif
@ -8105,6 +8286,7 @@ static Property x86_cpu_properties[] = {
false),
DEFINE_PROP_BOOL("x-intel-pt-auto-level", X86CPU, intel_pt_auto_level,
true),
DEFINE_PROP_BOOL("x-l1-cache-per-thread", X86CPU, l1_cache_per_core, true),
DEFINE_PROP_END_OF_LIST()
};

View File

@ -24,6 +24,7 @@
#include "cpu-qom.h"
#include "kvm/hyperv-proto.h"
#include "exec/cpu-defs.h"
#include "hw/i386/topology.h"
#include "qapi/qapi-types-common.h"
#include "qemu/cpu-float.h"
#include "qemu/timer.h"
@ -258,6 +259,7 @@ typedef enum X86Seg {
#define CR4_SMAP_MASK (1U << 21)
#define CR4_PKE_MASK (1U << 22)
#define CR4_PKS_MASK (1U << 24)
#define CR4_LAM_SUP_MASK (1U << 28)
#define CR4_RESERVED_MASK \
(~(target_ulong)(CR4_VME_MASK | CR4_PVI_MASK | CR4_TSD_MASK \
@ -266,7 +268,8 @@ typedef enum X86Seg {
| CR4_OSFXSR_MASK | CR4_OSXMMEXCPT_MASK | CR4_UMIP_MASK \
| CR4_LA57_MASK \
| CR4_FSGSBASE_MASK | CR4_PCIDE_MASK | CR4_OSXSAVE_MASK \
| CR4_SMEP_MASK | CR4_SMAP_MASK | CR4_PKE_MASK | CR4_PKS_MASK))
| CR4_SMEP_MASK | CR4_SMAP_MASK | CR4_PKE_MASK | CR4_PKS_MASK \
| CR4_LAM_SUP_MASK))
#define DR6_BD (1 << 13)
#define DR6_BS (1 << 14)
@ -927,6 +930,8 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w,
#define CPUID_7_1_EAX_AMX_FP16 (1U << 21)
/* Support for VPMADD52[H,L]UQ */
#define CPUID_7_1_EAX_AVX_IFMA (1U << 23)
/* Linear Address Masking */
#define CPUID_7_1_EAX_LAM (1U << 26)
/* Support for VPDPB[SU,UU,SS]D[,S] */
#define CPUID_7_1_EDX_AVX_VNNI_INT8 (1U << 4)
@ -1011,10 +1016,16 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w,
#define CPUID_MWAIT_EMX (1U << 0) /* enumeration supported */
/* CPUID[0xB].ECX level types */
#define CPUID_TOPOLOGY_LEVEL_INVALID (0U << 8)
#define CPUID_TOPOLOGY_LEVEL_SMT (1U << 8)
#define CPUID_TOPOLOGY_LEVEL_CORE (2U << 8)
#define CPUID_TOPOLOGY_LEVEL_DIE (5U << 8)
#define CPUID_B_ECX_TOPO_LEVEL_INVALID 0
#define CPUID_B_ECX_TOPO_LEVEL_SMT 1
#define CPUID_B_ECX_TOPO_LEVEL_CORE 2
/* COUID[0x1F].ECX level types */
#define CPUID_1F_ECX_TOPO_LEVEL_INVALID CPUID_B_ECX_TOPO_LEVEL_INVALID
#define CPUID_1F_ECX_TOPO_LEVEL_SMT CPUID_B_ECX_TOPO_LEVEL_SMT
#define CPUID_1F_ECX_TOPO_LEVEL_CORE CPUID_B_ECX_TOPO_LEVEL_CORE
#define CPUID_1F_ECX_TOPO_LEVEL_MODULE 3
#define CPUID_1F_ECX_TOPO_LEVEL_DIE 5
/* MSR Feature Bits */
#define MSR_ARCH_CAP_RDCL_NO (1U << 0)
@ -1578,6 +1589,13 @@ typedef struct CPUCacheInfo {
* address bits. CPUID[4].EDX[bit 2].
*/
bool complex_indexing;
/*
* Cache Topology. The level that cache is shared in.
* Used to encode CPUID[4].EAX[bits 25:14] or
* CPUID[0x8000001D].EAX[bits 25:14].
*/
enum CPUTopoLevel share_level;
} CPUCacheInfo;
@ -1887,6 +1905,12 @@ typedef struct CPUArchState {
/* Number of dies within this CPU package. */
unsigned nr_dies;
/* Number of modules within one die. */
unsigned nr_modules;
/* Bitmap of available CPU topology levels for this CPU. */
DECLARE_BITMAP(avail_cpu_topo, CPU_TOPO_LEVEL_MAX);
} CPUX86State;
struct kvm_msrs;
@ -1987,6 +2011,11 @@ struct ArchCPU {
*/
bool enable_l3_cache;
/* Compatibility bits for old machine types.
* If true present L1 cache as per-thread, not per-core.
*/
bool l1_cache_per_core;
/* Compatibility bits for old machine types.
* If true present the old cache topology information
*/
@ -2047,6 +2076,7 @@ struct ArchCPU {
int32_t node_id; /* NUMA node this CPU belongs to */
int32_t socket_id;
int32_t die_id;
int32_t module_id;
int32_t core_id;
int32_t thread_id;
@ -2561,6 +2591,9 @@ static inline uint64_t cr4_reserved_bits(CPUX86State *env)
if (!(env->features[FEAT_7_0_ECX] & CPUID_7_0_ECX_PKS)) {
reserved_bits |= CR4_PKS_MASK;
}
if (!(env->features[FEAT_7_1_EAX] & CPUID_7_1_EAX_LAM)) {
reserved_bits |= CR4_LAM_SUP_MASK;
}
return reserved_bits;
}

View File

@ -219,6 +219,10 @@ void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4)
new_cr4 &= ~CR4_PKS_MASK;
}
if (!(env->features[FEAT_7_1_EAX] & CPUID_7_1_EAX_LAM)) {
new_cr4 &= ~CR4_LAM_SUP_MASK;
}
env->cr[4] = new_cr4;
env->hflags = hflags;

View File

@ -22,8 +22,8 @@ DEF_HELPER_FLAGS_5(bndstx32, TCG_CALL_NO_WG, void, env, tl, tl, i64, i64)
DEF_HELPER_FLAGS_5(bndstx64, TCG_CALL_NO_WG, void, env, tl, tl, i64, i64)
DEF_HELPER_1(bnd_jmp, void, env)
DEF_HELPER_2(aam, void, env, int)
DEF_HELPER_2(aad, void, env, int)
DEF_HELPER_FLAGS_2(aam, TCG_CALL_NO_RWG_SE, tl, tl, tl)
DEF_HELPER_FLAGS_2(aad, TCG_CALL_NO_RWG_SE, tl, tl, tl)
DEF_HELPER_1(aaa, void, env)
DEF_HELPER_1(aas, void, env)
DEF_HELPER_1(daa, void, env)

View File

@ -81,7 +81,7 @@ int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit)
*/
async_safe_run_on_cpu(CPU(cpu), async_synic_update, RUN_ON_CPU_NULL);
return 0;
return EXCP_INTERRUPT;
case KVM_EXIT_HYPERV_HCALL: {
uint16_t code = exit->u.hcall.input & 0xffff;
bool fast = exit->u.hcall.input & HV_HYPERCALL_FAST;

View File

@ -51,6 +51,7 @@
#include "hw/i386/apic_internal.h"
#include "hw/i386/apic-msidef.h"
#include "hw/i386/intel_iommu.h"
#include "hw/i386/topology.h"
#include "hw/i386/x86-iommu.h"
#include "hw/i386/e820_memory_layout.h"
@ -1791,7 +1792,7 @@ static uint32_t kvm_x86_build_cpuid(CPUX86State *env,
break;
}
case 0x1f:
if (env->nr_dies < 2) {
if (!x86_has_extended_topo(env->avail_cpu_topo)) {
cpuid_i--;
break;
}

View File

@ -1480,8 +1480,8 @@ static const X86OpEntry opcodes_root[256] = {
[0xD1] = X86_OP_GROUP1(group2, E,v),
[0xD2] = X86_OP_GROUP2(group2, E,b, 1,b), /* CL */
[0xD3] = X86_OP_GROUP2(group2, E,v, 1,b), /* CL */
[0xD4] = X86_OP_ENTRYr(AAM, I,b),
[0xD5] = X86_OP_ENTRYr(AAD, I,b),
[0xD4] = X86_OP_ENTRY2(AAM, 0,w, I,b),
[0xD5] = X86_OP_ENTRY2(AAD, 0,w, I,b),
[0xD6] = X86_OP_ENTRYw(SALC, 0,b),
[0xD7] = X86_OP_ENTRY1(XLAT, 0,b, zextT0), /* AL read/written */

View File

@ -1084,8 +1084,8 @@ static void gen_AAA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
static void gen_AAD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
gen_helper_aad(tcg_env, tcg_constant_i32(decode->immediate));
set_cc_op(s, CC_OP_LOGICB);
gen_helper_aad(s->T0, s->T0, s->T1);
prepare_update1_cc(decode, s, CC_OP_LOGICB);
}
static void gen_AAM(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
@ -1093,8 +1093,8 @@ static void gen_AAM(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
if (decode->immediate == 0) {
gen_exception(s, EXCP00_DIVZ);
} else {
gen_helper_aam(tcg_env, tcg_constant_i32(decode->immediate));
set_cc_op(s, CC_OP_LOGICB);
gen_helper_aam(s->T0, s->T0, s->T1);
prepare_update1_cc(decode, s, CC_OP_LOGICB);
}
}
@ -2901,14 +2901,15 @@ static bool gen_eflags_adcox(DisasContext *s, X86DecodedInsn *decode, bool want_
return got_cf;
}
static void gen_rot_overflow(X86DecodedInsn *decode, TCGv result, TCGv old, TCGv count)
static void gen_rot_overflow(X86DecodedInsn *decode, TCGv result, TCGv old,
bool can_be_zero, TCGv count)
{
MemOp ot = decode->op[0].ot;
TCGv temp = count ? tcg_temp_new() : decode->cc_src2;
TCGv temp = can_be_zero ? tcg_temp_new() : decode->cc_src2;
tcg_gen_xor_tl(temp, old, result);
tcg_gen_extract_tl(temp, temp, (8 << ot) - 1, 1);
if (count) {
if (can_be_zero) {
tcg_gen_movcond_tl(TCG_COND_EQ, decode->cc_src2, count, tcg_constant_tl(0),
decode->cc_src2, temp);
}
@ -3000,7 +3001,7 @@ static void gen_RCL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
/* Compute result and outgoing overflow */
tcg_gen_mov_tl(decode->cc_src2, s->T0);
tcg_gen_or_tl(s->T0, low, high);
gen_rot_overflow(decode, s->T0, decode->cc_src2, NULL);
gen_rot_overflow(decode, s->T0, decode->cc_src2, false, NULL);
if (zero_label) {
gen_set_label(zero_label);
@ -3053,7 +3054,7 @@ static void gen_RCR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
/* Compute result and outgoing overflow */
tcg_gen_mov_tl(decode->cc_src2, s->T0);
tcg_gen_or_tl(s->T0, low, high);
gen_rot_overflow(decode, s->T0, decode->cc_src2, NULL);
gen_rot_overflow(decode, s->T0, decode->cc_src2, false, NULL);
if (zero_label) {
gen_set_label(zero_label);
@ -3129,9 +3130,10 @@ static TCGv_i32 gen_rot_replicate(MemOp ot, TCGv in)
}
}
static void gen_rot_carry(X86DecodedInsn *decode, TCGv result, TCGv count, int bit)
static void gen_rot_carry(X86DecodedInsn *decode, TCGv result,
bool can_be_zero, TCGv count, int bit)
{
if (count == NULL) {
if (!can_be_zero) {
tcg_gen_extract_tl(decode->cc_dst, result, bit, 1);
} else {
TCGv temp = tcg_temp_new();
@ -3165,8 +3167,8 @@ static void gen_ROL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
} else {
tcg_gen_rotl_tl(s->T0, s->T0, count);
}
gen_rot_carry(decode, s->T0, count, 0);
gen_rot_overflow(decode, s->T0, old, count);
gen_rot_carry(decode, s->T0, can_be_zero, count, 0);
gen_rot_overflow(decode, s->T0, old, can_be_zero, count);
}
static void gen_ROR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
@ -3190,12 +3192,12 @@ static void gen_ROR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
tcg_gen_rotr_i32(temp32, temp32, count32);
/* Zero extend to facilitate later optimization. */
tcg_gen_extu_i32_tl(s->T0, temp32);
gen_rot_carry(decode, s->T0, count, 31);
gen_rot_carry(decode, s->T0, can_be_zero, count, 31);
} else {
tcg_gen_rotr_tl(s->T0, s->T0, count);
gen_rot_carry(decode, s->T0, count, TARGET_LONG_BITS - 1);
gen_rot_carry(decode, s->T0, can_be_zero, count, TARGET_LONG_BITS - 1);
}
gen_rot_overflow(decode, s->T0, old, count);
gen_rot_overflow(decode, s->T0, old, can_be_zero, count);
}
static void gen_RORX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)

View File

@ -145,27 +145,24 @@ void helper_idivl_EAX(CPUX86State *env, target_ulong t0)
/* bcd */
/* XXX: exception */
void helper_aam(CPUX86State *env, int base)
target_ulong helper_aam(target_ulong al, target_ulong base)
{
int al, ah;
int ah;
al = env->regs[R_EAX] & 0xff;
al &= 0xff;
ah = al / base;
al = al % base;
env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al | (ah << 8);
CC_DST = al;
return al | (ah << 8);
}
void helper_aad(CPUX86State *env, int base)
target_ulong helper_aad(target_ulong ax, target_ulong base)
{
int al, ah;
al = env->regs[R_EAX] & 0xff;
ah = (env->regs[R_EAX] >> 8) & 0xff;
al = ax & 0xff;
ah = (ax >> 8) & 0xff;
al = ((ah * base) + al) & 0xff;
env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al;
CC_DST = al;
return al;
}
void helper_aaa(CPUX86State *env)

View File

@ -30,13 +30,17 @@ static void test_topo_bits(void)
{
X86CPUTopoInfo topo_info = {0};
/* simple tests for 1 thread per core, 1 core per die, 1 die per package */
topo_info = (X86CPUTopoInfo) {1, 1, 1};
/*
* simple tests for 1 thread per core, 1 core per module,
* 1 module per die, 1 die per package
*/
topo_info = (X86CPUTopoInfo) {1, 1, 1, 1};
g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 0);
g_assert_cmpuint(apicid_core_width(&topo_info), ==, 0);
g_assert_cmpuint(apicid_module_width(&topo_info), ==, 0);
g_assert_cmpuint(apicid_die_width(&topo_info), ==, 0);
topo_info = (X86CPUTopoInfo) {1, 1, 1};
topo_info = (X86CPUTopoInfo) {1, 1, 1, 1};
g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 0), ==, 0);
g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1), ==, 1);
g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2), ==, 2);
@ -45,39 +49,48 @@ static void test_topo_bits(void)
/* Test field width calculation for multiple values
*/
topo_info = (X86CPUTopoInfo) {1, 1, 2};
topo_info = (X86CPUTopoInfo) {1, 1, 1, 2};
g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 1);
topo_info = (X86CPUTopoInfo) {1, 1, 3};
topo_info = (X86CPUTopoInfo) {1, 1, 1, 3};
g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2);
topo_info = (X86CPUTopoInfo) {1, 1, 4};
topo_info = (X86CPUTopoInfo) {1, 1, 1, 4};
g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2);
topo_info = (X86CPUTopoInfo) {1, 1, 14};
topo_info = (X86CPUTopoInfo) {1, 1, 1, 14};
g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4);
topo_info = (X86CPUTopoInfo) {1, 1, 15};
topo_info = (X86CPUTopoInfo) {1, 1, 1, 15};
g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4);
topo_info = (X86CPUTopoInfo) {1, 1, 16};
topo_info = (X86CPUTopoInfo) {1, 1, 1, 16};
g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4);
topo_info = (X86CPUTopoInfo) {1, 1, 17};
topo_info = (X86CPUTopoInfo) {1, 1, 1, 17};
g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 5);
topo_info = (X86CPUTopoInfo) {1, 30, 2};
topo_info = (X86CPUTopoInfo) {1, 1, 30, 2};
g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5);
topo_info = (X86CPUTopoInfo) {1, 31, 2};
topo_info = (X86CPUTopoInfo) {1, 1, 31, 2};
g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5);
topo_info = (X86CPUTopoInfo) {1, 32, 2};
topo_info = (X86CPUTopoInfo) {1, 1, 32, 2};
g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5);
topo_info = (X86CPUTopoInfo) {1, 33, 2};
topo_info = (X86CPUTopoInfo) {1, 1, 33, 2};
g_assert_cmpuint(apicid_core_width(&topo_info), ==, 6);
topo_info = (X86CPUTopoInfo) {1, 30, 2};
topo_info = (X86CPUTopoInfo) {1, 6, 30, 2};
g_assert_cmpuint(apicid_module_width(&topo_info), ==, 3);
topo_info = (X86CPUTopoInfo) {1, 7, 30, 2};
g_assert_cmpuint(apicid_module_width(&topo_info), ==, 3);
topo_info = (X86CPUTopoInfo) {1, 8, 30, 2};
g_assert_cmpuint(apicid_module_width(&topo_info), ==, 3);
topo_info = (X86CPUTopoInfo) {1, 9, 30, 2};
g_assert_cmpuint(apicid_module_width(&topo_info), ==, 4);
topo_info = (X86CPUTopoInfo) {1, 6, 30, 2};
g_assert_cmpuint(apicid_die_width(&topo_info), ==, 0);
topo_info = (X86CPUTopoInfo) {2, 30, 2};
topo_info = (X86CPUTopoInfo) {2, 6, 30, 2};
g_assert_cmpuint(apicid_die_width(&topo_info), ==, 1);
topo_info = (X86CPUTopoInfo) {3, 30, 2};
topo_info = (X86CPUTopoInfo) {3, 6, 30, 2};
g_assert_cmpuint(apicid_die_width(&topo_info), ==, 2);
topo_info = (X86CPUTopoInfo) {4, 30, 2};
topo_info = (X86CPUTopoInfo) {4, 6, 30, 2};
g_assert_cmpuint(apicid_die_width(&topo_info), ==, 2);
/* build a weird topology and see if IDs are calculated correctly
@ -85,18 +98,19 @@ static void test_topo_bits(void)
/* This will use 2 bits for thread ID and 3 bits for core ID
*/
topo_info = (X86CPUTopoInfo) {1, 6, 3};
topo_info = (X86CPUTopoInfo) {1, 1, 6, 3};
g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2);
g_assert_cmpuint(apicid_core_offset(&topo_info), ==, 2);
g_assert_cmpuint(apicid_module_offset(&topo_info), ==, 5);
g_assert_cmpuint(apicid_die_offset(&topo_info), ==, 5);
g_assert_cmpuint(apicid_pkg_offset(&topo_info), ==, 5);
topo_info = (X86CPUTopoInfo) {1, 6, 3};
topo_info = (X86CPUTopoInfo) {1, 1, 6, 3};
g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 0), ==, 0);
g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1), ==, 1);
g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2), ==, 2);
topo_info = (X86CPUTopoInfo) {1, 6, 3};
topo_info = (X86CPUTopoInfo) {1, 1, 6, 3};
g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 0), ==,
(1 << 2) | 0);
g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 1), ==,