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