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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <sys/types.h> 27 #include <stddef.h> 28 #include <stdio.h> 29 #include <string.h> 30 #include <strings.h> 31 #include <libnvpair.h> 32 33 #include <scsi/libses.h> 34 #include "ses2_impl.h" 35 36 #define SES_UCODE_DEF_CHUNK (32 * 1024) 37 38 /*ARGSUSED*/ 39 static int 40 enc_do_ucode(ses_plugin_t *sp, ses_node_t *np, nvlist_t *nvl) 41 { 42 nvlist_t *props = ses_node_props(np); 43 uint64_t maxlen, bufid = 0; 44 uint8_t *data; 45 ses2_ucode_ctl_page_impl_t *uip; 46 size_t offset, len, pagelen; 47 uint_t datalen; 48 uint64_t mode; 49 uint64_t chunksz = SES_UCODE_DEF_CHUNK; 50 51 /* 52 * Get the data and check the length. 53 */ 54 if (nvlist_lookup_byte_array(nvl, SES_CTL_PROP_UCODE_DATA, 55 &data, &datalen) != 0) 56 return (ses_error(ESES_INVALID_PROP, 57 "missing or invalid %s property", SES_CTL_PROP_UCODE_DATA)); 58 59 if (nvlist_lookup_uint64(nvl, SES_CTL_PROP_UCODE_MODE, 60 &mode) != 0) 61 return (ses_error(ESES_INVALID_PROP, 62 "missing or invalid %s property", SES_CTL_PROP_UCODE_MODE)); 63 64 if (nvlist_lookup_uint64(props, SES_EN_PROP_UCODE_SZ, 65 &maxlen) != 0 || datalen > maxlen) 66 return (ses_error(ESES_RANGE, 67 "microcode image length (%u) exceeds maximum length (%llu)", 68 datalen, maxlen)); 69 70 /* 71 * Get the expected buffer ID, but allow the user to override it. 72 */ 73 (void) nvlist_lookup_uint64(props, SES_EN_PROP_UCODE_BUF, 74 &bufid); 75 76 if (bufid == 0xFF) 77 bufid = 0; 78 79 (void) nvlist_lookup_uint64(nvl, SES_CTL_PROP_UCODE_BUFID, &bufid); 80 (void) nvlist_lookup_uint64(nvl, SES_CTL_PROP_UCODE_DATA_LEN, &chunksz); 81 82 if (chunksz & 3) 83 return (ses_error(ESES_RANGE, 84 "upload chunk size %llu is not divisible by 4", chunksz)); 85 86 for (offset = 0; offset < datalen; offset += chunksz) { 87 88 len = MIN(datalen - offset, chunksz); 89 if (len & 0x3) 90 pagelen = (len + 4) & ~0x3; 91 else 92 pagelen = len; 93 94 if ((uip = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np), 95 SES2_DIAGPAGE_DL_MICROCODE_CTL_STATUS, pagelen, 96 np, B_TRUE)) == NULL) 97 return (-1); 98 99 uip->sucpi_buffer_id = (uint8_t)bufid; 100 uip->sucpi_dl_ucode_mode = mode; 101 SCSI_WRITE32(&uip->sucpi_buffer_offset, offset); 102 SCSI_WRITE32(&uip->sucpi_ucode_image_length, datalen); 103 SCSI_WRITE32(&uip->sucpi_ucode_data_length, len); 104 105 bcopy(data + offset, &uip->sucpi_ucode_data[0], 106 len); 107 108 if (len != pagelen) 109 bzero(&uip->sucpi_ucode_data[0] + len, 110 pagelen - len); 111 } 112 113 (void) nvlist_remove_all(nvl, SES_CTL_PROP_UCODE_DATA); 114 (void) nvlist_remove_all(nvl, SES_CTL_PROP_UCODE_MODE); 115 (void) nvlist_remove_all(nvl, SES_CTL_PROP_UCODE_BUFID); 116 (void) nvlist_remove_all(nvl, SES_CTL_PROP_UCODE_DATA_LEN); 117 118 return (0); 119 } 120 121 static int 122 enc_ctl_common(ses_plugin_t *sp, ses_node_t *np, ses2_diag_page_t page, 123 nvpair_t *nvp) 124 { 125 ses2_enclosure_ctl_impl_t *tp; 126 const char *name; 127 boolean_t boolval; 128 uint64_t intval; 129 130 ASSERT(page == SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS); 131 132 if ((tp = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np), 133 page, 0, np, B_FALSE)) == NULL) 134 return (-1); 135 136 name = nvpair_name(nvp); 137 (void) nvpair_value_boolean_value(nvp, &boolval); 138 (void) nvpair_value_uint64(nvp, &intval); 139 140 if (strcmp(name, SES_PROP_IDENT) == 0) 141 tp->seci_rqst_ident = boolval; 142 else if (strcmp(name, SES_PROP_WARN_REQ) == 0) 143 tp->seci_request_warning = boolval; 144 else if (strcmp(name, SES_PROP_FAIL_REQ) == 0) 145 tp->seci_request_failure = boolval; 146 else if (strcmp(name, SES_EN_PROP_POWER_DELAY) == 0) 147 tp->seci_power_cycle_delay = intval; 148 else if (strcmp(name, SES_EN_PROP_POWER_REQUEST) == 0) 149 tp->seci_power_cycle_request = intval; 150 else if (strcmp(name, SES_EN_PROP_POWER_DURATION) == 0) 151 tp->seci_power_off_duration = intval; 152 else 153 ses_panic("bad property %s", name); 154 155 return (0); 156 } 157 158 /*ARGSUSED*/ 159 static int 160 enc_ctl_string(ses_plugin_t *sp, ses_node_t *np, ses2_diag_page_t page, 161 nvpair_t *nvp) 162 { 163 ses2_substring_out_page_impl_t *spip; 164 ses2_string_out_page_impl_t *pip; 165 const uint8_t *data; 166 size_t datalen; 167 uint_t nvlen; 168 nvlist_t *props = ses_node_props(np); 169 uint64_t eid; 170 171 ASSERT(strcmp(nvpair_name(nvp), SES_EN_PROP_STRING) == 0); 172 173 VERIFY(nvlist_lookup_uint64(props, SES_EN_PROP_EID, &eid) == 0); 174 175 (void) nvpair_value_byte_array(nvp, (uint8_t **)&data, &nvlen); 176 datalen = (size_t)nvlen; 177 178 if ((spip = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np), 179 SES2_DIAGPAGE_SUBENCLOSURE_STRING_IO, datalen, np, 180 B_FALSE)) != NULL) { 181 spip->ssopi_subenclosure_identifier = eid; 182 bcopy(data, spip->ssopi_data, datalen); 183 } else { 184 if (eid != 0) 185 return (ses_error(ESES_NOTSUP, "target does not " 186 "support string data for secondary subenclosures")); 187 188 if ((pip = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np), 189 SES2_DIAGPAGE_STRING_IO, datalen, np, B_FALSE)) == NULL) 190 return (-1); 191 192 bcopy(data, pip->ssopi_data, datalen); 193 } 194 195 return (0); 196 } 197 198 static int 199 enc_ctl_nick(ses_plugin_t *sp, ses_node_t *np, ses2_diag_page_t page, 200 nvpair_t *nvp) 201 { 202 /* LINTED - dummy variable for sizeof */ 203 ses2_subnick_ctl_page_impl_t *pip, dummy; 204 const char *nick; 205 size_t len, max; 206 nvlist_t *props = ses_node_props(np); 207 uint64_t eid; 208 209 ASSERT(strcmp(nvpair_name(nvp), SES_EN_PROP_NICK) == 0); 210 ASSERT(page == SES2_DIAGPAGE_SUBENCLOSURE_NICKNAME_CTL_STATUS); 211 212 (void) nvpair_value_string(nvp, (char **)&nick); 213 len = strlen(nick); 214 215 VERIFY(nvlist_lookup_uint64(props, SES_EN_PROP_EID, &eid) == 0); 216 217 max = sizeof (dummy.sspci_subenclosure_nickname); 218 if (len > max) 219 return (ses_error(ESES_RANGE, "nickname '%s' exceeds " 220 "maximum length %lu", nick, max)); 221 222 if ((pip = ses_plugin_ctlpage_lookup(sp, ses_node_snapshot(np), 223 page, len, np, B_FALSE)) == NULL) 224 return (-1); 225 226 pip->sspci_subenclosure_identifier = eid; 227 bcopy(nick, pip->sspci_subenclosure_nickname, len); 228 229 return (0); 230 } 231 232 static const ses2_ctl_prop_t enc_props[] = { 233 SES_COMMON_CTL_PROPS, 234 { 235 .scp_name = SES_PROP_IDENT, 236 .scp_type = DATA_TYPE_BOOLEAN_VALUE, 237 .scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS, 238 .scp_setprop = enc_ctl_common 239 }, 240 { 241 .scp_name = SES_PROP_WARN_REQ, 242 .scp_type = DATA_TYPE_BOOLEAN_VALUE, 243 .scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS, 244 .scp_setprop = enc_ctl_common 245 }, 246 { 247 .scp_name = SES_PROP_FAIL_REQ, 248 .scp_type = DATA_TYPE_BOOLEAN_VALUE, 249 .scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS, 250 .scp_setprop = enc_ctl_common 251 }, 252 { 253 .scp_name = SES_EN_PROP_POWER_DELAY, 254 .scp_type = DATA_TYPE_UINT64, 255 .scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS, 256 .scp_setprop = enc_ctl_common 257 }, 258 { 259 .scp_name = SES_EN_PROP_POWER_DURATION, 260 .scp_type = DATA_TYPE_UINT64, 261 .scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS, 262 .scp_setprop = enc_ctl_common 263 }, 264 { 265 .scp_name = SES_EN_PROP_POWER_REQUEST, 266 .scp_type = DATA_TYPE_UINT64, 267 .scp_num = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS, 268 .scp_setprop = enc_ctl_common 269 }, 270 { 271 .scp_name = SES_EN_PROP_STRING, 272 .scp_type = DATA_TYPE_BYTE_ARRAY, 273 .scp_num = -1, 274 .scp_setprop = enc_ctl_string 275 }, 276 { 277 .scp_name = SES_EN_PROP_NICK, 278 .scp_type = DATA_TYPE_STRING, 279 .scp_num = SES2_DIAGPAGE_SUBENCLOSURE_NICKNAME_CTL_STATUS, 280 .scp_setprop = enc_ctl_nick 281 }, 282 { 283 NULL 284 } 285 }; 286 287 static int 288 enc_setdef_one(ses_node_t *np, ses2_diag_page_t page, void *data) 289 { 290 ses2_enclosure_ctl_impl_t *tp = data; 291 nvlist_t *props = ses_node_props(np); 292 293 if (page != SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS) 294 return (0); 295 296 SES_NV_CTLBOOL(props, SES_PROP_IDENT, tp->seci_rqst_ident); 297 SES_NV_CTLBOOL(props, SES_PROP_WARN_REQ, 298 tp->seci_request_warning); 299 SES_NV_CTLBOOL(props, SES_PROP_FAIL_REQ, 300 tp->seci_request_failure); 301 302 return (0); 303 } 304 305 int 306 ses2_enclosure_ctl(ses_plugin_t *sp, ses_node_t *np, const char *op, 307 nvlist_t *nvl) 308 { 309 if (strcmp(op, SES_CTL_OP_SETPROP) == 0) 310 return (ses2_setprop(sp, np, enc_props, nvl)); 311 else if (strcmp(op, SES_CTL_OP_DL_UCODE) == 0) 312 return (enc_do_ucode(sp, np, nvl)); 313 314 return (0); 315 } 316 317 int 318 ses2_enclosure_setdef(ses_node_t *np, ses2_diag_page_t page, void *data) 319 { 320 nvlist_t *props = ses_node_props(np); 321 uint64_t type; 322 323 VERIFY(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, &type) == 0); 324 325 if (type == SES_ET_ENCLOSURE && 326 enc_setdef_one(np, page, data) != 0) 327 return (-1); 328 329 return (0); 330 } 331