Just a little cleanup
[tpg/acess2.git] / KernelLand / Kernel / modules.c
1 /*
2  * Acess2
3  * - Module Loader
4  */
5 #define DEBUG   0
6 #include <acess.h>
7 #include <modules.h>
8
9 #define USE_EDI 0
10 #define USE_UDI 0
11 #define MODULE_FLAG_LOADERROR   0x1
12
13 // === PROTOTYPES ===
14  int    Module_int_Initialise(tModule *Module, const char *ArgString);
15 void    Modules_int_GetBuiltinArray(void);
16 void    Modules_LoadBuiltins(void);
17 void    Modules_SetBuiltinParams(const char *Name, char *ArgString);
18  int    Modules_InitialiseBuiltin(const char *Name);
19 // int  Module_RegisterLoader(tModuleLoader *Loader);
20 // int  Module_LoadMem(void *Buffer, Uint Length, char *ArgString);
21 // int  Module_LoadFile(char *Path, char *ArgString);
22  int    Module_int_ResolveDeps(tModule *Info);
23  int    Module_IsLoaded(const char *Name);
24 // int  Module_EnsureLoaded(const char *Name);
25
26 // === EXPORTS ===
27 EXPORT(Module_RegisterLoader);
28
29 // === IMPORTS ===
30 #if USE_UDI
31 extern int      UDI_LoadDriver(void *Base);
32 #endif
33 extern void     StartupPrint(const char *Str);
34 extern tModule  gKernelModules;
35 extern tModule  gKernelModulesEnd;
36
37 // === GLOBALS ===
38  int    giNumBuiltinModules = 0;
39 tShortSpinlock  glModuleSpinlock;
40 tModule *gLoadedModules = NULL;
41 tModuleLoader   *gModule_Loaders = NULL;
42 tModule *gLoadingModules = NULL;
43 tModule **gapBuiltinModules = NULL;
44 char    **gasBuiltinModuleArgs;
45
46 // === CODE ===
47 /**
48  * \brief Initialises a module
49  * \param Module        Pointer to the module header
50  * \param ArgString     Comma separated list of module arguments
51  * \return Zero on success, eModuleErrors or -1 on error
52  * \retval -1   Returned if a dependency fails, or a circular dependency
53  *              exists.
54  * \retval 0    Returned on success
55  * \retval >0   Error code form the module's initialisation function
56  */
57 int Module_int_Initialise(tModule *Module, const char *ArgString)
58 {
59          int    i, j;
60          int    ret;
61         const char      **deps;
62         char    **args;
63         tModule *mod;
64         
65         ENTER("pModule", Module);
66         LOG("Module->Magic = 0x%x", Module->Magic);
67         if(Module->Magic != MODULE_MAGIC) {
68                 Log_Warning(
69                         "Module",
70                         "Module %p is no a valid Acess2 module (0x%08x != 0x%08x)",
71                         Module, Module->Magic, MODULE_MAGIC
72                         );
73                 LEAVE('i', MODULE_ERR_BADMODULE);
74                 return MODULE_ERR_BADMODULE;
75         }
76         LOG("Module->Name = %p \"%s\"", Module->Name, Module->Name);
77         
78         if(Module->Arch != MODULE_ARCH_ID) {
79                 Log_Warning(
80                         "Module",
81                         "Module %p (%s) is for another architecture (%i)",
82                         Module, Module->Name, Module->Arch
83                         );
84                 LEAVE('i', MODULE_ERR_BADMODULE);
85                 return MODULE_ERR_BADMODULE;
86         }
87
88         LOG("Module->Flags = %x", Module->Flags);       
89         if(Module->Flags & MODULE_FLAG_LOADERROR ) {
90                 Log_Warning("Module", "%s has already attempted to load and encountered errors", Module->Name);
91                 LEAVE('i', MODULE_ERR_MISC);
92                 return MODULE_ERR_MISC;
93         }
94         
95         deps = Module->Dependencies;
96         
97         // Check if the module has been loaded
98         for( mod = gLoadedModules; mod; mod = mod->Next )
99         {
100                 if(mod == Module)       LEAVE_RET('i', 0);
101         }
102         
103         // Add to the "loading" (prevents circular deps)
104         Module->Next = gLoadingModules;
105         gLoadingModules = Module;
106         
107         // Scan dependency list
108         for( j = 0; deps && deps[j]; j++ )
109         {
110                 // Check if the module is already loaded
111                 for( mod = gLoadedModules; mod; mod = mod->Next )
112                 {
113                         if(strcmp(deps[j], mod->Name) == 0)
114                                 break;
115                 }
116                 if( mod )       continue;       // Dependency is loaded, check the rest
117                 
118                 // Ok, check if it's loading
119                 for( mod = gLoadingModules->Next; mod; mod = mod->Next )
120                 {
121                         if(strcmp(deps[j], mod->Name) == 0)
122                                 break;
123                 }
124                 if( mod ) {
125                         Log_Warning("Module", "Circular dependency detected (%s and %s)",
126                                 mod->Name, Module->Name);
127                         LEAVE_RET('i', -1);
128                 }
129                 
130                 // So, if it's not loaded, we better load it then
131                 for( i = 0; i < giNumBuiltinModules; i ++ )
132                 {
133                         if( strcmp(deps[j], gapBuiltinModules[i]->Name) == 0 )
134                                 break;
135                 }
136                 if( i == giNumBuiltinModules ) {
137                         Log_Warning("Module", "Dependency '%s' for module '%s' failed",
138                                 deps[j], Module->Name);
139                         return -1;
140                 }
141                 
142                 // Dependency is not loaded, so load it
143                 ret = Module_int_Initialise(
144                         gapBuiltinModules[i],
145                         gasBuiltinModuleArgs ? gasBuiltinModuleArgs[i] : NULL
146                         );
147                 if( ret )
148                 {
149                         // The only "ok" error is NOTNEEDED
150                         if(ret != MODULE_ERR_NOTNEEDED)
151                                 LEAVE_RET('i', -1);
152                 }
153         }
154         
155         // All Dependencies OK? Initialise
156         StartupPrint(Module->Name);
157         Log_Log("Module", "Starting %p '%s' v%i.%i",
158                 Module, Module->Name,
159                 Module->Version >> 8, Module->Version & 0xFF
160                 );
161         
162         if( ArgString )
163                 args = str_split( ArgString, ',' );
164         else
165                 args = NULL;
166         
167         ret = Module->Init(args);
168         
169         if(args)        free(args);
170         
171         // Remove from loading list
172         gLoadingModules = gLoadingModules->Next;
173         
174         if( ret != MODULE_ERR_OK ) {
175                 switch(ret)
176                 {
177                 case MODULE_ERR_MISC:
178                         Log_Warning("Module", "Unable to load, reason: Miscelanious");
179                         break;
180                 case MODULE_ERR_NOTNEEDED:
181 //                      Log_Debug("Module", "Unable to load, reason: Module not needed");
182                         break;
183                 case MODULE_ERR_MALLOC:
184                         Log_Warning("Module", "Unable to load, reason: Error in malloc/realloc/calloc, probably not good");
185                         break;
186                 default:
187                         Log_Warning("Module", "Unable to load reason - Unknown code %i", ret);
188                         break;
189                 }
190                 Module->Flags |= MODULE_FLAG_LOADERROR;
191                 LEAVE_RET('i', ret);
192                 return ret;
193         }
194         LOG("ret = %i", ret);
195         
196         // Add to loaded list
197         SHORTLOCK( &glModuleSpinlock );
198         Module->Next = gLoadedModules;
199         gLoadedModules = Module;
200         SHORTREL( &glModuleSpinlock );
201         
202         LEAVE_RET('i', 0);
203 }
204
205 /**
206  * \brief Scans the builtin modules and creates an array of them
207  */
208 void Modules_int_GetBuiltinArray(void)
209 {
210          int    i;
211         tModule *module;
212         
213         // Count
214         module = &gKernelModules;
215         i = 0;
216         while( (tVAddr)module < (tVAddr)&gKernelModulesEnd )
217         {
218                 if(module->Magic == MODULE_MAGIC) {
219                         i ++;
220                         module ++;
221                 }
222                 else
223                         module = (void*)( (tVAddr)module + 4 );
224         }
225         
226         // Create
227         giNumBuiltinModules = i;
228         gasBuiltinModuleArgs = calloc( giNumBuiltinModules, sizeof(char*) );
229         gapBuiltinModules = malloc( giNumBuiltinModules * sizeof(tModule*) );
230         
231         
232         // Fill
233         module = &gKernelModules;
234         i = 0;
235         while( (tVAddr)module < (tVAddr)&gKernelModulesEnd )
236         {
237                 if(module->Magic == MODULE_MAGIC) {
238                         gapBuiltinModules[i] = module;
239                         i ++;
240                         module ++;
241                 }
242                 else
243                         module = (void*)( (tVAddr)module + 4 );
244         }
245 }
246
247 /**
248  * \brief Initialises builtin modules
249  */
250 void Modules_LoadBuiltins()
251 {
252          int    i;
253         
254         if( !gapBuiltinModules )
255                 Modules_int_GetBuiltinArray();
256         
257         for( i = 0; i < giNumBuiltinModules; i++ )
258         {
259                 Module_int_Initialise(
260                         gapBuiltinModules[i],
261                         (gasBuiltinModuleArgs ? gasBuiltinModuleArgs[i] : NULL)
262                         );
263         }
264         
265         if( gasBuiltinModuleArgs != NULL )
266                 free(gasBuiltinModuleArgs);
267 }
268
269 /**
270  * \brief Initialise a builtin module given it's name
271  * 
272  * E.g. Used by VTerm to load an alternate video driver at runtime
273  */
274 int Modules_InitialiseBuiltin(const char *Name)
275 {
276          int    i;
277         
278         // Check if it's loaded
279         if( Module_IsLoaded(Name) )
280                 return 0;
281         
282         if( !gapBuiltinModules )
283                 Modules_int_GetBuiltinArray();
284         
285         for( i = 0; i < giNumBuiltinModules; i++ )
286         {
287                 if( strcmp(gapBuiltinModules[i]->Name, Name) == 0 ) {
288                         return Module_int_Initialise(gapBuiltinModules[i],
289                                 (gasBuiltinModuleArgs ? gasBuiltinModuleArgs[i] : NULL)
290                                 );
291                 }
292         }
293         return -1;
294 }
295
296 /**
297  * \brief Sets the parameters for a builtin module
298  */
299 void Modules_SetBuiltinParams(const char *Name, char *ArgString)
300 {
301          int    i;
302         
303         if( gasBuiltinModuleArgs == NULL )
304         {
305                 Modules_int_GetBuiltinArray();
306         }
307         
308         // I hate expensive scans
309         for( i = 0; i < giNumBuiltinModules; i++ )
310         {
311                 if(strcmp( gapBuiltinModules[i]->Name, Name ) == 0) {
312                         gasBuiltinModuleArgs[i] = ArgString;
313                         return ;
314                 }
315         }
316         
317         Log_Warning("Modules", "Unknown builtin kernel module '%s'", Name);
318 }
319
320 /**
321  * \brief Registers a tModuleLoader with the kernel
322  * \param Loader        Pointer to loader structure (must be persistent)
323  */
324 int Module_RegisterLoader(tModuleLoader *Loader)
325 {
326         if(!Loader)     return 1;
327         
328         Loader->Next = gModule_Loaders;
329         gModule_Loaders = Loader;
330         
331         return 0;
332 }
333
334 /**
335  * \fn int Module_LoadMem(void *Buffer, Uint Length, char *ArgString)
336  * \brief Load a module from a memory location
337  */
338 int Module_LoadMem(void *Buffer, Uint Length, const char *ArgString)
339 {
340         char    path[VFS_MEMPATH_SIZE];
341         
342         VFS_GetMemPath(path, Buffer, Length);
343         
344         return Module_LoadFile( path, ArgString );
345 }
346
347 /**
348  * \fn int Module_LoadFile(const char *Path, const char *ArgString)
349  * \brief Load a module from a file
350  */
351 int Module_LoadFile(const char *Path, const char *ArgString)
352 {
353         void    *base;
354         tModule *info;
355         tModuleLoader   *loader = NULL;
356         
357         // Load Binary
358         base = Binary_LoadKernel(Path);
359         
360         // Error check
361         if(base == NULL) {
362                 Log_Warning("Module", "Module_LoadFile - Unable to load '%s'", Path);
363                 return 0;
364         }
365
366         // TODO: I need a way of relocating the dependencies before everything else, so
367         // they can be resolved before any other link errors
368         if( !Binary_Relocate(base) ) {
369                 Log_Warning("Relocation of module %s failed", Path);
370                 Binary_Unload(base);
371                 return 0;
372         }
373         
374         // Check for Acess Driver
375         if( Binary_FindSymbol(base, "DriverInfo", (Uint*)&info ) == 0 )
376         {
377                 for( loader = gModule_Loaders; loader; loader = loader->Next)
378                 {
379                         if( loader->Detector(base) )
380                                 break;
381                 }
382                 
383                 // Unknown module type?, return error
384                 if( !loader ) {
385                         Binary_Unload(base);
386                         Log_Warning("Module", "Module '%s' does not have a Module Info struct", Path);
387                         return 0;
388                 }
389         }
390
391         if( !Module_int_ResolveDeps(info) ) {
392                 Log_Warning("Dependencies not met for '%s'", Path);
393                 Binary_Unload(base);
394                 return 0;
395         }
396
397         // Initialise (and register)
398         if( loader ? loader->Loader(base) : Module_int_Initialise( info, ArgString ) )
399         {
400                 Binary_Unload(base);
401                 return 0;
402         }
403         
404         return 1;
405 }
406
407 /**
408  * \fn int Module_int_ResolveDeps(tModule *Info)
409  * \brief Resolves the dependencies
410  * \todo Implement
411  * \note Currently does not resolve the dependencies, just checks them
412  */
413 int Module_int_ResolveDeps(tModule *Info)
414 {
415         const char      **names = Info->Dependencies;
416         
417         // Walk dependencies array
418         for( ; *names; names++ )
419         {
420                 // Check if the module is loaded
421                 if( !Module_IsLoaded(*names) ) {
422                         Log_Warning("Module", "Module `%s' requires `%s', which is not loaded\n", Info->Name, *names);
423                         return 0;
424                 }
425         }
426         return 1;
427 }
428
429 /**
430  * \fn int Module_IsLoaded(const char *Name)
431  * \brief Checks if a module is loaded
432  * \param Name  Name of module to find
433  */
434 int Module_IsLoaded(const char *Name)
435 {
436         tModule *mod = gLoadedModules;
437         
438         // Scan loaded list
439         for( ; mod; mod = mod->Next )
440         {
441                 // If found, return true
442                 if(strcmp(mod->Name, Name) == 0)
443                         return 1;
444         }
445         // not found - return false
446         return 0;
447 }
448
449 /**
450  * \brief Load a module if needed
451  */
452 int Module_EnsureLoaded(const char *Name)
453 {
454         if( Module_IsLoaded(Name) )
455                 return 0;
456
457         if( Modules_InitialiseBuiltin(Name) == 0 )
458                 return 0;
459
460         // TODO: Load from a file?
461
462         return -1;
463 }

UCC git Repository :: git.ucc.asn.au