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) 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/systeminfo.h> 28 #include <sys/scsi/generic/commands.h> 29 #include <sys/scsi/impl/commands.h> 30 31 #include <scsi/libsmp.h> 32 #include <scsi/libsmp_plugin.h> 33 34 #include <alloca.h> 35 #include <dlfcn.h> 36 #include <link.h> 37 #include <dirent.h> 38 #include <string.h> 39 #include <strings.h> 40 #include <limits.h> 41 42 #include "smp_impl.h" 43 44 static boolean_t _libsmp_plugin_dlclose; 45 46 /* 47 * As part of basic initialization, we always retrieve the REPORT GENERAL 48 * data so that we will know whether this target supports the long response 49 * format. 50 */ 51 static int 52 smp_report_general(smp_target_t *tp) 53 { 54 smp_action_t *ap; 55 smp_report_general_resp_t *rp; 56 smp_result_t result; 57 size_t len; 58 59 if ((ap = smp_action_alloc(SMP_FUNC_REPORT_GENERAL, tp, 0)) == NULL) 60 return (-1); 61 62 if (smp_exec(ap, tp) != 0) { 63 smp_action_free(ap); 64 return (smp_set_errno(ESMP_REPGEN_FAILED)); 65 } 66 67 smp_action_get_response(ap, &result, (void **)&rp, &len); 68 69 if (result != SMP_RES_FUNCTION_ACCEPTED || len < 24) { 70 smp_action_free(ap); 71 return (smp_set_errno(ESMP_REPGEN_FAILED)); 72 } 73 74 bcopy(rp, &tp->st_repgen, sizeof (tp->st_repgen)); 75 76 smp_action_free(ap); 77 78 return (0); 79 } 80 81 static int 82 smp_report_manufacturer_information(smp_target_t *tp) 83 { 84 smp_action_t *ap; 85 smp_report_manufacturer_info_resp_t *rp; 86 smp_result_t result; 87 size_t len; 88 89 ap = smp_action_alloc(SMP_FUNC_REPORT_MANUFACTURER_INFO, tp, 0); 90 if (ap == NULL) 91 return (-1); 92 93 if (smp_exec(ap, tp) != 0) { 94 smp_action_free(ap); 95 return (smp_set_errno(ESMP_REPGEN_FAILED)); 96 } 97 98 smp_action_get_response(ap, &result, (void **)&rp, &len); 99 100 if (result != SMP_RES_FUNCTION_ACCEPTED || 101 len != sizeof (smp_report_manufacturer_info_resp_t)) { 102 smp_action_free(ap); 103 return (0); /* Not supported */ 104 } 105 106 tp->st_vendor = smp_trim_strdup(rp->srmir_vendor_identification, 107 sizeof (rp->srmir_vendor_identification)); 108 tp->st_product = smp_trim_strdup(rp->srmir_product_identification, 109 sizeof (rp->srmir_product_identification)); 110 tp->st_revision = smp_trim_strdup(rp->srmir_product_revision_level, 111 sizeof (rp->srmir_product_revision_level)); 112 113 if (rp->srmir_sas_1_1_format) { 114 tp->st_component_vendor = 115 smp_trim_strdup(rp->srmir_component_vendor_identification, 116 sizeof (rp->srmir_component_vendor_identification)); 117 118 tp->st_component_id = SCSI_READ16(&rp->srmir_component_id); 119 tp->st_component_revision = rp->srmir_component_revision_level; 120 } 121 122 if (tp->st_vendor == NULL || tp->st_product == NULL || 123 tp->st_revision == NULL || 124 (rp->srmir_sas_1_1_format && tp->st_component_vendor == NULL)) { 125 smp_action_free(ap); 126 return (smp_set_errno(ESMP_NOMEM)); 127 } 128 129 smp_action_free(ap); 130 131 return (0); 132 } 133 134 static int 135 smp_target_fill(smp_target_t *tp) 136 { 137 if (smp_report_general(tp) != 0 || 138 smp_report_manufacturer_information(tp) != 0) 139 return (-1); 140 141 return (0); 142 } 143 144 const smp_function_def_t * 145 smp_get_funcdef(smp_target_t *tp, int fn) 146 { 147 smp_plugin_t *pp; 148 const smp_function_def_t *dp; 149 150 for (pp = tp->st_plugin_first; pp != NULL; pp = pp->sp_next) { 151 if (pp->sp_functions == NULL) 152 continue; 153 154 for (dp = &pp->sp_functions[0]; dp->sfd_rq_len != NULL; dp++) { 155 if (dp->sfd_function == fn) 156 return (dp); 157 } 158 } 159 160 (void) smp_error(ESMP_BADFUNC, "failed to find function 0x%x", fn); 161 return (NULL); 162 } 163 164 int 165 smp_plugin_register(smp_plugin_t *pp, int version, 166 const smp_plugin_config_t *pcp) 167 { 168 if (version != LIBSMP_PLUGIN_VERSION) 169 return (smp_set_errno(ESMP_VERSION)); 170 171 pp->sp_functions = pcp->spc_functions; 172 173 return (0); 174 } 175 176 void 177 smp_plugin_setspecific(smp_plugin_t *pp, void *data) 178 { 179 pp->sp_data = data; 180 } 181 182 void * 183 smp_plugin_getspecific(smp_plugin_t *pp) 184 { 185 return (pp->sp_data); 186 } 187 188 static void 189 smp_plugin_cleanstr(char *s) 190 { 191 while (*s != '\0') { 192 if (*s == ' ' || *s == '/') 193 *s = '-'; 194 s++; 195 } 196 } 197 198 static void 199 smp_plugin_destroy(smp_plugin_t *pp) 200 { 201 if (pp->sp_initialized && pp->sp_fini != NULL) 202 pp->sp_fini(pp); 203 204 if (_libsmp_plugin_dlclose) 205 (void) dlclose(pp->sp_object); 206 207 smp_free(pp); 208 } 209 210 static int 211 smp_plugin_loadone(smp_target_t *tp, const char *path, uint32_t pass) 212 { 213 smp_plugin_t *pp, **loc; 214 void *obj; 215 int (*smp_priority)(void); 216 217 if ((obj = dlopen(path, RTLD_PARENT | RTLD_LOCAL | RTLD_LAZY)) == NULL) 218 return (0); 219 220 if ((pp = smp_zalloc(sizeof (smp_plugin_t))) == NULL) { 221 (void) dlclose(obj); 222 return (-1); 223 } 224 225 pp->sp_object = obj; 226 pp->sp_init = (int (*)())dlsym(obj, "_smp_init"); 227 pp->sp_fini = (void (*)())dlsym(obj, "_smp_fini"); 228 pp->sp_target = tp; 229 230 if (pp->sp_init == NULL) { 231 smp_plugin_destroy(pp); 232 return (0); 233 } 234 235 /* 236 * Framework modules can establish an explicit prioritying by declaring 237 * the '_smp_priority' symbol, which returns an integer used to create 238 * an explicit ordering between plugins. 239 */ 240 if ((smp_priority = (int (*)())dlsym(obj, "_smp_priority")) != NULL) 241 pp->sp_priority = smp_priority(); 242 243 pp->sp_priority |= (uint64_t)pass << 32; 244 245 for (loc = &tp->st_plugin_first; *loc != NULL; loc = &(*loc)->sp_next) { 246 if ((*loc)->sp_priority > pp->sp_priority) 247 break; 248 } 249 250 if (*loc != NULL) 251 (*loc)->sp_prev = pp; 252 else 253 tp->st_plugin_last = pp; 254 255 pp->sp_next = *loc; 256 *loc = pp; 257 258 if (pp->sp_init(pp) != 0) 259 return (-1); 260 pp->sp_initialized = B_TRUE; 261 262 return (0); 263 } 264 265 static int 266 smp_plugin_load_dir(smp_target_t *tp, const char *pluginroot) 267 { 268 char path[PATH_MAX]; 269 DIR *dirp; 270 struct dirent64 *dp; 271 char *c_vendor, *vendor, *product, *revision; 272 char isa[257]; 273 274 (void) snprintf(path, sizeof (path), "%s/%s", 275 pluginroot, LIBSMP_PLUGIN_FRAMEWORK); 276 277 #if defined(_LP64) 278 if (sysinfo(SI_ARCHITECTURE_64, isa, sizeof (isa)) < 0) 279 isa[0] = '\0'; 280 #else 281 isa[0] = '\0'; 282 #endif 283 284 if ((dirp = opendir(path)) != NULL) { 285 while ((dp = readdir64(dirp)) != NULL) { 286 if (strcmp(dp->d_name, ".") == 0 || 287 strcmp(dp->d_name, "..") == 0) 288 continue; 289 290 (void) snprintf(path, sizeof (path), "%s/%s/%s/%s", 291 pluginroot, LIBSMP_PLUGIN_FRAMEWORK, 292 isa, dp->d_name); 293 294 if (smp_plugin_loadone(tp, path, 0) != 0) { 295 (void) closedir(dirp); 296 return (-1); 297 } 298 } 299 300 (void) closedir(dirp); 301 } 302 303 /* 304 * Now attempt to load platform-specific plugins. The framework 305 * plugins had better give us the ability to perform basic SMP 306 * functions like REPORT GENERAL and REPORT MANUFACTURER INFORMATION; 307 * if not, we're toast anyway. If the latter is not supported, we 308 * will not be able to use any vendor-specific plugins. Note that 309 * there are actually two possible specifications for vendor plugins: 310 * those matching the vendor/product/revision fields, and those 311 * matching the component vendor/id/revision fields. The component is 312 * less specific, so we try to load those first. 313 */ 314 315 if (smp_target_fill(tp) != 0) 316 return (-1); 317 318 if (tp->st_vendor == NULL) 319 return (0); 320 321 if (tp->st_component_vendor != NULL) { 322 c_vendor = alloca(strlen(tp->st_component_vendor) + 1); 323 (void) strcpy(c_vendor, tp->st_component_vendor); 324 smp_plugin_cleanstr(c_vendor); 325 } 326 327 vendor = alloca(strlen(tp->st_vendor) + 1); 328 product = alloca(strlen(tp->st_product) + 1); 329 revision = alloca(strlen(tp->st_revision) + 1); 330 331 (void) strcpy(vendor, tp->st_vendor); 332 (void) strcpy(product, tp->st_product); 333 (void) strcpy(revision, tp->st_revision); 334 335 smp_plugin_cleanstr(vendor); 336 smp_plugin_cleanstr(product); 337 smp_plugin_cleanstr(revision); 338 339 if (tp->st_component_vendor != NULL) { 340 (void) snprintf(path, sizeof (path), "%s/%s/%s/component_%s%s", 341 pluginroot, LIBSMP_PLUGIN_VENDOR, isa, c_vendor, 342 LIBSMP_PLUGIN_EXT); 343 if (smp_plugin_loadone(tp, path, 1) != 0) 344 return (-1); 345 346 (void) snprintf(path, sizeof (path), 347 "%s/%s/%s/component_%s-%04x%s", 348 pluginroot, LIBSMP_PLUGIN_VENDOR, isa, c_vendor, 349 tp->st_component_id, LIBSMP_PLUGIN_EXT); 350 if (smp_plugin_loadone(tp, path, 2) != 0) 351 return (-1); 352 353 (void) snprintf(path, sizeof (path), 354 "%s/%s/%s/component_%s-%04x-%02x%s", 355 pluginroot, LIBSMP_PLUGIN_VENDOR, isa, c_vendor, 356 tp->st_component_id, tp->st_component_revision, 357 LIBSMP_PLUGIN_EXT); 358 if (smp_plugin_loadone(tp, path, 3) != 0) 359 return (-1); 360 } 361 362 (void) snprintf(path, sizeof (path), "%s/%s/%s/%s%s", pluginroot, 363 LIBSMP_PLUGIN_VENDOR, isa, vendor, LIBSMP_PLUGIN_EXT); 364 if (smp_plugin_loadone(tp, path, 4) != 0) 365 return (-1); 366 367 (void) snprintf(path, sizeof (path), "%s/%s/%s/%s-%s%s", pluginroot, 368 LIBSMP_PLUGIN_VENDOR, isa, vendor, product, LIBSMP_PLUGIN_EXT); 369 if (smp_plugin_loadone(tp, path, 5) != 0) 370 return (-1); 371 372 (void) snprintf(path, sizeof (path), "%s/%s/%s/%s-%s-%s%s", pluginroot, 373 LIBSMP_PLUGIN_VENDOR, isa, vendor, product, 374 revision, LIBSMP_PLUGIN_EXT); 375 if (smp_plugin_loadone(tp, path, 6) != 0) 376 return (-1); 377 378 return (0); 379 } 380 381 int 382 smp_plugin_load(smp_target_t *tp) 383 { 384 char pluginroot[PATH_MAX]; 385 const char *pluginpath, *p, *q; 386 387 if ((pluginpath = getenv("SMP_PLUGINPATH")) == NULL) 388 pluginpath = LIBSMP_DEFAULT_PLUGINDIR; 389 _libsmp_plugin_dlclose = (getenv("SMP_NODLCLOSE") == NULL); 390 391 for (p = pluginpath; p != NULL; p = q) { 392 if ((q = strchr(p, ':')) != NULL) { 393 ptrdiff_t len = q - p; 394 (void) strncpy(pluginroot, p, len); 395 pluginroot[len] = '\0'; 396 while (*q == ':') 397 ++q; 398 if (*q == '\0') 399 q = NULL; 400 if (len == 0) 401 continue; 402 } else { 403 (void) strcpy(pluginroot, p); 404 } 405 406 if (pluginroot[0] != '/') 407 continue; 408 409 if (smp_plugin_load_dir(tp, pluginroot) != 0) 410 return (-1); 411 } 412 413 if (tp->st_plugin_first == NULL) 414 return (smp_error(ESMP_PLUGIN, "no plugins found")); 415 416 return (0); 417 } 418 419 void 420 smp_plugin_unload(smp_target_t *tp) 421 { 422 smp_plugin_t *pp; 423 424 while ((pp = tp->st_plugin_first) != NULL) { 425 tp->st_plugin_first = pp->sp_next; 426 smp_plugin_destroy(pp); 427 } 428 } 429