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