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
mc_offchip_open()80 mc_offchip_open()
81 {
82 mc_fd = open("/dev/mc/mc", O_RDONLY);
83 return (mc_fd != -1);
84 }
85
86 static int
mc_onchip(topo_instance_t id)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
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)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
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)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
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)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
mc_nb_create(topo_mod_t * mod,uint16_t chip_smbid,tnode_t * pnode,const char * name,nvlist_t * auth,nvlist_t * nvl)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 (NULL);
433 }
434
435 int
mc_node_create(topo_mod_t * mod,uint16_t chip_smbid,tnode_t * pnode,const char * name,nvlist_t * auth)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 (NULL);
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 (NULL);
464 } else if (ver != MCINTEL_NVLIST_VERS0) {
465 whinge(mod, NULL, "mc nvlist version mismatch\n");
466 nvlist_free(nvl);
467 return (NULL);
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
onchip_mc_create(topo_mod_t * mod,uint16_t chip_smbid,tnode_t * pnode,const char * name,nvlist_t * auth)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
mc_offchip_create(topo_mod_t * mod,tnode_t * pnode,const char * name,nvlist_t * auth)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