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 32 namespace FSShell { 33 34 35 #define MODULE_HASH_SIZE 16 36 37 enum 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 51 struct 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 */ 70 static 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 */ 75 static hash_table *sModulesHash; 76 77 78 /** calculates hash for a module using its name */ 79 80 static uint32_t 81 module_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 98 static int 99 module_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 110 static inline void 111 inc_module_ref_count(struct module *module) 112 { 113 module->ref_count++; 114 } 115 116 117 static inline void 118 dec_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 128 static fssh_status_t 129 create_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 183 static inline fssh_status_t 184 init_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 232 static inline int 233 uninit_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 279 void 280 register_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 290 static int 291 dump_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 317 fssh_status_t 318 module_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 339 using namespace FSShell; 340 341 342 fssh_status_t 343 fssh_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 374 err: 375 fssh_recursive_lock_unlock(&sModulesLock); 376 return FSSH_B_ENTRY_NOT_FOUND; 377 } 378 379 380 fssh_status_t 381 fssh_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