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