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