xref: /illumos-gate/usr/src/uts/sun4v/os/fillsysinfo.c (revision bfed486ad8de8b8ebc6345a8e10accae08bf2f45)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/errno.h>
27 #include <sys/types.h>
28 #include <sys/param.h>
29 #include <sys/cpu.h>
30 #include <sys/cpuvar.h>
31 #include <sys/clock.h>
32 #include <sys/promif.h>
33 #include <sys/promimpl.h>
34 #include <sys/systm.h>
35 #include <sys/machsystm.h>
36 #include <sys/debug.h>
37 #include <sys/sunddi.h>
38 #include <sys/modctl.h>
39 #include <sys/cpu_module.h>
40 #include <sys/kobj.h>
41 #include <sys/cmp.h>
42 #include <sys/async.h>
43 #include <vm/page.h>
44 #include <vm/vm_dep.h>
45 #include <vm/hat_sfmmu.h>
46 #include <sys/sysmacros.h>
47 #include <sys/mach_descrip.h>
48 #include <sys/mdesc.h>
49 #include <sys/archsystm.h>
50 #include <sys/error.h>
51 #include <sys/mmu.h>
52 #include <sys/bitmap.h>
53 #include <sys/intreg.h>
54 
55 struct cpu_node cpunodes[NCPU];
56 
57 uint64_t cpu_q_entries;
58 uint64_t dev_q_entries;
59 uint64_t cpu_rq_entries;
60 uint64_t cpu_nrq_entries;
61 uint64_t ncpu_guest_max;
62 
63 void fill_cpu(md_t *, mde_cookie_t);
64 
65 static uint64_t get_mmu_ctx_bits(md_t *, mde_cookie_t);
66 static uint64_t get_mmu_tsbs(md_t *, mde_cookie_t);
67 static uint64_t	get_mmu_shcontexts(md_t *, mde_cookie_t);
68 static uint64_t get_cpu_pagesizes(md_t *, mde_cookie_t);
69 static char *construct_isalist(md_t *, mde_cookie_t, char **);
70 static void init_md_broken(md_t *, mde_cookie_t *);
71 static int get_l2_cache_info(md_t *, mde_cookie_t, uint64_t *, uint64_t *,
72     uint64_t *);
73 static void get_hwcaps(md_t *, mde_cookie_t);
74 static void get_q_sizes(md_t *, mde_cookie_t);
75 static void get_va_bits(md_t *, mde_cookie_t);
76 static size_t get_ra_limit(md_t *);
77 static int get_l2_cache_node_count(md_t *);
78 static unsigned long names2bits(char *tokens, size_t tokenslen,
79     char *bit_formatter, char *warning);
80 
81 uint64_t	system_clock_freq;
82 uint_t		niommu_tsbs = 0;
83 
84 static int n_l2_caches = 0;
85 
86 /* prevent compilation with VAC defined */
87 #ifdef VAC
88 #error "The sun4v architecture does not support VAC"
89 #endif
90 
91 #define	S_VAC_SIZE	MMU_PAGESIZE
92 #define	S_VAC_SHIFT	MMU_PAGESHIFT
93 
94 int		vac_size = S_VAC_SIZE;
95 uint_t		vac_mask = MMU_PAGEMASK & (S_VAC_SIZE - 1);
96 int		vac_shift = S_VAC_SHIFT;
97 uintptr_t	shm_alignment = S_VAC_SIZE;
98 
99 void
100 map_wellknown_devices()
101 {
102 }
103 
104 void
105 fill_cpu(md_t *mdp, mde_cookie_t cpuc)
106 {
107 	struct cpu_node *cpunode;
108 	uint64_t cpuid;
109 	uint64_t clk_freq;
110 	char *namebuf;
111 	char *namebufp;
112 	int namelen;
113 	uint64_t associativity = 0, linesize = 0, size = 0;
114 
115 	if (md_get_prop_val(mdp, cpuc, "id", &cpuid)) {
116 		return;
117 	}
118 
119 	/* All out-of-range cpus will be stopped later. */
120 	if (cpuid >= NCPU) {
121 		cmn_err(CE_CONT, "fill_cpu: out of range cpuid %ld - "
122 		    "cpu excluded from configuration\n", cpuid);
123 
124 		return;
125 	}
126 
127 	cpunode = &cpunodes[cpuid];
128 	cpunode->cpuid = (int)cpuid;
129 	cpunode->device_id = cpuid;
130 
131 	if (sizeof (cpunode->fru_fmri) > strlen(CPU_FRU_FMRI))
132 		(void) strcpy(cpunode->fru_fmri, CPU_FRU_FMRI);
133 
134 	if (md_get_prop_data(mdp, cpuc,
135 	    "compatible", (uint8_t **)&namebuf, &namelen)) {
136 		cmn_err(CE_PANIC, "fill_cpu: Cannot read compatible "
137 		    "property");
138 	}
139 	namebufp = namebuf;
140 	if (strncmp(namebufp, "SUNW,", 5) == 0)
141 		namebufp += 5;
142 	if (strlen(namebufp) > sizeof (cpunode->name))
143 		cmn_err(CE_PANIC, "Compatible property too big to "
144 		    "fit into the cpunode name buffer");
145 	(void) strcpy(cpunode->name, namebufp);
146 
147 	if (md_get_prop_val(mdp, cpuc,
148 	    "clock-frequency", &clk_freq)) {
149 			clk_freq = 0;
150 	}
151 	cpunode->clock_freq = clk_freq;
152 
153 	ASSERT(cpunode->clock_freq != 0);
154 	/*
155 	 * Compute scaling factor based on rate of %tick. This is used
156 	 * to convert from ticks derived from %tick to nanoseconds. See
157 	 * comment in sun4u/sys/clock.h for details.
158 	 */
159 	cpunode->tick_nsec_scale = (uint_t)(((uint64_t)NANOSEC <<
160 	    (32 - TICK_NSEC_SHIFT)) / cpunode->clock_freq);
161 
162 	/*
163 	 * The nodeid is not used in sun4v at all. Setting it
164 	 * to positive value to make starting of slave CPUs
165 	 * code happy.
166 	 */
167 	cpunode->nodeid = cpuid + 1;
168 
169 	/*
170 	 * Obtain the L2 cache information from MD.
171 	 * If "Cache" node exists, then set L2 cache properties
172 	 * as read from MD.
173 	 * If node does not exists, then set the L2 cache properties
174 	 * in individual CPU module.
175 	 */
176 	if ((!get_l2_cache_info(mdp, cpuc,
177 	    &associativity, &size, &linesize)) ||
178 	    associativity == 0 || size == 0 || linesize == 0) {
179 		cpu_fiximp(cpunode);
180 	} else {
181 		/*
182 		 * Do not expect L2 cache properties to be bigger
183 		 * than 32-bit quantity.
184 		 */
185 		cpunode->ecache_associativity = (int)associativity;
186 		cpunode->ecache_size = (int)size;
187 		cpunode->ecache_linesize = (int)linesize;
188 	}
189 
190 	cpunode->ecache_setsize =
191 	    cpunode->ecache_size / cpunode->ecache_associativity;
192 
193 	/*
194 	 * Initialize the mapping for exec unit, chip and core.
195 	 */
196 	cpunode->exec_unit_mapping = NO_EU_MAPPING_FOUND;
197 	cpunode->l2_cache_mapping = NO_MAPPING_FOUND;
198 	cpunode->core_mapping = NO_CORE_MAPPING_FOUND;
199 
200 	if (ecache_setsize == 0)
201 		ecache_setsize = cpunode->ecache_setsize;
202 	if (ecache_alignsize == 0)
203 		ecache_alignsize = cpunode->ecache_linesize;
204 
205 }
206 
207 void
208 empty_cpu(int cpuid)
209 {
210 	bzero(&cpunodes[cpuid], sizeof (struct cpu_node));
211 }
212 
213 /*
214  * Use L2 cache node to derive the chip mapping.
215  */
216 void
217 setup_chip_mappings(md_t *mdp)
218 {
219 	int ncache, ncpu;
220 	mde_cookie_t *node, *cachelist;
221 	int i, j;
222 	processorid_t cpuid;
223 	int idx = 0;
224 
225 	ncache = md_alloc_scan_dag(mdp, md_root_node(mdp), "cache",
226 	    "fwd", &cachelist);
227 
228 	/*
229 	 * The "cache" node is optional in MD, therefore ncaches can be 0.
230 	 */
231 	if (ncache < 1) {
232 		return;
233 	}
234 
235 	for (i = 0; i < ncache; i++) {
236 		uint64_t cache_level;
237 		uint64_t lcpuid;
238 
239 		if (md_get_prop_val(mdp, cachelist[i], "level", &cache_level))
240 			continue;
241 
242 		if (cache_level != 2)
243 			continue;
244 
245 		/*
246 		 * Found a l2 cache node. Find out the cpu nodes it
247 		 * points to.
248 		 */
249 		ncpu = md_alloc_scan_dag(mdp, cachelist[i], "cpu",
250 		    "back", &node);
251 
252 		if (ncpu < 1)
253 			continue;
254 
255 		for (j = 0; j < ncpu; j++) {
256 			if (md_get_prop_val(mdp, node[j], "id", &lcpuid))
257 				continue;
258 			if (lcpuid >= NCPU)
259 				continue;
260 			cpuid = (processorid_t)lcpuid;
261 			cpunodes[cpuid].l2_cache_mapping = idx;
262 		}
263 		md_free_scan_dag(mdp, &node);
264 
265 		idx++;
266 	}
267 
268 	md_free_scan_dag(mdp, &cachelist);
269 }
270 
271 void
272 setup_exec_unit_mappings(md_t *mdp)
273 {
274 	int num, num_eunits;
275 	mde_cookie_t cpus_node;
276 	mde_cookie_t *node, *eunit;
277 	int idx, i, j;
278 	processorid_t cpuid;
279 	char *eunit_name = broken_md_flag ? "exec_unit" : "exec-unit";
280 	enum eu_type { INTEGER, FPU } etype;
281 
282 	/*
283 	 * Find the cpu integer exec units - and
284 	 * setup the mappings appropriately.
285 	 */
286 	num = md_alloc_scan_dag(mdp, md_root_node(mdp), "cpus", "fwd", &node);
287 	if (num < 1)
288 		cmn_err(CE_PANIC, "No cpus node in machine description");
289 	if (num > 1)
290 		cmn_err(CE_PANIC, "More than 1 cpus node in machine"
291 		    " description");
292 
293 	cpus_node = node[0];
294 	md_free_scan_dag(mdp, &node);
295 
296 	num_eunits = md_alloc_scan_dag(mdp, cpus_node, eunit_name,
297 	    "fwd", &eunit);
298 	if (num_eunits > 0) {
299 		char *int_str = broken_md_flag ? "int" : "integer";
300 		char *fpu_str = "fp";
301 
302 		/* Spin through and find all the integer exec units */
303 		for (i = 0; i < num_eunits; i++) {
304 			char *p;
305 			char *val;
306 			int vallen;
307 			uint64_t lcpuid;
308 
309 			/* ignore nodes with no type */
310 			if (md_get_prop_data(mdp, eunit[i], "type",
311 			    (uint8_t **)&val, &vallen))
312 				continue;
313 
314 			for (p = val; *p != '\0'; p += strlen(p) + 1) {
315 				if (strcmp(p, int_str) == 0) {
316 					etype = INTEGER;
317 					goto found;
318 				}
319 				if (strcmp(p, fpu_str) == 0) {
320 					etype = FPU;
321 					goto found;
322 				}
323 			}
324 
325 			continue;
326 found:
327 			idx = NCPU + i;
328 			/*
329 			 * find the cpus attached to this EU and
330 			 * update their mapping indices
331 			 */
332 			num = md_alloc_scan_dag(mdp, eunit[i], "cpu",
333 			    "back", &node);
334 
335 			if (num < 1)
336 				cmn_err(CE_PANIC, "exec-unit node in MD"
337 				    " not attached to a cpu node");
338 
339 			for (j = 0; j < num; j++) {
340 				if (md_get_prop_val(mdp, node[j], "id",
341 				    &lcpuid))
342 					continue;
343 				if (lcpuid >= NCPU)
344 					continue;
345 				cpuid = (processorid_t)lcpuid;
346 				switch (etype) {
347 				case INTEGER:
348 					cpunodes[cpuid].exec_unit_mapping = idx;
349 					break;
350 				case FPU:
351 					cpunodes[cpuid].fpu_mapping = idx;
352 					break;
353 				}
354 			}
355 			md_free_scan_dag(mdp, &node);
356 		}
357 		md_free_scan_dag(mdp, &eunit);
358 	}
359 }
360 
361 /*
362  * Setup instruction cache coherency.  The "memory-coherent" property
363  * is optional.  Default for Icache_coherency is 1 (I$ is coherent).
364  * If we find an Icache with coherency == 0, then enable non-coherent
365  * Icache support.
366  */
367 void
368 setup_icache_coherency(md_t *mdp)
369 {
370 	int ncache;
371 	mde_cookie_t *cachelist;
372 	int i;
373 
374 	ncache = md_alloc_scan_dag(mdp, md_root_node(mdp), "cache",
375 	    "fwd", &cachelist);
376 
377 	/*
378 	 * The "cache" node is optional in MD, therefore ncaches can be 0.
379 	 */
380 	if (ncache < 1) {
381 		return;
382 	}
383 
384 	for (i = 0; i < ncache; i++) {
385 		uint64_t cache_level;
386 		uint64_t memory_coherent;
387 		uint8_t *type;
388 		int typelen;
389 
390 		if (md_get_prop_val(mdp, cachelist[i], "level",
391 		    &cache_level))
392 			continue;
393 
394 		if (cache_level != 1)
395 			continue;
396 
397 		if (md_get_prop_data(mdp, cachelist[i], "type",
398 		    &type, &typelen))
399 			continue;
400 
401 		if (strcmp((char *)type, "instn") != 0)
402 			continue;
403 
404 		if (md_get_prop_val(mdp, cachelist[i], "memory-coherent",
405 		    &memory_coherent))
406 			continue;
407 
408 		if (memory_coherent != 0)
409 			continue;
410 
411 		mach_setup_icache(memory_coherent);
412 		break;
413 	}
414 
415 	md_free_scan_dag(mdp, &cachelist);
416 }
417 
418 /*
419  * All the common setup of sun4v CPU modules is done by this routine.
420  */
421 void
422 cpu_setup_common(char **cpu_module_isa_set)
423 {
424 	extern int mmu_exported_pagesize_mask;
425 	int nocpus, i;
426 	size_t ra_limit;
427 	mde_cookie_t *cpulist;
428 	md_t *mdp;
429 
430 	if ((mdp = md_get_handle()) == NULL)
431 		cmn_err(CE_PANIC, "Unable to initialize machine description");
432 
433 	boot_ncpus = nocpus = md_alloc_scan_dag(mdp,
434 	    md_root_node(mdp), "cpu", "fwd", &cpulist);
435 	if (nocpus < 1) {
436 		cmn_err(CE_PANIC, "cpu_common_setup: cpulist allocation "
437 		    "failed or incorrect number of CPUs in MD");
438 	}
439 
440 	init_md_broken(mdp, cpulist);
441 
442 	if (use_page_coloring) {
443 		do_pg_coloring = 1;
444 	}
445 
446 	/*
447 	 * Get the valid mmu page sizes mask, Q sizes and isalist/r
448 	 * from the MD for the first available CPU in cpulist.
449 	 *
450 	 * Do not expect the MMU page sizes mask to be more than 32-bit.
451 	 */
452 	mmu_exported_pagesize_mask = (int)get_cpu_pagesizes(mdp, cpulist[0]);
453 
454 	/*
455 	 * Get the number of contexts and tsbs supported.
456 	 */
457 	if (get_mmu_shcontexts(mdp, cpulist[0]) >= MIN_NSHCONTEXTS &&
458 	    get_mmu_tsbs(mdp, cpulist[0]) >= MIN_NTSBS) {
459 		shctx_on = 1;
460 	}
461 
462 	for (i = 0; i < nocpus; i++)
463 		fill_cpu(mdp, cpulist[i]);
464 
465 	/* setup l2 cache count. */
466 	n_l2_caches = get_l2_cache_node_count(mdp);
467 
468 	setup_chip_mappings(mdp);
469 	setup_exec_unit_mappings(mdp);
470 	setup_icache_coherency(mdp);
471 
472 	/*
473 	 * If MD is broken then append the passed ISA set,
474 	 * otherwise trust the MD.
475 	 */
476 
477 	if (broken_md_flag)
478 		isa_list = construct_isalist(mdp, cpulist[0],
479 		    cpu_module_isa_set);
480 	else
481 		isa_list = construct_isalist(mdp, cpulist[0], NULL);
482 
483 	get_hwcaps(mdp, cpulist[0]);
484 	get_q_sizes(mdp, cpulist[0]);
485 	get_va_bits(mdp, cpulist[0]);
486 
487 	/*
488 	 * ra_limit is the highest real address in the machine.
489 	 */
490 	ra_limit = get_ra_limit(mdp);
491 
492 	md_free_scan_dag(mdp, &cpulist);
493 
494 	(void) md_fini_handle(mdp);
495 
496 	/*
497 	 * Block stores invalidate all pages of the d$ so pagecopy
498 	 * et. al. do not need virtual translations with virtual
499 	 * coloring taken into consideration.
500 	 */
501 	pp_consistent_coloring = 0;
502 
503 	/*
504 	 * The kpm mapping window.
505 	 * kpm_size:
506 	 *	The size of a single kpm range.
507 	 *	The overall size will be: kpm_size * vac_colors.
508 	 * kpm_vbase:
509 	 *	The virtual start address of the kpm range within the kernel
510 	 *	virtual address space. kpm_vbase has to be kpm_size aligned.
511 	 */
512 
513 	/*
514 	 * Make kpm_vbase, kpm_size aligned to kpm_size_shift.
515 	 * To do this find the nearest power of 2 size that the
516 	 * actual ra_limit fits within.
517 	 * If it is an even power of two use that, otherwise use the
518 	 * next power of two larger than ra_limit.
519 	 */
520 
521 	ASSERT(ra_limit != 0);
522 
523 	kpm_size_shift = (ra_limit & (ra_limit - 1)) != 0 ?
524 	    highbit(ra_limit) : highbit(ra_limit) - 1;
525 
526 	/*
527 	 * No virtual caches on sun4v so size matches size shift
528 	 */
529 	kpm_size = 1ul << kpm_size_shift;
530 
531 	if (va_bits < VA_ADDRESS_SPACE_BITS) {
532 		/*
533 		 * In case of VA hole
534 		 * kpm_base = hole_end + 1TB
535 		 * Starting 1TB beyond where VA hole ends because on Niagara
536 		 * processor software must not use pages within 4GB of the
537 		 * VA hole as instruction pages to avoid problems with
538 		 * prefetching into the VA hole.
539 		 */
540 		kpm_vbase = (caddr_t)((0ull - (1ull << (va_bits - 1))) +
541 		    (1ull << 40));
542 	} else {		/* Number of VA bits 64 ... no VA hole */
543 		kpm_vbase = (caddr_t)0x8000000000000000ull;	/* 8 EB */
544 	}
545 
546 	/*
547 	 * The traptrace code uses either %tick or %stick for
548 	 * timestamping.  The sun4v require use of %stick.
549 	 */
550 	traptrace_use_stick = 1;
551 }
552 
553 /*
554  * Get the nctxs from MD. If absent panic.
555  */
556 static uint64_t
557 get_mmu_ctx_bits(md_t *mdp, mde_cookie_t cpu_node_cookie)
558 {
559 	uint64_t ctx_bits;
560 
561 	if (md_get_prop_val(mdp, cpu_node_cookie, "mmu-#context-bits",
562 	    &ctx_bits))
563 		ctx_bits = 0;
564 
565 	if (ctx_bits < MIN_NCTXS_BITS || ctx_bits > MAX_NCTXS_BITS)
566 		cmn_err(CE_PANIC, "Incorrect %ld number of contexts bits "
567 		    "returned by MD", ctx_bits);
568 
569 	return (ctx_bits);
570 }
571 
572 /*
573  * Get the number of tsbs from MD. If absent the default value is 0.
574  */
575 static uint64_t
576 get_mmu_tsbs(md_t *mdp, mde_cookie_t cpu_node_cookie)
577 {
578 	uint64_t number_tsbs;
579 
580 	if (md_get_prop_val(mdp, cpu_node_cookie, "mmu-max-#tsbs",
581 	    &number_tsbs))
582 		number_tsbs = 0;
583 
584 	return (number_tsbs);
585 }
586 
587 /*
588  * Get the number of shared contexts from MD. If absent the default value is 0.
589  *
590  */
591 static uint64_t
592 get_mmu_shcontexts(md_t *mdp, mde_cookie_t cpu_node_cookie)
593 {
594 	uint64_t number_contexts;
595 
596 	if (md_get_prop_val(mdp, cpu_node_cookie, "mmu-#shared-contexts",
597 	    &number_contexts))
598 		number_contexts = 0;
599 
600 	return (number_contexts);
601 }
602 
603 /*
604  * Initalize supported page sizes information.
605  * Set to 0, if the page sizes mask information is absent in MD.
606  */
607 static uint64_t
608 get_cpu_pagesizes(md_t *mdp, mde_cookie_t cpu_node_cookie)
609 {
610 	uint64_t mmu_page_size_list;
611 
612 	if (md_get_prop_val(mdp, cpu_node_cookie, "mmu-page-size-list",
613 	    &mmu_page_size_list))
614 		mmu_page_size_list = 0;
615 
616 	if (mmu_page_size_list == 0 || mmu_page_size_list > MAX_PAGESIZE_MASK)
617 		cmn_err(CE_PANIC, "Incorrect 0x%lx pagesize mask returned"
618 		    "by MD", mmu_page_size_list);
619 
620 	return (mmu_page_size_list);
621 }
622 
623 /*
624  * This routine gets the isalist information from MD and appends
625  * the CPU module ISA set if required.
626  */
627 static char *
628 construct_isalist(md_t *mdp, mde_cookie_t cpu_node_cookie,
629     char **cpu_module_isa_set)
630 {
631 	extern int at_flags;
632 	char *md_isalist;
633 	int md_isalen;
634 	char *isabuf;
635 	int isalen;
636 	char **isa_set;
637 	char *p, *q;
638 	int cpu_module_isalen = 0, found = 0;
639 
640 	(void) md_get_prop_data(mdp, cpu_node_cookie,
641 	    "isalist", (uint8_t **)&isabuf, &isalen);
642 
643 	/*
644 	 * We support binaries for all the cpus that have shipped so far.
645 	 * The kernel emulates instructions that are not supported by hardware.
646 	 */
647 	at_flags = EF_SPARC_SUN_US3 | EF_SPARC_32PLUS | EF_SPARC_SUN_US1;
648 
649 	/*
650 	 * Construct the space separated isa_list.
651 	 */
652 	if (cpu_module_isa_set != NULL) {
653 		for (isa_set = cpu_module_isa_set; *isa_set != NULL;
654 		    isa_set++) {
655 			cpu_module_isalen += strlen(*isa_set);
656 			cpu_module_isalen++;	/* for space character */
657 		}
658 	}
659 
660 	/*
661 	 * Allocate the buffer of MD isa buffer length + CPU module
662 	 * isa buffer length.
663 	 */
664 	md_isalen = isalen + cpu_module_isalen + 2;
665 	md_isalist = (char *)prom_alloc((caddr_t)0, md_isalen, 0);
666 	if (md_isalist == NULL)
667 		cmn_err(CE_PANIC, "construct_isalist: Allocation failed for "
668 		    "md_isalist");
669 
670 	md_isalist[0] = '\0'; /* create an empty string to start */
671 	for (p = isabuf, q = p + isalen; p < q; p += strlen(p) + 1) {
672 		(void) strlcat(md_isalist, p, md_isalen);
673 		(void) strcat(md_isalist, " ");
674 	}
675 
676 	/*
677 	 * Check if the isa_set is present in isalist returned by MD.
678 	 * If yes, then no need to append it, if no then append it to
679 	 * isalist returned by MD.
680 	 */
681 	if (cpu_module_isa_set != NULL) {
682 		for (isa_set = cpu_module_isa_set; *isa_set != NULL;
683 		    isa_set++) {
684 			found = 0;
685 			for (p = isabuf, q = p + isalen; p < q;
686 			    p += strlen(p) + 1) {
687 				if (strcmp(p, *isa_set) == 0) {
688 					found = 1;
689 					break;
690 				}
691 			}
692 			if (!found) {
693 				(void) strlcat(md_isalist, *isa_set, md_isalen);
694 				(void) strcat(md_isalist, " ");
695 			}
696 		}
697 	}
698 
699 	/* Get rid of any trailing white spaces */
700 	md_isalist[strlen(md_isalist) - 1] = '\0';
701 
702 	return (md_isalist);
703 }
704 
705 static void
706 get_hwcaps(md_t *mdp, mde_cookie_t cpu_node_cookie)
707 {
708 	char *hwcapbuf;
709 	int hwcaplen;
710 
711 	if (md_get_prop_data(mdp, cpu_node_cookie,
712 	    "hwcap-list", (uint8_t **)&hwcapbuf, &hwcaplen)) {
713 		/* Property not found */
714 		return;
715 	}
716 
717 	cpu_hwcap_flags |= names2bits(hwcapbuf, hwcaplen, FMT_AV_SPARC,
718 	    "unrecognized token: %s");
719 }
720 
721 
722 /*
723  * Does the opposite of cmn_err(9f) "%b" conversion specification:
724  * Given a list of strings, converts them to a bit-vector.
725  *
726  *  tokens - is a buffer of [NUL-terminated] strings.
727  *  tokenslen - length of tokenbuf in bytes.
728  *  bit_formatter - is a %b format string, such as FMT_AV_SPARC
729  *    from /usr/include/sys/auxv_SPARC.h, of the form:
730  *    <base-char>[<bit-char><token-string>]...
731  *        <base-char> is ignored.
732  *        <bit-char>  is [1-32], as per cmn_err(9f).
733  *  warning - is a printf-style format string containing "%s",
734  *    which is used to print a warning message when an unrecognized
735  *    token is found.  If warning is NULL, no warning is printed.
736  * Returns a bit-vector corresponding to the specified tokens.
737  */
738 
739 static unsigned long
740 names2bits(char *tokens, size_t tokenslen, char *bit_formatter, char *warning)
741 {
742 	char *cur;
743 	size_t  curlen;
744 	unsigned long ul = 0;
745 	char *hit;
746 	char *bs;
747 
748 	bit_formatter++;	/* skip base; not needed for input */
749 	cur = tokens;
750 	while (tokenslen) {
751 		curlen = strlen(cur);
752 		bs = bit_formatter;
753 		/*
754 		 * We need a complicated while loop and the >=32 check,
755 		 * instead of a simple "if (strstr())" so that when the
756 		 * token is "vis", we don't match on "vis2" (for example).
757 		 */
758 		/* LINTED E_EQUALITY_NOT_ASSIGNMENT */
759 		while ((hit = strstr(bs, cur)) &&
760 		    *(hit + curlen) >= 32) {
761 			/*
762 			 * We're still in the middle of a word, i.e., not
763 			 * pointing at a <bit-char>.  So advance ptr
764 			 * to ensure forward progress.
765 			 */
766 			bs = hit + curlen + 1;
767 		}
768 
769 		if (hit != NULL) {
770 			ul |= (1<<(*(hit-1) - 1));
771 		} else {
772 			/* The token wasn't found in bit_formatter */
773 			if (warning != NULL)
774 				cmn_err(CE_WARN, warning, cur);
775 		}
776 		tokenslen -= curlen + 1;
777 		cur += curlen + 1;
778 	}
779 	return (ul);
780 }
781 
782 
783 uint64_t
784 get_ra_limit(md_t *mdp)
785 {
786 	mde_cookie_t *mem_list;
787 	mde_cookie_t *mblock_list;
788 	int i;
789 	int memnodes;
790 	int nmblock;
791 	uint64_t base;
792 	uint64_t size;
793 	uint64_t ra_limit = 0, new_limit = 0;
794 
795 	memnodes = md_alloc_scan_dag(mdp,
796 	    md_root_node(mdp), "memory", "fwd", &mem_list);
797 
798 	ASSERT(memnodes == 1);
799 
800 	nmblock = md_alloc_scan_dag(mdp,
801 	    mem_list[0], "mblock", "fwd", &mblock_list);
802 	if (nmblock < 1)
803 		cmn_err(CE_PANIC, "cannot find mblock nodes in MD");
804 
805 	for (i = 0; i < nmblock; i++) {
806 		if (md_get_prop_val(mdp, mblock_list[i], "base", &base))
807 			cmn_err(CE_PANIC, "base property missing from MD"
808 			    " mblock node");
809 		if (md_get_prop_val(mdp, mblock_list[i], "size", &size))
810 			cmn_err(CE_PANIC, "size property missing from MD"
811 			    " mblock node");
812 
813 		ASSERT(size != 0);
814 
815 		new_limit = base + size;
816 
817 		if (base > new_limit)
818 			cmn_err(CE_PANIC, "mblock in MD wrapped around");
819 
820 		if (new_limit > ra_limit)
821 			ra_limit = new_limit;
822 	}
823 
824 	ASSERT(ra_limit != 0);
825 
826 	if (ra_limit > MAX_REAL_ADDRESS) {
827 		cmn_err(CE_WARN, "Highest real address in MD too large"
828 		    " clipping to %llx\n", MAX_REAL_ADDRESS);
829 		ra_limit = MAX_REAL_ADDRESS;
830 	}
831 
832 	md_free_scan_dag(mdp, &mblock_list);
833 
834 	md_free_scan_dag(mdp, &mem_list);
835 
836 	return (ra_limit);
837 }
838 
839 /*
840  * This routine sets the globals for CPU and DEV mondo queue entries and
841  * resumable and non-resumable error queue entries.
842  *
843  * First, look up the number of bits available to pass an entry number.
844  * This can vary by platform and may result in allocating an unreasonably
845  * (or impossibly) large amount of memory for the corresponding table,
846  * so we clamp it by 'max_entries'.  Finally, since the q size is used when
847  * calling contig_mem_alloc(), which expects a power of 2, clamp the q size
848  * down to a power of 2.  If the prop is missing, use 'default_entries'.
849  */
850 static uint64_t
851 get_single_q_size(md_t *mdp, mde_cookie_t cpu_node_cookie,
852     char *qnamep, uint64_t default_entries, uint64_t max_entries)
853 {
854 	uint64_t entries;
855 
856 	if (default_entries > max_entries)
857 		cmn_err(CE_CONT, "!get_single_q_size: dflt %ld > "
858 		    "max %ld for %s\n", default_entries, max_entries, qnamep);
859 
860 	if (md_get_prop_val(mdp, cpu_node_cookie, qnamep, &entries)) {
861 		if (!broken_md_flag)
862 			cmn_err(CE_PANIC, "Missing %s property in MD cpu node",
863 			    qnamep);
864 		entries = default_entries;
865 	} else {
866 		entries = 1 << entries;
867 	}
868 
869 	entries = MIN(entries, max_entries);
870 	/* If not a power of 2, truncate to a power of 2. */
871 	if ((entries & (entries - 1)) != 0) {
872 		entries = 1 << (highbit(entries) - 1);
873 	}
874 
875 	return (entries);
876 }
877 
878 /* Scaling constant used to compute size of cpu mondo queue */
879 #define	CPU_MONDO_Q_MULTIPLIER	8
880 
881 static void
882 get_q_sizes(md_t *mdp, mde_cookie_t cpu_node_cookie)
883 {
884 	uint64_t max_qsize;
885 	mde_cookie_t *platlist;
886 	int nrnode;
887 
888 	/*
889 	 * Compute the maximum number of entries for the cpu mondo queue.
890 	 * Use the appropriate property in the platform node, if it is
891 	 * available.  Else, base it on NCPU.
892 	 */
893 	nrnode = md_alloc_scan_dag(mdp,
894 	    md_root_node(mdp), "platform", "fwd", &platlist);
895 
896 	ASSERT(nrnode == 1);
897 
898 	ncpu_guest_max = NCPU;
899 	(void) md_get_prop_val(mdp, platlist[0], "max-cpus", &ncpu_guest_max);
900 	max_qsize = ncpu_guest_max * CPU_MONDO_Q_MULTIPLIER;
901 
902 	md_free_scan_dag(mdp, &platlist);
903 
904 	cpu_q_entries = get_single_q_size(mdp, cpu_node_cookie,
905 	    "q-cpu-mondo-#bits", DEFAULT_CPU_Q_ENTRIES, max_qsize);
906 
907 	dev_q_entries = get_single_q_size(mdp, cpu_node_cookie,
908 	    "q-dev-mondo-#bits", DEFAULT_DEV_Q_ENTRIES, MAXIVNUM);
909 
910 	cpu_rq_entries = get_single_q_size(mdp, cpu_node_cookie,
911 	    "q-resumable-#bits", CPU_RQ_ENTRIES, MAX_CPU_RQ_ENTRIES);
912 
913 	cpu_nrq_entries = get_single_q_size(mdp, cpu_node_cookie,
914 	    "q-nonresumable-#bits", CPU_NRQ_ENTRIES, MAX_CPU_NRQ_ENTRIES);
915 }
916 
917 
918 static void
919 get_va_bits(md_t *mdp, mde_cookie_t cpu_node_cookie)
920 {
921 	uint64_t value = VA_ADDRESS_SPACE_BITS;
922 
923 	if (md_get_prop_val(mdp, cpu_node_cookie, "mmu-#va-bits", &value))
924 		cmn_err(CE_PANIC, "mmu-#va-bits property  not found in MD");
925 
926 
927 	if (value == 0 || value > VA_ADDRESS_SPACE_BITS)
928 		cmn_err(CE_PANIC, "Incorrect number of va bits in MD");
929 
930 	/* Do not expect number of VA bits to be more than 32-bit quantity */
931 
932 	va_bits = (int)value;
933 
934 	/*
935 	 * Correct the value for VA bits on UltraSPARC-T1 based systems
936 	 * in case of broken MD.
937 	 */
938 	if (broken_md_flag)
939 		va_bits = DEFAULT_VA_ADDRESS_SPACE_BITS;
940 }
941 
942 int
943 l2_cache_node_count(void)
944 {
945 	return (n_l2_caches);
946 }
947 
948 /*
949  * count the number of l2 caches.
950  */
951 int
952 get_l2_cache_node_count(md_t *mdp)
953 {
954 	int i;
955 	mde_cookie_t *cachenodes;
956 	uint64_t level;
957 	int n_cachenodes = md_alloc_scan_dag(mdp, md_root_node(mdp),
958 	    "cache", "fwd", &cachenodes);
959 	int l2_caches = 0;
960 
961 	for (i = 0; i < n_cachenodes; i++) {
962 		if (md_get_prop_val(mdp, cachenodes[i], "level", &level) != 0) {
963 			level = 0;
964 		}
965 		if (level == 2) {
966 			l2_caches++;
967 		}
968 	}
969 	md_free_scan_dag(mdp, &cachenodes);
970 	return (l2_caches);
971 }
972 
973 /*
974  * This routine returns the L2 cache information such as -- associativity,
975  * size and linesize.
976  */
977 static int
978 get_l2_cache_info(md_t *mdp, mde_cookie_t cpu_node_cookie,
979 	    uint64_t *associativity, uint64_t *size, uint64_t *linesize)
980 {
981 	mde_cookie_t *cachelist;
982 	int ncaches, i;
983 	uint64_t cache_level = 0;
984 
985 	ncaches = md_alloc_scan_dag(mdp, cpu_node_cookie, "cache",
986 	    "fwd", &cachelist);
987 	/*
988 	 * The "cache" node is optional in MD, therefore ncaches can be 0.
989 	 */
990 	if (ncaches < 1) {
991 		return (0);
992 	}
993 
994 	for (i = 0; i < ncaches; i++) {
995 		uint64_t local_assoc;
996 		uint64_t local_size;
997 		uint64_t local_lsize;
998 
999 		if (md_get_prop_val(mdp, cachelist[i], "level", &cache_level))
1000 			continue;
1001 
1002 		if (cache_level != 2) continue;
1003 
1004 		/* If properties are missing from this cache ignore it */
1005 
1006 		if ((md_get_prop_val(mdp, cachelist[i],
1007 		    "associativity", &local_assoc))) {
1008 			continue;
1009 		}
1010 
1011 		if ((md_get_prop_val(mdp, cachelist[i],
1012 		    "size", &local_size))) {
1013 			continue;
1014 		}
1015 
1016 		if ((md_get_prop_val(mdp, cachelist[i],
1017 		    "line-size", &local_lsize))) {
1018 			continue;
1019 		}
1020 
1021 		*associativity = local_assoc;
1022 		*size = local_size;
1023 		*linesize = local_lsize;
1024 		break;
1025 	}
1026 
1027 	md_free_scan_dag(mdp, &cachelist);
1028 
1029 	return ((cache_level == 2) ? 1 : 0);
1030 }
1031 
1032 
1033 /*
1034  * Set the broken_md_flag to 1 if the MD doesn't have
1035  * the domaining-enabled property in the platform node and the
1036  * platform uses the UltraSPARC-T1 cpu. This flag is used to
1037  * workaround some of the incorrect MD properties.
1038  */
1039 static void
1040 init_md_broken(md_t *mdp, mde_cookie_t *cpulist)
1041 {
1042 	int nrnode;
1043 	mde_cookie_t *platlist, rootnode;
1044 	uint64_t val = 0;
1045 	char *namebuf;
1046 	int namelen;
1047 
1048 	rootnode = md_root_node(mdp);
1049 	ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE);
1050 	ASSERT(cpulist);
1051 
1052 	nrnode = md_alloc_scan_dag(mdp, rootnode, "platform", "fwd",
1053 	    &platlist);
1054 
1055 	if (nrnode < 1)
1056 		cmn_err(CE_PANIC, "init_md_broken: platform node missing");
1057 
1058 	if (md_get_prop_data(mdp, cpulist[0],
1059 	    "compatible", (uint8_t **)&namebuf, &namelen)) {
1060 		cmn_err(CE_PANIC, "init_md_broken: "
1061 		    "Cannot read 'compatible' property of 'cpu' node");
1062 	}
1063 
1064 	if (md_get_prop_val(mdp, platlist[0],
1065 	    "domaining-enabled", &val) == -1 &&
1066 	    strcmp(namebuf, "SUNW,UltraSPARC-T1") == 0)
1067 		broken_md_flag = 1;
1068 
1069 	md_free_scan_dag(mdp, &platlist);
1070 }
1071