1*6316Seschrock /*
2*6316Seschrock  * CDDL HEADER START
3*6316Seschrock  *
4*6316Seschrock  * The contents of this file are subject to the terms of the
5*6316Seschrock  * Common Development and Distribution License (the "License").
6*6316Seschrock  * You may not use this file except in compliance with the License.
7*6316Seschrock  *
8*6316Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*6316Seschrock  * or http://www.opensolaris.org/os/licensing.
10*6316Seschrock  * See the License for the specific language governing permissions
11*6316Seschrock  * and limitations under the License.
12*6316Seschrock  *
13*6316Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
14*6316Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*6316Seschrock  * If applicable, add the following below this CDDL HEADER, with the
16*6316Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
17*6316Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
18*6316Seschrock  *
19*6316Seschrock  * CDDL HEADER END
20*6316Seschrock  */
21*6316Seschrock 
22*6316Seschrock /*
23*6316Seschrock  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24*6316Seschrock  * Use is subject to license terms.
25*6316Seschrock  */
26*6316Seschrock 
27*6316Seschrock #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*6316Seschrock 
29*6316Seschrock #include <scsi/libses.h>
30*6316Seschrock #include "ses_impl.h"
31*6316Seschrock 
32*6316Seschrock #define	NEXT_ED(eip)	\
33*6316Seschrock 	((ses2_ed_impl_t *)((uint8_t *)(eip) + 	\
34*6316Seschrock 	    ((eip)->st_hdr.sehi_ed_len + sizeof (ses2_ed_hdr_impl_t))))
35*6316Seschrock 
36*6316Seschrock static ses_node_t *
37*6316Seschrock ses_find_enclosure(ses_snap_t *sp, uint64_t number)
38*6316Seschrock {
39*6316Seschrock 	ses_node_t *np;
40*6316Seschrock 
41*6316Seschrock 	for (np = sp->ss_root->sn_first_child; np != NULL;
42*6316Seschrock 	    np = np->sn_next_sibling) {
43*6316Seschrock 		ASSERT(np->sn_type == SES_NODE_ENCLOSURE);
44*6316Seschrock 		if (np->sn_enc_num == number)
45*6316Seschrock 			return ((ses_node_t *)np);
46*6316Seschrock 	}
47*6316Seschrock 
48*6316Seschrock 	return (NULL);
49*6316Seschrock }
50*6316Seschrock 
51*6316Seschrock void
52*6316Seschrock ses_node_teardown(ses_node_t *np)
53*6316Seschrock {
54*6316Seschrock 	ses_node_t *rp;
55*6316Seschrock 
56*6316Seschrock 	if (np == NULL)
57*6316Seschrock 		return;
58*6316Seschrock 
59*6316Seschrock 	for (; np != NULL; np = rp) {
60*6316Seschrock 		ses_node_teardown(np->sn_first_child);
61*6316Seschrock 		rp = np->sn_next_sibling;
62*6316Seschrock 		nvlist_free(np->sn_props);
63*6316Seschrock 		ses_free(np);
64*6316Seschrock 	}
65*6316Seschrock }
66*6316Seschrock 
67*6316Seschrock static ses_node_t *
68*6316Seschrock ses_node_alloc(ses_snap_t *sp, ses_node_t *pnp)
69*6316Seschrock {
70*6316Seschrock 	ses_node_t *np;
71*6316Seschrock 
72*6316Seschrock 	np = ses_zalloc(sizeof (ses_node_t));
73*6316Seschrock 	if (np == NULL)
74*6316Seschrock 		goto fail;
75*6316Seschrock 	if (nvlist_alloc(&np->sn_props, NV_UNIQUE_NAME, 0) != 0)
76*6316Seschrock 		goto fail;
77*6316Seschrock 
78*6316Seschrock 	np->sn_snapshot = sp;
79*6316Seschrock 	np->sn_id = sp->ss_n_nodes++;
80*6316Seschrock 
81*6316Seschrock 	if (pnp == NULL) {
82*6316Seschrock 		ASSERT(sp->ss_root == NULL);
83*6316Seschrock 		sp->ss_root = np;
84*6316Seschrock 	} else {
85*6316Seschrock 		np->sn_parent = pnp;
86*6316Seschrock 		np->sn_prev_sibling = pnp->sn_last_child;
87*6316Seschrock 
88*6316Seschrock 		if (pnp->sn_first_child == NULL)
89*6316Seschrock 			pnp->sn_first_child = np;
90*6316Seschrock 		else
91*6316Seschrock 			pnp->sn_last_child->sn_next_sibling = np;
92*6316Seschrock 
93*6316Seschrock 		pnp->sn_last_child = np;
94*6316Seschrock 	}
95*6316Seschrock 
96*6316Seschrock 	return (np);
97*6316Seschrock 
98*6316Seschrock fail:
99*6316Seschrock 	ses_free(np);
100*6316Seschrock 	ses_node_teardown(sp->ss_root);
101*6316Seschrock 	sp->ss_root = NULL;
102*6316Seschrock 	return (NULL);
103*6316Seschrock }
104*6316Seschrock 
105*6316Seschrock /*
106*6316Seschrock  * Parse element type descriptor.
107*6316Seschrock  */
108*6316Seschrock static int
109*6316Seschrock elem_parse_td(ses2_td_hdr_impl_t *tip, const char *tp, nvlist_t *nvl)
110*6316Seschrock {
111*6316Seschrock 	int nverr;
112*6316Seschrock 
113*6316Seschrock 	if (tp != NULL)
114*6316Seschrock 		SES_NV_ADD(fixed_string, nverr, nvl, SES_PROP_CLASS_DESCRIPTION,
115*6316Seschrock 		    tp, tip->sthi_text_len);
116*6316Seschrock 
117*6316Seschrock 	return (0);
118*6316Seschrock }
119*6316Seschrock 
120*6316Seschrock 
121*6316Seschrock /*
122*6316Seschrock  * Build a skeleton tree of nodes in the given snapshot.  This is the heart of
123*6316Seschrock  * libses, and is responsible for parsing the config page into a tree and
124*6316Seschrock  * populating nodes with data from the config page.
125*6316Seschrock  */
126*6316Seschrock static int
127*6316Seschrock ses_build_snap_skel(ses_snap_t *sp)
128*6316Seschrock {
129*6316Seschrock 	ses2_ed_impl_t *eip;
130*6316Seschrock 	ses2_td_hdr_impl_t *tip, *ftip;
131*6316Seschrock 	ses_node_t *np, *pnp, *cnp, *root;
132*6316Seschrock 	ses_snap_page_t *pp;
133*6316Seschrock 	ses2_config_page_impl_t *pip;
134*6316Seschrock 	int i, j, n_etds = 0;
135*6316Seschrock 	off_t toff;
136*6316Seschrock 	char *tp, *text;
137*6316Seschrock 	int err;
138*6316Seschrock 	uint64_t idx;
139*6316Seschrock 
140*6316Seschrock 	pp = ses_snap_find_page(sp, SES2_DIAGPAGE_CONFIG, B_FALSE);
141*6316Seschrock 	if (pp == NULL)
142*6316Seschrock 		return (ses_error(ESES_BAD_RESPONSE, "target does not support "
143*6316Seschrock 		    "configuration diagnostic page"));
144*6316Seschrock 	pip = (ses2_config_page_impl_t *)pp->ssp_page;
145*6316Seschrock 
146*6316Seschrock 	if (pp->ssp_len < offsetof(ses2_config_page_impl_t, scpi_data))
147*6316Seschrock 		return (ses_error(ESES_BAD_RESPONSE, "no enclosure "
148*6316Seschrock 		    "descriptors found"));
149*6316Seschrock 
150*6316Seschrock 	/*
151*6316Seschrock 	 * Start with the root of the tree, which is a target node, containing
152*6316Seschrock 	 * just the SCSI inquiry properties.
153*6316Seschrock 	 */
154*6316Seschrock 	if ((root = ses_node_alloc(sp, sp->ss_root)) == NULL)
155*6316Seschrock 		return (-1);
156*6316Seschrock 
157*6316Seschrock 	root->sn_type = SES_NODE_TARGET;
158*6316Seschrock 	SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_VENDOR,
159*6316Seschrock 	    libscsi_vendor(sp->ss_target->st_target));
160*6316Seschrock 	SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_PRODUCT,
161*6316Seschrock 	    libscsi_product(sp->ss_target->st_target));
162*6316Seschrock 	SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_REVISION,
163*6316Seschrock 	    libscsi_revision(sp->ss_target->st_target));
164*6316Seschrock 
165*6316Seschrock 	for (eip = (ses2_ed_impl_t *)pip->scpi_data, i = 0;
166*6316Seschrock 	    i < pip->scpi_n_subenclosures + 1;
167*6316Seschrock 	    i++, eip = NEXT_ED(eip)) {
168*6316Seschrock 		if (!SES_WITHIN_PAGE_STRUCT(eip, pp->ssp_page, pp->ssp_len))
169*6316Seschrock 			break;
170*6316Seschrock 
171*6316Seschrock 		n_etds += eip->st_hdr.sehi_n_etd_hdrs;
172*6316Seschrock 	}
173*6316Seschrock 	ftip = (ses2_td_hdr_impl_t *)eip;
174*6316Seschrock 
175*6316Seschrock 	/*
176*6316Seschrock 	 * There should really be only one Enclosure element possible for a
177*6316Seschrock 	 * give subenclosure ID.  The standard never comes out and says this,
178*6316Seschrock 	 * but it does describe this element as "managing the enclosure itself"
179*6316Seschrock 	 * which implies rather strongly that the subenclosure ID field is that
180*6316Seschrock 	 * of, well, the enclosure itself.  Since an enclosure can't contain
181*6316Seschrock 	 * itself, it follows logically that each subenclosure has at most one
182*6316Seschrock 	 * Enclosure type descriptor elements matching its ID.  Of course, some
183*6316Seschrock 	 * enclosure firmware is buggy, so this may not always work out; in
184*6316Seschrock 	 * this case we just ignore all but the first Enclosure-type element
185*6316Seschrock 	 * with our subenclosure ID.
186*6316Seschrock 	 */
187*6316Seschrock 	for (eip = (ses2_ed_impl_t *)pip->scpi_data, i = 0;
188*6316Seschrock 	    i < pip->scpi_n_subenclosures + 1;
189*6316Seschrock 	    i++, eip = NEXT_ED(eip)) {
190*6316Seschrock 		if (!SES_WITHIN_PAGE_STRUCT(eip, pp->ssp_page, pp->ssp_len))
191*6316Seschrock 			break;
192*6316Seschrock 
193*6316Seschrock 		if ((np = ses_node_alloc(sp, root)) == NULL)
194*6316Seschrock 			return (-1);
195*6316Seschrock 
196*6316Seschrock 		np->sn_type = SES_NODE_ENCLOSURE;
197*6316Seschrock 		np->sn_enc_num = eip->st_hdr.sehi_subenclosure_id;
198*6316Seschrock 
199*6316Seschrock 		if (!SES_WITHIN_PAGE(eip, eip->st_hdr.sehi_ed_len +
200*6316Seschrock 		    sizeof (ses2_ed_hdr_impl_t),
201*6316Seschrock 		    pp->ssp_page, pp->ssp_len))
202*6316Seschrock 			break;
203*6316Seschrock 
204*6316Seschrock 		if (enc_parse_ed(eip, np->sn_props) != 0)
205*6316Seschrock 			return (-1);
206*6316Seschrock 	}
207*6316Seschrock 
208*6316Seschrock 	if (root->sn_first_child == NULL)
209*6316Seschrock 		return (ses_error(ESES_BAD_RESPONSE, "no enclosure "
210*6316Seschrock 		    "descriptors found"));
211*6316Seschrock 
212*6316Seschrock 	tp = (char *)(ftip + n_etds);
213*6316Seschrock 
214*6316Seschrock 	for (i = 0, toff = 0, idx = 0; i < n_etds; i++) {
215*6316Seschrock 		tip = ftip + i;
216*6316Seschrock 
217*6316Seschrock 		if (!SES_WITHIN_PAGE_STRUCT(tip, pp->ssp_page, pp->ssp_len))
218*6316Seschrock 			break;
219*6316Seschrock 
220*6316Seschrock 		pnp = ses_find_enclosure(sp,
221*6316Seschrock 		    tip->sthi_subenclosure_id);
222*6316Seschrock 		if (pnp == NULL) {
223*6316Seschrock 			idx += tip->sthi_max_elements + 1;
224*6316Seschrock 			toff += tip->sthi_text_len;
225*6316Seschrock 			continue;
226*6316Seschrock 		}
227*6316Seschrock 
228*6316Seschrock 		if (tip->sthi_element_type == SES_ET_ENCLOSURE) {
229*6316Seschrock 			if (tip->sthi_max_elements == 0) {
230*6316Seschrock 				SES_NV_ADD(uint64, err, pnp->sn_props,
231*6316Seschrock 				    SES_PROP_ELEMENT_INDEX, idx);
232*6316Seschrock 				pnp->sn_rootidx = idx;
233*6316Seschrock 			} else {
234*6316Seschrock 				SES_NV_ADD(uint64, err, pnp->sn_props,
235*6316Seschrock 				    SES_PROP_ELEMENT_INDEX, idx + 1);
236*6316Seschrock 				pnp->sn_rootidx = idx + 1;
237*6316Seschrock 			}
238*6316Seschrock 
239*6316Seschrock 			if (tip->sthi_text_len > 0 &&
240*6316Seschrock 			    SES_WITHIN_PAGE(tp + toff, tip->sthi_text_len,
241*6316Seschrock 			    pp->ssp_page, pp->ssp_len)) {
242*6316Seschrock 				text = tp + toff;
243*6316Seschrock 				toff += tip->sthi_text_len;
244*6316Seschrock 			} else {
245*6316Seschrock 				text = NULL;
246*6316Seschrock 			}
247*6316Seschrock 
248*6316Seschrock 			SES_NV_ADD(uint64, err, pnp->sn_props,
249*6316Seschrock 			    SES_PROP_ELEMENT_TYPE, SES_ET_ENCLOSURE);
250*6316Seschrock 			if (enc_parse_td(tip, text, pnp->sn_props) != 0)
251*6316Seschrock 				return (-1);
252*6316Seschrock 
253*6316Seschrock 			idx += tip->sthi_max_elements + 1;
254*6316Seschrock 			continue;
255*6316Seschrock 		}
256*6316Seschrock 
257*6316Seschrock 		if ((np = ses_node_alloc(sp, pnp)) == NULL)
258*6316Seschrock 			return (-1);
259*6316Seschrock 
260*6316Seschrock 		np->sn_type = SES_NODE_AGGREGATE;
261*6316Seschrock 		np->sn_enc_num = tip->sthi_subenclosure_id;
262*6316Seschrock 		np->sn_parent = pnp;
263*6316Seschrock 		np->sn_rootidx = idx;
264*6316Seschrock 
265*6316Seschrock 		SES_NV_ADD(uint64, err, np->sn_props,
266*6316Seschrock 		    SES_PROP_ELEMENT_INDEX, idx);
267*6316Seschrock 		SES_NV_ADD(uint64, err, np->sn_props,
268*6316Seschrock 		    SES_PROP_ELEMENT_TYPE, tip->sthi_element_type);
269*6316Seschrock 
270*6316Seschrock 		if (tip->sthi_text_len > 0 &&
271*6316Seschrock 		    SES_WITHIN_PAGE(tp + toff, tip->sthi_text_len,
272*6316Seschrock 		    pp->ssp_page, pp->ssp_len)) {
273*6316Seschrock 			text = tp + toff;
274*6316Seschrock 			toff += tip->sthi_text_len;
275*6316Seschrock 		} else {
276*6316Seschrock 			text = NULL;
277*6316Seschrock 		}
278*6316Seschrock 
279*6316Seschrock 		if (elem_parse_td(tip, text, np->sn_props) != 0)
280*6316Seschrock 			return (-1);
281*6316Seschrock 
282*6316Seschrock 		idx += tip->sthi_max_elements + 1;
283*6316Seschrock 
284*6316Seschrock 		if (tip->sthi_max_elements == 0)
285*6316Seschrock 			continue;
286*6316Seschrock 
287*6316Seschrock 		for (j = 0; j < tip->sthi_max_elements; j++) {
288*6316Seschrock 			cnp = ses_node_alloc(sp, np);
289*6316Seschrock 			if (cnp == NULL)
290*6316Seschrock 				return (-1);
291*6316Seschrock 
292*6316Seschrock 			cnp->sn_type = SES_NODE_ELEMENT;
293*6316Seschrock 			SES_NV_ADD(uint64, err, cnp->sn_props,
294*6316Seschrock 			    SES_PROP_ELEMENT_INDEX, np->sn_rootidx + j + 1);
295*6316Seschrock 			SES_NV_ADD(uint64, err, cnp->sn_props,
296*6316Seschrock 			    SES_PROP_ELEMENT_CLASS_INDEX, j);
297*6316Seschrock 			SES_NV_ADD(uint64, err, cnp->sn_props,
298*6316Seschrock 			    SES_PROP_ELEMENT_TYPE, tip->sthi_element_type);
299*6316Seschrock 		}
300*6316Seschrock 	}
301*6316Seschrock 
302*6316Seschrock 	np->sn_snapshot->ss_n_elem = idx;
303*6316Seschrock 
304*6316Seschrock 	return (0);
305*6316Seschrock }
306*6316Seschrock 
307*6316Seschrock static int
308*6316Seschrock ses_fill_tree(ses_node_t *np)
309*6316Seschrock {
310*6316Seschrock 	if (np == NULL)
311*6316Seschrock 		return (0);
312*6316Seschrock 
313*6316Seschrock 	for (; np != NULL; np = np->sn_next_sibling) {
314*6316Seschrock 		if (ses_fill_node(np) != 0)
315*6316Seschrock 			return (-1);
316*6316Seschrock 		if (ses_fill_tree(np->sn_first_child) != 0)
317*6316Seschrock 			return (-1);
318*6316Seschrock 	}
319*6316Seschrock 
320*6316Seschrock 	return (0);
321*6316Seschrock }
322*6316Seschrock 
323*6316Seschrock int
324*6316Seschrock ses_fill_snap(ses_snap_t *sp)
325*6316Seschrock {
326*6316Seschrock 	if (ses_build_snap_skel(sp) != 0)
327*6316Seschrock 		return (-1);
328*6316Seschrock 
329*6316Seschrock 	if (ses_fill_tree(sp->ss_root) != 0)
330*6316Seschrock 		return (-1);
331*6316Seschrock 
332*6316Seschrock 	return (0);
333*6316Seschrock }
334*6316Seschrock 
335*6316Seschrock ses_node_t *
336*6316Seschrock ses_root_node(ses_snap_t *sp)
337*6316Seschrock {
338*6316Seschrock 	return (sp->ss_root);
339*6316Seschrock }
340*6316Seschrock 
341*6316Seschrock ses_node_t *
342*6316Seschrock ses_node_sibling(ses_node_t *np)
343*6316Seschrock {
344*6316Seschrock 	return (np->sn_next_sibling);
345*6316Seschrock }
346*6316Seschrock 
347*6316Seschrock ses_node_t *
348*6316Seschrock ses_node_prev_sibling(ses_node_t *np)
349*6316Seschrock {
350*6316Seschrock 	return (np->sn_prev_sibling);
351*6316Seschrock }
352*6316Seschrock 
353*6316Seschrock ses_node_t *
354*6316Seschrock ses_node_parent(ses_node_t *np)
355*6316Seschrock {
356*6316Seschrock 	return (np->sn_parent);
357*6316Seschrock }
358*6316Seschrock 
359*6316Seschrock ses_node_t *
360*6316Seschrock ses_node_child(ses_node_t *np)
361*6316Seschrock {
362*6316Seschrock 	return (np->sn_first_child);
363*6316Seschrock }
364*6316Seschrock 
365*6316Seschrock ses_node_type_t
366*6316Seschrock ses_node_type(ses_node_t *np)
367*6316Seschrock {
368*6316Seschrock 	return (np->sn_type);
369*6316Seschrock }
370*6316Seschrock 
371*6316Seschrock ses_snap_t *
372*6316Seschrock ses_node_snapshot(ses_node_t *np)
373*6316Seschrock {
374*6316Seschrock 	return ((ses_snap_t *)np->sn_snapshot);
375*6316Seschrock }
376*6316Seschrock 
377*6316Seschrock ses_target_t *
378*6316Seschrock ses_node_target(ses_node_t *np)
379*6316Seschrock {
380*6316Seschrock 	return (np->sn_snapshot->ss_target);
381*6316Seschrock }
382*6316Seschrock 
383*6316Seschrock nvlist_t *
384*6316Seschrock ses_node_props(ses_node_t *np)
385*6316Seschrock {
386*6316Seschrock 	return (np->sn_props);
387*6316Seschrock }
388*6316Seschrock 
389*6316Seschrock /*
390*6316Seschrock  * A node identifier is a (generation, index) tuple that can be used to lookup a
391*6316Seschrock  * node within this target at a later point.  This will be valid across
392*6316Seschrock  * snapshots, though it will return failure if the generation count has changed.
393*6316Seschrock  */
394*6316Seschrock uint64_t
395*6316Seschrock ses_node_id(ses_node_t *np)
396*6316Seschrock {
397*6316Seschrock 	return (((uint64_t)np->sn_snapshot->ss_generation << 32) |
398*6316Seschrock 	    np->sn_id);
399*6316Seschrock }
400