xref: /illumos-gate/usr/src/common/mc/zen_umc/zen_umc_dump.c (revision 012e6ce759c490003aed29439cc47d3d73a99ad3)
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 2022 Oxide Computer Company
14  */
15 
16 /*
17  * Dump and restore logic for external processing. Dump generally runs in kernel
18  * context from a well formed structure created by the driver. Restore is used
19  * in userland as part of testing and related.
20  *
21  * Note, there are a lot of fields in these structures that are not serialized
22  * because they are not used as part of the decoder (e.g. the various raw values
23  * which are captured to aid future debugging).
24  */
25 
26 #include "zen_umc.h"
27 #ifndef _KERNEL
28 #include <string.h>
29 #include <strings.h>
30 #include <libnvpair.h>
31 #endif
32 
33 static nvlist_t *
34 zen_umc_dump_dram_rule(df_dram_rule_t *rule)
35 {
36 	nvlist_t *nvl;
37 
38 	nvl = fnvlist_alloc();
39 	fnvlist_add_uint32(nvl, "ddr_flags", rule->ddr_flags);
40 	fnvlist_add_uint64(nvl, "ddr_base", rule->ddr_base);
41 	fnvlist_add_uint64(nvl, "ddr_limit", rule->ddr_limit);
42 	fnvlist_add_uint16(nvl, "ddr_dest_fabid", rule->ddr_dest_fabid);
43 	fnvlist_add_uint8(nvl, "ddr_sock_ileave_bits",
44 	    rule->ddr_sock_ileave_bits);
45 	fnvlist_add_uint8(nvl, "ddr_die_ileave_bits",
46 	    rule->ddr_die_ileave_bits);
47 	fnvlist_add_uint8(nvl, "ddr_addr_start", rule->ddr_addr_start);
48 	fnvlist_add_uint8(nvl, "ddr_remap_ent", rule->ddr_remap_ent);
49 	fnvlist_add_uint32(nvl, "ddr_chan_ileave", rule->ddr_chan_ileave);
50 
51 	return (nvl);
52 }
53 
54 static nvlist_t *
55 zen_umc_dump_cs(umc_cs_t *cs)
56 {
57 	nvlist_t *nvl = fnvlist_alloc();
58 	nvlist_t *base = fnvlist_alloc();
59 	nvlist_t *sec = fnvlist_alloc();
60 
61 	fnvlist_add_uint64(base, "udb_base", cs->ucs_base.udb_base);
62 	fnvlist_add_uint8(base, "udb_valid", cs->ucs_base.udb_valid);
63 	fnvlist_add_nvlist(nvl, "ucs_base", base);
64 	nvlist_free(base);
65 	fnvlist_add_uint64(sec, "udb_base", cs->ucs_sec.udb_base);
66 	fnvlist_add_uint8(sec, "udb_valid", cs->ucs_sec.udb_valid);
67 	fnvlist_add_nvlist(nvl, "ucs_sec", sec);
68 	nvlist_free(sec);
69 	fnvlist_add_uint64(nvl, "ucs_base_mask", cs->ucs_base_mask);
70 	fnvlist_add_uint64(nvl, "ucs_sec_mask", cs->ucs_sec_mask);
71 	fnvlist_add_uint8(nvl, "ucs_nrow_lo", cs->ucs_nrow_lo);
72 	fnvlist_add_uint8(nvl, "ucs_nrow_hi", cs->ucs_nrow_hi);
73 	fnvlist_add_uint8(nvl, "ucs_nbank_groups", cs->ucs_nbank_groups);
74 	fnvlist_add_uint8(nvl, "ucs_cs_xor", cs->ucs_cs_xor);
75 	fnvlist_add_uint8(nvl, "ucs_row_hi_bit", cs->ucs_row_hi_bit);
76 	fnvlist_add_uint8(nvl, "ucs_row_low_bit", cs->ucs_row_low_bit);
77 	fnvlist_add_uint8_array(nvl, "ucs_bank_bits", cs->ucs_bank_bits,
78 	    cs->ucs_nbanks);
79 	fnvlist_add_uint8_array(nvl, "ucs_col_bits", cs->ucs_col_bits,
80 	    cs->ucs_ncol);
81 	fnvlist_add_uint8(nvl, "ucs_inv_msbs", cs->ucs_inv_msbs);
82 	fnvlist_add_uint8_array(nvl, "ucs_rm_bits", cs->ucs_rm_bits,
83 	    cs->ucs_nrm);
84 	fnvlist_add_uint8(nvl, "ucs_inv_msbs_sec", cs->ucs_inv_msbs_sec);
85 	fnvlist_add_uint8_array(nvl, "ucs_rm_bits_sec", cs->ucs_rm_bits_sec,
86 	    cs->ucs_nrm);
87 	fnvlist_add_uint8(nvl, "ucs_subchan", cs->ucs_subchan);
88 
89 	return (nvl);
90 }
91 
92 static nvlist_t *
93 zen_umc_dump_dimm(umc_dimm_t *dimm)
94 {
95 	nvlist_t *nvl = fnvlist_alloc();
96 	nvlist_t *cs[ZEN_UMC_MAX_CS_PER_DIMM];
97 
98 	fnvlist_add_uint32(nvl, "ud_flags", dimm->ud_flags);
99 	fnvlist_add_uint32(nvl, "ud_width", dimm->ud_width);
100 	fnvlist_add_uint32(nvl, "ud_type", dimm->ud_type);
101 	fnvlist_add_uint32(nvl, "ud_kind", dimm->ud_kind);
102 	fnvlist_add_uint32(nvl, "ud_dimmno", dimm->ud_dimmno);
103 
104 	for (uint_t i = 0; i < ZEN_UMC_MAX_CS_PER_DIMM; i++) {
105 		cs[i] = zen_umc_dump_cs(&dimm->ud_cs[i]);
106 	}
107 	fnvlist_add_nvlist_array(nvl, "ud_cs", cs, ZEN_UMC_MAX_CS_PER_DIMM);
108 	for (uint_t i = 0; i < ZEN_UMC_MAX_CS_PER_DIMM; i++) {
109 		nvlist_free(cs[i]);
110 	}
111 
112 	return (nvl);
113 }
114 
115 static nvlist_t *
116 zen_umc_dump_chan_hash(umc_chan_hash_t *hash)
117 {
118 	nvlist_t *nvl = fnvlist_alloc();
119 
120 	fnvlist_add_uint32(nvl, "uch_flags", hash->uch_flags);
121 
122 	if (hash->uch_flags & UMC_CHAN_HASH_F_BANK) {
123 		nvlist_t *banks[ZEN_UMC_MAX_CHAN_BANK_HASH];
124 		for (uint_t i = 0; i < ZEN_UMC_MAX_CHAN_BANK_HASH; i++) {
125 			banks[i] = fnvlist_alloc();
126 
127 			fnvlist_add_uint32(banks[i], "ubh_row_xor",
128 			    hash->uch_bank_hashes[i].ubh_row_xor);
129 			fnvlist_add_uint32(banks[i], "ubh_col_xor",
130 			    hash->uch_bank_hashes[i].ubh_col_xor);
131 			fnvlist_add_boolean_value(banks[i], "ubh_en",
132 			    hash->uch_bank_hashes[i].ubh_en);
133 		}
134 		fnvlist_add_nvlist_array(nvl, "uch_bank_hashes", banks,
135 		    ZEN_UMC_MAX_CHAN_BANK_HASH);
136 
137 		for (uint_t i = 0; i < ZEN_UMC_MAX_CHAN_BANK_HASH; i++) {
138 			nvlist_free(banks[i]);
139 		}
140 	}
141 
142 	if (hash->uch_flags & UMC_CHAN_HASH_F_RM) {
143 		nvlist_t *rm[ZEN_UMC_MAX_CHAN_RM_HASH];
144 		for (uint_t i = 0; i < ZEN_UMC_MAX_CHAN_RM_HASH; i++) {
145 			rm[i] = fnvlist_alloc();
146 
147 			fnvlist_add_uint64(rm[i], "uah_addr_xor",
148 			    hash->uch_rm_hashes[i].uah_addr_xor);
149 			fnvlist_add_boolean_value(rm[i], "uah_en",
150 			    hash->uch_rm_hashes[i].uah_en);
151 		}
152 		fnvlist_add_nvlist_array(nvl, "uch_rm_hashes", rm,
153 		    ZEN_UMC_MAX_CHAN_RM_HASH);
154 
155 		for (uint_t i = 0; i < ZEN_UMC_MAX_CHAN_RM_HASH; i++) {
156 			nvlist_free(rm[i]);
157 		}
158 	}
159 
160 	if (hash->uch_flags & UMC_CHAN_HASH_F_CS) {
161 		nvlist_t *cs[ZEN_UMC_MAX_CHAN_CS_HASH];
162 		for (uint_t i = 0; i < ZEN_UMC_MAX_CHAN_CS_HASH; i++) {
163 			cs[i] = fnvlist_alloc();
164 
165 			fnvlist_add_uint64(cs[i], "uah_addr_xor",
166 			    hash->uch_rm_hashes[i].uah_addr_xor);
167 			fnvlist_add_boolean_value(cs[i], "uah_en",
168 			    hash->uch_rm_hashes[i].uah_en);
169 		}
170 		fnvlist_add_nvlist_array(nvl, "uch_cs_hashes", cs,
171 		    ZEN_UMC_MAX_CHAN_CS_HASH);
172 
173 		for (uint_t i = 0; i < ZEN_UMC_MAX_CHAN_CS_HASH; i++) {
174 			nvlist_free(cs[i]);
175 		}
176 	}
177 
178 	if (hash->uch_flags & UMC_CHAN_HASH_F_PC) {
179 		nvlist_t *pc = fnvlist_alloc();
180 
181 		fnvlist_add_uint32(pc, "uph_row_xor",
182 		    hash->uch_pc_hash.uph_row_xor);
183 		fnvlist_add_uint32(pc, "uph_col_xor",
184 		    hash->uch_pc_hash.uph_col_xor);
185 		fnvlist_add_uint8(pc, "uph_bank_xor",
186 		    hash->uch_pc_hash.uph_bank_xor);
187 		fnvlist_add_boolean_value(pc, "uph_en",
188 		    hash->uch_pc_hash.uph_en);
189 
190 		fnvlist_add_nvlist(nvl, "uch_pch_hash", pc);
191 		fnvlist_free(pc);
192 
193 	}
194 
195 	return (nvl);
196 }
197 
198 static nvlist_t *
199 zen_umc_dump_chan(zen_umc_chan_t *chan)
200 {
201 	nvlist_t *nvl, *hash;
202 	nvlist_t *rules[ZEN_UMC_MAX_CS_RULES];
203 	nvlist_t *offsets[ZEN_UMC_MAX_DRAM_OFFSET];
204 	nvlist_t *dimms[ZEN_UMC_MAX_DIMMS];
205 
206 	nvl = fnvlist_alloc();
207 	fnvlist_add_uint32(nvl, "chan_flags", chan->chan_flags);
208 	fnvlist_add_uint32(nvl, "chan_fabid", chan->chan_fabid);
209 	fnvlist_add_uint32(nvl, "chan_instid", chan->chan_instid);
210 	fnvlist_add_uint32(nvl, "chan_logid", chan->chan_logid);
211 	fnvlist_add_uint32(nvl, "chan_np2_space0", chan->chan_np2_space0);
212 
213 	for (uint_t i = 0; i < chan->chan_nrules; i++) {
214 		rules[i] = zen_umc_dump_dram_rule(&chan->chan_rules[i]);
215 	}
216 
217 	for (uint_t i = 0; i < chan->chan_nrules - 1; i++) {
218 		offsets[i] = fnvlist_alloc();
219 		fnvlist_add_boolean_value(offsets[i], "cho_valid",
220 		    chan->chan_offsets[i].cho_valid);
221 		fnvlist_add_uint64(offsets[i], "cho_offset",
222 		    chan->chan_offsets[i].cho_offset);
223 	}
224 
225 	for (uint_t i = 0; i < ZEN_UMC_MAX_DIMMS; i++) {
226 		dimms[i] = zen_umc_dump_dimm(&chan->chan_dimms[i]);
227 	}
228 
229 	fnvlist_add_nvlist_array(nvl, "chan_rules", rules, chan->chan_nrules);
230 	fnvlist_add_nvlist_array(nvl, "chan_offsets", offsets,
231 	    chan->chan_nrules - 1);
232 	fnvlist_add_nvlist_array(nvl, "chan_dimms", dimms, ZEN_UMC_MAX_DIMMS);
233 	hash = zen_umc_dump_chan_hash(&chan->chan_hash);
234 	fnvlist_add_nvlist(nvl, "chan_hash", hash);
235 
236 	for (uint_t i = 0; i < chan->chan_nrules; i++) {
237 		nvlist_free(rules[i]);
238 	}
239 
240 	for (uint_t i = 0; i < chan->chan_nrules - 1; i++) {
241 		nvlist_free(offsets[i]);
242 	}
243 
244 	for (uint_t i = 0; i < ZEN_UMC_MAX_DIMMS; i++) {
245 		nvlist_free(dimms[i]);
246 	}
247 
248 	nvlist_free(hash);
249 
250 	return (nvl);
251 }
252 
253 static nvlist_t *
254 zen_umc_dump_df(zen_umc_df_t *df)
255 {
256 	nvlist_t *nvl;
257 	nvlist_t *rules[ZEN_UMC_MAX_DRAM_RULES];
258 	nvlist_t *remap[ZEN_UMC_MAX_CS_REMAPS];
259 	nvlist_t *chan[ZEN_UMC_MAX_UMCS];
260 
261 	nvl = fnvlist_alloc();
262 	fnvlist_add_uint32(nvl, "zud_flags", df->zud_flags);
263 	fnvlist_add_uint32(nvl, "zud_dfno", df->zud_dfno);
264 	fnvlist_add_uint32(nvl, "zud_ccm_inst", df->zud_ccm_inst);
265 	fnvlist_add_uint64(nvl, "zud_hole_base", df->zud_hole_base);
266 
267 	for (uint_t i = 0; i < df->zud_dram_nrules; i++) {
268 		rules[i] = zen_umc_dump_dram_rule(&df->zud_rules[i]);
269 	}
270 
271 	for (uint_t i = 0; i < df->zud_cs_nremap; i++) {
272 		remap[i] = fnvlist_alloc();
273 		fnvlist_add_uint16_array(remap[i], "csr_remaps",
274 		    df->zud_remap[i].csr_remaps, df->zud_remap[i].csr_nremaps);
275 	}
276 
277 	for (uint_t i = 0; i < df->zud_nchan; i++) {
278 		chan[i] = zen_umc_dump_chan(&df->zud_chan[i]);
279 	}
280 
281 	fnvlist_add_nvlist_array(nvl, "zud_rules", rules, df->zud_dram_nrules);
282 	fnvlist_add_nvlist_array(nvl, "zud_remap", remap, df->zud_cs_nremap);
283 	fnvlist_add_nvlist_array(nvl, "zud_chan", chan, df->zud_nchan);
284 
285 	for (uint_t i = 0; i < df->zud_dram_nrules; i++) {
286 		nvlist_free(rules[i]);
287 	}
288 
289 	for (uint_t i = 0; i < df->zud_cs_nremap; i++) {
290 		nvlist_free(remap[i]);
291 	}
292 
293 	for (uint_t i = 0; i < df->zud_nchan; i++) {
294 		nvlist_free(chan[i]);
295 	}
296 
297 	return (nvl);
298 }
299 
300 nvlist_t *
301 zen_umc_dump_decoder(zen_umc_t *umc)
302 {
303 	nvlist_t *nvl, *umc_nvl, *decomp;
304 	nvlist_t *dfs[ZEN_UMC_MAX_DFS];
305 
306 	nvl = fnvlist_alloc();
307 	fnvlist_add_uint32(nvl, "mc_dump_version", 0);
308 	fnvlist_add_string(nvl, "mc_dump_driver", "zen_umc");
309 
310 	umc_nvl = fnvlist_alloc();
311 	fnvlist_add_uint64(umc_nvl, "umc_tom", umc->umc_tom);
312 	fnvlist_add_uint64(umc_nvl, "umc_tom2", umc->umc_tom2);
313 	fnvlist_add_uint32(umc_nvl, "umc_family", umc->umc_family);
314 	fnvlist_add_uint32(umc_nvl, "umc_df_rev", umc->umc_df_rev);
315 
316 	decomp = fnvlist_alloc();
317 	fnvlist_add_uint32(decomp, "dfd_sock_mask",
318 	    umc->umc_decomp.dfd_sock_mask);
319 	fnvlist_add_uint32(decomp, "dfd_die_mask",
320 	    umc->umc_decomp.dfd_die_mask);
321 	fnvlist_add_uint32(decomp, "dfd_node_mask",
322 	    umc->umc_decomp.dfd_node_mask);
323 	fnvlist_add_uint32(decomp, "dfd_comp_mask",
324 	    umc->umc_decomp.dfd_comp_mask);
325 	fnvlist_add_uint8(decomp, "dfd_sock_shift",
326 	    umc->umc_decomp.dfd_sock_shift);
327 	fnvlist_add_uint8(decomp, "dfd_die_shift",
328 	    umc->umc_decomp.dfd_die_shift);
329 	fnvlist_add_uint8(decomp, "dfd_node_shift",
330 	    umc->umc_decomp.dfd_node_shift);
331 	fnvlist_add_uint8(decomp, "dfd_comp_shift",
332 	    umc->umc_decomp.dfd_comp_shift);
333 	fnvlist_add_nvlist(umc_nvl, "umc_decomp", decomp);
334 	nvlist_free(decomp);
335 
336 	for (uint_t i = 0; i < umc->umc_ndfs; i++) {
337 		dfs[i] = zen_umc_dump_df(&umc->umc_dfs[i]);
338 	}
339 
340 	fnvlist_add_nvlist_array(umc_nvl, "umc_dfs", dfs, umc->umc_ndfs);
341 	fnvlist_add_nvlist(nvl, "zen_umc", umc_nvl);
342 	for (uint_t i = 0; i < umc->umc_ndfs; i++) {
343 		nvlist_free(dfs[i]);
344 	}
345 
346 	return (nvl);
347 }
348 
349 static boolean_t
350 zen_umc_restore_dram_rule(nvlist_t *nvl, df_dram_rule_t *rule)
351 {
352 	return (nvlist_lookup_pairs(nvl, 0,
353 	    "ddr_flags", DATA_TYPE_UINT32, &rule->ddr_flags,
354 	    "ddr_base", DATA_TYPE_UINT64, &rule->ddr_base,
355 	    "ddr_limit", DATA_TYPE_UINT64, &rule->ddr_limit,
356 	    "ddr_dest_fabid", DATA_TYPE_UINT16, &rule->ddr_dest_fabid,
357 	    "ddr_sock_ileave_bits", DATA_TYPE_UINT8,
358 	    &rule->ddr_sock_ileave_bits,
359 	    "ddr_die_ileave_bits", DATA_TYPE_UINT8, &rule->ddr_die_ileave_bits,
360 	    "ddr_addr_start", DATA_TYPE_UINT8, &rule->ddr_addr_start,
361 	    "ddr_remap_ent", DATA_TYPE_UINT8, &rule->ddr_remap_ent,
362 	    "ddr_chan_ileave", DATA_TYPE_UINT32, &rule->ddr_chan_ileave,
363 	    NULL) == 0);
364 }
365 
366 static boolean_t
367 zen_umc_restore_cs(nvlist_t *nvl, umc_cs_t *cs)
368 {
369 	nvlist_t *base, *sec;
370 	uint8_t *bank_bits, *col_bits, *rm_bits, *rm_bits_sec;
371 	uint_t nbanks, ncols, nrm, nrm_sec;
372 
373 	if (nvlist_lookup_pairs(nvl, 0,
374 	    "ucs_base", DATA_TYPE_NVLIST, &base,
375 	    "ucs_sec", DATA_TYPE_NVLIST, &sec,
376 	    "ucs_base_mask", DATA_TYPE_UINT64, &cs->ucs_base_mask,
377 	    "ucs_sec_mask", DATA_TYPE_UINT64, &cs->ucs_sec_mask,
378 	    "ucs_nrow_lo", DATA_TYPE_UINT8, &cs->ucs_nrow_lo,
379 	    "ucs_nrow_hi", DATA_TYPE_UINT8, &cs->ucs_nrow_hi,
380 	    "ucs_nbank_groups", DATA_TYPE_UINT8, &cs->ucs_nbank_groups,
381 	    "ucs_cs_xor", DATA_TYPE_UINT8, &cs->ucs_cs_xor,
382 	    "ucs_row_hi_bit", DATA_TYPE_UINT8, &cs->ucs_row_hi_bit,
383 	    "ucs_row_low_bit", DATA_TYPE_UINT8, &cs->ucs_row_low_bit,
384 	    "ucs_bank_bits", DATA_TYPE_UINT8_ARRAY, &bank_bits, &nbanks,
385 	    "ucs_col_bits", DATA_TYPE_UINT8_ARRAY, &col_bits, &ncols,
386 	    "ucs_inv_msbs", DATA_TYPE_UINT8, &cs->ucs_inv_msbs,
387 	    "ucs_rm_bits", DATA_TYPE_UINT8_ARRAY, &rm_bits, &nrm,
388 	    "ucs_inv_msbs_sec", DATA_TYPE_UINT8, &cs->ucs_inv_msbs_sec,
389 	    "ucs_rm_bits_sec", DATA_TYPE_UINT8_ARRAY, &rm_bits_sec, &nrm_sec,
390 	    "ucs_subchan", DATA_TYPE_UINT8, &cs->ucs_subchan,
391 	    NULL) != 0) {
392 		return (B_FALSE);
393 	}
394 
395 	if (nbanks > ZEN_UMC_MAX_BANK_BITS ||
396 	    ncols > ZEN_UMC_MAX_COL_BITS ||
397 	    nrm > ZEN_UMC_MAX_RM_BITS ||
398 	    nrm != nrm_sec) {
399 		return (B_FALSE);
400 	}
401 
402 	cs->ucs_nbanks = nbanks;
403 	cs->ucs_ncol = ncols;
404 	cs->ucs_nrm = nrm;
405 
406 	bcopy(bank_bits, cs->ucs_bank_bits, cs->ucs_nbanks *
407 	    sizeof (uint8_t));
408 	bcopy(col_bits, cs->ucs_col_bits, cs->ucs_ncol * sizeof (uint8_t));
409 	bcopy(rm_bits, cs->ucs_rm_bits, cs->ucs_nrm * sizeof (uint8_t));
410 	bcopy(rm_bits_sec, cs->ucs_rm_bits_sec, cs->ucs_nrm *
411 	    sizeof (uint8_t));
412 
413 	if (nvlist_lookup_pairs(base, 0,
414 	    "udb_base", DATA_TYPE_UINT64, &cs->ucs_base.udb_base,
415 	    "udb_valid", DATA_TYPE_UINT8, &cs->ucs_base.udb_valid,
416 	    NULL) != 0) {
417 		return (B_FALSE);
418 	}
419 
420 	if (nvlist_lookup_pairs(sec, 0,
421 	    "udb_base", DATA_TYPE_UINT64, &cs->ucs_sec.udb_base,
422 	    "udb_valid", DATA_TYPE_UINT8, &cs->ucs_sec.udb_valid,
423 	    NULL) != 0) {
424 		return (B_FALSE);
425 	}
426 
427 	return (B_TRUE);
428 }
429 
430 static boolean_t
431 zen_umc_restore_dimm(nvlist_t *nvl, umc_dimm_t *dimm)
432 {
433 	nvlist_t **cs;
434 	uint_t ncs;
435 
436 	if (nvlist_lookup_pairs(nvl, 0,
437 	    "ud_flags", DATA_TYPE_UINT32, &dimm->ud_flags,
438 	    "ud_width", DATA_TYPE_UINT32, &dimm->ud_width,
439 	    "ud_type", DATA_TYPE_UINT32, &dimm->ud_type,
440 	    "ud_kind", DATA_TYPE_UINT32, &dimm->ud_kind,
441 	    "ud_dimmno", DATA_TYPE_UINT32, &dimm->ud_dimmno,
442 	    "ud_cs", DATA_TYPE_NVLIST_ARRAY, &cs, &ncs,
443 	    NULL) != 0) {
444 		return (B_FALSE);
445 	}
446 
447 	if (ncs != ZEN_UMC_MAX_CS_PER_DIMM) {
448 		return (B_FALSE);
449 	}
450 
451 	for (uint_t i = 0; i < ZEN_UMC_MAX_CS_PER_DIMM; i++) {
452 		if (!zen_umc_restore_cs(cs[i], &dimm->ud_cs[i])) {
453 			return (B_FALSE);
454 		}
455 	}
456 
457 	return (B_TRUE);
458 }
459 
460 static boolean_t
461 zen_umc_restore_hash(nvlist_t *nvl, umc_chan_hash_t *hash)
462 {
463 	if (nvlist_lookup_uint32(nvl, "uch_flags", &hash->uch_flags) != 0) {
464 		return (B_FALSE);
465 	}
466 
467 	if (hash->uch_flags & UMC_CHAN_HASH_F_BANK) {
468 		nvlist_t **banks;
469 		uint_t nbanks;
470 
471 		if (nvlist_lookup_nvlist_array(nvl, "uch_bank_hashes", &banks,
472 		    &nbanks) != 0) {
473 			return (B_FALSE);
474 		}
475 
476 		if (nbanks != ZEN_UMC_MAX_CHAN_BANK_HASH) {
477 			return (B_FALSE);
478 		}
479 
480 		for (uint_t i = 0; i < nbanks; i++) {
481 			if (nvlist_lookup_pairs(banks[i], 0,
482 			    "ubh_row_xor", DATA_TYPE_UINT32,
483 			    &hash->uch_bank_hashes[i].ubh_row_xor,
484 			    "ubh_col_xor", DATA_TYPE_UINT32,
485 			    &hash->uch_bank_hashes[i].ubh_col_xor,
486 			    "ubh_en", DATA_TYPE_BOOLEAN_VALUE,
487 			    &hash->uch_bank_hashes[i].ubh_en,
488 			    NULL) != 0) {
489 				return (B_FALSE);
490 			}
491 		}
492 	}
493 
494 	if (hash->uch_flags & UMC_CHAN_HASH_F_RM) {
495 		nvlist_t **rm;
496 		uint_t nrm;
497 
498 		if (nvlist_lookup_nvlist_array(nvl, "uch_rm_hashes", &rm,
499 		    &nrm) != 0) {
500 			return (B_FALSE);
501 		}
502 
503 		if (nrm != ZEN_UMC_MAX_CHAN_RM_HASH) {
504 			return (B_FALSE);
505 		}
506 
507 		for (uint_t i = 0; i < nrm; i++) {
508 			if (nvlist_lookup_pairs(rm[i], 0,
509 			    "uah_addr_xor", DATA_TYPE_UINT64,
510 			    &hash->uch_rm_hashes[i].uah_addr_xor,
511 			    "uah_en", DATA_TYPE_BOOLEAN_VALUE,
512 			    &hash->uch_rm_hashes[i].uah_en,
513 			    NULL) != 0) {
514 				return (B_FALSE);
515 			}
516 		}
517 	}
518 
519 	if (hash->uch_flags & UMC_CHAN_HASH_F_CS) {
520 		nvlist_t **cs;
521 		uint_t ncs;
522 
523 		if (nvlist_lookup_nvlist_array(nvl, "uch_cs_hashes", &cs,
524 		    &ncs) != 0) {
525 			return (B_FALSE);
526 		}
527 
528 		if (ncs != ZEN_UMC_MAX_CHAN_CS_HASH) {
529 			return (B_FALSE);
530 		}
531 
532 		for (uint_t i = 0; i < ncs; i++) {
533 			if (nvlist_lookup_pairs(cs[i], 0,
534 			    "uah_addr_xor", DATA_TYPE_UINT64,
535 			    &hash->uch_cs_hashes[i].uah_addr_xor,
536 			    "uah_en", DATA_TYPE_BOOLEAN_VALUE,
537 			    &hash->uch_cs_hashes[i].uah_en,
538 			    NULL) != 0) {
539 				return (B_FALSE);
540 			}
541 		}
542 	}
543 
544 	if (hash->uch_flags & UMC_CHAN_HASH_F_PC) {
545 		nvlist_t *pc;
546 
547 		if (nvlist_lookup_nvlist(nvl, "uch_pch_hash", &pc) != 0) {
548 			return (B_FALSE);
549 		}
550 
551 		if (nvlist_lookup_pairs(pc, 0,
552 		    "uph_row_xor", DATA_TYPE_UINT32,
553 		    &hash->uch_pc_hash.uph_row_xor,
554 		    "uph_col_xor", DATA_TYPE_UINT32,
555 		    &hash->uch_pc_hash.uph_col_xor,
556 		    "uph_bank_xor", DATA_TYPE_UINT32,
557 		    &hash->uch_pc_hash.uph_bank_xor,
558 		    "uph_en", DATA_TYPE_BOOLEAN_VALUE,
559 		    &hash->uch_pc_hash.uph_en,
560 		    NULL) != 0) {
561 			return (B_FALSE);
562 		}
563 	}
564 	return (B_TRUE);
565 }
566 
567 static boolean_t
568 zen_umc_restore_chan(nvlist_t *nvl, zen_umc_chan_t *chan)
569 {
570 	uint_t noffsets, ndimms;
571 	nvlist_t **rules, **offsets, **dimms, *hash;
572 
573 	if (nvlist_lookup_pairs(nvl, 0,
574 	    "chan_flags", DATA_TYPE_UINT32, &chan->chan_flags,
575 	    "chan_fabid", DATA_TYPE_UINT32, &chan->chan_fabid,
576 	    "chan_instid", DATA_TYPE_UINT32, &chan->chan_instid,
577 	    "chan_logid", DATA_TYPE_UINT32, &chan->chan_logid,
578 	    "chan_rules", DATA_TYPE_NVLIST_ARRAY, &rules, &chan->chan_nrules,
579 	    "chan_np2_space0", DATA_TYPE_UINT32, &chan->chan_np2_space0,
580 	    "chan_offsets", DATA_TYPE_NVLIST_ARRAY, &offsets, &noffsets,
581 	    "chan_dimms", DATA_TYPE_NVLIST_ARRAY, &dimms, &ndimms,
582 	    "chan_hash", DATA_TYPE_NVLIST, &hash,
583 	    NULL) != 0) {
584 		return (B_FALSE);
585 	}
586 
587 	if (chan->chan_nrules > ZEN_UMC_MAX_CS_RULES ||
588 	    noffsets != chan->chan_nrules - 1 || ndimms != ZEN_UMC_MAX_DIMMS) {
589 		return (B_FALSE);
590 	}
591 
592 	for (uint_t i = 0; i < chan->chan_nrules; i++) {
593 		if (!zen_umc_restore_dram_rule(rules[i],
594 		    &chan->chan_rules[i])) {
595 			return (B_FALSE);
596 		}
597 	}
598 
599 	for (uint_t i = 0; i < chan->chan_nrules - 1; i++) {
600 		chan_offset_t *coff = &chan->chan_offsets[i];
601 
602 		if (nvlist_lookup_pairs(offsets[i], 0,
603 		    "cho_valid", DATA_TYPE_BOOLEAN_VALUE, &coff->cho_valid,
604 		    "cho_offset", DATA_TYPE_UINT64, &coff->cho_offset,
605 		    NULL) != 0) {
606 			return (B_FALSE);
607 		}
608 	}
609 
610 	for (uint_t i = 0; i < ZEN_UMC_MAX_DIMMS; i++) {
611 		if (!zen_umc_restore_dimm(dimms[i], &chan->chan_dimms[i])) {
612 			return (B_FALSE);
613 		}
614 	}
615 
616 	if (!zen_umc_restore_hash(hash, &chan->chan_hash)) {
617 		return (B_FALSE);
618 	}
619 
620 	return (B_TRUE);
621 }
622 
623 static boolean_t
624 zen_umc_restore_df(nvlist_t *nvl, zen_umc_df_t *df)
625 {
626 	nvlist_t **rules, **chan, **remap;
627 
628 	if (nvlist_lookup_pairs(nvl, 0,
629 	    "zud_flags", DATA_TYPE_UINT32, &df->zud_flags,
630 	    "zud_dfno", DATA_TYPE_UINT32, &df->zud_dfno,
631 	    "zud_ccm_inst", DATA_TYPE_UINT32, &df->zud_ccm_inst,
632 	    "zud_hole_base", DATA_TYPE_UINT64, &df->zud_hole_base,
633 	    "zud_rules", DATA_TYPE_NVLIST_ARRAY, &rules, &df->zud_dram_nrules,
634 	    "zud_remap", DATA_TYPE_NVLIST_ARRAY, &remap, &df->zud_cs_nremap,
635 	    "zud_chan", DATA_TYPE_NVLIST_ARRAY, &chan, &df->zud_nchan,
636 	    NULL != 0) ||
637 	    df->zud_dram_nrules > ZEN_UMC_MAX_DRAM_RULES ||
638 	    df->zud_cs_nremap > ZEN_UMC_MAX_CS_REMAPS ||
639 	    df->zud_nchan > ZEN_UMC_MAX_UMCS) {
640 		return (B_FALSE);
641 	}
642 
643 	for (uint_t i = 0; i < df->zud_dram_nrules; i++) {
644 		if (!zen_umc_restore_dram_rule(rules[i], &df->zud_rules[i])) {
645 			return (B_FALSE);
646 		}
647 	}
648 
649 	for (uint_t i = 0; i < df->zud_cs_nremap; i++) {
650 		uint16_t *u16p;
651 		if (nvlist_lookup_uint16_array(remap[i], "csr_remaps", &u16p,
652 		    &df->zud_remap[i].csr_nremaps) != 0 ||
653 		    df->zud_remap[i].csr_nremaps > ZEN_UMC_MAX_REMAP_ENTS) {
654 			return (B_FALSE);
655 		}
656 		bcopy(u16p, df->zud_remap[i].csr_remaps,
657 		    df->zud_remap[i].csr_nremaps);
658 	}
659 
660 	for (uint_t i = 0; i < df->zud_nchan; i++) {
661 		if (!zen_umc_restore_chan(chan[i], &df->zud_chan[i])) {
662 			return (B_FALSE);
663 		}
664 	}
665 
666 	return (B_TRUE);
667 }
668 
669 boolean_t
670 zen_umc_restore_decoder(nvlist_t *nvl, zen_umc_t *umc)
671 {
672 	uint32_t vers;
673 	char *driver;
674 	nvlist_t *umc_nvl, *decomp, **dfs;
675 	bzero(umc, sizeof (zen_umc_t));
676 
677 	if (nvlist_lookup_pairs(nvl, 0,
678 	    "mc_dump_version", DATA_TYPE_UINT32, &vers,
679 	    "mc_dump_driver", DATA_TYPE_STRING, &driver,
680 	    NULL) != 0 || vers != 0 || strcmp(driver, "zen_umc") != 0 ||
681 	    nvlist_lookup_nvlist(nvl, "zen_umc", &umc_nvl) != 0) {
682 		return (B_FALSE);
683 	}
684 
685 	if (nvlist_lookup_pairs(umc_nvl, 0,
686 	    "umc_tom", DATA_TYPE_UINT64, &umc->umc_tom,
687 	    "umc_tom2", DATA_TYPE_UINT64, &umc->umc_tom2,
688 	    "umc_family", DATA_TYPE_UINT32, &umc->umc_family,
689 	    "umc_df_rev", DATA_TYPE_UINT32, &umc->umc_df_rev,
690 	    "umc_decomp", DATA_TYPE_NVLIST, &decomp,
691 	    "umc_dfs", DATA_TYPE_NVLIST_ARRAY, &dfs, &umc->umc_ndfs,
692 	    NULL) != 0 || umc->umc_ndfs > ZEN_UMC_MAX_DFS) {
693 		return (B_FALSE);
694 	}
695 
696 
697 	if (nvlist_lookup_pairs(decomp, 0,
698 	    "dfd_sock_mask", DATA_TYPE_UINT32, &umc->umc_decomp.dfd_sock_mask,
699 	    "dfd_die_mask", DATA_TYPE_UINT32, &umc->umc_decomp.dfd_die_mask,
700 	    "dfd_node_mask", DATA_TYPE_UINT32, &umc->umc_decomp.dfd_node_mask,
701 	    "dfd_comp_mask", DATA_TYPE_UINT32, &umc->umc_decomp.dfd_comp_mask,
702 	    "dfd_sock_shift", DATA_TYPE_UINT8, &umc->umc_decomp.dfd_sock_shift,
703 	    "dfd_die_shift", DATA_TYPE_UINT8, &umc->umc_decomp.dfd_die_shift,
704 	    "dfd_node_shift", DATA_TYPE_UINT8, &umc->umc_decomp.dfd_node_shift,
705 	    "dfd_comp_shift", DATA_TYPE_UINT8, &umc->umc_decomp.dfd_comp_shift,
706 	    NULL) != 0) {
707 		return (B_FALSE);
708 	}
709 
710 	for (uint_t i = 0; i < umc->umc_ndfs; i++) {
711 		if (!zen_umc_restore_df(dfs[i], &umc->umc_dfs[i])) {
712 			return (B_FALSE);
713 		}
714 	}
715 
716 	return (B_TRUE);
717 }
718