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 2016 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  * FC protocol specific parsing for the given (fp) AES element descriptor.
  24  * Copy-pasted elem_parse_aes_fc() from ses2_elements.c
  25  */
  26 static int
  27 hp_elem_parse_aes_fc(const ses2_aes_descr_fc_eip_impl_t *fp,
  28     nvlist_t *nvl, size_t len)
  29 {
  30         int nverr, i;
  31         nvlist_t **nva;
  32         int nports;
  33 
  34         if (len < offsetof(ses2_aes_descr_fc_eip_impl_t,
  35             sadfi_ports))
  36                 return (0);
  37 
  38         SES_NV_ADD(uint64, nverr, nvl, SES_PROP_BAY_NUMBER,
  39             fp->sadfi_bay_number);
  40         SES_NV_ADD(uint64, nverr, nvl, SES_FC_PROP_NODE_NAME,
  41             SCSI_READ64(&fp->sadfi_node_name));
  42 
  43         nports = MIN(fp->sadfi_n_ports,
  44             (len - offsetof(ses2_aes_descr_fc_eip_impl_t,
  45             sadfi_ports)) / sizeof (ses2_aes_port_descr_impl_t));
  46 
  47         if (nports == 0)
  48                 return (0);
  49 
  50         nva = ses_zalloc(nports * sizeof (nvlist_t *));
  51         if (nva == NULL)
  52                 return (-1);
  53 
  54         for (i = 0; i < nports; i++) {
  55                 if ((nverr = nvlist_alloc(&nva[i], NV_UNIQUE_NAME, 0)) != 0)
  56                         goto fail;
  57                 if ((nverr = nvlist_add_uint64(nva[i], SES_FC_PROP_LOOP_POS,
  58                     fp->sadfi_ports[i].sapdi_port_loop_position)) != 0)
  59                         goto fail;
  60                 if ((nverr = nvlist_add_uint64(nva[i], SES_FC_PROP_REQ_HARDADDR,
  61                     fp->sadfi_ports[i].sapdi_port_requested_hard_address)) != 0)
  62                         goto fail;
  63                 nverr = nvlist_add_uint64(nva[i], SES_FC_PROP_N_PORT_ID,
  64                     SCSI_READ24(fp->sadfi_ports[i].sapdi_n_port_identifier));
  65                 if (nverr != 0)
  66                         goto fail;
  67                 if ((nverr = nvlist_add_uint64(nva[i], SES_FC_PROP_N_PORT_NAME,
  68                     SCSI_READ64(&fp->sadfi_ports[i].sapdi_n_port_name))) != 0)
  69                         goto fail;
  70         }
  71 
  72         if ((nverr = nvlist_add_nvlist_array(nvl, SES_FC_PROP_PORTS,
  73             nva, nports)) != 0)
  74                 goto fail;
  75 
  76         for (i = 0; i < nports && nva[i] != NULL; i++)
  77                 nvlist_free(nva[i]);
  78         ses_free(nva);
  79         return (0);
  80 
  81 fail:
  82         for (i = 0; i < nports && nva[i] != NULL; i++)
  83                 nvlist_free(nva[i]);
  84         ses_free(nva);
  85         return (ses_set_nverrno(nverr, NULL));
  86 }
  87 
  88 /*
  89  * Parse AES descriptor for the given element (dep).
  90  * Copy-pasted elem_parse_aes_device() from ses2_elements.c
  91  */
  92 static int
  93 hp_elem_parse_aes_device(const ses2_aes_descr_eip_impl_t *dep, nvlist_t *nvl,
  94     size_t len)
  95 {
  96         ses2_aes_descr_fc_eip_impl_t *fp;
  97         ses2_aes_descr_sas0_eip_impl_t *s0ep;
  98         ses2_aes_descr_sas0_impl_t *s0p;
  99         ses2_aes_descr_impl_t *dip;
 100         nvlist_t **nva;
 101         int nverr, i;
 102         size_t nphy;
 103 
 104         if (dep->sadei_eip) {
 105                 s0ep = (ses2_aes_descr_sas0_eip_impl_t *)
 106                     dep->sadei_protocol_specific;
 107                 s0p = (ses2_aes_descr_sas0_impl_t *)
 108                     dep->sadei_protocol_specific;
 109         } else {
 110                 dip = (ses2_aes_descr_impl_t *)dep;
 111                 s0ep = NULL;
 112                 s0p = (ses2_aes_descr_sas0_impl_t *)
 113                     dip->sadei_protocol_specific;
 114         }
 115 
 116         if (dep->sadei_invalid)
 117                 return (0);
 118 
 119         if (dep->sadei_protocol_identifier == SPC4_PROTO_FIBRE_CHANNEL) {
 120                 fp = (ses2_aes_descr_fc_eip_impl_t *)
 121                     dep->sadei_protocol_specific;
 122 
 123                 if (!SES_WITHIN_PAGE_STRUCT(fp, dep, len))
 124                         return (0);
 125 
 126                 return (hp_elem_parse_aes_fc(fp, nvl, len -
 127                     offsetof(ses2_aes_descr_eip_impl_t,
 128                     sadei_protocol_specific)));
 129         } else if (dep->sadei_protocol_identifier != SPC4_PROTO_SAS) {
 130                 return (0);
 131         }
 132 
 133         if (s0p->sadsi_descriptor_type != SES2_AESD_SAS_DEVICE)
 134                 return (0);
 135 
 136         SES_NV_ADD(boolean_value, nverr, nvl, SES_DEV_PROP_SAS_NOT_ALL_PHYS,
 137             s0p->sadsi_not_all_phys);
 138         if (s0ep != NULL) {
 139                 SES_NV_ADD(uint64, nverr, nvl, SES_PROP_BAY_NUMBER,
 140                     s0ep->sadsi_bay_number);
 141                 nphy = MIN(s0ep->sadsi_n_phy_descriptors,
 142                     (len - offsetof(ses2_aes_descr_sas0_eip_impl_t,
 143                     sadsi_phys)) / sizeof (ses2_aes_phy0_descr_impl_t));
 144         } else {
 145                 nphy = MIN(s0p->sadsi_n_phy_descriptors,
 146                     (len - offsetof(ses2_aes_descr_sas0_impl_t,
 147                     sadsi_phys)) / sizeof (ses2_aes_phy0_descr_impl_t));
 148         }
 149 
 150         if (nphy == 0)
 151                 return (0);
 152 
 153         nva = ses_zalloc(nphy * sizeof (nvlist_t *));
 154         if (nva == NULL)
 155                 return (-1);
 156 
 157         for (i = 0; i < nphy; i++) {
 158                 ses2_aes_phy0_descr_impl_t *pp;
 159                 pp = s0ep != NULL ? &s0ep->sadsi_phys[i] : &s0p->sadsi_phys[i];
 160                 if ((nverr = nvlist_alloc(&nva[i], NV_UNIQUE_NAME, 0)) != 0)
 161                         goto fail;
 162                 if ((nverr = nvlist_add_uint64(nva[i], SES_SAS_PROP_DEVICE_TYPE,
 163                     pp->sapdi_device_type)) != 0)
 164                         goto fail;
 165                 if ((nverr = nvlist_add_boolean_value(nva[i],
 166                     SES_SAS_PROP_SMPI_PORT, pp->sapdi_smp_initiator_port)) != 0)
 167                         goto fail;
 168                 if ((nverr = nvlist_add_boolean_value(nva[i],
 169                     SES_SAS_PROP_STPI_PORT, pp->sapdi_stp_initiator_port)) != 0)
 170                         goto fail;
 171                 if ((nverr = nvlist_add_boolean_value(nva[i],
 172                     SES_SAS_PROP_SSPI_PORT, pp->sapdi_ssp_initiator_port)) != 0)
 173                         goto fail;
 174                 if ((nverr = nvlist_add_boolean_value(nva[i],
 175                     SES_SAS_PROP_SATA_DEVICE, pp->sapdi_sata_device)) != 0)
 176                         goto fail;
 177                 if ((nverr = nvlist_add_boolean_value(nva[i],
 178                     SES_SAS_PROP_SMPT_PORT, pp->sapdi_smp_target_port)) != 0)
 179                         goto fail;
 180                 if ((nverr = nvlist_add_boolean_value(nva[i],
 181                     SES_SAS_PROP_STPT_PORT, pp->sapdi_stp_target_port)) != 0)
 182                         goto fail;
 183                 if ((nverr = nvlist_add_boolean_value(nva[i],
 184                     SES_SAS_PROP_SSPT_PORT, pp->sapdi_ssp_target_port)) != 0)
 185                         goto fail;
 186                 nverr = nvlist_add_uint64(nva[i], SES_SAS_PROP_ATT_ADDR,
 187                     SCSI_READ64(&pp->sapdi_attached_sas_address));
 188                 if (nverr != 0)
 189                         goto fail;
 190                 nverr = nvlist_add_uint64(nva[i], SES_SAS_PROP_ADDR,
 191                     SCSI_READ64(&pp->sapdi_sas_address));
 192                 if (nverr != 0)
 193                         goto fail;
 194                 if ((nverr = nvlist_add_uint64(nva[i], SES_SAS_PROP_PHY_ID,
 195                     pp->sapdi_phy_identifier)) != 0)
 196                         goto fail;
 197         }
 198 
 199         if ((nverr = nvlist_add_nvlist_array(nvl, SES_SAS_PROP_PHYS,
 200             nva, nphy)) != 0)
 201                 goto fail;
 202 
 203         for (i = 0; i < nphy && nva[i] != NULL; i++)
 204                 nvlist_free(nva[i]);
 205         ses_free(nva);
 206         return (0);
 207 
 208 fail:
 209         for (i = 0; i < nphy && nva[i] != NULL; i++)
 210                 nvlist_free(nva[i]);
 211         ses_free(nva);
 212         return (ses_set_nverrno(nverr, NULL));
 213 }
 214 
 215 /*
 216  * HP specific ses node parsing is needed to correct libses assumptions about
 217  * index numbering.
 218  */
 219 static int
 220 hp_parse_node(ses_plugin_t *sp, ses_node_t *np)
 221 {
 222         uint64_t i, type;
 223         int nverr;
 224         size_t len;
 225         nvlist_t *props;
 226         ses2_aes_descr_eip_impl_t *dep;
 227 
 228         if (ses_node_type(np) != SES_NODE_ELEMENT)
 229                 return (0);
 230 
 231         props = ses_node_props(np);
 232         VERIFY(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, &type) == 0);
 233         if (type != SES_ET_ARRAY_DEVICE && type != SES_ET_DEVICE)
 234                 return (0);
 235 
 236         if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_ONLY_INDEX, &i) != 0)
 237                 return (0);
 238 
 239         /*
 240          * We populated the element-only-index in ses_build_snap_skel().
 241          * This index starts at zero and is used internally by libses to match
 242          * device element indexes to the indexes obtained from the AES page (see
 243          * ses2_aes_index()).
 244          * HP starts their element index at one so we have an off by one error
 245          * that we are correcting here
 246          */
 247         SES_NV_ADD(uint64, nverr, props, SES_PROP_ELEMENT_ONLY_INDEX, i + 1);
 248 
 249         /* now that we've fixed the index we need to redo the AES parsing */
 250         if ((dep = ses_plugin_page_lookup(sp, ses_node_snapshot(np),
 251             SES2_DIAGPAGE_ADDL_ELEM_STATUS, np, &len)) == NULL)
 252                 return (0);
 253 
 254         return (hp_elem_parse_aes_device(dep, props, len));
 255 }
 256 
 257 int
 258 _ses_init(ses_plugin_t *sp)
 259 {
 260         ses_plugin_config_t config = {
 261                 .spc_node_parse = hp_parse_node
 262         };
 263 
 264         return (ses_plugin_register(sp, LIBSES_PLUGIN_VERSION, &config) != 0);
 265 }