bootblk.fth revision 1.2
1\	$NetBSD: bootblk.fth,v 1.2 2001/06/13 10:46:02 wiz Exp $
2\
3\	IEEE 1275 Open Firmware Boot Block
4\
5\	Parses disklabel and UFS and loads the file called `ofwboot'
6\
7\
8\	Copyright (c) 1998 Eduardo Horvath.
9\	All rights reserved.
10\
11\	Redistribution and use in source and binary forms, with or without
12\	modification, are permitted provided that the following conditions
13\	are met:
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.
19\	3. All advertising materials mentioning features or use of this software
20\	   must display the following acknowledgement:
21\	     This product includes software developed by Eduardo Horvath.
22\	4. The name of the author may not be used to endorse or promote products
23\	   derived from this software without specific prior written permission
24\
25\	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26\	IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27\	OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28\	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29\	INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30\	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31\	DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32\	THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33\	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34\	THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35\
36
37offset16
38hex
39headers
40
41false value boot-debug?
42
43\
44\ First some housekeeping:  Open /chosen and set up vectors into
45\	client-services
46
47" /chosen" find-package 0=  if ." Cannot find /chosen" 0 then
48constant chosen-phandle
49
50" /openprom/client-services" find-package 0=  if 
51	." Cannot find client-services" cr abort
52then constant cif-phandle
53
54defer cif-claim ( align size virt -- base )
55defer cif-release ( size virt -- )
56defer cif-open ( cstr -- ihandle|0 )
57defer cif-close ( ihandle -- )
58defer cif-read ( len adr ihandle -- #read )
59defer cif-seek ( low high ihandle -- -1|0|1 )
60\ defer cif-peer ( phandle -- phandle )
61\ defer cif-getprop ( len adr cstr phandle -- )
62
63: find-cif-method ( method,len -- xf )
64   cif-phandle find-method drop 
65;
66
67" claim" find-cif-method to cif-claim
68" open" find-cif-method to cif-open
69" close" find-cif-method to cif-close
70" read" find-cif-method to cif-read
71" seek" find-cif-method to cif-seek
72
73: twiddle ( -- ) ." ." ; \ Need to do this right.  Just spit out periods for now.
74
75\
76\ Support routines
77\
78
79: strcmp ( s1 l1 s2 l2 -- true:false )
80   rot tuck <> if  3drop false exit then
81   comp 0=
82;
83
84\ Move string into buffer
85
86: strmov ( s1 l1 d -- d l1 )
87   dup 2over swap -rot		( s1 l1 d s1 d l1 )
88   move				( s1 l1 d )
89   rot drop swap
90;
91
92\ Move s1 on the end of s2 and return the result
93
94: strcat ( s1 l1 s2 l2 -- d tot )
95   2over swap 				( s1 l1 s2 l2 l1 s1 )
96   2over + rot				( s1 l1 s2 l2 s1 d l1 )
97   move rot + 				( s1 s2 len )
98   rot drop				( s2 len )
99;
100
101: strchr ( s1 l1 c -- s2 l2 )
102   begin
103      dup 2over 0= if			( s1 l1 c c s1  )
104         2drop drop exit then
105      c@ = if				( s1 l1 c )
106         drop exit then
107      -rot /c - swap ca1+		( c l2 s2 )
108     swap rot
109  again
110;
111
112   
113: cstr ( ptr -- str len )
114   dup 
115   begin dup c@ 0<>  while + repeat
116   over -
117;
118
119\
120\ BSD FFS parameters
121\
122
123fload	assym.fth.h
124
125sbsize buffer: sb-buf
126-1 value boot-ihandle
127dev_bsize value bsize
128
129: strategy ( addr size start -- nread )
130   bsize * 0 " seek" boot-ihandle $call-method
131   -1 = if 
132      ." strategy: Seek failed" cr
133      abort
134   then
135   " read" boot-ihandle $call-method
136;
137
138\
139\ Cylinder group macros
140\
141
142: cgbase ( cg fs -- cgbase ) fs_fpg l@ * ;
143: cgstart ( cg fs -- cgstart ) 
144   2dup fs_cgmask l@ not and		( cg fs stuff -- )
145   over fs_cgoffset l@ * -rot		( stuffcg fs -- )
146   cgbase +
147;
148: cgdmin ( cg fs -- 1st-data-block ) dup fs_dblkno l@ -rot cgstart + ;
149: cgimin ( cg fs -- inode-block ) dup fs_iblkno l@ -rot cgstart + ;
150: cgsblock ( cg fs -- super-block ) dup fs_sblkno l@ -rot cgstart + ;
151: cgstod ( cg fs -- cg-block ) dup fs_cblkno l@ -rot cgstart + ;
152
153\
154\ Block and frag position macros
155\
156
157: blkoff ( pos fs -- off ) fs_qbmask x@ and ;
158: fragoff ( pos fs -- off ) fs_qfmask x@ and ;
159: lblktosize ( blk fs -- off ) fs_bshift l@ << ;
160: lblkno ( pos fs -- off ) fs_bshift l@ >> ;
161: numfrags ( pos fs -- off ) fs_fshift l@ >> ;
162: blkroundup ( pos fs -- off ) dup fs_bmask l@ -rot fs_qbmask x@ + and ;
163: fragroundup ( pos fs -- off ) dup fs_fmask l@ -rot fs_qfmask x@ + and ;
164\ : fragroundup ( pos fs -- off ) tuck fs_qfmask x@ + swap fs_fmask l@ and ;
165: fragstoblks ( pos fs -- off ) fs_fragshift l@ >> ;
166: blkstofrags ( blk fs -- frag ) fs_fragshift l@ << ;
167: fragnum ( fsb fs -- off ) fs_frag l@ 1- and ;
168: blknum ( fsb fs -- off ) fs_frag l@ 1- not and ;
169: dblksize ( lbn dino fs -- size )
170   -rot 				( fs lbn dino )
171   di_size x@				( fs lbn di_size )
172   -rot dup 1+				( di_size fs lbn lbn+1 )
173   2over fs_bshift l@			( di_size fs lbn lbn+1 di_size b_shift )
174   rot swap <<	>=			( di_size fs lbn res1 )
175   swap ndaddr >= or if			( di_size fs )
176      swap drop fs_bsize l@ exit	( size )
177   then	tuck blkoff swap fragroundup	( size )
178;
179
180
181: ino-to-cg ( ino fs -- cg ) fs_ipg l@ / ;
182: ino-to-fsbo ( ino fs -- fsb0 ) fs_inopb l@ mod ;
183: ino-to-fsba ( ino fs -- ba )	\ Need to remove the stupid stack diags someday
184   2dup 				( ino fs ino fs )
185   ino-to-cg				( ino fs cg )
186   over					( ino fs cg fs )
187   cgimin				( ino fs inode-blk )
188   -rot					( inode-blk ino fs )
189   tuck 				( inode-blk fs ino fs )
190   fs_ipg l@ 				( inode-blk fs ino ipg )
191   mod					( inode-blk fs mod )
192   swap					( inode-blk mod fs )
193   dup 					( inode-blk mod fs fs )
194   fs_inopb l@ 				( inode-blk mod fs inopb )
195   rot 					( inode-blk fs inopb mod )
196   swap					( inode-blk fs mod inopb )
197   /					( inode-blk fs div )
198   swap					( inode-blk div fs )
199   blkstofrags				( inode-blk frag )
200   +
201;
202: fsbtodb ( fsb fs -- db ) fs_fsbtodb l@ << ;
203
204\
205\ File stuff
206\
207
208niaddr /w* constant narraysize
209
210struct 
211   8		field	>f_ihandle	\ device handle
212   8 		field 	>f_seekp	\ seek pointer
213   8 		field 	>f_fs		\ pointer to super block
214   dinode_SIZEOF 	field 	>f_di	\ copy of on-disk inode
215   8		field	>f_buf		\ buffer for data block
216   4		field 	>f_buf_size	\ size of data block
217   4		field	>f_buf_blkno	\ block number of data block
218constant file_SIZEOF
219
220file_SIZEOF buffer: the-file
221sb-buf the-file >f_fs x!
222
223dinode_SIZEOF buffer: cur-inode
224h# 2000 buffer: indir-block
225-1 value indir-addr
226
227\
228\ Translate a fileblock to a disk block
229\
230\ We only allow single indirection
231\
232
233: block-map ( fileblock -- diskblock )
234   \ Direct block?
235   dup ndaddr <  if 			( fileblock )
236      cur-inode di_db			( arr-indx arr-start )
237      swap la+ l@ exit			( diskblock )
238   then 				( fileblock )
239   ndaddr -				( fileblock' )
240   \ Now we need to check the indirect block
241   dup sb-buf fs_nindir l@ <  if	( fileblock' )
242      cur-inode di_ib l@ dup		( fileblock' indir-block indir-block )
243      indir-addr <>  if 		( fileblock' indir-block )
244         to indir-addr			( fileblock' )
245         indir-block 			( fileblock' indir-block )
246         sb-buf dup fs_bsize l@		( fileblock' indir-block fs fs_bsize )
247         swap indir-addr swap		( fileblock' indir-block fs_bsize indiraddr fs )
248         fsbtodb 			( fileblock' indir-block fs_bsize db )
249         strategy			( fileblock' nread )
250      then				( fileblock' nread|indir-block )
251      drop \ Really should check return value
252      indir-block swap la+ l@ exit
253   then
254   dup sb-buf fs_nindir -		( fileblock'' )
255   \ Now try 2nd level indirect block -- just read twice 
256   dup sb-buf fs_nindir l@ dup * < if	( fileblock'' )
257      cur-inode di_ib 1 la+ l@		( fileblock'' indir2-block )
258      to indir-addr			( fileblock'' )
259      \ load 1st level indir block 
260      indir-block 			( fileblock'' indir-block )
261      sb-buf dup fs_bsize l@		( fileblock'' indir-block fs fs_bsize )
262      swap indir-addr swap		( fileblock'' indir-block fs_bsize indiraddr fs )
263      fsbtodb 				( fileblock'' indir-block fs_bsize db )
264      strategy				( fileblock'' nread )
265      drop				( fileblock'' )
266      dup sb-buf fs_nindir /		( fileblock'' indir-offset )
267      indir-block swap la+ l@		( fileblock'' indirblock )
268      to indir-addr			( fileblock'' )
269      \ load 2nd level indir block
270      indir-block 			( fileblock'' indir-block )
271      sb-buf dup fs_bsize l@		( fileblock'' indir-block fs fs_bsize )
272      swap indir-addr swap		( fileblock'' indir-block fs_bsize indiraddr fs )
273      fsbtodb 				( fileblock'' indir-block fs_bsize db )
274      strategy				( fileblock'' nread )
275      drop				( fileblock'' )
276      sb-buf fs_nindir l@ mod indir-block swap la+ l@ exit
277   then
278   ." block-map: exceeded max file size" cr
279   abort
280;
281
282\
283\ Read file into internal buffer and return pointer and len
284\
285
2862000 buffer: cur-block		\ Why do dynamic allocation?
287-1 value cur-blockno
2880 value cur-offset
289
290: buf-read-file ( fs -- len buf )
291   cur-offset swap			( seekp fs )
292   2dup blkoff				( seekp fs off )
293   -rot 2dup lblkno			( off seekp fs block )
294   swap 2dup cur-inode			( off seekp block fs block fs inop )
295   swap dblksize			( off seekp block fs size )
296   rot dup cur-blockno			( off seekp fs size block block cur )
297   <>  if 				( off seekp fs size block )
298      block-map				( off seekp fs size diskblock )
299      dup 0=  if			( off seekp fs size diskblock )
300         over cur-block swap 0 fill	( off seekp fs size diskblock )
301         boot-debug?  if ." buf-read-file fell off end of file" cr then
302      else
303         2dup sb-buf fsbtodb cur-block -rot strategy	( off seekp fs size diskblock nread )
304         rot 2dup <>  if " buf-read-file: short read." cr abort then
305      then				( off seekp fs diskblock nread size )
306      nip nip				( off seekp fs size )
307   else					( off seekp fs size block block cur )
308      2drop				( off seekp fs size )
309   then
310\   dup cur-offset + to cur-offset	\ Set up next xfer -- not done
311   nip nip swap -			( len )
312   cur-block
313;
314
315\
316\ Read inode into cur-inode -- uses cur-block
317\ 
318
319: read-inode ( inode fs -- )
320   twiddle				( inode fs -- inode fs )
321
322   cur-block				( inode fs -- inode fs buffer )
323
324   over					( inode fs buffer -- inode fs buffer fs )
325   fs_bsize l@				( inode fs buffer -- inode fs buffer size )
326
327   2over				( inode fs buffer size -- inode fs buffer size inode fs )
328   2over				( inode fs buffer size inode fs -- inode fs buffer size inode fs buffer size )
329   2swap tuck				( inode fs buffer size inode fs buffer size -- inode fs buffer size buffer size fs inode fs )
330
331   ino-to-fsba 				( inode fs buffer size buffer size fs inode fs -- inode fs buffer size buffer size fs fsba )
332   swap					( inode fs buffer size buffer size fs fsba -- inode fs buffer size buffer size fsba fs )
333   fsbtodb				( inode fs buffer size buffer size fsba fs -- inode fs buffer size buffer size db )
334
335   dup to cur-blockno			( inode fs buffer size buffer size dstart -- inode fs buffer size buffer size dstart )
336   strategy				( inode fs buffer size buffer size dstart -- inode fs buffer size nread )
337   <>  if ." read-inode - residual" cr abort then
338   dup 2over				( inode fs buffer -- inode fs buffer buffer inode fs )
339   ino-to-fsbo				( inode fs buffer -- inode fs buffer buffer fsbo )
340   dinode_SIZEOF * +			( inode fs buffer buffer fsbo -- inode fs buffer dinop )
341   cur-inode dinode_SIZEOF move 	( inode fs buffer dinop -- inode fs buffer )
342	\ clear out the old buffers
343   drop					( inode fs buffer -- inode fs )
344   2drop
345;
346
347\ Identify inode type
348
349: is-dir? ( dinode -- true:false ) di_mode w@ ifmt and ifdir = ;
350: is-symlink? ( dinode -- true:false ) di_mode w@ ifmt and iflnk = ;
351
352
353
354\
355\ Hunt for directory entry:
356\ 
357\ repeat
358\    load a buffer
359\    while entries do
360\       if entry == name return
361\       next entry
362\ until no buffers
363\
364
365: search-directory ( str len -- ino|0 )
366   0 to cur-offset
367   begin cur-offset cur-inode di_size x@ < while	( str len )
368      sb-buf buf-read-file		( str len len buf )
369      over 0=  if ." search-directory: buf-read-file zero len" cr abort then
370      swap dup cur-offset + to cur-offset	( str len buf len )
371      2dup + nip			( str len buf bufend )
372      swap 2swap rot			( bufend str len buf )
373      begin dup 4 pick < while		( bufend str len buf )
374         dup d_ino l@ 0<>  if 		( bufend str len buf )
375            boot-debug?  if dup dup d_name swap d_namlen c@ type cr then
376            2dup d_namlen c@ =  if	( bufend str len buf )
377               dup d_name 2over		( bufend str len buf dname str len )
378               comp 0= if		( bufend str len buf )
379                  \ Found it -- return inode
380                  d_ino l@ nip nip nip	( dino )
381                  boot-debug?  if ." Found it" cr then 
382                  exit 			( dino )
383               then
384            then			( bufend str len buf )
385         then				( bufend str len buf )
386         dup d_reclen w@ +		( bufend str len nextbuf )
387      repeat
388      drop rot drop			( str len )
389   repeat
390   2drop 2drop 0			( 0 )
391;
392
393: ffs_oldcompat ( -- )
394\ Make sure old ffs values in sb-buf are sane
395   sb-buf fs_npsect dup l@ sb-buf fs_nsect l@ max swap l!
396   sb-buf fs_interleave dup l@ 1 max swap l!
397   sb-buf fs_postblformat l@ fs_42postblfmt =  if
398      8 sb-buf fs_nrpos l!
399   then
400   sb-buf fs_inodefmt l@ fs_44inodefmt <  if
401      sb-buf fs_bsize l@ 
402      dup ndaddr * 1- sb-buf fs_maxfilesize x!
403      niaddr 0 ?do
404	sb-buf fs_nindir l@ * dup	( sizebp sizebp -- )
405	sb-buf fs_maxfilesize dup x@	( sizebp sizebp *fs_maxfilesize fs_maxfilesize -- )
406	rot ( sizebp *fs_maxfilesize fs_maxfilesize sizebp -- )
407	+ ( sizebp *fs_maxfilesize new_fs_maxfilesize  -- ) swap x! ( sizebp -- )
408      loop drop ( -- )
409      sb-buf dup fs_bmask l@ not swap fs_qbmask x!
410      sb-buf dup fs_fmask l@ not swap fs_qfmask x!
411   then
412;
413
414: ufs-open ( bootpath,len -- )
415   boot-ihandle -1 =  if
416      over cif-open dup 0=  if 		( boot-path len ihandle? )
417         ." Could not open device" space type cr 
418         abort
419      then 				( boot-path len ihandle )
420      to boot-ihandle			\ Save ihandle to boot device
421   then 2drop
422   sboff 0 " seek" boot-ihandle $call-method
423   -1 = if 
424      ." Seek failed" cr
425      abort
426   then
427   sb-buf sbsize " read" boot-ihandle $call-method
428   dup sbsize <>  if
429      ." Read of superblock failed" cr
430      ." requested" space sbsize .
431      ." actual" space . cr
432      abort
433   else 
434      drop
435   then
436   sb-buf fs_magic l@ fs_magic_value <>  if
437      ." Invalid superblock magic" cr
438      abort
439   then
440   sb-buf fs_bsize l@ dup maxbsize >  if
441      ." Superblock bsize" space . ." too large" cr
442      abort
443   then 
444   fs_SIZEOF <  if
445      ." Superblock bsize < size of superblock" cr
446      abort
447   then
448   ffs_oldcompat
449   boot-debug?  if ." ufs-open complete" cr then
450;
451
452: ufs-close ( -- ) 
453   boot-ihandle dup -1 <>  if
454      cif-close -1 to boot-ihandle 
455   then
456;
457
458: boot-path ( -- boot-path )
459   " bootpath" chosen-phandle get-package-property  if
460      ." Could not find bootpath in /chosen" cr
461      abort
462   else
463      decode-string 2swap 2drop
464   then
465;
466
467: boot-args ( -- boot-args )
468   " bootargs" chosen-phandle get-package-property  if
469      ." Could not find bootargs in /chosen" cr
470      abort
471   else
472      decode-string 2swap 2drop
473   then
474;
475
4762000 buffer: boot-path-str
4772000 buffer: boot-path-tmp
478
479: split-path ( path len -- right len left len )
480\ Split a string at the `/'
481   begin
482      dup -rot				( oldlen right len left )
483      ascii / left-parse-string		( oldlen right len left len )
484      dup 0<>  if 4 roll drop exit then
485      2drop				( oldlen right len )
486      rot over =			( right len diff )
487   until
488;
489
490: find-file ( load-file len -- )
491   rootino dup sb-buf read-inode	( load-file len -- load-file len ino )
492   -rot					( load-file len ino -- pino load-file len )
493   \
494   \ For each path component
495   \ 
496   begin split-path dup 0<> while	( pino right len left len -- )
497      cur-inode is-dir? not  if ." Inode not directory" cr abort then
498      boot-debug?  if ." Looking for" space 2dup type space ." in directory..." cr then
499      search-directory			( pino right len left len -- pino right len ino|false )
500      dup 0=  if ." Bad path" cr abort then	( pino right len cino )
501      sb-buf read-inode			( pino right len )
502      cur-inode is-symlink?  if		\ Symlink -- follow the damn thing
503         \ Save path in boot-path-tmp
504         boot-path-tmp strmov		( pino new-right len )
505
506         \ Now deal with symlink
507         cur-inode di_size x@		( pino right len linklen )
508         dup sb-buf fs_maxsymlinklen l@	( pino right len linklen linklen maxlinklen )
509         <  if				\ Now join the link to the path
510            cur-inode di_shortlink l@	( pino right len linklen linkp )
511            swap boot-path-str strmov	( pino right len new-linkp linklen )
512         else				\ Read file for symlink -- Ugh
513            \ Read link into boot-path-str
514            boot-path-str dup sb-buf fs_bsize l@
515            0 block-map			( pino right len linklen boot-path-str bsize blockno )
516            strategy drop swap		( pino right len boot-path-str linklen )
517         then 				( pino right len linkp linklen )
518         \ Concatenate the two paths
519         strcat				( pino new-right newlen )
520         swap dup c@ ascii / =  if	\ go to root inode?
521            rot drop rootino -rot	( rino len right )
522         then
523         rot dup sb-buf read-inode	( len right pino )
524         -rot swap			( pino right len )
525      then				( pino right len )
526   repeat
527   2drop drop
528;
529
530: read-file ( size addr -- )
531   \ Read x bytes from a file to buffer
532   begin over 0> while
533      cur-offset cur-inode di_size x@ >  if ." read-file EOF exceeded" cr abort then
534      sb-buf buf-read-file		( size addr len buf )
535      over 2over drop swap		( size addr len buf addr len )
536      move				( size addr len )
537      dup cur-offset + to cur-offset	( size len newaddr )
538      tuck +				( size len newaddr )
539      -rot - swap			( newaddr newsize )
540   repeat
541   2drop
542;
543
544h# 5000 constant loader-base
545
546\
547\ Elf support -- find the load addr
548\
549
550: is-elf? ( hdr -- res? ) h# 7f454c46 = ;
551
552\
553\ Finally we finish it all off
554\
555
556: load-file-signon ( load-file len boot-path len -- load-file len boot-path len )
557   ." Loading file" space 2over type cr ." from device" space 2dup type cr
558;
559
560: load-file-print-size ( size -- size )
561   ." Loading" space dup . space ." bytes of file..." cr 
562;
563
564: load-file ( load-file len boot-path len -- load-base )
565   boot-debug?  if load-file-signon then
566   the-file file_SIZEOF 0 fill		\ Clear out file structure
567   ufs-open 				( load-file len )
568   find-file				( )
569
570   \
571   \ Now we've found the file we should read it in in one big hunk
572   \
573
574   cur-inode di_size x@			( file-len )
575   dup " to file-size" evaluate		( file-len )
576   boot-debug?  if load-file-print-size then
577   0 to cur-offset
578   loader-base				( buf-len addr )
579   2dup read-file			( buf-len addr )
580   ufs-close				( buf-len addr )
581   dup is-elf?  if ." load-file: not an elf executable" cr abort then
582
583   \ Luckily the prom should be able to handle ELF executables by itself
584
585   nip					( addr )
586;
587
588: do-boot ( bootfile -- )
589   ." NetBSD IEEE 1275 Bootblock" cr
590   boot-path load-file ( -- load-base )
591   dup 0<> if  " to load-base init-program" evaluate then
592;
593
594
595boot-args ascii V strchr 0<> swap drop if
596 true to boot-debug?
597then
598
599boot-args ascii D strchr 0= swap drop if
600  " /ofwboot" do-boot
601then exit
602
603
604