1 \ *****************************************************************************
2 \ * Copyright (c) 2004, 2008 IBM Corporation
3 \ * All rights reserved.
4 \ * This program and the accompanying materials
5 \ * are made available under the terms of the BSD License
6 \ * which accompanies this distribution, and is available at
7 \ * http://www.opensource.org/licenses/bsd-license.php
10 \ * IBM Corporation - initial implementation
11 \ ****************************************************************************/
14 \ Set debug-disk-label? to true to get debug messages for the disk-label code.
15 false VALUE debug-disk-label?
17 \ This value defines the maximum number of blocks (512b) to load from a PREP
18 \ partition. This is required to keep the load time in reasonable limits if the
19 \ PREP partition becomes big.
20 \ If we ever want to put a large kernel with initramfs from a PREP partition
21 \ we might need to increase this value. The default value is 65536 blocks (32MB)
22 d# 65536 value max-prep-partition-blocks
23 d# 4096 CONSTANT block-array-size
25 s" disk-label" device-name
27 0 INSTANCE VALUE partition
28 0 INSTANCE VALUE part-offset
29 0 INSTANCE VALUE disk-chrp-boot
31 0 INSTANCE VALUE part-start
32 0 INSTANCE VALUE lpart-start
33 0 INSTANCE VALUE part-size
34 0 INSTANCE VALUE dos-logical-partitions
36 0 INSTANCE VALUE block-size
37 0 INSTANCE VALUE block
40 0 INSTANCE VALUE args-len
42 0 INSTANCE VALUE gpt-part-size
43 0 INSTANCE VALUE seek-pos
46 INSTANCE VARIABLE block# \ variable to store logical sector#
47 INSTANCE VARIABLE hit# \ partition counter
48 INSTANCE VARIABLE success-flag
50 \ ISO9660 specific information
51 0ff constant END-OF-DESC
52 3 constant PARTITION-ID
53 48 constant VOL-PART-LOC
56 \ DOS partition label (MBR) specific structures
59 1b8 field mbr>boot-loader
60 /l field mbr>disk-signature
62 40 field mbr>partition-table
68 /c field part-entry>active
69 /c field part-entry>start-head
70 /c field part-entry>start-sect
71 /c field part-entry>start-cyl
72 /c field part-entry>id
73 /c field part-entry>end-head
74 /c field part-entry>end-sect
75 /c field part-entry>end-cyl
76 /l field part-entry>sector-offset
77 /l field part-entry>sector-count
79 CONSTANT /partition-entry
84 4 field gpt>header-size
85 4 field gpt>header-crc32
87 8 field gpt>current-lba
88 8 field gpt>backup-lba
91 10 field gpt>disk-guid
92 8 field gpt>part-entry-lba
93 4 field gpt>num-part-entry
94 4 field gpt>part-entry-size
95 4 field gpt>part-array-crc32
96 1a4 field gpt>reserved
101 10 field gpt-part-entry>part-type-guid
102 10 field gpt-part-entry>part-guid
103 8 field gpt-part-entry>first-lba
104 8 field gpt-part-entry>last-lba
105 8 field gpt-part-entry>attribute
106 48 field gpt-part-entry>part-name
108 CONSTANT /gpt-part-entry
110 \ Defined by IEEE 1275-1994 (3.8.1)
112 : offset ( d.rel -- d.abs )
113 part-offset xlsplit d+
116 : seek ( pos.lo pos.hi -- status )
118 debug-disk-label? IF 2dup ." seek-parent: pos.hi=0x" u. ." pos.lo=0x" u. THEN
119 s" seek" $call-parent
120 debug-disk-label? IF dup ." status=" . cr THEN
123 : read ( addr len -- actual )
124 debug-disk-label? IF 2dup swap ." read-parent: addr=0x" u. ." len=" .d THEN
125 s" read" $call-parent
126 debug-disk-label? IF dup ." actual=" .d cr THEN
130 \ read sector to array "block"
131 : read-sector ( sector-number -- )
132 \ block-size is 0x200 on disks, 0x800 on cdrom drives
133 block-size * 0 seek drop \ seek to sector
134 block block-size read drop \ read sector
137 : (.part-entry) ( part-entry )
138 cr ." part-entry>active: " dup part-entry>active c@ .d
139 cr ." part-entry>start-head: " dup part-entry>start-head c@ .d
140 cr ." part-entry>start-sect: " dup part-entry>start-sect c@ .d
141 cr ." part-entry>start-cyl: " dup part-entry>start-cyl c@ .d
142 cr ." part-entry>id: " dup part-entry>id c@ .d
143 cr ." part-entry>end-head: " dup part-entry>end-head c@ .d
144 cr ." part-entry>end-sect: " dup part-entry>end-sect c@ .d
145 cr ." part-entry>end-cyl: " dup part-entry>end-cyl c@ .d
146 cr ." part-entry>sector-offset: " dup part-entry>sector-offset l@-le .d
147 cr ." part-entry>sector-count: " dup part-entry>sector-count l@-le .d
151 : (.name) r@ begin cell - dup @ <colon> = UNTIL xt>name cr type space ;
154 s" block-size" ['] $call-parent CATCH IF ABORT" parent has no block-size." THEN
156 block-array-size alloc-mem
157 dup block-array-size erase
160 ." init-block: block-size=" block-size .d ." block=0x" block u. cr
164 : partition>part-entry ( partition -- part-entry )
165 1- /partition-entry * block mbr>partition-table +
168 : partition>start-sector ( partition -- sector-offset )
169 partition>part-entry part-entry>sector-offset l@-le
172 \ This word returns true if the currently loaded block has _NO_ MBR magic
173 : no-mbr? ( -- true|false )
175 1 partition>part-entry part-entry>id c@ ee = IF TRUE EXIT THEN \ GPT partition found
176 block mbr>magic w@-le aa55 <>
179 \ This word returns true if the currently loaded block has _NO_ GPT partition id
180 : no-gpt? ( -- true|false )
182 1 partition>part-entry part-entry>id c@ ee <> IF true EXIT THEN
183 block mbr>magic w@-le aa55 <>
186 : pc-extended-partition? ( part-entry-addr -- true|false )
187 part-entry>id c@ ( id )
188 dup 5 = swap ( true|false id )
189 dup f = swap ( true|false true|false id )
190 85 = ( true|false true|false true|false )
194 : count-dos-logical-partitions ( -- #logical-partitions )
195 no-mbr? IF 0 EXIT THEN
197 i partition>part-entry ( current part-entry )
198 dup pc-extended-partition? IF
199 part-entry>sector-offset l@-le ( current sector )
200 dup to part-start to lpart-start ( current )
202 part-start read-sector \ read EBR
203 1 partition>start-sector IF
204 \ ." Logical Partition found at " part-start .d cr
206 THEN \ another logical partition
207 2 partition>start-sector
208 ( current relative-sector )
209 ?dup IF lpart-start + to part-start false ELSE true THEN
217 : (get-dos-partition-params) ( ext-part-start part-entry -- offset count active? id )
218 dup part-entry>sector-offset l@-le rot + swap ( offset part-entry )
219 dup part-entry>sector-count l@-le swap ( offset count part-entry )
220 dup part-entry>active c@ 80 = swap ( offset count active? part-entry )
221 part-entry>id c@ ( offset count active? id )
224 : find-dos-partition ( partition# -- false | offset count active? id true )
225 to partition 0 to part-start 0 to part-offset
227 \ no negative partitions
228 partition 0<= IF 0 to partition false EXIT THEN
230 \ load MBR and check it
231 no-mbr? IF 0 to partition false EXIT THEN
233 partition 4 <= IF \ Is this a primary partition?
234 0 partition partition>part-entry
235 (get-dos-partition-params)
236 \ FIXME sanity checks?
239 partition 4 - 0 5 1 DO ( logical-partition current )
240 i partition>part-entry ( log-part current part-entry )
241 dup pc-extended-partition? IF
242 part-entry>sector-offset l@-le ( log-part current sector )
243 dup to part-start to lpart-start ( log-part current )
245 part-start read-sector \ read EBR
246 1 partition>start-sector IF \ first partition entry
247 1+ 2dup = IF ( log-part current )
249 part-start 1 partition>part-entry
250 (get-dos-partition-params)
253 2 partition>start-sector
254 ( log-part current relative-sector )
256 ?dup IF lpart-start + to part-start false ELSE true THEN
269 : try-dos-partition ( -- okay? )
270 \ Read partition table and check magic.
272 debug-disk-label? IF cr ." No DOS disk-label found." cr THEN
276 count-dos-logical-partitions TO dos-logical-partitions
279 ." Found " dos-logical-partitions .d ." logical partitions" cr
280 ." Partition = " partition .d cr
283 partition 1 5 dos-logical-partitions +
285 cr ." Partition # not 1-" 4 dos-logical-partitions + . cr false EXIT
288 \ Could/should check for valid partition here... the magic is not enough really.
290 \ Get the partition offset.
292 partition find-dos-partition IF
293 ( offset count active? id )
296 block-size * to part-offset
303 \ Check for an ISO-9660 filesystem on the disk
304 \ : try-iso9660-partition ( -- true|false )
305 \ implement me if you can ;-)
309 \ Check for an ISO-9660 filesystem on the disk
310 \ (cf. CHRP IEEE 1275 spec., chapter 11.1.2.3)
311 : has-iso9660-filesystem ( -- TRUE|FALSE )
312 \ Seek to the beginning of logical 2048-byte sector 16
313 \ refer to Chapter C.11.1 in PAPR 2.0 Spec
314 \ was: 10 read-sector, but this might cause trouble if you
315 \ try booting an ISO image from a device with 512b sectors.
316 10 800 * 0 seek drop \ seek to sector
317 block 800 read drop \ read sector
318 \ Check for CD-ROM volume magic:
320 block 1+ 5 s" CD001" str=
322 dup IF 800 to block-size THEN
326 \ Load from first active DOS boot partition.
328 : fat-bootblock? ( addr -- flag )
329 \ byte 0-2 of the bootblock is a jump instruction in
330 \ all FAT filesystems.
331 \ e9 and eb are jump instructions in x86 assembler.
332 dup c@ e9 = IF drop true EXIT THEN
333 dup c@ eb = swap 2+ c@ 90 = and
336 \ NOTE: block-size is always 512 bytes for DOS partition tables.
338 : load-from-dos-boot-partition ( addr -- size )
339 no-mbr? IF drop FALSE EXIT THEN \ read MBR and check for DOS disk-label magic
341 count-dos-logical-partitions TO dos-logical-partitions
344 ." Found " dos-logical-partitions .d ." logical partitions" cr
345 ." Partition = " partition .d cr
348 \ Now walk through the partitions:
349 5 dos-logical-partitions + 1 DO
350 \ ." checking partition " i .
351 i find-dos-partition IF ( addr offset count active? id )
352 41 = and ( addr offset count prep-boot-part? )
353 IF ( addr offset count )
354 max-prep-partition-blocks min \ reduce load size
355 swap ( addr count offset )
356 block-size * to part-offset
357 0 0 seek drop ( addr offset )
358 block-size * read ( size )
368 \ Check for GPT PReP partition GUID. Only first 3 blocks are
369 \ byte-swapped treating last two blocks as contigous for simplifying
371 9E1A2D38 CONSTANT GPT-PREP-PARTITION-1
372 C612 CONSTANT GPT-PREP-PARTITION-2
373 4316 CONSTANT GPT-PREP-PARTITION-3
374 AA268B49521E5A8B CONSTANT GPT-PREP-PARTITION-4
376 : gpt-prep-partition? ( -- true|false )
377 block gpt-part-entry>part-type-guid
378 dup l@-le GPT-PREP-PARTITION-1 <> IF drop false EXIT THEN
379 dup 4 + w@-le GPT-PREP-PARTITION-2 <> IF drop false EXIT THEN
380 dup 6 + w@-le GPT-PREP-PARTITION-3 <> IF drop false EXIT THEN
381 8 + x@ GPT-PREP-PARTITION-4 =
384 \ Check for GPT MSFT BASIC DATA GUID - fat based
385 EBD0A0A2 CONSTANT GPT-BASIC-DATA-PARTITION-1
386 B9E5 CONSTANT GPT-BASIC-DATA-PARTITION-2
387 4433 CONSTANT GPT-BASIC-DATA-PARTITION-3
388 87C068B6B72699C7 CONSTANT GPT-BASIC-DATA-PARTITION-4
390 : gpt-basic-data-partition? ( -- true|false )
391 block gpt-part-entry>part-type-guid
392 dup l@-le GPT-BASIC-DATA-PARTITION-1 <> IF drop false EXIT THEN
393 dup 4 + w@-le GPT-BASIC-DATA-PARTITION-2 <> IF drop false EXIT THEN
394 dup 6 + w@-le GPT-BASIC-DATA-PARTITION-3 <> IF drop false EXIT THEN
395 8 + x@ GPT-BASIC-DATA-PARTITION-4 =
400 \ ("EFI PART", 45h 46h 49h 20h 50h 41h 52h 54h)
402 4546492050415254 CONSTANT GPT-SIGNATURE
404 \ The routine checks whether the protective MBR has GPT ID and then
405 \ reads the gpt data from the sector. Also set the seek position and
406 \ the partition size used in caller routines.
408 : get-gpt-partition ( -- true|false )
409 no-gpt? IF false EXIT THEN
410 debug-disk-label? IF cr ." GPT partition found " cr THEN
412 block gpt>part-entry-lba x@-le
413 block-size * to seek-pos
414 block gpt>part-entry-size l@-le to gpt-part-size
415 gpt-part-size block-array-size > IF
416 cr ." GPT part size exceeds buffer allocated " cr
419 block gpt>signature x@ GPT-SIGNATURE =
422 : load-from-gpt-prep-partition ( addr -- size )
423 get-gpt-partition 0= IF false EXIT THEN
424 block gpt>num-part-entry l@-le dup 0= IF false exit THEN
427 block gpt-part-size read drop gpt-prep-partition? IF
428 debug-disk-label? IF ." GPT PReP partition found " cr THEN
429 block gpt-part-entry>first-lba x@-le ( addr first-lba )
430 block gpt-part-entry>last-lba x@-le ( addr first-lba last-lba)
431 over - 1+ ( addr first-lba blocks )
432 swap ( addr blocks first-lba )
433 block-size * to part-offset ( addr blocks )
434 0 0 seek drop ( addr blocks )
435 block-size * read ( size )
438 seek-pos gpt-part-size + to seek-pos
443 : try-gpt-dos-partition ( -- true|false )
444 get-gpt-partition 0= IF false EXIT THEN
445 block gpt>num-part-entry l@-le dup 0= IF false EXIT THEN
448 block gpt-part-size read drop
449 gpt-basic-data-partition? IF
450 debug-disk-label? IF ." GPT BASIC DATA partition found " cr THEN
451 block gpt-part-entry>first-lba x@-le ( first-lba )
452 dup to part-start ( first-lba )
453 block gpt-part-entry>last-lba x@-le ( first-lba last-lba )
454 over - 1+ ( first-lba s1 )
455 block-size * to part-size ( first-lba )
456 block-size * to part-offset ( )
458 block block-size read drop
459 block fat-bootblock? ( true|false )
462 seek-pos gpt-part-size + to seek-pos
467 \ Extract the boot loader path from a bootinfo.txt file
468 \ In: address and length of buffer where the bootinfo.txt has been loaded to.
469 \ Out: string address and length of the boot loader (within the input buffer)
470 \ or a string with length = 0 when parsing failed.
472 \ Here is a sample bootinfo file:
474 \ <description>Linux Distribution</description>
475 \ <os-name>Linux</os-name>
476 \ <boot-script>boot &device;:1,\boot\yaboot.ibm</boot-script>
477 \ <icon size=64,64 color-space=3,3,2>
478 \ <bitmap>[..]</bitmap>
482 : parse-bootinfo-txt ( addr len -- str len )
483 2dup s" <boot-script>" find-substr ( addr len pos1 )
488 dup >r - swap r> + swap ( addr1 len1 )
490 2dup s" &device;:" find-substr ( addr1 len1 posdev )
494 9 + \ Skip the "&device;:" string
495 dup >r - swap r> + swap ( addr2 len2 )
496 2dup s" </boot-script>" find-substr nip ( addr2 len3 )
499 ." Extracted boot loader from bootinfo.txt: '"
504 \ Try to load \ppc\bootinfo.txt from the disk (used mainly on CD-ROMs), and if
505 \ available, get the boot loader path from this file and load it.
506 \ See the "CHRP system binding to IEEE 1275" specification for more information
507 \ about bootinfo.txt. An example file can be found in the comment of
508 \ parse-bootinfo-txt ( addr len -- str len )
510 : load-chrp-boot-file ( addr -- size )
511 \ Create bootinfo.txt path name and load that file:
512 my-parent instance>path
513 disk-chrp-boot @ 1 = IF
514 s" :1,\ppc\bootinfo.txt" $cat strdup ( addr str len )
516 s" :\ppc\bootinfo.txt" $cat strdup ( addr str len )
518 open-dev dup 0= IF 2drop 0 EXIT THEN
519 >r dup ( addr addr R:ihandle )
520 dup s" load" r@ $call-method ( addr addr size R:ihandle )
521 r> close-dev ( addr addr size )
523 \ Now parse the information from bootinfo.txt:
524 parse-bootinfo-txt ( addr fnstr fnlen )
525 dup 0= IF 3drop 0 EXIT THEN
526 \ Does the string contain parameters (i.e. a white space)?
528 ( addr fnstr fnlen offset )
529 >r 2dup r@ - 1- swap r@ + 1+ swap ( addr fnstr fnlen pstr plen R: offset )
530 encode-string s" bootargs" set-chosen
534 \ Create the full path to the boot loader:
535 my-parent instance>path ( addr fnstr fnlen nstr nlen )
536 s" :" $cat 2swap $cat strdup ( addr str len )
537 \ Update the bootpath:
538 2dup encode-string s" bootpath" set-chosen
539 \ And finally load the boot loader itself:
540 open-dev dup 0= IF ." failed to load CHRP boot loader." 2drop 0 EXIT THEN
541 >r s" load" r@ $call-method ( size R:ihandle )
542 r> close-dev ( size )
545 \ load from a bootable partition
546 : load-from-boot-partition ( addr -- size )
547 debug-disk-label? IF ." Trying DOS boot " .s cr THEN
548 dup load-from-dos-boot-partition ?dup 0 <> IF nip EXIT THEN
550 debug-disk-label? IF ." Trying CHRP boot " .s cr THEN
552 dup load-chrp-boot-file ?dup 0 <> IF nip EXIT THEN
555 debug-disk-label? IF ." Trying GPT boot " .s cr THEN
556 load-from-gpt-prep-partition
557 \ More boot partition formats ...
560 \ parse partition number from my-args
562 \ my-args has the following format
563 \ [<partition>[,<path>]]
565 \ | example my-args | example boot command |
566 \ +------------------+---------------------------+
567 \ | 1,\boot\vmlinuz | boot disk:1,\boot\vmlinuz |
568 \ | 2 | boot disk:2 |
570 \ 0 means the whole disk, this is the same behavior
571 \ as if no partition is specified (yaboot wants this).
573 : parse-partition ( -- okay? )
578 my-args to args-len to args
581 cr ." disk-label parse-partition: my-args=" my-args type cr
584 \ Called without arguments?
585 args-len 0 = IF true EXIT THEN
587 \ Check for "full disk" arguments.
588 my-args [char] , findchar 0= IF \ no comma?
589 args c@ isdigit not IF \ ... and not a partition number?
590 true EXIT \ ... then it's not a partition we can parse
595 my-args [char] , split to args-len to args
596 dup 0= IF 2drop true EXIT THEN \ no first argument
599 base @ >r decimal $number r> base !
600 IF cr ." Not a partition #" false EXIT THEN
602 \ Store part #, done.
608 \ try-files and try-partitions
610 : (interpose-filesystem) ( str len -- )
611 find-package IF args args-len rot interpose THEN
614 : try-dos-files ( -- found? )
615 no-mbr? IF false EXIT THEN
617 block fat-bootblock? 0= IF false EXIT THEN
618 s" fat-files" (interpose-filesystem)
622 : try-ext2-files ( -- found? )
623 2 read-sector \ read first superblock
624 block d# 56 + w@-le \ fetch s_magic
625 ef53 <> IF false EXIT THEN \ s_magic found?
626 s" ext2-files" (interpose-filesystem)
632 has-iso9660-filesystem 0= IF false exit THEN
633 s" iso-9660" (interpose-filesystem)
637 : try-files ( -- found? )
638 \ If no path, then full disk.
639 args-len 0= IF true EXIT THEN
641 try-dos-files IF true EXIT THEN
642 try-ext2-files IF true EXIT THEN
643 try-iso9660-files IF true EXIT THEN
645 \ ... more filesystem types here ...
650 : try-partitions ( -- found? )
651 try-dos-partition IF try-files EXIT THEN
652 try-gpt-dos-partition IF try-files EXIT THEN
653 \ try-iso9660-partition IF try-files EXIT THEN
654 \ ... more partition types here...
658 \ Interface functions for disk-label package
659 \ as defined by IEEE 1275-1994 3.8.1
662 debug-disk-label? IF ." Closing disk-label: block=0x" block u. ." block-size=" block-size .d cr THEN
663 block block-array-size free-mem
667 : open ( -- true|false )
670 parse-partition 0= IF
680 dup 0= IF debug-disk-label? IF ." not found." cr THEN close THEN \ free memory again
684 \ Boot & Load w/o arguments is assumed to be boot from boot partition
686 : load ( addr -- size )
692 TRUE ABORT" Load done w/o filesystem"
697 part-size max-prep-partition-blocks min \ Load size
699 max-prep-partition-blocks
703 has-iso9660-filesystem IF
704 dup load-chrp-boot-file ?dup 0 > IF nip EXIT THEN
706 load-from-boot-partition
707 dup 0= ABORT" No boot partition found"