16316Seschrock /*
26316Seschrock  * CDDL HEADER START
36316Seschrock  *
46316Seschrock  * The contents of this file are subject to the terms of the
56316Seschrock  * Common Development and Distribution License (the "License").
66316Seschrock  * You may not use this file except in compliance with the License.
76316Seschrock  *
86316Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96316Seschrock  * or http://www.opensolaris.org/os/licensing.
106316Seschrock  * See the License for the specific language governing permissions
116316Seschrock  * and limitations under the License.
126316Seschrock  *
136316Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
146316Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156316Seschrock  * If applicable, add the following below this CDDL HEADER, with the
166316Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
176316Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
186316Seschrock  *
196316Seschrock  * CDDL HEADER END
206316Seschrock  */
216316Seschrock 
226316Seschrock /*
236316Seschrock  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
246316Seschrock  * Use is subject to license terms.
256316Seschrock  */
266316Seschrock 
276316Seschrock #pragma ident	"%Z%%M%	%I%	%E% SMI"
286316Seschrock 
296316Seschrock #include <scsi/libses.h>
306316Seschrock #include "ses_impl.h"
316316Seschrock 
326316Seschrock #define	NEXT_ED(eip)	\
336316Seschrock 	((ses2_ed_impl_t *)((uint8_t *)(eip) + 	\
346316Seschrock 	    ((eip)->st_hdr.sehi_ed_len + sizeof (ses2_ed_hdr_impl_t))))
356316Seschrock 
366316Seschrock static ses_node_t *
376316Seschrock ses_find_enclosure(ses_snap_t *sp, uint64_t number)
386316Seschrock {
396316Seschrock 	ses_node_t *np;
406316Seschrock 
416316Seschrock 	for (np = sp->ss_root->sn_first_child; np != NULL;
426316Seschrock 	    np = np->sn_next_sibling) {
436316Seschrock 		ASSERT(np->sn_type == SES_NODE_ENCLOSURE);
446316Seschrock 		if (np->sn_enc_num == number)
456316Seschrock 			return ((ses_node_t *)np);
466316Seschrock 	}
476316Seschrock 
486316Seschrock 	return (NULL);
496316Seschrock }
506316Seschrock 
51*6846Sjmcp /*
52*6846Sjmcp  * ses_snap_primary_enclosure() finds the primary enclosure for
53*6846Sjmcp  * the supplied ses_snap_t.
54*6846Sjmcp  */
55*6846Sjmcp ses_node_t *
56*6846Sjmcp ses_snap_primary_enclosure(ses_snap_t *sp)
57*6846Sjmcp {
58*6846Sjmcp 	return (ses_find_enclosure(sp, 0));
59*6846Sjmcp }
60*6846Sjmcp 
616316Seschrock void
626316Seschrock ses_node_teardown(ses_node_t *np)
636316Seschrock {
646316Seschrock 	ses_node_t *rp;
656316Seschrock 
666316Seschrock 	if (np == NULL)
676316Seschrock 		return;
686316Seschrock 
696316Seschrock 	for (; np != NULL; np = rp) {
706316Seschrock 		ses_node_teardown(np->sn_first_child);
716316Seschrock 		rp = np->sn_next_sibling;
726316Seschrock 		nvlist_free(np->sn_props);
736316Seschrock 		ses_free(np);
746316Seschrock 	}
756316Seschrock }
766316Seschrock 
776316Seschrock static ses_node_t *
786316Seschrock ses_node_alloc(ses_snap_t *sp, ses_node_t *pnp)
796316Seschrock {
806316Seschrock 	ses_node_t *np;
816316Seschrock 
826316Seschrock 	np = ses_zalloc(sizeof (ses_node_t));
836316Seschrock 	if (np == NULL)
846316Seschrock 		goto fail;
856316Seschrock 	if (nvlist_alloc(&np->sn_props, NV_UNIQUE_NAME, 0) != 0)
866316Seschrock 		goto fail;
876316Seschrock 
886316Seschrock 	np->sn_snapshot = sp;
896316Seschrock 	np->sn_id = sp->ss_n_nodes++;
906316Seschrock 
916316Seschrock 	if (pnp == NULL) {
926316Seschrock 		ASSERT(sp->ss_root == NULL);
936316Seschrock 		sp->ss_root = np;
946316Seschrock 	} else {
956316Seschrock 		np->sn_parent = pnp;
966316Seschrock 		np->sn_prev_sibling = pnp->sn_last_child;
976316Seschrock 
986316Seschrock 		if (pnp->sn_first_child == NULL)
996316Seschrock 			pnp->sn_first_child = np;
1006316Seschrock 		else
1016316Seschrock 			pnp->sn_last_child->sn_next_sibling = np;
1026316Seschrock 
1036316Seschrock 		pnp->sn_last_child = np;
1046316Seschrock 	}
1056316Seschrock 
1066316Seschrock 	return (np);
1076316Seschrock 
1086316Seschrock fail:
1096316Seschrock 	ses_free(np);
1106316Seschrock 	ses_node_teardown(sp->ss_root);
1116316Seschrock 	sp->ss_root = NULL;
1126316Seschrock 	return (NULL);
1136316Seschrock }
1146316Seschrock 
1156316Seschrock /*
1166316Seschrock  * Parse element type descriptor.
1176316Seschrock  */
1186316Seschrock static int
1196316Seschrock elem_parse_td(ses2_td_hdr_impl_t *tip, const char *tp, nvlist_t *nvl)
1206316Seschrock {
1216316Seschrock 	int nverr;
1226316Seschrock 
1236316Seschrock 	if (tp != NULL)
1246316Seschrock 		SES_NV_ADD(fixed_string, nverr, nvl, SES_PROP_CLASS_DESCRIPTION,
1256316Seschrock 		    tp, tip->sthi_text_len);
1266316Seschrock 
1276316Seschrock 	return (0);
1286316Seschrock }
1296316Seschrock 
1306316Seschrock 
1316316Seschrock /*
1326316Seschrock  * Build a skeleton tree of nodes in the given snapshot.  This is the heart of
1336316Seschrock  * libses, and is responsible for parsing the config page into a tree and
1346316Seschrock  * populating nodes with data from the config page.
1356316Seschrock  */
1366316Seschrock static int
1376316Seschrock ses_build_snap_skel(ses_snap_t *sp)
1386316Seschrock {
1396316Seschrock 	ses2_ed_impl_t *eip;
1406316Seschrock 	ses2_td_hdr_impl_t *tip, *ftip;
1416316Seschrock 	ses_node_t *np, *pnp, *cnp, *root;
1426316Seschrock 	ses_snap_page_t *pp;
1436316Seschrock 	ses2_config_page_impl_t *pip;
1446316Seschrock 	int i, j, n_etds = 0;
1456316Seschrock 	off_t toff;
1466316Seschrock 	char *tp, *text;
1476316Seschrock 	int err;
1486316Seschrock 	uint64_t idx;
1496316Seschrock 
1506316Seschrock 	pp = ses_snap_find_page(sp, SES2_DIAGPAGE_CONFIG, B_FALSE);
1516316Seschrock 	if (pp == NULL)
1526316Seschrock 		return (ses_error(ESES_BAD_RESPONSE, "target does not support "
1536316Seschrock 		    "configuration diagnostic page"));
1546316Seschrock 	pip = (ses2_config_page_impl_t *)pp->ssp_page;
1556316Seschrock 
1566316Seschrock 	if (pp->ssp_len < offsetof(ses2_config_page_impl_t, scpi_data))
1576316Seschrock 		return (ses_error(ESES_BAD_RESPONSE, "no enclosure "
1586316Seschrock 		    "descriptors found"));
1596316Seschrock 
1606316Seschrock 	/*
1616316Seschrock 	 * Start with the root of the tree, which is a target node, containing
1626316Seschrock 	 * just the SCSI inquiry properties.
1636316Seschrock 	 */
1646316Seschrock 	if ((root = ses_node_alloc(sp, sp->ss_root)) == NULL)
1656316Seschrock 		return (-1);
1666316Seschrock 
1676316Seschrock 	root->sn_type = SES_NODE_TARGET;
1686316Seschrock 	SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_VENDOR,
1696316Seschrock 	    libscsi_vendor(sp->ss_target->st_target));
1706316Seschrock 	SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_PRODUCT,
1716316Seschrock 	    libscsi_product(sp->ss_target->st_target));
1726316Seschrock 	SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_REVISION,
1736316Seschrock 	    libscsi_revision(sp->ss_target->st_target));
1746316Seschrock 
1756316Seschrock 	for (eip = (ses2_ed_impl_t *)pip->scpi_data, i = 0;
1766316Seschrock 	    i < pip->scpi_n_subenclosures + 1;
1776316Seschrock 	    i++, eip = NEXT_ED(eip)) {
1786316Seschrock 		if (!SES_WITHIN_PAGE_STRUCT(eip, pp->ssp_page, pp->ssp_len))
1796316Seschrock 			break;
1806316Seschrock 
1816316Seschrock 		n_etds += eip->st_hdr.sehi_n_etd_hdrs;
1826316Seschrock 	}
1836316Seschrock 	ftip = (ses2_td_hdr_impl_t *)eip;
1846316Seschrock 
1856316Seschrock 	/*
1866316Seschrock 	 * There should really be only one Enclosure element possible for a
1876316Seschrock 	 * give subenclosure ID.  The standard never comes out and says this,
1886316Seschrock 	 * but it does describe this element as "managing the enclosure itself"
1896316Seschrock 	 * which implies rather strongly that the subenclosure ID field is that
1906316Seschrock 	 * of, well, the enclosure itself.  Since an enclosure can't contain
1916316Seschrock 	 * itself, it follows logically that each subenclosure has at most one
1926316Seschrock 	 * Enclosure type descriptor elements matching its ID.  Of course, some
1936316Seschrock 	 * enclosure firmware is buggy, so this may not always work out; in
1946316Seschrock 	 * this case we just ignore all but the first Enclosure-type element
1956316Seschrock 	 * with our subenclosure ID.
1966316Seschrock 	 */
1976316Seschrock 	for (eip = (ses2_ed_impl_t *)pip->scpi_data, i = 0;
1986316Seschrock 	    i < pip->scpi_n_subenclosures + 1;
1996316Seschrock 	    i++, eip = NEXT_ED(eip)) {
2006316Seschrock 		if (!SES_WITHIN_PAGE_STRUCT(eip, pp->ssp_page, pp->ssp_len))
2016316Seschrock 			break;
2026316Seschrock 
2036316Seschrock 		if ((np = ses_node_alloc(sp, root)) == NULL)
2046316Seschrock 			return (-1);
2056316Seschrock 
2066316Seschrock 		np->sn_type = SES_NODE_ENCLOSURE;
2076316Seschrock 		np->sn_enc_num = eip->st_hdr.sehi_subenclosure_id;
2086316Seschrock 
2096316Seschrock 		if (!SES_WITHIN_PAGE(eip, eip->st_hdr.sehi_ed_len +
2106316Seschrock 		    sizeof (ses2_ed_hdr_impl_t),
2116316Seschrock 		    pp->ssp_page, pp->ssp_len))
2126316Seschrock 			break;
2136316Seschrock 
2146316Seschrock 		if (enc_parse_ed(eip, np->sn_props) != 0)
2156316Seschrock 			return (-1);
2166316Seschrock 	}
2176316Seschrock 
2186316Seschrock 	if (root->sn_first_child == NULL)
2196316Seschrock 		return (ses_error(ESES_BAD_RESPONSE, "no enclosure "
2206316Seschrock 		    "descriptors found"));
2216316Seschrock 
2226316Seschrock 	tp = (char *)(ftip + n_etds);
2236316Seschrock 
2246316Seschrock 	for (i = 0, toff = 0, idx = 0; i < n_etds; i++) {
2256316Seschrock 		tip = ftip + i;
2266316Seschrock 
2276316Seschrock 		if (!SES_WITHIN_PAGE_STRUCT(tip, pp->ssp_page, pp->ssp_len))
2286316Seschrock 			break;
2296316Seschrock 
2306316Seschrock 		pnp = ses_find_enclosure(sp,
2316316Seschrock 		    tip->sthi_subenclosure_id);
2326316Seschrock 		if (pnp == NULL) {
2336316Seschrock 			idx += tip->sthi_max_elements + 1;
2346316Seschrock 			toff += tip->sthi_text_len;
2356316Seschrock 			continue;
2366316Seschrock 		}
2376316Seschrock 
2386316Seschrock 		if (tip->sthi_element_type == SES_ET_ENCLOSURE) {
2396316Seschrock 			if (tip->sthi_max_elements == 0) {
2406316Seschrock 				SES_NV_ADD(uint64, err, pnp->sn_props,
2416316Seschrock 				    SES_PROP_ELEMENT_INDEX, idx);
2426316Seschrock 				pnp->sn_rootidx = idx;
2436316Seschrock 			} else {
2446316Seschrock 				SES_NV_ADD(uint64, err, pnp->sn_props,
2456316Seschrock 				    SES_PROP_ELEMENT_INDEX, idx + 1);
2466316Seschrock 				pnp->sn_rootidx = idx + 1;
2476316Seschrock 			}
2486316Seschrock 
2496316Seschrock 			if (tip->sthi_text_len > 0 &&
2506316Seschrock 			    SES_WITHIN_PAGE(tp + toff, tip->sthi_text_len,
2516316Seschrock 			    pp->ssp_page, pp->ssp_len)) {
2526316Seschrock 				text = tp + toff;
2536316Seschrock 				toff += tip->sthi_text_len;
2546316Seschrock 			} else {
2556316Seschrock 				text = NULL;
2566316Seschrock 			}
2576316Seschrock 
2586316Seschrock 			SES_NV_ADD(uint64, err, pnp->sn_props,
2596316Seschrock 			    SES_PROP_ELEMENT_TYPE, SES_ET_ENCLOSURE);
2606316Seschrock 			if (enc_parse_td(tip, text, pnp->sn_props) != 0)
2616316Seschrock 				return (-1);
2626316Seschrock 
2636316Seschrock 			idx += tip->sthi_max_elements + 1;
2646316Seschrock 			continue;
2656316Seschrock 		}
2666316Seschrock 
2676316Seschrock 		if ((np = ses_node_alloc(sp, pnp)) == NULL)
2686316Seschrock 			return (-1);
2696316Seschrock 
2706316Seschrock 		np->sn_type = SES_NODE_AGGREGATE;
2716316Seschrock 		np->sn_enc_num = tip->sthi_subenclosure_id;
2726316Seschrock 		np->sn_parent = pnp;
2736316Seschrock 		np->sn_rootidx = idx;
2746316Seschrock 
2756316Seschrock 		SES_NV_ADD(uint64, err, np->sn_props,
2766316Seschrock 		    SES_PROP_ELEMENT_INDEX, idx);
2776316Seschrock 		SES_NV_ADD(uint64, err, np->sn_props,
2786316Seschrock 		    SES_PROP_ELEMENT_TYPE, tip->sthi_element_type);
2796316Seschrock 
2806316Seschrock 		if (tip->sthi_text_len > 0 &&
2816316Seschrock 		    SES_WITHIN_PAGE(tp + toff, tip->sthi_text_len,
2826316Seschrock 		    pp->ssp_page, pp->ssp_len)) {
2836316Seschrock 			text = tp + toff;
2846316Seschrock 			toff += tip->sthi_text_len;
2856316Seschrock 		} else {
2866316Seschrock 			text = NULL;
2876316Seschrock 		}
2886316Seschrock 
2896316Seschrock 		if (elem_parse_td(tip, text, np->sn_props) != 0)
2906316Seschrock 			return (-1);
2916316Seschrock 
2926316Seschrock 		idx += tip->sthi_max_elements + 1;
2936316Seschrock 
2946316Seschrock 		if (tip->sthi_max_elements == 0)
2956316Seschrock 			continue;
2966316Seschrock 
2976316Seschrock 		for (j = 0; j < tip->sthi_max_elements; j++) {
2986316Seschrock 			cnp = ses_node_alloc(sp, np);
2996316Seschrock 			if (cnp == NULL)
3006316Seschrock 				return (-1);
3016316Seschrock 
3026316Seschrock 			cnp->sn_type = SES_NODE_ELEMENT;
3036316Seschrock 			SES_NV_ADD(uint64, err, cnp->sn_props,
3046316Seschrock 			    SES_PROP_ELEMENT_INDEX, np->sn_rootidx + j + 1);
3056316Seschrock 			SES_NV_ADD(uint64, err, cnp->sn_props,
3066316Seschrock 			    SES_PROP_ELEMENT_CLASS_INDEX, j);
3076316Seschrock 			SES_NV_ADD(uint64, err, cnp->sn_props,
3086316Seschrock 			    SES_PROP_ELEMENT_TYPE, tip->sthi_element_type);
3096316Seschrock 		}
3106316Seschrock 	}
3116316Seschrock 
3126316Seschrock 	np->sn_snapshot->ss_n_elem = idx;
3136316Seschrock 
3146316Seschrock 	return (0);
3156316Seschrock }
3166316Seschrock 
3176316Seschrock static int
3186316Seschrock ses_fill_tree(ses_node_t *np)
3196316Seschrock {
3206316Seschrock 	if (np == NULL)
3216316Seschrock 		return (0);
3226316Seschrock 
3236316Seschrock 	for (; np != NULL; np = np->sn_next_sibling) {
3246316Seschrock 		if (ses_fill_node(np) != 0)
3256316Seschrock 			return (-1);
3266316Seschrock 		if (ses_fill_tree(np->sn_first_child) != 0)
3276316Seschrock 			return (-1);
3286316Seschrock 	}
3296316Seschrock 
3306316Seschrock 	return (0);
3316316Seschrock }
3326316Seschrock 
3336316Seschrock int
3346316Seschrock ses_fill_snap(ses_snap_t *sp)
3356316Seschrock {
3366316Seschrock 	if (ses_build_snap_skel(sp) != 0)
3376316Seschrock 		return (-1);
3386316Seschrock 
3396316Seschrock 	if (ses_fill_tree(sp->ss_root) != 0)
3406316Seschrock 		return (-1);
3416316Seschrock 
3426316Seschrock 	return (0);
3436316Seschrock }
3446316Seschrock 
3456316Seschrock ses_node_t *
3466316Seschrock ses_root_node(ses_snap_t *sp)
3476316Seschrock {
3486316Seschrock 	return (sp->ss_root);
3496316Seschrock }
3506316Seschrock 
3516316Seschrock ses_node_t *
3526316Seschrock ses_node_sibling(ses_node_t *np)
3536316Seschrock {
3546316Seschrock 	return (np->sn_next_sibling);
3556316Seschrock }
3566316Seschrock 
3576316Seschrock ses_node_t *
3586316Seschrock ses_node_prev_sibling(ses_node_t *np)
3596316Seschrock {
3606316Seschrock 	return (np->sn_prev_sibling);
3616316Seschrock }
3626316Seschrock 
3636316Seschrock ses_node_t *
3646316Seschrock ses_node_parent(ses_node_t *np)
3656316Seschrock {
3666316Seschrock 	return (np->sn_parent);
3676316Seschrock }
3686316Seschrock 
3696316Seschrock ses_node_t *
3706316Seschrock ses_node_child(ses_node_t *np)
3716316Seschrock {
3726316Seschrock 	return (np->sn_first_child);
3736316Seschrock }
3746316Seschrock 
3756316Seschrock ses_node_type_t
3766316Seschrock ses_node_type(ses_node_t *np)
3776316Seschrock {
3786316Seschrock 	return (np->sn_type);
3796316Seschrock }
3806316Seschrock 
3816316Seschrock ses_snap_t *
3826316Seschrock ses_node_snapshot(ses_node_t *np)
3836316Seschrock {
3846316Seschrock 	return ((ses_snap_t *)np->sn_snapshot);
3856316Seschrock }
3866316Seschrock 
3876316Seschrock ses_target_t *
3886316Seschrock ses_node_target(ses_node_t *np)
3896316Seschrock {
3906316Seschrock 	return (np->sn_snapshot->ss_target);
3916316Seschrock }
3926316Seschrock 
3936316Seschrock nvlist_t *
3946316Seschrock ses_node_props(ses_node_t *np)
3956316Seschrock {
3966316Seschrock 	return (np->sn_props);
3976316Seschrock }
3986316Seschrock 
3996316Seschrock /*
4006316Seschrock  * A node identifier is a (generation, index) tuple that can be used to lookup a
4016316Seschrock  * node within this target at a later point.  This will be valid across
4026316Seschrock  * snapshots, though it will return failure if the generation count has changed.
4036316Seschrock  */
4046316Seschrock uint64_t
4056316Seschrock ses_node_id(ses_node_t *np)
4066316Seschrock {
4076316Seschrock 	return (((uint64_t)np->sn_snapshot->ss_generation << 32) |
4086316Seschrock 	    np->sn_id);
4096316Seschrock }
410