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