10d63ce2bSvenki /*
20d63ce2bSvenki  * CDDL HEADER START
30d63ce2bSvenki  *
40d63ce2bSvenki  * The contents of this file are subject to the terms of the
50d63ce2bSvenki  * Common Development and Distribution License (the "License").
60d63ce2bSvenki  * You may not use this file except in compliance with the License.
70d63ce2bSvenki  *
80d63ce2bSvenki  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90d63ce2bSvenki  * or http://www.opensolaris.org/os/licensing.
100d63ce2bSvenki  * See the License for the specific language governing permissions
110d63ce2bSvenki  * and limitations under the License.
120d63ce2bSvenki  *
130d63ce2bSvenki  * When distributing Covered Code, include this CDDL HEADER in each
140d63ce2bSvenki  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150d63ce2bSvenki  * If applicable, add the following below this CDDL HEADER, with the
160d63ce2bSvenki  * fields enclosed by brackets "[]" replaced with your own identifying
170d63ce2bSvenki  * information: Portions Copyright [yyyy] [name of copyright owner]
180d63ce2bSvenki  *
190d63ce2bSvenki  * CDDL HEADER END
200d63ce2bSvenki  */
210d63ce2bSvenki /*
22*2ea390f3SMichael Bergknoff  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230d63ce2bSvenki  * Use is subject to license terms.
240d63ce2bSvenki  */
250d63ce2bSvenki 
260d63ce2bSvenki /*
270d63ce2bSvenki  * The PRI plug-in picks up memory configuration data from the PRI
280d63ce2bSvenki  * and injects this into PICL's /platform tree.  It only populates
290d63ce2bSvenki  * the logical view of memory: memory, memory-segment, memory-bank.
300d63ce2bSvenki  * It does not populate the /device tree since there are no memory
310d63ce2bSvenki  * controller devices on sun4v.
320d63ce2bSvenki  */
330d63ce2bSvenki 
340d63ce2bSvenki #include "priplugin.h"
350d63ce2bSvenki #include "../../common/memcfg/piclmemcfg.h"
360d63ce2bSvenki 
370d63ce2bSvenki static void
38a90d965dSfw add_memory_props(picl_nodehdl_t node, mde_cookie_t memorylistp, md_t *mdp,
39a90d965dSfw 	uint64_t size);
400d63ce2bSvenki 
410d63ce2bSvenki static void
420d63ce2bSvenki add_bank_props(picl_nodehdl_t node, mde_cookie_t banklistp,
43a90d965dSfw 	md_t *mdp, uint64_t *size, uint64_t *mask, unsigned int id);
440d63ce2bSvenki static uint64_t countbits(uint64_t v);
450d63ce2bSvenki 
460d63ce2bSvenki static void
470d63ce2bSvenki add_segment_props(picl_nodehdl_t node, mde_cookie_t segmentlistp,
48a90d965dSfw 	md_t *mdp, uint64_t interleave, uint64_t *size, uint64_t base);
490d63ce2bSvenki 
500d63ce2bSvenki /*
510d63ce2bSvenki  * Callback function for picl_walk_tree_by_class().
520d63ce2bSvenki  * NOTE: picl_walk_tree_by_class() maps the return codes PICL_WALK_CONTINUE
530d63ce2bSvenki  * and PICL_WALK_TERMINATE to PICL_SUCCESS.
540d63ce2bSvenki  */
550d63ce2bSvenki int
add_mem_prop(picl_nodehdl_t node,void * args)560d63ce2bSvenki add_mem_prop(picl_nodehdl_t node, void *args)
570d63ce2bSvenki {
580d63ce2bSvenki 	mde_cookie_t *memorylistp, *segmentlistp, *banklistp;
590d63ce2bSvenki 	picl_prophdl_t memh, segmenth, bankh;
60d2b9c676Sfw 	mde_cookie_t *buf, md_rootnode;
610d63ce2bSvenki 	int j, k, num_nodes, interleave, err;
620d63ce2bSvenki 	int nsegments, nbanks, nmemory;
630d63ce2bSvenki 	uint64_t memsize, segsize, segbase;
640d63ce2bSvenki 	uint64_t size, mask;
65a90d965dSfw 	md_t *mdp = (md_t *)args;
66a90d965dSfw 
67a90d965dSfw 	if (mdp == NULL)
68a90d965dSfw 		return (PICL_WALK_CONTINUE);
690d63ce2bSvenki 
70d2b9c676Sfw 	md_rootnode = md_root_node(mdp);
71d2b9c676Sfw 
720d63ce2bSvenki 	/*
730d63ce2bSvenki 	 * An absence of nodes or failure to obtain memory for searches
740d63ce2bSvenki 	 * or absence of the /memory node will cause this to fail.
750d63ce2bSvenki 	 * Return PICL_WALK_SUCCESS to allow the plug-in to continue.
760d63ce2bSvenki 	 */
770d63ce2bSvenki 	num_nodes = md_node_count(mdp);
780d63ce2bSvenki 	if (num_nodes == 0) {
790d63ce2bSvenki 		pri_debug(LOG_NOTICE, "add_mem_prop: no nodes to walk\n");
800d63ce2bSvenki 		return (PICL_SUCCESS);
810d63ce2bSvenki 	}
820d63ce2bSvenki 	buf = (mde_cookie_t *)malloc(sizeof (mde_cookie_t) * num_nodes * 3);
830d63ce2bSvenki 	if (buf == NULL) {
840d63ce2bSvenki 		pri_debug(LOG_NOTICE, "add_mem_prop: can't allocate memory\n");
850d63ce2bSvenki 		return (PICL_SUCCESS);
860d63ce2bSvenki 	}
870d63ce2bSvenki 
880d63ce2bSvenki 	memorylistp = &buf[0];
890d63ce2bSvenki 	segmentlistp = &buf[num_nodes];
900d63ce2bSvenki 	banklistp = &buf[num_nodes * 2];
910d63ce2bSvenki 
920d63ce2bSvenki 	if ((ptree_get_node_by_path(MEMORY_PATH, &memh)) != PICL_SUCCESS) {
930d63ce2bSvenki 		pri_debug(LOG_NOTICE,
940d63ce2bSvenki 		    "add_mem_prop: can't find /memory node in platform tree\n");
950d63ce2bSvenki 		free(buf);
960d63ce2bSvenki 		return (PICL_SUCCESS);
970d63ce2bSvenki 	}
980d63ce2bSvenki 
990d63ce2bSvenki 	/*
1000d63ce2bSvenki 	 * There should be only one memory node.
1010d63ce2bSvenki 	 * If we can't find what we're looking for in the DAG then
1020d63ce2bSvenki 	 * return PICL_PROPNOTFOUND to get the caller to re-try with
1030d63ce2bSvenki 	 * a different property name.
1040d63ce2bSvenki 	 */
105d2b9c676Sfw 	nmemory = md_scan_dag(mdp, md_rootnode, md_find_name(mdp,
106a90d965dSfw 	    "memory-segments"), md_find_name(mdp, "fwd"), memorylistp);
1070d63ce2bSvenki 	if (nmemory != 1) {
1080d63ce2bSvenki 		pri_debug(LOG_NOTICE,
1090d63ce2bSvenki 		    "add_mem_prop: wrong number of memory dags: expected "
1100d63ce2bSvenki 		    "1, got %d\n", nmemory);
1110d63ce2bSvenki 		free(buf);
1120d63ce2bSvenki 		return (PICL_PROPNOTFOUND);
1130d63ce2bSvenki 	}
1140d63ce2bSvenki 
1150d63ce2bSvenki 	nsegments = md_scan_dag(mdp, memorylistp[0],
1160d63ce2bSvenki 	    md_find_name(mdp, "memory-segment"),
1170d63ce2bSvenki 	    md_find_name(mdp, "fwd"),
1180d63ce2bSvenki 	    segmentlistp);
1190d63ce2bSvenki 
1200d63ce2bSvenki 	if (nsegments == 0) {
1210d63ce2bSvenki 		pri_debug(LOG_NOTICE, "add_mem_prop: wrong number of memory "
1220d63ce2bSvenki 		    "segments: expected >0, got %d\n", nsegments);
1230d63ce2bSvenki 		free(buf);
1240d63ce2bSvenki 		return (PICL_PROPNOTFOUND);
1250d63ce2bSvenki 	}
1260d63ce2bSvenki 
1270d63ce2bSvenki 	/*
1280d63ce2bSvenki 	 * Add memory segments, keep running total of system memory.
1290d63ce2bSvenki 	 */
1300d63ce2bSvenki 	for (memsize = 0, segsize = 0, j = 0; j < nsegments;
1310d63ce2bSvenki 	    ++j, memsize += segsize) {
1320d63ce2bSvenki 		nbanks = 0;
1330d63ce2bSvenki 		err = ptree_create_and_add_node(memh,
1340d63ce2bSvenki 		    PICL_NAME_MEMORY_SEGMENT,
1350d63ce2bSvenki 		    PICL_CLASS_MEMORY_SEGMENT, &segmenth);
1360d63ce2bSvenki 		if (err == PICL_SUCCESS) {
1370d63ce2bSvenki 			size = 0;
1380d63ce2bSvenki 			mask = 0;
1390d63ce2bSvenki 
1400d63ce2bSvenki 			/*
1410d63ce2bSvenki 			 * Need to pull this out here since it's used for
1420d63ce2bSvenki 			 * the ID.
1430d63ce2bSvenki 			 */
1440d63ce2bSvenki 			if (md_get_prop_val(mdp, segmentlistp[j], "base",
1450d63ce2bSvenki 			    &segbase))
1460d63ce2bSvenki 				segbase = 0ULL;
1470d63ce2bSvenki 
1480d63ce2bSvenki 			/*
1490d63ce2bSvenki 			 * Add banks under each segment.
1500d63ce2bSvenki 			 */
1510d63ce2bSvenki 			nbanks = md_scan_dag(mdp, segmentlistp[j],
1520d63ce2bSvenki 			    md_find_name(mdp, "memory-bank"),
1530d63ce2bSvenki 			    md_find_name(mdp, "fwd"),
1540d63ce2bSvenki 			    banklistp);
1550d63ce2bSvenki 
1560d63ce2bSvenki 			if (nbanks <= 0) {
1570d63ce2bSvenki 				pri_debug(LOG_NOTICE, "add_mem_prop: no banks "
1580d63ce2bSvenki 				    "found for segment %d\n", j);
1590d63ce2bSvenki 			} else {
1600d63ce2bSvenki 				for (k = 0; k < nbanks; ++k) {
1610d63ce2bSvenki 					err =
1620d63ce2bSvenki 					    ptree_create_and_add_node(segmenth,
1630d63ce2bSvenki 					    PICL_NAME_MEMORY_BANK,
1640d63ce2bSvenki 					    PICL_CLASS_MEMORY_BANK, &bankh);
1650d63ce2bSvenki 					if (err == PICL_SUCCESS) {
1660d63ce2bSvenki 						/*
1670d63ce2bSvenki 						 * Add AddressMatch,
1680d63ce2bSvenki 						 * AddressMask, Size, and
1690d63ce2bSvenki 						 * ID to each bank.
1700d63ce2bSvenki 						 */
1710d63ce2bSvenki 						add_bank_props(bankh,
1720d63ce2bSvenki 						    banklistp[k],
173a90d965dSfw 						    mdp,
1740d63ce2bSvenki 						    &size, &mask,
1750d63ce2bSvenki 						    (segbase >> 32) * j + k);
1760d63ce2bSvenki 					}
1770d63ce2bSvenki 				}
1780d63ce2bSvenki 			}
1790d63ce2bSvenki 		}
1800d63ce2bSvenki 
1810d63ce2bSvenki 		/*
1820d63ce2bSvenki 		 * Add Interleave, BaseAddress, and Size to each segment.
1830d63ce2bSvenki 		 */
1840d63ce2bSvenki 		interleave = 2 << (countbits(mask & (size - 1)) - 1);
1850d63ce2bSvenki 		add_segment_props(segmenth, segmentlistp[j],
186a90d965dSfw 		    mdp, interleave, &segsize, segbase);
1870d63ce2bSvenki 	}
1880d63ce2bSvenki 
1890d63ce2bSvenki 	/*
1900d63ce2bSvenki 	 * Add TransferSize and Size (total memory) to this node.
1910d63ce2bSvenki 	 */
192a90d965dSfw 	add_memory_props(memh, memorylistp[0], mdp, memsize);
1930d63ce2bSvenki 
1940d63ce2bSvenki 	free(buf);
1950d63ce2bSvenki 	return (PICL_WALK_CONTINUE);
1960d63ce2bSvenki }
1970d63ce2bSvenki 
1980d63ce2bSvenki static void
add_bank_props(picl_nodehdl_t bankh,mde_cookie_t banklistp,md_t * mdp,uint64_t * size,uint64_t * mask,unsigned int id)199a90d965dSfw add_bank_props(picl_nodehdl_t bankh, mde_cookie_t banklistp,
200a90d965dSfw 	md_t *mdp, uint64_t *size, uint64_t *mask, unsigned int id)
2010d63ce2bSvenki {
2020d63ce2bSvenki 	uint64_t int_value;
2030d63ce2bSvenki 	mde_cookie_t *dimmlistp;
2040d63ce2bSvenki 	int node_count, i, type_size, nac_size, status;
2050d63ce2bSvenki 	uint8_t *type;
2060d63ce2bSvenki 	char *pc, *nac;
207a90d965dSfw 	picl_prophdl_t dimmh;
2080d63ce2bSvenki 
2090d63ce2bSvenki 	*size = 0ULL;
2100d63ce2bSvenki 	*mask = 0ULL;
2110d63ce2bSvenki 
2120d63ce2bSvenki 	node_count = md_node_count(mdp);
2130d63ce2bSvenki 	dimmlistp = (mde_cookie_t *)malloc(node_count * sizeof (mde_cookie_t));
214*2ea390f3SMichael Bergknoff 	if (dimmlistp == NULL) {
215*2ea390f3SMichael Bergknoff 		pri_debug(LOG_NOTICE,
216*2ea390f3SMichael Bergknoff 		    "add_bank_props: can't allocate memory\n");
217*2ea390f3SMichael Bergknoff 		return;
218*2ea390f3SMichael Bergknoff 	}
2190d63ce2bSvenki 
2200d63ce2bSvenki 	if (!md_get_prop_val(mdp, banklistp, "size", &int_value)) {
221a90d965dSfw 		add_md_prop(bankh, sizeof (int_value), PICL_PROP_SIZE,
2220d63ce2bSvenki 		    &int_value, PICL_PTYPE_UNSIGNED_INT);
2230d63ce2bSvenki 		*size = int_value;
2240d63ce2bSvenki 	}
2250d63ce2bSvenki 	if (!md_get_prop_val(mdp, banklistp, "mask",
2260d63ce2bSvenki 	    &int_value)) {
227a90d965dSfw 		add_md_prop(bankh, sizeof (int_value),
2280d63ce2bSvenki 		    PICL_PROP_ADDRESSMASK,
2290d63ce2bSvenki 		    &int_value, PICL_PTYPE_UNSIGNED_INT);
2300d63ce2bSvenki 		*mask = int_value;
2310d63ce2bSvenki 	}
2320d63ce2bSvenki 	if (!md_get_prop_val(mdp, banklistp, "match",
2330d63ce2bSvenki 	    &int_value)) {
234a90d965dSfw 		add_md_prop(bankh, sizeof (int_value),
2350d63ce2bSvenki 		    PICL_PROP_ADDRESSMATCH,
2360d63ce2bSvenki 		    &int_value, PICL_PTYPE_UNSIGNED_INT);
2370d63ce2bSvenki 	}
2380d63ce2bSvenki 
239a90d965dSfw 	add_md_prop(bankh, sizeof (id), PICL_PROP_ID, &id,
2400d63ce2bSvenki 	    PICL_PTYPE_INT);
2410d63ce2bSvenki 
2420d63ce2bSvenki 	node_count = md_scan_dag(mdp, banklistp, md_find_name(mdp, "component"),
2430d63ce2bSvenki 	    md_find_name(mdp, "fwd"), dimmlistp);
2440d63ce2bSvenki 
2450d63ce2bSvenki 	for (i = 0; i < node_count; ++i) {
246a90d965dSfw 		status = md_get_prop_str(mdp, dimmlistp[i], "type",
247a90d965dSfw 		    (char **)&type);
248a90d965dSfw 		if (status == -1) {
2490d63ce2bSvenki 			status = md_get_prop_data(mdp, dimmlistp[i],
2500d63ce2bSvenki 			    "type", &type, &type_size);
2510d63ce2bSvenki 		}
252a90d965dSfw 		if (status == -1) /* can't get node type - just skip */
253a90d965dSfw 			continue;
254a90d965dSfw 		if (strcmp((const char *)type, "dimm") == 0) {
255a90d965dSfw 			if (md_get_prop_str(mdp, dimmlistp[i], "nac",
256a90d965dSfw 			    (char **)&nac) == 0) {
257a90d965dSfw 				nac_size = strlen(nac) + 1;
258a90d965dSfw 				if (ptree_create_and_add_node(bankh,
259a90d965dSfw 				    PICL_NAME_MEMORY_MODULE,
260a90d965dSfw 				    PICL_CLASS_MEMORY_MODULE, &dimmh) ==
261a90d965dSfw 				    PICL_SUCCESS) {
262a90d965dSfw 					add_md_prop(dimmh, nac_size,
2630d63ce2bSvenki 					    "nac", nac,
2640d63ce2bSvenki 					    PICL_PTYPE_CHARSTRING);
2650d63ce2bSvenki 					if ((pc = strrchr(nac, '/')) != NULL)
2660d63ce2bSvenki 						nac = ++pc;
2670d63ce2bSvenki 					nac_size = strlen(nac) + 1;
268a90d965dSfw 					add_md_prop(dimmh, nac_size,
2690d63ce2bSvenki 					    PICL_PROP_LABEL, nac,
2700d63ce2bSvenki 					    PICL_PTYPE_CHARSTRING);
2710d63ce2bSvenki 				}
2720d63ce2bSvenki 			}
2730d63ce2bSvenki 		}
2740d63ce2bSvenki 	}
275*2ea390f3SMichael Bergknoff 	free(dimmlistp);
2760d63ce2bSvenki }
2770d63ce2bSvenki 
2780d63ce2bSvenki static uint64_t
countbits(uint64_t v)2790d63ce2bSvenki countbits(uint64_t v)
2800d63ce2bSvenki {
2810d63ce2bSvenki 	uint64_t c;	/* c accumulates the total bits set in v */
2820d63ce2bSvenki 
2830d63ce2bSvenki 	for (c = 0; v; c++)
2840d63ce2bSvenki 		v &= v - 1;	/* clear the least significant bit set */
2850d63ce2bSvenki 	return (c);
2860d63ce2bSvenki }
2870d63ce2bSvenki 
2880d63ce2bSvenki static void
add_segment_props(picl_nodehdl_t node,mde_cookie_t segmentlistp,md_t * mdp,uint64_t interleave,uint64_t * size,uint64_t base)2890d63ce2bSvenki add_segment_props(picl_nodehdl_t node, mde_cookie_t segmentlistp,
290a90d965dSfw     md_t *mdp, uint64_t interleave, uint64_t *size, uint64_t base)
2910d63ce2bSvenki {
2920d63ce2bSvenki 	uint64_t int_value;
2930d63ce2bSvenki 
2940d63ce2bSvenki 	*size = 0;
2950d63ce2bSvenki 	if (!md_get_prop_val(mdp, segmentlistp, "size", &int_value)) {
2960d63ce2bSvenki 		add_md_prop(node, sizeof (int_value),
2970d63ce2bSvenki 		    PICL_PROP_SIZE, &int_value,
2980d63ce2bSvenki 		    PICL_PTYPE_UNSIGNED_INT);
2990d63ce2bSvenki 		*size = int_value;
3000d63ce2bSvenki 	}
3010d63ce2bSvenki 	add_md_prop(node, sizeof (base), PICL_PROP_BASEADDRESS,
302a90d965dSfw 	    &base, PICL_PTYPE_UNSIGNED_INT);
3030d63ce2bSvenki 
3040d63ce2bSvenki 	add_md_prop(node, sizeof (interleave), PICL_PROP_INTERLEAVE_FACTOR,
305a90d965dSfw 	    &interleave, PICL_PTYPE_UNSIGNED_INT);
3060d63ce2bSvenki }
3070d63ce2bSvenki 
3080d63ce2bSvenki static void
add_memory_props(picl_nodehdl_t node,mde_cookie_t memorylistp,md_t * mdp,uint64_t size)309a90d965dSfw add_memory_props(picl_nodehdl_t node, mde_cookie_t memorylistp, md_t *mdp,
310a90d965dSfw 	uint64_t size)
3110d63ce2bSvenki {
3120d63ce2bSvenki 	uint64_t int_value;
3130d63ce2bSvenki 
3140d63ce2bSvenki 	/*
3150d63ce2bSvenki 	 * If the top-level node has a size property then use that,
3160d63ce2bSvenki 	 * otherwise use the size that was calculated by the caller
3170d63ce2bSvenki 	 * and passed in.
3180d63ce2bSvenki 	 */
3190d63ce2bSvenki 	if (md_get_prop_val(mdp, memorylistp, "size", &int_value))
3200d63ce2bSvenki 		int_value = size;
3210d63ce2bSvenki 	add_md_prop(node, sizeof (int_value), PICL_PROP_SIZE, &int_value,
322a90d965dSfw 	    PICL_PTYPE_UNSIGNED_INT);
3230d63ce2bSvenki 	if (!md_get_prop_val(mdp, memorylistp, "transfer_size",
3240d63ce2bSvenki 	    &int_value)) {
3250d63ce2bSvenki 		add_md_prop(node, sizeof (int_value),
3260d63ce2bSvenki 		    PICL_PROP_TRANSFER_SIZE,
3270d63ce2bSvenki 		    &int_value, PICL_PTYPE_UNSIGNED_INT);
3280d63ce2bSvenki 	}
3290d63ce2bSvenki }
330