1 \ Copyright (c) 1999 Daniel C. Sobral <dcs@FreeBSD.org>
   2 \ Copyright (c) 2011-2015 Devin Teske <dteske@FreeBSD.org>
   3 \ All rights reserved.
   4 \
   5 \ Redistribution and use in source and binary forms, with or without
   6 \ modification, are permitted provided that the following conditions
   7 \ are met:
   8 \ 1. Redistributions of source code must retain the above copyright
   9 \    notice, this list of conditions and the following disclaimer.
  10 \ 2. Redistributions in binary form must reproduce the above copyright
  11 \    notice, this list of conditions and the following disclaimer in the
  12 \    documentation and/or other materials provided with the distribution.
  13 \
  14 \ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  15 \ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16 \ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  17 \ ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  18 \ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  19 \ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  20 \ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  21 \ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  22 \ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  23 \ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  24 \ SUCH DAMAGE.
  25 \
  26 \ $FreeBSD$
  27 
  28 only forth definitions
  29 
  30 s" arch-i386" environment? [if] [if]
  31         s" loader_version" environment?  [if]
  32                 11 < [if]
  33                         .( Loader version 1.1+ required) cr
  34                         abort
  35                 [then]
  36         [else]
  37                 .( Could not get loader version!) cr
  38                 abort
  39         [then]
  40 [then] [then]
  41 
  42 include /boot/forth/support.4th
  43 include /boot/forth/color.4th
  44 include /boot/forth/delay.4th
  45 include /boot/forth/check-password.4th
  46 s" efi-version" getenv? [if]
  47         include /boot/forth/efi.4th
  48 [then]
  49 
  50 only forth definitions
  51 
  52 : bootmsg ( -- )
  53   loader_color? dup ( -- bool bool )
  54   if 7 fg 4 bg then
  55   ." Booting..."
  56   if me then
  57   cr
  58 ;
  59 
  60 : try-menu-unset
  61   \ menu-unset may not be present
  62   s" beastie_disable" getenv
  63   dup -1 <> if
  64     s" YES" compare-insensitive 0= if
  65       exit
  66     then
  67   else
  68     drop
  69   then
  70   s" menu-unset"
  71   sfind if
  72     execute
  73   else
  74     drop
  75   then
  76   s" menusets-unset"
  77   sfind if
  78     execute
  79   else
  80     drop
  81   then
  82 ;
  83 
  84 only forth also support-functions also builtins definitions
  85 
  86 \ the boot-args was parsed to individual options while loaded
  87 \ now compose boot-args, so the boot can set kernel arguments
  88 \ note the command line switched for boot command will cause
  89 \ environment variable boot-args to be ignored
  90 \ There are 2 larger strings, acpi-user-options and existing boot-args
  91 \ other switches are 1 byte each, so allocate boot-args+acpi + extra bytes
  92 \ for rest. Be sure to review this, if more options are to be added into
  93 \ environment.
  94 
  95 : set-boot-args { | addr len baddr blen aaddr alen -- }
  96   s" boot-args" getenv dup -1 <> if
  97     to blen to baddr
  98   else
  99     drop
 100   then
 101   s" acpi-user-options" getenv dup -1 <> if
 102     to alen to aaddr
 103   else
 104     drop
 105   then
 106 
 107   \ allocate temporary space. max is:
 108   \  7 kernel switches
 109   \  26 for acpi, so use 40 for safety
 110   blen alen 40 + + allocate abort" out of memory"
 111   to addr
 112   \ boot-addr may have file name before options, copy it to addr
 113   baddr 0<> if
 114     baddr c@ [char] - <> if
 115       baddr blen [char] - strchr                ( addr len )
 116       dup 0= if                         \ no options, copy all
 117         2drop
 118         baddr addr blen move
 119         blen to len
 120         0 to blen
 121         0 to baddr
 122       else                              ( addr len )
 123         dup blen
 124         swap -
 125         to len                          ( addr len )
 126         to blen                         ( addr )
 127         baddr addr len move             ( addr )
 128         to baddr                        \ baddr points now to first option
 129       then
 130     then
 131   then
 132   \ now add kernel switches
 133   len 0<> if
 134     bl addr len + c! len 1+ to len
 135   then
 136   [char] - addr len + c! len 1+ to len
 137 
 138   s" boot_single" getenv dup -1 <> if
 139      s" YES" compare-insensitive 0= if
 140        [char] s addr len + c! len 1+ to len
 141      then
 142   else
 143     drop
 144   then
 145   s" boot_verbose" getenv dup -1 <> if
 146      s" YES" compare-insensitive 0= if
 147        [char] v addr len + c! len 1+ to len
 148      then
 149   else
 150     drop
 151   then
 152   s" boot_kmdb" getenv dup -1 <> if
 153      s" YES" compare-insensitive 0= if
 154        [char] k addr len + c! len 1+ to len
 155      then
 156   else
 157     drop
 158   then
 159   s" boot_drop_into_kmdb" getenv dup -1 <> if
 160      s" YES" compare-insensitive 0= if
 161        [char] d addr len + c! len 1+ to len
 162      then
 163   else
 164     drop
 165   then
 166   s" boot_reconfigure" getenv dup -1 <> if
 167      s" YES" compare-insensitive 0= if
 168        [char] r addr len + c! len 1+ to len
 169      then
 170   else
 171     drop
 172   then
 173   s" boot_ask" getenv dup -1 <> if
 174      s" YES" compare-insensitive 0= if
 175        [char] a addr len + c! len 1+ to len
 176      then
 177   else
 178     drop
 179   then
 180 
 181   \ now add remining boot args if blen != 0.
 182   \ baddr[0] is '-', if baddr[1] != 'B' append to addr,
 183   \ otherwise add space then copy
 184   blen 0<> if
 185     baddr 1+ c@ [char] B = if
 186       addr len + 1- c@ [char] - = if     \ if addr[len -1] == '-'
 187         baddr 1+ to baddr
 188         blen 1- to blen
 189       else
 190         bl addr len + c! len 1+ to len
 191       then
 192     else
 193       baddr 1+ to baddr
 194       blen 1- to blen
 195     then
 196     baddr addr len + blen move
 197     len blen + to len
 198     0 to baddr
 199     0 to blen
 200   then
 201   \ last part - add acpi.
 202   alen 0<> if
 203     addr len + 1- c@ [char] - <> if
 204       bl addr len + c! len 1+ to len
 205       [char] - addr len + c! len 1+ to len
 206     then
 207     s" B acpi-user-options=" dup -rot           ( len addr len )
 208     addr len + swap move                        ( len )
 209     len + to len
 210     aaddr addr len + alen move
 211     len alen + to len
 212   then
 213 
 214   \ check for left over '-'
 215   addr len 1- + c@ [char] - = if
 216     len 1- to len
 217                                 \ but now we may also have left over ' '
 218     len if ( len <> 0 )
 219       addr len 1- + c@ bl = if
 220         len 1- to len
 221       then
 222     then
 223   then
 224 
 225   \ if len != 0, set boot-args
 226   len 0<> if
 227     addr len s" boot-args" setenv
 228   then
 229   addr free drop
 230 ;
 231 
 232 : boot
 233   0= if ( interpreted ) get_arguments then
 234   set-boot-args
 235 
 236   \ Unload only if a path was passed. Paths start with /
 237   dup if
 238     >r over r> swap
 239     c@ [char] / = if
 240       0 1 unload drop
 241     else
 242       s" kernelname" getenv? if ( a kernel has been loaded )
 243         try-menu-unset
 244         bootmsg 1 boot exit
 245       then
 246       load_kernel_and_modules
 247       ?dup if exit then
 248       try-menu-unset
 249       bootmsg 0 1 boot exit
 250     then
 251   else
 252     s" kernelname" getenv? if ( a kernel has been loaded )
 253       try-menu-unset
 254       bootmsg 1 boot exit
 255     then
 256     load_kernel_and_modules
 257     ?dup if exit then
 258     try-menu-unset
 259     bootmsg 0 1 boot exit
 260   then
 261   load_kernel_and_modules
 262   ?dup 0= if bootmsg 0 1 boot then
 263 ;
 264 
 265 \ ***** boot-conf
 266 \
 267 \       Prepares to boot as specified by loaded configuration files.
 268 
 269 : boot-conf
 270   0= if ( interpreted ) get_arguments then
 271   0 1 unload drop
 272   load_kernel_and_modules
 273   ?dup 0= if 0 1 autoboot then
 274 ;
 275 
 276 also forth definitions previous
 277 
 278 builtin: boot
 279 builtin: boot-conf
 280 
 281 only forth definitions also support-functions
 282 
 283 \ 
 284 \ in case the boot-args is set, parse it and extract following options:
 285 \ -a to boot_ask=YES
 286 \ -s to boot_single=YES
 287 \ -v to boot_verbose=YES
 288 \ -k to boot_kmdb=YES
 289 \ -d to boot_drop_into_kmdb=YES
 290 \ -r to boot_reconfigure=YES
 291 \ -B acpi-user-options=X to acpi-user-options=X
 292 \ 
 293 \ This is needed so that the menu can manage these options. Unfortunately, this
 294 \ also means that boot-args will override previously set options, but we have no
 295 \ way to control the processing order here. boot-args will be rebuilt at boot.
 296 \ 
 297 \ NOTE: The best way to address the order is to *not* set any above options
 298 \ in boot-args.
 299 
 300 : parse-boot-args  { | baddr blen -- }
 301   s" boot-args" getenv dup -1 = if drop exit then
 302   to blen
 303   to baddr
 304 
 305   baddr blen
 306 
 307   \ loop over all instances of switch blocks, starting with '-'
 308   begin
 309     [char] - strchr
 310     2dup to blen to baddr
 311     dup 0<>
 312   while                         ( addr len ) \ points to -
 313     \ block for switch B. keep it on top of the stack for case
 314     \ the property list will get empty.
 315 
 316     over 1+ c@ [char] B = if
 317         2dup                    \ save "-B ...." in case options is empty
 318         2 - swap 2 +            ( addr len len-2 addr+2 ) \ skip -B
 319 
 320       begin                     \ skip spaces
 321         dup c@ bl =
 322       while
 323         1+ swap 1- swap
 324       repeat
 325 
 326                                 ( addr len len' addr' )
 327       \ its 3 cases now: end of string, -switch, or option list
 328 
 329       over 0= if                \ end of string, remove trailing -B
 330         2drop                   ( addr len )
 331         swap 0 swap c!          \ store 0 at -B
 332         blen swap               ( blen len )
 333         -                       ( rem )
 334         baddr swap              ( addr rem )
 335         dup 0= if
 336           s" boot-args" unsetenv
 337           2drop
 338           exit
 339         then
 340                                 \ trailing space(s)
 341         begin
 342           over                  ( addr rem addr )
 343           over + 1-             ( addr rem addr+rem-1 )
 344           c@ bl =
 345         while
 346           1- swap               ( rem-1 addr )
 347           over                  ( rem-1 addr rem-1 )
 348           over +                ( rem-1 addr addr+rem-1 )
 349           0 swap c!
 350           swap
 351         repeat
 352         s" boot-args" setenv
 353         recurse                 \ restart
 354         exit
 355       then
 356                                 ( addr len len' addr' )
 357       dup c@ [char] - = if      \ it is switch. set to boot-args
 358         swap s" boot-args" setenv
 359         2drop
 360         recurse                 \ restart
 361         exit
 362       then
 363                                 ( addr len len' addr' )
 364       \ its options string "option1,option2,... -..."
 365       \ cut acpi-user-options=xxx and restart the parser
 366       \ or skip to next option block
 367       begin
 368         dup c@ dup 0<> swap bl <> and \ stop if space or 0
 369       while
 370         dup 18 s" acpi-user-options=" compare 0= if     \ matched
 371                                 ( addr len len' addr' )
 372           \ addr' points to acpi options, find its end [',' or ' ' or 0 ]
 373           \ set it as acpi-user-options and move remaining to addr'
 374           2dup                  ( addr len len' addr' len' addr' )
 375           \ skip to next option in list
 376           \ loop to first , or bl or 0
 377           begin
 378             dup c@ [char] , <> >r
 379             dup c@ bl <> >r
 380             dup c@ 0<> r> r> and and
 381           while
 382             1+ swap 1- swap
 383           repeat
 384                                 ( addr len len' addr' len" addr" )
 385           >r >r           ( addr len len' addr' R: addr" len" )
 386           over r@ -             ( addr len len' addr' proplen R: addr" len" )
 387           dup 5 +               ( addr len len' addr' proplen proplen+5 )
 388           allocate abort" out of memory"
 389 
 390           0 s" set " strcat     ( addr len len' addr' proplen caddr clen )
 391           >r >r 2dup r> r> 2swap strcat ( addr len len' addr' proplen caddr clen )
 392           2dup + 0 swap c!      \ terminate with 0
 393           2dup evaluate drop free drop
 394                                 ( addr len len' addr' proplen R: addr" len" )
 395           \ acpi-user-options is set, now move remaining string to its place.
 396           \ addr: -B, addr': acpi... addr": reminder
 397           swap                  ( addr len len' proplen addr' )
 398           r> r>                   ( addr len len' proplen addr' len" addr" )
 399           dup c@ [char] , = if
 400             \ skip , and move addr" to addr'
 401             1+ swap 1-          ( addr len len' proplen addr' addr" len" )
 402             rot swap 1+ move    ( addr len len' proplen )
 403           else  \ its bl or 0   ( addr len len' proplen addr' len" addr" )
 404             \ for both bl and 0 we need to copy to addr'-1 to remove
 405             \ comma, then reset boot-args, and recurse will clear -B
 406             \ if there are no properties left.
 407             dup c@ 0= if
 408               2drop             ( addr len len' proplen addr' )
 409               1- 0 swap c!      ( addr len len' proplen )
 410             else
 411               >r >r               ( addr len len' proplen addr' R: addr" len" )
 412               1- swap 1+ swap
 413               r> r>               ( addr len len' proplen addr' len" addr" )
 414               rot rot move      ( addr len len' proplen )
 415             then
 416           then
 417 
 418           2swap 2drop           ( len' proplen )
 419           nip                   ( proplen )
 420           baddr blen rot -
 421           s" boot-args" setenv
 422           recurse
 423           exit
 424         else
 425                                 ( addr len len' addr' )
 426           \ not acpi option, skip to next option in list
 427           \ loop to first , or bl or 0
 428           begin
 429             dup c@ [char] , <> >r
 430             dup c@ bl <> >r
 431             dup c@ 0<> r> r> and and
 432           while
 433             1+ swap 1- swap
 434           repeat
 435           \ if its ',', skip over
 436           dup c@ [char] , = if
 437             1+ swap 1- swap
 438           then
 439         then
 440       repeat
 441                                 ( addr len len' addr' )
 442       \ this block is done, remove addr and len from stack
 443       2swap 2drop swap
 444     then
 445 
 446     over c@ [char] - = if       ( addr len )
 447       2dup 1- swap 1+           ( addr len len' addr' )
 448       begin                     \ loop till ' ' or 0
 449         dup c@ dup 0<> swap bl <> and
 450       while
 451         dup c@ [char] s = if
 452           s" set boot_single=YES" evaluate TRUE
 453         else dup c@ [char] v = if
 454           s" set boot_verbose=YES" evaluate TRUE
 455         else dup c@ [char] k = if
 456           s" set boot_kmdb=YES" evaluate TRUE
 457         else dup c@ [char] d = if
 458           s" set boot_drop_into_kmdb=YES" evaluate TRUE
 459         else dup c@ [char] r = if
 460           s" set boot_reconfigure=YES" evaluate TRUE
 461         else dup c@ [char] a = if
 462           s" set boot_ask=YES" evaluate TRUE
 463         then then then then then then
 464         dup TRUE = if
 465           drop
 466           dup >r             ( addr len len' addr' R: addr' )
 467           1+ swap 1-            ( addr len addr'+1 len'-1 R: addr' )
 468           r> swap move               ( addr len )
 469 
 470           2drop baddr blen 1-
 471           \ check if we have space after '-', if so, drop '- '
 472           swap dup 1+ c@ bl = if
 473               2 + swap 2 -
 474           else
 475               swap
 476           then
 477           dup dup 0= swap 1 = or if     \ empty or only '-' is left.
 478             2drop
 479             s" boot-args" unsetenv
 480             exit
 481           else
 482             s" boot-args" setenv
 483           then
 484           recurse
 485           exit
 486         then
 487         1+ swap 1- swap
 488       repeat
 489 
 490       2swap 2drop
 491       dup c@ 0= if              \ end of string
 492         2drop
 493         exit
 494       else
 495         swap
 496       then
 497     then
 498   repeat
 499 
 500   2drop
 501 ;
 502 
 503 \ ***** start
 504 \
 505 \       Initializes support.4th global variables, sets loader_conf_files,
 506 \       processes conf files, and, if any one such file was successfully
 507 \       read to the end, loads kernel and modules.
 508 
 509 : start  ( -- ) ( throws: abort & user-defined )
 510   s" /boot/defaults/loader.conf" initialize
 511   include_bootenv
 512   include_conf_files
 513   include_transient
 514   \ If the user defined a post-initialize hook, call it now
 515   s" post-initialize" sfind if execute else drop then
 516   parse-boot-args
 517   \ Will *NOT* try to load kernel and modules if no configuration file
 518   \ was successfully loaded!
 519   any_conf_read? if
 520     s" loader_delay" getenv -1 = if
 521       load_xen_throw
 522       load_kernel
 523       load_modules
 524     else
 525       drop
 526       ." Loading Kernel and Modules (Ctrl-C to Abort)" cr
 527       s" also support-functions" evaluate
 528       s" set delay_command='load_xen_throw load_kernel load_modules'" evaluate
 529       s" set delay_showdots" evaluate
 530       delay_execute
 531     then
 532   then
 533 ;
 534 
 535 \ ***** initialize
 536 \
 537 \       Overrides support.4th initialization word with one that does
 538 \       everything start one does, short of loading the kernel and
 539 \       modules. Returns a flag.
 540 
 541 : initialize ( -- flag )
 542   s" /boot/defaults/loader.conf" initialize
 543   include_bootenv
 544   include_conf_files
 545   include_transient
 546   \ If the user defined a post-initialize hook, call it now
 547   s" post-initialize" sfind if execute else drop then
 548   parse-boot-args
 549   any_conf_read?
 550 ;
 551 
 552 \ ***** read-conf
 553 \
 554 \       Read a configuration file, whose name was specified on the command
 555 \       line, if interpreted, or given on the stack, if compiled in.
 556 
 557 : (read-conf)  ( addr len -- )
 558   conf_files string=
 559   include_conf_files \ Will recurse on new loader_conf_files definitions
 560 ;
 561 
 562 : read-conf  ( <filename> | addr len -- ) ( throws: abort & user-defined )
 563   state @ if
 564     \ Compiling
 565     postpone (read-conf)
 566   else
 567     \ Interpreting
 568     bl parse (read-conf)
 569   then
 570 ; immediate
 571 
 572 \ show, enable, disable, toggle module loading. They all take module from
 573 \ the next word
 574 
 575 : set-module-flag ( module_addr val -- ) \ set and print flag
 576   over module.flag !
 577   dup module.name strtype
 578   module.flag @ if ."  will be loaded" else ."  will not be loaded" then cr
 579 ;
 580 
 581 : enable-module find-module ?dup if true set-module-flag then ;
 582 
 583 : disable-module find-module ?dup if false set-module-flag then ;
 584 
 585 : toggle-module find-module ?dup if dup module.flag @ 0= set-module-flag then ;
 586 
 587 \ ***** show-module
 588 \
 589 \       Show loading information about a module.
 590 
 591 : show-module ( <module> -- ) find-module ?dup if show-one-module then ;
 592 
 593 : set-module-path ( addr len <module> -- )
 594   find-module ?dup if
 595     module.loadname string=
 596   then
 597 ;
 598 
 599 \ Words to be used inside configuration files
 600 
 601 : retry false ;         \ For use in load error commands
 602 : ignore true ;         \ For use in load error commands
 603 
 604 \ Return to strict forth vocabulary
 605 
 606 : #type
 607   over - >r
 608   type
 609   r> spaces
 610 ;
 611 
 612 : .? 2 spaces 2swap 15 #type 2 spaces type cr ;
 613 
 614 : ?
 615   ['] ? execute
 616   s" boot-conf" s" load kernel and modules, then autoboot" .?
 617   s" read-conf" s" read a configuration file" .?
 618   s" enable-module" s" enable loading of a module" .?
 619   s" disable-module" s" disable loading of a module" .?
 620   s" toggle-module" s" toggle loading of a module" .?
 621   s" show-module" s" show module load data" .?
 622   s" try-include" s" try to load/interpret files" .?
 623   s" beadm" s" list or activate Boot Environments" .?
 624 ;
 625 
 626 : try-include ( -- ) \ see loader.4th(8)
 627   ['] include ( -- xt ) \ get the execution token of `include'
 628   catch ( xt -- exception# | 0 ) if \ failed
 629     LF parse ( c -- s-addr/u ) 2drop \ advance >in to EOL (drop data)
 630     \ ... prevents words unused by `include' from being interpreted
 631   then
 632 ; immediate \ interpret immediately for access to `source' (aka tib)
 633 
 634 include /boot/forth/beadm.4th
 635 only forth definitions