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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Topology Plugin Modules
31  *
32  * Topology plugin modules are shared libraries that are dlopen'd and
33  * used to enumerate resources in the system.
34  * They are loaded by our builtin scheme-specific plugins or other modules
35  * to enumerate and create nodes for resources that are present in the system.
36  * They may also export a set of resource (node) specific methods that can be
37  * called on node-by-node basis.
38  *
39  * Module Plugin API
40  *
41  * Enumerators must provide entry points for intialization and clean-up
42  * (_topo_init() and _topo_fini()).  In their _topo_init() function, an
43  * enumerator should register (topo_mod_register()) its enumeration callback
44  * and allocate resources required for a subsequent call to the callback.
45  * Optionally, methods may also be registered with topo_method_register().
46  *
47  * In its enumeration callback routine, the module should search for resources
48  * within its realm of resposibility and create any node ranges,
49  * topo_node_range_create() or nodes, topo_node_bind().  The Enumerator
50  * module is handed a node to which it may begin attaching additional
51  * topology nodes.
52  *
53  * If additional helper modules need to be loaded to complete the enumeration
54  * the module may do so by calling topo_mod_load().  Enumeration may then
55  * continue with the module handing off enumeration to its helper module
56  * by calling topo_mod_enumerate().
57  *
58  * If the module registers a release callback, it will be called on a node
59  * by node basis during topo_snap_rele().  Any private node data may be
60  * deallocated or methods unregistered at that time.  Global module data
61  * should be clean-up before or at the time that the module _topo_fini
62  * entry point is called.
63  */
64 
65 #include <pthread.h>
66 #include <assert.h>
67 #include <errno.h>
68 #include <dirent.h>
69 #include <limits.h>
70 #include <alloca.h>
71 #include <unistd.h>
72 #include <stdio.h>
73 
74 #include <topo_module.h>
75 #include <topo_alloc.h>
76 #include <topo_string.h>
77 #include <topo_error.h>
78 #include <topo_subr.h>
79 
80 topo_mod_t *
81 topo_mod_load(topo_mod_t *pmod, const char *path)
82 {
83 	int err = 0;
84 	char *p;
85 	topo_mod_t *mod = NULL;
86 	topo_hdl_t *thp;
87 
88 	thp = pmod->tm_hdl;
89 
90 	/*
91 	 * Already loaded, bump the ref count
92 	 */
93 	if ((mod = topo_mod_lookup(thp, path)) != NULL) {
94 		topo_mod_hold(mod);
95 		return (mod);
96 	}
97 
98 	/*
99 	 * Check for a valid path
100 	 */
101 	if (access(path, F_OK) != 0) {
102 		(void) topo_mod_seterrno(pmod, ETOPO_MOD_NOENT);
103 		return (NULL);
104 	}
105 
106 	if ((p = strrchr(path, '.')) != NULL && strcmp(p, ".so") == 0) {
107 		if ((mod = topo_modhash_load(thp, path,
108 		    &topo_rtld_ops)) == NULL) { /* returned with mod held */
109 			(void) topo_mod_seterrno(pmod, err ? err :
110 			    ETOPO_MOD_NOENT);
111 			return (NULL);
112 		}
113 	} else {
114 		(void) topo_mod_seterrno(pmod, err ? err : ETOPO_MOD_NOENT);
115 		return (NULL);
116 	}
117 
118 	return (mod);
119 }
120 
121 void
122 topo_mod_unload(topo_mod_t *mod)
123 {
124 	topo_mod_rele(mod);
125 }
126 
127 static int
128 set_register_error(topo_mod_t *mod, int err)
129 {
130 	if (mod->tm_info != NULL)
131 		topo_mod_unregister(mod);
132 
133 	topo_dprintf(TOPO_DBG_ERR, "module registration failed for %s: %s\n",
134 	    mod->tm_name, topo_strerror(err));
135 
136 	return (topo_mod_seterrno(mod, err));
137 }
138 
139 int
140 topo_mod_register(topo_mod_t *mod, const topo_modinfo_t *mip, void *priv)
141 {
142 
143 	assert(!(mod->tm_flags & TOPO_MOD_FINI ||
144 	    mod->tm_flags & TOPO_MOD_REG));
145 
146 	if (mod->tm_version > mip->tmi_version)
147 		return (set_register_error(mod, ETOPO_VER_OLD));
148 	if (mod->tm_version < mip->tmi_version)
149 		return (set_register_error(mod, ETOPO_VER_NEW));
150 
151 	if ((mod->tm_info = topo_mod_alloc(mod, sizeof (topo_modinfo_t)))
152 	    == NULL)
153 		return (set_register_error(mod, ETOPO_NOMEM));
154 
155 	mod->tm_info->tmi_desc = topo_mod_strdup(mod, mip->tmi_desc);
156 	if (mod->tm_info->tmi_desc == NULL)
157 		return (set_register_error(mod, ETOPO_NOMEM));
158 
159 	mod->tm_info->tmi_version = (topo_version_t)mip->tmi_version;
160 	mod->tm_info->tmi_enum = mip->tmi_enum;
161 	mod->tm_info->tmi_release = mip->tmi_release;
162 
163 	mod->tm_flags |= TOPO_MOD_REG;
164 	mod->tm_priv = priv;
165 
166 	if (mod == NULL) {
167 		topo_dprintf(TOPO_DBG_MOD, "registration succeeded for %s\n",
168 		    mod->tm_name);
169 
170 		return (0);
171 	}
172 
173 
174 	topo_dprintf(TOPO_DBG_MOD, "registration succeeded for %s\n",
175 	    mod->tm_name);
176 
177 	return (0);
178 }
179 
180 void
181 topo_mod_unregister(topo_mod_t *mod)
182 {
183 	if (mod->tm_info == NULL)
184 		return;
185 
186 	assert(!(mod->tm_flags & TOPO_MOD_FINI));
187 
188 	mod->tm_flags &= ~TOPO_MOD_REG;
189 
190 	if (mod->tm_info == NULL)
191 		return;
192 
193 	if (mod->tm_info->tmi_desc != NULL)
194 		topo_mod_strfree(mod, mod->tm_info->tmi_desc);
195 
196 	topo_mod_free(mod, mod->tm_info, sizeof (topo_modinfo_t));
197 
198 	mod->tm_info = NULL;
199 }
200 
201 int
202 topo_mod_enumerate(topo_mod_t *mod, tnode_t *node, const char *enum_name,
203     const char *name, topo_instance_t min, topo_instance_t max)
204 {
205 	int err = 0;
206 	topo_mod_t *enum_mod;
207 
208 	assert(mod->tm_flags & TOPO_MOD_REG);
209 
210 	if ((enum_mod = topo_mod_lookup(mod->tm_hdl, enum_name)) == NULL)
211 		return (topo_mod_seterrno(mod, ETOPO_MOD_NOENT));
212 
213 	topo_node_hold(node);
214 
215 	topo_dprintf(TOPO_DBG_MOD, "module %s enumerating node %s=%d\n",
216 	    (char *)mod->tm_name, (char *)node->tn_name, node->tn_instance);
217 
218 	topo_mod_enter(enum_mod);
219 	err = enum_mod->tm_info->tmi_enum(enum_mod, node, name, min, max,
220 	    enum_mod->tm_priv);
221 	topo_mod_exit(enum_mod);
222 
223 	if (err != 0) {
224 		(void) topo_mod_seterrno(mod, ETOPO_MODULE);
225 
226 		topo_dprintf(TOPO_DBG_ERR, "module %s failed enumeration for "
227 		    " node %s=%d\n", (char *)mod->tm_name,
228 		    (char *)node->tn_name, node->tn_instance);
229 
230 		topo_node_rele(node);
231 		return (-1);
232 	}
233 
234 	topo_node_rele(node);
235 
236 	return (0);
237 }
238 
239 char *
240 topo_mod_rootdir(topo_mod_t *mod)
241 {
242 	return (mod->tm_rootdir);
243 }
244 
245 topo_hdl_t *
246 topo_mod_handle(topo_mod_t *mod)
247 {
248 	return (mod->tm_hdl);
249 }
250 
251 void *
252 topo_mod_private(topo_mod_t *mod)
253 {
254 	return (mod->tm_priv);
255 }
256 
257 void
258 topo_mod_setdebug(topo_mod_t *mod, int mask)
259 {
260 	mod->tm_debug |= mask;
261 }
262 
263 void
264 topo_mod_clrdebug(topo_mod_t *mod)
265 {
266 	mod->tm_debug = 0;
267 }
268 
269 /*PRINTFLIKE2*/
270 void
271 topo_mod_dprintf(topo_mod_t *mod, const char *format, ...)
272 {
273 	if (mod->tm_debug & mod->tm_hdl->th_debug) {
274 		va_list alist;
275 
276 		va_start(alist, format);
277 		(void) fputs("libtopo DEBUG: ", stderr);
278 		(void) vfprintf(stderr, format, alist);
279 		va_end(alist);
280 	}
281 }
282