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/*
27 * The PRI plug-in picks up memory configuration data from the PRI
28 * and injects this into PICL's /platform tree.  It only populates
29 * the logical view of memory: memory, memory-segment, memory-bank.
30 * It does not populate the /device tree since there are no memory
31 * controller devices on sun4v.
32 */
33
34#include "priplugin.h"
35#include "../../common/memcfg/piclmemcfg.h"
36
37static void
38add_memory_props(picl_nodehdl_t node, mde_cookie_t memorylistp, md_t *mdp,
39	uint64_t size);
40
41static void
42add_bank_props(picl_nodehdl_t node, mde_cookie_t banklistp,
43	md_t *mdp, uint64_t *size, uint64_t *mask, unsigned int id);
44static uint64_t countbits(uint64_t v);
45
46static void
47add_segment_props(picl_nodehdl_t node, mde_cookie_t segmentlistp,
48	md_t *mdp, uint64_t interleave, uint64_t *size, uint64_t base);
49
50/*
51 * Callback function for picl_walk_tree_by_class().
52 * NOTE: picl_walk_tree_by_class() maps the return codes PICL_WALK_CONTINUE
53 * and PICL_WALK_TERMINATE to PICL_SUCCESS.
54 */
55int
56add_mem_prop(picl_nodehdl_t node, void *args)
57{
58	mde_cookie_t *memorylistp, *segmentlistp, *banklistp;
59	picl_prophdl_t memh, segmenth, bankh;
60	mde_cookie_t *buf, md_rootnode;
61	int j, k, num_nodes, interleave, err;
62	int nsegments, nbanks, nmemory;
63	uint64_t memsize, segsize, segbase;
64	uint64_t size, mask;
65	md_t *mdp = (md_t *)args;
66
67	if (mdp == NULL)
68		return (PICL_WALK_CONTINUE);
69
70	md_rootnode = md_root_node(mdp);
71
72	/*
73	 * An absence of nodes or failure to obtain memory for searches
74	 * or absence of the /memory node will cause this to fail.
75	 * Return PICL_WALK_SUCCESS to allow the plug-in to continue.
76	 */
77	num_nodes = md_node_count(mdp);
78	if (num_nodes == 0) {
79		pri_debug(LOG_NOTICE, "add_mem_prop: no nodes to walk\n");
80		return (PICL_SUCCESS);
81	}
82	buf = (mde_cookie_t *)malloc(sizeof (mde_cookie_t) * num_nodes * 3);
83	if (buf == NULL) {
84		pri_debug(LOG_NOTICE, "add_mem_prop: can't allocate memory\n");
85		return (PICL_SUCCESS);
86	}
87
88	memorylistp = &buf[0];
89	segmentlistp = &buf[num_nodes];
90	banklistp = &buf[num_nodes * 2];
91
92	if ((ptree_get_node_by_path(MEMORY_PATH, &memh)) != PICL_SUCCESS) {
93		pri_debug(LOG_NOTICE,
94		    "add_mem_prop: can't find /memory node in platform tree\n");
95		free(buf);
96		return (PICL_SUCCESS);
97	}
98
99	/*
100	 * There should be only one memory node.
101	 * If we can't find what we're looking for in the DAG then
102	 * return PICL_PROPNOTFOUND to get the caller to re-try with
103	 * a different property name.
104	 */
105	nmemory = md_scan_dag(mdp, md_rootnode, md_find_name(mdp,
106	    "memory-segments"), md_find_name(mdp, "fwd"), memorylistp);
107	if (nmemory != 1) {
108		pri_debug(LOG_NOTICE,
109		    "add_mem_prop: wrong number of memory dags: expected "
110		    "1, got %d\n", nmemory);
111		free(buf);
112		return (PICL_PROPNOTFOUND);
113	}
114
115	nsegments = md_scan_dag(mdp, memorylistp[0],
116	    md_find_name(mdp, "memory-segment"),
117	    md_find_name(mdp, "fwd"),
118	    segmentlistp);
119
120	if (nsegments == 0) {
121		pri_debug(LOG_NOTICE, "add_mem_prop: wrong number of memory "
122		    "segments: expected >0, got %d\n", nsegments);
123		free(buf);
124		return (PICL_PROPNOTFOUND);
125	}
126
127	/*
128	 * Add memory segments, keep running total of system memory.
129	 */
130	for (memsize = 0, segsize = 0, j = 0; j < nsegments;
131	    ++j, memsize += segsize) {
132		nbanks = 0;
133		err = ptree_create_and_add_node(memh,
134		    PICL_NAME_MEMORY_SEGMENT,
135		    PICL_CLASS_MEMORY_SEGMENT, &segmenth);
136		if (err == PICL_SUCCESS) {
137			size = 0;
138			mask = 0;
139
140			/*
141			 * Need to pull this out here since it's used for
142			 * the ID.
143			 */
144			if (md_get_prop_val(mdp, segmentlistp[j], "base",
145			    &segbase))
146				segbase = 0ULL;
147
148			/*
149			 * Add banks under each segment.
150			 */
151			nbanks = md_scan_dag(mdp, segmentlistp[j],
152			    md_find_name(mdp, "memory-bank"),
153			    md_find_name(mdp, "fwd"),
154			    banklistp);
155
156			if (nbanks <= 0) {
157				pri_debug(LOG_NOTICE, "add_mem_prop: no banks "
158				    "found for segment %d\n", j);
159			} else {
160				for (k = 0; k < nbanks; ++k) {
161					err =
162					    ptree_create_and_add_node(segmenth,
163					    PICL_NAME_MEMORY_BANK,
164					    PICL_CLASS_MEMORY_BANK, &bankh);
165					if (err == PICL_SUCCESS) {
166						/*
167						 * Add AddressMatch,
168						 * AddressMask, Size, and
169						 * ID to each bank.
170						 */
171						add_bank_props(bankh,
172						    banklistp[k],
173						    mdp,
174						    &size, &mask,
175						    (segbase >> 32) * j + k);
176					}
177				}
178			}
179		}
180
181		/*
182		 * Add Interleave, BaseAddress, and Size to each segment.
183		 */
184		interleave = 2 << (countbits(mask & (size - 1)) - 1);
185		add_segment_props(segmenth, segmentlistp[j],
186		    mdp, interleave, &segsize, segbase);
187	}
188
189	/*
190	 * Add TransferSize and Size (total memory) to this node.
191	 */
192	add_memory_props(memh, memorylistp[0], mdp, memsize);
193
194	free(buf);
195	return (PICL_WALK_CONTINUE);
196}
197
198static void
199add_bank_props(picl_nodehdl_t bankh, mde_cookie_t banklistp,
200	md_t *mdp, uint64_t *size, uint64_t *mask, unsigned int id)
201{
202	uint64_t int_value;
203	mde_cookie_t *dimmlistp;
204	int node_count, i, type_size, nac_size, status;
205	uint8_t *type;
206	char *pc, *nac;
207	picl_prophdl_t dimmh;
208
209	*size = 0ULL;
210	*mask = 0ULL;
211
212	node_count = md_node_count(mdp);
213	dimmlistp = (mde_cookie_t *)malloc(node_count * sizeof (mde_cookie_t));
214	if (dimmlistp == NULL) {
215		pri_debug(LOG_NOTICE,
216		    "add_bank_props: can't allocate memory\n");
217		return;
218	}
219
220	if (!md_get_prop_val(mdp, banklistp, "size", &int_value)) {
221		add_md_prop(bankh, sizeof (int_value), PICL_PROP_SIZE,
222		    &int_value, PICL_PTYPE_UNSIGNED_INT);
223		*size = int_value;
224	}
225	if (!md_get_prop_val(mdp, banklistp, "mask",
226	    &int_value)) {
227		add_md_prop(bankh, sizeof (int_value),
228		    PICL_PROP_ADDRESSMASK,
229		    &int_value, PICL_PTYPE_UNSIGNED_INT);
230		*mask = int_value;
231	}
232	if (!md_get_prop_val(mdp, banklistp, "match",
233	    &int_value)) {
234		add_md_prop(bankh, sizeof (int_value),
235		    PICL_PROP_ADDRESSMATCH,
236		    &int_value, PICL_PTYPE_UNSIGNED_INT);
237	}
238
239	add_md_prop(bankh, sizeof (id), PICL_PROP_ID, &id,
240	    PICL_PTYPE_INT);
241
242	node_count = md_scan_dag(mdp, banklistp, md_find_name(mdp, "component"),
243	    md_find_name(mdp, "fwd"), dimmlistp);
244
245	for (i = 0; i < node_count; ++i) {
246		status = md_get_prop_str(mdp, dimmlistp[i], "type",
247		    (char **)&type);
248		if (status == -1) {
249			status = md_get_prop_data(mdp, dimmlistp[i],
250			    "type", &type, &type_size);
251		}
252		if (status == -1) /* can't get node type - just skip */
253			continue;
254		if (strcmp((const char *)type, "dimm") == 0) {
255			if (md_get_prop_str(mdp, dimmlistp[i], "nac",
256			    (char **)&nac) == 0) {
257				nac_size = strlen(nac) + 1;
258				if (ptree_create_and_add_node(bankh,
259				    PICL_NAME_MEMORY_MODULE,
260				    PICL_CLASS_MEMORY_MODULE, &dimmh) ==
261				    PICL_SUCCESS) {
262					add_md_prop(dimmh, nac_size,
263					    "nac", nac,
264					    PICL_PTYPE_CHARSTRING);
265					if ((pc = strrchr(nac, '/')) != NULL)
266						nac = ++pc;
267					nac_size = strlen(nac) + 1;
268					add_md_prop(dimmh, nac_size,
269					    PICL_PROP_LABEL, nac,
270					    PICL_PTYPE_CHARSTRING);
271				}
272			}
273		}
274	}
275	free(dimmlistp);
276}
277
278static uint64_t
279countbits(uint64_t v)
280{
281	uint64_t c;	/* c accumulates the total bits set in v */
282
283	for (c = 0; v; c++)
284		v &= v - 1;	/* clear the least significant bit set */
285	return (c);
286}
287
288static void
289add_segment_props(picl_nodehdl_t node, mde_cookie_t segmentlistp,
290    md_t *mdp, uint64_t interleave, uint64_t *size, uint64_t base)
291{
292	uint64_t int_value;
293
294	*size = 0;
295	if (!md_get_prop_val(mdp, segmentlistp, "size", &int_value)) {
296		add_md_prop(node, sizeof (int_value),
297		    PICL_PROP_SIZE, &int_value,
298		    PICL_PTYPE_UNSIGNED_INT);
299		*size = int_value;
300	}
301	add_md_prop(node, sizeof (base), PICL_PROP_BASEADDRESS,
302	    &base, PICL_PTYPE_UNSIGNED_INT);
303
304	add_md_prop(node, sizeof (interleave), PICL_PROP_INTERLEAVE_FACTOR,
305	    &interleave, PICL_PTYPE_UNSIGNED_INT);
306}
307
308static void
309add_memory_props(picl_nodehdl_t node, mde_cookie_t memorylistp, md_t *mdp,
310	uint64_t size)
311{
312	uint64_t int_value;
313
314	/*
315	 * If the top-level node has a size property then use that,
316	 * otherwise use the size that was calculated by the caller
317	 * and passed in.
318	 */
319	if (md_get_prop_val(mdp, memorylistp, "size", &int_value))
320		int_value = size;
321	add_md_prop(node, sizeof (int_value), PICL_PROP_SIZE, &int_value,
322	    PICL_PTYPE_UNSIGNED_INT);
323	if (!md_get_prop_val(mdp, memorylistp, "transfer_size",
324	    &int_value)) {
325		add_md_prop(node, sizeof (int_value),
326		    PICL_PROP_TRANSFER_SIZE,
327		    &int_value, PICL_PTYPE_UNSIGNED_INT);
328	}
329}
330