Usermode/ld-acess - Disabled PIC (not needed)
[tpg/acess2.git] / Kernel / system.c
1 /*
2  * Acess 2
3  * Architecture Independent System Init
4  * system.c
5  */
6 #define DEBUG   0
7 #include <acess.h>
8
9 #define N_VARIABLES     16
10 #define N_MAX_ARGS      BITS
11
12 // === TYPES ===
13 typedef struct
14 {
15          int    TrueLine;
16          int    nParts;
17         char    **Parts;
18 }       tConfigLine;
19 typedef struct
20 {
21          int    nLines;
22         tConfigLine     Lines[];
23 }       tConfigFile;
24 typedef struct
25 {
26         const char      *Name;          // Name
27          int    MinArgs;        // Minimum number of arguments
28          int    MaxArgs;        // Maximum number of arguments
29         Uint    IntArgs;        // Bitmap of arguments that should be treated as integers
30          int    Index;          // 
31         const char      *OptDefaults[N_MAX_ARGS];       // Default values for optional arguments
32 }       tConfigCommand;
33
34 // === IMPORTS ===
35 extern void     Arch_LoadBootModules(void);
36 extern int      Modules_LoadBuiltins(void);
37 extern void     Modules_SetBuiltinParams(char *Name, char *ArgString);
38 extern void     Debug_SetKTerminal(const char *File);
39
40 // === PROTOTYPES ===
41 void    System_Init(char *Commandline);
42 void    System_ParseCommandLine(char *ArgString);
43 void    System_ExecuteCommandLine(void);
44 void    System_ParseVFS(char *Arg);
45 void    System_ParseModuleArgs(char *Arg);
46 void    System_ParseSetting(char *Arg);
47 void    System_ExecuteScript(void);
48 tConfigFile     *System_Int_ParseFile(char *File);
49
50 // === CONSTANTS ===
51 enum eConfigCommands {
52         CC_LOADMODULE,
53         CC_SPAWN,
54         CC_MOUNT,
55         CC_SYMLINK,
56         CC_MKDIR,
57         CC_OPEN,
58         CC_CLOSE,
59         CC_IOCTL
60 };
61 const tConfigCommand    caConfigCommands[] = {
62         {"module",  1,2, 00, CC_LOADMODULE, {"",NULL}}, // Load a module from a file
63         {"spawn",   1,1, 00, CC_SPAWN, {NULL}},         // Spawn a process
64         // --- VFS ---
65         {"mount",   3,4, 00, CC_MOUNT, {"",0}},         // Mount a device
66         {"symlink", 2,2, 00, CC_SYMLINK, {0}},  // Create a Symbolic Link
67         {"mkdir",   1,1, 00, CC_MKDIR, {0}},            // Create a Directory
68         {"open",    1,2, 00, CC_OPEN,  {(void*)VFS_OPENFLAG_READ,0}},   // Open a file
69         {"close",   1,1, 01, CC_CLOSE, {0}},    // Close an open file
70         {"ioctl",   3,3, 03, CC_IOCTL, {0}},    // Call an IOCtl
71         
72         {"", 0,0, 0, 0, {0}}
73 };
74 #define NUM_CONFIG_COMMANDS     (sizeof(caConfigCommands)/sizeof(caConfigCommands[0]))
75
76 // === GLOBALS ===
77 const char      *gsConfigScript = "/Acess/Conf/BootConf.cfg";
78 char    *argv[32];
79  int    argc;
80
81 // === CODE ===
82 void System_Init(char *CommandLine)
83 {
84         // Parse Kernel's Command Line
85         System_ParseCommandLine(CommandLine);
86         
87         // Initialise modules
88         Log_Log("Config", "Initialising builtin modules...");
89         Modules_LoadBuiltins();
90         Arch_LoadBootModules();
91         
92         System_ExecuteCommandLine();
93         
94         // - Execute the Config Script
95         Log_Log("Config", "Executing config script '%s'", gsConfigScript);
96         System_ExecuteScript();
97         
98         // Set the debug to be echoed to the terminal
99         Log_Log("Config", "Kernel now echoes to VT7 (Ctrl-Alt-F8)");
100         Debug_SetKTerminal("/Devices/VTerm/7");
101 }
102
103 /**
104  * \fn void System_ParseCommandLine(char *ArgString)
105  * \brief Parses the kernel's command line and sets the environment
106  */
107 void System_ParseCommandLine(char *ArgString)
108 {
109          int    i;
110         char    *str;
111         
112         Log_Log("Config", "Kernel Invocation (%p) \"%s\"", ArgString, ArgString);
113         Log_Log("Config", "Kernel Invocation '0x%x 0x%x'", ArgString[0], ArgString[1]);
114         
115         // --- Get Arguments ---
116         str = ArgString;
117         for( argc = 0; argc < 32; argc++ )
118         {
119                 // Eat Whitespace
120                 while(*str == ' ')      str++;
121                 // Check for the end of the string
122                 if(*str == '\0') {      argc--; break;} 
123                 argv[argc] = str;
124                 if(*str == '"') {
125                         while(*str && !(*str == '"' && str[-1] != '\\'))
126                                 str ++;
127                 }
128                 else {
129                         while(*str && *str != ' ')
130                                 str++;
131                 }
132                 if(*str == '\0')        break;  // Check for EOS
133                 *str = '\0';    // Cap off argument string
134                 str ++; // and increment the string pointer
135         }
136         if(argc < 32)
137                 argc ++;        // Count last argument
138         
139         // --- Parse Arguments (Pass 1) ---
140         for( i = 0; i < argc; i++ )
141         {
142                 switch(argv[i][0])
143                 {
144                 // --- VFS ---
145                 // Ignored on this pass
146                 case '/':
147                         break;
148                 
149                 // --- Module Paramaters ---
150                 // -VTerm:Width=640,Height=480,Scrollback=2
151                 case '-':
152                         System_ParseModuleArgs( argv[i] );
153                         break;
154                 // --- Config Options ---
155                 // SCRIPT=/Acess/Conf/BootConf.cfg
156                 default:
157                         System_ParseSetting( argv[i] );
158                         break;
159                 }
160         }
161 }
162
163 void System_ExecuteCommandLine(void)
164 {
165          int    i;
166         for( i = 0; i < argc; i++ )
167         {
168                 Log("argv[%i] = '%s'", i, argv[i]);
169                 switch(argv[i][0])
170                 {
171                 // --- VFS ---
172                 // Mount    /System=ext2:/Devices/ATA/A1
173                 // Symlink  /Acess=/System/Acess2
174                 case '/':
175                         System_ParseVFS( argv[i] );
176                         break;
177                 }
178         }
179 }
180
181 /**
182  * \fn void System_ParseVFS(char *Arg)
183  */
184 void System_ParseVFS(char *Arg)
185 {
186         char    *value;
187          int    fd;
188         
189         value = Arg;
190         // Search for the '=' token
191         while( *value && *value != '=' )
192                 value++;
193         
194         // Check if the equals was found
195         if( *value == '\0' ) {
196                 Log_Warning("Config", "Expected '=' in the string '%s'", Arg);
197                 return ;
198         }
199         
200         // Edit string
201         *value = '\0';  value ++;
202         
203         // Check assignment type
204         // - Symbolic Link <link>=<destination>
205         if(value[0] == '/')
206         {
207                 Log_Log("Config", "Symbolic link '%s' pointing to '%s'", Arg, value);
208                 VFS_Symlink(Arg, value);
209         }
210         // - Mount <mountpoint>=<fs>:<device>
211         else
212         {
213                 char    *dev = value;
214                 // Find colon
215                 while(*dev && *dev != ':')      dev++;
216                 if(*dev) {
217                         *dev = '\0';
218                         dev++;  // Eat ':'
219                 }
220                 // Create Mountpoint
221                 if( (fd = VFS_Open(Arg, 0)) == -1 ) {
222                         Log_Log("Config", "Creating directory '%s'", Arg, value);
223                         VFS_MkDir( Arg );
224                 } else {
225                         VFS_Close(fd);
226                 }
227                 // Mount
228                 Log_Log("Config", "Mounting '%s' to '%s' ('%s')", dev, Arg, value);
229                 VFS_Mount(dev, Arg, value, "");
230         }
231 }
232
233 /**
234  * \brief Parse a module argument string
235  * \param Arg   Argument string
236  */
237 void System_ParseModuleArgs(char *Arg)
238 {
239         char    *name, *args;
240          int    i;
241         
242         // Remove '-'   
243         name = Arg + 1;
244         
245         // Find the start of the args
246         i = strpos(name, ':');
247         if( i == -1 ) {
248                 Log_Warning("Config", "Module spec with no arguments");
249                 #if 1
250                 return ;
251                 #else
252                 i = strlen(name);
253                 args = name + i;
254                 #endif
255         }
256         else {
257                 name[i] = '\0';
258                 args = name + i + 1;
259         }
260         
261         Log_Log("Config", "Setting boot parameters for '%s' to '%s'", name, args);
262         Modules_SetBuiltinParams(name, args);
263 }
264
265 /**
266  * \fn void System_ParseSetting(char *Arg)
267  */
268 void System_ParseSetting(char *Arg)
269 {
270         char    *value;
271         value = Arg;
272
273         // Search for the '=' token
274         while( *value && *value != '=' )
275                 value++;
276         
277         // Check for boolean/flag (no '=')
278         if(*value == '\0')
279         {
280                 //if(strcmp(Arg, "") == 0) {
281                 //} else {
282                         Log_Warning("Config", "Kernel flag '%s' is not recognised", Arg);
283                 //}
284         }
285         else
286         {
287                 *value = '\0';  // Remove '='
288                 value ++;       // and eat it's position
289                 
290                 if(strcmp(Arg, "SCRIPT") == 0) {
291                         Log_Log("Config", "Config Script: '%s'", value);
292                         if(strlen(value) == 0)
293                                 gsConfigScript = NULL;
294                         else
295                                 gsConfigScript = value;
296                 } else {
297                         Log_Warning("Config", "Kernel config setting '%s' is not recognised", Arg);
298                 }
299                 
300         }
301 }
302
303 /**
304  * \fn void System_ExecuteScript()
305  * \brief Reads and parses the boot configuration script
306  */
307 void System_ExecuteScript(void)
308 {
309          int    fp;
310          int    fLen = 0;
311          int    i, j, k;
312          int    val;
313          int    result = 0;
314          int    variables[N_VARIABLES];
315          int    bReplaced[N_MAX_ARGS];
316         char    *fData;
317         char    *jmpTarget;
318         tConfigFile     *file;
319         tConfigLine     *line;
320         
321         // Open Script
322         fp = VFS_Open(gsConfigScript, VFS_OPENFLAG_READ);
323         if(fp == -1) {
324                 Log_Warning("Config", "Passed script '%s' does not exist", gsConfigScript);
325                 return;
326         }
327         
328         // Get length
329         VFS_Seek(fp, 0, SEEK_END);
330         fLen = VFS_Tell(fp);
331         Log_Debug("System", "VFS_Tell(%i) = %i", fp, fLen);
332         VFS_Seek(fp, 0, SEEK_SET);
333         // Read into memory buffer
334         fData = malloc(fLen+1);
335         VFS_Read(fp, fLen, fData);
336         fData[fLen] = '\0';
337         VFS_Close(fp);
338         
339         
340         // Parse File
341         file = System_Int_ParseFile(fData);
342         
343         // Parse each line
344         for( i = 0; i < file->nLines; i++ )
345         {
346                 line = &file->Lines[i];
347                 if( line->nParts == 0 ) continue;       // Skip blank
348                 
349                 if(line->Parts[0][0] == ':')    continue;       // Ignore labels
350                 
351                 // Prescan and eliminate variables
352                 for( j = 1; j < line->nParts; j++ )
353                 {
354                         Log_Debug("Config", "Arg #%i is '%s'", j, line->Parts[j]);
355                         bReplaced[j] = 0;
356                         if( line->Parts[j][0] != '$' )  continue;
357                         if( line->Parts[j][1] == '?' ) {
358                                 val = result;
359                         }
360                         else {
361                                 val = atoi( &line->Parts[j][1] );
362                                 if( val < 0 || val > N_VARIABLES )      continue;
363                                 val = variables[ val ];
364                         }
365                         Log_Debug("Config", "Replaced arg %i ('%s') with 0x%x", j, line->Parts[j], val);
366                         line->Parts[j] = malloc( BITS/8+2+1 );
367                         sprintf(line->Parts[j], "0x%x", val);
368                         bReplaced[j] = 1;
369                 }
370                 
371                 // Find the command name
372                 for( j = 0; j < NUM_CONFIG_COMMANDS; j++ )
373                 {
374                         const char      *args[N_MAX_ARGS];
375                         
376                         if(strcmp(line->Parts[0], caConfigCommands[j].Name) != 0)       continue;
377                         
378                         Log_Debug("Config", "Command '%s', %i args passed", line->Parts[0], line->nParts-1);
379                         
380                         // Check against minimum argument count
381                         if( line->nParts - 1 < caConfigCommands[j].MinArgs ) {
382                                 Log_Warning("Config",
383                                         "Configuration command '%s' requires at least %i arguments, %i given",
384                                         caConfigCommands[j].Name, caConfigCommands[j].MinArgs, line->nParts-1
385                                         );
386                                 break;
387                         }
388                         
389                         // Check for extra arguments
390                         if( line->nParts - 1 > caConfigCommands[j].MaxArgs ) {
391                                 Log_Warning("Config",
392                                         "Configuration command '%s' takes at most %i arguments, %i given",
393                                         caConfigCommands[j].Name, caConfigCommands[j].MaxArgs, line->nParts-1
394                                         );
395                                 break;
396                         }
397                         
398                         // Fill in defaults
399                         for( k = caConfigCommands[j].MaxArgs-1; k > line->nParts - 1; k-- ) {
400                                 args[k] = caConfigCommands[j].OptDefaults[k];
401                         }
402                         
403                         // Convert arguments to integers
404                         for( k = line->nParts-1; k--; )
405                         {
406                                 if( k < 32 && (caConfigCommands[j].IntArgs & (1 << k)) ) {
407                                         args[k] = (const char *)(Uint)atoi(line->Parts[k+1]);
408                                 }
409                                 else {
410                                         args[k] = (char *)line->Parts[k+1];
411                                 }
412                                 Log_Debug("Config", "args[%i] = %p", k, args[k]);
413                         }
414                         switch( (enum eConfigCommands) caConfigCommands[j].Index )
415                         {
416                         case CC_LOADMODULE:
417                                 result = Module_LoadFile( args[0], args[1] );
418                                 break;
419                         case CC_SPAWN:
420                                 result = Proc_Spawn( args[0] );
421                                 break;
422                         case CC_MOUNT:
423                                 result = VFS_Mount( args[0], args[1], args[2], args[3] );
424                                 break;
425                         case CC_SYMLINK:
426                                 result = VFS_Symlink( args[0], args[1] );
427                                 break;
428                         case CC_OPEN:
429                                 result = VFS_Open( args[0], (Uint)args[1] );
430                                 break;
431                         case CC_CLOSE:
432                                 VFS_Close( (Uint)args[0] );
433                                 result = 0;
434                                 break;
435                         case CC_MKDIR:
436                                 result = VFS_MkDir( args[0] );
437                                 break;
438                         case CC_IOCTL:
439                                 result = VFS_IOCtl( (Uint)args[0], (Uint)args[1], (void *)args[2] );
440                                 break;
441                         }
442                         Log_Debug("Config", "result = %i", result);
443                         break;
444                 }
445                 if( j < NUM_CONFIG_COMMANDS )   continue;
446                         
447                 // --- State and Variables ---
448                 if(strcmp(line->Parts[0], "set") == 0)
449                 {
450                          int    to, value;
451                         if( line->nParts-1 != 2 ) {
452                                 Log_Warning("Config", "Configuration command 'set' requires 2 arguments, %i given",
453                                         line->nParts-1);
454                                 continue;
455                         }
456                         
457                         to = atoi(line->Parts[1]);
458                         value = atoi(line->Parts[2]);
459                         
460                         variables[to] = value;
461                         result = value;
462                 }
463                 // if <val1> <op> <val2> <dest>
464                 else if(strcmp(line->Parts[0], "if") == 0)
465                 {
466                         if( line->nParts-1 != 4 ) {
467                                 Log_Warning("Config", "Configuration command 'if' requires 4 arguments, %i given",
468                                         line->nParts-1);
469                         }
470                         
471                         result = atoi(line->Parts[1]);
472                         val = atoi(line->Parts[3]);
473                         
474                         jmpTarget = line->Parts[4];
475                         
476                         Log_Log("Config", "IF 0x%x %s 0x%x THEN GOTO %s",
477                                 result, line->Parts[2], val, jmpTarget);
478                         
479                         if( strcmp(line->Parts[2], "<" ) == 0 ) {
480                                 if( result < val )      goto jumpToLabel;
481                         }
482                         else if( strcmp(line->Parts[2], "<=") == 0 ) {
483                                 if( result <= val )     goto jumpToLabel;
484                         }
485                         else if( strcmp(line->Parts[2], ">" ) == 0 ) {
486                                 if (result > val )      goto jumpToLabel;
487                         }
488                         else if( strcmp(line->Parts[2], ">=") == 0 ) {
489                                 if( result >= val )     goto jumpToLabel;
490                         }
491                         else if( strcmp(line->Parts[2],  "=") == 0 ) {
492                                 if( result == val )     goto jumpToLabel;
493                         }
494                         else if( strcmp(line->Parts[2], "!=") == 0 ) {
495                                 if( result != val )     goto jumpToLabel;
496                         }
497                         else {
498                                 Log_Warning("Config", "Unknown comparision '%s' in `if`", line->Parts[2]);
499                         }
500                         
501                 }
502                 else if(strcmp(line->Parts[0], "goto") == 0) {
503                         if( line->nParts-1 != 1 ) {
504                                 Log_Warning("Config", "Configuration command 'goto' requires 1 arguments, %i given",
505                                         line->nParts-1);
506                         }
507                         jmpTarget = line->Parts[1];
508                 
509                 jumpToLabel:
510                         for( j = 0; j < file->nLines; j ++ )
511                         {
512                                 if(file->Lines[j].nParts == 0)
513                                         continue;
514                                 if(file->Lines[j].Parts[0][0] != ':')
515                                         continue;
516                                 if( strcmp(file->Lines[j].Parts[0]+1, jmpTarget) == 0)
517                                         break;
518                         }
519                         if( j == file->nLines )
520                                 Log_Warning("Config", "Unable to find label '%s'", jmpTarget);
521                         else
522                                 i = j;
523                 }
524                 else {
525                         Log_Warning("Config", "Unknown configuration command '%s' on line %i",
526                                 line->Parts[0],
527                                 line->TrueLine
528                                 );
529                 }
530         }
531         
532         // Clean up after ourselves
533         for( i = 0; i < file->nLines; i++ ) {
534                 if( file->Lines[i].nParts == 0 )        continue;       // Skip blank
535                 for( j = 0; j < file->Lines[i].nParts; j++ ) {
536                         if(IsHeap(file->Lines[i].Parts[j]))
537                                 free(file->Lines[i].Parts[j]);
538                 }
539                 free( file->Lines[i].Parts );
540         }
541         
542         // Free data
543         free( file );
544         free( fData );
545 }
546
547 /**
548  * \brief Parses a config file
549  * \param FileData      Read/Write buffer containing the config file data
550  *                  (will be modified)
551  * \return ::tConfigFile structure that represents the original contents
552  *         of \a FileData
553  */
554 tConfigFile     *System_Int_ParseFile(char *FileData)
555 {
556         char    *ptr;
557         char    *start;
558          int    nLines = 1;
559          int    i, j;
560         tConfigFile     *ret;
561         
562         ENTER("pFileData", FileData);
563         
564         // Prescan and count the number of lines
565         for(ptr = FileData; *ptr; ptr++)
566         {               
567                 if(*ptr != '\n')        continue;
568                 
569                 if(ptr == FileData) {
570                         nLines ++;
571                         continue;
572                 }
573                 
574                 // Escaped EOL
575                 if(ptr[-1] == '\\')     continue;
576                 
577                 nLines ++;
578         }
579         
580         LOG("nLines = %i", nLines);
581         
582         // Ok so we have `nLines` lines, now to allocate our return
583         ret = malloc( sizeof(tConfigFile) + sizeof(tConfigLine)*nLines );
584         ret->nLines = nLines;
585         
586         // Read the file for real
587         for(
588                 ptr = FileData, i = 0;
589                 *ptr;
590                 i++
591                 )
592         {
593                 start = ptr;
594                 
595                 ret->Lines[i].nParts = 0;
596                 ret->Lines[i].Parts = NULL;
597                 
598                 // Count parts
599                 for(;;)
600                 {
601                         // Read leading whitespace
602                         while( *ptr == '\t' || *ptr == ' ' )    ptr++;
603                         
604                         // End of line/file
605                         if( *ptr == '\0' || *ptr == '\n' ) {
606                                 if(*ptr == '\n')        ptr ++;
607                                 break;
608                         }
609                         // Comment
610                         if( *ptr == '#' || *ptr == ';' ) {
611                                 while( *ptr && *ptr != '\n' )   ptr ++;
612                                 if(*ptr == '\n')        ptr ++;
613                                 break;
614                         }
615                         
616                         ret->Lines[i].nParts ++;
617                         // Quoted
618                         if( *ptr == '"' ) {
619                                 ptr ++;
620                                 while( *ptr && !(*ptr == '"' && ptr[-1] == '\\') && *ptr != '\n' )
621                                         ptr++;
622                                 continue;
623                         }
624                         // Unquoted
625                         while( *ptr && !(*ptr == '\t' || *ptr == ' ') && *ptr != '\n' )
626                                 ptr++;
627                 }
628                 
629                 LOG("ret->Lines[%i].nParts = %i", i, ret->Lines[i].nParts);
630                 
631                 if( ret->Lines[i].nParts == 0 ) {
632                         ret->Lines[i].Parts = NULL;
633                         continue;
634                 }
635                 
636                 // Allocate part list
637                 ret->Lines[i].Parts = malloc( sizeof(char*) * ret->Lines[i].nParts );
638                 
639                 // Fill list
640                 for( ptr = start, j = 0; ; j++ )
641                 {
642                         // Read leading whitespace
643                         while( *ptr == '\t' || *ptr == ' ' )    ptr++;
644                         
645                         // End of line/file
646                         if( *ptr == '\0' || *ptr == '\n' ) {
647                                 if(*ptr == '\n')        ptr ++;
648                                 break;
649                         }
650                         // Comment
651                         if( *ptr == '#' || *ptr == ';' ) {
652                                 while( *ptr && *ptr != '\n' )   ptr ++;
653                                 if(*ptr == '\n')        ptr ++;
654                                 break;
655                         }
656                         
657                         ret->Lines[i].Parts[j] = ptr;
658                         
659                         // Quoted
660                         if( *ptr == '"' ) {
661                                 ptr ++;
662                                 ret->Lines[i].Parts[j] = ptr;
663                                 while( *ptr && !(*ptr == '"' && ptr[-1] == '\\') && *ptr != '\n' )
664                                         ptr++;
665                         }
666                         // Unquoted
667                         else {
668                                 while( *ptr != '\t' && *ptr != ' ' && *ptr != '\n' )
669                                         ptr++;
670                         }
671                         
672                         // Break if we have reached NULL
673                         if( *ptr == '\0' ) {
674                                 LOG("ret->Lines[%i].Parts[%i] = '%s'", i, j, ret->Lines[i].Parts[j]);
675                                 break;
676                         }
677                         if( *ptr == '\n' ) {
678                                 *ptr = '\0';
679                                 LOG("ret->Lines[%i].Parts[%i] = '%s'", i, j, ret->Lines[i].Parts[j]);
680                                 ptr ++;
681                                 break;
682                         }
683                         *ptr = '\0';    // Cap off string
684                         LOG("ret->Lines[%i].Parts[%i] = '%s'", i, j, ret->Lines[i].Parts[j]);
685                         ptr ++; // And increment for the next round
686                 }
687         }
688         
689         if( i < ret->nLines ) {
690                 ret->Lines[i].nParts = 0;
691                 ret->Lines[i].Parts = NULL;
692                 Log_Log("System", "Cleaning up final empty line");
693         }
694         
695         LEAVE('p', ret);
696         return ret;
697 }

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