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