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