loader: updates from review
[unleashed.git] / usr / src / boot / sys / boot / forth / support.4th
blob2d2fe0342610b5a4e71c1778b072a33c2b7e1378
1 \ Copyright (c) 1999 Daniel C. Sobral <dcs@FreeBSD.org>
2 \ All rights reserved.
3
4 \ Redistribution and use in source and binary forms, with or without
5 \ modification, are permitted provided that the following conditions
6 \ are met:
7 \ 1. Redistributions of source code must retain the above copyright
8 \    notice, this list of conditions and the following disclaimer.
9 \ 2. Redistributions in binary form must reproduce the above copyright
10 \    notice, this list of conditions and the following disclaimer in the
11 \    documentation and/or other materials provided with the distribution.
13 \ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 \ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 \ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 \ ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 \ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 \ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 \ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 \ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 \ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 \ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 \ SUCH DAMAGE.
25 \ $FreeBSD$
27 \ Loader.rc support functions:
29 \ initialize ( addr len -- )    as above, plus load_conf_files
30 \ load_conf ( addr len -- )     load conf file given
31 \ include_bootenv ( -- )        load bootenv.rc
32 \ include_conf_files ( -- )     load all conf files in load_conf_files
33 \ print_syntax_error ( -- )     print line and marker of where a syntax
34 \                               error was detected
35 \ print_line ( -- )             print last line processed
36 \ load_kernel ( -- )            load kernel
37 \ load_modules ( -- )           load modules flagged
39 \ Exported structures:
41 \ string                        counted string structure
42 \       cell .addr                      string address
43 \       cell .len                       string length
44 \ module                        module loading information structure
45 \       cell module.flag                should we load it?
46 \       string module.name              module's name
47 \       string module.loadname          name to be used in loading the module
48 \       string module.type              module's type (file | hash | rootfs)
49 \       string module.hash              module's sha1 hash
50 \       string module.args              flags to be passed during load
51 \       string module.largs             internal argument list
52 \       string module.beforeload        command to be executed before load
53 \       string module.afterload         command to be executed after load
54 \       string module.loaderror         command to be executed if load fails
55 \       cell module.next                list chain
57 \ Exported global variables;
59 \ string conf_files             configuration files to be loaded
60 \ cell modules_options          pointer to first module information
61 \ value verbose?                indicates if user wants a verbose loading
62 \ value any_conf_read?          indicates if a conf file was succesfully read
64 \ Other exported words:
65 \    note, strlen is internal
66 \ strdup ( addr len -- addr' len)                       similar to strdup(3)
67 \ strcat ( addr len addr' len' -- addr len+len' )       similar to strcat(3)
68 \ s' ( | string' -- addr len | )                        similar to s"
69 \ rudimentary structure support
71 \ Exception values
73 1 constant ESYNTAX
74 2 constant ENOMEM
75 3 constant EFREE
76 4 constant ESETERROR    \ error setting environment variable
77 5 constant EREAD        \ error reading
78 6 constant EOPEN
79 7 constant EEXEC        \ XXX never catched
80 8 constant EBEFORELOAD
81 9 constant EAFTERLOAD
83 \ I/O constants
85 0 constant SEEK_SET
86 1 constant SEEK_CUR
87 2 constant SEEK_END
89 0 constant O_RDONLY
90 1 constant O_WRONLY
91 2 constant O_RDWR
93 \ Crude structure support
95 : structure:
96   create here 0 , ['] drop , 0
97   does> create here swap dup @ allot cell+ @ execute
99 : member: create dup , over , + does> cell+ @ + ;
100 : ;structure swap ! ;
101 : constructor! >body cell+ ! ;
102 : constructor: over :noname ;
103 : ;constructor postpone ; swap cell+ ! ; immediate
104 : sizeof ' >body @ state @ if postpone literal then ; immediate
105 : offsetof ' >body cell+ @ state @ if postpone literal then ; immediate
106 : ptr 1 cells member: ;
107 : int 1 cells member: ;
109 \ String structure
111 structure: string
112         ptr .addr
113         int .len
114         constructor:
115           0 over .addr !
116           0 swap .len !
117         ;constructor
118 ;structure
121 \ Module options linked list
123 structure: module
124         int module.flag
125         sizeof string member: module.name
126         sizeof string member: module.loadname
127         sizeof string member: module.type
128         sizeof string member: module.hash
129         sizeof string member: module.args
130         sizeof string member: module.largs
131         sizeof string member: module.beforeload
132         sizeof string member: module.afterload
133         sizeof string member: module.loaderror
134         ptr module.next
135 ;structure
137 \ Internal loader structures (preloaded_file, kernel_module, file_metadata)
138 \ must be in sync with the C struct in sys/boot/common/bootstrap.h
139 structure: preloaded_file
140         ptr pf.name
141         ptr pf.type
142         ptr pf.args
143         ptr pf.metadata \ file_metadata
144         int pf.loader
145         int pf.addr
146         int pf.size
147         ptr pf.modules  \ kernel_module
148         ptr pf.next     \ preloaded_file
149 ;structure
151 structure: kernel_module
152         ptr km.name
153         ptr km.args
154         ptr km.fp       \ preloaded_file
155         ptr km.next     \ kernel_module
156 ;structure
158 structure: file_metadata
159         int             md.size
160         2 member:       md.type \ this is not ANS Forth compatible (XXX)
161         ptr             md.next \ file_metadata
162         0 member:       md.data \ variable size
163 ;structure
165 \ end of structures
167 \ Global variables
169 string conf_files
170 create module_options sizeof module.next allot 0 module_options !
171 create last_module_option sizeof module.next allot 0 last_module_option !
172 0 value verbose?
174 \ Support string functions
175 : strdup { addr len -- addr' len' }
176   len allocate if ENOMEM throw then
177   addr over len move len
180 : strcat  { addr len addr' len' -- addr len+len' }
181   addr' addr len + len' move
182   addr len len' +
185 : strchr { addr len c -- addr' len' }
186   begin
187     len
188   while
189     addr c@ c = if addr len exit then
190     addr 1 + to addr
191     len 1 - to len
192   repeat
193   0 0
196 : s' \ same as s", allows " in the string
197   [char] ' parse
198   state @ if postpone sliteral then
199 ; immediate
201 : 2>r postpone >r postpone >r ; immediate
202 : 2r> postpone r> postpone r> ; immediate
203 : 2r@ postpone 2r> postpone 2dup postpone 2>r ; immediate
205 : getenv?  getenv -1 = if false else drop true then ;
207 \ determine if a word appears in a string, case-insensitive
208 : contains? ( addr1 len1 addr2 len2 -- 0 | -1 )
209         2 pick 0= if 2drop 2drop true exit then
210         dup 0= if 2drop 2drop false exit then
211         begin
212                 begin
213                         swap dup c@ dup 32 = over 9 = or over 10 = or
214                         over 13 = or over 44 = or swap drop
215                 while 1+ swap 1- repeat
216                 swap 2 pick 1- over <
217         while
218                 2over 2over drop over compare-insensitive 0= if
219                         2 pick over = if 2drop 2drop true exit then
220                         2 pick tuck - -rot + swap over c@ dup 32 =
221                         over 9 = or over 10 = or over 13 = or over 44 = or
222                         swap drop if 2drop 2drop true exit then
223                 then begin
224                         swap dup c@ dup 32 = over 9 = or over 10 = or
225                         over 13 = or over 44 = or swap drop
226                         if false else true then 2 pick 0> and
227                 while 1+ swap 1- repeat
228                 swap
229         repeat
230         2drop 2drop false
233 : boot_serial? ( -- 0 | -1 )
234         s" console" getenv dup -1 <> if
235                 2dup
236                 s" ttya" 2swap contains?        ( addr len f )
237                 -rot 2dup                       ( f addr len addr len )
238                 s" ttyb" 2swap contains?        ( f addr len f )
239                 -rot 2dup                       ( f f addr len addr len )
240                 s" ttyc" 2swap contains?        ( f f addr len f )
241                 -rot                            ( f f f addr len )
242                 s" ttyd" 2swap contains?        ( f f addr len f )
243                 or or or
244         else drop false then
245         s" boot_serial" getenv dup -1 <> if
246                 swap drop 0>
247         else drop false then
248         or \ console contains tty ( or ) boot_serial
249         s" boot_multicons" getenv dup -1 <> if
250                 swap drop 0>
251         else drop false then
252         or \ previous boolean ( or ) boot_multicons
255 \ Private definitions
257 vocabulary support-functions
258 only forth also support-functions definitions
260 \ Some control characters constants
262 7 constant bell
263 8 constant backspace
264 9 constant tab
265 10 constant lf
266 13 constant <cr>
268 \ Read buffer size
270 80 constant read_buffer_size
272 \ Standard suffixes
274 : load_module_suffix            s" _load" ;
275 : module_loadname_suffix        s" _name" ;
276 : module_type_suffix            s" _type" ;
277 : module_hash_suffix            s" _hash" ;
278 : module_args_suffix            s" _flags" ;
279 : module_beforeload_suffix      s" _before" ;
280 : module_afterload_suffix       s" _after" ;
281 : module_loaderror_suffix       s" _error" ;
283 \ Support operators
285 : >= < 0= ;
286 : <= > 0= ;
288 \ Assorted support functions
290 : free-memory free if EFREE throw then ;
292 : strget { var -- addr len } var .addr @ var .len @ ;
294 \ assign addr len to variable.
295 : strset  { addr len var -- } addr var .addr !  len var .len !  ;
297 \ free memory and reset fields
298 : strfree { var -- } var .addr @ ?dup if free-memory 0 0 var strset then ;
300 \ free old content, make a copy of the string and assign to variable
301 : string= { addr len var -- } var strfree addr len strdup var strset ;
303 : strtype ( str -- ) strget type ;
305 \ assign a reference to what is on the stack
306 : strref { addr len var -- addr len }
307   addr var .addr ! len var .len ! addr len
310 \ unquote a string
311 : unquote ( addr len -- addr len )
312   over c@ [char] " = if 2 chars - swap char+ swap then
315 \ Assignment data temporary storage
317 string name_buffer
318 string value_buffer
320 \ Line by line file reading functions
322 \ exported:
323 \       line_buffer
324 \       end_of_file?
325 \       fd
326 \       read_line
327 \       reset_line_reading
329 vocabulary line-reading
330 also line-reading definitions
332 \ File data temporary storage
334 string read_buffer
335 0 value read_buffer_ptr
337 \ File's line reading function
339 get-current ( -- wid ) previous definitions
341 string line_buffer
342 0 value end_of_file?
343 variable fd
345 >search ( wid -- ) definitions
347 : skip_newlines
348   begin
349     read_buffer .len @ read_buffer_ptr >
350   while
351     read_buffer .addr @ read_buffer_ptr + c@ lf = if
352       read_buffer_ptr char+ to read_buffer_ptr
353     else
354       exit
355     then
356   repeat
359 : scan_buffer  ( -- addr len )
360   read_buffer_ptr >r
361   begin
362     read_buffer .len @ r@ >
363   while
364     read_buffer .addr @ r@ + c@ lf = if
365       read_buffer .addr @ read_buffer_ptr +  ( -- addr )
366       r@ read_buffer_ptr -                   ( -- len )
367       r> to read_buffer_ptr
368       exit
369     then
370     r> char+ >r
371   repeat
372   read_buffer .addr @ read_buffer_ptr +  ( -- addr )
373   r@ read_buffer_ptr -                   ( -- len )
374   r> to read_buffer_ptr
377 : line_buffer_resize  ( len -- len )
378   >r
379   line_buffer .len @ if
380     line_buffer .addr @
381     line_buffer .len @ r@ +
382     resize if ENOMEM throw then
383   else
384     r@ allocate if ENOMEM throw then
385   then
386   line_buffer .addr !
387   r>
389     
390 : append_to_line_buffer  ( addr len -- )
391   line_buffer strget
392   2swap strcat
393   line_buffer .len !
394   drop
397 : read_from_buffer
398   scan_buffer            ( -- addr len )
399   line_buffer_resize     ( len -- len )
400   append_to_line_buffer  ( addr len -- )
403 : refill_required?
404   read_buffer .len @ read_buffer_ptr =
405   end_of_file? 0= and
408 : refill_buffer
409   0 to read_buffer_ptr
410   read_buffer .addr @ 0= if
411     read_buffer_size allocate if ENOMEM throw then
412     read_buffer .addr !
413   then
414   fd @ read_buffer .addr @ read_buffer_size fread
415   dup -1 = if EREAD throw then
416   dup 0= if true to end_of_file? then
417   read_buffer .len !
420 get-current ( -- wid ) previous definitions >search ( wid -- )
422 : reset_line_reading
423   0 to read_buffer_ptr
426 : read_line
427   line_buffer strfree
428   skip_newlines
429   begin
430     read_from_buffer
431     refill_required?
432   while
433     refill_buffer
434   repeat
437 only forth also support-functions definitions
439 \ Conf file line parser:
440 \ <line> ::= <spaces><name><spaces>'='<spaces><value><spaces>[<comment>] |
441 \            <spaces>[<comment>]
442 \ <name> ::= <letter>{<letter>|<digit>|'_'|'-'}
443 \ <vname> ::= <letter>{<letter>|<digit>|'_'|'-'|','}
444 \ <value> ::= '"'{<character_set>|'\'<anything>}'"' | <vname>
445 \ <character_set> ::= ASCII 32 to 126, except '\' and '"'
446 \ <comment> ::= '#'{<anything>}
448 \ bootenv line parser:
449 \ <line> ::= <spaces>setprop<spaces><name><spaces><value><spaces>[<comment>] |
450 \            <spaces>[<comment>]
452 \ exported:
453 \       line_pointer
454 \       process_conf
455 \       process_conf
457 0 value line_pointer
459 vocabulary file-processing
460 also file-processing definitions
462 \ parser functions
464 \ exported:
465 \       get_assignment
466 \       get_prop
468 vocabulary parser
469 also parser definitions
471 0 value parsing_function
472 0 value end_of_line
474 : end_of_line?  line_pointer end_of_line = ;
476 \ classifiers for various character classes in the input line
478 : letter?
479   line_pointer c@ >r
480   r@ [char] A >=
481   r@ [char] Z <= and
482   r@ [char] a >=
483   r> [char] z <= and
484   or
487 : digit?
488   line_pointer c@ >r
489   r@ [char] - =
490   r@ [char] 0 >=
491   r> [char] 9 <= and
492   or
495 : "quote?  line_pointer c@ [char] " = ;
497 : 'quote?  line_pointer c@ [char] ' = ;
499 : assignment_sign?  line_pointer c@ [char] = = ;
501 : comment?  line_pointer c@ [char] # = ;
503 : space?  line_pointer c@ bl = line_pointer c@ tab = or ;
505 : backslash?  line_pointer c@ [char] \ = ;
507 : underscore?  line_pointer c@ [char] _ = ;
509 : dot?  line_pointer c@ [char] . = ;
511 : dash?  line_pointer c@ [char] - = ;
513 : comma?  line_pointer c@ [char] , = ;
515 \ manipulation of input line
516 : skip_character line_pointer char+ to line_pointer ;
518 : skip_to_end_of_line end_of_line to line_pointer ;
520 : eat_space
521   begin
522     end_of_line? if 0 else space? then
523   while
524     skip_character
525   repeat
528 : parse_name  ( -- addr len )
529   line_pointer
530   begin
531     end_of_line? if 0 else
532       letter? digit? underscore? dot? dash?
533       or or or or
534     then
535   while
536     skip_character
537   repeat
538   line_pointer over -
539   strdup
542 : parse_value  ( -- addr len )
543   line_pointer
544   begin
545     end_of_line? if 0 else
546       letter? digit? underscore? dot? comma? dash?
547       or or or or or
548     then
549   while
550     skip_character
551   repeat
552   line_pointer over -
553   strdup
556 : remove_backslashes  { addr len | addr' len' -- addr' len' }
557   len allocate if ENOMEM throw then
558   to addr'
559   addr >r
560   begin
561     addr c@ [char] \ <> if
562       addr c@ addr' len' + c!
563       len' char+ to len'
564     then
565     addr char+ to addr
566     r@ len + addr =
567   until
568   r> drop
569   addr' len'
572 : parse_quote  ( xt -- addr len )
573   >r                    ( R: xt )
574   line_pointer
575   skip_character
576   end_of_line? if ESYNTAX throw then
577   begin
578     r@ execute 0=
579   while
580     backslash? if
581       skip_character
582       end_of_line? if ESYNTAX throw then
583     then
584     skip_character
585     end_of_line? if ESYNTAX throw then 
586   repeat
587   r> drop
588   skip_character
589   line_pointer over -
590   remove_backslashes
593 : read_name
594   parse_name            ( -- addr len )
595   name_buffer strset
598 : read_value
599   "quote? if
600     ['] "quote? parse_quote             ( -- addr len )
601   else
602     'quote? if
603       ['] 'quote? parse_quote           ( -- addr len )
604     else
605       parse_value               ( -- addr len )
606     then
607   then
608   value_buffer strset
611 : comment
612   skip_to_end_of_line
615 : white_space_4
616   eat_space
617   comment? if ['] comment to parsing_function exit then
618   end_of_line? 0= if ESYNTAX throw then
621 : variable_value
622   read_value
623   ['] white_space_4 to parsing_function
626 : white_space_3
627   eat_space
628   letter? digit? "quote? 'quote? or or or if
629     ['] variable_value to parsing_function exit
630   then
631   ESYNTAX throw
634 : assignment_sign
635   skip_character
636   ['] white_space_3 to parsing_function
639 : white_space_2
640   eat_space
641   assignment_sign? if ['] assignment_sign to parsing_function exit then
642   ESYNTAX throw
645 : variable_name
646   read_name
647   ['] white_space_2 to parsing_function
650 : white_space_1
651   eat_space
652   letter?  if ['] variable_name to parsing_function exit then
653   comment? if ['] comment to parsing_function exit then
654   end_of_line? 0= if ESYNTAX throw then
657 : prop_name
658   eat_space
659   read_name
660   ['] white_space_3 to parsing_function
663 : get_prop_cmd
664   eat_space
665   s" setprop" line_pointer over compare 0=
666   if line_pointer 7 + to line_pointer
667     ['] prop_name to parsing_function exit
668   then
669   comment? if ['] comment to parsing_function exit then
670   end_of_line? 0= if ESYNTAX throw then
673 get-current ( -- wid ) previous definitions >search ( wid -- )
675 : get_assignment
676   line_buffer strget + to end_of_line
677   line_buffer .addr @ to line_pointer
678   ['] white_space_1 to parsing_function
679   begin
680     end_of_line? 0=
681   while
682     parsing_function execute
683   repeat
684   parsing_function ['] comment =
685   parsing_function ['] white_space_1 =
686   parsing_function ['] white_space_4 =
687   or or 0= if ESYNTAX throw then
690 : get_prop
691   line_buffer strget + to end_of_line
692   line_buffer .addr @ to line_pointer
693   ['] get_prop_cmd to parsing_function
694   begin
695     end_of_line? 0=
696   while
697     parsing_function execute
698   repeat
699   parsing_function ['] comment =
700   parsing_function ['] get_prop_cmd =
701   parsing_function ['] white_space_4 =
702   or or 0= if ESYNTAX throw then
705 only forth also support-functions also file-processing definitions
707 \ Process line
709 : assignment_type?  ( addr len -- flag )
710   name_buffer strget
711   compare 0=
714 : suffix_type?  ( addr len -- flag )
715   name_buffer .len @ over <= if 2drop false exit then
716   name_buffer .len @ over - name_buffer .addr @ +
717   over compare 0=
720 : loader_conf_files?  s" loader_conf_files" assignment_type?  ;
722 : verbose_flag? s" verbose_loading" assignment_type?  ;
724 : execute? s" exec" assignment_type?  ;
726 : module_load? load_module_suffix suffix_type? ;
728 : module_loadname?  module_loadname_suffix suffix_type?  ;
730 : module_type?  module_type_suffix suffix_type?  ;
732 : module_hash?  module_hash_suffix suffix_type?  ;
734 : module_args?  module_args_suffix suffix_type?  ;
736 : module_beforeload?  module_beforeload_suffix suffix_type?  ;
738 : module_afterload?  module_afterload_suffix suffix_type?  ;
740 : module_loaderror?  module_loaderror_suffix suffix_type?  ;
742 \ build a 'set' statement and execute it
743 : set_environment_variable
744   name_buffer .len @ value_buffer .len @ + 5 chars + \ size of result string
745   allocate if ENOMEM throw then
746   dup 0  \ start with an empty string and append the pieces
747   s" set " strcat
748   name_buffer strget strcat
749   s" =" strcat
750   value_buffer strget strcat
751   ['] evaluate catch if
752     2drop free drop
753     ESETERROR throw
754   else
755     free-memory
756   then
759 : set_conf_files
760   set_environment_variable
761   s" loader_conf_files" getenv conf_files string=
764 : append_to_module_options_list  ( addr -- )
765   module_options @ 0= if
766     dup module_options !
767     last_module_option !
768   else
769     dup last_module_option @ module.next !
770     last_module_option !
771   then
774 : set_module_name  { addr -- }  \ check leaks
775   name_buffer strget addr module.name string=
778 : yes_value?
779   value_buffer strget unquote
780   s" yes" compare-insensitive 0=
783 : find_module_option  ( -- addr | 0 ) \ return ptr to entry matching name_buffer
784   module_options @
785   begin
786     dup
787   while
788     dup module.name strget
789     name_buffer strget
790     compare 0= if exit then
791     module.next @
792   repeat
795 : new_module_option  ( -- addr )
796   sizeof module allocate if ENOMEM throw then
797   dup sizeof module erase
798   dup append_to_module_options_list
799   dup set_module_name
802 : get_module_option  ( -- addr )
803   find_module_option
804   ?dup 0= if new_module_option then
807 : set_module_flag
808   name_buffer .len @ load_module_suffix nip - name_buffer .len !
809   yes_value? get_module_option module.flag !
812 : set_module_args
813   name_buffer .len @ module_args_suffix nip - name_buffer .len !
814   value_buffer strget unquote
815   get_module_option module.args string=
818 : set_module_loadname
819   name_buffer .len @ module_loadname_suffix nip - name_buffer .len !
820   value_buffer strget unquote
821   get_module_option module.loadname string=
824 : set_module_type
825   name_buffer .len @ module_type_suffix nip - name_buffer .len !
826   value_buffer strget unquote
827   get_module_option module.type string=
830 : set_module_hash
831   name_buffer .len @ module_hash_suffix nip - name_buffer .len !
832   value_buffer strget unquote
833   get_module_option module.hash string=
836 : set_module_beforeload
837   name_buffer .len @ module_beforeload_suffix nip - name_buffer .len !
838   value_buffer strget unquote
839   get_module_option module.beforeload string=
842 : set_module_afterload
843   name_buffer .len @ module_afterload_suffix nip - name_buffer .len !
844   value_buffer strget unquote
845   get_module_option module.afterload string=
848 : set_module_loaderror
849   name_buffer .len @ module_loaderror_suffix nip - name_buffer .len !
850   value_buffer strget unquote
851   get_module_option module.loaderror string=
854 : set_verbose
855   yes_value? to verbose?
858 : execute_command
859   value_buffer strget unquote
860   ['] evaluate catch if EEXEC throw then
863 : process_assignment
864   name_buffer .len @ 0= if exit then
865   loader_conf_files?    if set_conf_files exit then
866   verbose_flag?         if set_verbose exit then
867   execute?              if execute_command exit then
868   module_load?          if set_module_flag exit then
869   module_loadname?      if set_module_loadname exit then
870   module_type?          if set_module_type exit then
871   module_hash?          if set_module_hash exit then
872   module_args?          if set_module_args exit then
873   module_beforeload?    if set_module_beforeload exit then
874   module_afterload?     if set_module_afterload exit then
875   module_loaderror?     if set_module_loaderror exit then
876   set_environment_variable
879 \ free_buffer  ( -- )
881 \ Free some pointers if needed. The code then tests for errors
882 \ in freeing, and throws an exception if needed. If a pointer is
883 \ not allocated, it's value (0) is used as flag.
885 : free_buffers
886   name_buffer strfree
887   value_buffer strfree
890 \ Higher level file processing
892 get-current ( -- wid ) previous definitions >search ( wid -- )
894 : process_bootenv
895   begin
896     end_of_file? 0=
897   while
898     free_buffers
899     read_line
900     get_prop
901     ['] process_assignment catch
902     ['] free_buffers catch
903     swap throw throw
904   repeat
907 : process_conf
908   begin
909     end_of_file? 0=
910   while
911     free_buffers
912     read_line
913     get_assignment
914     ['] process_assignment catch
915     ['] free_buffers catch
916     swap throw throw
917   repeat
920 : peek_file ( addr len -- )
921   0 to end_of_file?
922   reset_line_reading
923   O_RDONLY fopen fd !
924   fd @ -1 = if EOPEN throw then
925   free_buffers
926   read_line
927   get_assignment
928   ['] process_assignment catch
929   ['] free_buffers catch
930   fd @ fclose
931   swap throw throw
933   
934 only forth also support-functions definitions
936 \ Interface to loading conf files
938 : load_conf  ( addr len -- )
939   0 to end_of_file?
940   reset_line_reading
941   O_RDONLY fopen fd !
942   fd @ -1 = if EOPEN throw then
943   ['] process_conf catch
944   fd @ fclose
945   throw
948 : print_line line_buffer strtype cr ;
950 : print_syntax_error
951   line_buffer strtype cr
952   line_buffer .addr @
953   begin
954     line_pointer over <>
955   while
956     bl emit char+
957   repeat
958   drop
959   ." ^" cr
962 : load_bootenv  ( addr len -- )
963   0 to end_of_file?
964   reset_line_reading
965   O_RDONLY fopen fd !
966   fd @ -1 = if EOPEN throw then
967   ['] process_bootenv catch
968   fd @ fclose
969   throw
972 \ Debugging support functions
974 only forth definitions also support-functions
976 : test-file 
977   ['] load_conf catch dup .
978   ESYNTAX = if cr print_syntax_error then
981 \ find a module name, leave addr on the stack (0 if not found)
982 : find-module ( <module> -- ptr | 0 )
983   bl parse ( addr len )
984   module_options @ >r ( store current pointer )
985   begin
986     r@
987   while
988     2dup ( addr len addr len )
989     r@ module.name strget
990     compare 0= if drop drop r> exit then ( found it )
991     r> module.next @ >r
992   repeat
993   type ."  was not found" cr r>
996 : show-nonempty ( addr len mod -- )
997   strget dup verbose? or if
998     2swap type type cr
999   else
1000     drop drop drop drop
1001   then ;
1003 : show-one-module { addr -- addr }
1004   ." Name:        " addr module.name strtype cr
1005   s" Path:        " addr module.loadname show-nonempty
1006   s" Type:        " addr module.type show-nonempty
1007   s" Hash:        " addr module.hash show-nonempty
1008   s" Flags:       " addr module.args show-nonempty
1009   s" Before load: " addr module.beforeload show-nonempty
1010   s" After load:  " addr module.afterload show-nonempty
1011   s" Error:       " addr module.loaderror show-nonempty
1012   ." Status:      " addr module.flag @ if ." Load" else ." Don't load" then cr
1013   cr
1014   addr
1017 : show-module-options
1018   module_options @
1019   begin
1020     ?dup
1021   while
1022     show-one-module
1023     module.next @
1024   repeat
1027 : free-one-module { addr -- addr }
1028   addr module.name strfree
1029   addr module.loadname strfree
1030   addr module.type strfree
1031   addr module.hash strfree
1032   addr module.args strfree
1033   addr module.largs strfree
1034   addr module.beforeload strfree
1035   addr module.afterload strfree
1036   addr module.loaderror strfree
1037   addr
1040 : free-module-options
1041   module_options @
1042   begin
1043     ?dup
1044   while
1045     free-one-module
1046     dup module.next @
1047     swap free-memory
1048   repeat
1049   0 module_options !
1050   0 last_module_option !
1053 only forth also support-functions definitions
1055 \ Variables used for processing multiple conf files
1057 string current_file_name_ref    \ used to print the file name
1059 \ Indicates if any conf file was succesfully read
1061 0 value any_conf_read?
1063 \ loader_conf_files processing support functions
1065 \ true if string in addr1 is smaller than in addr2
1066 : compar ( addr1 addr2 -- flag )
1067   swap                  ( addr2 addr1 )
1068   dup cell+             ( addr2 addr1 addr )
1069   swap @                ( addr2 addr len )
1070   rot                   ( addr len addr2 )
1071   dup cell+             ( addr len addr2 addr' )
1072   swap @                ( addr len addr' len' )
1073   compare -1 =
1076 \ insertion sort algorithm. we dont expect large amounts of data to be
1077 \ sorted, so insert should be ok. compar needs to implement < operator.
1078 : insert ( start end -- start )
1079   dup @ >r ( r: v )             \ v = a[i]
1080   begin
1081     2dup <                      \ j>0
1082   while
1083     r@ over cell- @ compar      \ a[j-1] > v
1084   while
1085     cell-                       \ j--
1086     dup @ over cell+ !          \ a[j] = a[j-1]
1087   repeat then
1088   r> swap !                     \ a[j] = v
1091 : sort ( array len -- )
1092   1 ?do dup i cells + insert loop drop
1095 : opendir
1096   s" /boot/conf.d" fopendir if fd ! else
1097     EOPEN throw
1098   then
1101 : readdir ( addr len flag | flag )
1102   fd @ freaddir
1105 : closedir
1106   fd @ fclosedir
1109 : entries       (  -- n )       \ count directory entries
1110   ['] opendir catch             ( n array )
1111   throw
1113   0             ( i )
1114   begin \ count the entries
1115   readdir       ( i addr len flag | i flag )
1116   dup -1 = if
1117     -ROT 2drop
1118     swap 1+ swap
1119   then
1120   0=
1121   until
1122   closedir
1125 \ built-in prefix directory name; it must end with /, so we don't
1126 \ need to check and insert it.
1127 : make_cstring  ( addr len -- addr' )
1128   dup           ( addr len len )
1129   s" /boot/conf.d/"     ( addr len len addr' len' )
1130   rot           ( addr len addr' len' len )
1131   over +        ( addr len addr' len' total )   \ space for prefix+str
1132   dup cell+ 1+                                  \ 1+ for '\0'
1133   allocate if
1134     -1 abort" malloc failed"
1135   then
1136                 ( addr len addr' len' total taddr )
1137   dup rot       ( addr len addr' len' taddr taddr total )
1138   swap !        ( addr len addr' len' taddr )   \ store length
1139   dup >r                                        \ save reference
1140   cell+                                         \ point to string area
1141   2dup 2>r      ( addr len addr' len' taddr' )  ( R: taddr len' taddr' )
1142   swap move     ( addr len )
1143   2r> +         ( addr len taddr' )             ( R: taddr )
1144   swap 1+ move                                  \ 1+ for '\0'
1145   r>            ( taddr )
1148 : scan_conf_dir ( -- addr len -1 | 0 )
1149   s" currdev" getenv dup -1 <> if
1150     s" pxe0:" compare 0= if 0 exit then \ readdir does not work on tftp
1151   else
1152     drop
1153   then
1155   ['] entries catch if
1156     0 exit
1157   then
1158   dup 0= if exit then           \ nothing to do
1160   dup cells allocate            ( n array flag )        \ allocate array
1161   if 0 exit then
1162   ['] opendir catch if          ( n array )
1163     free drop drop
1164     0 exit
1165   then
1166   over 0 do
1167     readdir                     ( n array addr len flag | n array flag )
1168     0= if -1 abort" unexpected readdir error" then      \ shouldnt happen
1169                                 ( n array addr len )
1170     \ we have relative name, make it absolute and convert to counted string
1171     make_cstring                ( n array addr )
1172     over I cells + !            ( n array )
1173   loop
1174   closedir
1175   2dup swap sort
1176   \ we have now array of strings with directory entry names.
1177   \ calculate size of concatenated string
1178   over 0 swap 0 do              ( n array 0 )
1179     over I cells + @            ( n array total array[I] )
1180     @ + 1+                      ( n array total' )
1181   loop
1182   dup allocate if drop free 2drop 0 exit then
1183                                 ( n array len addr )
1184   \ now concatenate all entries.
1185   2swap                         ( len addr n array )
1186   over 0 swap 0 do              ( len addr n array 0 )
1187     over I cells + @            ( len addr n array total array[I] )
1188     dup @ swap cell+            ( len addr n array total len addr' )
1189     over                        ( len addr n array total len addr' len )
1190     6 pick                      ( len addr n array total len addr' len addr )
1191     4 pick +                    ( len addr n array total len addr' len addr+total )
1192     swap move +                 ( len addr n array total+len )
1193     3 pick                      ( len addr n array total addr )
1194     over + bl swap c! 1+        ( len addr n array total )
1195     over I cells + @ free drop  \ free array[I]
1196   loop
1197   drop free drop drop           ( len addr )
1198   swap                          ( addr len )
1199   -1
1202 : get_conf_files ( -- addr len )  \ put addr/len on stack, reset var
1203   \ ." -- starting on <" conf_files strtype ." >" cr \ debugging
1204   scan_conf_dir if              \ concatenate with conf_files
1205                         ( addr len )
1206     dup conf_files .len @ + 2 + allocate abort" out of memory"  ( addr len addr' )
1207     dup conf_files strget       ( addr len addr' caddr clen )
1208     rot swap move               ( addr len addr' )
1209     \ add space
1210     dup conf_files .len @ +     ( addr len addr' addr'+clen )
1211     dup bl swap c! 1+           ( addr len addr' addr'' )
1212     3 pick swap                 ( addr len addr' addr addr'' )
1213     3 pick move                 ( addr len addr' )
1214     rot                         ( len addr' addr )
1215     free drop swap              ( addr' len )
1216     conf_files .len @ + 1+      ( addr len )
1217     conf_files strfree
1218   else
1219     conf_files strget 0 0 conf_files strset
1220   then
1223 : skip_leading_spaces  { addr len pos -- addr len pos' }
1224   begin
1225     pos len = if 0 else addr pos + c@ bl = then
1226   while
1227     pos char+ to pos
1228   repeat
1229   addr len pos
1232 \ return the file name at pos, or free the string if nothing left
1233 : get_file_name  { addr len pos -- addr len pos' addr' len' || 0 }
1234   pos len = if 
1235     addr free abort" Fatal error freeing memory"
1236     0 exit
1237   then
1238   pos >r
1239   begin
1240     \ stay in the loop until have chars and they are not blank
1241     pos len = if 0 else addr pos + c@ bl <> then
1242   while
1243     pos char+ to pos
1244   repeat
1245   addr len pos addr r@ + pos r> -
1248 : get_next_file  ( addr len ptr -- addr len ptr' addr' len' | 0 )
1249   skip_leading_spaces
1250   get_file_name
1253 : print_current_file
1254   current_file_name_ref strtype
1257 : process_conf_errors
1258   dup 0= if true to any_conf_read? drop exit then
1259   >r 2drop r>
1260   dup ESYNTAX = if
1261     ." Warning: syntax error on file " print_current_file cr
1262     print_syntax_error drop exit
1263   then
1264   dup ESETERROR = if
1265     ." Warning: bad definition on file " print_current_file cr
1266     print_line drop exit
1267   then
1268   dup EREAD = if
1269     ." Warning: error reading file " print_current_file cr drop exit
1270   then
1271   dup EOPEN = if
1272     verbose? if ." Warning: unable to open file " print_current_file cr then
1273     drop exit
1274   then
1275   dup EFREE = abort" Fatal error freeing memory"
1276   dup ENOMEM = abort" Out of memory"
1277   throw  \ Unknown error -- pass ahead
1280 \ Process loader_conf_files recursively
1281 \ Interface to loader_conf_files processing
1283 : include_bootenv
1284   s" /boot/solaris/bootenv.rc"
1285   ['] load_bootenv catch
1286   dup 0= if drop exit then
1287   >r 2drop r>
1288   dup ESYNTAX = if
1289     ." Warning: syntax error on /boot/solaris/bootenv.rc" cr drop exit
1290   then
1291   dup EREAD = if
1292     ." Warning: error reading /boot/solaris/bootenv.rc" cr drop exit
1293   then
1294   dup EOPEN = if
1295     verbose? if ." Warning: unable to open /boot/solaris/bootenv.rc" cr then
1296     drop exit
1297   then
1298   dup EFREE = abort" Fatal error freeing memory"
1299   dup ENOMEM = abort" Out of memory"
1300   throw  \ Unknown error -- pass ahead
1303 : include_transient
1304   s" /boot/transient.conf" ['] load_conf catch
1305   dup 0= if drop exit then      \ no error
1306   >r 2drop r>
1307   dup ESYNTAX = if
1308     ." Warning: syntax error on file /boot/transient.conf" cr
1309     drop exit
1310   then
1311   dup ESETERROR = if
1312     ." Warning: bad definition on file /boot/transient.conf" cr
1313     drop exit
1314   then
1315   dup EREAD = if
1316     ." Warning: error reading file /boot/transient.conf" cr drop exit
1317   then
1318   dup EOPEN = if
1319     verbose? if ." Warning: unable to open file /boot/transient.conf" cr then
1320     drop exit
1321   then
1322   dup EFREE = abort" Fatal error freeing memory"
1323   dup ENOMEM = abort" Out of memory"
1324   throw  \ Unknown error -- pass ahead
1327 : include_conf_files
1328   get_conf_files 0      ( addr len offset )
1329   begin
1330     get_next_file ?dup ( addr len 1 | 0 )
1331   while
1332     current_file_name_ref strref
1333     ['] load_conf catch
1334     process_conf_errors
1335     conf_files .addr @ if recurse then
1336   repeat
1339 \ Module loading functions
1341 \ concat two strings by allocating space
1342 : concat { a1 l1 a2 l2 -- a' l' }
1343    l1 l2 + allocate if ENOMEM throw then
1344    0 a1 l1 strcat
1345    a2 l2 strcat
1348 \ build module argument list as: "hash= name= module.args"
1349 \ if type is hash, name= will have module name without .hash suffix
1350 \ will free old largs and set new.
1352 : build_largs { addr -- addr }
1353   addr module.largs strfree
1354   addr module.hash .len @
1355   if ( set hash= )
1356     s" hash=" addr module.hash strget concat
1357     addr module.largs strset    \ largs = "hash=" + module.hash
1358   then
1360   addr module.type strget s" hash" compare 0=
1361   if ( module.type == "hash" )
1362     addr module.largs strget s"  name=" concat
1364     addr module.loadname .len @
1365     if ( module.loadname != NULL )
1366       addr module.loadname strget concat
1367     else
1368       addr module.name strget concat
1369     then
1371     addr module.largs strfree
1372     addr module.largs strset    \ largs = largs + name
1374     \ last thing to do is to strip off ".hash" suffix
1375     addr module.largs strget [char] . strchr
1376     dup if ( strchr module.largs '.' )
1377       s" .hash" compare 0=
1378       if ( it is ".hash" )
1379         addr module.largs .len @ 5 -
1380         addr module.largs .len !
1381       then
1382     else
1383       2drop
1384     then
1385   then
1386   \ and now add up the module.args
1387   addr module.largs strget s"  " concat
1388   addr module.args strget concat
1389   addr module.largs strfree
1390   addr module.largs strset
1391   addr
1394 : load_parameters  { addr -- addr addrN lenN ... addr1 len1 N }
1395   addr build_largs
1396   addr module.largs strget
1397   addr module.loadname .len @ if
1398     addr module.loadname strget
1399   else
1400     addr module.name strget
1401   then
1402   addr module.type .len @ if
1403     addr module.type strget
1404     s" -t "
1405     4 ( -t type name flags )
1406   else
1407     2 ( name flags )
1408   then
1411 : before_load  ( addr -- addr )
1412   dup module.beforeload .len @ if
1413     dup module.beforeload strget
1414     ['] evaluate catch if EBEFORELOAD throw then
1415   then
1418 : after_load  ( addr -- addr )
1419   dup module.afterload .len @ if
1420     dup module.afterload strget
1421     ['] evaluate catch if EAFTERLOAD throw then
1422   then
1425 : load_error  ( addr -- addr )
1426   dup module.loaderror .len @ if
1427     dup module.loaderror strget
1428     evaluate  \ This we do not intercept so it can throw errors
1429   then
1432 : pre_load_message  ( addr -- addr )
1433   verbose? if
1434     dup module.name strtype
1435     ." ..."
1436   then
1439 : load_error_message verbose? if ." failed!" cr then ;
1441 : load_succesful_message verbose? if ." ok" cr then ;
1443 : load_module
1444   load_parameters load
1447 : process_module  ( addr -- addr )
1448   pre_load_message
1449   before_load
1450   begin
1451     ['] load_module catch if
1452       dup module.loaderror .len @ if
1453         load_error                      \ Command should return a flag!
1454       else 
1455         load_error_message true         \ Do not retry
1456       then
1457     else
1458       after_load
1459       load_succesful_message true       \ Succesful, do not retry
1460     then
1461   until
1464 : process_module_errors  ( addr ior -- )
1465   dup EBEFORELOAD = if
1466     drop
1467     ." Module "
1468     dup module.name strtype
1469     dup module.loadname .len @ if
1470       ." (" dup module.loadname strtype ." )"
1471     then
1472     cr
1473     ." Error executing "
1474     dup module.beforeload strtype cr    \ XXX there was a typo here
1475     abort
1476   then
1478   dup EAFTERLOAD = if
1479     drop
1480     ." Module "
1481     dup module.name .addr @ over module.name .len @ type
1482     dup module.loadname .len @ if
1483       ." (" dup module.loadname strtype ." )"
1484     then
1485     cr
1486     ." Error executing "
1487     dup module.afterload strtype cr
1488     abort
1489   then
1491   throw  \ Don't know what it is all about -- pass ahead
1494 \ Module loading interface
1496 \ scan the list of modules, load enabled ones.
1497 : load_modules  ( -- ) ( throws: abort & user-defined )
1498   module_options @      ( list_head )
1499   begin
1500     ?dup
1501   while
1502     dup module.flag @ if
1503       ['] process_module catch
1504       process_module_errors
1505     then
1506     module.next @
1507   repeat
1510 \ h00h00 magic used to try loading either a kernel with a given name,
1511 \ or a kernel with the default name in a directory of a given name
1512 \ (the pain!)
1514 : bootpath s" /platform/" ;
1515 : modulepath s" module_path" ;
1517 \ Functions used to save and restore module_path's value.
1518 : saveenv ( addr len | -1 -- addr' len | 0 -1 )
1519   dup -1 = if 0 swap exit then
1520   strdup
1522 : freeenv ( addr len | 0 -1 )
1523   -1 = if drop else free abort" Freeing error" then
1525 : restoreenv  ( addr len | 0 -1 -- )
1526   dup -1 = if ( it wasn't set )
1527     2drop
1528     modulepath unsetenv
1529   else
1530     over >r
1531     modulepath setenv
1532     r> free abort" Freeing error"
1533   then
1536 : clip_args   \ Drop second string if only one argument is passed
1537   1 = if
1538     2swap 2drop
1539     1
1540   else
1541     2
1542   then
1545 also builtins
1547 \ Parse filename from a semicolon-separated list
1549 \ replacement, not working yet
1550 : newparse-; { addr len | a1 -- a' len-x addr x }
1551   addr len [char] ; strchr dup if       ( a1 len1 )
1552     swap to a1  ( store address )
1553     1 - a1 @ 1 + swap ( remove match )
1554     addr a1 addr -
1555   else
1556     0 0 addr len
1557   then
1560 : parse-; ( addr len -- addr' len-x addr x )
1561   over 0 2swap                  ( addr 0 addr len )
1562   begin
1563     dup 0 <>                    ( addr 0 addr len )
1564   while
1565     over c@ [char] ; <>         ( addr 0 addr len flag )
1566   while
1567     1- swap 1+ swap
1568     2swap 1+ 2swap
1569   repeat then
1570   dup 0 <> if
1571     1- swap 1+ swap
1572   then
1573   2swap
1576 \ Try loading one of multiple kernels specified
1578 : try_multiple_kernels ( addr len addr' len' args -- flag )
1579   >r
1580   begin
1581     parse-; 2>r
1582     2over 2r>
1583     r@ clip_args
1584     s" DEBUG" getenv? if
1585       s" echo Module_path: ${module_path}" evaluate
1586       ." Kernel     : " >r 2dup type r> cr
1587       dup 2 = if ." Flags      : " >r 2over type r> cr then
1588     then
1589     \ if it's xen, the xen kernel is loaded, unix needs to be loaded as module
1590     s" xen_kernel" getenv -1 = if
1591       1 load                            \ normal kernel
1592     else
1593       drop
1594       >r s" kernel" s" -t " r> 2 + 1 load
1595     then
1596   while
1597     dup 0=
1598   until
1599     1 >r \ Failure
1600   else
1601     0 >r \ Success
1602   then
1603   2drop 2drop
1604   r>
1605   r> drop
1608 \ Try to load a kernel; the kernel name is taken from one of
1609 \ the following lists, as ordered:
1611 \   1. The "bootfile" environment variable
1612 \   2. The "kernel" environment variable
1614 \ Flags are passed, if available. If not, dummy values must be given.
1616 \ The kernel gets loaded from the current module_path.
1618 : load_a_kernel ( flags len 1 | x x 0 -- flag )
1619   local args
1620   2local flags
1621   0 0 2local kernel
1622   end-locals
1624   \ Check if a default kernel name exists at all, exits if not
1625   s" bootfile" getenv dup -1 <> if
1626     to kernel
1627     flags kernel args 1+ try_multiple_kernels
1628     dup 0= if exit then
1629   then
1630   drop
1632   s" kernel" getenv dup -1 <> if
1633     to kernel
1634   else
1635     drop
1636     1 exit \ Failure
1637   then
1639   \ Try all default kernel names
1640   flags kernel args 1+ try_multiple_kernels
1643 \ Try to load a kernel; the kernel name is taken from one of
1644 \ the following lists, as ordered:
1646 \   1. The "bootfile" environment variable
1647 \   2. The "kernel" environment variable
1649 \ Flags are passed, if provided.
1651 \ The kernel will be loaded from a directory computed from the
1652 \ path given. Two directories will be tried in the following order:
1654 \   1. /boot/path
1655 \   2. path
1657 \ The module_path variable is overridden if load is succesful, by
1658 \ prepending the successful path.
1660 : load_from_directory ( path len 1 | flags len' path len 2 -- flag )
1661   local args
1662   2local path
1663   args 1 = if 0 0 then
1664   2local flags
1665   0 0 2local oldmodulepath \ like a string
1666   0 0 2local newmodulepath \ like a string
1667   end-locals
1669   \ Set the environment variable module_path, and try loading
1670   \ the kernel again.
1671   modulepath getenv saveenv to oldmodulepath
1673   \ Try prepending /boot/ first
1674   bootpath nip path nip +       \ total length
1675   oldmodulepath nip dup -1 = if
1676     drop
1677   else
1678     1+ +                        \ add oldpath -- XXX why the 1+ ?
1679   then
1680   allocate if ( out of memory ) 1 exit then \ XXX throw ?
1682   0
1683   bootpath strcat
1684   path strcat
1685   2dup to newmodulepath
1686   modulepath setenv
1688   \ Try all default kernel names
1689   flags args 1- load_a_kernel
1690   0= if ( success )
1691     oldmodulepath nip -1 <> if
1692       newmodulepath s" ;" strcat
1693       oldmodulepath strcat
1694       modulepath setenv
1695       newmodulepath drop free-memory
1696       oldmodulepath drop free-memory
1697     then
1698     0 exit
1699   then
1701   \ Well, try without the prepended /boot/
1702   path newmodulepath drop swap move
1703   newmodulepath drop path nip
1704   2dup to newmodulepath
1705   modulepath setenv
1707   \ Try all default kernel names
1708   flags args 1- load_a_kernel
1709   if ( failed once more )
1710     oldmodulepath restoreenv
1711     newmodulepath drop free-memory
1712     1
1713   else
1714     oldmodulepath nip -1 <> if
1715       newmodulepath s" ;" strcat
1716       oldmodulepath strcat
1717       modulepath setenv
1718       newmodulepath drop free-memory
1719       oldmodulepath drop free-memory
1720     then
1721     0
1722   then
1725 \ Try to load a kernel; the kernel name is taken from one of
1726 \ the following lists, as ordered:
1728 \   1. The "bootfile" environment variable
1729 \   2. The "kernel" environment variable
1730 \   3. The "path" argument
1732 \ Flags are passed, if provided.
1734 \ The kernel will be loaded from a directory computed from the
1735 \ path given. Two directories will be tried in the following order:
1737 \   1. /boot/path
1738 \   2. path
1740 \ Unless "path" is meant to be kernel name itself. In that case, it
1741 \ will first be tried as a full path, and, next, search on the
1742 \ directories pointed by module_path.
1744 \ The module_path variable is overridden if load is succesful, by
1745 \ prepending the successful path.
1747 : load_directory_or_file ( path len 1 | flags len' path len 2 -- flag )
1748   local args
1749   2local path
1750   args 1 = if 0 0 then
1751   2local flags
1752   end-locals
1754   \ First, assume path is an absolute path to a directory
1755   flags path args clip_args load_from_directory
1756   dup 0= if exit else drop then
1758   \ Next, assume path points to the kernel
1759   flags path args try_multiple_kernels
1762 : initialize  ( addr len -- )
1763   strdup conf_files strset
1766 : boot-args ( -- addr len 1 | 0 )
1767   s" boot-args" getenv
1768   dup -1 = if drop 0 else 1 then
1771 : standard_kernel_search  ( flags 1 | 0 -- flag )
1772   local args
1773   args 0= if 0 0 then
1774   2local flags
1775   s" kernel" getenv
1776   dup -1 = if 0 swap then
1777   2local path
1778   end-locals
1780   path nip -1 = if ( there isn't a "kernel" environment variable )
1781     flags args load_a_kernel
1782   else
1783     flags path args 1+ clip_args load_directory_or_file
1784   then
1787 : load_kernel  ( -- ) ( throws: abort )
1788   s" xen_kernel" getenv -1 = if
1789     boot-args standard_kernel_search
1790     abort" Unable to load a kernel!"
1791     exit
1792   then
1794   drop
1795   \ we have loaded the xen kernel, load unix as module
1796   s" bootfile" getenv dup -1 <> if
1797     s" kernel" s" -t " 3 1 load
1798   then
1799   abort" Unable to load a kernel!"
1802 : load_xen ( -- )
1803   s" xen_kernel" getenv dup -1 <> if
1804     1 1 load ( c-addr/u flag N -- flag )
1805   else
1806     drop
1807     0 ( -1 -- flag )
1808   then
1811 : load_xen_throw ( -- ) ( throws: abort )
1812   load_xen
1813   abort" Unable to load Xen!"
1816 : set_defaultoptions  ( -- )
1817   s" boot-args" getenv dup -1 = if
1818     drop
1819   else
1820     s" temp_options" setenv
1821   then
1824 \ pick the i-th argument, i starts at 0
1825 : argv[]  ( aN uN ... a1 u1 N i -- aN uN ... a1 u1 N ai+1 ui+1 )
1826   2dup = if 0 0 exit then       \ out of range
1827   dup >r
1828   1+ 2* ( skip N and ui )
1829   pick
1830   r>
1831   1+ 2* ( skip N and ai )
1832   pick
1835 : drop_args  ( aN uN ... a1 u1 N -- )
1836   0 ?do 2drop loop
1839 : argc
1840   dup
1843 : queue_argv  ( aN uN ... a1 u1 N a u -- a u aN uN ... a1 u1 N+1 )
1844   >r
1845   over 2* 1+ -roll
1846   r>
1847   over 2* 1+ -roll
1848   1+
1851 : unqueue_argv  ( aN uN ... a1 u1 N -- aN uN ... a2 u2 N-1 a1 u1 )
1852   1- -rot
1855 \ compute the length of the buffer including the spaces between words
1856 : strlen(argv) ( aN uN .. a1 u1 N -- aN uN .. a1 u1 N len )
1857   dup 0= if 0 exit then
1858   0 >r  \ Size
1859   0 >r  \ Index
1860   begin
1861     argc r@ <>
1862   while
1863     r@ argv[]
1864     nip
1865     r> r> rot + 1+
1866     >r 1+ >r
1867   repeat
1868   r> drop
1869   r>
1872 : concat_argv  ( aN uN ... a1 u1 N -- a u )
1873   strlen(argv) allocate if ENOMEM throw then
1874   0 2>r ( save addr 0 on return stack )
1876   begin
1877     dup
1878   while
1879     unqueue_argv ( ... N a1 u1 )
1880     2r> 2swap    ( old a1 u1 )
1881     strcat
1882     s"  " strcat ( append one space ) \ XXX this gives a trailing space
1883     2>r         ( store string on the result stack )
1884   repeat
1885   drop_args
1886   2r>
1889 : set_tempoptions  ( addrN lenN ... addr1 len1 N -- addr len 1 | 0 )
1890   \ Save the first argument, if it exists and is not a flag
1891   argc if
1892     0 argv[] drop c@ [char] - <> if
1893       unqueue_argv 2>r  \ Filename
1894       1 >r              \ Filename present
1895     else
1896       0 >r              \ Filename not present
1897     then
1898   else
1899     0 >r                \ Filename not present
1900   then
1902   \ If there are other arguments, assume they are flags
1903   ?dup if
1904     concat_argv
1905     2dup s" temp_options" setenv
1906     drop free if EFREE throw then
1907   else
1908     set_defaultoptions
1909   then
1911   \ Bring back the filename, if one was provided
1912   r> if 2r> 1 else 0 then
1915 : get_arguments ( -- addrN lenN ... addr1 len1 N )
1916   0
1917   begin
1918     \ Get next word on the command line
1919     parse-word
1920   ?dup while
1921     queue_argv
1922   repeat
1923   drop ( empty string )
1926 : load_kernel_and_modules  ( args -- flag )
1927   set_tempoptions
1928   argc >r
1929   s" temp_options" getenv dup -1 <> if
1930     queue_argv
1931   else
1932     drop
1933   then
1934   load_xen
1935   ?dup 0= if ( success )
1936     r> if ( a path was passed )
1937       load_directory_or_file
1938     else
1939       standard_kernel_search
1940     then
1941     ?dup 0= if ['] load_modules catch then
1942   then
1945 only forth definitions