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