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 2008 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 static const topo_method_t rank_methods[] = {
65 	{ TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
66 	    TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
67 	    mem_asru_compute },
68 	{ NULL }
69 };
70 
71 static const topo_method_t dimm_methods[] = {
72 	{ SIMPLE_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL,
73 	    simple_dimm_label},
74 	{ SIMPLE_DIMM_LBL_MP, "Property method", 0, TOPO_STABILITY_INTERNAL,
75 	    simple_dimm_label_mp},
76 	{ SEQ_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL,
77 	    seq_dimm_label},
78 	{ NULL }
79 };
80 
81 static int mc_fd;
82 
83 int
84 mc_offchip_open()
85 {
86 	mc_fd = open("/dev/mc/mc", O_RDONLY);
87 	return (mc_fd != -1);
88 }
89 
90 static int
91 mc_onchip(topo_instance_t id)
92 {
93 	char path[64];
94 
95 	(void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id);
96 	mc_fd = open(path, O_RDONLY);
97 	return (mc_fd != -1);
98 }
99 
100 void
101 mc_add_ranks(topo_mod_t *mod, tnode_t *dnode, nvlist_t *auth, int dimm,
102     nvlist_t **ranks_nvp, int nranks, char *serial, char *part, char *rev,
103     int maxranks)
104 {
105 	int i;
106 	int rank;
107 	tnode_t *rnode;
108 	nvpair_t *nvp;
109 	nvlist_t *fmri;
110 	int err = 0;
111 
112 	rank = dimm * maxranks;
113 	if (topo_node_range_create(mod, dnode, RANK, rank,
114 	    rank + nranks - 1) < 0) {
115 		whinge(mod, NULL, "mc_add_dimms: 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 		(void) topo_node_asru_set(rnode, fmri, TOPO_ASRU_COMPUTE, &err);
141 
142 		nvlist_free(fmri);
143 
144 		(void) topo_pgroup_create(rnode, &rank_pgroup, &err);
145 		for (nvp = nvlist_next_nvpair(ranks_nvp[i], NULL); nvp != NULL;
146 		    nvp = nvlist_next_nvpair(ranks_nvp[i], nvp)) {
147 			(void) nvprop_add(mod, nvp, PGNAME(RANK), rnode);
148 		}
149 		rank++;
150 	}
151 }
152 
153 static void
154 mc_add_dimms(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth,
155     nvlist_t **nvl, uint_t ndimms, int maxranks)
156 {
157 	int i;
158 	nvlist_t *fmri;
159 	tnode_t *dnode;
160 	nvpair_t *nvp;
161 	int err;
162 	nvlist_t **ranks_nvp;
163 	uint_t nranks = 0;
164 	char *serial = NULL;
165 	char *part = NULL;
166 	char *rev = NULL;
167 	char *label = NULL;
168 	char *name;
169 
170 	if (topo_node_range_create(mod, pnode, DIMM, 0, ndimms-1) < 0) {
171 		whinge(mod, NULL,
172 		    "mc_add_dimms: node range create failed\n");
173 		return;
174 	}
175 	for (i = 0; i < ndimms; i++) {
176 		for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL;
177 		    nvp = nvlist_next_nvpair(nvl[i], nvp)) {
178 			name = nvpair_name(nvp);
179 			if (strcmp(name, MCINTEL_NVLIST_RANKS) == 0) {
180 				(void) nvpair_value_nvlist_array(nvp,
181 				    &ranks_nvp, &nranks);
182 			} else if (strcmp(name, FM_FMRI_HC_SERIAL_ID) == 0) {
183 				(void) nvpair_value_string(nvp, &serial);
184 			} else if (strcmp(name, FM_FMRI_HC_PART) == 0) {
185 				(void) nvpair_value_string(nvp, &part);
186 			} else if (strcmp(name, FM_FMRI_HC_REVISION) == 0) {
187 				(void) nvpair_value_string(nvp, &rev);
188 			} else if (strcmp(name, FM_FAULT_FRU_LABEL) == 0) {
189 				(void) nvpair_value_string(nvp, &label);
190 			}
191 		}
192 		fmri = NULL;
193 		fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
194 		    DIMM, i, NULL, auth, part, rev, serial);
195 		if (fmri == NULL) {
196 			whinge(mod, NULL,
197 			    "mc_add_dimms: topo_mod_hcfmri failed\n");
198 			return;
199 		}
200 		if ((dnode = topo_node_bind(mod, pnode, DIMM, i,
201 		    fmri)) == NULL) {
202 			nvlist_free(fmri);
203 			whinge(mod, NULL, "mc_add_dimms: node bind failed"
204 			    " for dimm\n");
205 			return;
206 		}
207 
208 		if (topo_method_register(mod, dnode, dimm_methods) < 0)
209 			whinge(mod, NULL, "mc_add_dimms: "
210 			    "topo_method_register failed");
211 
212 		(void) topo_node_fru_set(dnode, fmri, 0, &err);
213 		nvlist_free(fmri);
214 		(void) topo_pgroup_create(dnode, &dimm_pgroup, &err);
215 
216 		for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL;
217 		    nvp = nvlist_next_nvpair(nvl[i], nvp)) {
218 			name = nvpair_name(nvp);
219 			if (strcmp(name, MCINTEL_NVLIST_RANKS) != 0 &&
220 			    strcmp(name, FM_FAULT_FRU_LABEL) != 0) {
221 				(void) nvprop_add(mod, nvp, PGNAME(DIMM),
222 				    dnode);
223 			}
224 		}
225 		if (label)
226 			(void) topo_node_label_set(dnode, label, &err);
227 
228 		if (nranks) {
229 			mc_add_ranks(mod, dnode, auth, i, ranks_nvp, nranks,
230 			    serial, part, rev, maxranks);
231 		}
232 	}
233 }
234 
235 static int
236 mc_add_channel(topo_mod_t *mod, tnode_t *pnode, int channel, nvlist_t *auth,
237     nvlist_t *nvl, int maxranks)
238 {
239 	tnode_t *mc_channel;
240 	nvlist_t *fmri;
241 	nvlist_t **dimm_nvl;
242 	nvpair_t *nvp;
243 	char *name;
244 	uint_t ndimms;
245 	int err;
246 
247 	if (mkrsrc(mod, pnode, DRAMCHANNEL, channel, auth, &fmri) != 0) {
248 		whinge(mod, NULL, "mc_add_channel: mkrsrc failed\n");
249 		return (-1);
250 	}
251 	if ((mc_channel = topo_node_bind(mod, pnode, DRAMCHANNEL, channel,
252 	    fmri)) == NULL) {
253 		whinge(mod, NULL, "mc_add_channel: node bind failed for %s\n",
254 		    DRAMCHANNEL);
255 		nvlist_free(fmri);
256 		return (-1);
257 	}
258 	(void) topo_node_fru_set(mc_channel, NULL, 0, &err);
259 	nvlist_free(fmri);
260 	(void) topo_pgroup_create(mc_channel, &dimm_channel_pgroup, &err);
261 	if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_DIMMS, &dimm_nvl,
262 	    &ndimms) == 0) {
263 		mc_add_dimms(mod, mc_channel, auth, dimm_nvl, ndimms, maxranks);
264 	}
265 	for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
266 	    nvp = nvlist_next_nvpair(nvl, nvp)) {
267 		name = nvpair_name(nvp);
268 		if (strcmp(name, MCINTEL_NVLIST_DIMMS) != 0) {
269 			(void) nvprop_add(mod, nvp, PGNAME(CHAN),
270 			    mc_channel);
271 		}
272 	}
273 	return (0);
274 }
275 
276 static int
277 mc_nb_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *auth,
278     nvlist_t *nvl)
279 {
280 	int err;
281 	int i, j;
282 	int channel;
283 	uint8_t nmc;
284 	uint8_t maxranks;
285 	tnode_t *mcnode;
286 	nvlist_t *fmri;
287 	nvlist_t **channel_nvl;
288 	nvpair_t *nvp;
289 	char *pname;
290 	uint_t nchannels;
291 
292 	if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_MC, &channel_nvl,
293 	    &nchannels) != 0) {
294 		whinge(mod, NULL,
295 		    "mc_nb_create: failed to find channel information\n");
296 		return (-1);
297 	}
298 	if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NMEM, &nmc) != 0) {
299 		/*
300 		 * if number of memory controllers is not specified then there
301 		 * are two channels per controller and the nchannels is total
302 		 * we will set up nmc as number of controllers and convert
303 		 * nchannels to channels per controller
304 		 */
305 		nmc = nchannels / 2;
306 		nchannels = nchannels / nmc;
307 	}
308 	if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NRANKS, &maxranks) != 0)
309 		maxranks = 2;
310 	if (topo_node_range_create(mod, pnode, name, 0, nmc-1) < 0) {
311 		whinge(mod, NULL,
312 		    "mc_nb_create: node range create failed\n");
313 		return (-1);
314 	}
315 	channel = 0;
316 	for (i = 0; i < nmc; i++) {
317 		if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) {
318 			whinge(mod, NULL, "mc_nb_create: mkrsrc failed\n");
319 			return (-1);
320 		}
321 		if ((mcnode = topo_node_bind(mod, pnode, name, i,
322 		    fmri)) == NULL) {
323 			whinge(mod, NULL, "mc_nb_create: node bind failed"
324 			    " for memory-controller\n");
325 			nvlist_free(fmri);
326 			return (-1);
327 		}
328 
329 		(void) topo_node_fru_set(mcnode, NULL, 0, &err);
330 		nvlist_free(fmri);
331 		(void) topo_pgroup_create(mcnode, &mc_pgroup, &err);
332 
333 		if (topo_node_range_create(mod, mcnode, DRAMCHANNEL, channel,
334 		    channel + nchannels - 1) < 0) {
335 			whinge(mod, NULL,
336 			    "mc_nb_create: channel node range create failed\n");
337 			return (-1);
338 		}
339 		for (j = 0; j < nchannels; j++) {
340 			if (mc_add_channel(mod, mcnode, channel, auth,
341 			    channel_nvl[channel], maxranks) < 0) {
342 				return (-1);
343 			}
344 			channel++;
345 		}
346 		for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
347 		    nvp = nvlist_next_nvpair(nvl, nvp)) {
348 			pname = nvpair_name(nvp);
349 			if (strcmp(pname, MCINTEL_NVLIST_MC) != 0 &&
350 			    strcmp(pname, MCINTEL_NVLIST_NMEM) != 0 &&
351 			    strcmp(pname, MCINTEL_NVLIST_NRANKS) != 0 &&
352 			    strcmp(pname, MCINTEL_NVLIST_VERSTR) != 0 &&
353 			    strcmp(pname, MCINTEL_NVLIST_MEM) != 0) {
354 				(void) nvprop_add(mod, nvp, PGNAME(MCT),
355 				    mcnode);
356 			}
357 		}
358 	}
359 
360 	return (NULL);
361 }
362 
363 int
364 mc_node_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
365     nvlist_t *auth)
366 {
367 	mc_snapshot_info_t mcs;
368 	void *buf = NULL;
369 	nvlist_t *nvl;
370 	uint8_t ver;
371 	int rc;
372 
373 	if (ioctl(mc_fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 ||
374 	    (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL ||
375 	    ioctl(mc_fd, MC_IOC_SNAPSHOT, buf) == -1) {
376 
377 		whinge(mod, NULL, "mc failed to snapshot %s\n",
378 		    strerror(errno));
379 
380 		free(buf);
381 		(void) close(mc_fd);
382 		return (NULL);
383 	}
384 	(void) close(mc_fd);
385 	(void) nvlist_unpack(buf, mcs.mcs_size, &nvl, 0);
386 	topo_mod_free(mod, buf, mcs.mcs_size);
387 
388 	if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_VERSTR, &ver) != 0) {
389 		whinge(mod, NULL, "mc nvlist is not versioned\n");
390 		nvlist_free(nvl);
391 		return (NULL);
392 	} else if (ver != MCINTEL_NVLIST_VERS0) {
393 		whinge(mod, NULL, "mc nvlist version mismatch\n");
394 		nvlist_free(nvl);
395 		return (NULL);
396 	}
397 
398 	rc = mc_nb_create(mod, pnode, name, auth, nvl);
399 
400 	nvlist_free(nvl);
401 	return (rc);
402 }
403 
404 void
405 onchip_mc_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
406     nvlist_t *auth)
407 {
408 	if (mc_onchip(topo_node_instance(pnode)))
409 		(void) mc_node_create(mod, pnode, name, auth);
410 }
411 
412 int
413 mc_offchip_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
414     nvlist_t *auth)
415 {
416 	return (mc_node_create(mod, pnode, name, auth));
417 }
418