fillsysinfo.c revision d2365b013d4199b49b3a1438d57aea23423e02ad
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/promimpl.h>
#include <sys/machsystm.h>
#include <sys/cpu_module.h>
#include <vm/hat_sfmmu.h>
#include <sys/sysmacros.h>
#include <sys/mach_descrip.h>
#include <sys/archsystm.h>
uint64_t *);
static int get_l2_cache_node_count(md_t *);
char *bit_formatter, char *warning);
uint_t niommu_tsbs = 0;
static int n_l2_caches = 0;
/* prevent compilation with VAC defined */
#ifdef VAC
#error "The sun4v architecture does not support VAC"
#endif
#define S_VAC_SIZE MMU_PAGESIZE
#define S_VAC_SHIFT MMU_PAGESHIFT
int vac_size = S_VAC_SIZE;
int vac_shift = S_VAC_SHIFT;
void
{
}
void
{
char *namebuf;
char *namebufp;
int namelen;
return;
}
/* All out-of-range cpus will be stopped later. */
"cpu excluded from configuration\n", cpuid);
return;
}
"property");
}
namebufp += 5;
"fit into the cpunode name buffer");
"clock-frequency", &clk_freq)) {
clk_freq = 0;
}
/*
* Compute scaling factor based on rate of %tick. This is used
* to convert from ticks derived from %tick to nanoseconds. See
*/
/*
* The nodeid is not used in sun4v at all. Setting it
* to positive value to make starting of slave CPUs
* code happy.
*/
/*
* Obtain the L2 cache information from MD.
* If "Cache" node exists, then set L2 cache properties
* as read from MD.
* If node does not exists, then set the L2 cache properties
* in individual CPU module.
*/
} else {
/*
* Do not expect L2 cache properties to be bigger
* than 32-bit quantity.
*/
}
/*
* Initialize the mapping for exec unit, chip and core.
*/
if (ecache_setsize == 0)
if (ecache_alignsize == 0)
}
void
{
}
/*
* Use L2 cache node to derive the chip mapping.
*/
void
{
int i, j;
int idx = 0;
"fwd", &cachelist);
/*
* The "cache" node is optional in MD, therefore ncaches can be 0.
*/
if (ncache < 1) {
return;
}
for (i = 0; i < ncache; i++) {
continue;
if (cache_level != 2)
continue;
/*
* Found a l2 cache node. Find out the cpu nodes it
* points to.
*/
"back", &node);
if (ncpu < 1)
continue;
for (j = 0; j < ncpu; j++) {
continue;
continue;
}
idx++;
}
}
void
{
int num, num_eunits;
int idx, i, j;
/*
* Find the cpu integer exec units - and
* setup the mappings appropriately.
*/
if (num < 1)
if (num > 1)
" description");
"fwd", &eunit);
if (num_eunits > 0) {
char *fpu_str = "fp";
/* Spin through and find all the integer exec units */
for (i = 0; i < num_eunits; i++) {
char *p;
char *val;
int vallen;
/* ignore nodes with no type */
continue;
goto found;
}
goto found;
}
}
continue;
/*
* find the cpus attached to this EU and
* update their mapping indices
*/
"back", &node);
if (num < 1)
" not attached to a cpu node");
for (j = 0; j < num; j++) {
&lcpuid))
continue;
continue;
switch (etype) {
case INTEGER:
break;
case FPU:
break;
}
}
}
}
}
/*
* All the common setup of sun4v CPU modules is done by this routine.
*/
void
cpu_setup_common(char **cpu_module_isa_set)
{
extern int mmu_exported_pagesize_mask;
int nocpus, i;
if (nocpus < 1) {
"failed or incorrect number of CPUs in MD");
}
if (use_page_coloring) {
do_pg_coloring = 1;
}
/*
* Get the valid mmu page sizes mask, Q sizes and isalist/r
* from the MD for the first available CPU in cpulist.
*
* Do not expect the MMU page sizes mask to be more than 32-bit.
*/
/*
* Get the number of contexts and tsbs supported.
*/
shctx_on = 1;
}
for (i = 0; i < nocpus; i++)
/* setup l2 cache count. */
/*
* If MD is broken then append the passed ISA set,
* otherwise trust the MD.
*/
if (broken_md_flag)
else
/*
* ra_limit is the highest real address in the machine.
*/
(void) md_fini_handle(mdp);
/*
* Block stores invalidate all pages of the d$ so pagecopy
* et. al. do not need virtual translations with virtual
* coloring taken into consideration.
*/
/*
* The kpm mapping window.
* kpm_size:
* The size of a single kpm range.
* The overall size will be: kpm_size * vac_colors.
* kpm_vbase:
* The virtual start address of the kpm range within the kernel
* virtual address space. kpm_vbase has to be kpm_size aligned.
*/
/*
* Make kpm_vbase, kpm_size aligned to kpm_size_shift.
* To do this find the nearest power of 2 size that the
* actual ra_limit fits within.
* If it is an even power of two use that, otherwise use the
* next power of two larger than ra_limit.
*/
/*
* No virtual caches on sun4v so size matches size shift
*/
if (va_bits < VA_ADDRESS_SPACE_BITS) {
/*
* In case of VA hole
* kpm_base = hole_end + 1TB
* Starting 1TB beyond where VA hole ends because on Niagara
* processor software must not use pages within 4GB of the
* VA hole as instruction pages to avoid problems with
* prefetching into the VA hole.
*/
(1ull << 40));
} else { /* Number of VA bits 64 ... no VA hole */
}
/*
* The traptrace code uses either %tick or %stick for
* timestamping. The sun4v require use of %stick.
*/
traptrace_use_stick = 1;
}
/*
* Get the nctxs from MD. If absent panic.
*/
static uint64_t
{
&ctx_bits))
ctx_bits = 0;
"returned by MD", ctx_bits);
return (ctx_bits);
}
/*
* Get the number of tsbs from MD. If absent the default value is 0.
*/
static uint64_t
{
&number_tsbs))
number_tsbs = 0;
return (number_tsbs);
}
/*
* Get the number of shared contexts from MD. If absent the default value is 0.
*
*/
static uint64_t
{
number_contexts = 0;
return (number_contexts);
}
/*
* Initalize supported page sizes information.
* Set to 0, if the page sizes mask information is absent in MD.
*/
static uint64_t
{
mmu_page_size_list = 0;
"by MD", mmu_page_size_list);
return (mmu_page_size_list);
}
/*
* This routine gets the isalist information from MD and appends
* the CPU module ISA set if required.
*/
static char *
char **cpu_module_isa_set)
{
extern int at_flags;
char *md_isalist;
int md_isalen;
char *isabuf;
int isalen;
char **isa_set;
char *p, *q;
int cpu_module_isalen = 0, found = 0;
/*
* We support binaries for all the cpus that have shipped so far.
* The kernel emulates instructions that are not supported by hardware.
*/
/*
* Construct the space separated isa_list.
*/
if (cpu_module_isa_set != NULL) {
isa_set++) {
cpu_module_isalen++; /* for space character */
}
}
/*
* Allocate the buffer of MD isa buffer length + CPU module
* isa buffer length.
*/
if (md_isalist == NULL)
"md_isalist");
}
/*
* Check if the isa_set is present in isalist returned by MD.
* If yes, then no need to append it, if no then append it to
* isalist returned by MD.
*/
if (cpu_module_isa_set != NULL) {
isa_set++) {
found = 0;
p += strlen(p) + 1) {
found = 1;
break;
}
}
if (!found) {
}
}
}
/* Get rid of any trailing white spaces */
return (md_isalist);
}
static void
{
char *hwcapbuf;
int hwcaplen;
/* Property not found */
return;
}
"unrecognized token: %s");
}
static void
{
char *mmbuf;
int mmlen;
char *p, *q;
/* Property not found */
return;
}
wmm = TSTATE_MM_TSO;
if (strcmp(p, "wc") == 0)
wmm = TSTATE_MM_WC;
}
}
/*
* Does the opposite of cmn_err(9f) "%b" conversion specification:
* Given a list of strings, converts them to a bit-vector.
*
* tokens - is a buffer of [NUL-terminated] strings.
* tokenslen - length of tokenbuf in bytes.
* bit_formatter - is a %b format string, such as FMT_AV_SPARC
* from /usr/include/sys/auxv_SPARC.h, of the form:
* <base-char>[<bit-char><token-string>]...
* <base-char> is ignored.
* <bit-char> is [1-32], as per cmn_err(9f).
* warning - is a printf-style format string containing "%s",
* which is used to print a warning message when an unrecognized
* token is found. If warning is NULL, no warning is printed.
* Returns a bit-vector corresponding to the specified tokens.
*/
static unsigned long
{
char *cur;
unsigned long ul = 0;
char *hit;
char *bs;
bit_formatter++; /* skip base; not needed for input */
while (tokenslen) {
bs = bit_formatter;
/*
* We need a complicated while loop and the >=32 check,
* instead of a simple "if (strstr())" so that when the
* token is "vis", we don't match on "vis2" (for example).
*/
/* LINTED E_EQUALITY_NOT_ASSIGNMENT */
/*
* We're still in the middle of a word, i.e., not
* pointing at a <bit-char>. So advance ptr
* to ensure forward progress.
*/
}
} else {
/* The token wasn't found in bit_formatter */
}
}
return (ul);
}
{
extern int ppvm_enable;
extern int meta_alloc_enable;
int i;
int memnodes;
int nmblock;
uint64_t r;
if (r == 0 || r > RA_ADDRESS_SPACE_BITS)
else {
/*
* Enable memory DR and metadata (page_t)
* allocation from existing memory.
*/
ppvm_enable = 1;
meta_alloc_enable = 1;
return (1ULL << r);
}
}
if (nmblock < 1)
for (i = 0; i < nmblock; i++) {
" mblock node");
" mblock node");
}
if (ra_limit > MAX_REAL_ADDRESS) {
" clipping to %llx\n", MAX_REAL_ADDRESS);
}
return (ra_limit);
}
/*
* This routine sets the globals for CPU and DEV mondo queue entries and
* resumable and non-resumable error queue entries.
*
* First, look up the number of bits available to pass an entry number.
* This can vary by platform and may result in allocating an unreasonably
* (or impossibly) large amount of memory for the corresponding table,
* so we clamp it by 'max_entries'. Finally, since the q size is used when
* calling contig_mem_alloc(), which expects a power of 2, clamp the q size
* down to a power of 2. If the prop is missing, use 'default_entries'.
*/
static uint64_t
{
if (default_entries > max_entries)
if (!broken_md_flag)
qnamep);
} else {
}
/* If not a power of 2, truncate to a power of 2. */
}
return (entries);
}
/* Scaling constant used to compute size of cpu mondo queue */
#define CPU_MONDO_Q_MULTIPLIER 8
static void
{
int nrnode;
/*
* Compute the maximum number of entries for the cpu mondo queue.
* Use the appropriate property in the platform node, if it is
* available. Else, base it on NCPU.
*/
}
static void
{
/* Do not expect number of VA bits to be more than 32-bit quantity */
/*
* Correct the value for VA bits on UltraSPARC-T1 based systems
* in case of broken MD.
*/
if (broken_md_flag)
}
int
l2_cache_node_count(void)
{
return (n_l2_caches);
}
/*
* count the number of l2 caches.
*/
int
{
int i;
int l2_caches = 0;
for (i = 0; i < n_cachenodes; i++) {
level = 0;
}
if (level == 2) {
l2_caches++;
}
}
return (l2_caches);
}
/*
* This routine returns the L2 cache information such as -- associativity,
* size and linesize.
*/
static int
{
int ncaches, i;
uint64_t cache_level = 0;
"fwd", &cachelist);
/*
* The "cache" node is optional in MD, therefore ncaches can be 0.
*/
if (ncaches < 1) {
return (0);
}
for (i = 0; i < ncaches; i++) {
continue;
if (cache_level != 2) continue;
/* If properties are missing from this cache ignore it */
"associativity", &local_assoc))) {
continue;
}
"size", &local_size))) {
continue;
}
"line-size", &local_lsize))) {
continue;
}
*size = local_size;
*linesize = local_lsize;
break;
}
}
/*
* Set the broken_md_flag to 1 if the MD doesn't have
* the domaining-enabled property in the platform node and the
* platform uses the UltraSPARC-T1 cpu. This flag is used to
* workaround some of the incorrect MD properties.
*/
static void
{
int nrnode;
char *namebuf;
int namelen;
&platlist);
if (nrnode < 1)
"Cannot read 'compatible' property of 'cpu' node");
}
broken_md_flag = 1;
}
/*
* Number of bits forming a valid context for use in a sun4v TTE and the MMU
* context registers. Sun4v defines the minimum default value to be 13 if this
* property is not specified in a cpu node in machine descriptor graph.
*/
#define MMU_INFO_CTXBITS_MIN 13
/* Convert context bits to number of contexts */
/*
* Read machine descriptor and load TLB to CPU mappings.
* Returned values: cpuid2pset[NCPU], nctxs[NCPU], md_gen
* - cpuid2pset is initialized so it can convert cpuids to processor set of CPUs
* that are shared between TLBs.
* - nctxs is initialized to number of contexts for each CPU
* - md_gen is set to generation number of machine descriptor from which this
* data was.
* Return: zero on success.
*/
static int
{
int ntlbs;
int ncp;
int retval = 1;
/* get MD handle, and string cookies for cpu and back nodes */
goto cleanup;
/* set generation number of current MD handle */
/* Find root element, and search for all TLBs in MD */
goto cleanup;
/*
* Build processor sets, one per possible context domain. For each tlb,
* search for connected CPUs. If any CPU is already in a set, then add
* all the TLB's CPUs to that set. Otherwise, create and populate a new
* pset. Thus, a single pset is built to represent multiple TLBs if
* they have CPUs in common.
*/
if (ncp < 0)
goto cleanup;
else if (ncp == 0)
continue;
/* Get the id and number of contexts for each cpu */
goto cleanup;
}
/*
* If a CPU is already in a set as shown by cpuid2pset[], then
* use that set.
*/
break;
}
/* No CPU has a set. Create a new one. */
CPUSET_ZERO(*ppset);
}
/* Add every CPU to the set, and record the set assignment. */
}
}
retval = 0;
(void) md_fini_handle(mdp);
return (retval);
}
/*
* Return MMU info based on cpuid.
*
* Algorithm:
* Read machine descriptor and find all CPUs that share the same TLB with CPU
* specified by cpuid. Go through found CPUs and see if any one of them already
* has MMU index, if so, set index based on that value. If CPU does not share
* TLB with any other CPU or if none of those CPUs has mmu_ctx pointer, find the
* smallest available MMU index and give it to current CPU. If no available
* domain, perform a round robin, and start assigning from the beginning.
*
* For optimization reasons, this function uses a cache to store all TLB to CPU
* mappings, and updates them only when machine descriptor graph is changed.
* Because of this, and because we search MMU table for smallest index id, this
* function needs to be serialized which is protected by cpu_lock.
*/
void
{
static uint_t next_domain = 0;
int idx;
/*
* Load TLB CPU mappings only if MD generation has changed, FW that do
* not provide generation number, always return MDESC_INVAL_GEN, and as
* result MD is read here only once on such machines: when cpuid2pset is
* NULL
*/
if (cpuid2pset == NULL) {
KM_SLEEP);
} else {
/* clean cpuid2pset[NCPU], before loading new values */
for (;;) {
if (id == CPUSET_NOTINSET)
break;
}
}
}
}
goto error_panic;
}
goto error_panic;
/* Search for a processor in the same TLB pset with MMU context */
for (;;) {
if (id == CPUSET_NOTINSET)
break;
return;
}
}
/*
* No CPU in the TLB pset has a context domain yet.
* Use next_domain if available, or search for an unused domain, or
* overload next_domain, in that order. Overloading is necessary when
* the number of TLB psets is greater than max_mmu_ctxdoms.
*/
idx = next_domain;
break;
if (idx == max_mmu_ctxdoms) {
/* overload next_domain */
idx = next_domain;
" to support CPUs with different nctxs");
}
}
return;
}