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