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 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <string.h>
28 #include <umem.h>
29 #include <sys/mdesc.h>
30 #include <sys/fm/ldom.h>
31 
32 #include <mem_mdesc.h>
33 
34 void *
mem_alloc(size_t size)35 mem_alloc(size_t size)
36 {
37 	return (umem_alloc(size, UMEM_DEFAULT));
38 }
39 
40 void
mem_free(void * data,size_t size)41 mem_free(void *data, size_t size)
42 {
43 	umem_free(data, size);
44 }
45 
46 #define	MEM_BYTES_PER_CACHELINE	64
47 
48 static void
mdesc_init_n1(topo_mod_t * mod,md_t * mdp,mde_cookie_t * listp,md_mem_info_t * mem)49 mdesc_init_n1(topo_mod_t *mod, md_t *mdp, mde_cookie_t *listp,
50     md_mem_info_t *mem)
51 {
52 	int idx, mdesc_dimm_count;
53 	mem_dimm_map_t *dm, *d;
54 	uint64_t sysmem_size, i;
55 	int dimms, min_chan, max_chan, min_rank, max_rank;
56 	int chan, rank, dimm, chans, chan_step;
57 	uint64_t mask, chan_mask, chan_value;
58 	uint64_t rank_mask, rank_value;
59 	char *unum, *serial, *part;
60 	mem_seg_map_t *seg;
61 	mem_bank_map_t *bm;
62 	mem_dimm_list_t *dlp;
63 	mem_grp_t *mg;
64 	char s[20];
65 
66 	mdesc_dimm_count = md_scan_dag(mdp,
67 	    MDE_INVAL_ELEM_COOKIE, md_find_name(mdp, "dimm_data"),
68 	    md_find_name(mdp, "fwd"), listp);
69 
70 	for (idx = 0; idx < mdesc_dimm_count; idx++) {
71 
72 		if (md_get_prop_str(mdp, listp[idx], "nac", &unum) < 0)
73 			unum = "";
74 		if (md_get_prop_str(mdp, listp[idx], "serial#",
75 		    &serial) < 0)
76 			serial = "";
77 		if (md_get_prop_str(mdp, listp[idx], "part#",
78 		    &part) < 0)
79 			part = "";
80 
81 		dm = topo_mod_alloc(mod, sizeof (mem_dimm_map_t));
82 		dm->dm_label = topo_mod_strdup(mod, unum);
83 		dm->dm_serid = topo_mod_strdup(mod, serial);
84 		dm->dm_part = topo_mod_strdup(mod, part);
85 
86 		dm->dm_next = mem->mem_dm;
87 		mem->mem_dm = dm;
88 	}
89 
90 	/* N1 (MD) specific segment initialization */
91 
92 	dimms = 0;
93 	min_chan = 99;
94 	max_chan = -1;
95 	min_rank = 99;
96 	max_rank = -1;
97 
98 	for (d = mem->mem_dm; d != NULL; d = d->dm_next) {
99 		if (sscanf(d->dm_label, "MB/CMP0/CH%d/R%d/D%d",
100 		    &chan, &rank, &dimm) != 3) /* didn't scan all 3 values */
101 			return;
102 		min_chan = MIN(min_chan, chan);
103 		max_chan = MAX(max_chan, chan);
104 		min_rank = MIN(min_rank, rank);
105 		max_rank = MAX(max_rank, rank);
106 		dimms++;
107 	}
108 
109 	mdesc_dimm_count = md_scan_dag(mdp,
110 	    MDE_INVAL_ELEM_COOKIE,
111 	    md_find_name(mdp, "mblock"),
112 	    md_find_name(mdp, "fwd"),
113 	    listp);
114 	sysmem_size = 0;
115 	for (idx = 0; idx < mdesc_dimm_count; idx++) {
116 		uint64_t size = 0;
117 		if (md_get_prop_val(mdp, listp[idx], "size", &size) == 0)
118 			sysmem_size += size;
119 	}
120 
121 	for (i = 1 << 30; i < sysmem_size; i = i << 1)
122 		;
123 	if (max_rank > min_rank) {
124 		chans = dimms/4;
125 		rank_mask = i >> 1;
126 	} else {
127 		chans = dimms/2;
128 		rank_mask = 0;
129 	}
130 
131 	chan_mask = (uint64_t)((chans - 1) * MEM_BYTES_PER_CACHELINE);
132 	mask = rank_mask | chan_mask;
133 
134 	if (chans > 2)
135 		chan_step = 1;
136 	else
137 		chan_step = max_chan - min_chan;
138 
139 	seg = topo_mod_zalloc(mod, sizeof (mem_seg_map_t));
140 	seg->sm_next = mem->mem_seg;
141 	mem->mem_seg = seg;
142 	seg->sm_base = 0;
143 	seg->sm_size = sysmem_size;
144 
145 	mg = topo_mod_zalloc(mod, sizeof (mem_grp_t));
146 	seg->sm_grp = mg;
147 	mem->mem_group = mg;
148 
149 	for (rank = min_rank, rank_value = 0;
150 	    rank <= max_rank;
151 	    rank++, rank_value += rank_mask) {
152 		for (chan = min_chan, chan_value = 0;
153 		    chan <= max_chan;
154 		    chan += chan_step,
155 		    chan_value += MEM_BYTES_PER_CACHELINE) {
156 			bm = topo_mod_zalloc(mod, sizeof (mem_bank_map_t));
157 			bm->bm_mask = mask;
158 			bm->bm_match = chan_value | rank_value;
159 			bm->bm_shift = 1;
160 			bm->bm_grp = mg->mg_bank;
161 			mg->mg_bank = bm;
162 			bm->bm_next = mem->mem_bank;
163 			mem->mem_bank = bm;
164 			(void) sprintf(s, "MB/CMP0/CH%1d/R%1d", chan, rank);
165 			idx = 0;
166 			for (d = mem->mem_dm; d != NULL; d = d->dm_next) {
167 				if (strncmp(s, d->dm_label, strlen(s)) == 0) {
168 					dlp = topo_mod_zalloc(mod,
169 					    sizeof (mem_dimm_list_t));
170 					dlp->dl_next = bm->bm_dlist;
171 					bm->bm_dlist = dlp;
172 					dlp->dl_dimm = d;
173 				}
174 			}
175 		}
176 	}
177 }
178 
179 uint16_t
mem_log2(uint64_t v)180 mem_log2(uint64_t v)
181 {
182 	uint16_t i;
183 	for (i = 0; v > 1; i++) {
184 		v = v >> 1;
185 	}
186 	return (i);
187 }
188 
189 mem_dimm_map_t *
mem_get_dimm_by_sn(char * sn,md_mem_info_t * mem)190 mem_get_dimm_by_sn(char *sn, md_mem_info_t *mem)
191 {
192 	mem_dimm_map_t *dp;
193 
194 	for (dp = mem->mem_dm; dp != NULL; dp = dp->dm_next) {
195 		if (strcmp(sn, dp->dm_serid) == 0)
196 			return (dp);
197 	}
198 	return (NULL);
199 }
200 
201 mem_grp_t *
find_grp(mde_cookie_t * listp,size_t n,mde_cookie_t * bclist,mem_bank_map_t ** banklist,size_t mem_bank_count,md_mem_info_t * mem)202 find_grp(mde_cookie_t *listp, size_t n, mde_cookie_t *bclist,
203     mem_bank_map_t **banklist, size_t mem_bank_count, md_mem_info_t *mem)
204 {
205 	mem_grp_t *mg;
206 	mem_bank_map_t *bp;
207 	size_t i, j;
208 	int err;
209 
210 	err = 0;
211 	for (mg = mem->mem_group; mg != NULL; mg = mg->mg_next) {
212 		if (mg->mg_size == n) {
213 			err = 0;
214 			for (i = 0, bp = mg->mg_bank;
215 			    i < n && bp != NULL;
216 			    i++, bp = bp->bm_grp) {
217 				for (j = 0; j < mem_bank_count; j++) {
218 					if (listp[i] == *(bclist+j) &&
219 					    bp == *(banklist+j))
220 						break;
221 				}
222 				if (bp == NULL) err++;
223 			}
224 		} else {
225 			err++;
226 		}
227 		if (err == 0)
228 			return (mg);
229 	}
230 	return (NULL);
231 }
232 
233 mem_grp_t *
create_grp(topo_mod_t * mod,mde_cookie_t * listp,size_t n,mde_cookie_t * bclist,mem_bank_map_t ** banklist,size_t mem_bank_count,md_mem_info_t * mem)234 create_grp(topo_mod_t *mod, mde_cookie_t *listp, size_t n, mde_cookie_t *bclist,
235     mem_bank_map_t **banklist, size_t mem_bank_count, md_mem_info_t *mem)
236 {
237 	mem_grp_t *mg;
238 	size_t i, j;
239 
240 	mg = topo_mod_zalloc(mod, sizeof (mem_grp_t));
241 	mg->mg_size = n;
242 	mg->mg_next = mem->mem_group;
243 	mem->mem_group = mg;
244 
245 	for (i = 0; i < n; i++) {
246 		for (j = 0; j < mem_bank_count; j++) {
247 			if (listp[i] == *(bclist+j)) {
248 				(*(banklist+j))->bm_grp = mg->mg_bank;
249 				mg->mg_bank = *(banklist+j);
250 			}
251 		}
252 	}
253 	return (mg);
254 }
255 
256 static void
mdesc_init_n2(topo_mod_t * mod,md_t * mdp,mde_cookie_t * listp,md_mem_info_t * mem,int num_comps)257 mdesc_init_n2(topo_mod_t *mod, md_t *mdp, mde_cookie_t *listp,
258     md_mem_info_t *mem, int num_comps)
259 {
260 	mde_cookie_t *dl, *bl, *bclist;
261 	int bc, idx, mdesc_dimm_count, mdesc_bank_count;
262 	mem_dimm_map_t *dm, **dp;
263 	uint64_t i;
264 	int n;
265 	uint64_t mask, match, base, size;
266 	char *unum, *serial, *part, *dash;
267 	mem_seg_map_t *smp;
268 	mem_bank_map_t *bmp, **banklist;
269 	mem_dimm_list_t *dlp;
270 	mem_grp_t *gmp;
271 	char *type, *sp, *jnum, *nac;
272 	size_t ss;
273 
274 	mdesc_dimm_count = 0;
275 	for (idx = 0; idx < num_comps; idx++) {
276 		if (md_get_prop_str(mdp, listp[idx], "type", &type) < 0)
277 			continue;
278 		if ((strcmp(type, "dimm") == 0) ||
279 		    (strcmp(type, "mem-board") == 0) ||
280 		    (strcmp(type, "memboard") == 0)) {
281 			mdesc_dimm_count++;
282 			if (md_get_prop_str(mdp, listp[idx], "nac",
283 			    &nac) < 0)
284 				nac = "";
285 			if (md_get_prop_str(mdp, listp[idx], "label",
286 			    &jnum) < 0)
287 				jnum = "";
288 			if (md_get_prop_str(mdp, listp[idx],
289 			    "serial_number", &serial) < 0)
290 				serial = "";
291 			if (md_get_prop_str(mdp, listp[idx],
292 			    "part_number", &part) < 0)
293 				part = "";
294 			if (md_get_prop_str(mdp, listp[idx],
295 			    "dash_number", &dash) < 0)
296 				dash = "";
297 
298 			ss = strlen(part) + strlen(dash) + 1;
299 			sp = topo_mod_alloc(mod, ss);
300 			sp = strcpy(sp, part);
301 			sp = strncat(sp, dash, strlen(dash) + 1);
302 
303 			dm = topo_mod_alloc(mod, sizeof (mem_dimm_map_t));
304 
305 			if ((strcmp(nac, "") != 0) &&
306 			    (strcmp(jnum, "") != 0)) {
307 				ss = strlen(nac) + strlen(jnum) + 2;
308 				unum = topo_mod_alloc(mod, ss);
309 				(void) snprintf(unum, ss, "%s/%s", nac,
310 				    jnum);
311 				dm->dm_label = unum;
312 			} else {
313 				unum = nac;
314 				dm->dm_label = topo_mod_strdup(mod, unum);
315 			}
316 
317 			dm->dm_serid = topo_mod_strdup(mod, serial);
318 			dm->dm_part = sp;
319 
320 			/* The following is an insertion sort. */
321 
322 			for (dp = &(mem->mem_dm); ; dp = &((*dp)->dm_next)) {
323 				if ((*dp == NULL) ||
324 				    (strcmp((*dp)->dm_label,
325 				    dm->dm_label) > 0)) {
326 					dm->dm_next = *dp;
327 					*dp = dm;
328 					break;
329 				}
330 			}
331 		}
332 	}
333 
334 	/* N2 (PRI) specific segment initialization occurs here */
335 
336 	mdesc_bank_count = md_scan_dag(mdp, MDE_INVAL_ELEM_COOKIE,
337 	    md_find_name(mdp, "memory-bank"),
338 	    md_find_name(mdp, "fwd"),
339 	    listp);
340 
341 	/*
342 	 * banklist and bclist will be parallel arrays.  For a given bank,
343 	 * bclist[i] will be the PRI node id, and *banklist+i will point to the
344 	 * mem_bank_map_t for that bank.
345 	 */
346 
347 	banklist = topo_mod_zalloc(mod, mdesc_bank_count *
348 	    sizeof (mem_bank_map_t *));
349 	bclist = topo_mod_zalloc(mod, mdesc_bank_count *
350 	    sizeof (mde_cookie_t));
351 
352 	dl = topo_mod_zalloc(mod, mdesc_dimm_count * sizeof (mde_cookie_t));
353 
354 	for (idx = 0; idx < mdesc_bank_count; idx++) {
355 		if (md_get_prop_val(mdp, listp[idx], "mask", &mask) < 0)
356 			mask = 0;
357 		if (md_get_prop_val(mdp, listp[idx], "match", &match) < 0)
358 			match = 0;
359 
360 		bmp = topo_mod_zalloc(mod, sizeof (mem_bank_map_t));
361 		bmp->bm_next = mem->mem_bank;
362 		mem->mem_bank = bmp;
363 		bmp->bm_mask = mask;
364 		bmp->bm_match = match;
365 		/* link this bank to its dimms */
366 		n = md_scan_dag(mdp, listp[idx],
367 		    md_find_name(mdp, "component"),
368 		    md_find_name(mdp, "fwd"),
369 		    dl);
370 		bmp->bm_shift = mem_log2(n);
371 
372 		bclist[idx] = listp[idx];
373 		*(banklist+idx) = bmp;
374 
375 		for (i = 0; i < n; i++) {
376 			if (md_get_prop_str(mdp, dl[i],
377 			    "serial_number", &serial) < 0)
378 				continue;
379 			if ((dm = mem_get_dimm_by_sn(serial, mem)) == NULL)
380 				continue;
381 			dlp = topo_mod_zalloc(mod, sizeof (mem_dimm_list_t));
382 			dlp->dl_next = bmp->bm_dlist;
383 			bmp->bm_dlist = dlp;
384 			dlp->dl_dimm = dm;
385 		}
386 	}
387 	topo_mod_free(mod, dl, mdesc_dimm_count * sizeof (mde_cookie_t));
388 
389 	bl = topo_mod_zalloc(mod, mdesc_bank_count * sizeof (mde_cookie_t));
390 	n = md_scan_dag(mdp, MDE_INVAL_ELEM_COOKIE,
391 	    md_find_name(mdp, "memory-segment"),
392 	    md_find_name(mdp, "fwd"),
393 	    listp);
394 	for (idx = 0; idx < n; idx++) {
395 		if (md_get_prop_val(mdp, listp[idx], "base", &base) < 0)
396 			base = 0;
397 		if (md_get_prop_val(mdp, listp[idx], "size", &size) < 0)
398 			size = 0;
399 		bc = md_scan_dag(mdp, listp[idx],
400 		    md_find_name(mdp, "memory-bank"),
401 		    md_find_name(mdp, "fwd"),
402 		    bl);
403 		smp = topo_mod_zalloc(mod, sizeof (mem_seg_map_t));
404 		smp->sm_next = mem->mem_seg;
405 		mem->mem_seg = smp;
406 		smp->sm_base = base;
407 		smp->sm_size = size;
408 		gmp = find_grp(bl, bc, bclist, banklist, mdesc_bank_count, mem);
409 		if (gmp == NULL)
410 			smp->sm_grp = create_grp(mod, bl, bc,
411 			    bclist, banklist, mdesc_bank_count, mem);
412 		else
413 			smp->sm_grp = gmp;
414 	}
415 	topo_mod_free(mod, bl, mdesc_bank_count * sizeof (mde_cookie_t));
416 	topo_mod_free(mod, bclist, mdesc_bank_count * sizeof (mde_cookie_t));
417 	topo_mod_free(mod, banklist,
418 	    mdesc_bank_count * sizeof (mem_bank_map_t *));
419 }
420 
421 int
mem_mdesc_init(topo_mod_t * mod,md_mem_info_t * mem)422 mem_mdesc_init(topo_mod_t *mod, md_mem_info_t *mem)
423 {
424 	int rc = 0;
425 	md_t *mdp;
426 	ssize_t bufsiz = 0;
427 	uint64_t *bufp;
428 	ldom_hdl_t *lhp;
429 	mde_cookie_t *listp;
430 	int num_nodes;
431 	int num_comps = 0;
432 	uint32_t type = 0;
433 
434 	/* get the PRI/MD */
435 	if ((lhp = ldom_init(mem_alloc, mem_free)) == NULL) {
436 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
437 	}
438 	(void) ldom_get_type(lhp, &type);
439 	if ((type & LDOM_TYPE_CONTROL) != 0) {
440 		bufsiz = ldom_get_core_md(lhp, &bufp);
441 	} else {
442 		bufsiz = ldom_get_local_md(lhp, &bufp);
443 	}
444 	if (bufsiz <= 0) {
445 		topo_mod_dprintf(mod, "failed to get the PRI/MD\n");
446 		ldom_fini(lhp);
447 		return (-1);
448 	}
449 
450 	if ((mdp = md_init_intern(bufp, mem_alloc, mem_free)) == NULL ||
451 	    md_node_count(mdp) <= 0) {
452 		mem_free(bufp, (size_t)bufsiz);
453 		ldom_fini(lhp);
454 		return (-1);
455 	}
456 
457 	num_nodes = md_node_count(mdp);
458 	listp = mem_alloc(sizeof (mde_cookie_t) * num_nodes);
459 
460 	num_comps = md_scan_dag(mdp,
461 	    MDE_INVAL_ELEM_COOKIE,
462 	    md_find_name(mdp, "component"),
463 	    md_find_name(mdp, "fwd"),
464 	    listp);
465 	if (num_comps == 0)
466 		mdesc_init_n1(mod, mdp, listp, mem);
467 	else
468 		mdesc_init_n2(mod, mdp, listp, mem, num_comps);
469 
470 	mem_free(listp, sizeof (mde_cookie_t) * num_nodes);
471 
472 	mem_free(bufp, (size_t)bufsiz);
473 	(void) md_fini(mdp);
474 	ldom_fini(lhp);
475 
476 	return (rc);
477 }
478 
479 void
mem_mdesc_fini(topo_mod_t * mod,md_mem_info_t * mem)480 mem_mdesc_fini(topo_mod_t *mod, md_mem_info_t *mem)
481 {
482 	mem_dimm_map_t *dm, *next;
483 	mem_dimm_list_t *dl, *nl;
484 	mem_bank_map_t *bm, *cm;
485 	mem_grp_t *gm, *hm;
486 	mem_seg_map_t *sm, *snext;
487 
488 	for (dm = mem->mem_dm; dm != NULL; dm = next) {
489 		next = dm->dm_next;
490 		topo_mod_strfree(mod, dm->dm_label);
491 		topo_mod_strfree(mod, dm->dm_serid);
492 		topo_mod_strfree(mod, dm->dm_part);
493 		topo_mod_free(mod, dm, sizeof (mem_dimm_map_t));
494 	}
495 	for (bm = mem->mem_bank; bm != NULL; bm = cm) {
496 		for (dl = bm->bm_dlist; dl != NULL; dl = nl) {
497 			nl = dl->dl_next;
498 			topo_mod_free(mod, dl, sizeof (mem_dimm_list_t));
499 		}
500 		cm = bm->bm_next;
501 		topo_mod_free(mod, bm, sizeof (mem_bank_map_t));
502 	}
503 	for (gm = mem->mem_group; gm != NULL; gm = hm) {
504 		hm = gm->mg_next;
505 		topo_mod_free(mod, gm, sizeof (mem_grp_t));
506 	}
507 	for (sm = mem->mem_seg; sm != NULL; sm = snext) {
508 		snext = sm->sm_next;
509 		topo_mod_free(mod, sm, sizeof (mem_seg_map_t));
510 	}
511 }
512