blob: 5983941fef44d7e263dd2e97835a24986b1da92a [file] [log] [blame]
Lucas De Marchie701e382012-01-26 17:01:41 -02001/*
2 * Copyright (C) 2012 ProFUSION embedded systems
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
Lucas De Marchi53646fc2012-01-26 02:09:28 -020018#include <assert.h>
19#include <errno.h>
20#include <dirent.h>
21#include <fcntl.h>
22#include <dlfcn.h>
23#include <limits.h>
24#include <stdlib.h>
25#include <stdarg.h>
26#include <stddef.h>
27#include <string.h>
28#include <stdio.h>
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <unistd.h>
32
33/* kmod_elf_get_section() is not exported, we need the private header */
34#include <libkmod-private.h>
35
36/* FIXME: hack, change name so we don't clash */
37#undef ERR
Lucas De Marchi5a2949c2012-05-25 00:30:37 -030038#include "mkdir.h"
Lucas De Marchi53646fc2012-01-26 02:09:28 -020039#include "testsuite.h"
40#include "stripped-module.h"
41
42struct mod {
43 struct mod *next;
44 int ret;
45 int errcode;
46 char name[];
47};
48
49static struct mod *modules;
50static bool need_init = true;
51
52static void parse_retcodes(struct mod *_modules, const char *s)
53{
54 const char *p;
55
56 if (s == NULL)
57 return;
58
59 for (p = s;;) {
60 struct mod *mod;
61 const char *modname;
62 char *end;
63 size_t modnamelen;
64 int ret, errcode;
65 long l;
66
67 modname = p;
68 if (modname == NULL || modname[0] == '\0')
69 break;
70
71 modnamelen = strcspn(s, ":");
72 if (modname[modnamelen] != ':')
73 break;
74
75 p = modname + modnamelen + 1;
76 if (p == NULL)
77 break;
78
79 l = strtol(p, &end, 0);
80 if (end == p || *end != ':')
81 break;
82 ret = (int) l;
83 p = end + 1;
84
85 l = strtol(p, &end, 0);
86 if (*end == ':')
87 p = end + 1;
88 else if (*end != '\0')
89 break;
90
91 errcode = (int) l;
92
93 mod = malloc(sizeof(*mod) + modnamelen + 1);
94 if (mod == NULL)
95 break;
96
97 memcpy(mod->name, modname, modnamelen);
98 mod->name[modnamelen] = '\0';
99 mod->ret = ret;
100 mod->errcode = errcode;
101 mod->next = _modules;
102 _modules = mod;
103 }
104}
105
Lucas De Marchi5a2949c2012-05-25 00:30:37 -0300106static int write_one_line_file(const char *fn, const char *line, int len)
107{
108 FILE *f;
109 int r;
110
111 assert(fn);
112 assert(line);
113
114 f = fopen(fn, "we");
115 if (!f)
116 return -errno;
117
118 errno = 0;
119 if (fputs(line, f) < 0) {
120 r = -errno;
121 goto finish;
122 }
123
124 fflush(f);
125
126 if (ferror(f)) {
127 if (errno != 0)
128 r = -errno;
129 else
130 r = -EIO;
131 } else
132 r = 0;
133
134finish:
135 fclose(f);
136 return r;
137}
138
139static int create_sysfs_files(const char *modname)
140{
141 char buf[PATH_MAX];
142 const char *sysfsmod = "/sys/module/";
143 int len = strlen(sysfsmod);
144
145 memcpy(buf, sysfsmod, len);
146 strcpy(buf + len, modname);
147 len += strlen(modname);
148
149 mkdir_p(buf, 0755);
150
151 strcpy(buf + len, "/initstate");
152 return write_one_line_file(buf, "live\n", strlen("live\n"));
153}
154
Lucas De Marchi53646fc2012-01-26 02:09:28 -0200155static struct mod *find_module(struct mod *_modules, const char *modname)
156{
157 struct mod *mod;
158
159 for (mod = _modules; mod != NULL; mod = mod->next) {
Lucas De Marchi90fc4102012-06-05 00:07:38 -0300160 if (strcmp(mod->name, modname) == 0)
Lucas De Marchi53646fc2012-01-26 02:09:28 -0200161 return mod;
162 }
163
164 return NULL;
165}
166
167static void init_retcodes(void)
168{
169 const char *s;
170
171 if (!need_init)
172 return;
173
174 need_init = false;
175 s = getenv(S_TC_INIT_MODULE_RETCODES);
176 if (s == NULL) {
177 fprintf(stderr, "TRAP init_module(): missing export %s?\n",
178 S_TC_INIT_MODULE_RETCODES);
179 }
180
181 parse_retcodes(modules, s);
182}
183
184TS_EXPORT long init_module(void *mem, unsigned long len, const char *args);
185
186/*
Lucas De Marchiddf1e7a2012-06-05 00:20:42 -0300187 * Default behavior is to try to mimic init_module behavior inside the kernel.
188 * If it is a simple test that you know the error code, set the return code
189 * in TESTSUITE_INIT_MODULE_RETCODES env var instead.
190 *
191 * The exception is when the module name is not find in the memory passed.
192 * This is because we want to be able to pass dummy modules (and not real
193 * ones) and it still work.
Lucas De Marchi53646fc2012-01-26 02:09:28 -0200194 */
195long init_module(void *mem, unsigned long len, const char *args)
196{
197 const char *modname;
198 struct kmod_elf *elf;
199 struct mod *mod;
200 const void *buf;
201 uint64_t bufsize;
202 int err;
203
204 init_retcodes();
205
206 elf = kmod_elf_new(mem, len);
207 if (elf == NULL)
208 return 0;
209
210 err = kmod_elf_get_section(elf, ".gnu.linkonce.this_module", &buf,
211 &bufsize);
212 kmod_elf_unref(elf);
213
214 /*
215 * We couldn't find the module's name inside the ELF file. Just exit
216 * as if it was successful
217 */
218 if (err < 0)
219 return 0;
220
221 modname = (char *)buf + offsetof(struct module, name);
222 mod = find_module(modules, modname);
Lucas De Marchiddf1e7a2012-06-05 00:20:42 -0300223 if (mod != NULL) {
224 errno = mod->errcode;
225 err = mod->ret;
226 } else {
227 /* mimic kernel behavior here */
228 err = 0;
Lucas De Marchi5a2949c2012-05-25 00:30:37 -0300229 }
Lucas De Marchi53646fc2012-01-26 02:09:28 -0200230
Lucas De Marchiddf1e7a2012-06-05 00:20:42 -0300231 if (err == 0)
232 create_sysfs_files(modname);
233
234 return err;
Lucas De Marchi53646fc2012-01-26 02:09:28 -0200235}
236
237/* the test is going away anyway, but lets keep valgrind happy */
238void free_resources(void) __attribute__((destructor));
239void free_resources(void)
240{
241 while (modules) {
242 struct mod *mod = modules->next;
243 free(modules);
244 modules = mod;
245 }
246}