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 2025 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 *
zen_umc_dump_dram_rule(df_dram_rule_t * rule)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 *
zen_umc_dump_cs(umc_cs_t * cs)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_uint32(nvl, "ucs_flags", cs->ucs_flags);
62 fnvlist_add_uint64(base, "udb_base", cs->ucs_base.udb_base);
63 fnvlist_add_uint8(base, "udb_valid", cs->ucs_base.udb_valid);
64 fnvlist_add_nvlist(nvl, "ucs_base", base);
65 nvlist_free(base);
66 fnvlist_add_uint64(sec, "udb_base", cs->ucs_sec.udb_base);
67 fnvlist_add_uint8(sec, "udb_valid", cs->ucs_sec.udb_valid);
68 fnvlist_add_nvlist(nvl, "ucs_sec", sec);
69 nvlist_free(sec);
70 fnvlist_add_uint64(nvl, "ucs_base_mask", cs->ucs_base_mask);
71 fnvlist_add_uint64(nvl, "ucs_sec_mask", cs->ucs_sec_mask);
72 fnvlist_add_uint8(nvl, "ucs_nrow_lo", cs->ucs_nrow_lo);
73 fnvlist_add_uint8(nvl, "ucs_nrow_hi", cs->ucs_nrow_hi);
74 fnvlist_add_uint8(nvl, "ucs_nbank_groups", cs->ucs_nbank_groups);
75 fnvlist_add_uint8(nvl, "ucs_cs_xor", cs->ucs_cs_xor);
76 fnvlist_add_uint8(nvl, "ucs_row_hi_bit", cs->ucs_row_hi_bit);
77 fnvlist_add_uint8(nvl, "ucs_row_low_bit", cs->ucs_row_low_bit);
78 fnvlist_add_uint8_array(nvl, "ucs_bank_bits", cs->ucs_bank_bits,
79 cs->ucs_nbanks);
80 fnvlist_add_uint8_array(nvl, "ucs_col_bits", cs->ucs_col_bits,
81 cs->ucs_ncol);
82 fnvlist_add_uint8(nvl, "ucs_inv_msbs", cs->ucs_inv_msbs);
83 fnvlist_add_uint8_array(nvl, "ucs_rm_bits", cs->ucs_rm_bits,
84 cs->ucs_nrm);
85 fnvlist_add_uint8(nvl, "ucs_inv_msbs_sec", cs->ucs_inv_msbs_sec);
86 fnvlist_add_uint8_array(nvl, "ucs_rm_bits_sec", cs->ucs_rm_bits_sec,
87 cs->ucs_nrm);
88 fnvlist_add_uint8(nvl, "ucs_subchan", cs->ucs_subchan);
89
90 return (nvl);
91 }
92
93 static nvlist_t *
zen_umc_dump_dimm(umc_dimm_t * dimm)94 zen_umc_dump_dimm(umc_dimm_t *dimm)
95 {
96 nvlist_t *nvl = fnvlist_alloc();
97 nvlist_t *cs[ZEN_UMC_MAX_CS_PER_DIMM];
98
99 fnvlist_add_uint32(nvl, "ud_flags", dimm->ud_flags);
100 fnvlist_add_uint32(nvl, "ud_width", dimm->ud_width);
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 *
zen_umc_dump_chan_hash(umc_chan_hash_t * hash)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 *
zen_umc_dump_chan(zen_umc_chan_t * chan)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 fnvlist_add_uint32(nvl, "chan_type", chan->chan_type);
213
214 for (uint_t i = 0; i < chan->chan_nrules; i++) {
215 rules[i] = zen_umc_dump_dram_rule(&chan->chan_rules[i]);
216 }
217
218 for (uint_t i = 0; i < chan->chan_nrules - 1; i++) {
219 offsets[i] = fnvlist_alloc();
220 fnvlist_add_boolean_value(offsets[i], "cho_valid",
221 chan->chan_offsets[i].cho_valid);
222 fnvlist_add_uint64(offsets[i], "cho_offset",
223 chan->chan_offsets[i].cho_offset);
224 }
225
226 for (uint_t i = 0; i < ZEN_UMC_MAX_DIMMS; i++) {
227 dimms[i] = zen_umc_dump_dimm(&chan->chan_dimms[i]);
228 }
229
230 fnvlist_add_nvlist_array(nvl, "chan_rules", rules, chan->chan_nrules);
231 fnvlist_add_nvlist_array(nvl, "chan_offsets", offsets,
232 chan->chan_nrules - 1);
233 fnvlist_add_nvlist_array(nvl, "chan_dimms", dimms, ZEN_UMC_MAX_DIMMS);
234 hash = zen_umc_dump_chan_hash(&chan->chan_hash);
235 fnvlist_add_nvlist(nvl, "chan_hash", hash);
236
237 for (uint_t i = 0; i < chan->chan_nrules; i++) {
238 nvlist_free(rules[i]);
239 }
240
241 for (uint_t i = 0; i < chan->chan_nrules - 1; i++) {
242 nvlist_free(offsets[i]);
243 }
244
245 for (uint_t i = 0; i < ZEN_UMC_MAX_DIMMS; i++) {
246 nvlist_free(dimms[i]);
247 }
248
249 nvlist_free(hash);
250
251 return (nvl);
252 }
253
254 static nvlist_t *
zen_umc_dump_df(zen_umc_df_t * df)255 zen_umc_dump_df(zen_umc_df_t *df)
256 {
257 nvlist_t *nvl;
258 nvlist_t *rules[ZEN_UMC_MAX_DRAM_RULES];
259 nvlist_t *remap[ZEN_UMC_MAX_CS_REMAPS];
260 nvlist_t *chan[ZEN_UMC_MAX_UMCS];
261
262 nvl = fnvlist_alloc();
263 fnvlist_add_uint32(nvl, "zud_flags", df->zud_flags);
264 fnvlist_add_uint32(nvl, "zud_dfno", df->zud_dfno);
265 fnvlist_add_uint32(nvl, "zud_ccm_inst", df->zud_ccm_inst);
266 fnvlist_add_uint64(nvl, "zud_hole_base", df->zud_hole_base);
267
268 for (uint_t i = 0; i < df->zud_dram_nrules; i++) {
269 rules[i] = zen_umc_dump_dram_rule(&df->zud_rules[i]);
270 }
271
272 for (uint_t i = 0; i < df->zud_cs_nremap; i++) {
273 remap[i] = fnvlist_alloc();
274 fnvlist_add_uint16_array(remap[i], "csr_remaps",
275 df->zud_remap[i].csr_remaps, df->zud_remap[i].csr_nremaps);
276 }
277
278 for (uint_t i = 0; i < df->zud_nchan; i++) {
279 chan[i] = zen_umc_dump_chan(&df->zud_chan[i]);
280 }
281
282 fnvlist_add_nvlist_array(nvl, "zud_rules", rules, df->zud_dram_nrules);
283 fnvlist_add_nvlist_array(nvl, "zud_remap", remap, df->zud_cs_nremap);
284 fnvlist_add_nvlist_array(nvl, "zud_chan", chan, df->zud_nchan);
285
286 for (uint_t i = 0; i < df->zud_dram_nrules; i++) {
287 nvlist_free(rules[i]);
288 }
289
290 for (uint_t i = 0; i < df->zud_cs_nremap; i++) {
291 nvlist_free(remap[i]);
292 }
293
294 for (uint_t i = 0; i < df->zud_nchan; i++) {
295 nvlist_free(chan[i]);
296 }
297
298 return (nvl);
299 }
300
301 nvlist_t *
zen_umc_dump_decoder(zen_umc_t * umc)302 zen_umc_dump_decoder(zen_umc_t *umc)
303 {
304 nvlist_t *nvl, *umc_nvl, *decomp;
305 nvlist_t *dfs[ZEN_UMC_MAX_DFS];
306
307 nvl = fnvlist_alloc();
308 fnvlist_add_uint32(nvl, "mc_dump_version", 0);
309 fnvlist_add_string(nvl, "mc_dump_driver", "zen_umc");
310
311 umc_nvl = fnvlist_alloc();
312 fnvlist_add_uint64(umc_nvl, "umc_tom", umc->umc_tom);
313 fnvlist_add_uint64(umc_nvl, "umc_tom2", umc->umc_tom2);
314 fnvlist_add_uint32(umc_nvl, "umc_family", umc->umc_family);
315 fnvlist_add_uint32(umc_nvl, "umc_df_rev", umc->umc_df_rev);
316
317 decomp = fnvlist_alloc();
318 fnvlist_add_uint32(decomp, "dfd_sock_mask",
319 umc->umc_decomp.dfd_sock_mask);
320 fnvlist_add_uint32(decomp, "dfd_die_mask",
321 umc->umc_decomp.dfd_die_mask);
322 fnvlist_add_uint32(decomp, "dfd_node_mask",
323 umc->umc_decomp.dfd_node_mask);
324 fnvlist_add_uint32(decomp, "dfd_comp_mask",
325 umc->umc_decomp.dfd_comp_mask);
326 fnvlist_add_uint8(decomp, "dfd_sock_shift",
327 umc->umc_decomp.dfd_sock_shift);
328 fnvlist_add_uint8(decomp, "dfd_die_shift",
329 umc->umc_decomp.dfd_die_shift);
330 fnvlist_add_uint8(decomp, "dfd_node_shift",
331 umc->umc_decomp.dfd_node_shift);
332 fnvlist_add_uint8(decomp, "dfd_comp_shift",
333 umc->umc_decomp.dfd_comp_shift);
334 fnvlist_add_nvlist(umc_nvl, "umc_decomp", decomp);
335 nvlist_free(decomp);
336
337 for (uint_t i = 0; i < umc->umc_ndfs; i++) {
338 dfs[i] = zen_umc_dump_df(&umc->umc_dfs[i]);
339 }
340
341 fnvlist_add_nvlist_array(umc_nvl, "umc_dfs", dfs, umc->umc_ndfs);
342 fnvlist_add_nvlist(nvl, "zen_umc", umc_nvl);
343 for (uint_t i = 0; i < umc->umc_ndfs; i++) {
344 nvlist_free(dfs[i]);
345 }
346
347 return (nvl);
348 }
349
350 static boolean_t
zen_umc_restore_dram_rule(nvlist_t * nvl,df_dram_rule_t * rule)351 zen_umc_restore_dram_rule(nvlist_t *nvl, df_dram_rule_t *rule)
352 {
353 return (nvlist_lookup_pairs(nvl, 0,
354 "ddr_flags", DATA_TYPE_UINT32, &rule->ddr_flags,
355 "ddr_base", DATA_TYPE_UINT64, &rule->ddr_base,
356 "ddr_limit", DATA_TYPE_UINT64, &rule->ddr_limit,
357 "ddr_dest_fabid", DATA_TYPE_UINT16, &rule->ddr_dest_fabid,
358 "ddr_sock_ileave_bits", DATA_TYPE_UINT8,
359 &rule->ddr_sock_ileave_bits,
360 "ddr_die_ileave_bits", DATA_TYPE_UINT8, &rule->ddr_die_ileave_bits,
361 "ddr_addr_start", DATA_TYPE_UINT8, &rule->ddr_addr_start,
362 "ddr_remap_ent", DATA_TYPE_UINT8, &rule->ddr_remap_ent,
363 "ddr_chan_ileave", DATA_TYPE_UINT32, &rule->ddr_chan_ileave,
364 NULL) == 0);
365 }
366
367 static boolean_t
zen_umc_restore_cs(nvlist_t * nvl,umc_cs_t * cs)368 zen_umc_restore_cs(nvlist_t *nvl, umc_cs_t *cs)
369 {
370 nvlist_t *base, *sec;
371 uint8_t *bank_bits, *col_bits, *rm_bits, *rm_bits_sec;
372 uint_t nbanks, ncols, nrm, nrm_sec;
373
374 if (nvlist_lookup_pairs(nvl, 0,
375 "ucs_flags", DATA_TYPE_UINT32, &cs->ucs_flags,
376 "ucs_base", DATA_TYPE_NVLIST, &base,
377 "ucs_sec", DATA_TYPE_NVLIST, &sec,
378 "ucs_base_mask", DATA_TYPE_UINT64, &cs->ucs_base_mask,
379 "ucs_sec_mask", DATA_TYPE_UINT64, &cs->ucs_sec_mask,
380 "ucs_nrow_lo", DATA_TYPE_UINT8, &cs->ucs_nrow_lo,
381 "ucs_nrow_hi", DATA_TYPE_UINT8, &cs->ucs_nrow_hi,
382 "ucs_nbank_groups", DATA_TYPE_UINT8, &cs->ucs_nbank_groups,
383 "ucs_cs_xor", DATA_TYPE_UINT8, &cs->ucs_cs_xor,
384 "ucs_row_hi_bit", DATA_TYPE_UINT8, &cs->ucs_row_hi_bit,
385 "ucs_row_low_bit", DATA_TYPE_UINT8, &cs->ucs_row_low_bit,
386 "ucs_bank_bits", DATA_TYPE_UINT8_ARRAY, &bank_bits, &nbanks,
387 "ucs_col_bits", DATA_TYPE_UINT8_ARRAY, &col_bits, &ncols,
388 "ucs_inv_msbs", DATA_TYPE_UINT8, &cs->ucs_inv_msbs,
389 "ucs_rm_bits", DATA_TYPE_UINT8_ARRAY, &rm_bits, &nrm,
390 "ucs_inv_msbs_sec", DATA_TYPE_UINT8, &cs->ucs_inv_msbs_sec,
391 "ucs_rm_bits_sec", DATA_TYPE_UINT8_ARRAY, &rm_bits_sec, &nrm_sec,
392 "ucs_subchan", DATA_TYPE_UINT8, &cs->ucs_subchan,
393 NULL) != 0) {
394 return (B_FALSE);
395 }
396
397 if (nbanks > ZEN_UMC_MAX_BANK_BITS ||
398 ncols > ZEN_UMC_MAX_COL_BITS ||
399 nrm > ZEN_UMC_MAX_RM_BITS ||
400 nrm != nrm_sec) {
401 return (B_FALSE);
402 }
403
404 cs->ucs_nbanks = nbanks;
405 cs->ucs_ncol = ncols;
406 cs->ucs_nrm = nrm;
407
408 bcopy(bank_bits, cs->ucs_bank_bits, cs->ucs_nbanks *
409 sizeof (uint8_t));
410 bcopy(col_bits, cs->ucs_col_bits, cs->ucs_ncol * sizeof (uint8_t));
411 bcopy(rm_bits, cs->ucs_rm_bits, cs->ucs_nrm * sizeof (uint8_t));
412 bcopy(rm_bits_sec, cs->ucs_rm_bits_sec, cs->ucs_nrm *
413 sizeof (uint8_t));
414
415 if (nvlist_lookup_pairs(base, 0,
416 "udb_base", DATA_TYPE_UINT64, &cs->ucs_base.udb_base,
417 "udb_valid", DATA_TYPE_UINT8, &cs->ucs_base.udb_valid,
418 NULL) != 0) {
419 return (B_FALSE);
420 }
421
422 if (nvlist_lookup_pairs(sec, 0,
423 "udb_base", DATA_TYPE_UINT64, &cs->ucs_sec.udb_base,
424 "udb_valid", DATA_TYPE_UINT8, &cs->ucs_sec.udb_valid,
425 NULL) != 0) {
426 return (B_FALSE);
427 }
428
429 return (B_TRUE);
430 }
431
432 static boolean_t
zen_umc_restore_dimm(nvlist_t * nvl,umc_dimm_t * dimm)433 zen_umc_restore_dimm(nvlist_t *nvl, umc_dimm_t *dimm)
434 {
435 nvlist_t **cs;
436 uint_t ncs;
437
438 if (nvlist_lookup_pairs(nvl, 0,
439 "ud_flags", DATA_TYPE_UINT32, &dimm->ud_flags,
440 "ud_width", DATA_TYPE_UINT32, &dimm->ud_width,
441 "ud_kind", DATA_TYPE_UINT32, &dimm->ud_kind,
442 "ud_dimmno", DATA_TYPE_UINT32, &dimm->ud_dimmno,
443 "ud_cs", DATA_TYPE_NVLIST_ARRAY, &cs, &ncs,
444 NULL) != 0) {
445 return (B_FALSE);
446 }
447
448 if (ncs != ZEN_UMC_MAX_CS_PER_DIMM) {
449 return (B_FALSE);
450 }
451
452 for (uint_t i = 0; i < ZEN_UMC_MAX_CS_PER_DIMM; i++) {
453 if (!zen_umc_restore_cs(cs[i], &dimm->ud_cs[i])) {
454 return (B_FALSE);
455 }
456 }
457
458 return (B_TRUE);
459 }
460
461 static boolean_t
zen_umc_restore_hash(nvlist_t * nvl,umc_chan_hash_t * hash)462 zen_umc_restore_hash(nvlist_t *nvl, umc_chan_hash_t *hash)
463 {
464 if (nvlist_lookup_uint32(nvl, "uch_flags", &hash->uch_flags) != 0) {
465 return (B_FALSE);
466 }
467
468 if (hash->uch_flags & UMC_CHAN_HASH_F_BANK) {
469 nvlist_t **banks;
470 uint_t nbanks;
471
472 if (nvlist_lookup_nvlist_array(nvl, "uch_bank_hashes", &banks,
473 &nbanks) != 0) {
474 return (B_FALSE);
475 }
476
477 if (nbanks != ZEN_UMC_MAX_CHAN_BANK_HASH) {
478 return (B_FALSE);
479 }
480
481 for (uint_t i = 0; i < nbanks; i++) {
482 if (nvlist_lookup_pairs(banks[i], 0,
483 "ubh_row_xor", DATA_TYPE_UINT32,
484 &hash->uch_bank_hashes[i].ubh_row_xor,
485 "ubh_col_xor", DATA_TYPE_UINT32,
486 &hash->uch_bank_hashes[i].ubh_col_xor,
487 "ubh_en", DATA_TYPE_BOOLEAN_VALUE,
488 &hash->uch_bank_hashes[i].ubh_en,
489 NULL) != 0) {
490 return (B_FALSE);
491 }
492 }
493 }
494
495 if (hash->uch_flags & UMC_CHAN_HASH_F_RM) {
496 nvlist_t **rm;
497 uint_t nrm;
498
499 if (nvlist_lookup_nvlist_array(nvl, "uch_rm_hashes", &rm,
500 &nrm) != 0) {
501 return (B_FALSE);
502 }
503
504 if (nrm != ZEN_UMC_MAX_CHAN_RM_HASH) {
505 return (B_FALSE);
506 }
507
508 for (uint_t i = 0; i < nrm; i++) {
509 if (nvlist_lookup_pairs(rm[i], 0,
510 "uah_addr_xor", DATA_TYPE_UINT64,
511 &hash->uch_rm_hashes[i].uah_addr_xor,
512 "uah_en", DATA_TYPE_BOOLEAN_VALUE,
513 &hash->uch_rm_hashes[i].uah_en,
514 NULL) != 0) {
515 return (B_FALSE);
516 }
517 }
518 }
519
520 if (hash->uch_flags & UMC_CHAN_HASH_F_CS) {
521 nvlist_t **cs;
522 uint_t ncs;
523
524 if (nvlist_lookup_nvlist_array(nvl, "uch_cs_hashes", &cs,
525 &ncs) != 0) {
526 return (B_FALSE);
527 }
528
529 if (ncs != ZEN_UMC_MAX_CHAN_CS_HASH) {
530 return (B_FALSE);
531 }
532
533 for (uint_t i = 0; i < ncs; i++) {
534 if (nvlist_lookup_pairs(cs[i], 0,
535 "uah_addr_xor", DATA_TYPE_UINT64,
536 &hash->uch_cs_hashes[i].uah_addr_xor,
537 "uah_en", DATA_TYPE_BOOLEAN_VALUE,
538 &hash->uch_cs_hashes[i].uah_en,
539 NULL) != 0) {
540 return (B_FALSE);
541 }
542 }
543 }
544
545 if (hash->uch_flags & UMC_CHAN_HASH_F_PC) {
546 nvlist_t *pc;
547
548 if (nvlist_lookup_nvlist(nvl, "uch_pch_hash", &pc) != 0) {
549 return (B_FALSE);
550 }
551
552 if (nvlist_lookup_pairs(pc, 0,
553 "uph_row_xor", DATA_TYPE_UINT32,
554 &hash->uch_pc_hash.uph_row_xor,
555 "uph_col_xor", DATA_TYPE_UINT32,
556 &hash->uch_pc_hash.uph_col_xor,
557 "uph_bank_xor", DATA_TYPE_UINT32,
558 &hash->uch_pc_hash.uph_bank_xor,
559 "uph_en", DATA_TYPE_BOOLEAN_VALUE,
560 &hash->uch_pc_hash.uph_en,
561 NULL) != 0) {
562 return (B_FALSE);
563 }
564 }
565 return (B_TRUE);
566 }
567
568 static boolean_t
zen_umc_restore_chan(nvlist_t * nvl,zen_umc_chan_t * chan)569 zen_umc_restore_chan(nvlist_t *nvl, zen_umc_chan_t *chan)
570 {
571 uint_t noffsets, ndimms;
572 nvlist_t **rules, **offsets, **dimms, *hash;
573
574 if (nvlist_lookup_pairs(nvl, 0,
575 "chan_flags", DATA_TYPE_UINT32, &chan->chan_flags,
576 "chan_fabid", DATA_TYPE_UINT32, &chan->chan_fabid,
577 "chan_instid", DATA_TYPE_UINT32, &chan->chan_instid,
578 "chan_logid", DATA_TYPE_UINT32, &chan->chan_logid,
579 "chan_rules", DATA_TYPE_NVLIST_ARRAY, &rules, &chan->chan_nrules,
580 "chan_np2_space0", DATA_TYPE_UINT32, &chan->chan_np2_space0,
581 "chan_type", DATA_TYPE_UINT32, &chan->chan_np2_space0,
582 "chan_offsets", DATA_TYPE_NVLIST_ARRAY, &offsets, &noffsets,
583 "chan_dimms", DATA_TYPE_NVLIST_ARRAY, &dimms, &ndimms,
584 "chan_hash", DATA_TYPE_NVLIST, &hash,
585 NULL) != 0) {
586 return (B_FALSE);
587 }
588
589 if (chan->chan_nrules > ZEN_UMC_MAX_CS_RULES ||
590 noffsets != chan->chan_nrules - 1 || ndimms != ZEN_UMC_MAX_DIMMS) {
591 return (B_FALSE);
592 }
593
594 for (uint_t i = 0; i < chan->chan_nrules; i++) {
595 if (!zen_umc_restore_dram_rule(rules[i],
596 &chan->chan_rules[i])) {
597 return (B_FALSE);
598 }
599 }
600
601 for (uint_t i = 0; i < chan->chan_nrules - 1; i++) {
602 chan_offset_t *coff = &chan->chan_offsets[i];
603
604 if (nvlist_lookup_pairs(offsets[i], 0,
605 "cho_valid", DATA_TYPE_BOOLEAN_VALUE, &coff->cho_valid,
606 "cho_offset", DATA_TYPE_UINT64, &coff->cho_offset,
607 NULL) != 0) {
608 return (B_FALSE);
609 }
610 }
611
612 for (uint_t i = 0; i < ZEN_UMC_MAX_DIMMS; i++) {
613 if (!zen_umc_restore_dimm(dimms[i], &chan->chan_dimms[i])) {
614 return (B_FALSE);
615 }
616 }
617
618 if (!zen_umc_restore_hash(hash, &chan->chan_hash)) {
619 return (B_FALSE);
620 }
621
622 return (B_TRUE);
623 }
624
625 static boolean_t
zen_umc_restore_df(nvlist_t * nvl,zen_umc_df_t * df)626 zen_umc_restore_df(nvlist_t *nvl, zen_umc_df_t *df)
627 {
628 nvlist_t **rules, **chan, **remap;
629
630 if (nvlist_lookup_pairs(nvl, 0,
631 "zud_flags", DATA_TYPE_UINT32, &df->zud_flags,
632 "zud_dfno", DATA_TYPE_UINT32, &df->zud_dfno,
633 "zud_ccm_inst", DATA_TYPE_UINT32, &df->zud_ccm_inst,
634 "zud_hole_base", DATA_TYPE_UINT64, &df->zud_hole_base,
635 "zud_rules", DATA_TYPE_NVLIST_ARRAY, &rules, &df->zud_dram_nrules,
636 "zud_remap", DATA_TYPE_NVLIST_ARRAY, &remap, &df->zud_cs_nremap,
637 "zud_chan", DATA_TYPE_NVLIST_ARRAY, &chan, &df->zud_nchan,
638 NULL != 0) ||
639 df->zud_dram_nrules > ZEN_UMC_MAX_DRAM_RULES ||
640 df->zud_cs_nremap > ZEN_UMC_MAX_CS_REMAPS ||
641 df->zud_nchan > ZEN_UMC_MAX_UMCS) {
642 return (B_FALSE);
643 }
644
645 for (uint_t i = 0; i < df->zud_dram_nrules; i++) {
646 if (!zen_umc_restore_dram_rule(rules[i], &df->zud_rules[i])) {
647 return (B_FALSE);
648 }
649 }
650
651 for (uint_t i = 0; i < df->zud_cs_nremap; i++) {
652 uint16_t *u16p;
653 if (nvlist_lookup_uint16_array(remap[i], "csr_remaps", &u16p,
654 &df->zud_remap[i].csr_nremaps) != 0 ||
655 df->zud_remap[i].csr_nremaps > ZEN_UMC_MAX_REMAP_ENTS) {
656 return (B_FALSE);
657 }
658 bcopy(u16p, df->zud_remap[i].csr_remaps,
659 df->zud_remap[i].csr_nremaps);
660 }
661
662 for (uint_t i = 0; i < df->zud_nchan; i++) {
663 if (!zen_umc_restore_chan(chan[i], &df->zud_chan[i])) {
664 return (B_FALSE);
665 }
666 }
667
668 return (B_TRUE);
669 }
670
671 boolean_t
zen_umc_restore_decoder(nvlist_t * nvl,zen_umc_t * umc)672 zen_umc_restore_decoder(nvlist_t *nvl, zen_umc_t *umc)
673 {
674 uint32_t vers;
675 char *driver;
676 nvlist_t *umc_nvl, *decomp, **dfs;
677 bzero(umc, sizeof (zen_umc_t));
678
679 if (nvlist_lookup_pairs(nvl, 0,
680 "mc_dump_version", DATA_TYPE_UINT32, &vers,
681 "mc_dump_driver", DATA_TYPE_STRING, &driver,
682 NULL) != 0 || vers != 0 || strcmp(driver, "zen_umc") != 0 ||
683 nvlist_lookup_nvlist(nvl, "zen_umc", &umc_nvl) != 0) {
684 return (B_FALSE);
685 }
686
687 if (nvlist_lookup_pairs(umc_nvl, 0,
688 "umc_tom", DATA_TYPE_UINT64, &umc->umc_tom,
689 "umc_tom2", DATA_TYPE_UINT64, &umc->umc_tom2,
690 "umc_family", DATA_TYPE_UINT32, &umc->umc_family,
691 "umc_df_rev", DATA_TYPE_UINT32, &umc->umc_df_rev,
692 "umc_decomp", DATA_TYPE_NVLIST, &decomp,
693 "umc_dfs", DATA_TYPE_NVLIST_ARRAY, &dfs, &umc->umc_ndfs,
694 NULL) != 0 || umc->umc_ndfs > ZEN_UMC_MAX_DFS) {
695 return (B_FALSE);
696 }
697
698
699 if (nvlist_lookup_pairs(decomp, 0,
700 "dfd_sock_mask", DATA_TYPE_UINT32, &umc->umc_decomp.dfd_sock_mask,
701 "dfd_die_mask", DATA_TYPE_UINT32, &umc->umc_decomp.dfd_die_mask,
702 "dfd_node_mask", DATA_TYPE_UINT32, &umc->umc_decomp.dfd_node_mask,
703 "dfd_comp_mask", DATA_TYPE_UINT32, &umc->umc_decomp.dfd_comp_mask,
704 "dfd_sock_shift", DATA_TYPE_UINT8, &umc->umc_decomp.dfd_sock_shift,
705 "dfd_die_shift", DATA_TYPE_UINT8, &umc->umc_decomp.dfd_die_shift,
706 "dfd_node_shift", DATA_TYPE_UINT8, &umc->umc_decomp.dfd_node_shift,
707 "dfd_comp_shift", DATA_TYPE_UINT8, &umc->umc_decomp.dfd_comp_shift,
708 NULL) != 0) {
709 return (B_FALSE);
710 }
711
712 for (uint_t i = 0; i < umc->umc_ndfs; i++) {
713 if (!zen_umc_restore_df(dfs[i], &umc->umc_dfs[i])) {
714 return (B_FALSE);
715 }
716 }
717
718 return (B_TRUE);
719 }
720