1 \ $NetBSD: bootblk.fth,v 1.8 2008/06/29 14:13:23 jdc Exp $
3 \ IEEE 1275 Open Firmware Boot Block
5 \ Parses disklabel and UFS and loads the file called `ofwboot'
8 \ Copyright (c) 1998 Eduardo Horvath.
11 \ Redistribution and use in source and binary forms, with or without
12 \ modification, are permitted provided that the following conditions
14 \ 1. Redistributions of source code must retain the above copyright
15 \ notice, this list of conditions and the following disclaimer.
16 \ 2. Redistributions in binary form must reproduce the above copyright
17 \ notice, this list of conditions and the following disclaimer in the
18 \ documentation and/or other materials provided with the distribution.
20 \ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 \ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 \ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 \ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 \ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 \ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 \ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 \ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 \ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 \ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 false value boot-debug?
39 \ First some housekeeping: Open /chosen and set up vectors into
42 " /chosen" find-package 0= if ." Cannot find /chosen" 0 then
43 constant chosen-phandle
45 " /openprom/client-services" find-package 0= if
46 ." Cannot find client-services" cr abort
47 then constant cif-phandle
49 defer cif-claim ( align size virt -- base )
50 defer cif-release ( size virt -- )
51 defer cif-open ( cstr -- ihandle|0 )
52 defer cif-close ( ihandle -- )
53 defer cif-read ( len adr ihandle -- #read )
54 defer cif-seek ( low high ihandle -- -1|0|1 )
55 \ defer cif-peer ( phandle -- phandle )
56 \ defer cif-getprop ( len adr cstr phandle -- )
58 : find-cif-method ( method,len -- xf )
59 cif-phandle find-method drop
62 " claim" find-cif-method to cif-claim
63 " open" find-cif-method to cif-open
64 " close" find-cif-method to cif-close
65 " read" find-cif-method to cif-read
66 " seek" find-cif-method to cif-seek
68 : twiddle ( -- ) ." ." ; \ Need to do this right. Just spit out periods for now.
74 : strcmp ( s1 l1 s2 l2 -- true:false )
75 rot tuck <> if 3drop false exit then
79 \ Move string into buffer
81 : strmov ( s1 l1 d -- d l1 )
82 dup 2over swap -rot ( s1 l1 d s1 d l1 )
87 \ Move s1 on the end of s2 and return the result
89 : strcat ( s1 l1 s2 l2 -- d tot )
90 2over swap ( s1 l1 s2 l2 l1 s1 )
91 2over + rot ( s1 l1 s2 l2 s1 d l1 )
92 move rot + ( s1 s2 len )
96 : strchr ( s1 l1 c -- s2 l2 )
98 dup 2over 0= if ( s1 l1 c c s1 )
102 -rot /c - swap ca1+ ( c l2 s2 )
108 : cstr ( ptr -- str len )
110 begin dup c@ 0<> while + repeat
120 sbsize buffer: sb-buf
121 -1 value boot-ihandle
122 dev_bsize value bsize
123 0 value raid-offset \ Offset if it's a raid-frame partition
124 false value force-raid \ Force reads from raid offset
126 : strategy ( addr size start -- nread )
127 raid-offset + bsize * 0 " seek" boot-ihandle $call-method
129 ." strategy: Seek failed" cr
132 " read" boot-ihandle $call-method
136 \ Cylinder group macros
139 : cgbase ( cg fs -- cgbase ) fs_fpg l@ * ;
140 : cgstart ( cg fs -- cgstart )
141 2dup fs_old_cgmask l@ not and ( cg fs stuff -- )
142 over fs_old_cgoffset l@ * -rot ( stuffcg fs -- )
145 : cgdmin ( cg fs -- 1st-data-block ) dup fs_dblkno l@ -rot cgstart + ;
146 : cgimin ( cg fs -- inode-block ) dup fs_iblkno l@ -rot cgstart + ;
147 : cgsblock ( cg fs -- super-block ) dup fs_sblkno l@ -rot cgstart + ;
148 : cgstod ( cg fs -- cg-block ) dup fs_cblkno l@ -rot cgstart + ;
151 \ Block and frag position macros
154 : blkoff ( pos fs -- off ) fs_qbmask x@ and ;
155 : fragoff ( pos fs -- off ) fs_qfmask x@ and ;
156 : lblktosize ( blk fs -- off ) fs_bshift l@ << ;
157 : lblkno ( pos fs -- off ) fs_bshift l@ >> ;
158 : numfrags ( pos fs -- off ) fs_fshift l@ >> ;
159 : blkroundup ( pos fs -- off ) dup fs_bmask l@ -rot fs_qbmask x@ + and ;
160 : fragroundup ( pos fs -- off ) dup fs_fmask l@ -rot fs_qfmask x@ + and ;
161 \ : fragroundup ( pos fs -- off ) tuck fs_qfmask x@ + swap fs_fmask l@ and ;
162 : fragstoblks ( pos fs -- off ) fs_fragshift l@ >> ;
163 : blkstofrags ( blk fs -- frag ) fs_fragshift l@ << ;
164 : fragnum ( fsb fs -- off ) fs_frag l@ 1- and ;
165 : blknum ( fsb fs -- off ) fs_frag l@ 1- not and ;
166 : dblksize ( lbn dino fs -- size )
168 di_size x@ ( fs lbn di_size )
169 -rot dup 1+ ( di_size fs lbn lbn+1 )
170 2over fs_bshift l@ ( di_size fs lbn lbn+1 di_size b_shift )
171 rot swap << >= ( di_size fs lbn res1 )
172 swap ndaddr >= or if ( di_size fs )
173 swap drop fs_bsize l@ exit ( size )
174 then tuck blkoff swap fragroundup ( size )
178 : ino-to-cg ( ino fs -- cg ) fs_ipg l@ / ;
179 : ino-to-fsbo ( ino fs -- fsb0 ) fs_inopb l@ mod ;
180 : ino-to-fsba ( ino fs -- ba ) \ Need to remove the stupid stack diags someday
181 2dup ( ino fs ino fs )
182 ino-to-cg ( ino fs cg )
183 over ( ino fs cg fs )
184 cgimin ( ino fs inode-blk )
185 -rot ( inode-blk ino fs )
186 tuck ( inode-blk fs ino fs )
187 fs_ipg l@ ( inode-blk fs ino ipg )
188 mod ( inode-blk fs mod )
189 swap ( inode-blk mod fs )
190 dup ( inode-blk mod fs fs )
191 fs_inopb l@ ( inode-blk mod fs inopb )
192 rot ( inode-blk fs inopb mod )
193 swap ( inode-blk fs mod inopb )
194 / ( inode-blk fs div )
195 swap ( inode-blk div fs )
196 blkstofrags ( inode-blk frag )
199 : fsbtodb ( fsb fs -- db ) fs_fsbtodb l@ << ;
205 niaddr /w* constant narraysize
208 8 field >f_ihandle \ device handle
209 8 field >f_seekp \ seek pointer
210 8 field >f_fs \ pointer to super block
211 ufs1_dinode_SIZEOF field >f_di \ copy of on-disk inode
212 8 field >f_buf \ buffer for data block
213 4 field >f_buf_size \ size of data block
214 4 field >f_buf_blkno \ block number of data block
217 file_SIZEOF buffer: the-file
218 sb-buf the-file >f_fs x!
220 ufs1_dinode_SIZEOF buffer: cur-inode
221 h# 2000 buffer: indir-block
225 \ Translate a fileblock to a disk block
227 \ We only allow single indirection
230 : block-map ( fileblock -- diskblock )
232 dup ndaddr < if ( fileblock )
233 cur-inode di_db ( arr-indx arr-start )
234 swap la+ l@ exit ( diskblock )
236 ndaddr - ( fileblock' )
237 \ Now we need to check the indirect block
238 dup sb-buf fs_nindir l@ < if ( fileblock' )
239 cur-inode di_ib l@ dup ( fileblock' indir-block indir-block )
240 indir-addr <> if ( fileblock' indir-block )
241 to indir-addr ( fileblock' )
242 indir-block ( fileblock' indir-block )
243 sb-buf dup fs_bsize l@ ( fileblock' indir-block fs fs_bsize )
244 swap indir-addr swap ( fileblock' indir-block fs_bsize indiraddr fs )
245 fsbtodb ( fileblock' indir-block fs_bsize db )
246 strategy ( fileblock' nread )
247 then ( fileblock' nread|indir-block )
248 drop \ Really should check return value
249 indir-block swap la+ l@ exit
251 dup sb-buf fs_nindir - ( fileblock'' )
252 \ Now try 2nd level indirect block -- just read twice
253 dup sb-buf fs_nindir l@ dup * < if ( fileblock'' )
254 cur-inode di_ib 1 la+ l@ ( fileblock'' indir2-block )
255 to indir-addr ( fileblock'' )
256 \ load 1st level indir block
257 indir-block ( fileblock'' indir-block )
258 sb-buf dup fs_bsize l@ ( fileblock'' indir-block fs fs_bsize )
259 swap indir-addr swap ( fileblock'' indir-block fs_bsize indiraddr fs )
260 fsbtodb ( fileblock'' indir-block fs_bsize db )
261 strategy ( fileblock'' nread )
263 dup sb-buf fs_nindir / ( fileblock'' indir-offset )
264 indir-block swap la+ l@ ( fileblock'' indirblock )
265 to indir-addr ( fileblock'' )
266 \ load 2nd level indir block
267 indir-block ( fileblock'' indir-block )
268 sb-buf dup fs_bsize l@ ( fileblock'' indir-block fs fs_bsize )
269 swap indir-addr swap ( fileblock'' indir-block fs_bsize indiraddr fs )
270 fsbtodb ( fileblock'' indir-block fs_bsize db )
271 strategy ( fileblock'' nread )
273 sb-buf fs_nindir l@ mod indir-block swap la+ l@ exit
275 ." block-map: exceeded max file size" cr
280 \ Read file into internal buffer and return pointer and len
283 0 value cur-block \ allocated dynamically in ufs-open
284 0 value cur-blocksize \ size of cur-block
288 : buf-read-file ( fs -- len buf )
289 cur-offset swap ( seekp fs )
290 2dup blkoff ( seekp fs off )
291 -rot 2dup lblkno ( off seekp fs block )
292 swap 2dup cur-inode ( off seekp block fs block fs inop )
293 swap dblksize ( off seekp block fs size )
294 rot dup cur-blockno ( off seekp fs size block block cur )
295 <> if ( off seekp fs size block )
296 block-map ( off seekp fs size diskblock )
297 dup 0= if ( off seekp fs size diskblock )
298 over cur-block swap 0 fill ( off seekp fs size diskblock )
299 boot-debug? if ." buf-read-file fell off end of file" cr then
301 2dup sb-buf fsbtodb cur-block -rot strategy ( off seekp fs size diskblock nread )
302 rot 2dup <> if " buf-read-file: short read." cr abort then
303 then ( off seekp fs diskblock nread size )
304 nip nip ( off seekp fs size )
305 else ( off seekp fs size block block cur )
306 2drop ( off seekp fs size )
308 \ dup cur-offset + to cur-offset \ Set up next xfer -- not done
309 nip nip swap - ( len )
314 \ Read inode into cur-inode -- uses cur-block
317 : read-inode ( inode fs -- )
318 twiddle ( inode fs -- inode fs )
320 cur-block ( inode fs -- inode fs buffer )
322 over ( inode fs buffer -- inode fs buffer fs )
323 fs_bsize l@ ( inode fs buffer -- inode fs buffer size )
325 2over ( inode fs buffer size -- inode fs buffer size inode fs )
326 2over ( inode fs buffer size inode fs -- inode fs buffer size inode fs buffer size )
327 2swap tuck ( inode fs buffer size inode fs buffer size -- inode fs buffer size buffer size fs inode fs )
329 ino-to-fsba ( inode fs buffer size buffer size fs inode fs -- inode fs buffer size buffer size fs fsba )
330 swap ( inode fs buffer size buffer size fs fsba -- inode fs buffer size buffer size fsba fs )
331 fsbtodb ( inode fs buffer size buffer size fsba fs -- inode fs buffer size buffer size db )
333 dup to cur-blockno ( inode fs buffer size buffer size dstart -- inode fs buffer size buffer size dstart )
334 strategy ( inode fs buffer size buffer size dstart -- inode fs buffer size nread )
335 <> if ." read-inode - residual" cr abort then
336 dup 2over ( inode fs buffer -- inode fs buffer buffer inode fs )
337 ino-to-fsbo ( inode fs buffer -- inode fs buffer buffer fsbo )
338 ufs1_dinode_SIZEOF * + ( inode fs buffer buffer fsbo -- inode fs buffer dinop )
339 cur-inode ufs1_dinode_SIZEOF move ( inode fs buffer dinop -- inode fs buffer )
340 \ clear out the old buffers
341 drop ( inode fs buffer -- inode fs )
345 \ Identify inode type
347 : is-dir? ( ufs1_dinode -- true:false ) di_mode w@ ifmt and ifdir = ;
348 : is-symlink? ( ufs1_dinode -- true:false ) di_mode w@ ifmt and iflnk = ;
353 \ Hunt for directory entry:
358 \ if entry == name return
363 : search-directory ( str len -- ino|0 )
365 begin cur-offset cur-inode di_size x@ < while ( str len )
366 sb-buf buf-read-file ( str len len buf )
367 over 0= if ." search-directory: buf-read-file zero len" cr abort then
368 swap dup cur-offset + to cur-offset ( str len buf len )
369 2dup + nip ( str len buf bufend )
370 swap 2swap rot ( bufend str len buf )
371 begin dup 4 pick < while ( bufend str len buf )
372 dup d_ino l@ 0<> if ( bufend str len buf )
373 boot-debug? if dup dup d_name swap d_namlen c@ type cr then
374 2dup d_namlen c@ = if ( bufend str len buf )
375 dup d_name 2over ( bufend str len buf dname str len )
376 comp 0= if ( bufend str len buf )
377 \ Found it -- return inode
378 d_ino l@ nip nip nip ( dino )
379 boot-debug? if ." Found it" cr then
382 then ( bufend str len buf )
383 then ( bufend str len buf )
384 dup d_reclen w@ + ( bufend str len nextbuf )
386 drop rot drop ( str len )
391 : ffs_oldcompat ( -- )
392 \ Make sure old ffs values in sb-buf are sane
393 sb-buf fs_old_npsect dup l@ sb-buf fs_old_nsect l@ max swap l!
394 sb-buf fs_old_interleave dup l@ 1 max swap l!
395 sb-buf fs_old_postblformat l@ fs_42postblfmt = if
396 8 sb-buf fs_old_nrpos l!
398 sb-buf fs_old_inodefmt l@ fs_44inodefmt < if
400 dup ndaddr * 1- sb-buf fs_maxfilesize x!
402 sb-buf fs_nindir l@ * dup ( sizebp sizebp -- )
403 sb-buf fs_maxfilesize dup x@ ( sizebp sizebp *fs_maxfilesize fs_maxfilesize -- )
404 rot ( sizebp *fs_maxfilesize fs_maxfilesize sizebp -- )
405 + ( sizebp *fs_maxfilesize new_fs_maxfilesize -- )
406 swap x! ( sizebp -- )
408 sb-buf dup fs_bmask l@ not swap fs_qbmask x!
409 sb-buf dup fs_fmask l@ not swap fs_qfmask x!
413 : read-super ( sector -- )
414 0 " seek" boot-ihandle $call-method
419 sb-buf sbsize " read" boot-ihandle $call-method
421 ." Read of superblock failed" cr
422 ." requested" space sbsize .
423 ." actual" space . cr
430 : ufs-open ( bootpath,len -- )
432 over cif-open dup 0= if ( boot-path len ihandle? )
433 ." Could not open device" space type cr
435 then ( boot-path len ihandle )
436 to boot-ihandle \ Save ihandle to boot device
439 boot-debug? if ." Force RAID superblock read" cr then
440 rf_protected dup to raid-offset
441 dev_bsize * sboff + read-super
442 sb-buf fs_magic l@ fs_magic_value <> if
443 ." Invalid superblock magic" cr
447 boot-debug? if ." Normal superblock read" cr then
449 sb-buf fs_magic l@ fs_magic_value <> if
450 boot-debug? if ." RAID superblock read" cr then
452 rf_protected dup to raid-offset
453 dev_bsize * sboff + read-super
454 sb-buf fs_magic l@ fs_magic_value <> if
455 ." Invalid superblock magic" cr
460 sb-buf fs_bsize l@ dup maxbsize > if
461 ." Superblock bsize" space . ." too large" cr
465 ." Superblock bsize < size of superblock" cr
468 ffs_oldcompat ( fs_bsize -- fs_bsize )
469 dup to cur-blocksize alloc-mem to cur-block \ Allocate cur-block
470 boot-debug? if ." ufs-open complete" cr then
474 boot-ihandle dup -1 <> if
475 cif-close -1 to boot-ihandle
478 cur-block cur-blocksize free-mem
482 : boot-path ( -- boot-path )
483 " bootpath" chosen-phandle get-package-property if
484 ." Could not find bootpath in /chosen" cr
487 decode-string 2swap 2drop
491 : boot-args ( -- boot-args )
492 " bootargs" chosen-phandle get-package-property if
493 ." Could not find bootargs in /chosen" cr
496 decode-string 2swap 2drop
500 2000 buffer: boot-path-str
501 2000 buffer: boot-path-tmp
503 : split-path ( path len -- right len left len )
504 \ Split a string at the `/'
506 dup -rot ( oldlen right len left )
507 ascii / left-parse-string ( oldlen right len left len )
508 dup 0<> if 4 roll drop exit then
509 2drop ( oldlen right len )
510 rot over = ( right len diff )
514 : find-file ( load-file len -- )
515 rootino dup sb-buf read-inode ( load-file len -- load-file len ino )
516 -rot ( load-file len ino -- pino load-file len )
518 \ For each path component
520 begin split-path dup 0<> while ( pino right len left len -- )
521 cur-inode is-dir? not if ." Inode not directory" cr false exit then
522 boot-debug? if ." Looking for" space 2dup type space ." in directory..." cr then
523 search-directory ( pino right len left len -- pino right len ino|false )
524 dup 0= if ." Bad path" cr abort then ( pino right len cino )
525 sb-buf read-inode ( pino right len )
526 cur-inode is-symlink? if \ Symlink -- follow the damn thing
527 \ Save path in boot-path-tmp
528 boot-path-tmp strmov ( pino new-right len )
530 \ Now deal with symlink
531 cur-inode di_size x@ ( pino right len linklen )
532 dup sb-buf fs_maxsymlinklen l@ ( pino right len linklen linklen maxlinklen )
533 < if \ Now join the link to the path
534 cur-inode di_db l@ ( pino right len linklen linkp )
535 swap boot-path-str strmov ( pino right len new-linkp linklen )
536 else \ Read file for symlink -- Ugh
537 \ Read link into boot-path-str
538 boot-path-str dup sb-buf fs_bsize l@
539 0 block-map ( pino right len linklen boot-path-str bsize blockno )
540 strategy drop swap ( pino right len boot-path-str linklen )
541 then ( pino right len linkp linklen )
542 \ Concatenate the two paths
543 strcat ( pino new-right newlen )
544 swap dup c@ ascii / = if \ go to root inode?
545 rot drop rootino -rot ( rino len right )
547 rot dup sb-buf read-inode ( len right pino )
548 -rot swap ( pino right len )
549 then ( pino right len )
555 : read-file ( size addr -- )
556 \ Read x bytes from a file to buffer
558 cur-offset cur-inode di_size x@ > if ." read-file EOF exceeded" cr abort then
559 sb-buf buf-read-file ( size addr len buf )
560 over 2over drop swap ( size addr len buf addr len )
561 move ( size addr len )
562 dup cur-offset + to cur-offset ( size len newaddr )
563 tuck + ( size len newaddr )
564 -rot - swap ( newaddr newsize )
570 \ According to the 1275 addendum for SPARC processors:
571 \ Default load-base is 0x4000. At least 0x8.0000 or
572 \ 512KB must be available at that address.
574 \ The Fcode bootblock can take up up to 8KB (O.K., 7.5KB)
575 \ so load programs at 0x4000 + 0x2000=> 0x6000
578 h# 6000 constant loader-base
581 \ Elf support -- find the load addr
584 : is-elf? ( hdr -- res? ) h# 7f454c46 = ;
587 \ Finally we finish it all off
590 : load-file-signon ( load-file len boot-path len -- load-file len boot-path len )
591 ." Loading file" space 2over type cr ." from device" space 2dup type cr
594 : load-file-print-size ( size -- size )
595 ." Loading" space dup . space ." bytes of file..." cr
598 : load-file ( load-file len boot-path len -- load-base )
599 boot-debug? if load-file-signon then
600 the-file file_SIZEOF 0 fill \ Clear out file structure
601 \ copy "load-file len boot-path len" in case we need to set "force-raid"
602 2over 2over ( load-file len boot-path len load-file len boot-path len )
603 ufs-open ( load-file len boot-path len load-file len )
604 find-file not if ( load-file len boot-path len )
606 true to force-raid ( )
608 2drop 2drop drop ( load-file len boot-path len )
609 ufs-open ( load-file len )
610 find-file ( true:false )
616 \ We didn't set "force-raid", discard the copies
621 \ Now we've found the file we should read it in in one big hunk
624 cur-inode di_size x@ ( file-len )
625 dup " to file-size" evaluate ( file-len )
626 boot-debug? if load-file-print-size then
628 loader-base ( buf-len addr )
629 2dup read-file ( buf-len addr )
630 ufs-close ( buf-len addr )
631 dup is-elf? if ." load-file: not an elf executable" cr abort then
633 \ Luckily the prom should be able to handle ELF executables by itself
638 : do-boot ( bootfile -- )
639 ." NetBSD IEEE 1275 Bootblock" cr
640 boot-path load-file ( -- load-base )
641 dup 0<> if " to load-base init-program" evaluate then
645 boot-args ascii V strchr 0<> swap drop if
649 boot-args ascii D strchr 0= swap drop if