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
smp_report_general(smp_target_t * tp)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
smp_report_manufacturer_information(smp_target_t * tp)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
smp_target_fill(smp_target_t * tp)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 *
smp_get_funcdef(smp_target_t * tp,int fn)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
smp_plugin_register(smp_plugin_t * pp,int version,const smp_plugin_config_t * pcp)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
smp_plugin_setspecific(smp_plugin_t * pp,void * data)176 smp_plugin_setspecific(smp_plugin_t *pp, void *data)
177 {
178 pp->sp_data = data;
179 }
180
181 void *
smp_plugin_getspecific(smp_plugin_t * pp)182 smp_plugin_getspecific(smp_plugin_t *pp)
183 {
184 return (pp->sp_data);
185 }
186
187 static void
smp_plugin_cleanstr(char * s)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
smp_plugin_destroy(smp_plugin_t * pp)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
smp_plugin_loadone(smp_target_t * tp,const char * path,uint32_t pass)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
smp_plugin_load_dir(smp_target_t * tp,const char * pluginroot)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
smp_plugin_load(smp_target_t * tp)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
smp_plugin_unload(smp_target_t * tp)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