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