xref: /illumos-gate/usr/src/uts/sun4v/os/fillsysinfo.c (revision 15deec58)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/errno.h>
29 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/cpu.h>
32 #include <sys/cpuvar.h>
33 #include <sys/clock.h>
34 #include <sys/promif.h>
35 #include <sys/promimpl.h>
36 #include <sys/systm.h>
37 #include <sys/machsystm.h>
38 #include <sys/debug.h>
39 #include <sys/sunddi.h>
40 #include <sys/modctl.h>
41 #include <sys/cpu_module.h>
42 #include <sys/kobj.h>
43 #include <sys/cmp.h>
44 #include <sys/async.h>
45 #include <vm/page.h>
46 #include <vm/hat_sfmmu.h>
47 #include <sys/sysmacros.h>
48 #include <sys/mach_descrip.h>
49 #include <sys/mdesc.h>
50 #include <sys/archsystm.h>
51 #include <sys/error.h>
52 #include <sys/mmu.h>
53 #include <sys/bitmap.h>
54 #include <sys/intreg.h>
55 
56 int ncpunode;
57 struct cpu_node cpunodes[NCPU];
58 
59 uint64_t cpu_q_entries;
60 uint64_t dev_q_entries;
61 uint64_t cpu_rq_entries;
62 uint64_t cpu_nrq_entries;
63 uint64_t ncpu_guest_max;
64 
65 void fill_cpu(md_t *, mde_cookie_t);
66 
67 static uint64_t get_mmu_ctx_bits(md_t *, mde_cookie_t);
68 static uint64_t get_mmu_tsbs(md_t *, mde_cookie_t);
69 static uint64_t	get_mmu_shcontexts(md_t *, mde_cookie_t);
70 static uint64_t get_cpu_pagesizes(md_t *, mde_cookie_t);
71 static char *construct_isalist(md_t *, mde_cookie_t, char **);
72 static void set_at_flags(char *, int, char **);
73 static void init_md_broken(md_t *, mde_cookie_t *);
74 static int get_l2_cache_info(md_t *, mde_cookie_t, uint64_t *, uint64_t *,
75     uint64_t *);
76 static id_t get_exec_unit_mapping(md_t *, mde_cookie_t, mde_cookie_t *);
77 static int find_exec_unit_id(mde_cookie_t, mde_cookie_t *);
78 static void get_q_sizes(md_t *, mde_cookie_t);
79 static void get_va_bits(md_t *, mde_cookie_t);
80 static size_t get_ra_limit(md_t *);
81 
82 uint64_t	system_clock_freq;
83 int		niobus = 0;
84 uint_t		niommu_tsbs = 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 		 * Start off by assigning the cpu id as the default
195 		 * mapping index.
196 		 */
197 
198 	cpunode->exec_unit_mapping = NO_EU_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 	ncpunode++;
206 }
207 
208 void
209 empty_cpu(int cpuid)
210 {
211 	bzero(&cpunodes[cpuid], sizeof (struct cpu_node));
212 	ncpunode--;
213 }
214 
215 void
216 setup_exec_unit_mappings(md_t *mdp)
217 {
218 	uint64_t num, num_eunits;
219 	mde_cookie_t cpus_node;
220 	mde_cookie_t *node, *eunit;
221 	int idx, i, j;
222 	processorid_t cpuid;
223 	char *eunit_name = broken_md_flag ? "exec_unit" : "exec-unit";
224 	enum eu_type { INTEGER, FPU } etype;
225 
226 	/*
227 	 * Find the cpu integer exec units - and
228 	 * setup the mappings appropriately.
229 	 */
230 	num = md_alloc_scan_dag(mdp, md_root_node(mdp), "cpus", "fwd", &node);
231 	if (num < 1)
232 		cmn_err(CE_PANIC, "No cpus node in machine description");
233 	if (num > 1)
234 		cmn_err(CE_PANIC, "More than 1 cpus node in machine"
235 		    " description");
236 
237 	cpus_node = node[0];
238 	md_free_scan_dag(mdp, &node);
239 
240 	num_eunits = md_alloc_scan_dag(mdp, cpus_node, eunit_name,
241 	    "fwd", &eunit);
242 	if (num_eunits > 0) {
243 		char *int_str = broken_md_flag ? "int" : "integer";
244 		char *fpu_str = "fp";
245 
246 		/* Spin through and find all the integer exec units */
247 		for (i = 0; i < num_eunits; i++) {
248 			char *p;
249 			char *val;
250 			int vallen;
251 			uint64_t lcpuid;
252 
253 			/* ignore nodes with no type */
254 			if (md_get_prop_data(mdp, eunit[i], "type",
255 				(uint8_t **)&val, &vallen)) continue;
256 
257 			for (p = val; *p != '\0'; p += strlen(p) + 1) {
258 				if (strcmp(p, int_str) == 0) {
259 					etype = INTEGER;
260 					goto found;
261 				}
262 				if (strcmp(p, fpu_str) == 0) {
263 					etype = FPU;
264 					goto found;
265 				}
266 			}
267 
268 			continue;
269 found:
270 			idx = NCPU + i;
271 			/*
272 			 * find the cpus attached to this EU and
273 			 * update their mapping indices
274 			 */
275 			num = md_alloc_scan_dag(mdp, eunit[i], "cpu",
276 			    "back", &node);
277 
278 			if (num < 1)
279 				cmn_err(CE_PANIC, "exec-unit node in MD"
280 				    " not attached to a cpu node");
281 
282 			for (j = 0; j < num; j++) {
283 				if (md_get_prop_val(mdp, node[j], "id",
284 				    &lcpuid))
285 					continue;
286 				if (lcpuid >= NCPU)
287 					continue;
288 				cpuid = (processorid_t)lcpuid;
289 				switch (etype) {
290 				case INTEGER:
291 					cpunodes[cpuid].exec_unit_mapping = idx;
292 					break;
293 				case FPU:
294 					cpunodes[cpuid].fpu_mapping = idx;
295 					break;
296 				}
297 			}
298 			md_free_scan_dag(mdp, &node);
299 		}
300 
301 
302 		md_free_scan_dag(mdp, &eunit);
303 	}
304 }
305 
306 /*
307  * All the common setup of sun4v CPU modules is done by this routine.
308  */
309 void
310 cpu_setup_common(char **cpu_module_isa_set)
311 {
312 	extern int mmu_exported_pagesize_mask;
313 	int nocpus, i;
314 	size_t ra_limit;
315 	mde_cookie_t *cpulist;
316 	md_t *mdp;
317 
318 	if ((mdp = md_get_handle()) == NULL)
319 		cmn_err(CE_PANIC, "Unable to initialize machine description");
320 
321 	nocpus = md_alloc_scan_dag(mdp,
322 	    md_root_node(mdp), "cpu", "fwd", &cpulist);
323 	if (nocpus < 1) {
324 		cmn_err(CE_PANIC, "cpu_common_setup: cpulist allocation "
325 		    "failed or incorrect number of CPUs in MD");
326 	}
327 
328 	init_md_broken(mdp, cpulist);
329 
330 	if (use_page_coloring) {
331 		do_pg_coloring = 1;
332 	}
333 
334 	/*
335 	 * Get the valid mmu page sizes mask, Q sizes and isalist/r
336 	 * from the MD for the first available CPU in cpulist.
337 	 *
338 	 * Do not expect the MMU page sizes mask to be more than 32-bit.
339 	 */
340 	mmu_exported_pagesize_mask = (int)get_cpu_pagesizes(mdp, cpulist[0]);
341 
342 	/*
343 	 * Get the number of contexts and tsbs supported.
344 	 */
345 	if (get_mmu_shcontexts(mdp, cpulist[0]) >= MIN_NSHCONTEXTS &&
346 	    get_mmu_tsbs(mdp, cpulist[0]) >= MIN_NTSBS) {
347 		shctx_on = 1;
348 	}
349 
350 	for (i = 0; i < nocpus; i++)
351 		fill_cpu(mdp, cpulist[i]);
352 
353 	setup_exec_unit_mappings(mdp);
354 
355 	/*
356 	 * If MD is broken then append the passed ISA set,
357 	 * otherwise trust the MD.
358 	 */
359 
360 	if (broken_md_flag)
361 		isa_list = construct_isalist(mdp, cpulist[0],
362 		    cpu_module_isa_set);
363 	else
364 		isa_list = construct_isalist(mdp, cpulist[0], NULL);
365 
366 	get_q_sizes(mdp, cpulist[0]);
367 
368 	get_va_bits(mdp, cpulist[0]);
369 
370 	/*
371 	 * ra_limit is the highest real address in the machine.
372 	 */
373 	ra_limit = get_ra_limit(mdp);
374 
375 	md_free_scan_dag(mdp, &cpulist);
376 
377 	(void) md_fini_handle(mdp);
378 
379 	/*
380 	 * Block stores invalidate all pages of the d$ so pagecopy
381 	 * et. al. do not need virtual translations with virtual
382 	 * coloring taken into consideration.
383 	 */
384 	pp_consistent_coloring = 0;
385 
386 	/*
387 	 * The kpm mapping window.
388 	 * kpm_size:
389 	 *	The size of a single kpm range.
390 	 *	The overall size will be: kpm_size * vac_colors.
391 	 * kpm_vbase:
392 	 *	The virtual start address of the kpm range within the kernel
393 	 *	virtual address space. kpm_vbase has to be kpm_size aligned.
394 	 */
395 
396 	/*
397 	 * Make kpm_vbase, kpm_size aligned to kpm_size_shift.
398 	 * To do this find the nearest power of 2 size that the
399 	 * actual ra_limit fits within.
400 	 * If it is an even power of two use that, otherwise use the
401 	 * next power of two larger than ra_limit.
402 	 */
403 
404 	ASSERT(ra_limit != 0);
405 
406 	kpm_size_shift = (ra_limit & (ra_limit - 1)) != 0 ?
407 		highbit(ra_limit) : highbit(ra_limit) - 1;
408 
409 	/*
410 	 * No virtual caches on sun4v so size matches size shift
411 	 */
412 	kpm_size = 1ul << kpm_size_shift;
413 
414 	if (va_bits < VA_ADDRESS_SPACE_BITS) {
415 		/*
416 		 * In case of VA hole
417 		 * kpm_base = hole_end + 1TB
418 		 * Starting 1TB beyond where VA hole ends because on Niagara
419 		 * processor software must not use pages within 4GB of the
420 		 * VA hole as instruction pages to avoid problems with
421 		 * prefetching into the VA hole.
422 		 */
423 		kpm_vbase = (caddr_t)((0ull - (1ull << (va_bits - 1))) +
424 		    (1ull << 40));
425 	} else {		/* Number of VA bits 64 ... no VA hole */
426 		kpm_vbase = (caddr_t)0x8000000000000000ull;	/* 8 EB */
427 	}
428 
429 	/*
430 	 * The traptrace code uses either %tick or %stick for
431 	 * timestamping.  The sun4v require use of %stick.
432 	 */
433 	traptrace_use_stick = 1;
434 }
435 
436 /*
437  * Get the nctxs from MD. If absent panic.
438  */
439 static uint64_t
440 get_mmu_ctx_bits(md_t *mdp, mde_cookie_t cpu_node_cookie)
441 {
442 	uint64_t ctx_bits;
443 
444 	if (md_get_prop_val(mdp, cpu_node_cookie, "mmu-#context-bits",
445 	    &ctx_bits))
446 		ctx_bits = 0;
447 
448 	if (ctx_bits < MIN_NCTXS_BITS || ctx_bits > MAX_NCTXS_BITS)
449 		cmn_err(CE_PANIC, "Incorrect %ld number of contexts bits "
450 		    "returned by MD", ctx_bits);
451 
452 	return (ctx_bits);
453 }
454 
455 /*
456  * Get the number of tsbs from MD. If absent the default value is 0.
457  */
458 static uint64_t
459 get_mmu_tsbs(md_t *mdp, mde_cookie_t cpu_node_cookie)
460 {
461 	uint64_t number_tsbs;
462 
463 	if (md_get_prop_val(mdp, cpu_node_cookie, "mmu-max-#tsbs",
464 	    &number_tsbs))
465 		number_tsbs = 0;
466 
467 	return (number_tsbs);
468 }
469 
470 /*
471  * Get the number of shared contexts from MD. This property more accurately
472  * describes the total number of contexts available, not just "shared contexts".
473  * If absent the default value is 1,
474  *
475  */
476 static uint64_t
477 get_mmu_shcontexts(md_t *mdp, mde_cookie_t cpu_node_cookie)
478 {
479 	uint64_t number_contexts;
480 
481 	if (md_get_prop_val(mdp, cpu_node_cookie, "mmu-#shared-contexts",
482 	    &number_contexts))
483 		number_contexts = 0;
484 
485 	return (number_contexts);
486 }
487 
488 /*
489  * Initalize supported page sizes information.
490  * Set to 0, if the page sizes mask information is absent in MD.
491  */
492 static uint64_t
493 get_cpu_pagesizes(md_t *mdp, mde_cookie_t cpu_node_cookie)
494 {
495 	uint64_t mmu_page_size_list;
496 
497 	if (md_get_prop_val(mdp, cpu_node_cookie, "mmu-page-size-list",
498 	    &mmu_page_size_list))
499 		mmu_page_size_list = 0;
500 
501 	if (mmu_page_size_list == 0 || mmu_page_size_list > MAX_PAGESIZE_MASK)
502 		cmn_err(CE_PANIC, "Incorrect 0x%lx pagesize mask returned"
503 		    "by MD", mmu_page_size_list);
504 
505 	return (mmu_page_size_list);
506 }
507 
508 /*
509  * This routine gets the isalist information from MD and appends
510  * the CPU module ISA set if required.
511  */
512 static char *
513 construct_isalist(md_t *mdp, mde_cookie_t cpu_node_cookie,
514     char **cpu_module_isa_set)
515 {
516 	extern int at_flags;
517 	char *md_isalist;
518 	int md_isalen;
519 	char *isabuf;
520 	int isalen;
521 	char **isa_set;
522 	char *p, *q;
523 	int cpu_module_isalen = 0, found = 0;
524 
525 	(void) md_get_prop_data(mdp, cpu_node_cookie,
526 	    "isalist", (uint8_t **)&isabuf, &isalen);
527 
528 	/*
529 	 * We support binaries for all the cpus that have shipped so far.
530 	 * The kernel emulates instructions that are not supported by hardware.
531 	 */
532 	at_flags = EF_SPARC_SUN_US3 | EF_SPARC_32PLUS | EF_SPARC_SUN_US1;
533 
534 	/*
535 	 * Construct the space separated isa_list.
536 	 */
537 	if (cpu_module_isa_set != NULL) {
538 		for (isa_set = cpu_module_isa_set; *isa_set != NULL;
539 		    isa_set++) {
540 			cpu_module_isalen += strlen(*isa_set);
541 			cpu_module_isalen++;	/* for space character */
542 		}
543 	}
544 
545 	/*
546 	 * Allocate the buffer of MD isa buffer length + CPU module
547 	 * isa buffer length.
548 	 */
549 	md_isalen = isalen + cpu_module_isalen + 2;
550 	md_isalist = (char *)prom_alloc((caddr_t)0, md_isalen, 0);
551 	if (md_isalist == NULL)
552 		cmn_err(CE_PANIC, "construct_isalist: Allocation failed for "
553 		    "md_isalist");
554 
555 	md_isalist[0] = '\0'; /* create an empty string to start */
556 	for (p = isabuf, q = p + isalen; p < q; p += strlen(p) + 1) {
557 		(void) strlcat(md_isalist, p, md_isalen);
558 		(void) strcat(md_isalist, " ");
559 	}
560 
561 	/*
562 	 * Check if the isa_set is present in isalist returned by MD.
563 	 * If yes, then no need to append it, if no then append it to
564 	 * isalist returned by MD.
565 	 */
566 	if (cpu_module_isa_set != NULL) {
567 		for (isa_set = cpu_module_isa_set; *isa_set != NULL;
568 		    isa_set++) {
569 			found = 0;
570 			for (p = isabuf, q = p + isalen; p < q;
571 			    p += strlen(p) + 1) {
572 				if (strcmp(p, *isa_set) == 0) {
573 					found = 1;
574 					break;
575 				}
576 			}
577 			if (!found) {
578 				(void) strlcat(md_isalist, *isa_set, md_isalen);
579 				(void) strcat(md_isalist, " ");
580 			}
581 		}
582 	}
583 
584 	/* Get rid of any trailing white spaces */
585 	md_isalist[strlen(md_isalist) - 1] = '\0';
586 
587 	return (md_isalist);
588 }
589 
590 uint64_t
591 get_ra_limit(md_t *mdp)
592 {
593 	mde_cookie_t *mem_list;
594 	mde_cookie_t *mblock_list;
595 	int i;
596 	int memnodes;
597 	int nmblock;
598 	uint64_t base;
599 	uint64_t size;
600 	uint64_t ra_limit = 0, new_limit = 0;
601 
602 	memnodes = md_alloc_scan_dag(mdp,
603 	    md_root_node(mdp), "memory", "fwd", &mem_list);
604 
605 	ASSERT(memnodes == 1);
606 
607 	nmblock = md_alloc_scan_dag(mdp,
608 	    mem_list[0], "mblock", "fwd", &mblock_list);
609 	if (nmblock < 1)
610 		cmn_err(CE_PANIC, "cannot find mblock nodes in MD");
611 
612 	for (i = 0; i < nmblock; i++) {
613 		if (md_get_prop_val(mdp, mblock_list[i], "base", &base))
614 			cmn_err(CE_PANIC, "base property missing from MD"
615 			    " mblock node");
616 		if (md_get_prop_val(mdp, mblock_list[i], "size", &size))
617 			cmn_err(CE_PANIC, "size property missing from MD"
618 			    " mblock node");
619 
620 		ASSERT(size != 0);
621 
622 		new_limit = base + size;
623 
624 		if (base > new_limit)
625 			cmn_err(CE_PANIC, "mblock in MD wrapped around");
626 
627 		if (new_limit > ra_limit)
628 		    ra_limit = new_limit;
629 	}
630 
631 	ASSERT(ra_limit != 0);
632 
633 	if (ra_limit > MAX_REAL_ADDRESS) {
634 		cmn_err(CE_WARN, "Highest real address in MD too large"
635 		    " clipping to %llx\n", MAX_REAL_ADDRESS);
636 		ra_limit = MAX_REAL_ADDRESS;
637 	}
638 
639 	md_free_scan_dag(mdp, &mblock_list);
640 
641 	md_free_scan_dag(mdp, &mem_list);
642 
643 	return (ra_limit);
644 }
645 
646 /*
647  * This routine sets the globals for CPU and DEV mondo queue entries and
648  * resumable and non-resumable error queue entries.
649  *
650  * First, look up the number of bits available to pass an entry number.
651  * This can vary by platform and may result in allocating an unreasonably
652  * (or impossibly) large amount of memory for the corresponding table,
653  * so we clamp it by 'max_entries'.  If the prop is missing, use
654  * 'default_entries'.
655  */
656 static uint64_t
657 get_single_q_size(md_t *mdp, mde_cookie_t cpu_node_cookie,
658     char *qnamep, uint64_t default_entries, uint64_t max_entries)
659 {
660 	uint64_t entries;
661 
662 	if (default_entries > max_entries)
663 		cmn_err(CE_CONT, "!get_single_q_size: dflt %ld > "
664 		    "max %ld for %s\n", default_entries, max_entries, qnamep);
665 
666 	if (md_get_prop_val(mdp, cpu_node_cookie, qnamep, &entries)) {
667 		if (!broken_md_flag)
668 			cmn_err(CE_PANIC, "Missing %s property in MD cpu node",
669 				qnamep);
670 		entries = default_entries;
671 	} else {
672 		entries = 1 << entries;
673 	}
674 
675 	entries = MIN(entries, max_entries);
676 
677 	return (entries);
678 }
679 
680 /* Scaling constant used to compute size of cpu mondo queue */
681 #define	CPU_MONDO_Q_MULTIPLIER	8
682 
683 static void
684 get_q_sizes(md_t *mdp, mde_cookie_t cpu_node_cookie)
685 {
686 	uint64_t max_qsize;
687 	mde_cookie_t *platlist;
688 	int nrnode;
689 
690 	/*
691 	 * Compute the maximum number of entries for the cpu mondo queue.
692 	 * Use the appropriate property in the platform node, if it is
693 	 * available.  Else, base it on NCPU.
694 	 */
695 	nrnode = md_alloc_scan_dag(mdp,
696 	    md_root_node(mdp), "platform", "fwd", &platlist);
697 
698 	ASSERT(nrnode == 1);
699 
700 	ncpu_guest_max = NCPU;
701 	(void) md_get_prop_val(mdp, platlist[0], "max-cpus", &ncpu_guest_max);
702 	max_qsize = ncpu_guest_max * CPU_MONDO_Q_MULTIPLIER;
703 
704 	md_free_scan_dag(mdp, &platlist);
705 
706 	cpu_q_entries = get_single_q_size(mdp, cpu_node_cookie,
707 	    "q-cpu-mondo-#bits", DEFAULT_CPU_Q_ENTRIES, max_qsize);
708 
709 	dev_q_entries = get_single_q_size(mdp, cpu_node_cookie,
710 	    "q-dev-mondo-#bits", DEFAULT_DEV_Q_ENTRIES, MAXIVNUM);
711 
712 	cpu_rq_entries = get_single_q_size(mdp, cpu_node_cookie,
713 	    "q-resumable-#bits", CPU_RQ_ENTRIES, MAX_CPU_RQ_ENTRIES);
714 
715 	cpu_nrq_entries = get_single_q_size(mdp, cpu_node_cookie,
716 	    "q-nonresumable-#bits", CPU_NRQ_ENTRIES, MAX_CPU_NRQ_ENTRIES);
717 }
718 
719 
720 static void
721 get_va_bits(md_t *mdp, mde_cookie_t cpu_node_cookie)
722 {
723 	uint64_t value = VA_ADDRESS_SPACE_BITS;
724 
725 	if (md_get_prop_val(mdp, cpu_node_cookie, "mmu-#va-bits", &value))
726 		cmn_err(CE_PANIC, "mmu-#va-bits property  not found in MD");
727 
728 
729 	if (value == 0 || value > VA_ADDRESS_SPACE_BITS)
730 		cmn_err(CE_PANIC, "Incorrect number of va bits in MD");
731 
732 	/* Do not expect number of VA bits to be more than 32-bit quantity */
733 
734 	va_bits = (int)value;
735 
736 	/*
737 	 * Correct the value for VA bits on UltraSPARC-T1 based systems
738 	 * in case of broken MD.
739 	 */
740 	if (broken_md_flag)
741 		va_bits = DEFAULT_VA_ADDRESS_SPACE_BITS;
742 }
743 
744 /*
745  * This routine returns the L2 cache information such as -- associativity,
746  * size and linesize.
747  */
748 static int
749 get_l2_cache_info(md_t *mdp, mde_cookie_t cpu_node_cookie,
750 	    uint64_t *associativity, uint64_t *size, uint64_t *linesize)
751 {
752 	mde_cookie_t *cachelist;
753 	int ncaches, i;
754 	uint64_t max_level;
755 
756 	ncaches = md_alloc_scan_dag(mdp, cpu_node_cookie, "cache",
757 	    "fwd", &cachelist);
758 	/*
759 	 * The "cache" node is optional in MD, therefore ncaches can be 0.
760 	 */
761 	if (ncaches < 1) {
762 		return (0);
763 	}
764 
765 	max_level = 0;
766 	for (i = 0; i < ncaches; i++) {
767 		uint64_t cache_level;
768 		uint64_t local_assoc;
769 		uint64_t local_size;
770 		uint64_t local_lsize;
771 
772 		if (md_get_prop_val(mdp, cachelist[i], "level", &cache_level))
773 			continue;
774 
775 		if (cache_level <= max_level) continue;
776 
777 		/* If properties are missing from this cache ignore it */
778 
779 		if ((md_get_prop_val(mdp, cachelist[i],
780 		    "associativity", &local_assoc))) {
781 			continue;
782 		}
783 
784 		if ((md_get_prop_val(mdp, cachelist[i],
785 		    "size", &local_size))) {
786 			continue;
787 		}
788 
789 		if ((md_get_prop_val(mdp, cachelist[i],
790 		    "line-size", &local_lsize))) {
791 			continue;
792 		}
793 
794 		max_level = cache_level;
795 		*associativity = local_assoc;
796 		*size = local_size;
797 		*linesize = local_lsize;
798 	}
799 
800 	md_free_scan_dag(mdp, &cachelist);
801 
802 	return ((max_level > 0) ? 1 : 0);
803 }
804 
805 
806 /*
807  * Set the broken_md_flag to 1 if the MD doesn't have
808  * the domaining-enabled property in the platform node and the
809  * platform uses the UltraSPARC-T1 cpu. This flag is used to
810  * workaround some of the incorrect MD properties.
811  */
812 static void
813 init_md_broken(md_t *mdp, mde_cookie_t *cpulist)
814 {
815 	int nrnode;
816 	mde_cookie_t *platlist, rootnode;
817 	uint64_t val = 0;
818 	char *namebuf;
819 	int namelen;
820 
821 	rootnode = md_root_node(mdp);
822 	ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE);
823 	ASSERT(cpulist);
824 
825 	nrnode = md_alloc_scan_dag(mdp, rootnode, "platform", "fwd",
826 	    &platlist);
827 
828 	if (nrnode < 1)
829 		cmn_err(CE_PANIC, "init_md_broken: platform node missing");
830 
831 	if (md_get_prop_data(mdp, cpulist[0],
832 	    "compatible", (uint8_t **)&namebuf, &namelen)) {
833 		cmn_err(CE_PANIC, "init_md_broken: "
834 		    "Cannot read 'compatible' property of 'cpu' node");
835 	}
836 
837 	if (md_get_prop_val(mdp, platlist[0],
838 		"domaining-enabled", &val) == -1 &&
839 	    strcmp(namebuf, "SUNW,UltraSPARC-T1") == 0)
840 		broken_md_flag = 1;
841 
842 	md_free_scan_dag(mdp, &platlist);
843 }
844