1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <string.h> 30 #include <strings.h> 31 32 #include <scsi/libses.h> 33 #include <scsi/libses_plugin.h> 34 35 #include <scsi/plugins/ses/framework/ses2_impl.h> 36 37 static int 38 sun_loki_fix_bay(ses_plugin_t *sp, ses_node_t *np) 39 { 40 ses2_aes_descr_eip_impl_t *dep; 41 ses2_aes_descr_sas0_eip_impl_t *s0ep; 42 size_t len; 43 int nverr; 44 nvlist_t *props = ses_node_props(np); 45 46 /* 47 * The spec conveniently defines the bay number as part of the 48 * additional element status descriptor. However, the AES descriptor 49 * is technically only valid if the device is inserted. This is a 50 * problem for loki because the bay numbers don't match the element 51 * class index, so when a device is removed we have no way of knowing 52 * *which* bay is empty. Thankfully, loki defines this value even if 53 * the invalid bit is set, so we override this value, even for empty 54 * bays. 55 */ 56 if ((dep = ses_plugin_page_lookup(sp, ses_node_snapshot(np), 57 SES2_DIAGPAGE_ADDL_ELEM_STATUS, np, &len)) == NULL) 58 return (0); 59 60 if (dep->sadei_protocol_identifier != SPC4_PROTO_SAS || 61 !dep->sadei_eip || !dep->sadei_invalid) 62 return (0); 63 64 s0ep = (ses2_aes_descr_sas0_eip_impl_t *)dep->sadei_protocol_specific; 65 66 SES_NV_ADD(uint64, nverr, props, SES_PROP_BAY_NUMBER, 67 s0ep->sadsi_bay_number); 68 69 return (0); 70 } 71 72 static int 73 sun_loki_parse_node(ses_plugin_t *sp, ses_node_t *np) 74 { 75 ses_node_t *encp; 76 nvlist_t *props = ses_node_props(np); 77 nvlist_t *encprops; 78 uint8_t *stringin; 79 uint_t len; 80 nvlist_t *lid; 81 int nverr; 82 char serial[17]; 83 uint8_t fieldlen; 84 char *field; 85 uint64_t wwn; 86 uint64_t type, index; 87 int i; 88 89 if (ses_node_type(np) != SES_NODE_ENCLOSURE && 90 ses_node_type(np) != SES_NODE_ELEMENT) 91 return (0); 92 93 if (ses_node_type(np) == SES_NODE_ELEMENT) { 94 VERIFY(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, 95 &type) == 0); 96 97 if (type == SES_ET_ARRAY_DEVICE) 98 return (sun_loki_fix_bay(sp, np)); 99 100 if (type != SES_ET_COOLING && 101 type != SES_ET_POWER_SUPPLY) 102 return (0); 103 104 VERIFY(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX, 105 &index) == 0); 106 } 107 108 /* 109 * Find the containing enclosure node and extract the STRING IN 110 * information. 111 */ 112 for (encp = np; ses_node_type(encp) != SES_NODE_ENCLOSURE; 113 encp = ses_node_parent(encp)) 114 ; 115 116 encprops = ses_node_props(encp); 117 if (nvlist_lookup_byte_array(encprops, SES_EN_PROP_STRING, 118 &stringin, &len) != 0 || len < 4) 119 return (0); 120 121 /* 122 * If this is an enclosure, then calculate the chassis WWN by masking 123 * off the bottom 8 bits of the WWN. 124 */ 125 if (ses_node_type(np) == SES_NODE_ENCLOSURE) { 126 VERIFY(nvlist_lookup_nvlist(props, SES_EN_PROP_LID, &lid) == 0); 127 VERIFY(nvlist_lookup_uint64(lid, SPC3_NAA_INT, &wwn) == 0); 128 (void) snprintf(serial, sizeof (serial), "%llx", 129 wwn & ~0xFFULL); 130 SES_NV_ADD(string, nverr, props, LIBSES_EN_PROP_CSN, serial); 131 } 132 133 /* 134 * The STRING IN data is organized into a series of variable-length 135 * fields, where each field can be either a key ("Fan PartNUM") or a 136 * value. If the field length is less than our shortest expected 137 * identifier, then something has gone awry and we assume that the data 138 * is corrupt. 139 */ 140 fieldlen = stringin[3]; 141 if (fieldlen < 11) 142 return (0); 143 144 for (field = (char *)stringin + 4; 145 field + fieldlen <= (char *)stringin + len; field += fieldlen) { 146 if (strncmp(field, "Storage J4500", 13) == 0) { 147 /* 148 * This is the part number for the enclosure itself. 149 */ 150 if (ses_node_type(np) != SES_NODE_ENCLOSURE) 151 continue; 152 153 field += fieldlen; 154 if (field + fieldlen > (char *)stringin + len) 155 break; 156 157 if (ses_node_type(np) == SES_NODE_ENCLOSURE) { 158 SES_NV_ADD(fixed_string_trunc, nverr, props, 159 LIBSES_PROP_PART, field, fieldlen); 160 return (0); 161 } 162 163 } else if (strncmp(field, "Fan PartNUM", 11) == 0) { 164 /* 165 * Part numbers for the fans, of which there are 5. 166 */ 167 if (ses_node_type(np) != SES_NODE_ELEMENT || 168 type != SES_ET_COOLING) { 169 field += fieldlen * 5; 170 continue; 171 } 172 173 field += fieldlen; 174 175 for (i = 0; i < 5 && 176 field + fieldlen <= (char *)stringin + len; 177 i++, field += fieldlen) { 178 if (index == i && 179 strncmp(field, "Unknown", 7) != 0 && 180 strncmp(field, "Not Installed", 13) != 0) { 181 SES_NV_ADD(fixed_string_trunc, nverr, 182 props, LIBSES_PROP_PART, 183 field, fieldlen); 184 return (0); 185 } 186 } 187 188 } else if (strncmp(field, "PS PartNUM", 10) == 0) { 189 /* 190 * Part numbers for the power supplies, of which there 191 * are 2. 192 */ 193 if (ses_node_type(np) != SES_NODE_ELEMENT || 194 type != SES_ET_POWER_SUPPLY) { 195 field += fieldlen * 2; 196 continue; 197 } 198 199 field += fieldlen; 200 201 for (i = 0; i < 2 && 202 field + fieldlen <= (char *)stringin + len; 203 i++, field += fieldlen) { 204 if (index == i && 205 strncmp(field, "Unknown", 7) != 0 && 206 strncmp(field, "Not Installed", 13) != 0) { 207 SES_NV_ADD(fixed_string_trunc, nverr, 208 props, LIBSES_PROP_PART, 209 field, fieldlen); 210 return (0); 211 } 212 } 213 } 214 } 215 216 return (0); 217 } 218 219 int 220 _ses_init(ses_plugin_t *sp) 221 { 222 ses_plugin_config_t config = { 223 .spc_node_parse = sun_loki_parse_node 224 }; 225 226 return (ses_plugin_register(sp, LIBSES_PLUGIN_VERSION, 227 &config) != 0); 228 } 229