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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
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 static const topo_pgroup_info_t dimm_channel_pgroup =
57 	{ PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
58 static const topo_pgroup_info_t dimm_pgroup =
59 	{ PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
60 static const topo_pgroup_info_t rank_pgroup =
61 	{ PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
62 static const topo_pgroup_info_t mc_pgroup =
63 	{ PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
64 
65 static const topo_method_t dimm_methods[] = {
66 	{ SIMPLE_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL,
67 	    simple_dimm_label},
68 	{ SIMPLE_DIMM_LBL_MP, "Property method", 0, TOPO_STABILITY_INTERNAL,
69 	    simple_dimm_label_mp},
70 	{ SEQ_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL,
71 	    seq_dimm_label},
72 	{ NULL }
73 };
74 
75 extern const topo_method_t rank_methods[];
76 extern const topo_method_t ntv_page_retire_methods[];
77 
78 static int mc_fd;
79 
80 int
81 mc_offchip_open()
82 {
83 	mc_fd = open("/dev/mc/mc", O_RDONLY);
84 	return (mc_fd != -1);
85 }
86 
87 static int
88 mc_onchip(topo_instance_t id)
89 {
90 	char path[64];
91 
92 	(void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id);
93 	mc_fd = open(path, O_RDONLY);
94 	return (mc_fd != -1);
95 }
96 
97 static void
98 mc_add_ranks(topo_mod_t *mod, tnode_t *dnode, nvlist_t *auth, int dimm,
99     nvlist_t **ranks_nvp, int start_rank, int nranks, char *serial, char *part,
100     char *rev, int maxranks)
101 {
102 	int i;
103 	int rank;
104 	tnode_t *rnode;
105 	nvpair_t *nvp;
106 	nvlist_t *fmri;
107 	int err = 0;
108 
109 	/*
110 	 * If start_rank is defined, it is assigned to the first rank of this
111 	 * dimm.
112 	 */
113 	rank = start_rank >= 0 ? start_rank : dimm * maxranks;
114 	if (topo_node_range_create(mod, dnode, RANK, rank,
115 	    rank + nranks - 1) < 0) {
116 		whinge(mod, NULL, "mc_add_ranks: node range create failed"
117 		    " for rank\n");
118 		return;
119 	}
120 	for (i = 0; i < nranks; i++) {
121 		fmri = topo_mod_hcfmri(mod, dnode, FM_HC_SCHEME_VERSION,
122 		    RANK, rank, NULL, auth, part, rev, serial);
123 		if (fmri == NULL) {
124 			whinge(mod, NULL,
125 			    "mc_add_ranks: topo_mod_hcfmri failed\n");
126 			return;
127 		}
128 		if ((rnode = topo_node_bind(mod, dnode, RANK, rank,
129 		    fmri)) == NULL) {
130 			nvlist_free(fmri);
131 			whinge(mod, NULL, "mc_add_ranks: node bind failed"
132 			    " for ranks\n");
133 			return;
134 		}
135 		(void) topo_node_fru_set(rnode, NULL, 0, &err);
136 
137 		if (topo_method_register(mod, rnode, rank_methods) < 0)
138 			whinge(mod, &err, "rank_create: "
139 			    "topo_method_register failed");
140 
141 		if (! is_xpv() && topo_method_register(mod, rnode,
142 		    ntv_page_retire_methods) < 0)
143 			whinge(mod, &err, "mc_add_ranks: "
144 			    "topo_method_register failed");
145 
146 		(void) topo_node_asru_set(rnode, fmri, TOPO_ASRU_COMPUTE, &err);
147 
148 		if (FM_AWARE_SMBIOS(mod))
149 			(void) topo_node_label_set(rnode, NULL, &err);
150 
151 		nvlist_free(fmri);
152 
153 		(void) topo_pgroup_create(rnode, &rank_pgroup, &err);
154 		for (nvp = nvlist_next_nvpair(ranks_nvp[i], NULL); nvp != NULL;
155 		    nvp = nvlist_next_nvpair(ranks_nvp[i], nvp)) {
156 			(void) nvprop_add(mod, nvp, PGNAME(RANK), rnode);
157 		}
158 		rank++;
159 	}
160 }
161 
162 static void
163 mc_add_dimms(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
164     nvlist_t *auth, nvlist_t **nvl, uint_t ndimms, int maxdimms, int maxranks)
165 {
166 	int i;
167 	nvlist_t *fmri;
168 	tnode_t *dnode;
169 	nvpair_t *nvp;
170 	int err;
171 	nvlist_t **ranks_nvp;
172 	int32_t start_rank = -1;
173 	uint_t nranks = 0;
174 	uint32_t dimm_number;
175 	char *serial = NULL;
176 	char *part = NULL;
177 	char *rev = NULL;
178 	char *label = NULL;
179 	char *name;
180 	id_t smbid;
181 
182 	if (topo_node_range_create(mod, pnode, DIMM, 0,
183 	    maxdimms ? maxdimms-1 : ndimms-1) < 0) {
184 		whinge(mod, NULL,
185 		    "mc_add_dimms: node range create failed\n");
186 		return;
187 	}
188 	for (i = 0; i < ndimms; i++) {
189 		dimm_number = i;
190 		for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL;
191 		    nvp = nvlist_next_nvpair(nvl[i], nvp)) {
192 			name = nvpair_name(nvp);
193 			if (strcmp(name, MCINTEL_NVLIST_RANKS) == 0) {
194 				(void) nvpair_value_nvlist_array(nvp,
195 				    &ranks_nvp, &nranks);
196 			} else if (strcmp(name, MCINTEL_NVLIST_1ST_RANK) == 0) {
197 				(void) nvpair_value_int32(nvp, &start_rank);
198 			} else if (strcmp(name, FM_FMRI_HC_SERIAL_ID) == 0) {
199 				(void) nvpair_value_string(nvp, &serial);
200 			} else if (strcmp(name, FM_FMRI_HC_PART) == 0) {
201 				(void) nvpair_value_string(nvp, &part);
202 			} else if (strcmp(name, FM_FMRI_HC_REVISION) == 0) {
203 				(void) nvpair_value_string(nvp, &rev);
204 			} else if (strcmp(name, FM_FAULT_FRU_LABEL) == 0) {
205 				(void) nvpair_value_string(nvp, &label);
206 			} else if (strcmp(name, MCINTEL_NVLIST_DIMM_NUM) == 0) {
207 				(void) nvpair_value_uint32(nvp, &dimm_number);
208 			}
209 		}
210 		fmri = NULL;
211 
212 		if (FM_AWARE_SMBIOS(mod)) {
213 			int channum;
214 
215 			channum = topo_node_instance(pnode);
216 			smbid = memnode_to_smbiosid(chip_smbid, DIMM_NODE_NAME,
217 			    i, &channum);
218 			if (serial == NULL)
219 				serial = (char *)chip_serial_smbios_get(mod,
220 				    smbid);
221 			if (part == NULL)
222 				part = (char *)chip_part_smbios_get(mod,
223 				    smbid);
224 			if (rev == NULL)
225 				rev = (char *)chip_rev_smbios_get(mod,
226 				    smbid);
227 		}
228 
229 		fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
230 		    DIMM, dimm_number, NULL, auth, part, rev, serial);
231 		if (fmri == NULL) {
232 			whinge(mod, NULL,
233 			    "mc_add_dimms: topo_mod_hcfmri failed\n");
234 			return;
235 		}
236 		if ((dnode = topo_node_bind(mod, pnode, DIMM, dimm_number,
237 		    fmri)) == NULL) {
238 			nvlist_free(fmri);
239 			whinge(mod, NULL, "mc_add_dimms: node bind failed"
240 			    " for dimm\n");
241 			return;
242 		}
243 
244 		if (!FM_AWARE_SMBIOS(mod))
245 			if (topo_method_register(mod, dnode, dimm_methods) < 0)
246 				whinge(mod, NULL, "mc_add_dimms: "
247 				    "topo_method_register failed");
248 
249 		(void) topo_pgroup_create(dnode, &dimm_pgroup, &err);
250 
251 		for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL;
252 		    nvp = nvlist_next_nvpair(nvl[i], nvp)) {
253 			name = nvpair_name(nvp);
254 			if (strcmp(name, MCINTEL_NVLIST_RANKS) != 0 &&
255 			    strcmp(name, FM_FAULT_FRU_LABEL) != 0 &&
256 			    strcmp(name, MCINTEL_NVLIST_1ST_RANK) != 0) {
257 				(void) nvprop_add(mod, nvp, PGNAME(DIMM),
258 				    dnode);
259 			}
260 		}
261 
262 		if (FM_AWARE_SMBIOS(mod)) {
263 			nvlist_free(fmri);
264 			(void) topo_node_resource(dnode, &fmri, &err);
265 			/*
266 			 * We will use a full absolute parent/child label
267 			 */
268 			label = (char *)chip_label_smbios_get(mod,
269 			    pnode, smbid, label);
270 		}
271 
272 		(void) topo_node_label_set(dnode, label, &err);
273 
274 		if (FM_AWARE_SMBIOS(mod))
275 			topo_mod_strfree(mod, label);
276 
277 		(void) topo_node_fru_set(dnode, fmri, 0, &err);
278 		(void) topo_node_asru_set(dnode, fmri, 0, &err);
279 		nvlist_free(fmri);
280 
281 		if (nranks) {
282 			mc_add_ranks(mod, dnode, auth, dimm_number, ranks_nvp,
283 			    start_rank, nranks, serial, part, rev, maxranks);
284 		}
285 	}
286 }
287 
288 static int
289 mc_add_channel(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
290     int channel, nvlist_t *auth, nvlist_t *nvl, int maxdimms, int maxranks)
291 {
292 	tnode_t *mc_channel;
293 	nvlist_t *fmri;
294 	nvlist_t **dimm_nvl;
295 	nvpair_t *nvp;
296 	char *name;
297 	uint_t ndimms;
298 	int err;
299 
300 	if (mkrsrc(mod, pnode, DRAMCHANNEL, channel, auth, &fmri) != 0) {
301 		whinge(mod, NULL, "mc_add_channel: mkrsrc failed\n");
302 		return (-1);
303 	}
304 	if ((mc_channel = topo_node_bind(mod, pnode, DRAMCHANNEL, channel,
305 	    fmri)) == NULL) {
306 		whinge(mod, NULL, "mc_add_channel: node bind failed for %s\n",
307 		    DRAMCHANNEL);
308 		nvlist_free(fmri);
309 		return (-1);
310 	}
311 	(void) topo_node_fru_set(mc_channel, NULL, 0, &err);
312 	nvlist_free(fmri);
313 	(void) topo_pgroup_create(mc_channel, &dimm_channel_pgroup, &err);
314 
315 	if (FM_AWARE_SMBIOS(mod))
316 		(void) topo_node_label_set(mc_channel, NULL, &err);
317 
318 	if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_DIMMS, &dimm_nvl,
319 	    &ndimms) == 0) {
320 		mc_add_dimms(mod, chip_smbid, mc_channel, auth, dimm_nvl,
321 		    ndimms, maxdimms, maxranks);
322 	}
323 	for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
324 	    nvp = nvlist_next_nvpair(nvl, nvp)) {
325 		name = nvpair_name(nvp);
326 		if (strcmp(name, MCINTEL_NVLIST_DIMMS) != 0) {
327 			(void) nvprop_add(mod, nvp, PGNAME(CHAN),
328 			    mc_channel);
329 		}
330 	}
331 
332 	return (0);
333 }
334 
335 static int
336 mc_nb_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
337     const char *name, nvlist_t *auth, nvlist_t *nvl)
338 {
339 	int err;
340 	int i, j;
341 	int channel;
342 	uint8_t nmc;
343 	uint8_t maxranks;
344 	uint8_t maxdimms;
345 	tnode_t *mcnode;
346 	nvlist_t *fmri;
347 	nvlist_t **channel_nvl;
348 	nvpair_t *nvp;
349 	char *pname;
350 	uint_t nchannels;
351 
352 	if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_MC, &channel_nvl,
353 	    &nchannels) != 0) {
354 		whinge(mod, NULL,
355 		    "mc_nb_create: failed to find channel information\n");
356 		return (-1);
357 	}
358 	if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NMEM, &nmc) == 0) {
359 		/*
360 		 * Assume channels are evenly divided among the controllers.
361 		 * Convert nchannels to channels per controller
362 		 */
363 		nchannels = nchannels / nmc;
364 	} else {
365 		/*
366 		 * if number of memory controllers is not specified then there
367 		 * are two channels per controller and the nchannels is total
368 		 * we will set up nmc as number of controllers and convert
369 		 * nchannels to channels per controller
370 		 */
371 		nmc = nchannels / 2;
372 		nchannels = nchannels / nmc;
373 	}
374 	if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NRANKS, &maxranks) != 0)
375 		maxranks = 2;
376 	if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NDIMMS, &maxdimms) != 0)
377 		maxdimms = 0;
378 	if (topo_node_range_create(mod, pnode, name, 0, nmc-1) < 0) {
379 		whinge(mod, NULL,
380 		    "mc_nb_create: node range create failed\n");
381 		return (-1);
382 	}
383 	channel = 0;
384 	for (i = 0; i < nmc; i++) {
385 		if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) {
386 			whinge(mod, NULL, "mc_nb_create: mkrsrc failed\n");
387 			return (-1);
388 		}
389 		if ((mcnode = topo_node_bind(mod, pnode, name, i,
390 		    fmri)) == NULL) {
391 			whinge(mod, NULL, "mc_nb_create: node bind failed"
392 			    " for memory-controller\n");
393 			nvlist_free(fmri);
394 			return (-1);
395 		}
396 
397 		(void) topo_node_fru_set(mcnode, NULL, 0, &err);
398 		nvlist_free(fmri);
399 		(void) topo_pgroup_create(mcnode, &mc_pgroup, &err);
400 
401 		if (FM_AWARE_SMBIOS(mod))
402 			(void) topo_node_label_set(mcnode, NULL, &err);
403 
404 		if (topo_node_range_create(mod, mcnode, DRAMCHANNEL, channel,
405 		    channel + nchannels - 1) < 0) {
406 			whinge(mod, NULL,
407 			    "mc_nb_create: channel node range create failed\n");
408 			return (-1);
409 		}
410 		for (j = 0; j < nchannels; j++) {
411 			if (mc_add_channel(mod, chip_smbid, mcnode, channel,
412 			    auth, channel_nvl[channel], maxdimms,
413 			    maxranks) < 0) {
414 				return (-1);
415 			}
416 			channel++;
417 		}
418 		for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
419 		    nvp = nvlist_next_nvpair(nvl, nvp)) {
420 			pname = nvpair_name(nvp);
421 			if (strcmp(pname, MCINTEL_NVLIST_MC) != 0 &&
422 			    strcmp(pname, MCINTEL_NVLIST_NMEM) != 0 &&
423 			    strcmp(pname, MCINTEL_NVLIST_NRANKS) != 0 &&
424 			    strcmp(pname, MCINTEL_NVLIST_NDIMMS) != 0 &&
425 			    strcmp(pname, MCINTEL_NVLIST_VERSTR) != 0 &&
426 			    strcmp(pname, MCINTEL_NVLIST_MEM) != 0) {
427 				(void) nvprop_add(mod, nvp, PGNAME(MCT),
428 				    mcnode);
429 			}
430 		}
431 	}
432 
433 	return (NULL);
434 }
435 
436 int
437 mc_node_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
438     const char *name, nvlist_t *auth)
439 {
440 	mc_snapshot_info_t mcs;
441 	void *buf = NULL;
442 	nvlist_t *nvl;
443 	uint8_t ver;
444 	int rc;
445 
446 	if (ioctl(mc_fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 ||
447 	    (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL ||
448 	    ioctl(mc_fd, MC_IOC_SNAPSHOT, buf) == -1) {
449 
450 		whinge(mod, NULL, "mc failed to snapshot %s\n",
451 		    strerror(errno));
452 
453 		free(buf);
454 		(void) close(mc_fd);
455 		return (NULL);
456 	}
457 	(void) close(mc_fd);
458 	(void) nvlist_unpack(buf, mcs.mcs_size, &nvl, 0);
459 	topo_mod_free(mod, buf, mcs.mcs_size);
460 
461 	if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_VERSTR, &ver) != 0) {
462 		whinge(mod, NULL, "mc nvlist is not versioned\n");
463 		nvlist_free(nvl);
464 		return (NULL);
465 	} else if (ver != MCINTEL_NVLIST_VERS0) {
466 		whinge(mod, NULL, "mc nvlist version mismatch\n");
467 		nvlist_free(nvl);
468 		return (NULL);
469 	}
470 
471 	rc = mc_nb_create(mod, chip_smbid, pnode, name, auth, nvl);
472 
473 	nvlist_free(nvl);
474 	return (rc);
475 }
476 
477 void
478 onchip_mc_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
479     const char *name, nvlist_t *auth)
480 {
481 	if (mc_onchip(topo_node_instance(pnode)))
482 		(void) mc_node_create(mod, chip_smbid, pnode, name, auth);
483 }
484 
485 int
486 mc_offchip_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
487     nvlist_t *auth)
488 {
489 	return (mc_node_create(mod, IGNORE_ID, pnode, name, auth));
490 }
491