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 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  26  */
  27 
  28 #include        <sun_sas.h>
  29 #include        <sys/types.h>
  30 #include        <netinet/in.h>
  31 #include        <inttypes.h>
  32 #include        <ctype.h>
  33 #include        <sys/scsi/scsi_address.h>
  34 #include        <libdevid.h>
  35 
  36 /*
  37  * Get the preferred minor node for the given path.
  38  * ":n" for tapes, ":c,raw" for disks,
  39  * and ":0" for enclosures.
  40  */
  41 static void
  42 get_minor(char *devpath, char *minor)
  43 {
  44         const char      ROUTINE[] = "get_minor";
  45         char    fullpath[MAXPATHLEN];
  46         int     fd;
  47 
  48         if ((strstr(devpath, "/st@")) || (strstr(devpath, "/tape@"))) {
  49                 (void) strcpy(minor, ":n");
  50         } else if (strstr(devpath, "/smp@")) {
  51                 (void) strcpy(minor, ":smp");
  52         } else if ((strstr(devpath, "/ssd@")) || (strstr(devpath, "/sd@")) ||
  53             (strstr(devpath, "/disk@"))) {
  54                 (void) strcpy(minor, ":c,raw");
  55         } else if ((strstr(devpath, "/ses@")) || (strstr(devpath,
  56             "/enclosure@"))) {
  57                 (void) snprintf(fullpath, MAXPATHLEN, "%s%s%s", DEVICES_DIR,
  58                     devpath, ":0");
  59                 /* reset errno to 0 */
  60                 errno = 0;
  61                 if ((fd = open(fullpath, O_RDONLY)) == -1) {
  62                         /*
  63                          * :0 minor doesn't exist. assume bound to sgen driver
  64                          * and :ses minor exist.
  65                          */
  66                         if (errno == ENOENT) {
  67                                 (void) strcpy(minor, ":ses");
  68                         }
  69                 } else {
  70                         (void) strcpy(minor, ":0");
  71                         (void) close(fd);
  72                 }
  73         } else {
  74                 log(LOG_DEBUG, ROUTINE, "Unrecognized target (%s)",
  75                     devpath);
  76                 minor[0] = '\0';
  77         }
  78 
  79 }
  80 
  81 /*
  82  * Free the attached port allocation.
  83  */
  84 static void
  85 free_attached_port(struct sun_sas_port *port_ptr)
  86 {
  87         struct sun_sas_port     *tgt_port, *last_tgt_port;
  88         struct ScsiEntryList    *scsi_info = NULL, *last_scsi_info = NULL;
  89 
  90         tgt_port = port_ptr->first_attached_port;
  91         while (tgt_port != NULL) {
  92                 /* Free target mapping data list first. */
  93                 scsi_info = tgt_port->scsiInfo;
  94                 while (scsi_info != NULL) {
  95                         last_scsi_info = scsi_info;
  96                         scsi_info = scsi_info->next;
  97                         free(last_scsi_info);
  98                 }
  99                 last_tgt_port = tgt_port;
 100                 tgt_port = tgt_port->next;
 101                 free(last_tgt_port->port_attributes.\
 102                     PortSpecificAttribute.SASPort);
 103                 free(last_tgt_port);
 104         }
 105 
 106         port_ptr->first_attached_port = NULL;
 107         port_ptr->port_attributes.PortSpecificAttribute.\
 108             SASPort->NumberofDiscoveredPorts = 0;
 109 }
 110 
 111 /*
 112  * Fill domainPortWWN.
 113  * should be called after completing discovered port discovery.
 114  */
 115 void
 116 fillDomainPortWWN(struct sun_sas_port *port_ptr)
 117 {
 118         const char    ROUTINE[] = "fillDomainPortWWN";
 119         struct sun_sas_port *disco_port_ptr;
 120         struct phy_info *phy_ptr;
 121         uint64_t    domainPort = 0;
 122         struct ScsiEntryList        *mapping_ptr;
 123 
 124         for (disco_port_ptr = port_ptr->first_attached_port;
 125             disco_port_ptr != NULL; disco_port_ptr = disco_port_ptr->next) {
 126                 if (disco_port_ptr->port_attributes.PortType ==
 127                     HBA_PORTTYPE_SASEXPANDER &&
 128                     wwnConversion(disco_port_ptr->port_attributes.
 129                     PortSpecificAttribute.SASPort->
 130                     AttachedSASAddress.wwn) ==
 131                     wwnConversion(port_ptr->port_attributes.
 132                     PortSpecificAttribute.SASPort->
 133                     LocalSASAddress.wwn)) {
 134                         (void) memcpy(&domainPort,
 135                             disco_port_ptr->port_attributes.
 136                             PortSpecificAttribute.
 137                             SASPort->LocalSASAddress.wwn, 8);
 138                         break;
 139                 }
 140         }
 141 
 142         if (domainPort == 0) {
 143                 if (port_ptr->first_attached_port) {
 144                         /*
 145                          * there is no expander device attached on an HBA port
 146                          * domainPortWWN should not stay to 0 since multiple
 147                          * hba ports can have the same LocalSASAddres within
 148                          * the same HBA.
 149                          * Set the SAS address of direct attached target.
 150                          */
 151                         if (wwnConversion(port_ptr->port_attributes.
 152                             PortSpecificAttribute.SASPort->
 153                             LocalSASAddress.wwn) ==
 154                             wwnConversion(port_ptr->first_attached_port->
 155                             port_attributes.PortSpecificAttribute.
 156                             SASPort->AttachedSASAddress.wwn)) {
 157                                 (void) memcpy(&domainPort,
 158                                     port_ptr->first_attached_port->
 159                                     port_attributes.PortSpecificAttribute.
 160                                     SASPort->LocalSASAddress.wwn, 8);
 161                         } else {
 162                                 /*
 163                                  * SAS address is not upstream connected.
 164                                  * domainPortWWN stays as 0.
 165                                  */
 166                                 log(LOG_DEBUG, ROUTINE,
 167                                     "DomainPortWWN is not set. "
 168                                     "Device(s) are visible on the HBA port "
 169                                     "but there is no expander or directly "
 170                                     "attached port with matching upsteam "
 171                                     "attached SAS address for "
 172                                     "HBA port (Local SAS Address: %016llx).",
 173                                     wwnConversion(port_ptr->port_attributes.
 174                                     PortSpecificAttribute.
 175                                     SASPort->LocalSASAddress.wwn));
 176                                 return;
 177                         }
 178                 } else {
 179                         /*
 180                          * There existss an iport without properly configured
 181                          * child smp ndoes or  child node or pathinfo.
 182                          * domainPortWWN stays as 0.
 183                          */
 184                         log(LOG_DEBUG, ROUTINE,
 185                             "DomainPortWWN is not set.  No properly "
 186                             "configured smp or directly attached port "
 187                             "found on HBA port(Local SAS Address: %016llx).",
 188                             wwnConversion(port_ptr->port_attributes.
 189                             PortSpecificAttribute.
 190                             SASPort->LocalSASAddress.wwn));
 191                         return;
 192                 }
 193         }
 194 
 195         /* fill up phy info */
 196         for (phy_ptr = port_ptr->first_phy; phy_ptr != NULL;
 197             phy_ptr = phy_ptr->next) {
 198                 (void) memcpy(phy_ptr->phy.domainPortWWN.wwn, &domainPort, 8);
 199         }
 200 
 201         /* fill up target mapping */
 202         for (disco_port_ptr = port_ptr->first_attached_port;
 203             disco_port_ptr != NULL; disco_port_ptr = disco_port_ptr->next) {
 204                 for (mapping_ptr = disco_port_ptr->scsiInfo;
 205                     mapping_ptr != NULL;
 206                     mapping_ptr = mapping_ptr->next) {
 207                         (void) memcpy(mapping_ptr->entry.PortLun.
 208                             domainPortWWN.wwn, &domainPort, 8);
 209                 }
 210         }
 211 }
 212 
 213 /*
 214  * Finds attached device(target) from devinfo node.
 215  */
 216 static HBA_STATUS
 217 get_attached_devices_info(di_node_t node, struct sun_sas_port *port_ptr)
 218 {
 219         const char                  ROUTINE[] = "get_attached_devices_info";
 220         char                        *propStringData = NULL;
 221         int                         *propIntData = NULL;
 222         int64_t                     *propInt64Data = NULL;
 223         scsi_lun_t                  samLun;
 224         ddi_devid_t                 devid;
 225         char                        *guidStr;
 226         char                        *unit_address;
 227         char                        *charptr;
 228         char                        *devpath, link[MAXNAMELEN];
 229         char                        fullpath[MAXPATHLEN+1];
 230         char                        minorname[MAXNAMELEN+1];
 231         struct ScsiEntryList        *mapping_ptr;
 232         HBA_WWN                     SASAddress, AttachedSASAddress;
 233         struct sun_sas_port         *disco_port_ptr;
 234         uint_t                      state = 0;
 235         int                         portfound, rval, size;
 236         int                         port_state = HBA_PORTSTATE_ONLINE;
 237         uint64_t                    tmpAddr;
 238 
 239         if (port_ptr == NULL) {
 240                 log(LOG_DEBUG, ROUTINE, "NULL port_ptr argument");
 241                 return (HBA_STATUS_ERROR);
 242         }
 243 
 244         if ((devpath = di_devfs_path(node)) == NULL) {
 245                 log(LOG_DEBUG, ROUTINE,
 246                     "Device in device tree has no path. Skipping.");
 247                 return (HBA_STATUS_ERROR);
 248         }
 249 
 250         if ((di_instance(node) == -1) || di_retired(node)) {
 251                 log(LOG_DEBUG, ROUTINE,
 252                     "dev node (%s) returned instance of -1 or is retired. "
 253                     " Skipping.", devpath);
 254                 di_devfs_path_free(devpath);
 255                 return (HBA_STATUS_OK);
 256         }
 257         state = di_state(node);
 258         /* when node is not attached and online, set the state to offline. */
 259         if (((state & DI_DRIVER_DETACHED) == DI_DRIVER_DETACHED) ||
 260             ((state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE)) {
 261                 log(LOG_DEBUG, ROUTINE,
 262                     "dev node (%s) is either OFFLINE or DETACHED",
 263                     devpath);
 264                 port_state = HBA_PORTSTATE_OFFLINE;
 265         }
 266 
 267         /* add the "/devices" in the begining at the end */
 268         (void) snprintf(fullpath, sizeof (fullpath), "%s%s",
 269             DEVICES_DIR, devpath);
 270 
 271         (void) memset(&SASAddress, 0, sizeof (SASAddress));
 272         if ((unit_address = di_bus_addr(node)) != NULL) {
 273                 if ((charptr = strchr(unit_address, ',')) != NULL) {
 274                         *charptr = '\0';
 275                 }
 276                 for (charptr = unit_address; *charptr != '\0'; charptr++) {
 277                         if (isxdigit(*charptr)) {
 278                                 break;
 279                         }
 280                 }
 281                 if (*charptr != '\0') {
 282                         tmpAddr = htonll(strtoll(charptr, NULL, 16));
 283                         (void) memcpy(&SASAddress.wwn[0], &tmpAddr, 8);
 284                 } else {
 285                         log(LOG_DEBUG, ROUTINE,
 286                             "No proper target port info on unit address of %s",
 287                             fullpath);
 288                         di_devfs_path_free(devpath);
 289                         return (HBA_STATUS_ERROR);
 290                 }
 291         } else {
 292                 log(LOG_DEBUG, ROUTINE,
 293                     "Fail to get unit address of %s.",
 294                     fullpath);
 295                 di_devfs_path_free(devpath);
 296                 return (HBA_STATUS_ERROR);
 297         }
 298 
 299         (void) memset(&AttachedSASAddress, 0, sizeof (AttachedSASAddress));
 300         if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "attached-port",
 301             &propStringData) != -1) {
 302                 for (charptr = propStringData; *charptr != '\0'; charptr++) {
 303                         if (isxdigit(*charptr)) {
 304                                 break;
 305                         }
 306                 }
 307                 if (*charptr != '\0') {
 308                         tmpAddr = htonll(strtoll(charptr, NULL, 16));
 309                         (void) memcpy(AttachedSASAddress.wwn, &tmpAddr, 8);
 310                         /* check the attached address of hba port. */
 311                         if (memcmp(port_ptr->port_attributes.
 312                             PortSpecificAttribute.SASPort->LocalSASAddress.wwn,
 313                             &tmpAddr, 8) == 0) {
 314                                 /*
 315                                  * When attached-port is set from iport
 316                                  * attached-port prop, we do the cross check
 317                                  * with device's own SAS address.
 318                                  *
 319                                  * If not set, we store device's own SAS
 320                                  * address to iport attached SAS address.
 321                                  */
 322                                 if (wwnConversion(port_ptr->port_attributes.
 323                                     PortSpecificAttribute.SASPort->
 324                                     AttachedSASAddress.wwn)) {
 325                                         /* verify the Attaached SAS Addr. */
 326                                         if (memcmp(port_ptr->port_attributes.
 327                                             PortSpecificAttribute.SASPort->
 328                                             AttachedSASAddress.wwn,
 329                                             SASAddress.wwn, 8) != 0) {
 330                                 /* indentation move begin. */
 331                                 log(LOG_DEBUG, ROUTINE,
 332                                     "iport attached-port(%016llx) do not"
 333                                     " match with level 1 Local"
 334                                     " SAS address(%016llx).",
 335                                     wwnConversion(port_ptr->port_attributes.
 336                                     PortSpecificAttribute.
 337                                     SASPort->AttachedSASAddress.wwn),
 338                                     wwnConversion(SASAddress.wwn));
 339                                 di_devfs_path_free(devpath);
 340                                 free_attached_port(port_ptr);
 341                                 return (HBA_STATUS_ERROR);
 342                                 /* indentation move ends. */
 343                                         }
 344                                 } else {
 345                                         (void) memcpy(port_ptr->port_attributes.
 346                                             PortSpecificAttribute.
 347                                             SASPort->AttachedSASAddress.wwn,
 348                                             &SASAddress.wwn[0], 8);
 349                                 }
 350                         }
 351                 } else {
 352                         log(LOG_DEBUG, ROUTINE,
 353                             "No proper attached SAS address value on device %s",
 354                             fullpath);
 355                         di_devfs_path_free(devpath);
 356                         free_attached_port(port_ptr);
 357                         return (HBA_STATUS_ERROR);
 358                 }
 359         } else {
 360                 log(LOG_DEBUG, ROUTINE,
 361                     "Property AttachedSASAddress not found for device \"%s\"",
 362                     fullpath);
 363                 di_devfs_path_free(devpath);
 364                 free_attached_port(port_ptr);
 365                 return (HBA_STATUS_ERROR);
 366         }
 367 
 368         /*
 369          * walk the disco list to make sure that there isn't a matching
 370          * port and node wwn or a matching device path
 371          */
 372         portfound = 0;
 373         for (disco_port_ptr = port_ptr->first_attached_port;
 374             disco_port_ptr != NULL;
 375             disco_port_ptr = disco_port_ptr->next) {
 376                 if ((disco_port_ptr->port_attributes.PortState !=
 377                     HBA_PORTSTATE_ERROR) && (memcmp(disco_port_ptr->
 378                     port_attributes.PortSpecificAttribute.
 379                     SASPort->LocalSASAddress.wwn, SASAddress.wwn, 8) == 0)) {
 380                         /*
 381                          * found matching disco_port
 382                          * look for matching device path
 383                          */
 384                         portfound = 1;
 385                         for (mapping_ptr = disco_port_ptr->scsiInfo;
 386                             mapping_ptr != NULL;
 387                             mapping_ptr = mapping_ptr->next) {
 388                                 if (strstr(mapping_ptr-> entry.ScsiId.
 389                                     OSDeviceName, devpath) != 0) {
 390                                         log(LOG_DEBUG, ROUTINE,
 391                                             "Found an already discovered "
 392                                             "device %s.", fullpath);
 393                                         di_devfs_path_free(devpath);
 394                                         return (HBA_STATUS_OK);
 395                                 }
 396                         }
 397                         if (portfound == 1) {
 398                                 break;
 399                         }
 400                 }
 401         }
 402 
 403         if (portfound == 0) {
 404                 /*
 405                  * there are no matching SAS address.
 406                  * this must be a new device
 407                  */
 408                 if ((disco_port_ptr = (struct sun_sas_port *)calloc(1,
 409                     sizeof (struct sun_sas_port))) == NULL)  {
 410                         OUT_OF_MEMORY(ROUTINE);
 411                         di_devfs_path_free(devpath);
 412                         free_attached_port(port_ptr);
 413                         return (HBA_STATUS_ERROR);
 414                 }
 415 
 416                 if ((disco_port_ptr->port_attributes.PortSpecificAttribute.\
 417                     SASPort = (struct SMHBA_SAS_Port *)calloc(1,
 418                     sizeof (struct SMHBA_SAS_Port))) == NULL) {
 419                         OUT_OF_MEMORY("add_hba_port_info");
 420                         di_devfs_path_free(devpath);
 421                         free_attached_port(port_ptr);
 422                         return (HBA_STATUS_ERROR);
 423                 }
 424 
 425                 (void) memcpy(disco_port_ptr->port_attributes.
 426                     PortSpecificAttribute.SASPort->LocalSASAddress.wwn,
 427                     SASAddress.wwn, 8);
 428                 (void) memcpy(disco_port_ptr->port_attributes.
 429                     PortSpecificAttribute.SASPort->AttachedSASAddress.wwn,
 430                     AttachedSASAddress.wwn, 8);
 431 
 432                 /* Default to unknown until we figure out otherwise */
 433                 rval = di_prop_lookup_strings(DDI_DEV_T_ANY, node,
 434                     "variant", &propStringData);
 435                 if (rval < 0) {
 436                         /* check if it is SMP target */
 437                         charptr = di_driver_name(node);
 438                         if (charptr != NULL && (strncmp(charptr, "smp",
 439                             strlen(charptr)) == 0)) {
 440                                 disco_port_ptr->port_attributes.PortType =
 441                                     HBA_PORTTYPE_SASEXPANDER;
 442                                 disco_port_ptr->port_attributes.
 443                                     PortSpecificAttribute.
 444                                     SASPort->PortProtocol =
 445                                     HBA_SASPORTPROTOCOL_SMP;
 446                                 if (lookupSMPLink(devpath, (char *)link) ==
 447                                     HBA_STATUS_OK) {
 448                 /* indentation changed here. */
 449                 (void) strlcpy(disco_port_ptr->port_attributes.
 450                     OSDeviceName, link,
 451                     sizeof (disco_port_ptr->port_attributes.OSDeviceName));
 452                 /* indentation change ends here. */
 453                                 } else {
 454                 /* indentation changed here. */
 455                 get_minor(devpath, minorname);
 456                 (void) snprintf(fullpath, sizeof (fullpath), "%s%s%s",
 457                     DEVICES_DIR, devpath, minorname);
 458                 (void) strlcpy(disco_port_ptr->port_attributes.
 459                     OSDeviceName, fullpath,
 460                     sizeof (disco_port_ptr->port_attributes.OSDeviceName));
 461                 /* indentation change ends here. */
 462                                 }
 463                         } else {
 464                                 disco_port_ptr->port_attributes.PortType =
 465                                     HBA_PORTTYPE_SASDEVICE;
 466                                 disco_port_ptr->port_attributes.\
 467                                     PortSpecificAttribute.\
 468                                     SASPort->PortProtocol =
 469                                     HBA_SASPORTPROTOCOL_SSP;
 470                         }
 471                 } else {
 472                         if ((strcmp(propStringData, "sata") == 0) ||
 473                             (strcmp(propStringData, "atapi") == 0)) {
 474                                 disco_port_ptr->port_attributes.PortType =
 475                                     HBA_PORTTYPE_SATADEVICE;
 476                                 disco_port_ptr->port_attributes.\
 477                                     PortSpecificAttribute.SASPort->PortProtocol
 478                                     = HBA_SASPORTPROTOCOL_SATA;
 479                         } else {
 480                                 log(LOG_DEBUG, ROUTINE,
 481                                     "Unexpected variant prop value %s found on",
 482                                     " device %s", propStringData, fullpath);
 483                                 /*
 484                                  * Port type will be 0
 485                                  * which is not valid type.
 486                                  */
 487                         }
 488                 }
 489 
 490                 /* SMP device was handled already */
 491                 if (disco_port_ptr->port_attributes.OSDeviceName[0] == '\0') {
 492                 /* indentation change due to ctysle check on sizeof. */
 493                 size = sizeof (disco_port_ptr->port_attributes.OSDeviceName);
 494                         (void) strlcpy(disco_port_ptr->port_attributes.
 495                             OSDeviceName, fullpath, size);
 496                 }
 497 
 498                 /* add new discovered port into the list */
 499 
 500                 if (port_ptr->first_attached_port == NULL) {
 501                         port_ptr->first_attached_port = disco_port_ptr;
 502                         disco_port_ptr->index = 0;
 503                         port_ptr->port_attributes.PortSpecificAttribute.\
 504                             SASPort->NumberofDiscoveredPorts = 1;
 505                 } else {
 506                         disco_port_ptr->next = port_ptr->first_attached_port;
 507                         port_ptr->first_attached_port = disco_port_ptr;
 508                         disco_port_ptr->index = port_ptr->port_attributes.\
 509                             PortSpecificAttribute.\
 510                             SASPort->NumberofDiscoveredPorts;
 511                         port_ptr->port_attributes.PortSpecificAttribute.\
 512                             SASPort->NumberofDiscoveredPorts++;
 513                 }
 514                 disco_port_ptr->port_attributes.PortState = port_state;
 515         }
 516 
 517         if (disco_port_ptr->port_attributes.PortType ==
 518             HBA_PORTTYPE_SASEXPANDER) {
 519             /* No mapping data for expander device.  return ok here. */
 520                 di_devfs_path_free(devpath);
 521                 return (HBA_STATUS_OK);
 522         }
 523 
 524         if ((mapping_ptr = (struct ScsiEntryList *)calloc
 525                     (1, sizeof (struct ScsiEntryList))) == NULL) {
 526                 OUT_OF_MEMORY(ROUTINE);
 527                 di_devfs_path_free(devpath);
 528                 free_attached_port(port_ptr);
 529                 return (HBA_STATUS_ERROR);
 530         }
 531 
 532         if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "lun",
 533             &propIntData) != -1) {
 534                 mapping_ptr->entry.ScsiId.ScsiOSLun = *propIntData;
 535         } else {
 536                 if ((charptr = strchr(unit_address, ',')) != NULL) {
 537                         charptr++;
 538                         mapping_ptr->entry.ScsiId.ScsiOSLun =
 539                             strtoull(charptr, NULL, 10);
 540                 } else {
 541                         log(LOG_DEBUG, ROUTINE,
 542                             "Failed to get LUN from the unit address of device "
 543                             " %s.", fullpath);
 544                         di_devfs_path_free(devpath);
 545                         free_attached_port(port_ptr);
 546                         return (HBA_STATUS_ERROR);
 547                 }
 548         }
 549 
 550         /* get TargetLun(SAM-LUN). */
 551         if (di_prop_lookup_int64(DDI_DEV_T_ANY, node, "lun64",
 552             &propInt64Data) != -1) {
 553                 samLun = scsi_lun64_to_lun(*propInt64Data);
 554                 (void) memcpy(&mapping_ptr->entry.PortLun.TargetLun,
 555                     &samLun, 8);
 556         } else {
 557                 log(LOG_DEBUG, "get_attached_devices_info",
 558                     "No lun64 prop found on device %s.", fullpath);
 559                 di_devfs_path_free(devpath);
 560                 free_attached_port(port_ptr);
 561                 return (HBA_STATUS_ERROR);
 562         }
 563 
 564         if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
 565             "target", &propIntData) != -1) {
 566                 mapping_ptr->entry.ScsiId.ScsiTargetNumber = *propIntData;
 567         } else {
 568                 mapping_ptr->entry.ScsiId.ScsiTargetNumber = di_instance(node);
 569         }
 570 
 571         /* get ScsiBusNumber */
 572         mapping_ptr->entry.ScsiId.ScsiBusNumber = port_ptr->cntlNumber;
 573 
 574         (void) memcpy(mapping_ptr->entry.PortLun.PortWWN.wwn,
 575             SASAddress.wwn, 8);
 576 
 577         /* Store the devices path for now.  We'll convert to /dev later */
 578         get_minor(devpath, minorname);
 579         (void) snprintf(mapping_ptr->entry.ScsiId.OSDeviceName,
 580             sizeof (mapping_ptr->entry.ScsiId.OSDeviceName),
 581             "%s%s%s", DEVICES_DIR, devpath, minorname);
 582 
 583         /* reset errno to 0 */
 584         errno = 0;
 585         if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "devid",
 586             &propStringData) != -1) {
 587                 if (devid_str_decode(propStringData, &devid, NULL) != -1) {
 588                         guidStr = devid_to_guid(devid);
 589                         if (guidStr != NULL) {
 590                                 (void) strlcpy(mapping_ptr->entry.LUID.buffer,
 591                                     guidStr,
 592                                     sizeof (mapping_ptr->entry.LUID.buffer));
 593                                 devid_free_guid(guidStr);
 594                         } else {
 595                                 /*
 596                                  * Note:
 597                                  * if logical unit associated page 83 id
 598                                  * descriptor is not avaialble for the device
 599                                  * devid_to_guid returns NULL with errno 0.
 600                                  */
 601                                 log(LOG_DEBUG, ROUTINE,
 602                                     "failed to get devid guid on (%s) : %s",
 603                                     devpath, strerror(errno));
 604                         }
 605 
 606                         devid_free(devid);
 607                 } else {
 608                         /*
 609                          * device may not support proper page 83 id descriptor.
 610                          * leave LUID attribute to NULL and continue.
 611                          */
 612                         log(LOG_DEBUG, ROUTINE,
 613                             "failed to decode devid prop on (%s) : %s",
 614                             devpath, strerror(errno));
 615                 }
 616         } else {
 617                 /* leave LUID attribute to NULL and continue. */
 618                 log(LOG_DEBUG, ROUTINE,
 619                     "failed to get devid prop on (%s) : %s",
 620                     devpath, strerror(errno));
 621         }
 622 
 623         if (disco_port_ptr->scsiInfo == NULL) {
 624                 disco_port_ptr->scsiInfo = mapping_ptr;
 625         } else {
 626                 mapping_ptr->next = disco_port_ptr->scsiInfo;
 627                 disco_port_ptr->scsiInfo = mapping_ptr;
 628         }
 629 
 630         di_devfs_path_free(devpath);
 631 
 632         return (HBA_STATUS_OK);
 633 }
 634 
 635 /*
 636  * Finds attached device(target) from pathinfo node.
 637  */
 638 static HBA_STATUS
 639 get_attached_paths_info(di_path_t path, struct sun_sas_port *port_ptr)
 640 {
 641         char                        ROUTINE[] = "get_attached_paths_info";
 642         char                        *propStringData = NULL;
 643         int                         *propIntData = NULL;
 644         int64_t                     *propInt64Data = NULL;
 645         scsi_lun_t                  samLun;
 646         ddi_devid_t                 devid;
 647         char                        *guidStr;
 648         char                        *unit_address;
 649         char                        *charptr;
 650         char                        *clientdevpath = NULL;
 651         char                        *pathdevpath = NULL;
 652         char                        fullpath[MAXPATHLEN+1];
 653         char                        minorname[MAXNAMELEN+1];
 654         struct ScsiEntryList        *mapping_ptr;
 655         HBA_WWN                     SASAddress, AttachedSASAddress;
 656         struct sun_sas_port         *disco_port_ptr;
 657         di_path_state_t             state = 0;
 658         di_node_t                   clientnode;
 659         int                         portfound, size;
 660         int                         port_state = HBA_PORTSTATE_ONLINE;
 661         uint64_t                    tmpAddr;
 662 
 663         if (port_ptr == NULL) {
 664                 log(LOG_DEBUG, ROUTINE, "NULL port_ptr argument");
 665                 return (HBA_STATUS_ERROR);
 666         }
 667 
 668         /* if not null, free before return. */
 669         pathdevpath = di_path_devfs_path(path);
 670 
 671         state = di_path_state(path);
 672         /* when node is not attached and online, set the state to offline. */
 673         if ((state == DI_PATH_STATE_OFFLINE) ||
 674             (state == DI_PATH_STATE_FAULT)) {
 675                 log(LOG_DEBUG, ROUTINE,
 676                     "path node (%s) is either OFFLINE or FAULT state",
 677                     pathdevpath ?  pathdevpath : "(missing device path)");
 678                 port_state = HBA_PORTSTATE_OFFLINE;
 679         }
 680 
 681         if (clientnode = di_path_client_node(path)) {
 682                 if (di_retired(clientnode)) {
 683                         log(LOG_DEBUG, ROUTINE,
 684                             "client node of path (%s) is retired. Skipping.",
 685                             pathdevpath ?  pathdevpath :
 686                             "(missing device path)");
 687                         if (pathdevpath) di_devfs_path_free(pathdevpath);
 688                         return (HBA_STATUS_OK);
 689                 }
 690                 if ((clientdevpath = di_devfs_path(clientnode)) == NULL) {
 691                         log(LOG_DEBUG, ROUTINE,
 692                             "Client device of path (%s) has no path. Skipping.",
 693                             pathdevpath ?  pathdevpath :
 694                             "(missing device path)");
 695                         if (pathdevpath) di_devfs_path_free(pathdevpath);
 696                         return (HBA_STATUS_ERROR);
 697                 }
 698         } else {
 699                 log(LOG_DEBUG, ROUTINE,
 700                     "Failed to get client device from a path (%s).",
 701                     pathdevpath ?  pathdevpath :
 702                     "(missing device path)");
 703                 if (pathdevpath) di_devfs_path_free(pathdevpath);
 704                 return (HBA_STATUS_ERROR);
 705         }
 706 
 707         /* add the "/devices" in the begining and the :devctl at the end */
 708         (void) snprintf(fullpath, sizeof (fullpath), "%s%s", DEVICES_DIR,
 709             clientdevpath);
 710 
 711         (void) memset(&SASAddress, 0, sizeof (SASAddress));
 712         if ((unit_address = di_path_bus_addr(path)) != NULL) {
 713                 if ((charptr = strchr(unit_address, ',')) != NULL) {
 714                         *charptr = '\0';
 715                 }
 716                 for (charptr = unit_address; *charptr != '\0'; charptr++) {
 717                         if (isxdigit(*charptr)) {
 718                                 break;
 719                         }
 720                 }
 721                 if (charptr != '\0') {
 722                         tmpAddr = htonll(strtoll(charptr, NULL, 16));
 723                         (void) memcpy(&SASAddress.wwn[0], &tmpAddr, 8);
 724                 } else {
 725                         log(LOG_DEBUG, ROUTINE,
 726                             "No proper target port info on unit address of "
 727                             "path (%s).", pathdevpath ?  pathdevpath :
 728                             "(missing device path)");
 729                         if (pathdevpath) di_devfs_path_free(pathdevpath);
 730                         di_devfs_path_free(clientdevpath);
 731                         return (HBA_STATUS_ERROR);
 732                 }
 733         } else {
 734                 log(LOG_DEBUG, ROUTINE, "Fail to get unit address of path(%s).",
 735                     "path (%s).", pathdevpath ?  pathdevpath :
 736                     "(missing device path)");
 737                 if (pathdevpath) di_devfs_path_free(pathdevpath);
 738                 di_devfs_path_free(clientdevpath);
 739                 return (HBA_STATUS_ERROR);
 740         }
 741 
 742         (void) memset(&AttachedSASAddress, 0, sizeof (AttachedSASAddress));
 743         if (di_path_prop_lookup_strings(path, "attached-port",
 744             &propStringData) != -1) {
 745                 for (charptr = propStringData; *charptr != '\0'; charptr++) {
 746                         if (isxdigit(*charptr)) {
 747                                 break;
 748                         }
 749                 }
 750                 if (*charptr != '\0') {
 751                         tmpAddr = htonll(strtoll(charptr, NULL, 16));
 752                         (void) memcpy(AttachedSASAddress.wwn, &tmpAddr, 8);
 753                         /*  check the attached address of hba port. */
 754                         if (memcmp(port_ptr->port_attributes.
 755                             PortSpecificAttribute.SASPort->
 756                             LocalSASAddress.wwn, &tmpAddr, 8) == 0) {
 757                                 if (wwnConversion(port_ptr->port_attributes.
 758                                     PortSpecificAttribute.SASPort->
 759                                     AttachedSASAddress.wwn)) {
 760                                         /* verify the attaached SAS Addr. */
 761                                         if (memcmp(port_ptr->port_attributes.
 762                                             PortSpecificAttribute.SASPort->
 763                                             AttachedSASAddress.wwn,
 764                                             SASAddress.wwn, 8) != 0) {
 765                                 /* indentation move begin. */
 766                                 log(LOG_DEBUG, ROUTINE,
 767                                     "iport attached-port(%016llx) do not"
 768                                     " match with level 1 Local"
 769                                     " SAS address(%016llx).",
 770                                     wwnConversion(port_ptr->port_attributes.
 771                                     PortSpecificAttribute.
 772                                     SASPort->AttachedSASAddress.wwn),
 773                                     wwnConversion(SASAddress.wwn));
 774                                 if (pathdevpath)
 775                                         di_devfs_path_free(pathdevpath);
 776                                 di_devfs_path_free(clientdevpath);
 777                                 free_attached_port(port_ptr);
 778                                 return (HBA_STATUS_ERROR);
 779                                 /* indentation move ends. */
 780                                         }
 781                                 } else {
 782                                         /* store the Attaached SAS Addr. */
 783                                         (void) memcpy(port_ptr->port_attributes.
 784                                             PortSpecificAttribute.
 785                                             SASPort->AttachedSASAddress.wwn,
 786                                             &SASAddress.wwn[0], 8);
 787                                 }
 788                         }
 789                 } else {
 790                         log(LOG_DEBUG, ROUTINE,
 791                             "No proper attached SAS address value of path (%s)",
 792                             pathdevpath ?  pathdevpath :
 793                             "(missing device path)");
 794                         if (pathdevpath) di_devfs_path_free(pathdevpath);
 795                         di_devfs_path_free(clientdevpath);
 796                         free_attached_port(port_ptr);
 797                         return (HBA_STATUS_ERROR);
 798                 }
 799         } else {
 800                 log(LOG_DEBUG, ROUTINE,
 801                     "Property attached-port not found for path (%s)",
 802                     pathdevpath ?  pathdevpath :
 803                     "(missing device path)");
 804                 if (pathdevpath) di_devfs_path_free(pathdevpath);
 805                 di_devfs_path_free(clientdevpath);
 806                 free_attached_port(port_ptr);
 807                 return (HBA_STATUS_ERROR);
 808         }
 809 
 810         /*
 811          * walk the disco list to make sure that there isn't a matching
 812          * port and node wwn or a matching device path
 813          */
 814         portfound = 0;
 815         for (disco_port_ptr = port_ptr->first_attached_port;
 816             disco_port_ptr != NULL;
 817             disco_port_ptr = disco_port_ptr->next) {
 818                 if ((disco_port_ptr->port_attributes.PortState !=
 819                     HBA_PORTSTATE_ERROR) &&
 820                     (memcmp(disco_port_ptr->port_attributes.
 821                     PortSpecificAttribute.SASPort->LocalSASAddress.wwn,
 822                     SASAddress.wwn, 8) == 0)) {
 823                         /*
 824                          * found matching disco_port
 825                          * look for matching device path
 826                          */
 827                         portfound = 1;
 828                         for (mapping_ptr = disco_port_ptr->scsiInfo;
 829                             mapping_ptr != NULL;
 830                             mapping_ptr = mapping_ptr->next) {
 831                                 if (strstr(mapping_ptr-> entry.ScsiId.
 832                                     OSDeviceName, clientdevpath) != 0) {
 833                                         log(LOG_DEBUG, ROUTINE,
 834                                             "Found an already discovered "
 835                                             "device %s.", clientdevpath);
 836                                         if (pathdevpath)
 837                                                 di_devfs_path_free(pathdevpath);
 838                                         di_devfs_path_free(clientdevpath);
 839                                         return (HBA_STATUS_OK);
 840                                 }
 841                         }
 842                         if (portfound == 1) {
 843                                 break;
 844                         }
 845                 }
 846         }
 847 
 848         if (portfound == 0) {
 849                 /*
 850                  * there are no matching SAS address.
 851                  * this must be a new device
 852                  */
 853                 if ((disco_port_ptr = (struct sun_sas_port *)calloc(1,
 854                                     sizeof (struct sun_sas_port))) == NULL)  {
 855                         OUT_OF_MEMORY(ROUTINE);
 856                         if (pathdevpath) di_devfs_path_free(pathdevpath);
 857                         di_devfs_path_free(clientdevpath);
 858                         free_attached_port(port_ptr);
 859                         return (HBA_STATUS_ERROR);
 860                 }
 861 
 862                 if ((disco_port_ptr->port_attributes.PortSpecificAttribute.\
 863                     SASPort = (struct SMHBA_SAS_Port *)calloc(1,
 864                     sizeof (struct SMHBA_SAS_Port))) == NULL) {
 865                         OUT_OF_MEMORY("add_hba_port_info");
 866                         if (pathdevpath) di_devfs_path_free(pathdevpath);
 867                         di_devfs_path_free(clientdevpath);
 868                         free_attached_port(port_ptr);
 869                         return (HBA_STATUS_ERROR);
 870                 }
 871 
 872                 (void) memcpy(disco_port_ptr->port_attributes.
 873                     PortSpecificAttribute.
 874                     SASPort->LocalSASAddress.wwn, SASAddress.wwn, 8);
 875                 (void) memcpy(disco_port_ptr->port_attributes.
 876                     PortSpecificAttribute.
 877                     SASPort->AttachedSASAddress.wwn, AttachedSASAddress.wwn, 8);
 878 
 879                 /* Default to unknown until we figure out otherwise */
 880                 if (di_path_prop_lookup_strings(path, "variant",
 881                     &propStringData) != -1) {
 882                         if ((strcmp(propStringData, "sata") == 0) ||
 883                             (strcmp(propStringData, "atapi") == 0)) {
 884                                 disco_port_ptr->port_attributes.PortType =
 885                                     HBA_PORTTYPE_SATADEVICE;
 886                                 disco_port_ptr->port_attributes.\
 887                                     PortSpecificAttribute.SASPort->PortProtocol
 888                                     = HBA_SASPORTPROTOCOL_SATA;
 889                         } else {
 890                                 log(LOG_DEBUG, ROUTINE,
 891                                     "Unexpected variant prop value %s found on",
 892                                     " path (%s)", propStringData,
 893                                     pathdevpath ?  pathdevpath :
 894                                     "(missing device path)");
 895                                 /*
 896                                  * Port type will be 0
 897                                  * which is not valid type.
 898                                  */
 899                         }
 900                 } else {
 901                         disco_port_ptr->port_attributes.PortType =
 902                             HBA_PORTTYPE_SASDEVICE;
 903                         disco_port_ptr->port_attributes.PortSpecificAttribute.\
 904                             SASPort->PortProtocol = HBA_SASPORTPROTOCOL_SSP;
 905                 }
 906 
 907                 if (disco_port_ptr->port_attributes.OSDeviceName[0] == '\0') {
 908                 /* indentation change due to ctysle check on sizeof. */
 909                 size = sizeof (disco_port_ptr->port_attributes.OSDeviceName);
 910                         if (pathdevpath != NULL) {
 911                                 (void) strlcpy(disco_port_ptr->port_attributes.
 912                                     OSDeviceName, pathdevpath, size);
 913                         }
 914                 }
 915 
 916                 /* add new discovered port into the list */
 917                 if (port_ptr->first_attached_port == NULL) {
 918                         port_ptr->first_attached_port = disco_port_ptr;
 919                         disco_port_ptr->index = 0;
 920                         port_ptr->port_attributes.PortSpecificAttribute.\
 921                             SASPort->NumberofDiscoveredPorts = 1;
 922                 } else {
 923                         disco_port_ptr->next = port_ptr->first_attached_port;
 924                         port_ptr->first_attached_port = disco_port_ptr;
 925                         disco_port_ptr->index = port_ptr->port_attributes.\
 926                             PortSpecificAttribute.\
 927                             SASPort->NumberofDiscoveredPorts;
 928                         port_ptr->port_attributes.PortSpecificAttribute.\
 929                             SASPort->NumberofDiscoveredPorts++;
 930                 }
 931                 disco_port_ptr->port_attributes.PortState = port_state;
 932         }
 933 
 934         if ((mapping_ptr = (struct ScsiEntryList *)calloc
 935                     (1, sizeof (struct ScsiEntryList))) == NULL) {
 936                 OUT_OF_MEMORY(ROUTINE);
 937                 if (pathdevpath) di_devfs_path_free(pathdevpath);
 938                 di_devfs_path_free(clientdevpath);
 939                 free_attached_port(port_ptr);
 940                 return (HBA_STATUS_ERROR);
 941         }
 942 
 943         if (di_path_prop_lookup_ints(path, "lun", &propIntData) != -1) {
 944                 mapping_ptr->entry.ScsiId.ScsiOSLun = *propIntData;
 945         } else {
 946                 if ((charptr = strchr(unit_address, ',')) != NULL) {
 947                         charptr++;
 948                         mapping_ptr->entry.ScsiId.ScsiOSLun =
 949                             strtoull(charptr, NULL, 10);
 950                 } else {
 951                         log(LOG_DEBUG, ROUTINE,
 952                             "Failed to get LUN from unit address of path(%s).",
 953                             pathdevpath ?  pathdevpath :
 954                             "(missing device path)");
 955                         if (pathdevpath) di_devfs_path_free(pathdevpath);
 956                         di_devfs_path_free(clientdevpath);
 957                         free_attached_port(port_ptr);
 958                         return (HBA_STATUS_ERROR);
 959                 }
 960         }
 961 
 962         /* Get TargetLun(SAM LUN). */
 963         if (di_path_prop_lookup_int64s(path, "lun64", &propInt64Data) != -1) {
 964                 samLun = scsi_lun64_to_lun(*propInt64Data);
 965                 (void) memcpy(&mapping_ptr->entry.PortLun.TargetLun,
 966                     &samLun, 8);
 967         } else {
 968                 log(LOG_DEBUG, ROUTINE, "No lun64 prop found on path (%s)",
 969                     pathdevpath ?  pathdevpath :
 970                     "(missing device path)");
 971                 if (pathdevpath) di_devfs_path_free(pathdevpath);
 972                 di_devfs_path_free(clientdevpath);
 973                 free_attached_port(port_ptr);
 974                 return (HBA_STATUS_ERROR);
 975         }
 976 
 977         if (di_path_prop_lookup_ints(path, "target", &propIntData) != -1) {
 978                 mapping_ptr->entry.ScsiId.ScsiTargetNumber = *propIntData;
 979         } else {
 980                 mapping_ptr->entry.ScsiId.ScsiTargetNumber =
 981                     di_path_instance(path);
 982         }
 983 
 984         /* get ScsiBusNumber */
 985         mapping_ptr->entry.ScsiId.ScsiBusNumber = port_ptr->cntlNumber;
 986 
 987         (void) memcpy(mapping_ptr->entry.PortLun.PortWWN.wwn,
 988             SASAddress.wwn, 8);
 989 
 990         /* Store the devices path for now.  We'll convert to /dev later */
 991         get_minor(clientdevpath, minorname);
 992         (void) snprintf(mapping_ptr->entry.ScsiId.OSDeviceName,
 993             sizeof (mapping_ptr->entry.ScsiId.OSDeviceName),
 994             "%s%s%s", DEVICES_DIR, clientdevpath, minorname);
 995 
 996         /* get luid. */
 997         errno = 0; /* reset errno to 0 */
 998         if (di_prop_lookup_strings(DDI_DEV_T_ANY, clientnode, "devid",
 999             &propStringData) != -1) {
1000                 if (devid_str_decode(propStringData, &devid, NULL) != -1) {
1001                         guidStr = devid_to_guid(devid);
1002                         if (guidStr != NULL) {
1003                                 (void) strlcpy(mapping_ptr->entry.LUID.buffer,
1004                                     guidStr,
1005                                     sizeof (mapping_ptr->entry.LUID.buffer));
1006                                 devid_free_guid(guidStr);
1007                         } else {
1008                                 /*
1009                                  * Note:
1010                                  * if logical unit associated page 83 id
1011                                  * descriptor is not avaialble for the device
1012                                  * devid_to_guid returns NULL with errno 0.
1013                                  */
1014                                 log(LOG_DEBUG, ROUTINE,
1015                                     "failed to get devid guid on (%s)",
1016                                     " associated with path(%s) : %s",
1017                                     clientdevpath,
1018                                     pathdevpath ?  pathdevpath :
1019                                     "(missing device path)",
1020                                     strerror(errno));
1021                         }
1022 
1023                         devid_free(devid);
1024                 } else {
1025                         /*
1026                          * device may not support proper page 83 id descriptor.
1027                          * leave LUID attribute to NULL and continue.
1028                          */
1029                         log(LOG_DEBUG, ROUTINE,
1030                             "failed to decode devid prop on (%s)",
1031                             " associated with path(%s) : %s",
1032                             clientdevpath,
1033                             pathdevpath ?  pathdevpath :
1034                             "(missing device path)",
1035                             strerror(errno));
1036                 }
1037         } else {
1038                 /* leave LUID attribute to NULL and continue. */
1039                 log(LOG_DEBUG, ROUTINE, "Failed to get devid on %s"
1040                     " associated with path(%s) : %s", clientdevpath,
1041                     pathdevpath ?  pathdevpath : "(missing device path)",
1042                     strerror(errno));
1043         }
1044 
1045         if (disco_port_ptr->scsiInfo == NULL) {
1046                 disco_port_ptr->scsiInfo = mapping_ptr;
1047         } else {
1048                 mapping_ptr->next = disco_port_ptr->scsiInfo;
1049                 disco_port_ptr->scsiInfo = mapping_ptr;
1050         }
1051 
1052         if (pathdevpath) di_devfs_path_free(pathdevpath);
1053         di_devfs_path_free(clientdevpath);
1054 
1055         return (HBA_STATUS_OK);
1056 }
1057 
1058 /*
1059  * walks the devinfo tree retrieving all hba information
1060  */
1061 extern HBA_STATUS
1062 devtree_attached_devices(di_node_t node, struct sun_sas_port *port_ptr)
1063 {
1064         const char              ROUTINE[] = "devtree_attached_devices";
1065         di_node_t               nodechild = DI_NODE_NIL;
1066         di_path_t               path = DI_PATH_NIL;
1067 
1068         /* child should be device */
1069         if ((nodechild = di_child_node(node)) == DI_NODE_NIL) {
1070                 log(LOG_DEBUG, ROUTINE,
1071                     "No devinfo child on the HBA port node.");
1072         }
1073 
1074         if ((path = di_path_phci_next_path(node, path)) ==
1075             DI_PATH_NIL) {
1076                 log(LOG_DEBUG, ROUTINE,
1077                     "No pathinfo node on the HBA port node.");
1078         }
1079 
1080         if ((nodechild == DI_NODE_NIL) && (path == DI_PATH_NIL)) {
1081                 return (HBA_STATUS_OK);
1082         }
1083 
1084         while (nodechild != DI_NODE_NIL) {
1085                 if (get_attached_devices_info(nodechild, port_ptr)
1086                     != HBA_STATUS_OK) {
1087                         break;
1088                 }
1089                 nodechild = di_sibling_node(nodechild);
1090         }
1091 
1092 
1093         while (path != DI_PATH_NIL) {
1094                 if (get_attached_paths_info(path, port_ptr)
1095                     != HBA_STATUS_OK) {
1096                         break;
1097                 }
1098                 path = di_path_phci_next_path(node, path);
1099         }
1100 
1101         return (HBA_STATUS_OK);
1102 }