1/* 2 * Copyright 2002-2008, Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Copyright 2001, Thomas Kurschel. All rights reserved. 6 * Distributed under the terms of the NewOS License. 7 */ 8 9/** Manages kernel add-ons and their exported modules. */ 10 11#include "module.h" 12 13#include <stdlib.h> 14 15#include "fssh_errors.h" 16#include "fssh_kernel_export.h" 17#include "fssh_lock.h" 18#include "fssh_module.h" 19#include "fssh_string.h" 20#include "hash.h" 21 22 23//#define TRACE_MODULE 24#ifdef TRACE_MODULE 25# define TRACE(x) fssh_dprintf x 26#else 27# define TRACE(x) ; 28#endif 29#define FATAL(x) fssh_dprintf x 30 31 32namespace FSShell { 33 34 35#define MODULE_HASH_SIZE 16 36 37enum module_state { 38 MODULE_QUERIED = 0, 39 MODULE_LOADED, 40 MODULE_INIT, 41 MODULE_READY, 42 MODULE_UNINIT, 43 MODULE_ERROR 44}; 45 46 47/* Each known module will have this structure which is put in the 48 * gModulesHash, and looked up by name. 49 */ 50 51struct module { 52 struct module *next; 53 char *name; 54 char *file; 55 int32_t ref_count; 56 fssh_module_info *info; /* will only be valid if ref_count > 0 */ 57 int32_t offset; /* this is the offset in the headers */ 58 module_state state; /* state of module */ 59 uint32_t flags; 60}; 61 62#define FSSH_B_BUILT_IN_MODULE 2 63 64 65/* locking scheme: there is a global lock only; having several locks 66 * makes trouble if dependent modules get loaded concurrently -> 67 * they have to wait for each other, i.e. we need one lock per module; 68 * also we must detect circular references during init and not dead-lock 69 */ 70static fssh_recursive_lock sModulesLock; 71 72/* we store the loaded modules by directory path, and all known modules by module name 73 * in a hash table for quick access 74 */ 75static hash_table *sModulesHash; 76 77 78/** calculates hash for a module using its name */ 79 80static uint32_t 81module_hash(void *_module, const void *_key, uint32_t range) 82{ 83 module *module = (struct module *)_module; 84 const char *name = (const char *)_key; 85 86 if (module != NULL) 87 return hash_hash_string(module->name) % range; 88 89 if (name != NULL) 90 return hash_hash_string(name) % range; 91 92 return 0; 93} 94 95 96/** compares a module to a given name */ 97 98static int 99module_compare(void *_module, const void *_key) 100{ 101 module *module = (struct module *)_module; 102 const char *name = (const char *)_key; 103 if (name == NULL) 104 return -1; 105 106 return fssh_strcmp(module->name, name); 107} 108 109 110static inline void 111inc_module_ref_count(struct module *module) 112{ 113 module->ref_count++; 114} 115 116 117static inline void 118dec_module_ref_count(struct module *module) 119{ 120 module->ref_count--; 121} 122 123 124/** Extract the information from the module_info structure pointed at 125 * by "info" and create the entries required for access to it's details. 126 */ 127 128static fssh_status_t 129create_module(fssh_module_info *info, const char *file, int offset, module **_module) 130{ 131 module *module; 132 133 TRACE(("create_module(info = %p, file = \"%s\", offset = %d, _module = %p)\n", 134 info, file, offset, _module)); 135 136 if (!info->name) 137 return FSSH_B_BAD_VALUE; 138 139 module = (struct module *)hash_lookup(sModulesHash, info->name); 140 if (module) { 141 FATAL(("Duplicate module name (%s) detected... ignoring new one\n", info->name)); 142 return FSSH_B_FILE_EXISTS; 143 } 144 145 if ((module = (struct module *)malloc(sizeof(struct module))) == NULL) 146 return FSSH_B_NO_MEMORY; 147 148 TRACE(("create_module: name = \"%s\", file = \"%s\"\n", info->name, file)); 149 150 module->name = fssh_strdup(info->name); 151 if (module->name == NULL) { 152 free(module); 153 return FSSH_B_NO_MEMORY; 154 } 155 156 module->file = fssh_strdup(file); 157 if (module->file == NULL) { 158 free(module->name); 159 free(module); 160 return FSSH_B_NO_MEMORY; 161 } 162 163 module->state = MODULE_QUERIED; 164 module->info = info; 165 module->offset = offset; 166 // record where the module_info can be found in the module_info array 167 module->ref_count = 0; 168 module->flags = info->flags; 169 170 fssh_recursive_lock_lock(&sModulesLock); 171 hash_insert(sModulesHash, module); 172 fssh_recursive_lock_unlock(&sModulesLock); 173 174 if (_module) 175 *_module = module; 176 177 return FSSH_B_OK; 178} 179 180 181/** Initializes a loaded module depending on its state */ 182 183static inline fssh_status_t 184init_module(module *module) 185{ 186 switch (module->state) { 187 case MODULE_QUERIED: 188 case MODULE_LOADED: 189 { 190 fssh_status_t status; 191 module->state = MODULE_INIT; 192 193 // init module 194 195 TRACE(("initializing module %s (at %p)... \n", module->name, module->info->std_ops)); 196 status = module->info->std_ops(FSSH_B_MODULE_INIT); 197 TRACE(("...done (%s)\n", strerror(status))); 198 199 if (status >= FSSH_B_OK) 200 module->state = MODULE_READY; 201 else { 202 module->state = MODULE_LOADED; 203 } 204 205 return status; 206 } 207 208 case MODULE_READY: 209 return FSSH_B_OK; 210 211 case MODULE_INIT: 212 FATAL(("circular reference to %s\n", module->name)); 213 return FSSH_B_ERROR; 214 215 case MODULE_UNINIT: 216 FATAL(("tried to load module %s which is currently unloading\n", module->name)); 217 return FSSH_B_ERROR; 218 219 case MODULE_ERROR: 220 FATAL(("cannot load module %s because its earlier unloading failed\n", module->name)); 221 return FSSH_B_ERROR; 222 223 default: 224 return FSSH_B_ERROR; 225 } 226 // never trespasses here 227} 228 229 230/** Uninitializes a module depeding on its state */ 231 232static inline int 233uninit_module(module *module) 234{ 235 TRACE(("uninit_module(%s)\n", module->name)); 236 237 switch (module->state) { 238 case MODULE_QUERIED: 239 case MODULE_LOADED: 240 return FSSH_B_NO_ERROR; 241 242 case MODULE_INIT: 243 fssh_panic("Trying to unload module %s which is initializing\n", module->name); 244 return FSSH_B_ERROR; 245 246 case MODULE_UNINIT: 247 fssh_panic("Trying to unload module %s which is un-initializing\n", module->name); 248 return FSSH_B_ERROR; 249 250 case MODULE_READY: 251 { 252 fssh_status_t status; 253 254 module->state = MODULE_UNINIT; 255 256 TRACE(("uninitializing module %s...\n", module->name)); 257 status = module->info->std_ops(FSSH_B_MODULE_UNINIT); 258 TRACE(("...done (%s)\n", strerror(status))); 259 260 if (status == FSSH_B_NO_ERROR) { 261 module->state = MODULE_LOADED; 262 return 0; 263 } 264 265 FATAL(("Error unloading module %s (%s)\n", module->name, fssh_strerror(status))); 266 267 module->state = MODULE_ERROR; 268 module->flags |= FSSH_B_KEEP_LOADED; 269 270 return status; 271 } 272 default: 273 return FSSH_B_ERROR; 274 } 275 // never trespasses here 276} 277 278 279void 280register_builtin_module(struct fssh_module_info *info) 281{ 282 info->flags |= FSSH_B_BUILT_IN_MODULE; 283 // this is an internal flag, it doesn't have to be set by modules itself 284 285 if (create_module(info, "", -1, NULL) != FSSH_B_OK) 286 fssh_dprintf("creation of built-in module \"%s\" failed!\n", info->name); 287} 288 289 290static int 291dump_modules(int argc, char **argv) 292{ 293 hash_iterator iterator; 294 struct module *module; 295 296 hash_rewind(sModulesHash, &iterator); 297 fssh_dprintf("-- known modules:\n"); 298 299 while ((module = (struct module *)hash_next(sModulesHash, &iterator)) != NULL) { 300 fssh_dprintf("%p: \"%s\", \"%s\" (%d), refcount = %d, state = %d\n", 301 module, module->name, module->file, (int)module->offset, (int)module->ref_count, 302 module->state); 303 } 304 305 return 0; 306} 307 308 309// #pragma mark - 310// Exported Kernel API (private part) 311 312 313/** Setup the module structures and data for use - must be called 314 * before any other module call. 315 */ 316 317fssh_status_t 318module_init(kernel_args *args) 319{ 320 fssh_recursive_lock_init(&sModulesLock, "modules rlock"); 321 322 sModulesHash = hash_init(MODULE_HASH_SIZE, 0, module_compare, module_hash); 323 if (sModulesHash == NULL) 324 return FSSH_B_NO_MEMORY; 325 326 fssh_add_debugger_command("modules", &dump_modules, "list all known & loaded modules"); 327 328 return FSSH_B_OK; 329} 330 331 332} // namespace FSShell 333 334 335// #pragma mark - 336// Exported Kernel API (public part) 337 338 339using namespace FSShell; 340 341 342fssh_status_t 343fssh_get_module(const char *path, fssh_module_info **_info) 344{ 345 module *module; 346 fssh_status_t status; 347 348 TRACE(("get_module(%s)\n", path)); 349 350 if (path == NULL) 351 return FSSH_B_BAD_VALUE; 352 353 fssh_recursive_lock_lock(&sModulesLock); 354 355 module = (struct module *)hash_lookup(sModulesHash, path); 356 if (module == NULL) 357 goto err; 358 359 // The state will be adjusted by the call to init_module 360 // if we have just loaded the file 361 if (module->ref_count == 0) 362 status = init_module(module); 363 else 364 status = FSSH_B_OK; 365 366 if (status == FSSH_B_OK) { 367 inc_module_ref_count(module); 368 *_info = module->info; 369 } 370 371 fssh_recursive_lock_unlock(&sModulesLock); 372 return status; 373 374err: 375 fssh_recursive_lock_unlock(&sModulesLock); 376 return FSSH_B_ENTRY_NOT_FOUND; 377} 378 379 380fssh_status_t 381fssh_put_module(const char *path) 382{ 383 module *module; 384 385 TRACE(("put_module(path = %s)\n", path)); 386 387 fssh_recursive_lock_lock(&sModulesLock); 388 389 module = (struct module *)hash_lookup(sModulesHash, path); 390 if (module == NULL) { 391 FATAL(("module: We don't seem to have a reference to module %s\n", path)); 392 fssh_recursive_lock_unlock(&sModulesLock); 393 return FSSH_B_BAD_VALUE; 394 } 395 396 if ((module->flags & FSSH_B_KEEP_LOADED) == 0) { 397 dec_module_ref_count(module); 398 399 if (module->ref_count == 0) 400 uninit_module(module); 401 } 402 403 fssh_recursive_lock_unlock(&sModulesLock); 404 return FSSH_B_OK; 405} 406