blob: a754489213f182883e6e695e27e8cf0066236003 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <arch/ops.h>
#include <arch/x86/cpu_topology.h>
#include <arch/x86/feature.h>
#include <bits.h>
#include <pow2.h>
#include <stdio.h>
#include <string.h>
#include <trace.h>
#define LOCAL_TRACE 0
// Use various methods to decode the apic id to different levels of cpu topology.
//
// The heirarchy is currently
// package (socket) : node (die within the socket) : core (within the die) : thread
// Default to all bits, so that the topology system fails towards
// distinguishing all CPUs
static uint32_t package_mask = ~0;
static uint32_t package_shift = 0;
static uint32_t node_mask = 0;
static uint32_t node_shift = 0;
static uint32_t core_mask = 0;
static uint32_t core_shift = 0;
static uint32_t smt_mask = 0;
static void legacy_topology_init();
static void modern_intel_topology_init();
static void extended_amd_topology_init();
void x86_cpu_topology_init() {
static int initialized;
if (atomic_swap(&initialized, 1)) {
return;
}
if (x86_vendor == X86_VENDOR_INTEL && x86_get_cpuid_leaf(X86_CPUID_BASE)->a >= X86_CPUID_TOPOLOGY) {
modern_intel_topology_init();
} else if (x86_vendor == X86_VENDOR_AMD) {
extended_amd_topology_init();
} else {
legacy_topology_init();
}
}
static void modern_intel_topology_init() {
// This is based off of Intel 3A's Example 8-18 "Support Routine for
// Identifying Package, Core, and Logical Processors from 32-bit x2APIC ID"
struct x86_topology_level info;
if (!x86_topology_enumerate(0, &info)) {
return;
}
if (info.type == X86_TOPOLOGY_SMT) {
smt_mask = (1 << info.right_shift) - 1;
core_shift = info.right_shift;
}
for (uint8_t level = 0; x86_topology_enumerate(level, &info); ++level) {
if (info.type == X86_TOPOLOGY_CORE) {
core_mask = ((1 << info.right_shift) - 1) ^ smt_mask;
package_shift = info.right_shift;
package_mask = ~(core_mask | smt_mask);
break;
}
}
}
static void extended_amd_topology_init() {
// Described in AMD CPUID Specification, version 2.34, section 3.2
const struct cpuid_leaf* leaf = x86_get_cpuid_leaf(X86_CPUID_ADDR_WIDTH);
if (!leaf)
return;
// width of the core part of the apic id
uint32_t apic_id_core_id_size = BITS_SHIFT(leaf->c, 15, 12);
if (apic_id_core_id_size == 0) {
legacy_topology_init();
return;
}
// initial state of variables that are optionally set below
// smt_mask = 0;
// core_shift = 0;
// node_shift = 0;
// node_mask = 0;
uint32_t node_size = 0;
// check to see if AMD topology extensions are enabled
if (x86_feature_test(X86_FEATURE_AMD_TOPO)) {
leaf = x86_get_cpuid_leaf(X86_CPUID_AMD_TOPOLOGY);
if (!leaf)
return;
uint32_t cores_per_compute_unit = BITS_SHIFT(leaf->b, 15, 8) + 1;
if (cores_per_compute_unit == 2) {
// SMT is enabled, the bottom bit of the APIC id is the SMT id
// This is according to the BKDG and PPR for family 15h-17h
smt_mask = 1;
core_shift = 1;
} else if (cores_per_compute_unit > 2) {
// not sure how to handle this, display message and move on
TRACEF("WARNING: cores per compute unit > 2 (%u), unhandled\n", cores_per_compute_unit);
}
uint32_t nodes_per_processor = BITS_SHIFT(leaf->c, 10, 8) + 1;
if (nodes_per_processor > 0 && ispow2(nodes_per_processor)) {
// a pow2 number of bits between the core number and package number refer to node
node_size = log2_uint_floor(nodes_per_processor);
node_shift = apic_id_core_id_size - node_size;
node_mask = (nodes_per_processor - 1) << node_shift;
// node number chews in to the core number, so subtract node_size off
// apic_id_core_id_size so that it computes the core mask properly
apic_id_core_id_size -= node_size;
}
}
// core is the mask of the bottom half of the APIC id space
core_mask = (1u << apic_id_core_id_size) - 1;
// package soaks up all the high bits of APIC id space
package_shift = node_size + apic_id_core_id_size;
package_mask = UINT32_MAX << (node_size + apic_id_core_id_size);
}
static void legacy_topology_init() {
const struct cpuid_leaf* leaf = x86_get_cpuid_leaf(X86_CPUID_MODEL_FEATURES);
if (!leaf) {
return;
}
bool pkg_size_valid = !!(leaf->d & (1 << 28));
if (!pkg_size_valid) {
return;
}
// Get the maximum number of addressable IDs at the sub-package level.
uint8_t max_num_subpackage = (leaf->b >> 16) & 0xff;
leaf = x86_get_cpuid_leaf(X86_CPUID_CACHE_V2);
if (!leaf) {
return;
}
// Get the maximum number of addressable cores with a package.
uint max_num_core = (leaf->a >> 26) + 1;
uint max_num_ht = max_num_subpackage / max_num_core;
package_mask = ~(max_num_subpackage - 1);
package_shift = __builtin_ctz(package_mask);
smt_mask = max_num_ht - 1;
core_shift = __builtin_ctz(~smt_mask);
core_mask = ~package_mask ^ smt_mask;
}
void x86_cpu_topology_decode(uint32_t apic_id, x86_cpu_topology_t* topo) {
*topo = {};
LTRACEF("id 0x%x: package shift/mask %u:%#x node shift/mask %u:%#x "
"core shift/mask %u:%#x smt mask %#x\n",
apic_id, package_shift, package_mask, node_shift, node_mask,
core_shift, core_mask, smt_mask);
topo->package_id = (apic_id & package_mask) >> package_shift;
topo->node_id = (apic_id & node_mask) >> node_shift;
topo->core_id = (apic_id & core_mask) >> core_shift;
topo->smt_id = apic_id & smt_mask;
}