Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
\	$NetBSD: bootblk.fth,v 1.15 2015/08/20 05:40:08 dholland Exp $
\
\	IEEE 1275 Open Firmware Boot Block
\
\	Parses disklabel and UFS and loads the file called `ofwboot'
\
\
\	Copyright (c) 1998-2010 Eduardo Horvath.
\	All rights reserved.
\
\	Redistribution and use in source and binary forms, with or without
\	modification, are permitted provided that the following conditions
\	are met:
\	1. Redistributions of source code must retain the above copyright
\	   notice, this list of conditions and the following disclaimer.
\	2. Redistributions in binary form must reproduce the above copyright
\	   notice, this list of conditions and the following disclaimer in the
\	   documentation and/or other materials provided with the distribution.
\
\	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
\	IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
\	OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
\	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
\	INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
\	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
\	DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
\	THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
\	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
\	THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\

offset16
hex
headers

false value boot-debug?

: KB d# 1024 * ;

\
\ First some housekeeping:  Open /chosen and set up vectors into
\	client-services

" /chosen" find-package 0=  if ." Cannot find /chosen" 0 then
constant chosen-phandle

" /openprom/client-services" find-package 0=  if 
	." Cannot find client-services" cr abort
then constant cif-phandle

defer cif-claim ( align size virt -- base )
defer cif-release ( size virt -- )
defer cif-open ( cstr -- ihandle|0 )
defer cif-close ( ihandle -- )
defer cif-read ( len adr ihandle -- #read )
defer cif-seek ( low high ihandle -- -1|0|1 )
\ defer cif-peer ( phandle -- phandle )
\ defer cif-getprop ( len adr cstr phandle -- )

: find-cif-method ( method len -- xf )
   cif-phandle find-method drop 
;

" claim" find-cif-method  to  cif-claim
" open" find-cif-method  to  cif-open
" close" find-cif-method  to  cif-close
" read" find-cif-method  to  cif-read
" seek" find-cif-method  to  cif-seek

: twiddle ( -- ) ." ." ; \ Need to do this right.  Just spit out periods for now.

\
\ Support routines
\

\ 64-bit math support

here h# ffff over l! <w@ constant little-endian?
: ul>d ( l -- d.lo d.hi )	0 ;
: l>d ( l -- d.lo d.hi )	dup 0<  if  -1  else  0  then ;
: d>l ( d.lo d.hi -- l )	drop ;
: d@ ( addr -- d.lo d.hi )	dup l@ swap la1+ l@ little-endian? invert  if  swap  then ;
: d! ( d.lo d.hi addr -- )
   little-endian? invert  if  -rot swap rot  then  tuck la1+ l! l! ;
: d-and ( d1 d2 -- d1-and-d2 )  rot and -rot and swap ;
: d*u ( d1 u -- d2 )		tuck um* drop -rot um* rot + ;
: d<< ( d1 n -- d1<<n )	\ Hope this works
   ?dup  if  \ Shifting by 0 doesn't appear to work properly.
      tuck <<			( d.lo n d.hi' )
      -rot 2dup <<		( d.hi' d.lo n d.lo' )
      -rot d# 32 swap - >>	( d.hi' d.lo' lo.hi )
      rot +
   then
;
: d>> ( d1 n -- d1>>n )	\ Hope this works
   ?dup  if  \ Shifting by 0 doesn't appear to work properly.
      rot over >>	-rot	( d.lo' d.hi n )
      2dup >> -rot		( d.lo' d.hi' d.hi n )
      d# 32 swap - << rot + swap
   then
;
: d> ( d1 d2 -- d1>d2? )
   rot swap 2dup = if
      2drop > exit
   then
   > nip nip
;
: d>= ( d1 d2 -- d1>=d2? )
   rot swap 2dup =  if
      2drop >= exit
   then
   >= nip nip
;
: d< ( d1 d2 -- d1<d2? )	d>= invert ;
: d= ( d1 d2 -- d1=d2? )	rot = -rot = and ;
: d<> ( d1 d2 -- d1<>d2? )	d= invert ;


\ String support 

: strcmp ( s1 l1 s2 l2 -- true:false )
   rot tuck <>  if  3drop false exit  then
   comp 0=
;

\ Move string into buffer

: strmov ( s1 l1 d -- d l1 )
   dup 2over swap -rot		( s1 l1 d s1 d l1 )
   move				( s1 l1 d )
   rot drop swap
;

\ Move s1 on the end of s2 and return the result

: strcat ( s1 l1 s2 l2 -- d tot )
   2over swap 				( s1 l1 s2 l2 l1 s1 )
   2over + rot				( s1 l1 s2 l2 s1 d l1 )
   move rot + 				( s1 s2 len )
   rot drop				( s2 len )
;

: strchr ( s1 l1 c -- s2 l2 )
   begin
      dup 2over 0= if			( s1 l1 c c s1  )
         2drop drop exit then
      c@ = if				( s1 l1 c )
         drop exit then
      -rot /c - swap ca1+		( c l2 s2 )
     swap rot
  again
;

   
: cstr ( ptr -- str len )
   dup 
   begin dup c@ 0<>  while + repeat
   over -
;

\
\ BSD UFS parameters
\

fload	ffs.fth.h
fload   lfs.fth.h

sbsize buffer: sb-buf
-1 value boot-ihandle
dev_bsize value bsize
0 value raid-offset	\ Offset if it's a raid-frame partition

: strategy ( addr size db.lo db.hi -- nread )
    raid-offset l>d d+			( addr size db.lo' db.hi' )
    bsize d*u				( addr size sector.lo sector.hi )
    " seek" boot-ihandle $call-method -1 = if 
	." strategy: Seek failed" cr
	abort
    then				( addr size )
    " read" boot-ihandle $call-method
;


\
\ Multi-FS support
\
\ XXX Maybe the different filesystems should be segregated into separate files
\ XXX that are individually fload-ed.
\

defer fs-size
defer di-size
defer di-mode
defer /dino
defer cgstart
defer di-db@
defer di-ib@
defer ib-ib@
defer fs-bsize
defer fsbtodb
defer blksize
defer lblkno
defer blkoff
defer read-inode
\ LFS ifile
defer /ifile
defer if_daddr

\
\ FFS Cylinder group macros
\

: cgdmin ( cg fs -- d-1st-data-block )	dup fs_dblkno l@ l>d 2swap cgstart d+ ;
: cgimin ( cg fs -- d-inode-block )	dup fs_iblkno l@ l>d 2swap cgstart d+ ;
: cgsblock ( cg fs -- d-super-block )	dup fs_sblkno l@ l>d 2swap cgstart d+ ;
: cgstod ( cg fs -- d-cg-block )	dup fs_cblkno l@ l>d 2swap cgstart d+ ;

\
\ FFS Block and frag position macros
\

: ffs-blkoff ( pos.lo pos.hi fs -- off.lo off.hi )	fs_qbmask d@ d-and ;
\ : ffs-fragoff ( pos.lo pos.hi fs -- off.lo off.hi )	fs_qfmask d@ d-and ;
\ : ffs-lblktosize ( blk fs -- off.lo off.hi )		0 fs_bshift l@ d<< ;
: ffs-lblkno ( pos.lo pos.hi fs -- off.lo off.hi )	fs_bshift l@ d>> ;
: ffs-numfrags ( pos.lo pos.hi fs -- off.lo off.hi )	fs_fshift l@ d>> ;
: ffs-blkroundup ( pos.lo pos.hi fs -- off.lo off.hi )
    >r r@ fs_qbmask d@ d+ r> fs_bmask l@ l>d d-and
;
: ffs-fragroundup ( pos.lo pos.hi fs -- off.lo off.hi )
    >r r@ fs_qfmask d@ d+ r> fs_fmask l@ l>d d-and
;
: ffs-fragstoblks ( pos.lo pos.hi fs -- off.lo off.hi )	fs_fragshift l@ d>> ;
: ffs-blkstofrags ( blk fs -- frag )			fs_fragshift l@ << ;
\ : ffs-fragnum ( fsb fs -- off )			fs_frag l@ 1- and ;
\ : ffs-blknum ( fsb fs -- off )			fs_frag l@ 1- not and ;
: ffs-dblksize ( lbn.lo lbn.hi inodep fs -- size )
   >r -rot 2dup ndaddr l>d d>		( inop d-lbn >ndaddr? )
   -rot 1 0 d+				( inop >ndaddr? d-lbn+1 )
   r@ fs_bshift l@ d<<			( inop >ndaddr? d-lbn+1<<bshift )
   2swap >r di-size d@			( d-lbn+1<<bshift d-size )
   2swap 2over d< r> or  if		( d-size )
	2drop r> fs-bsize l@ exit
    then
    r@ ffs-blkoff			( size.lo size.hi )
    r> ffs-fragroundup d>l		( size )
;

: ino-to-cg ( ino fs -- cg )		fs_ipg l@ / ;
: ino-to-fsbo ( ino fs -- fsb0 )	fs_inopb l@ mod ;
: ino-to-fsba ( ino fs -- ba.lo ba.hi )	\ Need to remove the stupid stack diags someday
   2dup 				( ino fs ino fs )
   ino-to-cg				( ino fs cg )
   over					( ino fs cg fs )
   cgimin				( ino fs inode-blk.lo inode-blk.hi )
   2swap				( d-inode-blk ino fs )
   tuck 				( d-inode-blk fs ino fs )
   fs_ipg l@ 				( d-inode-blk fs ino ipg )
   mod					( d-inode-blk fs mod )
   swap					( d-inode-blk mod fs )
   dup 					( d-inode-blk mod fs fs )
   fs_inopb l@ 				( d-inode-blk mod fs inopb )
   rot 					( d-inode-blk fs inopb mod )
   swap					( d-inode-blk fs mod inopb )
   /					( d-inode-blk fs div )
   swap					( d-inode-blk div fs )
   ffs-blkstofrags			( d-inode-blk frag )
   0 d+
;
: ffs-fsbtodb ( fsb.lo fsb.hi fs -- db.lo db.hi )
    fs_fsbtodb l@ d<<
;


\
\ LFS suff
\
: lfs-blkoff ( pos.lo pos.hi fs -- off.lo off.hi )	lfs_bmask d@ d-and ;
\ : lfs-fragoff ( pos.lo pos.hi fs -- off.lo off.hi )	lfs_ffmask d@ d-and ;
\ : lfs-lblktosize ( blk fs -- off.lo off.hi )		0 lfs_bshift l@ d<< ;
: lfs-lblkno ( pos.lo pos.hi fs -- off.lo off.hi )	lfs_bshift l@ d>> ;
: lfs-numfrags ( pos.lo pos.hi fs -- off.lo off.hi )	lfs_ffshift l@ d>> ;
: lfs-roundup ( pos.lo pos.hi mask.lo mask.hi )
   2swap 2over d+ 2swap			( d-pos* d-mask )
   invert swap invert swap d-and
;
: lfs-blkroundup ( pos.lo pos.hi fs -- off.lo off.hi )	lfs_bmask d@ lfs-roundup ;
: lfs-fragroundup ( pos.lo pos.hi fs -- off.lo off.hi )	lfs_ffmask d@ lfs-roundup ;
: lfs-fragstoblks ( pos.lo pos.hi fs -- off.lo off.hi )	lfs_fbshift l@ d>> ;
: lfs-dblksize ( lbn.lo lbn.hi inodep fs -- size )
   >r -rot 2dup ndaddr l>d d>		( inop d-lbn >ndaddr? )
   -rot 1 0 d+				( inop >ndaddr? d-lbn+1 )
   r@ fs_bshift l@ d<<			( inop >ndaddr? d-lbn+1<<bshift )
   2swap >r di-size d@			( d-lbn+1<<bshift d-size )
   2swap 2over d< r> or  if		( d-size )
      2drop r> fs-bsize l@ exit
   then
   r@ lfs-blkoff			( size.lo size.hi )
   r> lfs-fragroundup d>l		( size )
;
: lfs-fsbtodb ( fsb.lo fsb.hi fs -- db.lo db.hi )
    lfs_fsbtodb l@ d<<
;

\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\
\ The rest of the multi-filesystem stuff
\
\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

\
\ FFS v1
\
: di-db-v1@ ( indx dinode -- db.lo db.hi )	di1_db swap la+ l@ l>d ;
: di-ib-v1@ ( indx dinode -- db.lo db.hi )	di1_ib swap la+ l@ l>d ;
: ib-ib-v1@ ( indx iblk -- db.lo db.hi )	swap la+ l@ l>d ;

: cgbase ( cg fs -- daddr.lo daddr.hi ) fs_fpg l@ um* ;
: cgstart-ufs1 ( cg fs -- cgstart ) 
    2dup fs_old_cgmask l@ invert and		( cg fs stuff )
    over fs_old_cgoffset l@ um*			( cg fs off.lo off.hi )
    2swap cgbase d+				( off.lo off.hi )
;

\
\ FFS v2
\

: di-db-v2@ ( indx dinode -- db.lo db.hi )	di2_db swap 2* la+ d@ ;
: di-ib-v2@ ( indx dinode -- db.lo db.hi )	di2_ib swap 2* la+ d@ ;
: ib-ib-v2@ ( indx iblk -- db.lo db.hi )	2* la+ d@ ;

\
\ LFS v1
\


\
\ File stuff
\

niaddr /w* constant narraysize

\ Assume UFS2 dinodes are always biger than UFS1
ufs2_dinode_SIZEOF buffer: cur-inode
0 value indir-block
create indir-addr -1 , -1 ,

\
\ Translate a fileblock to a disk block
\
\ We don't do triple indirect blocks.
\

\ Get the disk address from a single indirect block
: ib@ ( indx indir.lo indir.hi -- db.lo db.hi )
    2dup indir-addr d@ d<>  if		( indx indir.hi indir.lo )
	indir-addr d!			( indx )
	indir-block 			( indx indir-block )
	sb-buf fs-bsize l@		( indx indir-block fs fs-bsize )
	indir-addr d@ sb-buf		( indx indir-block fs-bsize indiraddr fs )
	fsbtodb 			( indx indir-block fs-bsize db.lo db.hi )
	strategy 0			( indx nread 0 ) \ Really should check return value
    then
    2drop				( indx )
    indir-block ib-ib@
;


: block-map ( fileblock -- diskblock.lo diskblock.hi )
    \ Direct block?
    dup ndaddr <  if			( fileblock )
	cur-inode di-db@ exit		( diskblock.lo diskblock.hi )
    then 				( fileblock )
    ndaddr -				( fileblock' )
    \ Now we need to check the indirect block
    dup sb-buf fs_nindir l@ <  if	( fileblock' )
	0 cur-inode di-ib@		( fileblock' indir.lo indir.hi )
	ib@ exit			( db.lo db.hi )
   then
   dup sb-buf fs_nindir -		( fileblock'' )
   \ Now try 2nd level indirect block -- just read twice 
   dup sb-buf fs_nindir l@ dup * >= if	( fileblock'' )
       ." block-map: exceeded max file size" cr
       abort
   then
       
   1 cur-inode di-ib@		( fileblock'' ib.lo ib.hi )
   
   \ Get 1st indirect block and find the 2nd indirect block
   rot dup sb-buf fs_nindir u/mod	( ib2.lo ib2.hi indx2 indx1 )
   2swap ib@			( indx2 ib2.lo ib2.hi )
   
   \ Get 2nd indirect block and find our diskblock
   ib@				( db.lo db.hi )
;

\
\ Read file into internal buffer and return pointer and len
\

0 value cur-block			\ allocated dynamically in ufs-open
0 value cur-blocksize			\ size allocated  to  cur-block
create cur-blockno -1 l, -1 l,		\ Current disk block.
-1 value file-blockno			\ Current file block no.
0 value file-offset			\ Current file offset, max 4GB.

: buf-read-file ( fs -- buf len )
    >r file-offset			( seek )
    dup l>d r@ lblkno drop		( seek blk )
    dup l>d cur-inode r@ blksize	( seek blk blksize )
    over file-blockno <> if		( seek blk blksize )
	over  to  file-blockno
	swap block-map			( seek blksize fsblk.lo fsblk.hi )
	2dup or 0=  if			( seek blksize fsblk.lo fsblk.hi )
	    \ Clear out curblock  XXX Why? Idunno.
	    2drop dup
	    cur-block swap erase	( seek blksize )
	    boot-debug?  if ." buf-read-file reading block 0" cr then
	    -1 l>d			\ Invalid disk block
	else
	    \ Call strategy to load the correct block.
	    r@ fsbtodb			( seek blksize dblk.lo dblk.hi )
	    rot >r cur-block r@ 2over	( seek addr size db.lo db.hi )
	    strategy r@	<>  if  ." buf-read-file: short read." cr abort  then
	    r> -rot			( seek size db.lo db.hi )
	then
	\ Save the new current disk block number
	cur-blockno d!			( seek size )
   else					
      nip				( seek size )
   then
   \ Now figure out how much we have in the buffer.
   swap l>d r> blkoff			( size off.lo off.hi )
   d>l cur-block over +			( size off buf )
   -rot -				( buf siz )
;

\
\ Read inode into cur-inode -- uses cur-block
\ 

: read-inode-ffs ( inode fs -- )
    twiddle

    >r dup r@ ino-to-fsba		( ino fsblk.lo fsblck.hi )
    r@ fsbtodb				( ino dblk.lo dblk.hi )
    2dup cur-blockno d@ d<>  if		( ino dblk.lo dblk.hi )
	\ We need  to  read the block
	cur-block r@ fs-bsize l@	( ino dblk.lo dblk.hi addr size )
	>r r@ 2over strategy r> <> if	( ino dblk.lo dblk.hi )
	    ." read-inode - residual" cr abort
	then
	2dup cur-blockno d!		( ino dblk.lo dblk.hi )
    then 2drop				( ino )

    r> ino-to-fsbo /dino *		( off )
    cur-block + cur-inode /dino move	( )
;

: find-inode-sector ( ino fs -- d-dblkno true | false )
   >r r@ lfs_ifile l@ r@  read-inode	( ino )

   r@ lfs_ifpb l@ u/mod			( rem q )

   r@ lfs_cleansz l@ +
   r@ lfs_segtabsz l@ +			( rem blkno )

   r@ fs-bsize l@ um* rot /ifile um* d+	( dseekp )

   drop  to  file-offset r@ buf-read-file	( buf len )

   /ifile <  if  r> 2drop false exit  then	( buf )

   if_daddr l@ l>d r> fsbtodb		( daddr )
   2dup lfs_unused_daddr l>d d=  if  2drop false  then
   true
;

: read-inode-lfs ( inode fs -- )
   twiddle

   >r dup r@ lfs_ifile l@ =  if		( ino  r: fs )
      r@ lfs_idaddr l@ l>d		( ino d-idaddr )
      r@ fsbtodb			( ino d-db )
   else
      dup r@ find-inode-sector 0= abort" Could not find inode sector!"
   then					( ino d-db )
   
    2dup cur-blockno d@ d<>  if		( ino dblk.lo dblk.hi )
	\ We need to read the block
	cur-block r@ fs-bsize l@	( ino dblk.lo dblk.hi addr size )
	>r r@ 2over strategy r> <> if	( ino dblk.lo dblk.hi )
	    ." read-inode - residual" cr abort
	then
	2dup cur-blockno d!		( ino dblk.lo dblk.hi )
    then  2drop				( ino )

    r@ lfs_inopb l@			( ino cnt )
    swap cur-block  begin		( cnt ino p )
       tuck di_inumber l@ over <>	( cnt p ino !found? )
    while				( cnt p ino )
	  rot 1- ?dup 0=  abort" Could not find inode!"
	  rot /dino + swap -rot		( cnt ino p )
    repeat  swap			( cnt ino p )

    cur-inode /dino move		( cnt ino )
	  
    r> 3drop
;

\ Identify inode type

: is-dir? ( ufs1_dinode -- is-dir? )		di-mode w@ ifmt and ifdir = ;
: is-symlink? ( ufs1_dinode -- is-symlink? )	di-mode w@ ifmt and iflnk = ;

\
\ Multi-FS initialiation.
\
\ It's way down here so all the fs-specific routines have already been defined.
\

: init-ffs-common ( -- )
   ' fs_SIZEOF  to  fs-size 
   ' fs_bsize  to  fs-bsize
   ' ffs-dblksize  to  blksize
   ' read-inode-ffs  to  read-inode
   ' ffs-fsbtodb  to  fsbtodb
   ' ffs-lblkno  to  lblkno
   ' ffs-blkoff  to   blkoff
;   


: ffs-oldcompat ( -- )
   \ Make sure old ffs values in sb-buf are sane
   sb-buf fs_old_npsect dup l@ sb-buf fs_old_nsect l@ max swap l!
   sb-buf fs_old_interleave dup l@ 1 max swap l!
   sb-buf fs_old_postblformat l@ fs_42postblfmt =  if
      8 sb-buf fs_old_nrpos l!
   then
   sb-buf fs_old_inodefmt l@ fs_44inodefmt <  if
      sb-buf fs-bsize l@ 
      dup ndaddr um* 1 d- sb-buf fs_maxfilesize d!
      niaddr 0  ?do
	 sb-buf fs_nindir l@ * dup	( sizebp sizebp )
	 sb-buf fs_maxfilesize dup d@ ( sizebp sizebp *mxfs mxfs.lo mxfs.hi )
	 2over drop l>d d+ 2swap d!	( sizebp )
      loop  drop 			( )
      sb-buf dup fs_bmask l@ invert l>d rot fs_qbmask d!
      sb-buf dup fs_fmask l@ invert l>d rot fs_qfmask d!
   then
;


: init-ffs-v1 ( -- )
   boot-debug?  if  ." FFS v1" cr  then
   init-ffs-common
   ' di1_size  to  di-size
   ' di1_mode  to  di-mode
   ' ufs1_dinode_SIZEOF  to  /dino
   ' cgstart-ufs1  to  cgstart
   ' di-db-v1@  to  di-db@
   ' di-ib-v1@  to  di-ib@
   ' ib-ib-v1@  to  ib-ib@
   ffs-oldcompat
;

: init-ffs-v2 ( -- )
   boot-debug?  if  ." FFS v2" cr  then
   init-ffs-common
   ' di2_size  to  di-size
   ' di2_mode  to  di-mode
   ' ufs2_dinode_SIZEOF  to  /dino
   ' cgbase  to  cgstart
   ' di-db-v2@  to  di-db@
   ' di-ib-v2@  to  di-ib@
   ' ib-ib-v2@  to  ib-ib@
;

: init-lfs-common ( -- )
   ' dlfs_SIZEOF  to  fs-size 
   ' di1_size  to  di-size
   ' di1_mode  to  di-mode
   ' lfs32_dinode_SIZEOF  to  /dino
   ' cgbase  to  cgstart
   ' di-db-v1@  to  di-db@
   ' di-ib-v1@  to  di-ib@
   ' ib-ib-v1@  to  ib-ib@
   ' lfs-dblksize  to  blksize
   ' read-inode-lfs  to  read-inode
   ' lfs-fsbtodb  to  fsbtodb
   ' lfs-lblkno  to  lblkno
   ' lfs-blkoff  to  blkoff
;

: init-lfs-v1 ( -- )
   boot-debug?  if  ." LFS v1" cr  then
   init-lfs-common
   ' lfs_ibsize  to  fs-bsize
   ' ifile_v1_SIZEOF  to  /ifile
   ' if1_daddr  to  if_daddr
;   

: init-lfs-v2 ( -- )
   boot-debug?  if  ." LFS v2" cr  then
   init-lfs-common
   ' lfs_bsize  to  fs-bsize
   ' ifile32_SIZEOF  to  /ifile
   ' if2_daddr  to  if_daddr
;   


: fs-magic? ( sb -- is-ufs? )
   \ The LFS magic is the first word in the superblock
   dup lfs_magic l@ lfs_magic_value =  if
      dup lfs_version l@  case		( sb sel )
	 1  of  init-lfs-v1 drop true exit  endof
	 2  of  init-lfs-v2 drop true exit  endof
	 ." Invalid LFS version."  \ Try FFS.
      endcase
   then					( sb )
   \ The FFS magic is at the end of the superblock
   \ XXX we should check to make sure this is not an alternate SB.
   fs_magic l@  case
      fs1_magic_value  of  init-ffs-v1 true  endof
      fs2_magic_value  of  init-ffs-v2 true  endof
      false swap	\ Return false
   endcase
;



\
\ Hunt for directory entry:
\ 
\ repeat
\    load a buffer
\    while entries do
\       if entry == name return
\       next entry
\ until no buffers
\

: search-dir-block ( str len buf len -- ino | 0 )
    2dup + nip				( str len buf bufend )
    swap 2swap rot			( bufend str len direct )
    begin  dup 4 pick <  while		( bufend str len direct )
	    dup d_ino l@ 0<>  if	( bufend str len direct )
		boot-debug?  if
		    \ Print the current file name
		    dup dup d_name swap d_namlen c@ type cr
		then
		2dup d_namlen c@ =  if	( bufend str len direct )
		    dup d_name 2over	( bufend str len direct dname str len )
		    comp 0= if		( bufend str len direct )
			\ Found it -- return inode
			d_ino l@ nip nip nip	( dino )
			boot-debug?  if  ." Found it" cr  then 
			exit 		( dino )
		    then
		then			( bufend str len direct )
	    then			( bufend str len direct )
	    dup d_reclen w@ +		( bufend str len nextdirect )
    repeat
    2drop 2drop 0
;
    

: search-directory ( str len -- ino | 0 )
    0  to  file-offset
    begin
	file-offset cur-inode di-size d@ drop <
    while				( str len )
	    \ Read a directory block
	    sb-buf buf-read-file	( str len buf len )
	    dup 0=  if  ." search-directory: buf-read-file zero len" cr abort  then
	    dup file-offset +  to  file-offset	( str len buf len )

	    2over 2swap search-dir-block ?dup  if
		\ Found it
		nip nip exit
	    then			( str len )
    repeat
    2drop 2drop 0			( 0 )
;

: read-super ( sector -- )
   0 " seek" boot-ihandle $call-method -1 =  if 
      ." Seek failed" cr abort
   then
   sb-buf sbsize " read" boot-ihandle $call-method
   dup sbsize <>  if
      ." Read of superblock failed" cr
      ." requested" space sbsize .
      ." actual" space . cr
      abort
   else 
      drop
   then
;

: check-supers ( -- found? )
   \ Superblocks used to be 8KB into the partition, but ffsv2 changed that.
   \ See comments in src/sys/ufs/ffs/fs.h
   \ Put a list of offets to check on the stack, ending with -1
   -1
   0
   d# 128 KB
   d# 64 KB
   8 KB
   
   begin  dup -1 <>  while			( -1 .. off )
	 raid-offset dev_bsize * + read-super	( -1 .. )
	 sb-buf fs-magic?  if			( -1 .. )
	    begin  -1 =  until	 \ Clean out extra stuff from stack
	    true exit
	 then
   repeat
   drop false
;

: ufs-open ( bootpath len -- )
   boot-ihandle -1 =  if
      2dup + 0 swap c!	\ Nul terminate.
      over cif-open dup 0=  if 	( boot-path len ihandle? )
	 ." Could not open device" space type cr 
	 abort
      then 				( boot-path len ihandle )
      to  boot-ihandle			\ Save ihandle to boot device
   then
   2drop
   
   boot-debug?  if ." Try a RAID superblock read" cr  then
   \ RAIDFRAME skips 64 sectors.
   d# 64  to  raid-offset
   check-supers invert  if
      boot-debug?  if ." Try a normal superblock read" cr  then
      0  to  raid-offset 
      check-supers 0=  abort" Invalid superblock magic"
   then
   sb-buf fs-bsize l@ dup maxbsize >  if
      ." Superblock bsize" space . ." too large" cr
      abort
   then 
   dup fs-size <  if
      ." Superblock bsize < size of superblock" cr
      abort
   then
   dup  to  cur-blocksize alloc-mem  to  cur-block    \ Allocate cur-block
   cur-blocksize alloc-mem  to  indir-block
   boot-debug?  if  ." ufs-open complete" cr  then
;

: ufs-close ( -- ) 
    boot-ihandle dup -1 <>  if
	cif-close -1  to  boot-ihandle 
    then
    cur-block 0<> if
       cur-block cur-blocksize free-mem
       indir-block cur-blocksize free-mem
    then
;

: boot-path ( -- boot-path )
    " bootpath" chosen-phandle get-package-property  if
	." Could not find bootpath in /chosen" cr
	abort
    else
	decode-string 2swap 2drop
    then
;

: boot-args ( -- boot-args )
    " bootargs" chosen-phandle get-package-property  if
	." Could not find bootargs in /chosen" cr
	abort
    else
	decode-string 2swap 2drop
    then
;

2000 buffer: boot-path-str
2000 buffer: boot-path-tmp

: split-path ( path len -- right len left len )
\ Split a string at the `/'
    begin
	dup -rot				( oldlen right len left )
	ascii / left-parse-string		( oldlen right len left len )
	dup 0<>  if  4 roll drop exit  then
	2drop					( oldlen right len )
	rot over =				( right len diff )
    until
;

: find-file ( load-file len -- )
    rootino dup sb-buf read-inode	( load-file len pino )
    -rot				( pino load-file len )
    \
    \ For each path component
    \ 
    begin  split-path dup 0<>  while	( pino right len left len )
	    cur-inode is-dir? not  if  ." Inode not directory" cr abort  then
	    boot-debug?  if  ." Looking for" space 2dup type space ." in directory..." cr  then
	    search-directory		( pino right len ino|false )
	    dup 0=  abort" Bad path" 	( pino right len cino )
	    sb-buf read-inode			( pino right len )
	    cur-inode is-symlink?  if		\ Symlink -- follow the damn thing
		\ Save path in boot-path-tmp
		boot-path-tmp strmov		( pino new-right len )
		
		\ Now deal with symlink  XXX drop high word of linklen
		cur-inode di-size d@ drop	( pino right len linklen.lo )
		dup sb-buf fs_maxsymlinklen l@	( pino right len linklen linklen maxlinklen )
		<  if				\ Now join the link to the path
		    0 cur-inode di-db@ drop	( pino right len linklen linkp )
		    swap boot-path-str strmov	( pino right len new-linkp linklen )
		else				\ Read file for symlink -- Ugh
		    \ Read link into boot-path-str
		    boot-path-str dup sb-buf fs-bsize l@
		    0 block-map			( pino right len linklen boot-path-str bsize blockno.lo blockno.hi )
		    strategy drop swap		( pino right len boot-path-str linklen )
		then 				( pino right len linkp linklen )
		\ Concatenate the two paths
		strcat				( pino new-right newlen )
		swap dup c@ ascii / =  if	\ go to root inode?
		    rot drop rootino -rot	( rino len right )
		then
		rot dup sb-buf read-inode	( len right pino )
		-rot swap			( pino right len )
	    then				( pino right len )
    repeat
    2drop drop
;

: .read-file-msg ( addr xxx siz -- addr xxx siz )
    boot-debug? if
	." Copying " dup . ." bytes to " 3 pick . cr
    then
;
       
: read-file ( addr size -- )
    noop \ In case we need to debug this
    \ Read x bytes from a file to buffer
    begin  dup 0>  while
	    file-offset cur-inode di-size d@ drop >  if
		." read-file EOF exceeded" cr abort
	    then
	    sb-buf buf-read-file		( addr size buf len )
	    
	    .read-file-msg
	    
	    \ Copy len bytes to addr  XXX min ( len, size ) ?
	    2over drop 3dup swap move drop	( addr size buf len )
	    
	    dup file-offset +  to  file-offset	( addr size buf len )
	    
	    nip tuck - -rot + swap		( addr' size' )
    repeat
    2drop
;

" load-base " evaluate constant loader-base

: load-file-signon ( load-file len boot-path len -- load-file len boot-path len )
   ." Loading file" space 2over type cr ." from device" space 2dup type cr
;

: load-file ( load-file len boot-path len -- load-base )
   boot-debug?  if  load-file-signon  then
   
   ufs-open 				( load-file len )
   find-file				( )

    \
    \ Now we've found the file we should read it in in one big hunk
    \
    
    cur-inode di-size d@  if  ." File len >2GB!" cr abort  then
\    dup " to file-size " evaluate	( file-len ) \ Wassthis?
    boot-debug?  if
	." Loading " dup . ."  bytes of file..." cr
    then
    0  to  file-offset
    -1  to  file-blockno
    loader-base				( buf-len addr )
    tuck swap read-file			( addr )
    ufs-close				( addr )
;

: do-boot ( bootfile -- )
   ." NetBSD IEEE 1275 Multi-FS Bootblock" cr
   ." Version $NetBSD: bootblk.fth,v 1.15 2015/08/20 05:40:08 dholland Exp $" cr
   boot-path load-file ( -- load-base )
   dup 0<>  if  " init-program " evaluate  then
; 


boot-args ascii V strchr 0<> swap drop  if
    true  to  boot-debug?
then

boot-args ascii D strchr 0= swap drop  if
    " /ofwboot" do-boot
then  exit