]>
Commit | Line | Data |
---|---|---|
e701e381 | 1 | /* |
e6b0e49b | 2 | * Copyright (C) 2012-2013 ProFUSION embedded systems |
0ae58609 | 3 | * Copyright (C) 2012-2013 Lucas De Marchi <lucas.de.marchi@gmail.com> |
e701e381 | 4 | * |
e1b1ab24 LDM |
5 | * This program is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public | |
7 | * License as published by the Free Software Foundation; either | |
8 | * version 2.1 of the License, or (at your option) any later version. | |
e701e381 LDM |
9 | * |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
e1b1ab24 LDM |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. | |
e701e381 | 14 | * |
e1b1ab24 LDM |
15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this library; if not, write to the Free Software | |
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
e701e381 LDM |
18 | */ |
19 | ||
55112d19 LDM |
20 | #ifndef HAVE_FINIT_MODULE |
21 | #define HAVE_FINIT_MODULE 1 | |
22 | #endif | |
23 | ||
53646fc5 | 24 | #include <assert.h> |
fca5b9bc | 25 | #include <elf.h> |
53646fc5 LDM |
26 | #include <errno.h> |
27 | #include <dirent.h> | |
28 | #include <fcntl.h> | |
29 | #include <dlfcn.h> | |
30 | #include <limits.h> | |
31 | #include <stdlib.h> | |
32 | #include <stdarg.h> | |
33 | #include <stddef.h> | |
34 | #include <string.h> | |
35 | #include <stdio.h> | |
e87352d2 | 36 | #include <sys/mman.h> |
53646fc5 LDM |
37 | #include <sys/types.h> |
38 | #include <sys/stat.h> | |
0ae58609 | 39 | #include <sys/syscall.h> |
53646fc5 LDM |
40 | #include <unistd.h> |
41 | ||
42 | /* kmod_elf_get_section() is not exported, we need the private header */ | |
43 | #include <libkmod-private.h> | |
44 | ||
45 | /* FIXME: hack, change name so we don't clash */ | |
46 | #undef ERR | |
5a2949cd | 47 | #include "mkdir.h" |
53646fc5 LDM |
48 | #include "testsuite.h" |
49 | #include "stripped-module.h" | |
50 | ||
51 | struct mod { | |
52 | struct mod *next; | |
53 | int ret; | |
54 | int errcode; | |
55 | char name[]; | |
56 | }; | |
57 | ||
58 | static struct mod *modules; | |
59 | static bool need_init = true; | |
a6553705 | 60 | static struct kmod_ctx *ctx; |
53646fc5 LDM |
61 | |
62 | static void parse_retcodes(struct mod *_modules, const char *s) | |
63 | { | |
64 | const char *p; | |
65 | ||
66 | if (s == NULL) | |
67 | return; | |
68 | ||
69 | for (p = s;;) { | |
70 | struct mod *mod; | |
71 | const char *modname; | |
72 | char *end; | |
73 | size_t modnamelen; | |
74 | int ret, errcode; | |
75 | long l; | |
76 | ||
77 | modname = p; | |
78 | if (modname == NULL || modname[0] == '\0') | |
79 | break; | |
80 | ||
81 | modnamelen = strcspn(s, ":"); | |
82 | if (modname[modnamelen] != ':') | |
83 | break; | |
84 | ||
85 | p = modname + modnamelen + 1; | |
86 | if (p == NULL) | |
87 | break; | |
88 | ||
89 | l = strtol(p, &end, 0); | |
90 | if (end == p || *end != ':') | |
91 | break; | |
92 | ret = (int) l; | |
93 | p = end + 1; | |
94 | ||
95 | l = strtol(p, &end, 0); | |
96 | if (*end == ':') | |
97 | p = end + 1; | |
98 | else if (*end != '\0') | |
99 | break; | |
100 | ||
101 | errcode = (int) l; | |
102 | ||
103 | mod = malloc(sizeof(*mod) + modnamelen + 1); | |
104 | if (mod == NULL) | |
105 | break; | |
106 | ||
107 | memcpy(mod->name, modname, modnamelen); | |
108 | mod->name[modnamelen] = '\0'; | |
109 | mod->ret = ret; | |
110 | mod->errcode = errcode; | |
111 | mod->next = _modules; | |
112 | _modules = mod; | |
113 | } | |
114 | } | |
115 | ||
5a2949cd LDM |
116 | static int write_one_line_file(const char *fn, const char *line, int len) |
117 | { | |
118 | FILE *f; | |
119 | int r; | |
120 | ||
121 | assert(fn); | |
122 | assert(line); | |
123 | ||
124 | f = fopen(fn, "we"); | |
125 | if (!f) | |
126 | return -errno; | |
127 | ||
128 | errno = 0; | |
129 | if (fputs(line, f) < 0) { | |
130 | r = -errno; | |
131 | goto finish; | |
132 | } | |
133 | ||
134 | fflush(f); | |
135 | ||
136 | if (ferror(f)) { | |
137 | if (errno != 0) | |
138 | r = -errno; | |
139 | else | |
140 | r = -EIO; | |
141 | } else | |
142 | r = 0; | |
143 | ||
144 | finish: | |
145 | fclose(f); | |
146 | return r; | |
147 | } | |
148 | ||
149 | static int create_sysfs_files(const char *modname) | |
150 | { | |
151 | char buf[PATH_MAX]; | |
152 | const char *sysfsmod = "/sys/module/"; | |
153 | int len = strlen(sysfsmod); | |
154 | ||
155 | memcpy(buf, sysfsmod, len); | |
156 | strcpy(buf + len, modname); | |
157 | len += strlen(modname); | |
158 | ||
33202e84 | 159 | assert(mkdir_p(buf, 0755) >= 0); |
5a2949cd LDM |
160 | |
161 | strcpy(buf + len, "/initstate"); | |
162 | return write_one_line_file(buf, "live\n", strlen("live\n")); | |
163 | } | |
164 | ||
53646fc5 LDM |
165 | static struct mod *find_module(struct mod *_modules, const char *modname) |
166 | { | |
167 | struct mod *mod; | |
168 | ||
169 | for (mod = _modules; mod != NULL; mod = mod->next) { | |
90fc410b | 170 | if (strcmp(mod->name, modname) == 0) |
53646fc5 LDM |
171 | return mod; |
172 | } | |
173 | ||
174 | return NULL; | |
175 | } | |
176 | ||
177 | static void init_retcodes(void) | |
178 | { | |
179 | const char *s; | |
180 | ||
181 | if (!need_init) | |
182 | return; | |
183 | ||
184 | need_init = false; | |
185 | s = getenv(S_TC_INIT_MODULE_RETCODES); | |
186 | if (s == NULL) { | |
187 | fprintf(stderr, "TRAP init_module(): missing export %s?\n", | |
188 | S_TC_INIT_MODULE_RETCODES); | |
189 | } | |
190 | ||
a6553705 LDM |
191 | ctx = kmod_new(NULL, NULL); |
192 | ||
53646fc5 LDM |
193 | parse_retcodes(modules, s); |
194 | } | |
195 | ||
a6553705 LDM |
196 | static inline bool module_is_inkernel(const char *modname) |
197 | { | |
198 | struct kmod_module *mod; | |
199 | int state; | |
200 | bool ret; | |
201 | ||
202 | if (kmod_module_new_from_name(ctx, modname, &mod) < 0) | |
203 | return false; | |
204 | ||
205 | state = kmod_module_get_initstate(mod); | |
206 | ||
207 | if (state == KMOD_MODULE_LIVE || | |
208 | state == KMOD_MODULE_BUILTIN) | |
209 | ret = true; | |
210 | else | |
211 | ret = false; | |
212 | ||
213 | kmod_module_unref(mod); | |
214 | ||
215 | return ret; | |
216 | } | |
217 | ||
fca5b9bc LDM |
218 | static uint8_t elf_identify(void *mem) |
219 | { | |
220 | uint8_t *p = mem; | |
221 | return p[EI_CLASS]; | |
222 | } | |
223 | ||
53646fc5 LDM |
224 | TS_EXPORT long init_module(void *mem, unsigned long len, const char *args); |
225 | ||
226 | /* | |
ddf1e7a6 LDM |
227 | * Default behavior is to try to mimic init_module behavior inside the kernel. |
228 | * If it is a simple test that you know the error code, set the return code | |
229 | * in TESTSUITE_INIT_MODULE_RETCODES env var instead. | |
230 | * | |
231 | * The exception is when the module name is not find in the memory passed. | |
232 | * This is because we want to be able to pass dummy modules (and not real | |
233 | * ones) and it still work. | |
53646fc5 LDM |
234 | */ |
235 | long init_module(void *mem, unsigned long len, const char *args) | |
236 | { | |
237 | const char *modname; | |
238 | struct kmod_elf *elf; | |
239 | struct mod *mod; | |
240 | const void *buf; | |
241 | uint64_t bufsize; | |
242 | int err; | |
fca5b9bc LDM |
243 | uint8_t class; |
244 | off_t offset; | |
53646fc5 LDM |
245 | |
246 | init_retcodes(); | |
247 | ||
248 | elf = kmod_elf_new(mem, len); | |
249 | if (elf == NULL) | |
250 | return 0; | |
251 | ||
252 | err = kmod_elf_get_section(elf, ".gnu.linkonce.this_module", &buf, | |
253 | &bufsize); | |
254 | kmod_elf_unref(elf); | |
255 | ||
fca5b9bc | 256 | /* We couldn't parse the ELF file. Just exit as if it was successful */ |
53646fc5 LDM |
257 | if (err < 0) |
258 | return 0; | |
259 | ||
fca5b9bc LDM |
260 | /* We need to open both 32 and 64 bits module - hack! */ |
261 | class = elf_identify(mem); | |
262 | if (class == ELFCLASS64) | |
263 | offset = MODULE_NAME_OFFSET_64; | |
264 | else | |
265 | offset = MODULE_NAME_OFFSET_32; | |
266 | ||
267 | modname = (char *)buf + offset; | |
53646fc5 | 268 | mod = find_module(modules, modname); |
ddf1e7a6 LDM |
269 | if (mod != NULL) { |
270 | errno = mod->errcode; | |
271 | err = mod->ret; | |
a6553705 LDM |
272 | } else if (module_is_inkernel(modname)) { |
273 | err = -1; | |
274 | errno = EEXIST; | |
275 | } else | |
ddf1e7a6 | 276 | err = 0; |
53646fc5 | 277 | |
ddf1e7a6 LDM |
278 | if (err == 0) |
279 | create_sysfs_files(modname); | |
280 | ||
281 | return err; | |
53646fc5 LDM |
282 | } |
283 | ||
e87352d2 KC |
284 | TS_EXPORT int finit_module(const int fd, const char *args, const int flags); |
285 | ||
286 | int finit_module(const int fd, const char *args, const int flags) | |
287 | { | |
288 | int err; | |
289 | void *mem; | |
290 | unsigned long len; | |
291 | struct stat st; | |
292 | ||
293 | if (fstat(fd, &st) < 0) | |
294 | return -1; | |
295 | ||
296 | len = st.st_size; | |
297 | mem = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); | |
298 | if (mem == MAP_FAILED) | |
299 | return -1; | |
300 | ||
301 | err = init_module(mem, len, args); | |
302 | munmap(mem, len); | |
303 | ||
304 | return err; | |
305 | } | |
306 | ||
0ae58609 LDM |
307 | TS_EXPORT long int syscall(long int __sysno, ...) |
308 | { | |
309 | va_list ap; | |
310 | long ret; | |
311 | ||
312 | switch (__sysno) { | |
313 | case -1: | |
314 | errno = -ENOSYS; | |
315 | return -1; | |
316 | case __NR_finit_module: { | |
317 | const char *args; | |
318 | int flags; | |
319 | int fd; | |
320 | ||
321 | va_start(ap, __sysno); | |
322 | ||
323 | fd = va_arg(ap, int); | |
324 | args = va_arg(ap, const char *); | |
325 | flags = va_arg(ap, int); | |
326 | ||
327 | ret = finit_module(fd, args, flags); | |
328 | ||
329 | va_end(ap); | |
330 | return ret; | |
331 | } | |
332 | } | |
333 | ||
334 | /* | |
335 | * FIXME: no way to call the libc function - let's hope there are no | |
336 | * other users. | |
337 | */ | |
338 | abort(); | |
339 | } | |
340 | ||
53646fc5 LDM |
341 | /* the test is going away anyway, but lets keep valgrind happy */ |
342 | void free_resources(void) __attribute__((destructor)); | |
343 | void free_resources(void) | |
344 | { | |
345 | while (modules) { | |
346 | struct mod *mod = modules->next; | |
347 | free(modules); | |
348 | modules = mod; | |
349 | } | |
a6553705 LDM |
350 | |
351 | if (ctx) | |
352 | kmod_unref(ctx); | |
53646fc5 | 353 | } |