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
43static 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 */
50static int
51smp_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
80static int
81smp_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
133static int
134smp_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
143const smp_function_def_t *
144smp_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
163int
164smp_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
175void
176smp_plugin_setspecific(smp_plugin_t *pp, void *data)
177{
178	pp->sp_data = data;
179}
180
181void *
182smp_plugin_getspecific(smp_plugin_t *pp)
183{
184	return (pp->sp_data);
185}
186
187static void
188smp_plugin_cleanstr(char *s)
189{
190	while (*s != '\0') {
191		if (*s == ' ' || *s == '/')
192			*s = '-';
193		s++;
194	}
195}
196
197static void
198smp_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
209static int
210smp_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
264static int
265smp_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
375int
376smp_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
413void
414smp_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