xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/dimm/topo_dimm.c (revision 92101ea43dccf9afc9af34c02a30de381d3fe66a)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2023 Oxide Computer Company
14  */
15 
16 /*
17  * This implements common DIMM creation for the hc tree. Currently this is based
18  * primarily on providing SPD data.
19  */
20 
21 #include <sys/fm/protocol.h>
22 #include <fm/topo_mod.h>
23 #include <libjedec.h>
24 #include <string.h>
25 #include <stdbool.h>
26 
27 #include "topo_dimm.h"
28 
29 typedef struct {
30 	uint32_t sc_dram_type;
31 	uint32_t sc_mod_type;
32 	const char *sc_dram_str;
33 	const char *sc_mod_str;
34 	bool sc_asym;
35 	uint32_t sc_nranks;
36 	uint32_t sc_even_ranks;
37 	uint32_t sc_odd_ranks;
38 	uint32_t sc_data_bits;
39 	uint32_t sc_ecc_bits;
40 	uint32_t sc_nsubchan;
41 	uint32_t sc_pkg_sl[2];
42 	uint32_t sc_pkg_ndie[2];
43 	uint64_t sc_die_size[2];
44 	uint32_t sc_dram_width[2];
45 	uint32_t sc_nrows[2];
46 	uint32_t sc_ncols[2];
47 	uint32_t sc_nbank_bits[2];
48 	uint32_t sc_nbgrp_bits[2];
49 	uint32_t sc_vdd;
50 	uint32_t sc_devices;
51 } spd_cache_t;
52 
53 static const topo_pgroup_info_t topo_dimm_pgroup = {
54 	TOPO_PGROUP_DIMM_PROPS,
55 	TOPO_STABILITY_PRIVATE,
56 	TOPO_STABILITY_PRIVATE,
57 	1
58 };
59 
60 static const topo_pgroup_info_t topo_dimm_comps_pgroup = {
61 	TOPO_PGROUP_DIMM_COMPONENTS,
62 	TOPO_STABILITY_PRIVATE,
63 	TOPO_STABILITY_PRIVATE,
64 	1
65 };
66 
67 /*
68  * Translate a subset of the DDR types that we're likely to support into the
69  * corresponding current DDR information. We only really support taking these
70  * apart, so that's OK.
71  */
72 static const char *
73 topo_dimm_dram_type2str(spd_dram_type_t type)
74 {
75 	switch (type) {
76 	case SPD_DT_DDR4_SDRAM:
77 		return (TOPO_DIMM_TYPE_DDR4);
78 	case SPD_DT_LPDDR4_SDRAM:
79 		return (TOPO_DIMM_TYPE_LPDDR4);
80 	case SPD_DT_DDR5_SDRAM:
81 		return (TOPO_DIMM_TYPE_DDR5);
82 	case SPD_DT_LPDDR5_SDRAM:
83 		return (TOPO_DIMM_TYPE_LPDDR5);
84 	default:
85 		return (NULL);
86 	}
87 
88 }
89 
90 /*
91  * Various string functions for different component types.
92  */
93 static const char *
94 topo_dimm_temp2str(uint32_t val)
95 {
96 	switch (val) {
97 	case SPD_TEMP_T_TSE2002:
98 		return ("TSE2002");
99 	case SPD_TEMP_T_TSE2004av:
100 		return ("TSE2004av");
101 	case SPD_TEMP_T_TS5111:
102 		return ("TS5111");
103 	case SPD_TEMP_T_TS5110:
104 		return ("TS5110");
105 	default:
106 		return ("unknown");
107 	}
108 }
109 
110 static const char *
111 topo_dimm_pmic2str(uint32_t val)
112 {
113 	switch (val) {
114 	case SPD_PMIC_T_PMIC5000:
115 		return ("PMIC5000");
116 	case SPD_PMIC_T_PMIC5010:
117 		return ("PMIC5010");
118 	case SPD_PMIC_T_PMIC5100:
119 		return ("PMIC5100");
120 	default:
121 		return ("unknown");
122 	}
123 }
124 
125 static const char *
126 topo_dimm_cd2str(uint32_t val)
127 {
128 	switch (val) {
129 	case SPD_CD_T_DDR5CK01:
130 		return ("DDR5CK01");
131 	default:
132 		return ("unknown");
133 	}
134 }
135 
136 static const char *
137 topo_dimm_rcd2str(uint32_t val)
138 {
139 	switch (val) {
140 	case SPD_RCD_T_SSTE32882:
141 		return ("SSTE32882");
142 	case SPD_RCD_T_DDR4RCD01:
143 		return ("DDR4RCD01");
144 	case SPD_RCD_T_DDR4RCD02:
145 		return ("DDR4RCD02");
146 	case SPD_RCD_T_DDR5RCD01:
147 		return ("DDR5RCD01");
148 	case SPD_RCD_T_DDR5RCD02:
149 		return ("DDR5RCD02");
150 	case SPD_RCD_T_DDR5RCD03:
151 		return ("DDR5RCD03");
152 	default:
153 		return ("unknown");
154 	}
155 }
156 
157 static const char *
158 topo_dimm_db2str(uint32_t val)
159 {
160 	switch (val) {
161 	case SPD_DB_T_DDR4DB01:
162 		return ("DDR4DB01");
163 	case SPD_DB_T_DDR4DB02:
164 		return ("DDR4DB02");
165 	case SPD_DB_T_DDR5DB01:
166 		return ("DDR5DB01");
167 	case SPD_DB_T_DDR5DB02:
168 		return ("DDR5DB02");
169 	default:
170 		return ("unknown");
171 	}
172 }
173 
174 static const char *
175 topo_dimm_mrcd2str(uint32_t val)
176 {
177 	switch (val) {
178 	case SPD_MRCD_T_DDR5MRCD01:
179 		return ("DDR5MRCD01");
180 	default:
181 		return ("unknown");
182 	}
183 }
184 
185 static const char *
186 topo_dimm_mdb2str(uint32_t val)
187 {
188 	switch (val) {
189 	case SPD_MDB_T_DDR5MDB01:
190 		return ("DDR5MDB01");
191 	default:
192 		return ("unknown");
193 	}
194 }
195 
196 static const char *
197 topo_dimm_dmb2str(uint32_t val)
198 {
199 	switch (val) {
200 	case SPD_DMB_T_DMB5011:
201 		return ("DMB5011");
202 	default:
203 		return ("unknown");
204 	}
205 }
206 
207 static const char *
208 topo_dimm_spd2str(uint32_t val)
209 {
210 	switch (val) {
211 	case SPD_SPD_T_EE1004:
212 		return ("EE1004");
213 	case SPD_SPD_T_SPD5118:
214 		return ("SPD5118");
215 	case SPD_SPD_T_ESPD5216:
216 		return ("ESPD5216");
217 	default:
218 		return ("unknown");
219 	}
220 }
221 
222 /*
223  * DDR4 and DDR5 have a fixed voltage. DDR3 had a range of voltages that could
224  * be selected. In addition, LPDDR4 and LPDDR5 depend on the specifics of the
225  * memory controller as they allow for variable options here.
226  */
227 static uint32_t
228 topo_dimm_mod_vdd(spd_dram_type_t type)
229 {
230 	switch (type) {
231 	case SPD_DT_DDR4_SDRAM:
232 		return (1200);
233 	case SPD_DT_DDR5_SDRAM:
234 		return (1100);
235 	default:
236 		return (0);
237 	}
238 }
239 
240 static const char *
241 topo_dimm_mod_type2str(spd_module_type_t type)
242 {
243 	switch (type) {
244 	case SPD_MOD_TYPE_RDIMM:
245 		return ("RDIMM");
246 	case SPD_MOD_TYPE_UDIMM:
247 		return ("UDIMM");
248 	case SPD_MOD_TYPE_SODIMM:
249 		return ("SO-DIMM");
250 	case SPD_MOD_TYPE_LRDIMM:
251 		return ("LRDIMM");
252 	case SPD_MOD_TYPE_MRDIMM:
253 		return ("MRDIMM");
254 	case SPD_MOD_TYPE_DDIMM:
255 		return ("DDIMM");
256 	case SPD_MOD_TYPE_SOLDER:
257 		return ("solder-down");
258 	case SPD_MOD_TYPE_MINI_RDIMM:
259 		return ("Mini-RDIMM");
260 	case SPD_MOD_TYPE_MINI_UDIMM:
261 		return ("Mini-UDIMM");
262 	case SPD_MOD_TYPE_72b_SO_RDIMM:
263 		return ("72b-SO-RDIMM");
264 	case SPD_MOD_TYPE_72b_SO_UDIMM:
265 		return ("72b-SO-UDIMM");
266 	case SPD_MOD_TYPE_16b_SO_DIMM:
267 		return ("16b-SO-DIMM");
268 	case SPD_MOD_TYPE_32b_SO_DIMM:
269 		return ("32b-SO-DIMM");
270 	default:
271 		return (NULL);
272 	}
273 }
274 
275 /*
276  * Go through and cache common properties that we would look up in the NVL into
277  * a structure. We do this once and then reuse this for common settings. We
278  * don't generally include PN/SN/Rev information in here since not having that
279  * is OK and we can still create nodes and due to the fact that we generally
280  * only use it once.
281  */
282 static bool
283 topo_dimm_cache_spd(topo_mod_t *mod, nvlist_t *spd, spd_cache_t *cache)
284 {
285 	/*
286 	 * First go through and look up values that we expect to always be
287 	 * present.
288 	 */
289 	if (nvlist_lookup_pairs(spd, 0,
290 	    SPD_KEY_MOD_TYPE, DATA_TYPE_UINT32, &cache->sc_mod_type,
291 	    SPD_KEY_NRANKS, DATA_TYPE_UINT32, &cache->sc_nranks,
292 	    SPD_KEY_NSUBCHAN, DATA_TYPE_UINT32, &cache->sc_nsubchan,
293 	    SPD_KEY_DATA_WIDTH, DATA_TYPE_UINT32, &cache->sc_data_bits,
294 	    SPD_KEY_ECC_WIDTH, DATA_TYPE_UINT32, &cache->sc_ecc_bits,
295 	    SPD_KEY_NBANK_BITS, DATA_TYPE_UINT32, &cache->sc_nbank_bits[0],
296 	    SPD_KEY_NBGRP_BITS, DATA_TYPE_UINT32, &cache->sc_nbgrp_bits[0],
297 	    SPD_KEY_NROW_BITS, DATA_TYPE_UINT32, &cache->sc_nrows[0],
298 	    SPD_KEY_NCOL_BITS, DATA_TYPE_UINT32, &cache->sc_ncols[0],
299 
300 	    SPD_KEY_PKG_SL, DATA_TYPE_UINT32, &cache->sc_pkg_sl[0],
301 	    SPD_KEY_PKG_NDIE, DATA_TYPE_UINT32, &cache->sc_pkg_ndie[0],
302 	    SPD_KEY_DRAM_WIDTH, DATA_TYPE_UINT32, &cache->sc_dram_width[0],
303 	    SPD_KEY_DIE_SIZE, DATA_TYPE_UINT64, &cache->sc_die_size[0],
304 	    SPD_KEY_DEVS, DATA_TYPE_UINT32, &cache->sc_devices,
305 	    NULL) != 0) {
306 		topo_mod_dprintf(mod, "failed to find expected primary SPD "
307 		    "keys");
308 		return (false);
309 	}
310 
311 	/*
312 	 * Set information that should be valid based on the types that we
313 	 * support right now.
314 	 */
315 	cache->sc_dram_str = topo_dimm_dram_type2str(cache->sc_dram_type);
316 	cache->sc_mod_str = topo_dimm_mod_type2str(cache->sc_mod_type);
317 	cache->sc_vdd = topo_dimm_mod_vdd(cache->sc_dram_type);
318 
319 	/*
320 	 * Next we have keys that may or may not be present.
321 	 */
322 	cache->sc_asym = nvlist_lookup_boolean(spd, SPD_KEY_RANK_ASYM) == 0;
323 
324 	if (!cache->sc_asym)
325 		return (true);
326 
327 	cache->sc_even_ranks = cache->sc_odd_ranks = cache->sc_nranks / 2;
328 	if (cache->sc_nranks % 2 == 1)
329 		cache->sc_even_ranks++;
330 
331 	/*
332 	 * Now go through and look up keys that we believe should always be
333 	 * present given that we have an asymmetric configuration.
334 	 */
335 	if (nvlist_lookup_pairs(spd, 0,
336 	    SPD_KEY_SEC_NBANK_BITS, DATA_TYPE_UINT32, &cache->sc_nbank_bits[1],
337 	    SPD_KEY_SEC_NBGRP_BITS, DATA_TYPE_UINT32, &cache->sc_nbgrp_bits[1],
338 	    SPD_KEY_SEC_NROW_BITS, DATA_TYPE_UINT32, &cache->sc_nrows[1],
339 	    SPD_KEY_SEC_NCOL_BITS, DATA_TYPE_UINT32, &cache->sc_ncols[1],
340 	    SPD_KEY_SEC_PKG_SL, DATA_TYPE_UINT32, &cache->sc_pkg_sl[1],
341 	    SPD_KEY_SEC_PKG_NDIE, DATA_TYPE_UINT32, &cache->sc_pkg_ndie[1],
342 	    SPD_KEY_SEC_DRAM_WIDTH, DATA_TYPE_UINT32, &cache->sc_dram_width[1],
343 	    SPD_KEY_SEC_DIE_SIZE, DATA_TYPE_UINT32, &cache->sc_die_size[1],
344 	    NULL) != 0) {
345 		topo_mod_dprintf(mod, "failed to get secondary keys for SPD "
346 		    "size calculation");
347 		return (false);
348 	}
349 
350 	return (true);
351 }
352 
353 /*
354  * Calculating the size here is a little nuanced. The rough formula is provided
355  * by JEDEC in the various SPD Annexes. The rough formula is:
356  *
357  * (SDRAM Capacity / 8) * (Bus width / SDRAM width) * Logical ranks
358  *
359  * Phrased in terms of SPD macros this is really:
360  *
361  * SPD_KEY_DIE_SIZE / 8 * (SPD_KEY_DATA_WIDTH / SPD_KEY_DRAM_WIDTH) * Logical
362  * Ranks
363  *
364  * The DIMM operates in chunks that are equal to its data width multiplied by
365  * the number of sub-channels. In general for DDR4/5 this is always going to be
366  * 64-bits or 8 bytes. The ECC is not included in this. The SDRAM width is
367  * fairly straightforward. The logical ranks depends on the die type and the
368  * number of actual ranks present. This is basically SPD_KEY_PKG_NDIE *
369  * SPD_KEY_NRANKS.
370  *
371  * However, there are two small wrinkles: the calculation of logical ranks and
372  * asymmetrical modules. With asymmetrical modules the data width doesn't
373  * change, the capacity and SDRAM width may change. In addition, calculating
374  * logical ranks is a bit nuanced here. First, each module declares the number
375  * of ranks that exist in the package. This has to then be transformed into
376  * logical ranks, which happens if we're using 3DS based DIMMs, which is
377  * determined based on the SPD_KEY_PKG_SL key. When using 3DS we need to
378  * multiple the number of dies by the number of ranks, otherwise it stays at
379  * 1x.
380  *
381  * When we're using asymmetrical DIMMs, the primary fields nominally apply to
382  * the even ranks and the secondary fields to the odd ranks. This is explicitly
383  * the case in DDR5. It is less explicit in DDR4, but we treat it the same way.
384  */
385 static bool
386 topo_dimm_calc_size(topo_mod_t *mod, const spd_cache_t *cache, uint64_t *sizep)
387 {
388 	uint32_t pndie = cache->sc_pkg_ndie[0];
389 	uint32_t width = cache->sc_data_bits * cache->sc_nsubchan /
390 	    cache->sc_dram_width[0];
391 
392 	*sizep = 0;
393 	if (cache->sc_pkg_sl[0] != SPD_SL_3DS)
394 		pndie = 1;
395 
396 	if (!cache->sc_asym) {
397 		*sizep = pndie * width * cache->sc_nranks *
398 		    cache->sc_die_size[0] / 8;
399 		return (true);
400 	}
401 
402 	if (cache->sc_nranks < 2) {
403 		topo_mod_dprintf(mod, "encountered asymmetrical module but it "
404 		    "only has %u ranks", cache->sc_nranks);
405 		return (false);
406 	}
407 
408 	*sizep = pndie * width * cache->sc_even_ranks *
409 	    cache->sc_die_size[0] / 8;
410 
411 	pndie = cache->sc_pkg_ndie[1];
412 	if (cache->sc_pkg_sl[1] != SPD_SL_3DS)
413 		pndie = 1;
414 
415 	*sizep += pndie * width * cache->sc_odd_ranks *
416 	    cache->sc_die_size[1] / 8;
417 	return (true);
418 }
419 
420 /*
421  * Add basic information to the DIMM. Some information like the current memory
422  * speed or LPDDR voltage can only be derived from the memory controller or
423  * systems firmware (i.e. SMBIOS).
424  */
425 static bool
426 topo_dimm_add_props(topo_mod_t *mod, tnode_t *dimm, const spd_cache_t *cache)
427 {
428 	uint32_t nbanks[2], nbgrps[2], nbpbg[2];
429 	uint_t arr_len = 1;
430 	uint64_t size;
431 
432 	nbgrps[0] = 1 << cache->sc_nbgrp_bits[0];
433 	nbpbg[0] = 1 << cache->sc_nbank_bits[0];
434 	nbanks[0] = nbgrps[0] * nbpbg[0];
435 
436 	if (!topo_dimm_calc_size(mod, cache, &size)) {
437 		return (false);
438 	}
439 
440 	/*
441 	 * This indicates that we have an asymmetrical DIMM configuration. This
442 	 * implies that the number of banks and bank groups actually vary based
443 	 * on whether it's an odd/even rank.
444 	 */
445 	if (cache->sc_asym) {
446 		arr_len = 2;
447 		nbgrps[1] = 1 << cache->sc_nbgrp_bits[1];
448 		nbpbg[1] = 1 << cache->sc_nbank_bits[1];
449 		nbanks[1] = nbgrps[1] * nbpbg[1];
450 	}
451 
452 	if (topo_create_props(mod, dimm, TOPO_PROP_IMMUTABLE, &topo_dimm_pgroup,
453 	    TOPO_PROP_DIMM_RANKS, TOPO_TYPE_UINT32, cache->sc_nranks,
454 	    TOPO_PROP_DIMM_BANKS, TOPO_TYPE_UINT32_ARRAY, nbanks, arr_len,
455 	    TOPO_PROP_DIMM_BANK_GROUPS, TOPO_TYPE_UINT32_ARRAY, nbgrps, arr_len,
456 	    TOPO_PROP_DIMM_BANKS_PER_GROUP, TOPO_TYPE_UINT32_ARRAY, nbpbg,
457 	    arr_len,
458 	    TOPO_PROP_DIMM_SUBCHANNELS, TOPO_TYPE_UINT32, cache->sc_nsubchan,
459 	    TOPO_PROP_DIMM_DATA_WIDTH, TOPO_TYPE_UINT32, cache->sc_data_bits,
460 	    TOPO_PROP_DIMM_ECC_WIDTH, TOPO_TYPE_UINT32, cache->sc_ecc_bits,
461 	    TOPO_PROP_DIMM_VDD, TOPO_TYPE_UINT32, cache->sc_vdd,
462 	    TOPO_PROP_DIMM_SIZE, TOPO_TYPE_UINT64, size,
463 	    TOPO_PROP_DIMM_TYPE, TOPO_TYPE_STRING, cache->sc_dram_str,
464 	    TOPO_PROP_DIMM_MODULE_TYPE, TOPO_TYPE_STRING, cache->sc_mod_str,
465 	    NULL) != 0) {
466 		topo_mod_dprintf(mod, "failed to set basic DIMM properties: %s",
467 		    topo_mod_errmsg(mod));
468 		return (false);
469 	}
470 
471 	return (true);
472 }
473 
474 static int
475 topo_dimm_create_tn(topo_mod_t *mod, tnode_t *pn, tnode_t **tnp,
476     const char *name, topo_instance_t inst, const char *part, const char *rev,
477     const char *serial)
478 {
479 	int ret;
480 	nvlist_t *auth = NULL;
481 	nvlist_t *fmri = NULL;
482 	tnode_t *tn;
483 
484 	if ((auth = topo_mod_auth(mod, pn)) == NULL) {
485 		topo_mod_dprintf(mod, "failed to get auth data: %s",
486 		    topo_mod_errmsg(mod));
487 		ret = -1;
488 		goto out;
489 	}
490 
491 	if ((fmri = topo_mod_hcfmri(mod, pn, FM_HC_SCHEME_VERSION, name,
492 	    inst, NULL, auth, part, rev, serial)) == NULL) {
493 		topo_mod_dprintf(mod, "failed to create fmri for %s[%" PRIu64
494 		    "]: %s\n", name, inst, topo_mod_errmsg(mod));
495 		ret = -1;
496 		goto out;
497 	}
498 
499 	if ((tn = topo_node_bind(mod, pn, name, inst, fmri)) == NULL) {
500 		topo_mod_dprintf(mod, "failed to bind fmri for %s[%" PRIu64
501 		    "]: %s\n", name, inst, topo_mod_errmsg(mod));
502 		ret = -1;
503 		goto out;
504 	}
505 
506 	topo_pgroup_hcset(tn, auth);
507 	if (topo_node_fru_set(tn, fmri, 0, &ret) != 0) {
508 		topo_mod_dprintf(mod, "failed to set FRU: %s\n",
509 		    topo_strerror(ret));
510 		ret = topo_mod_seterrno(mod, ret);
511 		goto out;
512 	}
513 
514 	*tnp = tn;
515 	ret = 0;
516 out:
517 	nvlist_free(auth);
518 	nvlist_free(fmri);
519 	return (ret);
520 }
521 
522 static bool
523 topo_dimm_crc_ok(topo_mod_t *mod, nvlist_t *nvl, spd_dram_type_t type)
524 {
525 	nvlist_t *errs;
526 	const char *crc_keys[2] = { NULL };
527 
528 	/*
529 	 * Note: Because this function determines which forms of SPD we support,
530 	 * if you end up adding something to the list you should update
531 	 * topo_dimm_add_props() to make sure that any additional variants have
532 	 * been added there or that we have information from their corresponding
533 	 * memory controllers.
534 	 */
535 	switch (type) {
536 	case SPD_DT_DDR4_SDRAM:
537 		crc_keys[0] = SPD_KEY_CRC_DDR4_BASE;
538 		crc_keys[1] = SPD_KEY_CRC_DDR4_BLK1;
539 		break;
540 	case SPD_DT_DDR5_SDRAM:
541 		crc_keys[0] = SPD_KEY_CRC_DDR5;
542 		break;
543 	default:
544 		topo_mod_dprintf(mod, "unsupported DRAM type: 0x%x", type);
545 		return (false);
546 	}
547 
548 	/*
549 	 * If there are no errors then we're likely OK and we can continue.
550 	 */
551 	if (nvlist_lookup_nvlist(nvl, SPD_KEY_ERRS, &errs) != 0) {
552 		return (true);
553 	}
554 
555 	for (size_t i = 0; i < ARRAY_SIZE(crc_keys); i++) {
556 		nvlist_t *key;
557 
558 		if (crc_keys[i] == NULL)
559 			continue;
560 
561 		if (nvlist_lookup_nvlist(errs, crc_keys[i], &key) == 0) {
562 			return (false);
563 		}
564 	}
565 
566 	return (true);
567 }
568 
569 typedef struct dimm_comp {
570 	const char *dc_comp;
571 	spd_device_t dc_mask;
572 	bool dc_always;
573 	uint32_t (*dc_count)(const struct dimm_comp *, const spd_cache_t *,
574 	    nvlist_t *);
575 	/* XXX determine if cache is needed */
576 	bool (*dc_mfg)(topo_mod_t *, tnode_t *, const struct dimm_comp *,
577 	    const spd_cache_t *, nvlist_t *, void *);
578 	const char *(*dc_type2str)(uint32_t);
579 	void *dc_mfg_arg;
580 } dimm_comp_t;
581 
582 static uint32_t
583 dimm_comp_count_solo(const dimm_comp_t *comp, const spd_cache_t *cache,
584     nvlist_t *spd)
585 {
586 	return (1);
587 }
588 
589 /*
590  * We'd like to determine the number of dies that are actually present. One
591  * way to calculate this is to look at the data bits and ecc bits that are
592  * required and divide that by the DRAM width. There should be one set of such
593  * dies for each primary rank. In DDR4/5 these contain the banks/groups.
594  *
595  * In a physical sense, even when using DDP or 3DS stacked modules, then there
596  * is still only a single refdes basically on the board so we create it that
597  * way. In the DDR4/5 world when there are more than two ranks, they are stacked
598  * or using the older DDP technology. So basically we assume there are only up
599  * to two ranks worth of dies at most.
600  */
601 static uint32_t
602 dimm_comp_count_dies(const dimm_comp_t *comp, const spd_cache_t *cache,
603     nvlist_t *spd)
604 {
605 	uint32_t chan_width = (cache->sc_ecc_bits + cache->sc_data_bits) *
606 	    cache->sc_nsubchan;
607 	uint32_t ndies_rank[2] = { 0, 0 };
608 
609 	ndies_rank[0] = chan_width / cache->sc_dram_width[0];
610 	if (cache->sc_asym) {
611 		ndies_rank[1] = chan_width / cache->sc_dram_width[1];
612 	} else if (cache->sc_nranks >= 2) {
613 		ndies_rank[1] = ndies_rank[0];
614 	}
615 
616 	return (ndies_rank[0] + ndies_rank[1]);
617 }
618 
619 static uint32_t
620 dimm_comp_count_mask(const dimm_comp_t *comp, const spd_cache_t *cache,
621     nvlist_t *spd)
622 {
623 	uint32_t ret = 0;
624 	uint32_t combo_mask = cache->sc_devices & comp->dc_mask;
625 
626 	for (uint32_t i = 0; i < sizeof (uint32_t) * NBBY; i++) {
627 		if (((1 << i) & combo_mask) != 0)
628 			ret++;
629 	}
630 
631 	return (ret);
632 }
633 
634 /*
635  * In the DDR4 SPD information, there is an explicit key for the number of
636  * registers that actually exist in the system. If the key exists then we return
637  * that, otherwise we don't do anything.
638  */
639 static uint32_t
640 dimm_comp_count_regs(const dimm_comp_t *comp, const spd_cache_t *cache,
641     nvlist_t *spd)
642 {
643 	uint32_t ret;
644 
645 	if (nvlist_lookup_uint32(spd, SPD_KEY_MOD_NREGS, &ret) != 0)
646 		return (0);
647 	return (ret);
648 }
649 
650 /*
651  * This enum indicates the possible state for all the keys of a given type.
652  * Basically we need to make sure that for the given range of keys they are
653  * generally consistent.
654  */
655 typedef enum {
656 	DIMM_COMP_K_VALID,
657 	DIMM_COMP_K_ERR,
658 	DIMM_COMP_K_ENOENT
659 } dimm_comp_key_state_t;
660 
661 static dimm_comp_key_state_t
662 dimm_comp_keys_exist(nvlist_t *spd, const char *const *keys, uint_t nents,
663     bool partial_enoent)
664 {
665 	dimm_comp_key_state_t ret;
666 
667 	if (nents == 0) {
668 		return (DIMM_COMP_K_ERR);
669 	}
670 
671 	if (keys == NULL) {
672 		return (DIMM_COMP_K_ENOENT);
673 	}
674 
675 	for (uint_t i = 0; i < nents; i++) {
676 		dimm_comp_key_state_t cur;
677 
678 		cur = nvlist_exists(spd, keys[i]) ? DIMM_COMP_K_VALID :
679 		    DIMM_COMP_K_ENOENT;
680 		if (i == 0) {
681 			ret = cur;
682 			continue;
683 		}
684 
685 		/*
686 		 * If we have changed disposition that is a problem. However, we
687 		 * will allow a partial ENOENT to exist if we've been given the
688 		 * flag to cover for the case where we don't have a translation
689 		 * for a given manufacturer's JEDEC ID name.
690 		 */
691 		if (ret != cur) {
692 			if (partial_enoent) {
693 				ret = DIMM_COMP_K_VALID;
694 			} else {
695 				return (DIMM_COMP_K_ERR);
696 			}
697 		}
698 	}
699 
700 	return (ret);
701 }
702 
703 /*
704  * The JEDEC IDs are a pair of two digits. Because we don't really have arrays
705  * of arrays in topo, we instead convert this into a string of the form
706  * 0x%x:0x%x with the continuation first and then the specific value.
707  */
708 static bool
709 dimm_comp_mfg_common_ids(topo_mod_t *mod, tnode_t *dimm, nvlist_t *spd,
710     const char *prop, const char *const *keys, uint_t nents)
711 {
712 	char **strs = NULL;
713 	bool ret = false;
714 	int err;
715 
716 	if ((strs = topo_mod_zalloc(mod, sizeof (char *) * nents)) == NULL) {
717 		topo_mod_dprintf(mod, "failed to allocate memory for %s string "
718 		    "array: %s", prop, topo_strerror(EMOD_NOMEM));
719 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
720 		return (false);
721 	}
722 
723 	for (size_t i = 0; i < nents; i++) {
724 		uint32_t *data;
725 		uint_t nvals;
726 		int nret = nvlist_lookup_uint32_array(spd, keys[i], &data,
727 		    &nvals);
728 
729 		if (nret != 0) {
730 			topo_mod_dprintf(mod, "failed to look up %s: %s",
731 			    keys[i], strerror(nret));
732 			(void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
733 			goto out;
734 		}
735 
736 		if (nvals != 2) {
737 			topo_mod_dprintf(mod, "key %s has wrong number of "
738 			    "array entries: found %u, expected %u", keys[i],
739 			    nvals, 2);
740 			(void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
741 			goto out;
742 		}
743 
744 		if (topo_mod_asprintf(mod, &strs[i], "0x%x:0x%x", data[0],
745 		    data[1]) == -1) {
746 			topo_mod_dprintf(mod, "failed to construct ID string "
747 			    "for %s: %s\n", keys[i], strerror(errno));
748 			(void) topo_mod_seterrno(mod, EMOD_NOMEM);
749 			goto out;
750 		}
751 	}
752 
753 	if (topo_prop_set_string_array(dimm, TOPO_PGROUP_DIMM_COMPONENTS, prop,
754 	    TOPO_PROP_IMMUTABLE, (const char **)strs, nents, &err) != 0) {
755 		topo_mod_dprintf(mod, "failed to set property %s: %s", prop,
756 		    topo_strerror(err));
757 		(void) topo_mod_seterrno(mod, err);
758 		goto out;
759 	}
760 
761 	ret = true;
762 out:
763 	for (uint_t i = 0; i < nents; i++) {
764 		topo_mod_strfree(mod, strs[i]);
765 	}
766 	topo_mod_free(mod, strs, sizeof (char *) * nents);
767 	return (ret);
768 }
769 
770 static bool
771 dimm_comp_mfg_common_strings(topo_mod_t *mod, tnode_t *dimm, nvlist_t *spd,
772     const char *prop, const char *const *keys, uint_t nents, bool allow_enoent)
773 {
774 	char **strs = NULL;
775 	int err;
776 	bool ret = false;
777 
778 	if ((strs = topo_mod_zalloc(mod, sizeof (char *) * nents)) == NULL) {
779 		topo_mod_dprintf(mod, "failed to allocate memory for %s string "
780 		    "array: %s", prop, topo_strerror(EMOD_NOMEM));
781 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
782 		return (false);
783 	}
784 
785 	for (size_t i = 0; i < nents; i++) {
786 		int nret = nvlist_lookup_string(spd, keys[i], &strs[i]);
787 		if (nret != 0 && !(allow_enoent && nret == ENOENT)) {
788 			topo_mod_dprintf(mod, "failed to look up %s: %s",
789 			    keys[i], strerror(nret));
790 			(void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
791 			goto out;
792 		}
793 	}
794 
795 	if (topo_prop_set_string_array(dimm, TOPO_PGROUP_DIMM_COMPONENTS, prop,
796 	    TOPO_PROP_IMMUTABLE, (const char **)strs, nents, &err) != 0) {
797 		topo_mod_dprintf(mod, "failed to set property %s: %s", prop,
798 		    topo_strerror(err));
799 		(void) topo_mod_seterrno(mod, err);
800 		goto out;
801 	}
802 
803 	ret = true;
804 out:
805 	topo_mod_free(mod, strs, sizeof (char *) * nents);
806 	return (ret);
807 }
808 
809 /*
810  * The type of a part is encoded as a uint32_t and has a corresponding enum. We
811  * want to translate that into a string. The table for that is stored in the
812  * dimm_comp_t.
813  */
814 static bool
815 dimm_comp_mfg_common_type(topo_mod_t *mod, tnode_t *dimm, nvlist_t *spd,
816     const dimm_comp_t *comp, const char *const *keys, uint_t nents)
817 {
818 	const char **strs = NULL;
819 	int err;
820 	bool ret = false;
821 	char prop[64];
822 
823 	if (comp->dc_type2str == NULL) {
824 		(void) topo_mod_dprintf(mod, "missing type2str function for "
825 		    "component type %s", comp->dc_comp);
826 		(void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
827 		return (false);
828 	}
829 
830 	(void) snprintf(prop, sizeof (prop), "%s-type", comp->dc_comp);
831 	if ((strs = topo_mod_zalloc(mod, sizeof (char *) * nents)) == NULL) {
832 		topo_mod_dprintf(mod, "failed to allocate memory for %s string "
833 		    "array: %s", prop, topo_strerror(EMOD_NOMEM));
834 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
835 		return (false);
836 	}
837 
838 	for (size_t i = 0; i < nents; i++) {
839 		uint32_t raw;
840 
841 		int nret = nvlist_lookup_uint32(spd, keys[i], &raw);
842 		if (nret != 0) {
843 			topo_mod_dprintf(mod, "failed to look up %s: %s",
844 			    keys[i], strerror(nret));
845 			(void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
846 			goto out;
847 		}
848 
849 		strs[i] = comp->dc_type2str(raw);
850 	}
851 
852 	if (topo_prop_set_string_array(dimm, TOPO_PGROUP_DIMM_COMPONENTS, prop,
853 	    TOPO_PROP_IMMUTABLE, strs, nents, &err) != 0) {
854 		topo_mod_dprintf(mod, "failed to set property %s: %s", prop,
855 		    topo_strerror(err));
856 		(void) topo_mod_seterrno(mod, err);
857 		goto out;
858 	}
859 
860 	ret = true;
861 out:
862 	topo_mod_free(mod, strs, sizeof (char *) * nents);
863 	return (ret);
864 }
865 
866 
867 /*
868  * Given a number of keys to check for each item type, attempt to look up each
869  * item and add a property based on it. Prior to DDR5, we generally won't have
870  * information for the manufacturers or revisions. As such, when we fail to get
871  * any keys of a given type, that's fine. However, we do want to make sure that
872  * we are always adding things consistently, that is if we are told we have
873  * three keys for something and sometimes only look up two, that's an error.
874  */
875 static bool
876 dimm_comp_mfg_common(topo_mod_t *mod, tnode_t *dimm, const dimm_comp_t *comp,
877     nvlist_t *spd, const char *const *mfg_id_key,
878     const char *const *mfg_name_key, const char *const *type_key,
879     const char *const *rev_key, uint_t nents)
880 {
881 	dimm_comp_key_state_t mfg_id_valid, mfg_name_valid, type_valid;
882 	dimm_comp_key_state_t rev_valid;
883 
884 	if (nents == 0) {
885 		return (true);
886 	}
887 
888 	mfg_id_valid = dimm_comp_keys_exist(spd, mfg_id_key, nents, false);
889 	mfg_name_valid = dimm_comp_keys_exist(spd, mfg_name_key, nents, true);
890 	type_valid = dimm_comp_keys_exist(spd, type_key, nents, false);
891 	rev_valid = dimm_comp_keys_exist(spd, rev_key, nents, false);
892 
893 	if (mfg_name_valid == DIMM_COMP_K_ERR || rev_valid == DIMM_COMP_K_ERR ||
894 	    mfg_id_valid == DIMM_COMP_K_ERR || type_valid == DIMM_COMP_K_ERR) {
895 		topo_mod_dprintf(mod, "encountered erroneous keys: 0x%x 0x%x "
896 		    "0x%x 0x%x", mfg_name_valid, rev_valid, mfg_id_valid,
897 		    type_valid);
898 		(void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
899 		return (false);
900 	}
901 
902 	if (mfg_id_valid == DIMM_COMP_K_VALID) {
903 		char key[64];
904 
905 		(void) snprintf(key, sizeof (key), "%s-id",
906 		    comp->dc_comp);
907 		if (!dimm_comp_mfg_common_ids(mod, dimm, spd, key,
908 		    mfg_id_key, nents)) {
909 			return (false);
910 		}
911 	}
912 
913 	if (mfg_name_valid == DIMM_COMP_K_VALID) {
914 		char key[64];
915 
916 		(void) snprintf(key, sizeof (key), "%s-mfg-name",
917 		    comp->dc_comp);
918 		if (!dimm_comp_mfg_common_strings(mod, dimm, spd, key,
919 		    mfg_name_key, nents, true)) {
920 			return (false);
921 		}
922 	}
923 
924 	if (rev_valid == DIMM_COMP_K_VALID) {
925 		char key[64];
926 
927 		(void) snprintf(key, sizeof (key), "%s-revision",
928 		    comp->dc_comp);
929 		if (!dimm_comp_mfg_common_strings(mod, dimm, spd, key, rev_key,
930 		    nents, false)) {
931 			return (false);
932 		}
933 	}
934 
935 	if (type_valid == DIMM_COMP_K_VALID) {
936 		if (!dimm_comp_mfg_common_type(mod, dimm, spd, comp, type_key,
937 		    nents)) {
938 			return (false);
939 		}
940 	}
941 
942 	return (true);
943 }
944 
945 static bool
946 dimm_comp_mfg_die(topo_mod_t *mod, tnode_t *dimm, const dimm_comp_t *comp,
947     const spd_cache_t *cache, nvlist_t *spd, void *arg)
948 {
949 	const char *mfg_id = SPD_KEY_MFG_DRAM_MFG_ID;
950 	const char *mfg_name = SPD_KEY_MFG_DRAM_MFG_NAME;
951 	const char *rev = SPD_KEY_MFG_DRAM_STEP;
952 
953 	return (dimm_comp_mfg_common(mod, dimm, comp, spd, &mfg_id, &mfg_name,
954 	    NULL, &rev, 1));
955 }
956 
957 static bool
958 dimm_comp_mfg_single(topo_mod_t *mod, tnode_t *dimm, const dimm_comp_t *comp,
959     const spd_cache_t *cache, nvlist_t *spd, void *arg)
960 {
961 	char *mfg_key = NULL, *mfg_str_key = NULL, *type_key = NULL;
962 	char *rev_key = NULL;
963 	const char *name = arg;
964 	bool ret;
965 
966 	if (name == NULL) {
967 		name = comp->dc_comp;
968 	}
969 
970 	if (topo_mod_asprintf(mod, &mfg_key, "module.%s.mfg-id", name) == -1 ||
971 	    topo_mod_asprintf(mod, &mfg_str_key, "module.%s.mfg-name",
972 	    name) == -1 ||
973 	    topo_mod_asprintf(mod, &type_key, "module.%s.type", name) == -1 ||
974 	    topo_mod_asprintf(mod, &rev_key, "module.%s.revision", name) ==
975 	    -1) {
976 		ret = false;
977 		goto done;
978 	}
979 
980 	ret = dimm_comp_mfg_common(mod, dimm, comp, spd,
981 	    (const char **)&mfg_key, (const char **)&mfg_str_key,
982 	    (const char **)&type_key, (const char **)&rev_key, 1);
983 
984 done:
985 	topo_mod_strfree(mod, mfg_key);
986 	topo_mod_strfree(mod, mfg_str_key);
987 	topo_mod_strfree(mod, type_key);
988 	topo_mod_strfree(mod, rev_key);
989 	return (ret);
990 }
991 
992 static bool
993 dimm_comp_mfg_pmic(topo_mod_t *mod, tnode_t *dimm, const dimm_comp_t *comp,
994     const spd_cache_t *cache, nvlist_t *spd, void *arg)
995 {
996 	const char **mfg_keys = NULL, **mfg_str_keys = NULL, **type_keys = NULL;
997 	const char **rev_keys = NULL;
998 	bool ret = false;
999 	uint32_t nents = 0, curent = 0;
1000 	size_t alen;
1001 
1002 	if ((cache->sc_devices & SPD_DEVICE_PMIC_0) != 0)
1003 		nents++;
1004 	if ((cache->sc_devices & SPD_DEVICE_PMIC_1) != 0)
1005 		nents++;
1006 	if ((cache->sc_devices & SPD_DEVICE_PMIC_2) != 0)
1007 		nents++;
1008 
1009 	if (nents == 0) {
1010 		return (true);
1011 	}
1012 
1013 	alen = sizeof (char *) * nents;
1014 
1015 	if ((mfg_keys = topo_mod_zalloc(mod, alen)) == NULL ||
1016 	    (mfg_str_keys = topo_mod_zalloc(mod, alen)) == NULL ||
1017 	    (type_keys = topo_mod_zalloc(mod, alen)) == NULL ||
1018 	    (rev_keys = topo_mod_zalloc(mod, alen)) == NULL) {
1019 		goto done;
1020 	}
1021 
1022 	if ((cache->sc_devices & SPD_DEVICE_PMIC_0) != 0) {
1023 		mfg_keys[curent] = SPD_KEY_DEV_PMIC0_MFG;
1024 		mfg_str_keys[curent] = SPD_KEY_DEV_PMIC0_MFG_NAME;
1025 		type_keys[curent] = SPD_KEY_DEV_PMIC0_TYPE;
1026 		rev_keys[curent] = SPD_KEY_DEV_PMIC0_REV;
1027 		curent++;
1028 	}
1029 
1030 	if ((cache->sc_devices & SPD_DEVICE_PMIC_1) != 0) {
1031 		mfg_keys[curent] = SPD_KEY_DEV_PMIC1_MFG;
1032 		mfg_str_keys[curent] = SPD_KEY_DEV_PMIC1_MFG_NAME;
1033 		type_keys[curent] = SPD_KEY_DEV_PMIC1_TYPE;
1034 		rev_keys[curent] = SPD_KEY_DEV_PMIC1_REV;
1035 		curent++;
1036 	}
1037 
1038 	if ((cache->sc_devices & SPD_DEVICE_PMIC_2) != 0) {
1039 		mfg_keys[curent] = SPD_KEY_DEV_PMIC2_MFG;
1040 		mfg_str_keys[curent] = SPD_KEY_DEV_PMIC2_MFG_NAME;
1041 		type_keys[curent] = SPD_KEY_DEV_PMIC2_TYPE;
1042 		rev_keys[curent] = SPD_KEY_DEV_PMIC2_REV;
1043 		curent++;
1044 	}
1045 
1046 	ret = dimm_comp_mfg_common(mod, dimm, comp, spd, mfg_keys,
1047 	    mfg_str_keys, type_keys, rev_keys, nents);
1048 
1049 done:
1050 	topo_mod_free(mod, mfg_keys, alen);
1051 	topo_mod_free(mod, mfg_str_keys, alen);
1052 	topo_mod_free(mod, type_keys, alen);
1053 	topo_mod_free(mod, rev_keys, alen);
1054 	return (ret);
1055 }
1056 static const dimm_comp_t dimm_comps[] = {
1057 	{ .dc_comp = TOPO_PROP_DIMM_COMP_DIE, .dc_always = true,
1058 	    .dc_count = dimm_comp_count_dies, .dc_mfg = dimm_comp_mfg_die },
1059 	{ .dc_comp = TOPO_PROP_DIMM_COMP_SPD, .dc_mask = SPD_DEVICE_SPD,
1060 	    .dc_count = dimm_comp_count_solo, .dc_mfg = dimm_comp_mfg_single,
1061 	    .dc_type2str = topo_dimm_spd2str },
1062 	{ .dc_comp = TOPO_PROP_DIMM_COMP_TS, .dc_mask = SPD_DEVICE_TEMP_1 |
1063 	    SPD_DEVICE_TEMP_2, .dc_count = dimm_comp_count_mask,
1064 	    .dc_mfg = dimm_comp_mfg_single, .dc_mfg_arg = "temp",
1065 	    .dc_type2str = topo_dimm_temp2str },
1066 	{ .dc_comp = TOPO_PROP_DIMM_COMP_HS, .dc_mask = SPD_DEVICE_HS },
1067 	{ .dc_comp = TOPO_PROP_DIMM_COMP_PMIC, .dc_mask = SPD_DEVICE_PMIC_0 |
1068 	    SPD_DEVICE_PMIC_1 | SPD_DEVICE_PMIC_2, .dc_mfg = dimm_comp_mfg_pmic,
1069 	    .dc_type2str = topo_dimm_pmic2str  },
1070 	{ .dc_comp = TOPO_PROP_DIMM_COMP_CD, .dc_mask = SPD_DEVICE_CD,
1071 	    .dc_mfg = dimm_comp_mfg_single, .dc_type2str = topo_dimm_cd2str },
1072 	{ .dc_comp = TOPO_PROP_DIMM_COMP_RCD, .dc_mask = SPD_DEVICE_RCD,
1073 	    .dc_count = dimm_comp_count_regs, .dc_mfg = dimm_comp_mfg_single,
1074 	    .dc_type2str = topo_dimm_rcd2str },
1075 	{ .dc_comp = TOPO_PROP_DIMM_COMP_DB, .dc_mask = SPD_DEVICE_DB,
1076 	    .dc_mfg = dimm_comp_mfg_single, .dc_type2str = topo_dimm_db2str },
1077 	{ .dc_comp = TOPO_PROP_DIMM_COMP_MRCD, .dc_mask = SPD_DEVICE_MRCD,
1078 	    .dc_mfg = dimm_comp_mfg_single, .dc_type2str = topo_dimm_mrcd2str },
1079 	{ .dc_comp = TOPO_PROP_DIMM_COMP_MDB, .dc_mask = SPD_DEVICE_MDB,
1080 	    .dc_mfg = dimm_comp_mfg_single, .dc_type2str = topo_dimm_mdb2str },
1081 	{ .dc_comp = TOPO_PROP_DIMM_COMP_DMB, .dc_mask = SPD_DEVICE_DMB,
1082 	    .dc_mfg = dimm_comp_mfg_single, .dc_type2str = topo_dimm_dmb2str }
1083 };
1084 
1085 /*
1086  * Go through and add the different information that exists for each type of
1087  * component that we might have. For most items on here, we can know they are
1088  * present, but we may not be able to get the count and much more than a
1089  * revision string or type. See additional discussion at the definition of
1090  * TOPO_PGROUP_DIMM_COMPONENTS for this property group and a bit of the design.
1091  */
1092 static bool
1093 topo_dimm_add_comps(topo_mod_t *mod, tnode_t *dimm, nvlist_t *spd,
1094     const spd_cache_t *cache)
1095 {
1096 	int ret;
1097 	const char *devs[ARRAY_SIZE(dimm_comps)];
1098 	uint_t ndevs = 0;
1099 	const char *pg = topo_dimm_comps_pgroup.tpi_name;
1100 
1101 	/*
1102 	 * Always create the pgroup, as we'll at least have information about
1103 	 * the DRAM dies to add.
1104 	 */
1105 	if (topo_pgroup_create(dimm, &topo_dimm_comps_pgroup, &ret) != 0) {
1106 		topo_mod_dprintf(mod, "failed to create property group %s: %s",
1107 		    pg, topo_strerror(ret));
1108 		(void) topo_mod_seterrno(mod, ret);
1109 		return (false);
1110 	}
1111 
1112 	for (size_t i = 0; i < ARRAY_SIZE(dimm_comps); i++) {
1113 		const dimm_comp_t *c = &dimm_comps[i];
1114 		char prop[64];
1115 		bool pres = false;
1116 
1117 		if (c->dc_always || (cache->sc_devices & c->dc_mask) != 0) {
1118 			pres = true;
1119 			devs[ndevs] = dimm_comps[i].dc_comp;
1120 			ndevs++;
1121 		}
1122 
1123 		if (pres && c->dc_count != NULL) {
1124 			uint32_t count = c->dc_count(c, cache, spd);
1125 			(void) snprintf(prop, sizeof (prop), "%s-count",
1126 			    c->dc_comp);
1127 			if (count != 0 && topo_prop_set_uint32(dimm, pg, prop,
1128 			    TOPO_PROP_IMMUTABLE, count, &ret) != 0) {
1129 				topo_mod_dprintf(mod, "failed to set property "
1130 				    "%s: %s", prop, topo_strerror(ret));
1131 				(void) topo_mod_seterrno(mod, ret);
1132 				return (false);
1133 			}
1134 		}
1135 
1136 		if (pres && c->dc_mfg != NULL && !c->dc_mfg(mod, dimm, c, cache,
1137 		    spd, c->dc_mfg_arg)) {
1138 			return (false);
1139 		}
1140 	}
1141 
1142 	if (topo_prop_set_string_array(dimm, pg, TOPO_PROP_DIMM_COMP,
1143 	    TOPO_PROP_IMMUTABLE, devs, ndevs, &ret) != 0) {
1144 		topo_mod_dprintf(mod, "failed to create components array: %s",
1145 		    topo_strerror(ret));
1146 		(void) topo_mod_seterrno(mod, ret);
1147 		return (false);
1148 	}
1149 
1150 	return (true);
1151 }
1152 
1153 static int
1154 topo_dimm_enum(topo_mod_t *mod, tnode_t *pn, const char *name,
1155     topo_instance_t min, topo_instance_t max, void *modarg, void *data)
1156 {
1157 	int ret;
1158 	const topo_dimm_t *dimm;
1159 	spd_error_t spd_err;
1160 	nvlist_t *spd_nvl = NULL;
1161 	uint32_t dram_type;
1162 	char *mod_pn = NULL, *mod_sn = NULL, *mod_rev = NULL;
1163 	char *mod_c_pn = NULL, *mod_c_sn = NULL, *mod_c_rev = NULL;
1164 	tnode_t *dimm_tn;
1165 	spd_cache_t spd_cache;
1166 
1167 	topo_mod_dprintf(mod, "asked to enum %s [%" PRIu64 ", %" PRIu64 "] on "
1168 	    "%s%" PRIu64 "\n", name, min, max, topo_node_name(pn),
1169 	    topo_node_instance(pn));
1170 
1171 	if (strcmp(name, DIMM) != 0) {
1172 		topo_mod_dprintf(mod, "cannot enumerate %s: unknown type\n",
1173 		    name);
1174 		ret = -1;
1175 		goto out;
1176 	}
1177 
1178 	if (data == NULL) {
1179 		topo_mod_dprintf(mod, "cannot enumerate %s: missing required "
1180 		    "data\n", name);
1181 		ret = topo_mod_seterrno(mod, EMOD_METHOD_INVAL);
1182 		goto out;
1183 	}
1184 
1185 	if (min != max) {
1186 		topo_mod_dprintf(mod, "cannot enumerate %s: multiple instances "
1187 		    "requested\n", name);
1188 		ret = topo_mod_seterrno(mod, EMOD_METHOD_INVAL);
1189 		goto out;
1190 	}
1191 
1192 	dimm = data;
1193 	if (dimm->td_nspd == 0 || dimm->td_spd == NULL) {
1194 		topo_mod_dprintf(mod, "cannot enumerate %s: no valid DIMM "
1195 		    "data provided", name);
1196 		ret = topo_mod_seterrno(mod, EMOD_METHOD_INVAL);
1197 		goto out;
1198 	}
1199 
1200 	spd_nvl = libjedec_spd(dimm->td_spd, dimm->td_nspd, &spd_err);
1201 	if (spd_nvl == NULL) {
1202 		topo_mod_dprintf(mod, "failed to parse SPD information: got "
1203 		    "error 0x%x", spd_err);
1204 		ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
1205 	}
1206 
1207 	if ((ret = nvlist_lookup_uint32(spd_nvl, SPD_KEY_DRAM_TYPE,
1208 	    &dram_type)) != 0) {
1209 		topo_mod_dprintf(mod, "failed to get SPD key %s: %s",
1210 		    SPD_KEY_DRAM_TYPE, strerror(ret));
1211 		ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
1212 		goto out;
1213 	}
1214 
1215 	if (!topo_dimm_crc_ok(mod, spd_nvl, dram_type)) {
1216 		ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
1217 		goto out;
1218 	}
1219 
1220 	/*
1221 	 * If we have SPD data, we'd expect all of the basic part, serial, and
1222 	 * revision information to be available for the module. However, if
1223 	 * there was bad data for some reason, we allow ourselves to not be able
1224 	 * to look it up.
1225 	 */
1226 	if (nvlist_lookup_pairs(spd_nvl, NV_FLAG_NOENTOK,
1227 	    SPD_KEY_MFG_MOD_PN, DATA_TYPE_STRING, &mod_pn,
1228 	    SPD_KEY_MFG_MOD_SN, DATA_TYPE_STRING, &mod_sn,
1229 	    SPD_KEY_MFG_MOD_REV, DATA_TYPE_STRING, &mod_rev, NULL) != 0) {
1230 		topo_mod_dprintf(mod, "failed to look up basic DIMM FMRI "
1231 		    "information");
1232 		ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
1233 		goto out;
1234 	}
1235 
1236 	mod_c_pn = topo_mod_clean_str(mod, mod_pn);
1237 	mod_c_sn = topo_mod_clean_str(mod, mod_sn);
1238 	mod_c_rev = topo_mod_clean_str(mod, mod_rev);
1239 
1240 	if ((ret = topo_node_range_create(mod, pn, DIMM, 0, 0)) != 0) {
1241 		topo_mod_dprintf(mod, "failed to create DIMM range: %s",
1242 		    topo_mod_errmsg(mod));
1243 		goto out;
1244 	}
1245 
1246 	if ((ret = topo_dimm_create_tn(mod, pn, &dimm_tn, DIMM, 0, mod_c_pn,
1247 	    mod_c_rev, mod_c_sn)) != 0) {
1248 		goto out;
1249 	}
1250 
1251 	if (topo_node_label_set(dimm_tn, NULL, &ret) != 0) {
1252 		topo_mod_dprintf(mod, "failed to set label on DIMM: %s",
1253 		    topo_mod_errmsg(mod));
1254 		ret = topo_mod_seterrno(mod, ret);
1255 		goto out;
1256 	}
1257 
1258 	(void) memset(&spd_cache, 0, sizeof (spd_cache));
1259 	spd_cache.sc_dram_type = dram_type;
1260 	if (!topo_dimm_cache_spd(mod, spd_nvl, &spd_cache))
1261 		goto out;
1262 
1263 	if (!topo_dimm_add_props(mod, dimm_tn, &spd_cache))
1264 		goto out;
1265 
1266 	if (!topo_dimm_add_comps(mod, dimm_tn, spd_nvl, &spd_cache))
1267 		goto out;
1268 
1269 	ret = 0;
1270 out:
1271 	topo_mod_strfree(mod, mod_c_sn);
1272 	topo_mod_strfree(mod, mod_c_pn);
1273 	topo_mod_strfree(mod, mod_c_rev);
1274 	nvlist_free(spd_nvl);
1275 	return (ret);
1276 }
1277 
1278 static const topo_modops_t topo_dimm_ops = {
1279 	topo_dimm_enum, NULL
1280 };
1281 
1282 static topo_modinfo_t topo_dimm_mod = {
1283 	"Common DIMM Enumerator", FM_FMRI_SCHEME_HC, TOPO_MOD_DIMM_VERS,
1284 	    &topo_dimm_ops
1285 };
1286 
1287 int
1288 _topo_init(topo_mod_t *mod, topo_version_t version)
1289 {
1290 	if (getenv("TOPODIMMDEBUG") != NULL) {
1291 		topo_mod_setdebug(mod);
1292 	}
1293 	topo_mod_dprintf(mod, "module initializing\n");
1294 
1295 	return (topo_mod_register(mod, &topo_dimm_mod, TOPO_VERSION));
1296 }
1297 
1298 void
1299 _topo_fini(topo_mod_t *mod)
1300 {
1301 	topo_mod_unregister(mod);
1302 }
1303