This is xnu-10002.1.13. See this file in:
/*
 * Copyright (c) 2003-2016 Apple Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 *
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */
#include <vm/vm_kern.h>
#include <kern/zalloc.h>
#include <kern/lock_group.h>
#include <kern/timer_queue.h>
#include <mach/machine.h>
#include <i386/cpu_threads.h>
#include <i386/cpuid.h>
#include <i386/machine_cpu.h>
#include <i386/pmCPU.h>
#include <i386/bit_routines.h>

#if MONOTONIC
#include <kern/monotonic.h>
#endif /* MONOTONIC */

#define DIVISOR_GUARD(denom)                            \
	if ((denom) == 0) {                             \
	        kprintf("%s: %d Zero divisor: " #denom, \
	                __FILE__, __LINE__);            \
	}

static void debug_topology_print(void);

boolean_t       topo_dbg = FALSE;

x86_pkg_t       *x86_pkgs               = NULL;
uint32_t        num_Lx_caches[MAX_CACHE_DEPTH]  = { 0 };

static x86_pkg_t        *free_pkgs      = NULL;
static x86_die_t        *free_dies      = NULL;
static x86_core_t       *free_cores     = NULL;
static uint32_t         num_dies        = 0;

static x86_cpu_cache_t  *x86_caches     = NULL;
static uint32_t         num_caches      = 0;

static boolean_t        topoParmsInited = FALSE;
x86_topology_parameters_t       topoParms;

decl_simple_lock_data(, x86_topo_lock);

static struct cpu_cache {
	int     level; int     type;
} cpu_caches[LCACHE_MAX] = {
	[L1D] = {       1, CPU_CACHE_TYPE_DATA },
	[L1I] = {       1, CPU_CACHE_TYPE_INST },
	[L2U] = { 2, CPU_CACHE_TYPE_UNIF },
	[L3U] = { 3, CPU_CACHE_TYPE_UNIF },
};

static boolean_t
cpu_is_hyperthreaded(void)
{
	i386_cpu_info_t     *cpuinfo;

	cpuinfo = cpuid_info();
	return cpuinfo->thread_count > cpuinfo->core_count;
}

static x86_cpu_cache_t *
x86_cache_alloc(void)
{
	x86_cpu_cache_t     *cache;
	int                 i;

	if (x86_caches == NULL) {
		cache = zalloc_permanent_tag(sizeof(x86_cpu_cache_t) +
		    (MAX_CPUS * sizeof(x86_lcpu_t *)),
		    ZALIGN(x86_cpu_cache_t), VM_KERN_MEMORY_CPU);
		if (cache == NULL) {
			return NULL;
		}
	} else {
		cache = x86_caches;
		x86_caches = cache->next;
		cache->next = NULL;
	}

	cache->next = NULL;
	cache->maxcpus = MAX_CPUS;
	for (i = 0; i < cache->maxcpus; i += 1) {
		cache->cpus[i] = NULL;
	}

	num_caches += 1;

	return cache;
}

static void
x86_LLC_info(void)
{
	int                 cache_level     = 0;
	uint32_t            nCPUsSharing    = 1;
	i386_cpu_info_t     *cpuinfo;
	struct cpu_cache    *cachep;
	int                 i;

	cpuinfo = cpuid_info();

	for (i = 0, cachep = &cpu_caches[0]; i < LCACHE_MAX; i++, cachep++) {
		if (cachep->type == 0 || cpuid_info()->cache_size[i] == 0) {
			continue;
		}

		/*
		 * Only worry about it if it's a deeper level than
		 * what we've seen before.
		 */
		if (cachep->level > cache_level) {
			cache_level = cachep->level;

			/*
			 * Save the number of CPUs sharing this cache.
			 */
			nCPUsSharing = cpuinfo->cache_sharing[i];
		}
	}

	/*
	 * Make the level of the LLC be 0 based.
	 */
	topoParms.LLCDepth = cache_level - 1;

	/*
	 * nCPUsSharing represents the *maximum* number of cores or
	 * logical CPUs sharing the cache.
	 */
	topoParms.maxSharingLLC = nCPUsSharing;

	topoParms.nCoresSharingLLC = nCPUsSharing / (cpuinfo->thread_count /
	    cpuinfo->core_count);
	topoParms.nLCPUsSharingLLC = nCPUsSharing;

	/*
	 * nCPUsSharing may not be the number of *active* cores or
	 * threads that are sharing the cache.
	 */
	if (nCPUsSharing > cpuinfo->core_count) {
		topoParms.nCoresSharingLLC = cpuinfo->core_count;
	}
	if (nCPUsSharing > cpuinfo->thread_count) {
		topoParms.nLCPUsSharingLLC = cpuinfo->thread_count;
	}
}

static void
initTopoParms(void)
{
	i386_cpu_info_t     *cpuinfo;

	topoParms.stable = FALSE;

	cpuinfo = cpuid_info();

	PE_parse_boot_argn("-topo", &topo_dbg, sizeof(topo_dbg));

	/*
	 * We need to start with getting the LLC information correct.
	 */
	x86_LLC_info();

	/*
	 * Compute the number of threads (logical CPUs) per core.
	 */
	DIVISOR_GUARD(cpuinfo->core_count);
	topoParms.nLThreadsPerCore = cpuinfo->thread_count / cpuinfo->core_count;
	DIVISOR_GUARD(cpuinfo->cpuid_cores_per_package);
	topoParms.nPThreadsPerCore = cpuinfo->cpuid_logical_per_package / cpuinfo->cpuid_cores_per_package;

	/*
	 * Compute the number of dies per package.
	 */
	DIVISOR_GUARD(topoParms.nCoresSharingLLC);
	topoParms.nLDiesPerPackage = cpuinfo->core_count / topoParms.nCoresSharingLLC;
	DIVISOR_GUARD(topoParms.nPThreadsPerCore);
	DIVISOR_GUARD(topoParms.maxSharingLLC / topoParms.nPThreadsPerCore);
	topoParms.nPDiesPerPackage = cpuinfo->cpuid_cores_per_package / (topoParms.maxSharingLLC / topoParms.nPThreadsPerCore);


	/*
	 * Compute the number of cores per die.
	 */
	topoParms.nLCoresPerDie = topoParms.nCoresSharingLLC;
	topoParms.nPCoresPerDie = (topoParms.maxSharingLLC / topoParms.nPThreadsPerCore);

	/*
	 * Compute the number of threads per die.
	 */
	topoParms.nLThreadsPerDie = topoParms.nLThreadsPerCore * topoParms.nLCoresPerDie;
	topoParms.nPThreadsPerDie = topoParms.nPThreadsPerCore * topoParms.nPCoresPerDie;

	/*
	 * Compute the number of cores per package.
	 */
	topoParms.nLCoresPerPackage = topoParms.nLCoresPerDie * topoParms.nLDiesPerPackage;
	topoParms.nPCoresPerPackage = topoParms.nPCoresPerDie * topoParms.nPDiesPerPackage;

	/*
	 * Compute the number of threads per package.
	 */
	topoParms.nLThreadsPerPackage = topoParms.nLThreadsPerCore * topoParms.nLCoresPerPackage;
	topoParms.nPThreadsPerPackage = topoParms.nPThreadsPerCore * topoParms.nPCoresPerPackage;

	TOPO_DBG("\nCache Topology Parameters:\n");
	TOPO_DBG("\tLLC Depth:           %d\n", topoParms.LLCDepth);
	TOPO_DBG("\tCores Sharing LLC:   %d\n", topoParms.nCoresSharingLLC);
	TOPO_DBG("\tThreads Sharing LLC: %d\n", topoParms.nLCPUsSharingLLC);
	TOPO_DBG("\tmax Sharing of LLC:  %d\n", topoParms.maxSharingLLC);

	TOPO_DBG("\nLogical Topology Parameters:\n");
	TOPO_DBG("\tThreads per Core:  %d\n", topoParms.nLThreadsPerCore);
	TOPO_DBG("\tCores per Die:     %d\n", topoParms.nLCoresPerDie);
	TOPO_DBG("\tThreads per Die:   %d\n", topoParms.nLThreadsPerDie);
	TOPO_DBG("\tDies per Package:  %d\n", topoParms.nLDiesPerPackage);
	TOPO_DBG("\tCores per Package: %d\n", topoParms.nLCoresPerPackage);
	TOPO_DBG("\tThreads per Package: %d\n", topoParms.nLThreadsPerPackage);

	TOPO_DBG("\nPhysical Topology Parameters:\n");
	TOPO_DBG("\tThreads per Core: %d\n", topoParms.nPThreadsPerCore);
	TOPO_DBG("\tCores per Die:     %d\n", topoParms.nPCoresPerDie);
	TOPO_DBG("\tThreads per Die:   %d\n", topoParms.nPThreadsPerDie);
	TOPO_DBG("\tDies per Package:  %d\n", topoParms.nPDiesPerPackage);
	TOPO_DBG("\tCores per Package: %d\n", topoParms.nPCoresPerPackage);
	TOPO_DBG("\tThreads per Package: %d\n", topoParms.nPThreadsPerPackage);

	topoParmsInited = TRUE;
}

static void
x86_cache_free(x86_cpu_cache_t *cache)
{
	num_caches -= 1;
	if (cache->level > 0 && cache->level <= MAX_CACHE_DEPTH) {
		num_Lx_caches[cache->level - 1] -= 1;
	}
	cache->next = x86_caches;
	x86_caches = cache;
}

/*
 * This returns a list of cache structures that represent the
 * caches for a CPU.  Some of the structures may have to be
 * "freed" if they are actually shared between CPUs.
 */
static x86_cpu_cache_t *
x86_cache_list(void)
{
	x86_cpu_cache_t     *root   = NULL;
	x86_cpu_cache_t     *cur    = NULL;
	x86_cpu_cache_t     *last   = NULL;
	struct cpu_cache    *cachep;
	int                 i;

	/*
	 * Cons up a list driven not by CPUID leaf 4 (deterministic cache params)
	 * but by the table above plus parameters already cracked from cpuid...
	 */
	for (i = 0, cachep = &cpu_caches[0]; i < LCACHE_MAX; i++, cachep++) {
		if (cachep->type == 0 || cpuid_info()->cache_size[i] == 0) {
			continue;
		}

		cur = x86_cache_alloc();
		if (cur == NULL) {
			break;
		}

		cur->type       = cachep->type;
		cur->level      = cachep->level;
		cur->nlcpus     = 0;
		cur->maxcpus    = cpuid_info()->cache_sharing[i];
		cur->partitions = cpuid_info()->cache_partitions[i];
		cur->cache_size = cpuid_info()->cache_size[i];
		cur->line_size  = cpuid_info()->cache_linesize;

		if (last == NULL) {
			root = cur;
			last = cur;
		} else {
			last->next = cur;
			last = cur;
		}
		num_Lx_caches[cur->level - 1] += 1;
	}
	return root;
}


static x86_cpu_cache_t *
x86_match_cache(x86_cpu_cache_t *list, x86_cpu_cache_t *matcher)
{
	x86_cpu_cache_t     *cur_cache;

	cur_cache = list;
	while (cur_cache != NULL) {
		if (cur_cache->maxcpus == matcher->maxcpus
		    && cur_cache->type == matcher->type
		    && cur_cache->level == matcher->level
		    && cur_cache->partitions == matcher->partitions
		    && cur_cache->line_size == matcher->line_size
		    && cur_cache->cache_size == matcher->cache_size) {
			break;
		}

		cur_cache = cur_cache->next;
	}

	return cur_cache;
}

static void
x86_lcpu_init(int cpu)
{
	cpu_data_t          *cpup;
	x86_lcpu_t          *lcpu;
	int                 i;

	cpup = cpu_datap(cpu);

	lcpu = &cpup->lcpu;
	lcpu->lcpu = lcpu;
	lcpu->cpu  = cpup;
	lcpu->next_in_core = NULL;
	lcpu->next_in_die  = NULL;
	lcpu->next_in_pkg  = NULL;
	lcpu->core         = NULL;
	lcpu->die          = NULL;
	lcpu->package      = NULL;
	lcpu->cpu_num = cpu;
	lcpu->lnum = cpu;
	lcpu->pnum = cpup->cpu_phys_number;
	lcpu->state = LCPU_OFF;
	for (i = 0; i < MAX_CACHE_DEPTH; i += 1) {
		lcpu->caches[i] = NULL;
	}
}

static x86_core_t *
x86_core_alloc(int cpu)
{
	x86_core_t  *core;
	cpu_data_t  *cpup;

	cpup = cpu_datap(cpu);

	mp_safe_spin_lock(&x86_topo_lock);
	if (free_cores != NULL) {
		core = free_cores;
		free_cores = core->next_in_die;
		core->next_in_die = NULL;
		simple_unlock(&x86_topo_lock);
	} else {
		simple_unlock(&x86_topo_lock);
		core = zalloc_permanent_type(x86_core_t);
		if (core == NULL) {
			panic("x86_core_alloc() alloc of x86_core_t failed!");
		}
	}

	core->pcore_num = cpup->cpu_phys_number / topoParms.nPThreadsPerCore;
	core->lcore_num = core->pcore_num % topoParms.nPCoresPerPackage;

	core->flags = X86CORE_FL_PRESENT | X86CORE_FL_READY
	    | X86CORE_FL_HALTED | X86CORE_FL_IDLE;

	return core;
}

static void
x86_core_free(x86_core_t *core)
{
	mp_safe_spin_lock(&x86_topo_lock);
	core->next_in_die = free_cores;
	free_cores = core;
	simple_unlock(&x86_topo_lock);
}

static x86_pkg_t *
x86_package_find(int cpu)
{
	x86_pkg_t   *pkg;
	cpu_data_t  *cpup;
	uint32_t    pkg_num;

	cpup = cpu_datap(cpu);

	pkg_num = cpup->cpu_phys_number / topoParms.nPThreadsPerPackage;

	pkg = x86_pkgs;
	while (pkg != NULL) {
		if (pkg->ppkg_num == pkg_num) {
			break;
		}
		pkg = pkg->next;
	}

	return pkg;
}

static x86_die_t *
x86_die_find(int cpu)
{
	x86_die_t   *die;
	x86_pkg_t   *pkg;
	cpu_data_t  *cpup;
	uint32_t    die_num;

	cpup = cpu_datap(cpu);

	die_num = cpup->cpu_phys_number / topoParms.nPThreadsPerDie;

	pkg = x86_package_find(cpu);
	if (pkg == NULL) {
		return NULL;
	}

	die = pkg->dies;
	while (die != NULL) {
		if (die->pdie_num == die_num) {
			break;
		}
		die = die->next_in_pkg;
	}

	return die;
}

static x86_core_t *
x86_core_find(int cpu)
{
	x86_core_t  *core;
	x86_die_t   *die;
	cpu_data_t  *cpup;
	uint32_t    core_num;

	cpup = cpu_datap(cpu);

	core_num = cpup->cpu_phys_number / topoParms.nPThreadsPerCore;

	die = x86_die_find(cpu);
	if (die == NULL) {
		return NULL;
	}

	core = die->cores;
	while (core != NULL) {
		if (core->pcore_num == core_num) {
			break;
		}
		core = core->next_in_die;
	}

	return core;
}

void
x86_set_logical_topology(x86_lcpu_t *lcpu, int pnum, int lnum)
{
	x86_core_t  *core = lcpu->core;
	x86_die_t   *die  = lcpu->die;
	x86_pkg_t   *pkg  = lcpu->package;

	assert(core != NULL);
	assert(die != NULL);
	assert(pkg != NULL);

	lcpu->cpu_num = lnum;
	lcpu->pnum = pnum;
	lcpu->master = (lnum == master_cpu);
	lcpu->primary = (lnum % topoParms.nLThreadsPerPackage) == 0;

	lcpu->lnum = lnum % topoParms.nLThreadsPerCore;

	core->pcore_num = lnum / topoParms.nLThreadsPerCore;
	core->lcore_num = core->pcore_num % topoParms.nLCoresPerDie;

	die->pdie_num = lnum / (topoParms.nLThreadsPerCore * topoParms.nLCoresPerDie);
	die->ldie_num = die->pdie_num % topoParms.nLDiesPerPackage;

	pkg->ppkg_num = lnum / topoParms.nLThreadsPerPackage;
	pkg->lpkg_num = pkg->ppkg_num;
}

static x86_die_t *
x86_die_alloc(int cpu)
{
	x86_die_t   *die;
	cpu_data_t  *cpup;

	cpup = cpu_datap(cpu);

	mp_safe_spin_lock(&x86_topo_lock);
	if (free_dies != NULL) {
		die = free_dies;
		free_dies = die->next_in_pkg;
		die->next_in_pkg = NULL;
		simple_unlock(&x86_topo_lock);
	} else {
		simple_unlock(&x86_topo_lock);
		die = zalloc_permanent_type(x86_die_t);
		if (die == NULL) {
			panic("x86_die_alloc() alloc of x86_die_t failed!");
		}
	}

	die->pdie_num = cpup->cpu_phys_number / topoParms.nPThreadsPerDie;

	die->ldie_num = num_dies;
	atomic_incl((long *) &num_dies, 1);

	die->flags = X86DIE_FL_PRESENT;
	return die;
}

static void
x86_die_free(x86_die_t *die)
{
	mp_safe_spin_lock(&x86_topo_lock);
	die->next_in_pkg = free_dies;
	free_dies = die;
	atomic_decl((long *) &num_dies, 1);
	simple_unlock(&x86_topo_lock);
}

static x86_pkg_t *
x86_package_alloc(int cpu)
{
	x86_pkg_t   *pkg;
	cpu_data_t  *cpup;

	cpup = cpu_datap(cpu);

	mp_safe_spin_lock(&x86_topo_lock);
	if (free_pkgs != NULL) {
		pkg = free_pkgs;
		free_pkgs = pkg->next;
		pkg->next = NULL;
		simple_unlock(&x86_topo_lock);
	} else {
		simple_unlock(&x86_topo_lock);
		pkg = zalloc_permanent_type(x86_pkg_t);
		if (pkg == NULL) {
			panic("x86_package_alloc() alloc of x86_pkg_t failed!");
		}
	}

	pkg->ppkg_num = cpup->cpu_phys_number / topoParms.nPThreadsPerPackage;

	pkg->lpkg_num = topoParms.nPackages;
	atomic_incl((long *) &topoParms.nPackages, 1);

	pkg->flags = X86PKG_FL_PRESENT | X86PKG_FL_READY;
	return pkg;
}

static void
x86_package_free(x86_pkg_t *pkg)
{
	mp_safe_spin_lock(&x86_topo_lock);
	pkg->next = free_pkgs;
	free_pkgs = pkg;
	atomic_decl((long *) &topoParms.nPackages, 1);
	simple_unlock(&x86_topo_lock);
}

static void
x86_cache_add_lcpu(x86_cpu_cache_t *cache, x86_lcpu_t *lcpu)
{
	x86_cpu_cache_t     *cur_cache;
	int                 i;

	/*
	 * Put the new CPU into the list of the cache.
	 */
	cur_cache = lcpu->caches[cache->level - 1];
	lcpu->caches[cache->level - 1] = cache;
	cache->next = cur_cache;
	cache->nlcpus += 1;
	for (i = 0; i < cache->nlcpus; i += 1) {
		if (cache->cpus[i] == NULL) {
			cache->cpus[i] = lcpu;
			break;
		}
	}
}

static void
x86_lcpu_add_caches(x86_lcpu_t *lcpu)
{
	x86_cpu_cache_t     *list;
	x86_cpu_cache_t     *cur;
	x86_cpu_cache_t     *match;
	x86_die_t           *die;
	x86_core_t          *core;
	x86_lcpu_t          *cur_lcpu;
	uint32_t            level;
	boolean_t           found           = FALSE;

	assert(lcpu != NULL);

	/*
	 * Add the cache data to the topology.
	 */
	list = x86_cache_list();

	mp_safe_spin_lock(&x86_topo_lock);

	while (list != NULL) {
		/*
		 * Remove the cache from the front of the list.
		 */
		cur = list;
		list = cur->next;
		cur->next = NULL;
		level = cur->level - 1;

		/*
		 * If the cache isn't shared then just put it where it
		 * belongs.
		 */
		if (cur->maxcpus == 1) {
			x86_cache_add_lcpu(cur, lcpu);
			continue;
		}

		/*
		 * We'll assume that all of the caches at a particular level
		 * have the same sharing.  So if we have a cache already at
		 * this level, we'll just skip looking for the match.
		 */
		if (lcpu->caches[level] != NULL) {
			x86_cache_free(cur);
			continue;
		}

		/*
		 * This is a shared cache, so we have to figure out if
		 * this is the first time we've seen this cache.  We do
		 * this by searching through the topology and seeing if
		 * this cache is already described.
		 *
		 * Assume that L{LLC-1} are all at the core level and that
		 * LLC is shared at the die level.
		 */
		if (level < topoParms.LLCDepth) {
			/*
			 * Shared at the core.
			 */
			core = lcpu->core;
			cur_lcpu = core->lcpus;
			while (cur_lcpu != NULL) {
				/*
				 * Skip ourselves.
				 */
				if (cur_lcpu == lcpu) {
					cur_lcpu = cur_lcpu->next_in_core;
					continue;
				}

				/*
				 * If there's a cache on this logical CPU,
				 * then use that one.
				 */
				match = x86_match_cache(cur_lcpu->caches[level], cur);
				if (match != NULL) {
					x86_cache_free(cur);
					x86_cache_add_lcpu(match, lcpu);
					found = TRUE;
					break;
				}

				cur_lcpu = cur_lcpu->next_in_core;
			}
		} else {
			/*
			 * Shared at the die.
			 */
			die = lcpu->die;
			cur_lcpu = die->lcpus;
			while (cur_lcpu != NULL) {
				/*
				 * Skip ourselves.
				 */
				if (cur_lcpu == lcpu) {
					cur_lcpu = cur_lcpu->next_in_die;
					continue;
				}

				/*
				 * If there's a cache on this logical CPU,
				 * then use that one.
				 */
				match = x86_match_cache(cur_lcpu->caches[level], cur);
				if (match != NULL) {
					x86_cache_free(cur);
					x86_cache_add_lcpu(match, lcpu);
					found = TRUE;
					break;
				}

				cur_lcpu = cur_lcpu->next_in_die;
			}
		}

		/*
		 * If a shared cache wasn't found, then this logical CPU must
		 * be the first one encountered.
		 */
		if (!found) {
			x86_cache_add_lcpu(cur, lcpu);
		}
	}

	simple_unlock(&x86_topo_lock);
}

static void
x86_core_add_lcpu(x86_core_t *core, x86_lcpu_t *lcpu)
{
	assert(core != NULL);
	assert(lcpu != NULL);

	mp_safe_spin_lock(&x86_topo_lock);

	lcpu->next_in_core = core->lcpus;
	lcpu->core = core;
	core->lcpus = lcpu;
	core->num_lcpus += 1;
	simple_unlock(&x86_topo_lock);
}

static void
x86_die_add_lcpu(x86_die_t *die, x86_lcpu_t *lcpu)
{
	assert(die != NULL);
	assert(lcpu != NULL);

	lcpu->next_in_die = die->lcpus;
	lcpu->die = die;
	die->lcpus = lcpu;
}

static void
x86_die_add_core(x86_die_t *die, x86_core_t *core)
{
	assert(die != NULL);
	assert(core != NULL);

	core->next_in_die = die->cores;
	core->die = die;
	die->cores = core;
	die->num_cores += 1;
}

static void
x86_package_add_lcpu(x86_pkg_t *pkg, x86_lcpu_t *lcpu)
{
	assert(pkg != NULL);
	assert(lcpu != NULL);

	lcpu->next_in_pkg = pkg->lcpus;
	lcpu->package = pkg;
	pkg->lcpus = lcpu;
}

static void
x86_package_add_core(x86_pkg_t *pkg, x86_core_t *core)
{
	assert(pkg != NULL);
	assert(core != NULL);

	core->next_in_pkg = pkg->cores;
	core->package = pkg;
	pkg->cores = core;
}

static void
x86_package_add_die(x86_pkg_t *pkg, x86_die_t *die)
{
	assert(pkg != NULL);
	assert(die != NULL);

	die->next_in_pkg = pkg->dies;
	die->package = pkg;
	pkg->dies = die;
	pkg->num_dies += 1;
}

void *
cpu_thread_alloc(int cpu)
{
	x86_core_t  *core           = NULL;
	x86_die_t   *die            = NULL;
	x86_pkg_t   *pkg            = NULL;
	cpu_data_t  *cpup;
	uint32_t    phys_cpu;

	/*
	 * Only allow one to manipulate the topology at a time.
	 */
	mp_safe_spin_lock(&x86_topo_lock);

	/*
	 * Make sure all of the topology parameters have been initialized.
	 */
	if (!topoParmsInited) {
		initTopoParms();
	}

	cpup = cpu_datap(cpu);

	phys_cpu = cpup->cpu_phys_number;

	x86_lcpu_init(cpu);

	/*
	 * Assume that all cpus have the same features.
	 */
	if (cpu_is_hyperthreaded()) {
		cpup->cpu_threadtype = CPU_THREADTYPE_INTEL_HTT;
	} else {
		cpup->cpu_threadtype = CPU_THREADTYPE_NONE;
	}

	/*
	 * Get the package that the logical CPU is in.
	 */
	do {
		pkg = x86_package_find(cpu);
		if (pkg == NULL) {
			/*
			 * Package structure hasn't been created yet, do it now.
			 */
			simple_unlock(&x86_topo_lock);
			pkg = x86_package_alloc(cpu);
			mp_safe_spin_lock(&x86_topo_lock);
			if (x86_package_find(cpu) != NULL) {
				x86_package_free(pkg);
				continue;
			}

			/*
			 * Add the new package to the global list of packages.
			 */
			pkg->next = x86_pkgs;
			x86_pkgs = pkg;
		}
	} while (pkg == NULL);

	/*
	 * Get the die that the logical CPU is in.
	 */
	do {
		die = x86_die_find(cpu);
		if (die == NULL) {
			/*
			 * Die structure hasn't been created yet, do it now.
			 */
			simple_unlock(&x86_topo_lock);
			die = x86_die_alloc(cpu);
			mp_safe_spin_lock(&x86_topo_lock);
			if (x86_die_find(cpu) != NULL) {
				x86_die_free(die);
				continue;
			}

			/*
			 * Add the die to the package.
			 */
			x86_package_add_die(pkg, die);
		}
	} while (die == NULL);

	/*
	 * Get the core for this logical CPU.
	 */
	do {
		core = x86_core_find(cpu);
		if (core == NULL) {
			/*
			 * Allocate the core structure now.
			 */
			simple_unlock(&x86_topo_lock);
			core = x86_core_alloc(cpu);
			mp_safe_spin_lock(&x86_topo_lock);
			if (x86_core_find(cpu) != NULL) {
				x86_core_free(core);
				continue;
			}

			/*
			 * Add the core to the die & package.
			 */
			x86_die_add_core(die, core);
			x86_package_add_core(pkg, core);
			machine_info.physical_cpu_max += 1;
		}
	} while (core == NULL);


	/*
	 * Done manipulating the topology, so others can get in.
	 */
	machine_info.logical_cpu_max += 1;
	simple_unlock(&x86_topo_lock);

	/*
	 * Add the logical CPU to the other topology structures.
	 */
	x86_core_add_lcpu(core, &cpup->lcpu);
	x86_die_add_lcpu(core->die, &cpup->lcpu);
	x86_package_add_lcpu(core->package, &cpup->lcpu);
	x86_lcpu_add_caches(&cpup->lcpu);

	return (void *) core;
}

void
cpu_thread_init(void)
{
	int         my_cpu          = get_cpu_number();
	cpu_data_t  *cpup           = current_cpu_datap();
	x86_core_t  *core;
	static int  initialized     = 0;

	/*
	 * If we're the boot processor, we do all of the initialization of
	 * the CPU topology infrastructure.
	 */
	if (my_cpu == master_cpu && !initialized) {
		simple_lock_init(&x86_topo_lock, 0);

		/*
		 * Put this logical CPU into the physical CPU topology.
		 */
		cpup->lcpu.core = cpu_thread_alloc(my_cpu);

		initialized = 1;
	}

	/*
	 * Do the CPU accounting.
	 */
	core = cpup->lcpu.core;
	mp_safe_spin_lock(&x86_topo_lock);
	machine_info.logical_cpu += 1;
	if (core->active_lcpus == 0) {
		machine_info.physical_cpu += 1;
	}
	core->active_lcpus += 1;
	simple_unlock(&x86_topo_lock);

	pmCPUMarkRunning(cpup);
	timer_resync_deadlines();
}

/*
 * Called for a cpu to halt permanently
 * (as opposed to halting and expecting an interrupt to awaken it).
 */
__attribute__((noreturn))
void
cpu_thread_halt(void)
{
	x86_core_t  *core;
	cpu_data_t  *cpup = current_cpu_datap();

	mp_safe_spin_lock(&x86_topo_lock);
	machine_info.logical_cpu -= 1;
	core = cpup->lcpu.core;
	core->active_lcpus -= 1;
	if (core->active_lcpus == 0) {
		machine_info.physical_cpu -= 1;
	}
	simple_unlock(&x86_topo_lock);

	/*
	 * Let the power management code determine the best way to "stop"
	 * the processor.
	 */
	ml_set_interrupts_enabled(FALSE);
	while (1) {
		pmCPUHalt(PM_HALT_NORMAL);
	}
	/* NOT REACHED */
}

/*
 * Validates that the topology was built correctly.  Must be called only
 * after the complete topology is built and no other changes are being made.
 */
void
x86_validate_topology(void)
{
	x86_pkg_t           *pkg;
	x86_die_t           *die;
	x86_core_t          *core;
	x86_lcpu_t          *lcpu;
	uint32_t            nDies;
	uint32_t            nCores;
	uint32_t            nCPUs;

	if (topo_dbg) {
		debug_topology_print();
	}

	/*
	 * Called after processors are registered but before non-boot processors
	 * are started:
	 *  - real_ncpus: number of registered processors driven from MADT
	 *  - max_ncpus:  max number of processors that will be started
	 */
	nCPUs = topoParms.nPackages * topoParms.nLThreadsPerPackage;
	if (nCPUs != real_ncpus) {
		panic("x86_validate_topology() %d threads but %d registered from MADT",
		    nCPUs, real_ncpus);
	}

	pkg = x86_pkgs;
	while (pkg != NULL) {
		/*
		 * Make sure that the package has the correct number of dies.
		 */
		nDies = 0;
		die = pkg->dies;
		while (die != NULL) {
			if (die->package == NULL) {
				panic("Die(%d)->package is NULL",
				    die->pdie_num);
			}
			if (die->package != pkg) {
				panic("Die %d points to package %d, should be %d",
				    die->pdie_num, die->package->lpkg_num, pkg->lpkg_num);
			}

			TOPO_DBG("Die(%d)->package %d\n",
			    die->pdie_num, pkg->lpkg_num);

			/*
			 * Make sure that the die has the correct number of cores.
			 */
			TOPO_DBG("Die(%d)->cores: ", die->pdie_num);
			nCores = 0;
			core = die->cores;
			while (core != NULL) {
				if (core->die == NULL) {
					panic("Core(%d)->die is NULL",
					    core->pcore_num);
				}
				if (core->die != die) {
					panic("Core %d points to die %d, should be %d",
					    core->pcore_num, core->die->pdie_num, die->pdie_num);
				}
				nCores += 1;
				TOPO_DBG("%d ", core->pcore_num);
				core = core->next_in_die;
			}
			TOPO_DBG("\n");

			if (nCores != topoParms.nLCoresPerDie) {
				panic("Should have %d Cores, but only found %d for Die %d",
				    topoParms.nLCoresPerDie, nCores, die->pdie_num);
			}

			/*
			 * Make sure that the die has the correct number of CPUs.
			 */
			TOPO_DBG("Die(%d)->lcpus: ", die->pdie_num);
			nCPUs = 0;
			lcpu = die->lcpus;
			while (lcpu != NULL) {
				if (lcpu->die == NULL) {
					panic("CPU(%d)->die is NULL",
					    lcpu->cpu_num);
				}
				if (lcpu->die != die) {
					panic("CPU %d points to die %d, should be %d",
					    lcpu->cpu_num, lcpu->die->pdie_num, die->pdie_num);
				}
				nCPUs += 1;
				TOPO_DBG("%d ", lcpu->cpu_num);
				lcpu = lcpu->next_in_die;
			}
			TOPO_DBG("\n");

			if (nCPUs != topoParms.nLThreadsPerDie) {
				panic("Should have %d Threads, but only found %d for Die %d",
				    topoParms.nLThreadsPerDie, nCPUs, die->pdie_num);
			}

			nDies += 1;
			die = die->next_in_pkg;
		}

		if (nDies != topoParms.nLDiesPerPackage) {
			panic("Should have %d Dies, but only found %d for package %d",
			    topoParms.nLDiesPerPackage, nDies, pkg->lpkg_num);
		}

		/*
		 * Make sure that the package has the correct number of cores.
		 */
		nCores = 0;
		core = pkg->cores;
		while (core != NULL) {
			if (core->package == NULL) {
				panic("Core(%d)->package is NULL",
				    core->pcore_num);
			}
			if (core->package != pkg) {
				panic("Core %d points to package %d, should be %d",
				    core->pcore_num, core->package->lpkg_num, pkg->lpkg_num);
			}
			TOPO_DBG("Core(%d)->package %d\n",
			    core->pcore_num, pkg->lpkg_num);

			/*
			 * Make sure that the core has the correct number of CPUs.
			 */
			nCPUs = 0;
			lcpu = core->lcpus;
			TOPO_DBG("Core(%d)->lcpus: ", core->pcore_num);
			while (lcpu != NULL) {
				if (lcpu->core == NULL) {
					panic("CPU(%d)->core is NULL",
					    lcpu->cpu_num);
				}
				if (lcpu->core != core) {
					panic("CPU %d points to core %d, should be %d",
					    lcpu->cpu_num, lcpu->core->pcore_num, core->pcore_num);
				}
				TOPO_DBG("%d ", lcpu->cpu_num);
				nCPUs += 1;
				lcpu = lcpu->next_in_core;
			}
			TOPO_DBG("\n");

			if (nCPUs != topoParms.nLThreadsPerCore) {
				panic("Should have %d Threads, but only found %d for Core %d",
				    topoParms.nLThreadsPerCore, nCPUs, core->pcore_num);
			}
			nCores += 1;
			core = core->next_in_pkg;
		}

		if (nCores != topoParms.nLCoresPerPackage) {
			panic("Should have %d Cores, but only found %d for package %d",
			    topoParms.nLCoresPerPackage, nCores, pkg->lpkg_num);
		}

		/*
		 * Make sure that the package has the correct number of CPUs.
		 */
		nCPUs = 0;
		lcpu = pkg->lcpus;
		while (lcpu != NULL) {
			if (lcpu->package == NULL) {
				panic("CPU(%d)->package is NULL",
				    lcpu->cpu_num);
			}
			if (lcpu->package != pkg) {
				panic("CPU %d points to package %d, should be %d",
				    lcpu->cpu_num, lcpu->package->lpkg_num, pkg->lpkg_num);
			}
			TOPO_DBG("CPU(%d)->package %d\n",
			    lcpu->cpu_num, pkg->lpkg_num);
			nCPUs += 1;
			lcpu = lcpu->next_in_pkg;
		}

		if (nCPUs != topoParms.nLThreadsPerPackage) {
			panic("Should have %d Threads, but only found %d for package %d",
			    topoParms.nLThreadsPerPackage, nCPUs, pkg->lpkg_num);
		}

		pkg = pkg->next;
	}
}

/*
 * Prints out the topology
 */
static void
debug_topology_print(void)
{
	x86_pkg_t           *pkg;
	x86_die_t           *die;
	x86_core_t          *core;
	x86_lcpu_t          *cpu;

	pkg = x86_pkgs;
	while (pkg != NULL) {
		kprintf("Package:\n");
		kprintf("    Physical: %d\n", pkg->ppkg_num);
		kprintf("    Logical:  %d\n", pkg->lpkg_num);

		die = pkg->dies;
		while (die != NULL) {
			kprintf("    Die:\n");
			kprintf("        Physical: %d\n", die->pdie_num);
			kprintf("        Logical:  %d\n", die->ldie_num);

			core = die->cores;
			while (core != NULL) {
				kprintf("        Core:\n");
				kprintf("            Physical: %d\n", core->pcore_num);
				kprintf("            Logical:  %d\n", core->lcore_num);

				cpu = core->lcpus;
				while (cpu != NULL) {
					kprintf("            LCPU:\n");
					kprintf("                CPU #:    %d\n", cpu->cpu_num);
					kprintf("                Physical: %d\n", cpu->pnum);
					kprintf("                Logical:  %d\n", cpu->lnum);
					kprintf("                Flags:    ");
					if (cpu->master) {
						kprintf("MASTER ");
					}
					if (cpu->primary) {
						kprintf("PRIMARY");
					}
					if (!cpu->master && !cpu->primary) {
						kprintf("(NONE)");
					}
					kprintf("\n");

					cpu = cpu->next_in_core;
				}

				core = core->next_in_die;
			}

			die = die->next_in_pkg;
		}

		pkg = pkg->next;
	}
}