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 /*
27 * AMD memory enumeration
28 */
29
30 #include <sys/types.h>
31 #include <unistd.h>
32 #include <stropts.h>
33 #include <sys/fm/protocol.h>
34 #include <sys/mc.h>
35 #include <sys/mc_amd.h>
36 #include <fm/topo_mod.h>
37 #include <strings.h>
38 #include <sys/stat.h>
39 #include <fcntl.h>
40
41 #include "chip.h"
42
43 #define MAX_CHANNUM 1
44 #define MAX_DIMMNUM 7
45 #define MAX_CSNUM 7
46
47 static const topo_pgroup_info_t cs_pgroup =
48 { PGNAME(CS), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
49 static const topo_pgroup_info_t dimm_pgroup =
50 { PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
51 static const topo_pgroup_info_t mc_pgroup =
52 { PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
53 static const topo_pgroup_info_t rank_pgroup =
54 { PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
55 static const topo_pgroup_info_t chan_pgroup =
56 { PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
57
58 static const topo_method_t dimm_methods[] = {
59 { SIMPLE_DIMM_LBL, "Property method", 0,
60 TOPO_STABILITY_INTERNAL, simple_dimm_label},
61 { SIMPLE_DIMM_LBL_MP, "Property method", 0,
62 TOPO_STABILITY_INTERNAL, simple_dimm_label_mp},
63 { SEQ_DIMM_LBL, "Property method", 0,
64 TOPO_STABILITY_INTERNAL, seq_dimm_label},
65 { G4_DIMM_LBL, "Property method", 0,
66 TOPO_STABILITY_INTERNAL, g4_dimm_label},
67 { G12F_DIMM_LBL, "Property method", 0,
68 TOPO_STABILITY_INTERNAL, g12f_dimm_label},
69 { GET_DIMM_SERIAL, "Property method", 0,
70 TOPO_STABILITY_INTERNAL, get_dimm_serial},
71 { NULL }
72 };
73
74 const topo_method_t rank_methods[] = {
75 { TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
76 TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
77 mem_asru_compute },
78 { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC,
79 TOPO_METH_PRESENT_VERSION, TOPO_STABILITY_INTERNAL,
80 rank_fmri_present },
81 { TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC,
82 TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL,
83 rank_fmri_replaced },
84 { NULL }
85 };
86
87 const topo_method_t ntv_page_retire_methods[] = {
88 { TOPO_METH_RETIRE, TOPO_METH_RETIRE_DESC,
89 TOPO_METH_RETIRE_VERSION, TOPO_STABILITY_INTERNAL,
90 ntv_page_retire },
91 { TOPO_METH_UNRETIRE, TOPO_METH_UNRETIRE_DESC,
92 TOPO_METH_UNRETIRE_VERSION, TOPO_STABILITY_INTERNAL,
93 ntv_page_unretire },
94 { TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC,
95 TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL,
96 ntv_page_service_state },
97 { NULL }
98 };
99
100 /*
101 * Serials, Labels are obtained from SMBIOS, so
102 * we leave out the related methods, any other
103 * methods that will be added to gen_cs_methods
104 * should be added to x86pi_gen_cs_methods too
105 */
106 static const topo_method_t x86pi_gen_cs_methods[] = {
107 { TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
108 TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
109 mem_asru_compute },
110 { NULL }
111 };
112
113 static const topo_method_t gen_cs_methods[] = {
114 { TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
115 TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
116 mem_asru_compute },
117 { SIMPLE_CS_LBL_MP, "Property method", 0,
118 TOPO_STABILITY_INTERNAL, simple_cs_label_mp},
119 { GET_DIMM_SERIAL, "Property method", 0,
120 TOPO_STABILITY_INTERNAL, get_dimm_serial},
121 { NULL }
122 };
123
124 static nvlist_t *cs_fmri[MC_CHIP_NCS];
125
126 /*
127 * Called when there is no memory-controller driver to provide topology
128 * information. Generate a maximal memory topology that is appropriate
129 * for the chip revision. The memory-controller node has already been
130 * bound as mcnode, and the parent of that is cnode.
131 *
132 * We create a tree of dram-channel and chip-select nodes below the
133 * memory-controller node. There will be two dram channels and 8 chip-selects
134 * below each, regardless of actual socket type, processor revision and so on.
135 * This is adequate for generic diagnosis up to family 0x10 revision D.
136 */
137 /*ARGSUSED*/
138 static int
amd_generic_mc_create(topo_mod_t * mod,uint16_t smbid,tnode_t * cnode,tnode_t * mcnode,int family,int model,nvlist_t * auth)139 amd_generic_mc_create(topo_mod_t *mod, uint16_t smbid, tnode_t *cnode,
140 tnode_t *mcnode, int family, int model, nvlist_t *auth)
141 {
142 int chan, cs;
143
144 /*
145 * Elsewhere we have already returned for families less than 0xf.
146 * This "generic" topology is adequate for all of family 0xf and
147 * for revisions A to E of family 0x10 (for the list of models
148 * in each revision, refer to usr/src/uts/i86pc/os/cpuid_subr.c).
149 * We cover all family 0x10 models, till model 10.
150 */
151 if (family > 0x10 || (family == 0x10 && model > 10))
152 return (1);
153
154 if (topo_node_range_create(mod, mcnode, CHAN_NODE_NAME, 0,
155 MAX_CHANNUM) < 0) {
156 whinge(mod, NULL, "amd_generic_mc_create: range create for "
157 "channels failed\n");
158 return (-1);
159 }
160
161 for (chan = 0; chan <= MAX_CHANNUM; chan++) {
162 tnode_t *chnode;
163 nvlist_t *fmri;
164 int err;
165
166 if (mkrsrc(mod, mcnode, CHAN_NODE_NAME, chan, auth,
167 &fmri) != 0) {
168 whinge(mod, NULL, "amd_generic_mc_create: mkrsrc "
169 "failed\n");
170 return (-1);
171 }
172
173 if ((chnode = topo_node_bind(mod, mcnode, CHAN_NODE_NAME,
174 chan, fmri)) == NULL) {
175 nvlist_free(fmri);
176 whinge(mod, NULL, "amd_generic_mc_create: node "
177 "bind failed\n");
178 return (-1);
179 }
180
181 nvlist_free(fmri);
182
183 (void) topo_pgroup_create(chnode, &chan_pgroup, &err);
184
185 (void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel",
186 TOPO_PROP_IMMUTABLE, chan == 0 ? "A" : "B", &err);
187
188 if (FM_AWARE_SMBIOS(mod)) {
189 if (topo_node_label_set(chnode, NULL, &err) == -1)
190 whinge(mod, NULL, "amd_generic_mc_create: "
191 "topo_node_label_set\n");
192 if (topo_node_fru_set(chnode, NULL, 0, &err) != 0)
193 whinge(mod, NULL, "amd_generic_mc_create: "
194 "topo_node_fru_set failed\n");
195 }
196
197 if (topo_node_range_create(mod, chnode, CS_NODE_NAME,
198 0, MAX_CSNUM) < 0) {
199 whinge(mod, NULL, "amd_generic_mc_create: "
200 "range create for cs failed\n");
201 return (-1);
202 }
203
204 for (cs = 0; cs <= MAX_CSNUM; cs++) {
205 tnode_t *csnode;
206
207 if (mkrsrc(mod, chnode, CS_NODE_NAME, cs, auth,
208 &fmri) != 0) {
209 whinge(mod, NULL, "amd_generic_mc_create: "
210 "mkrsrc for cs failed\n");
211 return (-1);
212 }
213
214 if ((csnode = topo_node_bind(mod, chnode, CS_NODE_NAME,
215 cs, fmri)) == NULL) {
216 nvlist_free(fmri);
217 whinge(mod, NULL, "amd_generic_mc_create: "
218 "bind for cs failed\n");
219 return (-1);
220 }
221
222 /*
223 * Dynamic ASRU for page faults within a chip-select.
224 * The topology does not represent pages (there are
225 * too many) so when a page is faulted we generate
226 * an ASRU to represent the individual page.
227 * If SMBIOS meets FMA needs, derive labels & serials
228 * for DIMMS and apply to chip-select nodes.
229 * If deriving from SMBIOS, skip IPMI
230 */
231 if (FM_AWARE_SMBIOS(mod)) {
232 if (topo_method_register(mod, csnode,
233 x86pi_gen_cs_methods) < 0)
234 whinge(mod, NULL,
235 "amd_generic_mc_create: "
236 "method registration failed\n");
237 } else {
238 if (topo_method_register(mod, csnode,
239 gen_cs_methods) < 0)
240 whinge(mod, NULL,
241 "amd_generic_mc_create: method"
242 "registration failed\n");
243 }
244
245 (void) topo_node_asru_set(csnode, fmri,
246 TOPO_ASRU_COMPUTE, &err);
247 nvlist_free(fmri);
248
249 /*
250 * If SMBIOS meets FMA needs, set DIMM as the FRU for
251 * the chip-select node. Use the channel & chip-select
252 * numbers to get the DIMM instance.
253 * Send via inst : dram channel number
254 * Receive via inst : dimm instance
255 */
256 if (FM_AWARE_SMBIOS(mod)) {
257 int inst;
258 id_t dimm_smbid;
259 const char *serial;
260 const char *part;
261 const char *rev;
262 char *label;
263
264 (void) topo_pgroup_create(csnode,
265 &cs_pgroup, &err);
266 inst = chan;
267 dimm_smbid = memnode_to_smbiosid(mod, smbid,
268 CS_NODE_NAME, cs, &inst);
269 serial = chip_serial_smbios_get(mod,
270 dimm_smbid);
271 part = chip_part_smbios_get(mod,
272 dimm_smbid);
273 rev = chip_rev_smbios_get(mod, dimm_smbid);
274 label = (char *)chip_label_smbios_get(mod,
275 chnode, dimm_smbid, NULL);
276
277 (void) topo_prop_set_string(csnode, PGNAME(CS),
278 FM_FMRI_HC_SERIAL_ID, TOPO_PROP_IMMUTABLE,
279 serial, &err);
280 (void) topo_prop_set_string(csnode, PGNAME(CS),
281 FM_FMRI_HC_PART, TOPO_PROP_IMMUTABLE,
282 part, &err);
283 (void) topo_prop_set_string(csnode, PGNAME(CS),
284 FM_FMRI_HC_REVISION, TOPO_PROP_IMMUTABLE,
285 rev, &err);
286
287 /*
288 * We apply DIMM labels to chip-select nodes,
289 * FRU for chip-selects should be DIMMs, and
290 * we do not derive dimm nodes for Family 0x10
291 * so FRU fmri is NULL, but FRU Labels are set,
292 * the FRU labels point to the DIMM.
293 */
294 (void) topo_node_label_set(csnode, label, &err);
295 topo_mod_strfree(mod, label);
296 }
297 }
298 }
299
300 return (0);
301 }
302
303 static nvlist_t *
amd_lookup_by_mcid(topo_mod_t * mod,topo_instance_t id)304 amd_lookup_by_mcid(topo_mod_t *mod, topo_instance_t id)
305 {
306 mc_snapshot_info_t mcs;
307 void *buf = NULL;
308 uint8_t ver;
309
310 nvlist_t *nvl = NULL;
311 char path[64];
312 int fd, err;
313
314 (void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id);
315 fd = open(path, O_RDONLY);
316
317 if (fd == -1) {
318 /*
319 * Some v20z and v40z systems may have had the 3rd-party
320 * NWSnps packagae installed which installs a /dev/mc
321 * link. So try again via /devices.
322 */
323 (void) snprintf(path, sizeof (path),
324 "/devices/pci@0,0/pci1022,1102@%x,2:mc-amd",
325 MC_AMD_DEV_OFFSET + id);
326 fd = open(path, O_RDONLY);
327 }
328
329 if (fd == -1)
330 return (NULL); /* do not whinge */
331
332 if (ioctl(fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 ||
333 (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL ||
334 ioctl(fd, MC_IOC_SNAPSHOT, buf) == -1) {
335
336 whinge(mod, NULL, "mc failed to snapshot %s: %s\n",
337 path, strerror(errno));
338
339 free(buf);
340 (void) close(fd);
341 return (NULL);
342 }
343
344 (void) close(fd);
345 err = nvlist_unpack(buf, mcs.mcs_size, &nvl, 0);
346 topo_mod_free(mod, buf, mcs.mcs_size);
347
348 if (nvlist_lookup_uint8(nvl, MC_NVLIST_VERSTR, &ver) != 0) {
349 whinge(mod, NULL, "mc nvlist is not versioned\n");
350 nvlist_free(nvl);
351 return (NULL);
352 } else if (ver != MC_NVLIST_VERS1) {
353 whinge(mod, NULL, "mc nvlist version mismatch\n");
354 nvlist_free(nvl);
355 return (NULL);
356 }
357
358 return (err ? NULL : nvl);
359 }
360
361 int
amd_rank_create(topo_mod_t * mod,tnode_t * pnode,nvlist_t * dimmnvl,nvlist_t * auth)362 amd_rank_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *dimmnvl,
363 nvlist_t *auth)
364 {
365 uint64_t *csnumarr;
366 char **csnamearr;
367 uint_t ncs, ncsname;
368 tnode_t *ranknode;
369 nvlist_t *fmri, *pfmri = NULL;
370 uint64_t dsz, rsz;
371 int nerr = 0;
372 int err;
373 int i;
374
375 if (nvlist_lookup_uint64_array(dimmnvl, "csnums", &csnumarr,
376 &ncs) != 0 || nvlist_lookup_string_array(dimmnvl, "csnames",
377 &csnamearr, &ncsname) != 0 || ncs != ncsname) {
378 whinge(mod, &nerr, "amd_rank_create: "
379 "csnums/csnames extraction failed\n");
380 return (nerr);
381 }
382
383 if (topo_node_resource(pnode, &pfmri, &err) < 0) {
384 whinge(mod, &nerr, "amd_rank_create: parent fmri lookup "
385 "failed\n");
386 return (nerr);
387 }
388
389 if (topo_node_range_create(mod, pnode, RANK_NODE_NAME, 0, ncs) < 0) {
390 whinge(mod, &nerr, "amd_rank_create: range create failed\n");
391 nvlist_free(pfmri);
392 return (nerr);
393 }
394
395 if (topo_prop_get_uint64(pnode, PGNAME(DIMM), "size", &dsz,
396 &err) == 0) {
397 rsz = dsz / ncs;
398 } else {
399 whinge(mod, &nerr, "amd_rank_create: parent dimm has no "
400 "size\n");
401 return (nerr);
402 }
403
404 for (i = 0; i < ncs; i++) {
405 if (mkrsrc(mod, pnode, RANK_NODE_NAME, i, auth, &fmri) < 0) {
406 whinge(mod, &nerr, "amd_rank_create: mkrsrc failed\n");
407 continue;
408 }
409
410 if ((ranknode = topo_node_bind(mod, pnode, RANK_NODE_NAME, i,
411 fmri)) == NULL) {
412 nvlist_free(fmri);
413 whinge(mod, &nerr, "amd_rank_create: node bind "
414 "failed\n");
415 continue;
416 }
417
418 nvlist_free(fmri);
419 if (FM_AWARE_SMBIOS(mod))
420 (void) topo_node_fru_set(ranknode, NULL, 0, &err);
421 else
422 (void) topo_node_fru_set(ranknode, pfmri, 0, &err);
423
424 /*
425 * If a rank is faulted the asru is the associated
426 * chip-select, but if a page within a rank is faulted
427 * the asru is just that page. Hence the dual preconstructed
428 * and computed ASRU.
429 */
430 if (topo_method_register(mod, ranknode, rank_methods) < 0)
431 whinge(mod, &nerr, "amd_rank_create: "
432 "topo_method_register failed");
433
434 if (! is_xpv() && topo_method_register(mod, ranknode,
435 ntv_page_retire_methods) < 0)
436 whinge(mod, &nerr, "amd_rank_create: "
437 "topo_method_register failed");
438
439 (void) topo_node_asru_set(ranknode, cs_fmri[csnumarr[i]],
440 TOPO_ASRU_COMPUTE, &err);
441
442 (void) topo_pgroup_create(ranknode, &rank_pgroup, &err);
443
444 (void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "size",
445 TOPO_PROP_IMMUTABLE, rsz, &err);
446
447 (void) topo_prop_set_string(ranknode, PGNAME(RANK), "csname",
448 TOPO_PROP_IMMUTABLE, csnamearr[i], &err);
449
450 (void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "csnum",
451 TOPO_PROP_IMMUTABLE, csnumarr[i], &err);
452 }
453
454 nvlist_free(pfmri);
455
456 return (nerr);
457 }
458
459 static int
amd_dimm_create(topo_mod_t * mod,uint16_t chip_smbid,tnode_t * pnode,const char * name,nvlist_t * mc,nvlist_t * auth)460 amd_dimm_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
461 const char *name, nvlist_t *mc, nvlist_t *auth)
462 {
463 int i, err, nerr = 0;
464 int perr = 0;
465 nvpair_t *nvp;
466 tnode_t *dimmnode;
467 nvlist_t *fmri, **dimmarr = NULL;
468 uint64_t num;
469 uint_t ndimm;
470 id_t smbid;
471 const char *serial;
472 const char *part;
473 const char *rev;
474
475 if (nvlist_lookup_nvlist_array(mc, "dimmlist", &dimmarr, &ndimm) != 0) {
476 whinge(mod, NULL, "amd_dimm_create: dimmlist lookup failed\n");
477 return (-1);
478 }
479
480 if (ndimm == 0)
481 return (0); /* no dimms present on this node */
482
483 if (topo_node_range_create(mod, pnode, name, 0, MAX_DIMMNUM) < 0) {
484 whinge(mod, NULL, "amd_dimm_create: range create failed\n");
485 return (-1);
486 }
487
488 for (i = 0; i < ndimm; i++) {
489 if (nvlist_lookup_uint64(dimmarr[i], "num", &num) != 0) {
490 whinge(mod, &nerr, "amd_dimm_create: dimm num property "
491 "missing\n");
492 continue;
493 }
494
495 if (mkrsrc(mod, pnode, name, num, auth, &fmri) < 0) {
496 whinge(mod, &nerr, "amd_dimm_create: mkrsrc failed\n");
497 continue;
498 }
499 if (FM_AWARE_SMBIOS(mod)) {
500 smbid = memnode_to_smbiosid(mod, chip_smbid,
501 DIMM_NODE_NAME, i, NULL);
502 serial = chip_serial_smbios_get(mod, smbid);
503 part = chip_part_smbios_get(mod, smbid);
504 rev = chip_rev_smbios_get(mod, smbid);
505 perr += nvlist_add_string(fmri, FM_FMRI_HC_SERIAL_ID,
506 serial);
507 perr += nvlist_add_string(fmri, FM_FMRI_HC_PART,
508 part);
509 perr += nvlist_add_string(fmri, FM_FMRI_HC_REVISION,
510 rev);
511
512 if (perr != 0)
513 whinge(mod, NULL, "amd_dimm_create:"
514 "nvlist_add_string failed\n");
515 }
516
517 if ((dimmnode = topo_node_bind(mod, pnode, name, num, fmri))
518 == NULL) {
519 nvlist_free(fmri);
520 whinge(mod, &nerr, "amd_dimm_create: node bind "
521 "failed\n");
522 continue;
523 }
524
525 if (!FM_AWARE_SMBIOS(mod))
526 if (topo_method_register(mod,
527 dimmnode, dimm_methods) < 0)
528 whinge(mod, &nerr, "amd_dimm_create: "
529 "topo_method_register failed");
530
531 (void) topo_pgroup_create(dimmnode, &dimm_pgroup, &err);
532
533 if (FM_AWARE_SMBIOS(mod)) {
534 char *label;
535
536 nvlist_free(fmri);
537 (void) topo_node_resource(dimmnode,
538 &fmri, &err);
539
540 label = (char *)chip_label_smbios_get(mod,
541 pnode, smbid, NULL);
542 if (topo_node_label_set(dimmnode, label,
543 &perr) == -1)
544 topo_mod_dprintf(mod, "Failed"
545 "to set label\n");
546 topo_mod_strfree(mod, label);
547
548 (void) topo_prop_set_string(dimmnode, PGNAME(DIMM),
549 FM_FMRI_HC_SERIAL_ID, TOPO_PROP_IMMUTABLE,
550 serial, &err);
551 (void) topo_prop_set_string(dimmnode, PGNAME(DIMM),
552 FM_FMRI_HC_PART, TOPO_PROP_IMMUTABLE,
553 part, &err);
554 (void) topo_prop_set_string(dimmnode, PGNAME(DIMM),
555 FM_FMRI_HC_REVISION, TOPO_PROP_IMMUTABLE,
556 rev, &err);
557 }
558
559 (void) topo_node_asru_set(dimmnode, fmri, 0, &err);
560 (void) topo_node_fru_set(dimmnode, fmri, 0, &err);
561 nvlist_free(fmri);
562
563 for (nvp = nvlist_next_nvpair(dimmarr[i], NULL); nvp != NULL;
564 nvp = nvlist_next_nvpair(dimmarr[i], nvp)) {
565 if (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY &&
566 strcmp(nvpair_name(nvp), "csnums") == 0 ||
567 nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY &&
568 strcmp(nvpair_name(nvp), "csnames") == 0)
569 continue; /* used in amd_rank_create() */
570
571 nerr += nvprop_add(mod, nvp, PGNAME(DIMM), dimmnode);
572 }
573
574 nerr += amd_rank_create(mod, dimmnode, dimmarr[i], auth);
575 }
576
577 return (nerr == 0 ? 0 : -1);
578 }
579
580 static int
amd_cs_create(topo_mod_t * mod,tnode_t * pnode,const char * name,nvlist_t * mc,nvlist_t * auth)581 amd_cs_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc,
582 nvlist_t *auth)
583 {
584 int i, err, nerr = 0;
585 nvpair_t *nvp;
586 tnode_t *csnode;
587 nvlist_t *fmri, **csarr = NULL;
588 uint64_t csnum;
589 uint_t ncs;
590
591 if (nvlist_lookup_nvlist_array(mc, "cslist", &csarr, &ncs) != 0)
592 return (-1);
593
594 if (ncs == 0)
595 return (0); /* no chip-selects configured on this node */
596
597 if (topo_node_range_create(mod, pnode, name, 0, MAX_CSNUM) < 0)
598 return (-1);
599
600 for (i = 0; i < ncs; i++) {
601 if (nvlist_lookup_uint64(csarr[i], "num", &csnum) != 0) {
602 whinge(mod, &nerr, "amd_cs_create: cs num property "
603 "missing\n");
604 continue;
605 }
606
607 if (mkrsrc(mod, pnode, name, csnum, auth, &fmri) != 0) {
608 whinge(mod, &nerr, "amd_cs_create: mkrsrc failed\n");
609 continue;
610 }
611
612 if ((csnode = topo_node_bind(mod, pnode, name, csnum, fmri))
613 == NULL) {
614 nvlist_free(fmri);
615 whinge(mod, &nerr, "amd_cs_create: node bind failed\n");
616 continue;
617 }
618
619 cs_fmri[csnum] = fmri; /* nvlist will be freed in mc_create */
620
621 (void) topo_node_asru_set(csnode, fmri, 0, &err);
622
623 (void) topo_node_fru_set(csnode, fmri, 0, &err);
624
625 (void) topo_pgroup_create(csnode, &cs_pgroup, &err);
626
627 for (nvp = nvlist_next_nvpair(csarr[i], NULL); nvp != NULL;
628 nvp = nvlist_next_nvpair(csarr[i], nvp)) {
629 nerr += nvprop_add(mod, nvp, PGNAME(CS), csnode);
630 }
631 }
632
633 return (nerr == 0 ? 0 : -1);
634 }
635
636 static int
amd_dramchan_create(topo_mod_t * mod,tnode_t * pnode,const char * name,nvlist_t * auth)637 amd_dramchan_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
638 nvlist_t *auth)
639 {
640 tnode_t *chnode;
641 nvlist_t *fmri;
642 char *socket;
643 int i, nchan;
644 nvlist_t *pfmri = NULL;
645 int err, nerr = 0;
646
647 /*
648 * We will enumerate the number of channels present even if only
649 * channel A is in use (i.e., running in 64-bit mode). Only
650 * the socket 754 package has a single channel.
651 */
652 if (topo_prop_get_string(pnode, PGNAME(MCT), "socket",
653 &socket, &err) == 0 && strcmp(socket, "Socket 754") == 0)
654 nchan = 1;
655 else
656 nchan = 2;
657
658 topo_mod_strfree(mod, socket);
659
660 if (topo_node_range_create(mod, pnode, name, 0, nchan - 1) < 0)
661 return (-1);
662
663 (void) topo_node_fru(pnode, &pfmri, NULL, &err);
664
665 for (i = 0; i < nchan; i++) {
666 if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) {
667 whinge(mod, &nerr, "amd_dramchan_create: mkrsrc "
668 "failed\n");
669 continue;
670 }
671
672 if ((chnode = topo_node_bind(mod, pnode, name, i, fmri))
673 == NULL) {
674 nvlist_free(fmri);
675 whinge(mod, &nerr, "amd_dramchan_create: node bind "
676 "failed\n");
677 continue;
678 }
679
680 (void) topo_node_asru_set(chnode, fmri, 0, &err);
681 if (pfmri)
682 (void) topo_node_fru_set(chnode, pfmri, 0, &err);
683
684 nvlist_free(fmri);
685
686 (void) topo_pgroup_create(chnode, &chan_pgroup, &err);
687
688 (void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel",
689 TOPO_PROP_IMMUTABLE, i == 0 ? "A" : "B", &err);
690 }
691 nvlist_free(pfmri);
692
693 return (nerr == 0 ? 0 : -1);
694 }
695
696 static int
amd_htconfig(topo_mod_t * mod,tnode_t * cnode,nvlist_t * htnvl)697 amd_htconfig(topo_mod_t *mod, tnode_t *cnode, nvlist_t *htnvl)
698 {
699 nvpair_t *nvp;
700 int nerr = 0;
701
702 if (strcmp(topo_node_name(cnode), CHIP_NODE_NAME) != 0) {
703 whinge(mod, &nerr, "amd_htconfig: must pass a chip node!");
704 return (-1);
705 }
706
707 for (nvp = nvlist_next_nvpair(htnvl, NULL); nvp != NULL;
708 nvp = nvlist_next_nvpair(htnvl, nvp)) {
709 if (nvprop_add(mod, nvp, PGNAME(CHIP), cnode) != 0)
710 nerr++;
711 }
712
713 return (nerr == 0 ? 0 : -1);
714 }
715
716 void
amd_mc_create(topo_mod_t * mod,uint16_t smbid,tnode_t * pnode,const char * name,nvlist_t * auth,int32_t procnodeid,int32_t procnodes_per_pkg,int family,int model,int * nerrp)717 amd_mc_create(topo_mod_t *mod, uint16_t smbid, tnode_t *pnode,
718 const char *name, nvlist_t *auth, int32_t procnodeid,
719 int32_t procnodes_per_pkg, int family,
720 int model, int *nerrp)
721 {
722 tnode_t *mcnode;
723 nvlist_t *rfmri, *fmri;
724 nvpair_t *nvp;
725 nvlist_t *mc = NULL;
726 int i, err;
727 int mcnum = procnodeid % procnodes_per_pkg;
728 char *serial = NULL;
729 char *part = NULL;
730 char *rev = NULL;
731
732 /*
733 * Return with no error for anything before AMD family 0xf - we
734 * won't generate even a generic memory topology for earlier
735 * families.
736 */
737 if (family < 0xf)
738 return;
739
740 if (topo_node_lookup(pnode, name, mcnum) != NULL)
741 return;
742
743 if (FM_AWARE_SMBIOS(mod)) {
744 (void) topo_node_resource(pnode, &rfmri, &err);
745 (void) nvlist_lookup_string(rfmri, "serial", &serial);
746 (void) nvlist_lookup_string(rfmri, "part", &part);
747 (void) nvlist_lookup_string(rfmri, "revision", &rev);
748 }
749
750 if (mkrsrc(mod, pnode, name, mcnum, auth, &fmri) != 0) {
751 if (FM_AWARE_SMBIOS(mod))
752 nvlist_free(rfmri);
753 whinge(mod, nerrp, "mc_create: mkrsrc failed\n");
754 return;
755 }
756
757 if (FM_AWARE_SMBIOS(mod)) {
758 (void) nvlist_add_string(fmri, "serial", serial);
759 (void) nvlist_add_string(fmri, "part", part);
760 (void) nvlist_add_string(fmri, "revision", rev);
761 nvlist_free(rfmri);
762 }
763
764 if ((mcnode = topo_node_bind(mod, pnode, name, mcnum,
765 fmri)) == NULL) {
766 nvlist_free(fmri);
767 whinge(mod, nerrp, "mc_create: mc bind failed\n");
768 return;
769 }
770 if (topo_node_fru_set(mcnode, NULL, 0, &err) < 0)
771 whinge(mod, nerrp, "mc_create: topo_node_fru_set failed\n");
772
773 if (FM_AWARE_SMBIOS(mod)) {
774 if (topo_node_label_set(mcnode, NULL, &err) == -1)
775 topo_mod_dprintf(mod, "Failed to set label\n");
776 }
777
778 nvlist_free(fmri);
779
780 if (topo_pgroup_create(mcnode, &mc_pgroup, &err) < 0)
781 whinge(mod, nerrp, "mc_create: topo_pgroup_create failed\n");
782
783 if (topo_prop_set_int32(mcnode, PGNAME(MCT), MCT_PROCNODE_ID,
784 TOPO_PROP_IMMUTABLE, procnodeid, nerrp) != 0)
785 whinge(mod, nerrp, "mc_create: topo_prop_set_int32 failed to"
786 "add node id\n");
787
788 if ((mc = amd_lookup_by_mcid(mod, topo_node_instance(pnode))) == NULL) {
789 /*
790 * If a memory-controller driver exists for this chip model
791 * it has not attached or has otherwise malfunctioned;
792 * alternatively no memory-controller driver exists for this
793 * (presumably newly-released) cpu model. We fallback to
794 * creating a generic maximal topology.
795 */
796 if (amd_generic_mc_create(mod, smbid, pnode, mcnode,
797 family, model, auth) != 0)
798 whinge(mod, nerrp,
799 "mc_create: amd_generic_mc_create failed\n");
800 return;
801 }
802
803 /*
804 * Add memory controller properties
805 */
806 for (nvp = nvlist_next_nvpair(mc, NULL); nvp != NULL;
807 nvp = nvlist_next_nvpair(mc, nvp)) {
808 char *name = nvpair_name(nvp);
809 data_type_t type = nvpair_type(nvp);
810
811 if (type == DATA_TYPE_NVLIST_ARRAY &&
812 (strcmp(name, "cslist") == 0 ||
813 strcmp(name, "dimmlist") == 0)) {
814 continue;
815 } else if (type == DATA_TYPE_UINT8 &&
816 strcmp(name, MC_NVLIST_VERSTR) == 0) {
817 continue;
818 } else if (type == DATA_TYPE_NVLIST &&
819 strcmp(name, "htconfig") == 0) {
820 nvlist_t *htnvl;
821
822 (void) nvpair_value_nvlist(nvp, &htnvl);
823 if (amd_htconfig(mod, pnode, htnvl) != 0)
824 whinge(mod, nerrp,
825 "mc_create: amd_htconfig failed\n");
826 } else {
827 if (nvprop_add(mod, nvp, PGNAME(MCT), mcnode) != 0)
828 whinge(mod, nerrp,
829 "mc_create: nvprop_add failed\n");
830 }
831 }
832
833 if (amd_dramchan_create(mod, mcnode, CHAN_NODE_NAME, auth) != 0 ||
834 amd_cs_create(mod, mcnode, CS_NODE_NAME, mc, auth) != 0 ||
835 amd_dimm_create(mod, smbid, mcnode, DIMM_NODE_NAME, mc, auth) != 0)
836 whinge(mod, nerrp, "mc_create: create children failed\n");
837
838 /*
839 * Free the fmris for the chip-selects allocated in amd_cs_create
840 */
841 for (i = 0; i < MC_CHIP_NCS; i++) {
842 if (cs_fmri[i] != NULL) {
843 nvlist_free(cs_fmri[i]);
844 cs_fmri[i] = NULL;
845 }
846 }
847
848 nvlist_free(mc);
849 }
850