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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2019 Joyent, Inc.
25  */
26 
27 #include <unistd.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdarg.h>
31 #include <string.h>
32 #include <strings.h>
33 #include <limits.h>
34 #include <alloca.h>
35 #include <kstat.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #include <libnvpair.h>
39 #include <sys/types.h>
40 #include <sys/bitmap.h>
41 #include <sys/processor.h>
42 #include <sys/param.h>
43 #include <sys/fm/protocol.h>
44 #include <sys/systeminfo.h>
45 #include <sys/mc.h>
46 #include <sys/mc_amd.h>
47 #include <sys/mc_intel.h>
48 #include <fm/topo_mod.h>
49 
50 #include "chip.h"
51 
52 #ifndef MAX
53 #define	MAX(a, b)	((a) > (b) ? (a) : (b))
54 #endif
55 
56 /*
57  * These are names that we use for properties. In the v0 scheme we try to pull
58  * these directly from the memory controller (which also causes a lot more
59  * variation). In the v1 scheme we translate to the name that topo wants to use,
60  * regardless of what the parent is called.
61  */
62 #define	MC_PROP_ECC	"memory-ecc"
63 #define	MC_PROP_POLICY	"memory-policy"
64 #define	CHAN_PROP_MODE	"channel-mode"
65 #define	DIMM_SIZE	"size"
66 #define	DIMM_STRING_SIZE	"dimm-size"
67 #define	DIMM_COL	"ncolumn"
68 #define	DIMM_ROW	"nrow"
69 #define	DIMM_DENSITY	"density"
70 #define	DIMM_WIDTH	"width"
71 #define	DIMM_RANKS	"ranks"
72 #define	DIMM_BANKS	"nbanks"
73 #define	DIMM_HDRL	"hdrl-enabled"
74 #define	DIMM_HDRL_PARITY	"hdrl-parity"
75 #define	DIMM_3DRANK	"3d-subranks"
76 #define	RANK_STATUS	"dimm-rank-status"
77 #define	RANK_SIZE	"dimm-rank-size"
78 
79 static const topo_pgroup_info_t dimm_channel_pgroup =
80 	{ PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
81 static const topo_pgroup_info_t dimm_pgroup =
82 	{ PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
83 static const topo_pgroup_info_t rank_pgroup =
84 	{ PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
85 static const topo_pgroup_info_t mc_pgroup =
86 	{ PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
87 
88 static const topo_method_t dimm_methods[] = {
89 	{ SIMPLE_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL,
90 	    simple_dimm_label},
91 	{ SIMPLE_DIMM_LBL_MP, "Property method", 0, TOPO_STABILITY_INTERNAL,
92 	    simple_dimm_label_mp},
93 	{ SEQ_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL,
94 	    seq_dimm_label},
95 	{ NULL }
96 };
97 
98 extern const topo_method_t rank_methods[];
99 extern const topo_method_t ntv_page_retire_methods[];
100 
101 static int mc_fd;
102 
103 int
mc_offchip_open()104 mc_offchip_open()
105 {
106 	mc_fd = open("/dev/mc/mc", O_RDONLY);
107 	return (mc_fd != -1);
108 }
109 
110 static int
mc_onchip(topo_instance_t id)111 mc_onchip(topo_instance_t id)
112 {
113 	char path[64];
114 
115 	(void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id);
116 	mc_fd = open(path, O_RDONLY);
117 	return (mc_fd != -1);
118 }
119 
120 static void
mc_add_ranks(topo_mod_t * mod,tnode_t * dnode,nvlist_t * auth,int dimm,nvlist_t ** ranks_nvp,int start_rank,int nranks,char * serial,char * part,char * rev,int maxranks)121 mc_add_ranks(topo_mod_t *mod, tnode_t *dnode, nvlist_t *auth, int dimm,
122     nvlist_t **ranks_nvp, int start_rank, int nranks, char *serial, char *part,
123     char *rev, int maxranks)
124 {
125 	int i;
126 	int rank;
127 	tnode_t *rnode;
128 	nvpair_t *nvp;
129 	nvlist_t *fmri;
130 	int err = 0;
131 
132 	/*
133 	 * If start_rank is defined, it is assigned to the first rank of this
134 	 * dimm.
135 	 */
136 	rank = start_rank >= 0 ? start_rank : dimm * maxranks;
137 	if (topo_node_range_create(mod, dnode, RANK, rank,
138 	    rank + nranks - 1) < 0) {
139 		whinge(mod, NULL, "mc_add_ranks: node range create failed"
140 		    " for rank\n");
141 		return;
142 	}
143 	for (i = 0; i < nranks; i++) {
144 		fmri = topo_mod_hcfmri(mod, dnode, FM_HC_SCHEME_VERSION,
145 		    RANK, rank, NULL, auth, part, rev, serial);
146 		if (fmri == NULL) {
147 			whinge(mod, NULL,
148 			    "mc_add_ranks: topo_mod_hcfmri failed\n");
149 			return;
150 		}
151 		if ((rnode = topo_node_bind(mod, dnode, RANK, rank,
152 		    fmri)) == NULL) {
153 			nvlist_free(fmri);
154 			whinge(mod, NULL, "mc_add_ranks: node bind failed"
155 			    " for ranks\n");
156 			return;
157 		}
158 		(void) topo_node_fru_set(rnode, NULL, 0, &err);
159 
160 		if (topo_method_register(mod, rnode, rank_methods) < 0)
161 			whinge(mod, &err, "mc_add_ranks: "
162 			    "topo_method_register failed");
163 
164 		if (! is_xpv() && topo_method_register(mod, rnode,
165 		    ntv_page_retire_methods) < 0)
166 			whinge(mod, &err, "mc_add_ranks: "
167 			    "topo_method_register failed");
168 
169 		(void) topo_node_asru_set(rnode, fmri, TOPO_ASRU_COMPUTE, &err);
170 
171 		if (FM_AWARE_SMBIOS(mod))
172 			(void) topo_node_label_set(rnode, NULL, &err);
173 
174 		nvlist_free(fmri);
175 
176 		(void) topo_pgroup_create(rnode, &rank_pgroup, &err);
177 		for (nvp = nvlist_next_nvpair(ranks_nvp[i], NULL); nvp != NULL;
178 		    nvp = nvlist_next_nvpair(ranks_nvp[i], nvp)) {
179 			(void) nvprop_add(mod, nvp, PGNAME(RANK), rnode);
180 		}
181 		rank++;
182 	}
183 }
184 
185 static void
mc_add_dimms(topo_mod_t * mod,uint16_t chip_smbid,tnode_t * pnode,nvlist_t * auth,nvlist_t ** nvl,uint_t ndimms,int maxdimms,int maxranks)186 mc_add_dimms(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
187     nvlist_t *auth, nvlist_t **nvl, uint_t ndimms, int maxdimms, int maxranks)
188 {
189 	int i;
190 	nvlist_t *fmri;
191 	tnode_t *dnode;
192 	nvpair_t *nvp;
193 	int err;
194 	nvlist_t **ranks_nvp;
195 	int32_t start_rank = -1;
196 	uint_t nranks = 0;
197 	uint32_t dimm_number;
198 	char *serial = NULL;
199 	char *part = NULL;
200 	char *rev = NULL;
201 	char *label = NULL;
202 	char *name;
203 	id_t smbid = -1;
204 
205 	if (topo_node_range_create(mod, pnode, DIMM, 0,
206 	    maxdimms ? maxdimms-1 : ndimms-1) < 0) {
207 		whinge(mod, NULL,
208 		    "mc_add_dimms: node range create failed\n");
209 		return;
210 	}
211 	for (i = 0; i < ndimms; i++) {
212 		dimm_number = i;
213 		for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL;
214 		    nvp = nvlist_next_nvpair(nvl[i], nvp)) {
215 			name = nvpair_name(nvp);
216 			if (strcmp(name, MCINTEL_NVLIST_RANKS) == 0) {
217 				(void) nvpair_value_nvlist_array(nvp,
218 				    &ranks_nvp, &nranks);
219 			} else if (strcmp(name, MCINTEL_NVLIST_1ST_RANK) == 0) {
220 				(void) nvpair_value_int32(nvp, &start_rank);
221 			} else if (strcmp(name, FM_FMRI_HC_SERIAL_ID) == 0) {
222 				(void) nvpair_value_string(nvp, &serial);
223 			} else if (strcmp(name, FM_FMRI_HC_PART) == 0) {
224 				(void) nvpair_value_string(nvp, &part);
225 			} else if (strcmp(name, FM_FMRI_HC_REVISION) == 0) {
226 				(void) nvpair_value_string(nvp, &rev);
227 			} else if (strcmp(name, FM_FAULT_FRU_LABEL) == 0) {
228 				(void) nvpair_value_string(nvp, &label);
229 			} else if (strcmp(name, MCINTEL_NVLIST_DIMM_NUM) == 0) {
230 				(void) nvpair_value_uint32(nvp, &dimm_number);
231 			}
232 		}
233 		fmri = NULL;
234 
235 		if (FM_AWARE_SMBIOS(mod)) {
236 			int channum;
237 
238 			channum = topo_node_instance(pnode);
239 			smbid = memnode_to_smbiosid(mod, chip_smbid,
240 			    DIMM_NODE_NAME, i, &channum);
241 			if (serial == NULL)
242 				serial = (char *)chip_serial_smbios_get(mod,
243 				    smbid);
244 			if (part == NULL)
245 				part = (char *)chip_part_smbios_get(mod,
246 				    smbid);
247 			if (rev == NULL)
248 				rev = (char *)chip_rev_smbios_get(mod,
249 				    smbid);
250 		}
251 
252 		fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
253 		    DIMM, dimm_number, NULL, auth, part, rev, serial);
254 		if (fmri == NULL) {
255 			whinge(mod, NULL,
256 			    "mc_add_dimms: topo_mod_hcfmri failed\n");
257 			return;
258 		}
259 		if ((dnode = topo_node_bind(mod, pnode, DIMM, dimm_number,
260 		    fmri)) == NULL) {
261 			nvlist_free(fmri);
262 			whinge(mod, NULL, "mc_add_dimms: node bind failed"
263 			    " for dimm\n");
264 			return;
265 		}
266 
267 		if (!FM_AWARE_SMBIOS(mod))
268 			if (topo_method_register(mod, dnode, dimm_methods) < 0)
269 				whinge(mod, NULL, "mc_add_dimms: "
270 				    "topo_method_register failed");
271 
272 		(void) topo_pgroup_create(dnode, &dimm_pgroup, &err);
273 
274 		for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL;
275 		    nvp = nvlist_next_nvpair(nvl[i], nvp)) {
276 			name = nvpair_name(nvp);
277 			if (strcmp(name, MCINTEL_NVLIST_RANKS) != 0 &&
278 			    strcmp(name, FM_FAULT_FRU_LABEL) != 0 &&
279 			    strcmp(name, MCINTEL_NVLIST_1ST_RANK) != 0) {
280 				(void) nvprop_add(mod, nvp, PGNAME(DIMM),
281 				    dnode);
282 			}
283 		}
284 
285 		if (FM_AWARE_SMBIOS(mod)) {
286 			nvlist_free(fmri);
287 			(void) topo_node_resource(dnode, &fmri, &err);
288 			/*
289 			 * We will use a full absolute parent/child label
290 			 */
291 			label = (char *)chip_label_smbios_get(mod,
292 			    pnode, smbid, label);
293 		}
294 
295 		(void) topo_node_label_set(dnode, label, &err);
296 
297 		if (FM_AWARE_SMBIOS(mod))
298 			topo_mod_strfree(mod, label);
299 
300 		(void) topo_node_fru_set(dnode, fmri, 0, &err);
301 		(void) topo_node_asru_set(dnode, fmri, 0, &err);
302 		nvlist_free(fmri);
303 
304 		if (nranks) {
305 			mc_add_ranks(mod, dnode, auth, dimm_number, ranks_nvp,
306 			    start_rank, nranks, serial, part, rev, maxranks);
307 		}
308 	}
309 }
310 
311 static int
mc_add_channel(topo_mod_t * mod,uint16_t chip_smbid,tnode_t * pnode,int channel,nvlist_t * auth,nvlist_t * nvl,int maxdimms,int maxranks)312 mc_add_channel(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
313     int channel, nvlist_t *auth, nvlist_t *nvl, int maxdimms, int maxranks)
314 {
315 	tnode_t *mc_channel;
316 	nvlist_t *fmri;
317 	nvlist_t **dimm_nvl;
318 	nvpair_t *nvp;
319 	char *name;
320 	uint_t ndimms;
321 	int err;
322 
323 	if (mkrsrc(mod, pnode, DRAMCHANNEL, channel, auth, &fmri) != 0) {
324 		whinge(mod, NULL, "mc_add_channel: mkrsrc failed\n");
325 		return (-1);
326 	}
327 	if ((mc_channel = topo_node_bind(mod, pnode, DRAMCHANNEL, channel,
328 	    fmri)) == NULL) {
329 		whinge(mod, NULL, "mc_add_channel: node bind failed for %s\n",
330 		    DRAMCHANNEL);
331 		nvlist_free(fmri);
332 		return (-1);
333 	}
334 	(void) topo_node_fru_set(mc_channel, NULL, 0, &err);
335 	nvlist_free(fmri);
336 	(void) topo_pgroup_create(mc_channel, &dimm_channel_pgroup, &err);
337 
338 	if (FM_AWARE_SMBIOS(mod))
339 		(void) topo_node_label_set(mc_channel, NULL, &err);
340 
341 	if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_DIMMS, &dimm_nvl,
342 	    &ndimms) == 0) {
343 		mc_add_dimms(mod, chip_smbid, mc_channel, auth, dimm_nvl,
344 		    ndimms, maxdimms, maxranks);
345 	}
346 	for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
347 	    nvp = nvlist_next_nvpair(nvl, nvp)) {
348 		name = nvpair_name(nvp);
349 		if (strcmp(name, MCINTEL_NVLIST_DIMMS) != 0) {
350 			(void) nvprop_add(mod, nvp, PGNAME(CHAN),
351 			    mc_channel);
352 		}
353 	}
354 
355 	return (0);
356 }
357 
358 static int
mc_nb_create(topo_mod_t * mod,uint16_t chip_smbid,tnode_t * pnode,const char * name,nvlist_t * auth,nvlist_t * nvl)359 mc_nb_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
360     const char *name, nvlist_t *auth, nvlist_t *nvl)
361 {
362 	int err;
363 	int i, j;
364 	int channel;
365 	uint8_t nmc;
366 	uint8_t maxranks;
367 	uint8_t maxdimms;
368 	tnode_t *mcnode;
369 	nvlist_t *fmri;
370 	nvlist_t **channel_nvl;
371 	nvpair_t *nvp;
372 	char *pname;
373 	uint_t nchannels;
374 
375 	if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_MC, &channel_nvl,
376 	    &nchannels) != 0) {
377 		whinge(mod, NULL,
378 		    "mc_nb_create: failed to find channel information\n");
379 		return (-1);
380 	}
381 	if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NMEM, &nmc) == 0) {
382 		/*
383 		 * Assume channels are evenly divided among the controllers.
384 		 * Convert nchannels to channels per controller
385 		 */
386 		nchannels = nchannels / nmc;
387 	} else {
388 		/*
389 		 * if number of memory controllers is not specified then there
390 		 * are two channels per controller and the nchannels is total
391 		 * we will set up nmc as number of controllers and convert
392 		 * nchannels to channels per controller
393 		 */
394 		nmc = nchannels / 2;
395 		nchannels = nchannels / nmc;
396 	}
397 	if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NRANKS, &maxranks) != 0)
398 		maxranks = 2;
399 	if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NDIMMS, &maxdimms) != 0)
400 		maxdimms = 0;
401 	if (topo_node_range_create(mod, pnode, name, 0, nmc-1) < 0) {
402 		whinge(mod, NULL,
403 		    "mc_nb_create: node range create failed\n");
404 		return (-1);
405 	}
406 	channel = 0;
407 	for (i = 0; i < nmc; i++) {
408 		if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) {
409 			whinge(mod, NULL, "mc_nb_create: mkrsrc failed\n");
410 			return (-1);
411 		}
412 		if ((mcnode = topo_node_bind(mod, pnode, name, i,
413 		    fmri)) == NULL) {
414 			whinge(mod, NULL, "mc_nb_create: node bind failed"
415 			    " for memory-controller\n");
416 			nvlist_free(fmri);
417 			return (-1);
418 		}
419 
420 		(void) topo_node_fru_set(mcnode, NULL, 0, &err);
421 		nvlist_free(fmri);
422 		(void) topo_pgroup_create(mcnode, &mc_pgroup, &err);
423 
424 		if (FM_AWARE_SMBIOS(mod))
425 			(void) topo_node_label_set(mcnode, NULL, &err);
426 
427 		if (topo_node_range_create(mod, mcnode, DRAMCHANNEL, channel,
428 		    channel + nchannels - 1) < 0) {
429 			whinge(mod, NULL,
430 			    "mc_nb_create: channel node range create failed\n");
431 			return (-1);
432 		}
433 		for (j = 0; j < nchannels; j++) {
434 			if (mc_add_channel(mod, chip_smbid, mcnode, channel,
435 			    auth, channel_nvl[channel], maxdimms,
436 			    maxranks) < 0) {
437 				return (-1);
438 			}
439 			channel++;
440 		}
441 		for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
442 		    nvp = nvlist_next_nvpair(nvl, nvp)) {
443 			pname = nvpair_name(nvp);
444 			if (strcmp(pname, MCINTEL_NVLIST_MC) != 0 &&
445 			    strcmp(pname, MCINTEL_NVLIST_NMEM) != 0 &&
446 			    strcmp(pname, MCINTEL_NVLIST_NRANKS) != 0 &&
447 			    strcmp(pname, MCINTEL_NVLIST_NDIMMS) != 0 &&
448 			    strcmp(pname, MCINTEL_NVLIST_VERSTR) != 0 &&
449 			    strcmp(pname, MCINTEL_NVLIST_MEM) != 0) {
450 				(void) nvprop_add(mod, nvp, PGNAME(MCT),
451 				    mcnode);
452 			}
453 		}
454 	}
455 
456 	return (0);
457 }
458 
459 static int
mc_rank_create_v1(topo_mod_t * mod,tnode_t * pnode,nvlist_t * auth,nvlist_t * dimm_nvl,uint64_t rsize,uint32_t id)460 mc_rank_create_v1(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth,
461     nvlist_t *dimm_nvl, uint64_t rsize, uint32_t id)
462 {
463 	nvlist_t *fmri;
464 	tnode_t *rank;
465 	int err;
466 	boolean_t *disabled;
467 	uint_t ndisabled;
468 	const char *status;
469 
470 	fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, RANK, id,
471 	    NULL, auth, NULL, NULL, NULL);
472 	if (fmri == NULL) {
473 		whinge(mod, NULL, "mc_rank_create_v1: topo_mod_hcfmri "
474 		    "failed\n");
475 		return (-1);
476 	}
477 
478 	if ((rank = topo_node_bind(mod, pnode, RANK, id, fmri)) == NULL) {
479 		whinge(mod, NULL, "mc_rank_create_v1: node bind failed for "
480 		    "DIMM\n");
481 		nvlist_free(fmri);
482 		return (-1);
483 	}
484 
485 	if (topo_method_register(mod, rank, rank_methods) < 0) {
486 		whinge(mod, NULL, "mc_rank_create_v1: topo_method_register "
487 		    "failed for rank_methods: %d", topo_mod_errno(mod));
488 	}
489 
490 	if (!is_xpv() && topo_method_register(mod, rank,
491 	    ntv_page_retire_methods) != 0) {
492 		whinge(mod, NULL, "mc_rank_create_v1: topo_method_register "
493 		    "failed for page retire: %d", topo_mod_errno(mod));
494 	}
495 
496 	if (topo_node_asru_set(rank, fmri, TOPO_ASRU_COMPUTE, &err) != 0) {
497 		whinge(mod, NULL, "mc_rank_create_v1: failed to set asru: %d",
498 		    err);
499 		nvlist_free(fmri);
500 		return (topo_mod_seterrno(mod, err));
501 	}
502 
503 	if (topo_node_fru_set(rank, NULL, 0, &err) != 0) {
504 		whinge(mod, NULL, "mc_rank_create_v1: fru set failed: "
505 		    "%d\n", err);
506 		nvlist_free(fmri);
507 		return (topo_mod_seterrno(mod, err));
508 	}
509 	nvlist_free(fmri);
510 
511 	if (topo_pgroup_create(rank, &rank_pgroup, &err) != 0) {
512 		whinge(mod, NULL, "mc_rank_create_v1: failed to create "
513 		    "property group: %d\n", err);
514 		return (topo_mod_seterrno(mod, err));
515 	}
516 
517 	/*
518 	 * The traditional northbridge driver broke down each rank into the
519 	 * interleave targets that led to it. At this time, the imc driver (the
520 	 * only v1 provider) does not supply that information and therefore we
521 	 * cannot set that. Instead we just set basic properties on this, the
522 	 * size of the rank and whether or not it is disabled.
523 	 */
524 	if (rsize != 0 && topo_prop_set_uint64(rank, PGNAME(RANK), RANK_SIZE,
525 	    TOPO_PROP_IMMUTABLE, rsize, &err) != 0) {
526 		whinge(mod, NULL, "mc_rank_create_v1: failed to set %s "
527 		    "property: %d", RANK_SIZE, err);
528 		return (topo_mod_seterrno(mod, err));
529 	}
530 
531 	if (nvlist_lookup_boolean_array(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_RDIS,
532 	    &disabled, &ndisabled) != 0) {
533 		whinge(mod, NULL, "mc_rank_create_v1: Couldn't find disabled "
534 		    "ranks array");
535 		return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
536 	}
537 
538 	if (id >= ndisabled) {
539 		whinge(mod, NULL, "mc_rank_create_v1: Found rank %u with id "
540 		    "larger than supported by hardware", id);
541 		return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
542 	}
543 
544 	status = disabled[id] ? "disabled" : "enabled";
545 	if (topo_prop_set_string(rank, PGNAME(RANK), RANK_STATUS,
546 	    TOPO_PROP_IMMUTABLE, status, &err) != 0) {
547 		whinge(mod, NULL, "mc_rank_create_v1: failed to set %s "
548 		    "property: %d", RANK_STATUS, err);
549 		return (topo_mod_seterrno(mod, err));
550 	}
551 
552 	return (0);
553 }
554 
555 static int
mc_dimm_create_v1(topo_mod_t * mod,tnode_t * pnode,nvlist_t * auth,nvlist_t * dimm_nvl,uint_t id)556 mc_dimm_create_v1(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth,
557     nvlist_t *dimm_nvl, uint_t id)
558 {
559 	int err, ret;
560 	tnode_t *dimm;
561 	nvlist_t *fmri;
562 	boolean_t present;
563 	uint64_t size, density, rsize;
564 	uint32_t cols, rows, width, ranks, banks, i;
565 
566 	/*
567 	 * First, figure out if this DIMM is present. If not, we don't bother
568 	 * creating anything.
569 	 */
570 	if (nvlist_lookup_boolean_value(dimm_nvl,
571 	    MCINTEL_NVLIST_V1_DIMM_PRESENT, &present) != 0) {
572 		return (-1);
573 	} else if (!present) {
574 		return (0);
575 	}
576 
577 	fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, DIMM, id,
578 	    NULL, auth, NULL, NULL, NULL);
579 	if (fmri == NULL) {
580 		whinge(mod, NULL, "mc_dimm_create_v1: topo_mod_hcfmri "
581 		    "failed\n");
582 		return (-1);
583 	}
584 
585 	if ((dimm = topo_node_bind(mod, pnode, DIMM, id, fmri)) == NULL) {
586 		whinge(mod, NULL, "mc_dimm_create_v1: node bind failed for "
587 		    "DIMM\n");
588 		nvlist_free(fmri);
589 		return (-1);
590 	}
591 
592 	if (topo_node_fru_set(dimm, NULL, 0, &err) != 0) {
593 		whinge(mod, NULL, "mc_dimm_create_v1: fru set failed: "
594 		    "%d\n", err);
595 		nvlist_free(fmri);
596 		return (topo_mod_seterrno(mod, err));
597 	}
598 	nvlist_free(fmri);
599 
600 	if (topo_pgroup_create(dimm, &dimm_pgroup, &err) != 0) {
601 		whinge(mod, NULL, "mc_dimm_create_v1: failed to create "
602 		    "property group: %d\n", err);
603 		return (topo_mod_seterrno(mod, err));
604 	}
605 
606 	ret = 0;
607 	if (nvlist_lookup_uint64(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_SIZE,
608 	    &size) == 0) {
609 		char buf[64];
610 		const char *suffix;
611 		uint64_t tsize;
612 		ret |= topo_prop_set_uint64(dimm, PGNAME(DIMM), DIMM_SIZE,
613 		    TOPO_PROP_IMMUTABLE, size, &err);
614 		tsize = size;
615 
616 		/*
617 		 * We must manually cons up a dimm-size property which is the
618 		 * size of the dimm as a round number with a prefix such as M or
619 		 * G. This is used by Intel CPU eversholt rules. Older memory
620 		 * controller drivers did this in the driver, but we instead opt
621 		 * to do so in user land.
622 		 */
623 		if (tsize >= (1ULL << 40)) {
624 			tsize /= (1ULL << 40);
625 			suffix = "T";
626 		} else if (tsize >= (1ULL << 30)) {
627 			tsize /= (1ULL << 30);
628 			suffix = "G";
629 		} else if (tsize >= (1ULL << 20)) {
630 			tsize /= (1ULL << 20);
631 			suffix = "M";
632 		} else {
633 			suffix = NULL;
634 		}
635 
636 		if (suffix != NULL) {
637 			if (snprintf(buf, sizeof (buf), "%"PRIu64"%s", tsize,
638 			    suffix) >= sizeof (buf)) {
639 				whinge(mod, NULL, "failed to construct DIMM "
640 				    "size due to buffer overflow");
641 				return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
642 			}
643 			ret = topo_prop_set_string(dimm, PGNAME(DIMM),
644 			    DIMM_STRING_SIZE, TOPO_PROP_IMMUTABLE, buf, &err);
645 			if (ret != 0) {
646 				return (topo_mod_seterrno(mod, err));
647 			}
648 		}
649 	} else {
650 		size = 0;
651 	}
652 
653 	if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_NCOLS,
654 	    &cols) == 0) {
655 		ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_COL,
656 		    TOPO_PROP_IMMUTABLE, cols, &err);
657 		if (ret != 0) {
658 			return (topo_mod_seterrno(mod, err));
659 		}
660 	}
661 
662 	if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_NROWS,
663 	    &rows) == 0) {
664 		ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_ROW,
665 		    TOPO_PROP_IMMUTABLE, rows, &err);
666 		if (ret != 0) {
667 			return (topo_mod_seterrno(mod, err));
668 		}
669 	}
670 
671 	if (nvlist_lookup_uint64(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_DENSITY,
672 	    &density) == 0) {
673 		ret = topo_prop_set_uint64(dimm, PGNAME(DIMM), DIMM_DENSITY,
674 		    TOPO_PROP_IMMUTABLE, density, &err);
675 		if (ret != 0) {
676 			return (topo_mod_seterrno(mod, err));
677 		}
678 	}
679 
680 	if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_WIDTH,
681 	    &width) == 0) {
682 		ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_WIDTH,
683 		    TOPO_PROP_IMMUTABLE, width, &err);
684 		if (ret != 0) {
685 			return (topo_mod_seterrno(mod, err));
686 		}
687 	}
688 
689 	if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_BANKS,
690 	    &banks) == 0) {
691 		ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_BANKS,
692 		    TOPO_PROP_IMMUTABLE, banks, &err);
693 		if (ret != 0) {
694 			return (topo_mod_seterrno(mod, err));
695 		}
696 	} else {
697 		banks = 0;
698 	}
699 
700 	if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_RANKS,
701 	    &ranks) == 0) {
702 		ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_RANKS,
703 		    TOPO_PROP_IMMUTABLE, ranks, &err);
704 		if (ret != 0) {
705 			return (topo_mod_seterrno(mod, err));
706 		}
707 	}
708 
709 	if (ret != 0) {
710 		return (-1);
711 	}
712 
713 	if (topo_node_range_create(mod, dimm, RANK, 0, ranks - 1) < 0) {
714 		whinge(mod, NULL, "mc_dimm_create_v1: rank node range "
715 		    "create failed\n");
716 		return (-1);
717 	}
718 
719 	rsize = 0;
720 	if (size != 0 && banks != 0) {
721 		rsize = size / banks;
722 	}
723 
724 	for (i = 0; i < ranks; i++) {
725 		if (mc_rank_create_v1(mod, dimm, auth, dimm_nvl, rsize, i) !=
726 		    0) {
727 			return (-1);
728 		}
729 	}
730 
731 	return (0);
732 }
733 
734 static int
mc_channel_create_v1(topo_mod_t * mod,tnode_t * pnode,nvlist_t * auth,nvlist_t * chan_nvl,uint_t id,const char * cmode)735 mc_channel_create_v1(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth,
736     nvlist_t *chan_nvl, uint_t id, const char *cmode)
737 {
738 	int err;
739 	tnode_t *chnode;
740 	nvlist_t *fmri;
741 	nvlist_t **dimms;
742 	uint_t ndimms, i;
743 
744 	if (mkrsrc(mod, pnode, DRAMCHANNEL, id, auth, &fmri) != 0) {
745 		whinge(mod, NULL, "mc_channel_create_v1: mkrsrc failed\n");
746 		return (-1);
747 	}
748 
749 	if ((chnode = topo_node_bind(mod, pnode, DRAMCHANNEL, id, fmri)) ==
750 	    NULL) {
751 		whinge(mod, NULL, "mc_channel_create_v1: node bind failed"
752 		    " for dram-channel\n");
753 		nvlist_free(fmri);
754 		return (-1);
755 	}
756 
757 	nvlist_free(fmri);
758 	if (topo_node_fru_set(chnode, NULL, 0, &err) != 0) {
759 		whinge(mod, NULL, "mc_channel_create_v1: fru set failed: "
760 		    "%d\n", err);
761 		return (topo_mod_seterrno(mod, err));
762 	}
763 
764 	if (topo_pgroup_create(chnode, &dimm_channel_pgroup, &err) != 0) {
765 		whinge(mod, NULL, "mc_channel_create_v1: failed to create "
766 		    "property group: %d\n", err);
767 		return (topo_mod_seterrno(mod, err));
768 	}
769 
770 	if (topo_prop_set_string(chnode, PGNAME(CHAN), CHAN_PROP_MODE,
771 	    TOPO_PROP_IMMUTABLE, cmode, &err) != 0) {
772 		return (topo_mod_seterrno(mod, err));
773 	}
774 
775 	if (nvlist_lookup_nvlist_array(chan_nvl, MCINTEL_NVLIST_V1_CHAN_DIMMS,
776 	    &dimms, &ndimms) != 0) {
777 		whinge(mod, NULL, "mc_channel_create_v1: No DIMMS provided");
778 		return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
779 	}
780 
781 	if (topo_node_range_create(mod, chnode, DIMM, 0, ndimms - 1) < 0) {
782 		whinge(mod, NULL, "mc_channel_create_v1: dimm node range "
783 		    "create failed\n");
784 		return (-1);
785 	}
786 
787 	for (i = 0; i < ndimms; i++) {
788 		if (mc_dimm_create_v1(mod, chnode, auth, dimms[i], i) != 0)
789 			return (-1);
790 	}
791 
792 	return (0);
793 }
794 
795 static int
mc_imc_create_v1(topo_mod_t * mod,tnode_t * pnode,const char * name,nvlist_t * auth,nvlist_t * mc_nvl,uint_t id)796 mc_imc_create_v1(topo_mod_t *mod, tnode_t *pnode, const char *name,
797     nvlist_t *auth, nvlist_t *mc_nvl, uint_t id)
798 {
799 	int err, ret;
800 	tnode_t *mcnode;
801 	nvlist_t *fmri, **channels;
802 	boolean_t ecc;
803 	char *page, *cmode;
804 	uint_t nchans, i;
805 
806 	if (mkrsrc(mod, pnode, name, id, auth, &fmri) != 0) {
807 		whinge(mod, NULL, "mc_imc_create_v1: mkrsrc failed\n");
808 		return (-1);
809 	}
810 
811 	if ((mcnode = topo_node_bind(mod, pnode, name, id, fmri)) == NULL) {
812 		whinge(mod, NULL, "mc_imc_create_v1: node bind failed"
813 		    " for memory-controller\n");
814 		nvlist_free(fmri);
815 		return (-1);
816 	}
817 
818 	nvlist_free(fmri);
819 	if (topo_node_fru_set(mcnode, NULL, 0, &err) != 0) {
820 		whinge(mod, NULL, "mc_imc_create_v1: fru set failed: "
821 		    "%d\n", err);
822 		return (topo_mod_seterrno(mod, err));
823 	}
824 
825 	if (topo_pgroup_create(mcnode, &mc_pgroup, &err) != 0) {
826 		whinge(mod, NULL, "mc_imc_create_v1: failed to create "
827 		    "property group: %d\n", err);
828 		return (topo_mod_seterrno(mod, err));
829 	}
830 
831 	/*
832 	 * Add properties to the controller. Our contract allows for these
833 	 * properties to be missing.
834 	 */
835 	ret = 0;
836 	if (nvlist_lookup_boolean_value(mc_nvl, MCINTEL_NVLIST_V1_MC_ECC,
837 	    &ecc) == 0) {
838 		const char *pval = ecc ? "enabled" : "disabled";
839 		ret = topo_prop_set_string(mcnode, PGNAME(MCT), MC_PROP_ECC,
840 		    TOPO_PROP_IMMUTABLE, pval, &err);
841 		if (ret != 0) {
842 			return (topo_mod_seterrno(mod, err));
843 		}
844 	}
845 
846 	if (nvlist_lookup_string(mc_nvl, MCINTEL_NVLIST_V1_MC_POLICY,
847 	    &page) == 0) {
848 		ret = topo_prop_set_string(mcnode, PGNAME(MCT), MC_PROP_POLICY,
849 		    TOPO_PROP_IMMUTABLE, page, &err);
850 		if (ret != 0) {
851 			return (topo_mod_seterrno(mod, err));
852 		}
853 	}
854 
855 	if (nvlist_lookup_string(mc_nvl, MCINTEL_NVLIST_V1_MC_CHAN_MODE,
856 	    &cmode) != 0) {
857 		cmode = NULL;
858 	}
859 
860 	if (nvlist_lookup_nvlist_array(mc_nvl, MCINTEL_NVLIST_V1_MC_CHANNELS,
861 	    &channels, &nchans) != 0) {
862 		whinge(mod, NULL, "mc_imc_create_v1: missing channels entry");
863 		return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
864 	}
865 
866 	if (topo_node_range_create(mod, mcnode, DRAMCHANNEL, 0,
867 	    nchans - 1) < 0) {
868 		whinge(mod, NULL, "mc_imc_create_v1: channel node range create "
869 		    "failed\n");
870 		return (-1);
871 	}
872 
873 	for (i = 0; i < nchans; i++) {
874 		if (mc_channel_create_v1(mod, mcnode, auth, channels[i], i,
875 		    cmode) != 0) {
876 			return (-1);
877 		}
878 	}
879 
880 	return (0);
881 }
882 
883 static int
mc_nb_create_v1(topo_mod_t * mod,tnode_t * pnode,const char * name,nvlist_t * auth,nvlist_t * nvl)884 mc_nb_create_v1(topo_mod_t *mod, tnode_t *pnode, const char *name,
885     nvlist_t *auth, nvlist_t *nvl)
886 {
887 	nvlist_t **mc_nvl;
888 	uint_t nmc, i;
889 
890 	if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_V1_MCS,
891 	    &mc_nvl, &nmc) != 0) {
892 		whinge(mod, NULL, "mc_nb_create_v1: failed to find memory "
893 		    "controller information\n");
894 		return (-1);
895 	}
896 
897 	if (topo_node_range_create(mod, pnode, name, 0, nmc - 1) < 0) {
898 		whinge(mod, NULL,
899 		    "mc_nb_create_v1: node range create failed\n");
900 		return (-1);
901 	}
902 
903 	for (i = 0; i < nmc; i++) {
904 		if (mc_imc_create_v1(mod, pnode, name, auth, mc_nvl[i], i) != 0)
905 			return (-1);
906 	}
907 
908 	return (0);
909 }
910 
911 int
mc_node_create(topo_mod_t * mod,uint16_t chip_smbid,tnode_t * pnode,const char * name,nvlist_t * auth)912 mc_node_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
913     const char *name, nvlist_t *auth)
914 {
915 	mc_snapshot_info_t mcs;
916 	void *buf = NULL;
917 	nvlist_t *nvl;
918 	uint8_t ver;
919 	int rc;
920 
921 	if (ioctl(mc_fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 ||
922 	    (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL ||
923 	    ioctl(mc_fd, MC_IOC_SNAPSHOT, buf) == -1) {
924 
925 		whinge(mod, NULL, "mc failed to snapshot %s\n",
926 		    strerror(errno));
927 
928 		free(buf);
929 		(void) close(mc_fd);
930 		return (0);
931 	}
932 	(void) close(mc_fd);
933 	(void) nvlist_unpack(buf, mcs.mcs_size, &nvl, 0);
934 	topo_mod_free(mod, buf, mcs.mcs_size);
935 
936 	if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_VERSTR, &ver) != 0) {
937 		whinge(mod, NULL, "mc nvlist is not versioned\n");
938 		nvlist_free(nvl);
939 		return (0);
940 	} else if (ver != MCINTEL_NVLIST_VERS0 &&
941 	    ver != MCINTEL_NVLIST_VERS1) {
942 		whinge(mod, NULL, "mc nvlist version mismatch\n");
943 		nvlist_free(nvl);
944 		return (0);
945 	}
946 
947 	if (ver == MCINTEL_NVLIST_VERS1) {
948 		rc = mc_nb_create_v1(mod, pnode, name, auth, nvl);
949 	} else {
950 		rc = mc_nb_create(mod, chip_smbid, pnode, name, auth, nvl);
951 	}
952 
953 	nvlist_free(nvl);
954 	return (rc);
955 }
956 
957 void
onchip_mc_create(topo_mod_t * mod,uint16_t chip_smbid,tnode_t * pnode,const char * name,nvlist_t * auth)958 onchip_mc_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
959     const char *name, nvlist_t *auth)
960 {
961 	if (mc_onchip(topo_node_instance(pnode)))
962 		(void) mc_node_create(mod, chip_smbid, pnode, name, auth);
963 }
964 
965 int
mc_offchip_create(topo_mod_t * mod,tnode_t * pnode,const char * name,nvlist_t * auth)966 mc_offchip_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
967     nvlist_t *auth)
968 {
969 	return (mc_node_create(mod, IGNORE_ID, pnode, name, auth));
970 }
971