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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <unistd.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <limits.h>
36 #include <alloca.h>
37 #include <kstat.h>
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <libnvpair.h>
41 #include <sys/types.h>
42 #include <sys/bitmap.h>
43 #include <sys/processor.h>
44 #include <sys/param.h>
45 #include <sys/fm/protocol.h>
46 #include <sys/systeminfo.h>
47 #include <sys/mc.h>
48 #include <sys/mc_amd.h>
49 #include <sys/mc_intel.h>
50 #include <fm/topo_mod.h>
51 
52 #include "chip.h"
53 
54 #ifndef MAX
55 #define	MAX(a, b)	((a) > (b) ? (a) : (b))
56 #endif
57 
58 static const topo_pgroup_info_t dimm_channel_pgroup =
59 	{ PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
60 static const topo_pgroup_info_t dimm_pgroup =
61 	{ PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
62 static const topo_pgroup_info_t rank_pgroup =
63 	{ PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
64 static const topo_pgroup_info_t mc_pgroup =
65 	{ PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
66 static const topo_method_t rank_methods[] = {
67 	{ TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
68 	    TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
69 	    mem_asru_compute },
70 	{ NULL }
71 };
72 
73 static const topo_method_t dimm_methods[] = {
74 	{ SIMPLE_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL,
75 	    simple_dimm_label},
76 	{ SIMPLE_DIMM_LBL_MP, "Property method", 0, TOPO_STABILITY_INTERNAL,
77 	    simple_dimm_label_mp},
78 	{ SEQ_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL,
79 	    seq_dimm_label},
80 	{ NULL }
81 };
82 
83 static int mc_fd;
84 
85 int
86 mc_offchip_open()
87 {
88 	mc_fd = open("/dev/mc/mc", O_RDONLY);
89 	return (mc_fd != -1);
90 }
91 
92 void
93 mc_add_ranks(topo_mod_t *mod, tnode_t *dnode, nvlist_t *auth, int dimm,
94     nvlist_t **ranks_nvp, int nranks, char *serial, char *part, char *rev)
95 {
96 	int i;
97 	int rank;
98 	tnode_t *rnode;
99 	nvpair_t *nvp;
100 	nvlist_t *fmri;
101 	int err = 0;
102 
103 	rank = dimm * 2;
104 	if (topo_node_range_create(mod, dnode, RANK, rank,
105 	    rank + nranks - 1) < 0) {
106 		whinge(mod, NULL, "mc_add_dimms: node range create failed"
107 		    " for rank\n");
108 		return;
109 	}
110 	for (i = 0; i < nranks; i++) {
111 		fmri = topo_mod_hcfmri(mod, dnode, FM_HC_SCHEME_VERSION,
112 		    RANK, rank, NULL, auth, part, rev, serial);
113 		if (fmri == NULL) {
114 			whinge(mod, NULL,
115 			    "mc_add_ranks: topo_mod_hcfmri failed\n");
116 			return;
117 		}
118 		if ((rnode = topo_node_bind(mod, dnode, RANK, rank,
119 		    fmri)) == NULL) {
120 			nvlist_free(fmri);
121 			whinge(mod, NULL, "mc_add_ranks: node bind failed"
122 			    " for ranks\n");
123 			return;
124 		}
125 		(void) topo_node_fru_set(rnode, NULL, 0, &err);
126 
127 		if (topo_method_register(mod, rnode, rank_methods) < 0)
128 			whinge(mod, &err, "rank_create: "
129 			    "topo_method_register failed");
130 
131 		(void) topo_node_asru_set(rnode, fmri, TOPO_ASRU_COMPUTE, &err);
132 
133 		nvlist_free(fmri);
134 
135 		(void) topo_pgroup_create(rnode, &rank_pgroup, &err);
136 		for (nvp = nvlist_next_nvpair(ranks_nvp[i], NULL); nvp != NULL;
137 		    nvp = nvlist_next_nvpair(ranks_nvp[i], nvp)) {
138 			(void) nvprop_add(mod, nvp, PGNAME(RANK), rnode);
139 		}
140 		rank++;
141 	}
142 }
143 
144 static void
145 mc_add_dimms(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth,
146     nvlist_t **nvl, uint_t ndimms)
147 {
148 	int i;
149 	nvlist_t *fmri;
150 	tnode_t *dnode;
151 	nvpair_t *nvp;
152 	int err;
153 	nvlist_t **ranks_nvp;
154 	uint_t nranks = 0;
155 	char *serial = NULL;
156 	char *part = NULL;
157 	char *rev = NULL;
158 	char *label = NULL;
159 	char *name;
160 
161 	if (topo_node_range_create(mod, pnode, DIMM, 0, ndimms-1) < 0) {
162 		whinge(mod, NULL,
163 		    "mc_add_dimms: node range create failed\n");
164 		return;
165 	}
166 	for (i = 0; i < ndimms; i++) {
167 		for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL;
168 		    nvp = nvlist_next_nvpair(nvl[i], nvp)) {
169 			name = nvpair_name(nvp);
170 			if (strcmp(name, MCINTEL_NVLIST_RANKS) == 0) {
171 				(void) nvpair_value_nvlist_array(nvp,
172 				    &ranks_nvp, &nranks);
173 			} else if (strcmp(name, FM_FMRI_HC_SERIAL_ID) == 0) {
174 				(void) nvpair_value_string(nvp, &serial);
175 			} else if (strcmp(name, FM_FMRI_HC_PART) == 0) {
176 				(void) nvpair_value_string(nvp, &part);
177 			} else if (strcmp(name, FM_FMRI_HC_REVISION) == 0) {
178 				(void) nvpair_value_string(nvp, &rev);
179 			} else if (strcmp(name, FM_FAULT_FRU_LABEL) == 0) {
180 				(void) nvpair_value_string(nvp, &label);
181 			}
182 		}
183 		fmri = NULL;
184 		fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
185 		    DIMM, i, NULL, auth, part, rev, serial);
186 		if (fmri == NULL) {
187 			whinge(mod, NULL,
188 			    "mc_add_dimms: topo_mod_hcfmri failed\n");
189 			return;
190 		}
191 		if ((dnode = topo_node_bind(mod, pnode, DIMM, i,
192 		    fmri)) == NULL) {
193 			nvlist_free(fmri);
194 			whinge(mod, NULL, "mc_add_dimms: node bind failed"
195 			    " for dimm\n");
196 			return;
197 		}
198 
199 		if (topo_method_register(mod, dnode, dimm_methods) < 0)
200 			whinge(mod, NULL, "mc_add_dimms: "
201 			    "topo_method_register failed");
202 
203 		(void) topo_node_fru_set(dnode, fmri, 0, &err);
204 		nvlist_free(fmri);
205 		(void) topo_pgroup_create(dnode, &dimm_pgroup, &err);
206 
207 		for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL;
208 		    nvp = nvlist_next_nvpair(nvl[i], nvp)) {
209 			name = nvpair_name(nvp);
210 			if (strcmp(name, MCINTEL_NVLIST_RANKS) != 0 &&
211 			    strcmp(name, FM_FAULT_FRU_LABEL) != 0) {
212 				(void) nvprop_add(mod, nvp, PGNAME(DIMM),
213 				    dnode);
214 			}
215 		}
216 		if (label)
217 			(void) topo_node_label_set(dnode, label, &err);
218 
219 		if (nranks) {
220 			mc_add_ranks(mod, dnode, auth, i, ranks_nvp, nranks,
221 			    serial, part, rev);
222 		}
223 	}
224 }
225 
226 static int
227 mc_add_channel(topo_mod_t *mod, tnode_t *pnode, int channel, nvlist_t *auth,
228     nvlist_t *nvl)
229 {
230 	tnode_t *mc_channel;
231 	nvlist_t *fmri;
232 	nvlist_t **dimm_nvl;
233 	uint_t ndimms;
234 	int err;
235 
236 	if (mkrsrc(mod, pnode, DRAMCHANNEL, channel, auth, &fmri) != 0) {
237 		whinge(mod, NULL, "mc_add_channel: mkrsrc failed\n");
238 		return (-1);
239 	}
240 	if ((mc_channel = topo_node_bind(mod, pnode, DRAMCHANNEL, channel,
241 	    fmri)) == NULL) {
242 		whinge(mod, NULL, "mc_add_channel: node bind failed for %s\n",
243 		    DRAMCHANNEL);
244 		nvlist_free(fmri);
245 		return (-1);
246 	}
247 	(void) topo_node_fru_set(mc_channel, NULL, 0, &err);
248 	nvlist_free(fmri);
249 	(void) topo_pgroup_create(mc_channel, &dimm_channel_pgroup, &err);
250 	if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_DIMMS, &dimm_nvl,
251 	    &ndimms) == 0) {
252 		mc_add_dimms(mod, mc_channel, auth, dimm_nvl, ndimms);
253 	}
254 	return (0);
255 }
256 
257 static int
258 mc_nb_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *auth,
259     nvlist_t *nvl)
260 {
261 	int err;
262 	int i, j;
263 	int channel;
264 	int nmc;
265 	tnode_t *mcnode;
266 	nvlist_t *fmri;
267 	nvlist_t **channel_nvl;
268 	uint_t nchannels;
269 
270 	if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_MC, &channel_nvl,
271 	    &nchannels) != 0) {
272 		whinge(mod, NULL,
273 		    "mc_nb_create: failed to find channel information\n");
274 		return (-1);
275 	}
276 	nmc = nchannels / 2;
277 	if (topo_node_range_create(mod, pnode, name, 0, nmc-1) < 0) {
278 		whinge(mod, NULL,
279 		    "mc_nb_create: node range create failed\n");
280 		return (-1);
281 	}
282 	channel = 0;
283 	for (i = 0; i < nmc; i++) {
284 		if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) {
285 			whinge(mod, NULL, "mc_nb_create: mkrsrc failed\n");
286 			return (-1);
287 		}
288 		if ((mcnode = topo_node_bind(mod, pnode, name, i,
289 		    fmri)) == NULL) {
290 			whinge(mod, NULL, "chip_create: node bind failed"
291 			    " for memory-controller\n");
292 			nvlist_free(fmri);
293 			return (-1);
294 		}
295 
296 		(void) topo_node_fru_set(mcnode, NULL, 0, &err);
297 		nvlist_free(fmri);
298 		(void) topo_pgroup_create(mcnode, &mc_pgroup, &err);
299 
300 		if (topo_node_range_create(mod, mcnode, DRAMCHANNEL, channel,
301 		    channel + 1) < 0) {
302 			whinge(mod, NULL,
303 			    "mc_nb_create: channel node range create failed\n");
304 			return (-1);
305 		}
306 		for (j = 0; j < 2; j++) {
307 			if (mc_add_channel(mod, mcnode, channel, auth,
308 			    channel_nvl[channel]) < 0) {
309 				return (-1);
310 			}
311 			channel++;
312 		}
313 	}
314 
315 	return (NULL);
316 }
317 
318 int
319 mc_offchip_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
320     nvlist_t *auth)
321 {
322 	mc_snapshot_info_t mcs;
323 	void *buf = NULL;
324 	nvlist_t *nvl;
325 	uint8_t ver;
326 	int rc;
327 
328 	if (ioctl(mc_fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 ||
329 	    (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL ||
330 	    ioctl(mc_fd, MC_IOC_SNAPSHOT, buf) == -1) {
331 
332 		whinge(mod, NULL, "mc failed to snapshot %s\n",
333 		    strerror(errno));
334 
335 		free(buf);
336 		(void) close(mc_fd);
337 		return (NULL);
338 	}
339 	(void) close(mc_fd);
340 	(void) nvlist_unpack(buf, mcs.mcs_size, &nvl, 0);
341 	topo_mod_free(mod, buf, mcs.mcs_size);
342 
343 	if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_VERSTR, &ver) != 0) {
344 		whinge(mod, NULL, "mc nvlist is not versioned\n");
345 		nvlist_free(nvl);
346 		return (NULL);
347 	} else if (ver != MCINTEL_NVLIST_VERS0) {
348 		whinge(mod, NULL, "mc nvlist version mismatch\n");
349 		nvlist_free(nvl);
350 		return (NULL);
351 	}
352 
353 	rc = mc_nb_create(mod, pnode, name, auth, nvl);
354 
355 	nvlist_free(nvl);
356 	return (rc);
357 }
358