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 2019 Joyent, Inc.
14 */
15
16/*
17 * This implements logic to allow us to dump IMC data for decoding purposes,
18 * such that we can later encode it elsewhere. In general, dumping is done by
19 * the kernel and reconstituting this data is done by user land.
20 */
21
22#include "imc.h"
23
24#ifndef _KERNEL
25#include <stdint.h>
26#include <strings.h>
27#endif	/* !_KERNEL */
28
29
30static nvlist_t *
31imc_dump_sad(imc_sad_t *sad)
32{
33	uint_t i;
34	nvlist_t *nvl;
35	nvlist_t *rules[IMC_MAX_SAD_RULES];
36	nvlist_t *routes[IMC_MAX_SAD_MCROUTES];
37
38	nvl = fnvlist_alloc();
39	fnvlist_add_uint32(nvl, "isad_flags", sad->isad_flags);
40	fnvlist_add_uint32(nvl, "isad_valid", sad->isad_valid);
41	fnvlist_add_uint64(nvl, "isad_tolm", sad->isad_tolm);
42	fnvlist_add_uint64(nvl, "isad_tohm", sad->isad_tohm);
43
44	for (i = 0; i < sad->isad_nrules; i++) {
45		nvlist_t *n = fnvlist_alloc();
46		imc_sad_rule_t *r = &sad->isad_rules[i];
47
48		fnvlist_add_boolean_value(n, "isr_enable", r->isr_enable);
49		fnvlist_add_boolean_value(n, "isr_a7mode", r->isr_a7mode);
50		fnvlist_add_boolean_value(n, "isr_need_mod3", r->isr_need_mod3);
51		fnvlist_add_uint64(n, "isr_limit", r->isr_limit);
52		fnvlist_add_uint32(n, "isr_type", r->isr_type);
53		fnvlist_add_uint32(n, "isr_imode", r->isr_imode);
54		fnvlist_add_uint32(n, "isr_mod_mode", r->isr_mod_mode);
55		fnvlist_add_uint32(n, "isr_mod_type", r->isr_mod_type);
56		fnvlist_add_uint8_array(n, "isr_targets", r->isr_targets,
57		    r->isr_ntargets);
58
59		rules[i] = n;
60	}
61	fnvlist_add_nvlist_array(nvl, "isad_rules", rules, sad->isad_nrules);
62	for (i = 0; i < sad->isad_nrules; i++) {
63		nvlist_free(rules[i]);
64	}
65
66	if (sad->isad_mcroute.ismc_nroutes == 0) {
67		return (nvl);
68	}
69
70	for (i = 0; i <  sad->isad_mcroute.ismc_nroutes; i++) {
71		nvlist_t *r = fnvlist_alloc();
72		imc_sad_mcroute_entry_t *e =
73		    &sad->isad_mcroute.ismc_mcroutes[i];
74
75		fnvlist_add_uint8(r, "ismce_imc", e->ismce_imc);
76		fnvlist_add_uint8(r, "ismce_pchannel", e->ismce_pchannel);
77		routes[i] = r;
78	}
79	fnvlist_add_nvlist_array(nvl, "isad_mcroute", routes, i);
80	for (i = 0; i <  sad->isad_mcroute.ismc_nroutes; i++) {
81		nvlist_free(routes[i]);
82	}
83
84	return (nvl);
85}
86
87static nvlist_t *
88imc_dump_tad(imc_tad_t *tad)
89{
90	uint_t i;
91	nvlist_t *nvl;
92	nvlist_t *rules[IMC_MAX_TAD_RULES];
93
94	nvl = fnvlist_alloc();
95	fnvlist_add_uint32(nvl, "itad_valid", tad->itad_valid);
96	fnvlist_add_uint32(nvl, "itad_flags", tad->itad_flags);
97	for (i = 0; i < tad->itad_nrules; i++) {
98		nvlist_t *t = fnvlist_alloc();
99		imc_tad_rule_t *r = &tad->itad_rules[i];
100
101		fnvlist_add_uint64(t, "itr_base", r->itr_base);
102		fnvlist_add_uint64(t, "itr_limit", r->itr_limit);
103		fnvlist_add_uint8(t, "itr_sock_way", r->itr_sock_way);
104		fnvlist_add_uint8(t, "itr_chan_way", r->itr_chan_way);
105		fnvlist_add_uint32(t, "itr_sock_gran", r->itr_sock_gran);
106		fnvlist_add_uint32(t, "itr_chan_gran", r->itr_chan_gran);
107		fnvlist_add_uint8_array(t, "itr_targets", r->itr_targets,
108		    r->itr_ntargets);
109
110		rules[i] = t;
111	}
112	fnvlist_add_nvlist_array(nvl, "itad_rules", rules, tad->itad_nrules);
113	for (i = 0; i < tad->itad_nrules; i++) {
114		nvlist_free(rules[i]);
115	}
116
117	return (nvl);
118}
119
120static nvlist_t *
121imc_dump_channel(imc_channel_t *chan)
122{
123	uint_t i;
124	nvlist_t *nvl;
125	nvlist_t *dimms[IMC_MAX_DIMMPERCHAN];
126	nvlist_t *ranks[IMC_MAX_RANK_WAYS];
127
128	nvl = fnvlist_alloc();
129	fnvlist_add_uint32(nvl, "ich_valid", chan->ich_valid);
130	for (i = 0; i < chan->ich_ndimms; i++) {
131		nvlist_t *d = fnvlist_alloc();
132		imc_dimm_t *dimm = &chan->ich_dimms[i];
133
134		fnvlist_add_uint32(d, "idimm_valid", dimm->idimm_valid);
135		fnvlist_add_boolean_value(d, "idimm_present",
136		    dimm->idimm_present);
137		if (!dimm->idimm_present)
138			goto add;
139
140		fnvlist_add_uint8(d, "idimm_nbanks", dimm->idimm_nbanks);
141		fnvlist_add_uint8(d, "idimm_nranks", dimm->idimm_nranks);
142		fnvlist_add_uint8(d, "idimm_width", dimm->idimm_width);
143		fnvlist_add_uint8(d, "idimm_density", dimm->idimm_density);
144		fnvlist_add_uint8(d, "idimm_nrows", dimm->idimm_nrows);
145		fnvlist_add_uint8(d, "idimm_ncolumns", dimm->idimm_ncolumns);
146		fnvlist_add_uint64(d, "idimm_size", dimm->idimm_size);
147add:
148		dimms[i] = d;
149	}
150	fnvlist_add_nvlist_array(nvl, "ich_dimms", dimms, i);
151	for (i = 0; i < chan->ich_ndimms; i++) {
152		nvlist_free(dimms[i]);
153	}
154
155	fnvlist_add_uint64_array(nvl, "ich_tad_offsets", chan->ich_tad_offsets,
156	    chan->ich_ntad_offsets);
157
158	for (i = 0; i < chan->ich_nrankileaves; i++) {
159		uint_t j;
160		nvlist_t *r = fnvlist_alloc();
161		nvlist_t *ileaves[IMC_MAX_RANK_INTERLEAVES];
162		imc_rank_ileave_t *rank = &chan->ich_rankileaves[i];
163
164		fnvlist_add_boolean_value(r, "irle_enabled",
165		    rank->irle_enabled);
166		fnvlist_add_uint8(r, "irle_nways", rank->irle_nways);
167		fnvlist_add_uint8(r, "irle_nwaysbits", rank->irle_nwaysbits);
168		fnvlist_add_uint64(r, "irle_limit", rank->irle_limit);
169
170		for (j = 0; j < rank->irle_nentries; j++) {
171			nvlist_t *e = fnvlist_alloc();
172
173			fnvlist_add_uint8(e, "irle_target",
174			    rank->irle_entries[j].irle_target);
175			fnvlist_add_uint64(e, "irle_offset",
176			    rank->irle_entries[j].irle_offset);
177			ileaves[j] = e;
178		}
179		fnvlist_add_nvlist_array(r, "irle_entries", ileaves, j);
180		for (j = 0; j < rank->irle_nentries; j++) {
181			nvlist_free(ileaves[j]);
182		}
183
184		ranks[i] = r;
185	}
186	fnvlist_add_nvlist_array(nvl, "ich_rankileaves", ranks, i);
187	for (i = 0; i < chan->ich_nrankileaves; i++) {
188		nvlist_free(ranks[i]);
189	}
190
191	return (nvl);
192}
193
194static nvlist_t *
195imc_dump_mc(imc_mc_t *mc)
196{
197	uint_t i;
198	nvlist_t *nvl;
199	nvlist_t *channels[IMC_MAX_CHANPERMC];
200
201	nvl = fnvlist_alloc();
202	fnvlist_add_boolean_value(nvl, "icn_ecc", mc->icn_ecc);
203	fnvlist_add_boolean_value(nvl, "icn_lockstep", mc->icn_lockstep);
204	fnvlist_add_boolean_value(nvl, "icn_closed", mc->icn_closed);
205	fnvlist_add_uint32(nvl, "icn_dimm_type", mc->icn_dimm_type);
206
207	for (i = 0; i < mc->icn_nchannels; i++) {
208		channels[i] = imc_dump_channel(&mc->icn_channels[i]);
209	}
210	fnvlist_add_nvlist_array(nvl, "icn_channels", channels, i);
211	for (i = 0; i < mc->icn_nchannels; i++) {
212		nvlist_free(channels[i]);
213	}
214
215	return (nvl);
216}
217
218static nvlist_t *
219imc_dump_socket(imc_socket_t *sock)
220{
221	uint_t i;
222	nvlist_t *nvl, *sad;
223	nvlist_t *tad[IMC_MAX_TAD];
224	nvlist_t *mc[IMC_MAX_IMCPERSOCK];
225
226	nvl = fnvlist_alloc();
227
228	sad = imc_dump_sad(&sock->isock_sad);
229	fnvlist_add_nvlist(nvl, "isock_sad", sad);
230	nvlist_free(sad);
231
232	for (i = 0; i < sock->isock_ntad; i++) {
233		tad[i] = imc_dump_tad(&sock->isock_tad[i]);
234	}
235	fnvlist_add_nvlist_array(nvl, "isock_tad", tad, i);
236	for (i = 0; i < sock->isock_ntad; i++) {
237		fnvlist_free(tad[i]);
238	}
239
240	fnvlist_add_uint32(nvl, "isock_nodeid", sock->isock_nodeid);
241
242	for (i = 0; i  < sock->isock_nimc; i++) {
243		mc[i] = imc_dump_mc(&sock->isock_imcs[i]);
244	}
245	fnvlist_add_nvlist_array(nvl, "isock_imcs", mc, i);
246	for (i = 0; i < sock->isock_nimc; i++) {
247		fnvlist_free(mc[i]);
248	}
249	return (nvl);
250}
251
252nvlist_t *
253imc_dump_decoder(imc_t *imc)
254{
255	uint_t i;
256	nvlist_t *nvl, *invl;
257	nvlist_t *sockets[IMC_MAX_SOCKETS];
258
259	nvl = fnvlist_alloc();
260	fnvlist_add_uint32(nvl, "mc_dump_version", 0);
261	fnvlist_add_string(nvl, "mc_dump_driver", "imc");
262
263	invl = fnvlist_alloc();
264	fnvlist_add_uint32(invl, "imc_gen", imc->imc_gen);
265
266	for (i = 0; i < imc->imc_nsockets; i++) {
267		sockets[i] = imc_dump_socket(&imc->imc_sockets[i]);
268	}
269	fnvlist_add_nvlist_array(invl, "imc_sockets", sockets, i);
270	fnvlist_add_nvlist(nvl, "imc", invl);
271
272	for (i = 0; i < imc->imc_nsockets; i++) {
273		nvlist_free(sockets[i]);
274	}
275	nvlist_free(invl);
276
277	return (nvl);
278}
279
280static boolean_t
281imc_restore_sad(nvlist_t *nvl, imc_sad_t *sad)
282{
283	nvlist_t **rules, **routes;
284	uint_t i, nroutes;
285
286	if (nvlist_lookup_uint32(nvl, "isad_flags", &sad->isad_flags) != 0 ||
287	    nvlist_lookup_uint32(nvl, "isad_valid", &sad->isad_valid) != 0 ||
288	    nvlist_lookup_uint64(nvl, "isad_tolm", &sad->isad_tolm) != 0 ||
289	    nvlist_lookup_uint64(nvl, "isad_tohm", &sad->isad_tohm) != 0 ||
290	    nvlist_lookup_nvlist_array(nvl, "isad_rules",
291	    &rules, &sad->isad_nrules) != 0) {
292		return (B_FALSE);
293	}
294
295	for (i = 0; i < sad->isad_nrules; i++) {
296		imc_sad_rule_t *r = &sad->isad_rules[i];
297		uint8_t *targs;
298
299		if (nvlist_lookup_boolean_value(rules[i], "isr_enable",
300		    &r->isr_enable) != 0 ||
301		    nvlist_lookup_boolean_value(rules[i], "isr_a7mode",
302		    &r->isr_a7mode) != 0 ||
303		    nvlist_lookup_boolean_value(rules[i], "isr_need_mod3",
304		    &r->isr_need_mod3) != 0 ||
305		    nvlist_lookup_uint64(rules[i], "isr_limit",
306		    &r->isr_limit) != 0 ||
307		    nvlist_lookup_uint32(rules[i], "isr_type",
308		    &r->isr_type) != 0 ||
309		    nvlist_lookup_uint32(rules[i], "isr_imode",
310		    &r->isr_imode) != 0 ||
311		    nvlist_lookup_uint32(rules[i], "isr_mod_mode",
312		    &r->isr_mod_mode) != 0 ||
313		    nvlist_lookup_uint32(rules[i], "isr_mod_type",
314		    &r->isr_mod_type) != 0 ||
315		    nvlist_lookup_uint8_array(rules[i], "isr_targets", &targs,
316		    &r->isr_ntargets) != 0 ||
317		    r->isr_ntargets > IMC_MAX_SAD_RULES) {
318			return (B_FALSE);
319		}
320
321		bcopy(targs, r->isr_targets, r->isr_ntargets *
322		    sizeof (uint8_t));
323	}
324
325	/*
326	 * The mcroutes entry right now is only included conditionally.
327	 */
328	if (nvlist_lookup_nvlist_array(nvl, "isad_mcroute", &routes,
329	    &nroutes) == 0) {
330		if (nroutes > IMC_MAX_SAD_MCROUTES)
331			return (B_FALSE);
332		sad->isad_mcroute.ismc_nroutes = nroutes;
333		for (i = 0; i < nroutes; i++) {
334			imc_sad_mcroute_entry_t *r =
335			    &sad->isad_mcroute.ismc_mcroutes[i];
336			if (nvlist_lookup_uint8(routes[i], "ismce_imc",
337			    &r->ismce_imc) != 0 ||
338			    nvlist_lookup_uint8(routes[i], "ismce_pchannel",
339			    &r->ismce_pchannel) != 0) {
340				return (B_FALSE);
341			}
342		}
343	}
344
345	return (B_TRUE);
346}
347
348static boolean_t
349imc_restore_tad(nvlist_t *nvl, imc_tad_t *tad)
350{
351	nvlist_t **rules;
352
353	if (nvlist_lookup_uint32(nvl, "itad_valid", &tad->itad_valid) != 0 ||
354	    nvlist_lookup_uint32(nvl, "itad_flags", &tad->itad_flags) != 0 ||
355	    nvlist_lookup_nvlist_array(nvl, "itad_rules", &rules,
356	    &tad->itad_nrules) != 0 || tad->itad_nrules > IMC_MAX_TAD_RULES) {
357		return (B_FALSE);
358	}
359
360	for (uint_t i = 0; i < tad->itad_nrules; i++) {
361		imc_tad_rule_t *r = &tad->itad_rules[i];
362		uint8_t *targs;
363
364		if (nvlist_lookup_uint64(rules[i], "itr_base",
365		    &r->itr_base) != 0 ||
366		    nvlist_lookup_uint64(rules[i], "itr_limit",
367		    &r->itr_limit) != 0 ||
368		    nvlist_lookup_uint8(rules[i], "itr_sock_way",
369		    &r->itr_sock_way) != 0 ||
370		    nvlist_lookup_uint8(rules[i], "itr_chan_way",
371		    &r->itr_chan_way) != 0 ||
372		    nvlist_lookup_uint32(rules[i], "itr_sock_gran",
373		    &r->itr_sock_gran) != 0 ||
374		    nvlist_lookup_uint32(rules[i], "itr_chan_gran",
375		    &r->itr_chan_gran) != 0 ||
376		    nvlist_lookup_uint8_array(rules[i], "itr_targets",
377		    &targs, &r->itr_ntargets) != 0 ||
378		    r->itr_ntargets > IMC_MAX_TAD_TARGETS) {
379			return (B_FALSE);
380		}
381
382		bcopy(targs, r->itr_targets, r->itr_ntargets *
383		    sizeof (uint8_t));
384	}
385
386	return (B_TRUE);
387}
388
389static boolean_t
390imc_restore_channel(nvlist_t *nvl, imc_channel_t *chan)
391{
392	nvlist_t **dimms, **rir;
393	uint64_t *tadoff;
394
395	if (nvlist_lookup_uint32(nvl, "ich_valid", &chan->ich_valid) != 0 ||
396	    nvlist_lookup_nvlist_array(nvl, "ich_dimms", &dimms,
397	    &chan->ich_ndimms) != 0 ||
398	    chan->ich_ndimms > IMC_MAX_DIMMPERCHAN ||
399	    nvlist_lookup_uint64_array(nvl, "ich_tad_offsets", &tadoff,
400	    &chan->ich_ntad_offsets) != 0 ||
401	    chan->ich_ntad_offsets > IMC_MAX_TAD_RULES ||
402	    nvlist_lookup_nvlist_array(nvl, "ich_rankileaves", &rir,
403	    &chan->ich_nrankileaves) != 0 ||
404	    chan->ich_nrankileaves > IMC_MAX_RANK_WAYS) {
405		return (B_FALSE);
406	}
407
408	for (uint_t i = 0; i < chan->ich_ndimms; i++) {
409		imc_dimm_t *d = &chan->ich_dimms[i];
410
411		if (nvlist_lookup_uint32(dimms[i], "idimm_valid",
412		    &d->idimm_valid) != 0 ||
413		    nvlist_lookup_boolean_value(dimms[i], "idimm_present",
414		    &d->idimm_present) != 0) {
415			return (B_FALSE);
416		}
417
418		if (!d->idimm_present)
419			continue;
420
421		if (nvlist_lookup_uint8(dimms[i], "idimm_nbanks",
422		    &d->idimm_nbanks) != 0 ||
423		    nvlist_lookup_uint8(dimms[i], "idimm_nranks",
424		    &d->idimm_nranks) != 0 ||
425		    nvlist_lookup_uint8(dimms[i], "idimm_width",
426		    &d->idimm_width) != 0 ||
427		    nvlist_lookup_uint8(dimms[i], "idimm_density",
428		    &d->idimm_density) != 0 ||
429		    nvlist_lookup_uint8(dimms[i], "idimm_nrows",
430		    &d->idimm_nrows) != 0 ||
431		    nvlist_lookup_uint8(dimms[i], "idimm_ncolumns",
432		    &d->idimm_ncolumns) != 0 ||
433		    nvlist_lookup_uint64(dimms[i], "idimm_size",
434		    &d->idimm_size) != 0) {
435			return (B_FALSE);
436		}
437	}
438
439	bcopy(tadoff, chan->ich_tad_offsets, chan->ich_ntad_offsets *
440	    sizeof (uint64_t));
441
442	for (uint_t i = 0; i < chan->ich_nrankileaves; i++) {
443		nvlist_t **ileaves;
444		imc_rank_ileave_t *r = &chan->ich_rankileaves[i];
445
446		if (nvlist_lookup_boolean_value(rir[i], "irle_enabled",
447		    &r->irle_enabled) != 0 ||
448		    nvlist_lookup_uint8(rir[i], "irle_nways",
449		    &r->irle_nways) != 0 ||
450		    nvlist_lookup_uint8(rir[i], "irle_nwaysbits",
451		    &r->irle_nwaysbits) != 0 ||
452		    nvlist_lookup_uint64(rir[i], "irle_limit",
453		    &r->irle_limit) != 0 ||
454		    nvlist_lookup_nvlist_array(rir[i], "irle_entries",
455		    &ileaves, &r->irle_nentries) != 0 ||
456		    r->irle_nentries > IMC_MAX_RANK_INTERLEAVES) {
457			return (B_FALSE);
458		}
459
460		for (uint_t j = 0; j < r->irle_nentries; j++) {
461			imc_rank_ileave_entry_t *ril = &r->irle_entries[j];
462
463			if (nvlist_lookup_uint8(ileaves[j], "irle_target",
464			    &ril->irle_target) != 0 ||
465			    nvlist_lookup_uint64(ileaves[j], "irle_offset",
466			    &ril->irle_offset) != 0) {
467				return (B_FALSE);
468			}
469		}
470	}
471
472	return (B_TRUE);
473}
474
475static boolean_t
476imc_restore_mc(nvlist_t *nvl, imc_mc_t *mc)
477{
478	nvlist_t **channels;
479
480	if (nvlist_lookup_boolean_value(nvl, "icn_ecc", &mc->icn_ecc) != 0 ||
481	    nvlist_lookup_boolean_value(nvl, "icn_lockstep",
482	    &mc->icn_lockstep) != 0 ||
483	    nvlist_lookup_boolean_value(nvl, "icn_closed",
484	    &mc->icn_closed) != 0 ||
485	    nvlist_lookup_uint32(nvl, "icn_dimm_type",
486	    &mc->icn_dimm_type) != 0 ||
487	    nvlist_lookup_nvlist_array(nvl, "icn_channels", &channels,
488	    &mc->icn_nchannels) != 0 || mc->icn_nchannels > IMC_MAX_CHANPERMC) {
489		return (B_FALSE);
490	}
491
492	for (uint_t i = 0; i < mc->icn_nchannels; i++) {
493		if (!imc_restore_channel(channels[i], &mc->icn_channels[i])) {
494			return (B_FALSE);
495		}
496	}
497
498	return (B_TRUE);
499}
500
501static boolean_t
502imc_restore_socket(nvlist_t *nvl, imc_socket_t *sock)
503{
504	uint_t i;
505	nvlist_t *sad, **tads, **imcs;
506
507	if (nvlist_lookup_nvlist(nvl, "isock_sad", &sad) != 0 ||
508	    nvlist_lookup_nvlist_array(nvl, "isock_tad", &tads,
509	    &sock->isock_ntad) != 0 ||
510	    nvlist_lookup_uint32(nvl, "isock_nodeid",
511	    &sock->isock_nodeid) != 0 ||
512	    nvlist_lookup_nvlist_array(nvl, "isock_imcs", &imcs,
513	    &sock->isock_nimc) != 0 ||
514	    sock->isock_ntad > IMC_MAX_TAD ||
515	    sock->isock_nimc > IMC_MAX_IMCPERSOCK) {
516		return (B_FALSE);
517	}
518
519	if (!imc_restore_sad(sad, &sock->isock_sad)) {
520		return (B_FALSE);
521	}
522
523	for (i = 0; i < sock->isock_ntad; i++) {
524		if (!imc_restore_tad(tads[i], &sock->isock_tad[i])) {
525			return (B_FALSE);
526		}
527	}
528
529	for (i = 0; i < sock->isock_nimc; i++) {
530		if (!imc_restore_mc(imcs[i], &sock->isock_imcs[i])) {
531			return (B_FALSE);
532		}
533	}
534
535	return (B_TRUE);
536}
537
538boolean_t
539imc_restore_decoder(nvlist_t *nvl, imc_t *imc)
540{
541	uint_t i;
542	uint32_t vers;
543	nvlist_t *invl, **socks;
544	char *driver;
545
546	bzero(imc, sizeof (imc_t));
547
548	if (nvlist_lookup_uint32(nvl, "mc_dump_version", &vers) != 0 ||
549	    vers != 0 ||
550	    nvlist_lookup_string(nvl, "mc_dump_driver", &driver) != 0 ||
551	    strcmp(driver, "imc") != 0 ||
552	    nvlist_lookup_nvlist(nvl, "imc", &invl) != 0) {
553		return (B_FALSE);
554	}
555
556	if (nvlist_lookup_uint32(invl, "imc_gen", &imc->imc_gen) != 0 ||
557	    nvlist_lookup_nvlist_array(invl, "imc_sockets", &socks,
558	    &imc->imc_nsockets) != 0 ||
559	    imc->imc_nsockets > IMC_MAX_SOCKETS) {
560		return (B_FALSE);
561	}
562
563	for (i = 0; i < imc->imc_nsockets; i++) {
564		if (!imc_restore_socket(socks[i], &imc->imc_sockets[i]))
565			return (B_FALSE);
566	}
567
568	return (B_TRUE);
569}
570