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 2017 Nexenta Systems, Inc.  All rights reserved.
  14  */
  15 
  16 #include <stddef.h>
  17 #include <libnvpair.h>
  18 #include <scsi/libses.h>
  19 #include <scsi/libses_plugin.h>
  20 #include <scsi/plugins/ses/framework/ses2_impl.h>
  21 
  22 /*
  23  * This module is an almost identical copy of hp
  24  */
  25 
  26 /*
  27  * Override bay number if the invalid bit is set for the AES descriptor
  28  * Same approach used for Lenovo and Sun Loki, with a minor change
  29  * to add 1 to the bay number to address the requirement for the bay numbering
  30  * on the Ericsson SRU-0101 to start with 1 rather than 0.
  31  */
  32 static int
  33 ercsn_0101_fix_bay(ses_plugin_t *sp, ses_node_t *np)
  34 {
  35         ses2_aes_descr_eip_impl_t *dep;
  36         ses2_aes_descr_sas0_eip_impl_t *s0ep;
  37         size_t len;
  38         int nverr;
  39         nvlist_t *props = ses_node_props(np);
  40 
  41         /*
  42          * The spec conveniently defines the bay number as part of the
  43          * additional element status descriptor. However, the AES descriptor
  44          * is technically only valid if the device is inserted.
  45          * Thankfully, the enclosure defines this value even if
  46          * the invalid bit is set, so we override bay value, even for empty
  47          * bays.
  48          */
  49         if ((dep = ses_plugin_page_lookup(sp, ses_node_snapshot(np),
  50             SES2_DIAGPAGE_ADDL_ELEM_STATUS, np, &len)) == NULL)
  51                 return (0);
  52 
  53         if (dep->sadei_protocol_identifier != SPC4_PROTO_SAS ||
  54             !dep->sadei_eip || !dep->sadei_invalid)
  55                 return (0);
  56 
  57         s0ep = (ses2_aes_descr_sas0_eip_impl_t *)dep->sadei_protocol_specific;
  58 
  59         /*
  60          * This nested macro can return -1 on error
  61          */
  62         SES_NV_ADD(uint64, nverr, props, SES_PROP_BAY_NUMBER,
  63             s0ep->sadsi_bay_number + 1);
  64 
  65         return (0);
  66 }
  67 
  68 /*
  69  * FC protocol specific parsing for the given (fp) AES element descriptor.
  70  * Copy-pasted elem_parse_aes_fc() from ses2_elements.c with a minor change
  71  * to add 1 to the bay number to address the requirement for the bay numbering
  72  * on the Ericsson SRU-0101 to start with 1 rather than 0.
  73  */
  74 static int
  75 ercsn_0101_elem_parse_aes_fc(const ses2_aes_descr_fc_eip_impl_t *fp,
  76     nvlist_t *nvl, size_t len)
  77 {
  78         int nverr, i;
  79         nvlist_t **nva;
  80         int nports;
  81 
  82         if (len < offsetof(ses2_aes_descr_fc_eip_impl_t,
  83             sadfi_ports))
  84                 return (0);
  85 
  86         SES_NV_ADD(uint64, nverr, nvl, SES_PROP_BAY_NUMBER,
  87             fp->sadfi_bay_number + 1);
  88         SES_NV_ADD(uint64, nverr, nvl, SES_FC_PROP_NODE_NAME,
  89             SCSI_READ64(&fp->sadfi_node_name));
  90 
  91         nports = MIN(fp->sadfi_n_ports,
  92             (len - offsetof(ses2_aes_descr_fc_eip_impl_t,
  93             sadfi_ports)) / sizeof (ses2_aes_port_descr_impl_t));
  94 
  95         if (nports == 0)
  96                 return (0);
  97 
  98         nva = ses_zalloc(nports * sizeof (nvlist_t *));
  99         if (nva == NULL)
 100                 return (-1);
 101 
 102         for (i = 0; i < nports; i++) {
 103                 if ((nverr = nvlist_alloc(&nva[i], NV_UNIQUE_NAME, 0)) != 0)
 104                         goto fail;
 105                 if ((nverr = nvlist_add_uint64(nva[i], SES_FC_PROP_LOOP_POS,
 106                     fp->sadfi_ports[i].sapdi_port_loop_position)) != 0)
 107                         goto fail;
 108                 if ((nverr = nvlist_add_uint64(nva[i], SES_FC_PROP_REQ_HARDADDR,
 109                     fp->sadfi_ports[i].sapdi_port_requested_hard_address)) != 0)
 110                         goto fail;
 111                 nverr = nvlist_add_uint64(nva[i], SES_FC_PROP_N_PORT_ID,
 112                     SCSI_READ24(fp->sadfi_ports[i].sapdi_n_port_identifier));
 113                 if (nverr != 0)
 114                         goto fail;
 115                 if ((nverr = nvlist_add_uint64(nva[i], SES_FC_PROP_N_PORT_NAME,
 116                     SCSI_READ64(&fp->sadfi_ports[i].sapdi_n_port_name))) != 0)
 117                         goto fail;
 118         }
 119 
 120         if ((nverr = nvlist_add_nvlist_array(nvl, SES_FC_PROP_PORTS,
 121             nva, nports)) != 0)
 122                 goto fail;
 123 
 124         for (i = 0; i < nports && nva[i] != NULL; i++)
 125                 nvlist_free(nva[i]);
 126         ses_free(nva);
 127         return (0);
 128 
 129 fail:
 130         for (i = 0; i < nports && nva[i] != NULL; i++)
 131                 nvlist_free(nva[i]);
 132         ses_free(nva);
 133         return (ses_set_nverrno(nverr, NULL));
 134 }
 135 
 136 /*
 137  * Parse AES descriptor for the given element (dep).
 138  * Copy-pasted elem_parse_aes_device() from ses2_elements.c with a minor change
 139  * to add 1 to the bay number to address the requirement for the bay numbering
 140  * on the Ericsson SRU-0101 to start with 1 rather than 0.
 141  */
 142 static int
 143 ercsn_0101_elem_parse_aes_device(const ses2_aes_descr_eip_impl_t *dep, nvlist_t *nvl,
 144     size_t len)
 145 {
 146         ses2_aes_descr_fc_eip_impl_t *fp;
 147         ses2_aes_descr_sas0_eip_impl_t *s0ep;
 148         ses2_aes_descr_sas0_impl_t *s0p;
 149         ses2_aes_descr_impl_t *dip;
 150         nvlist_t **nva;
 151         int nverr, i;
 152         size_t nphy;
 153 
 154         if (dep->sadei_eip) {
 155                 s0ep = (ses2_aes_descr_sas0_eip_impl_t *)
 156                     dep->sadei_protocol_specific;
 157                 s0p = (ses2_aes_descr_sas0_impl_t *)
 158                     dep->sadei_protocol_specific;
 159         } else {
 160                 dip = (ses2_aes_descr_impl_t *)dep;
 161                 s0ep = NULL;
 162                 s0p = (ses2_aes_descr_sas0_impl_t *)
 163                     dip->sadei_protocol_specific;
 164         }
 165 
 166         if (dep->sadei_invalid)
 167                 return (0);
 168 
 169         if (dep->sadei_protocol_identifier == SPC4_PROTO_FIBRE_CHANNEL) {
 170                 fp = (ses2_aes_descr_fc_eip_impl_t *)
 171                     dep->sadei_protocol_specific;
 172 
 173                 if (!SES_WITHIN_PAGE_STRUCT(fp, dep, len))
 174                         return (0);
 175 
 176                 return (ercsn_0101_elem_parse_aes_fc(fp, nvl, len -
 177                     offsetof(ses2_aes_descr_eip_impl_t,
 178                     sadei_protocol_specific)));
 179         } else if (dep->sadei_protocol_identifier != SPC4_PROTO_SAS) {
 180                 return (0);
 181         }
 182 
 183         if (s0p->sadsi_descriptor_type != SES2_AESD_SAS_DEVICE)
 184                 return (0);
 185 
 186         SES_NV_ADD(boolean_value, nverr, nvl, SES_DEV_PROP_SAS_NOT_ALL_PHYS,
 187             s0p->sadsi_not_all_phys);
 188         if (s0ep != NULL) {
 189                 SES_NV_ADD(uint64, nverr, nvl, SES_PROP_BAY_NUMBER,
 190                     s0ep->sadsi_bay_number + 1);
 191                 nphy = MIN(s0ep->sadsi_n_phy_descriptors,
 192                     (len - offsetof(ses2_aes_descr_sas0_eip_impl_t,
 193                     sadsi_phys)) / sizeof (ses2_aes_phy0_descr_impl_t));
 194         } else {
 195                 nphy = MIN(s0p->sadsi_n_phy_descriptors,
 196                     (len - offsetof(ses2_aes_descr_sas0_impl_t,
 197                     sadsi_phys)) / sizeof (ses2_aes_phy0_descr_impl_t));
 198         }
 199 
 200         if (nphy == 0)
 201                 return (0);
 202 
 203         nva = ses_zalloc(nphy * sizeof (nvlist_t *));
 204         if (nva == NULL)
 205                 return (-1);
 206 
 207         for (i = 0; i < nphy; i++) {
 208                 ses2_aes_phy0_descr_impl_t *pp;
 209                 pp = s0ep != NULL ? &s0ep->sadsi_phys[i] : &s0p->sadsi_phys[i];
 210                 if ((nverr = nvlist_alloc(&nva[i], NV_UNIQUE_NAME, 0)) != 0)
 211                         goto fail;
 212                 if ((nverr = nvlist_add_uint64(nva[i], SES_SAS_PROP_DEVICE_TYPE,
 213                     pp->sapdi_device_type)) != 0)
 214                         goto fail;
 215                 if ((nverr = nvlist_add_boolean_value(nva[i],
 216                     SES_SAS_PROP_SMPI_PORT, pp->sapdi_smp_initiator_port)) != 0)
 217                         goto fail;
 218                 if ((nverr = nvlist_add_boolean_value(nva[i],
 219                     SES_SAS_PROP_STPI_PORT, pp->sapdi_stp_initiator_port)) != 0)
 220                         goto fail;
 221                 if ((nverr = nvlist_add_boolean_value(nva[i],
 222                     SES_SAS_PROP_SSPI_PORT, pp->sapdi_ssp_initiator_port)) != 0)
 223                         goto fail;
 224                 if ((nverr = nvlist_add_boolean_value(nva[i],
 225                     SES_SAS_PROP_SATA_DEVICE, pp->sapdi_sata_device)) != 0)
 226                         goto fail;
 227                 if ((nverr = nvlist_add_boolean_value(nva[i],
 228                     SES_SAS_PROP_SMPT_PORT, pp->sapdi_smp_target_port)) != 0)
 229                         goto fail;
 230                 if ((nverr = nvlist_add_boolean_value(nva[i],
 231                     SES_SAS_PROP_STPT_PORT, pp->sapdi_stp_target_port)) != 0)
 232                         goto fail;
 233                 if ((nverr = nvlist_add_boolean_value(nva[i],
 234                     SES_SAS_PROP_SSPT_PORT, pp->sapdi_ssp_target_port)) != 0)
 235                         goto fail;
 236                 nverr = nvlist_add_uint64(nva[i], SES_SAS_PROP_ATT_ADDR,
 237                     SCSI_READ64(&pp->sapdi_attached_sas_address));
 238                 if (nverr != 0)
 239                         goto fail;
 240                 nverr = nvlist_add_uint64(nva[i], SES_SAS_PROP_ADDR,
 241                     SCSI_READ64(&pp->sapdi_sas_address));
 242                 if (nverr != 0)
 243                         goto fail;
 244                 if ((nverr = nvlist_add_uint64(nva[i], SES_SAS_PROP_PHY_ID,
 245                     pp->sapdi_phy_identifier)) != 0)
 246                         goto fail;
 247         }
 248 
 249         if ((nverr = nvlist_add_nvlist_array(nvl, SES_SAS_PROP_PHYS,
 250             nva, nphy)) != 0)
 251                 goto fail;
 252 
 253         for (i = 0; i < nphy && nva[i] != NULL; i++)
 254                 nvlist_free(nva[i]);
 255         ses_free(nva);
 256         return (0);
 257 
 258 fail:
 259         for (i = 0; i < nphy && nva[i] != NULL; i++)
 260                 nvlist_free(nva[i]);
 261         ses_free(nva);
 262         return (ses_set_nverrno(nverr, NULL));
 263 }
 264 
 265 /*
 266  * Ericcson SRU-0101 specific ses node parsing is needed to correct
 267  * libses assumptions about index numbering.
 268  */
 269 static int
 270 ercsn_0101_parse_node(ses_plugin_t *sp, ses_node_t *np)
 271 {
 272         uint64_t i, type;
 273         int nverr;
 274         size_t len;
 275         nvlist_t *props;
 276         ses2_aes_descr_eip_impl_t *dep;
 277 
 278 
 279         if (ses_node_type(np) != SES_NODE_ELEMENT)
 280                 return (0);
 281 
 282         props = ses_node_props(np);
 283         type = fnvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE);
 284         if (type != SES_ET_ARRAY_DEVICE)
 285                 return (0);
 286 
 287         /*
 288          * Fix bay numbering oddity
 289          */
 290         if (ercsn_0101_fix_bay(sp, np) != 0)
 291                 return (-1);
 292 
 293         if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_ONLY_INDEX, &i) != 0)
 294                 return (0);
 295 
 296         /*
 297          * We populated the element-only-index in ses_build_snap_skel().
 298          * This index starts at zero and is used internally by libses to match
 299          * device element indexes to the indexes obtained from the AES page (see
 300          * ses2_aes_index()).
 301          * Ericsson starts their element index at one so we have an off by one
 302          * error that we are correcting here
 303          */
 304         SES_NV_ADD(uint64, nverr, props, SES_PROP_ELEMENT_ONLY_INDEX, i + 1);
 305 
 306         /* now that we've fixed the index we need to redo the AES parsing */
 307         if ((dep = ses_plugin_page_lookup(sp, ses_node_snapshot(np),
 308             SES2_DIAGPAGE_ADDL_ELEM_STATUS, np, &len)) == NULL)
 309                 return (0);
 310 
 311         return (ercsn_0101_elem_parse_aes_device(dep, props, len));
 312 }
 313 
 314 int
 315 _ses_init(ses_plugin_t *sp)
 316 {
 317         ses_plugin_config_t config = {
 318                 .spc_node_parse = ercsn_0101_parse_node
 319         };
 320 
 321         return (ses_plugin_register(sp, LIBSES_PLUGIN_VERSION, &config) != 0);
 322 }