xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/mod.c (revision 76716eaced8d7659d4594350eb3f343c31fe2806)
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 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 #include <limits.h>
30 #include <strings.h>
31 #include <unistd.h>
32 #include <libnvpair.h>
33 #include <fm/topo_mod.h>
34 #include <sys/fm/protocol.h>
35 
36 #include <fcntl.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/objfs.h>
40 #include <sys/modctl.h>
41 #include <libelf.h>
42 #include <gelf.h>
43 
44 #include <topo_error.h>
45 
46 static int mod_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
47     topo_instance_t, void *);
48 static void mod_release(topo_mod_t *, tnode_t *);
49 static int mod_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t,
50     nvlist_t *, nvlist_t **);
51 
52 #define	MOD_VERSION	TOPO_VERSION
53 
54 static const topo_method_t mod_methods[] = {
55 	{ TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
56 	    TOPO_STABILITY_INTERNAL, mod_fmri_create_meth },
57 	{ NULL }
58 };
59 
60 static const topo_modinfo_t mod_info =
61 	{ "mod", MOD_VERSION, mod_enum, mod_release };
62 
63 void
64 mod_init(topo_mod_t *mod)
65 {
66 	topo_mod_setdebug(mod, TOPO_DBG_ALL);
67 	topo_mod_dprintf(mod, "initializing mod builtin\n");
68 
69 	if (topo_mod_register(mod, &mod_info, NULL) != 0) {
70 		topo_mod_dprintf(mod, "failed to register mod_info: "
71 		    "%s\n", topo_mod_errmsg(mod));
72 		return;
73 	}
74 }
75 
76 void
77 mod_fini(topo_mod_t *mod)
78 {
79 	topo_mod_unregister(mod);
80 }
81 
82 /*ARGSUSED*/
83 static int
84 mod_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
85     topo_instance_t min, topo_instance_t max, void *arg)
86 {
87 	(void) topo_method_register(mod, pnode, mod_methods);
88 	return (0);
89 }
90 
91 static void
92 mod_release(topo_mod_t *mod, tnode_t *node)
93 {
94 	topo_method_unregister_all(mod, node);
95 }
96 
97 static char *
98 mod_binary_path_get(topo_mod_t *mp, char *objpath)
99 {
100 	static char Pathbuf[PATH_MAX];
101 	Elf *elf = NULL;
102 	Elf_Scn *scn = NULL;
103 	Elf_Data *edata;
104 	GElf_Ehdr ehdr;
105 	GElf_Shdr shdr;
106 	int fd;
107 
108 	if ((fd = open(objpath, O_RDONLY)) < 0) {
109 		topo_mod_dprintf(mp, "failed to open %s", objpath);
110 		goto mbpg_bail;
111 	}
112 	if (elf_version(EV_CURRENT) == EV_NONE) {
113 		topo_mod_dprintf(mp, "Elf version out of whack\n");
114 		goto mbpg_bail;
115 	}
116 	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
117 		topo_mod_dprintf(mp, "elf_begin failed\n");
118 		goto mbpg_bail;
119 	}
120 	if ((gelf_getehdr(elf, &ehdr)) == NULL) {
121 		topo_mod_dprintf(mp, "gelf_getehdr failed\n");
122 		goto mbpg_bail;
123 	}
124 	scn = elf_getscn(elf, 0);	/* "seek" to start of sections */
125 	while ((scn = elf_nextscn(elf, scn)) != NULL) {
126 		const char *sh_name;
127 		if (gelf_getshdr(scn, &shdr) == NULL) {
128 			topo_mod_dprintf(mp, "gelf_getshdr failed\n");
129 			goto mbpg_bail;
130 		}
131 		if (shdr.sh_type != SHT_PROGBITS)
132 			continue;
133 		sh_name = elf_strptr(elf,
134 		    ehdr.e_shstrndx, (size_t)shdr.sh_name);
135 		if (strcmp(sh_name, ".filename") != 0)
136 			continue;
137 		if ((edata = elf_getdata(scn, NULL)) == NULL) {
138 			topo_mod_dprintf(mp, "no filename data");
139 			break;
140 		}
141 		(void) strlcpy(Pathbuf, edata->d_buf, PATH_MAX);
142 		break;
143 	}
144 	elf_end(elf);
145 	(void) close(fd);
146 	return (Pathbuf);
147 
148 mbpg_bail:
149 	if (elf != NULL)
150 		elf_end(elf);
151 	if (fd >= 0)
152 		(void) close(fd);
153 	(void) topo_mod_seterrno(mp, EMOD_METHOD_INVAL);
154 	return (NULL);
155 }
156 
157 static int
158 mod_nvl_data(topo_mod_t *mp, nvlist_t *out, const char *path)
159 {
160 	struct modinfo mi;
161 	struct stat64 s;
162 	int id, e;
163 
164 	if (stat64(path, &s) < 0) {
165 		topo_mod_dprintf(mp,
166 		    "No system object file for driver %s", path);
167 		return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL));
168 	}
169 
170 	id = OBJFS_MODID(s.st_ino);
171 	mi.mi_id = mi.mi_nextid = id;
172 	mi.mi_info = MI_INFO_ONE | MI_INFO_NOBASE;
173 	if (modctl(MODINFO, id, &mi) < 0) {
174 		topo_mod_dprintf(mp, "failed to get modinfo for %s", path);
175 		return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL));
176 	}
177 	mi.mi_name[MODMAXNAMELEN - 1] = '\0';
178 	mi.mi_msinfo[0].msi_linkinfo[MODMAXNAMELEN - 1] = '\0';
179 	e = nvlist_add_string(out, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MOD);
180 	e |= nvlist_add_uint8(out, FM_VERSION, FM_MOD_SCHEME_VERSION);
181 	e |= nvlist_add_int32(out, FM_FMRI_MOD_ID, id);
182 	e |= nvlist_add_string(out, FM_FMRI_MOD_NAME, mi.mi_name);
183 	e |= nvlist_add_string(out,
184 	    FM_FMRI_MOD_DESC, mi.mi_msinfo[0].msi_linkinfo);
185 	if (e != 0)
186 		return (topo_mod_seterrno(mp, EMOD_FMRI_NVL));
187 
188 	return (0);
189 }
190 
191 static nvlist_t *
192 mod_fmri_create(topo_mod_t *mp, const char *driver)
193 {
194 	topo_hdl_t *thp;
195 	nvlist_t *arg = NULL;
196 	nvlist_t *out = NULL;
197 	nvlist_t *pkg = NULL;
198 	char objpath[PATH_MAX];
199 	char *path = NULL;
200 	int err;
201 
202 	if (topo_mod_nvalloc(mp, &arg, NV_UNIQUE_NAME) != 0 ||
203 	    topo_mod_nvalloc(mp, &out, NV_UNIQUE_NAME) != 0) {
204 		(void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
205 		goto mfc_bail;
206 	}
207 
208 	(void) snprintf(objpath, PATH_MAX, "%s/%s/object", OBJFS_ROOT, driver);
209 
210 	if ((path = mod_binary_path_get(mp, objpath)) == NULL)
211 		goto mfc_bail;
212 	if (nvlist_add_string(arg, "path", path) != 0) {
213 		(void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
214 		goto mfc_bail;
215 	}
216 
217 	if (mod_nvl_data(mp, out, objpath) < 0)
218 		goto mfc_bail;
219 
220 	thp = topo_mod_handle(mp);
221 	pkg = topo_fmri_create(thp,
222 	    FM_FMRI_SCHEME_PKG, FM_FMRI_SCHEME_PKG, 0, arg, &err);
223 	if (pkg == NULL) {
224 		(void) topo_mod_seterrno(mp, err);
225 		goto mfc_bail;
226 	}
227 	nvlist_free(arg);
228 	arg = NULL;
229 
230 	if (nvlist_add_nvlist(out, FM_FMRI_MOD_PKG, pkg) != 0) {
231 		(void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
232 		goto mfc_bail;
233 	}
234 	nvlist_free(pkg);
235 
236 	return (out);
237 
238 mfc_bail:
239 	nvlist_free(pkg);
240 	nvlist_free(out);
241 	nvlist_free(arg);
242 	return (NULL);
243 }
244 
245 /*ARGSUSED*/
246 static int
247 mod_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version,
248     nvlist_t *in, nvlist_t **out)
249 {
250 	nvlist_t *args;
251 	nvlist_t *modnvl;
252 	char *driver;
253 
254 	if (version > TOPO_METH_FMRI_VERSION)
255 		return (topo_mod_seterrno(mp, EMOD_VER_NEW));
256 
257 	if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 ||
258 	    nvlist_lookup_string(args, "DRIVER", &driver) != 0) {
259 		topo_mod_dprintf(mp, "no DRIVER string in method argument\n");
260 		return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL));
261 	}
262 
263 	modnvl = mod_fmri_create(mp, driver);
264 	if (modnvl == NULL) {
265 		*out = NULL;
266 		topo_mod_dprintf(mp, "failed to create contained mod FMRI\n");
267 		return (topo_mod_seterrno(mp, EMOD_FMRI_NVL));
268 	}
269 	*out = modnvl;
270 	return (0);
271 }
272