1 ; Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 ; @APPLE_LICENSE_HEADER_START@
5 ; Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
6 ; Reserved. This file contains Original Code and/or Modifications of
7 ; Original Code as defined in and that are subject to the Apple Public
8 ; Source License Version 1.1 (the "License"). You may not use this file
9 ; except in compliance with the License. Please obtain a copy of the
10 ; License at http://www.apple.com/publicsource and read it before using
13 ; The Original Code and all software distributed under the License are
14 ; distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 ; EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 ; INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 ; FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
18 ; License for the specific language governing rights and limitations
21 ; @APPLE_LICENSE_HEADER_END@
25 ; A small boot sector program written in x86 assembly whose only
26 ; responsibility is to locate the booter partition, load the
27 ; booter into memory, and jump to the booter's entry point.
28 ; The booter partition can be a primary or a logical partition.
29 ; But the booter partition must reside within the 8GB limit
30 ; imposed by CHS addressing + translation.
32 ; This boot loader can be placed at any of the following places:
33 ; * Master Boot Record (MBR)
34 ; * Boot sector of an extended partition
35 ; * Boot sector of a primary partition
36 ; * Boot sector of a logical partition
38 ; In order to coexist with a fdisk partition table (64 bytes), and
39 ; leave room for a two byte signature (0xAA55) in the end, boot0 is
40 ; restricted to 446 bytes (512 - 64 - 2). If boot0 did not have to
41 ; live in the MBR, then we would have 510 bytes to play with.
43 ; boot0 is always loaded by the BIOS or another first level booter
46 ; This code is written for the NASM assembler.
47 ; nasm boot0.s -o boot0
49 ;--------------------------------------------------------------------------
52 FLOPPY EQU 0x00 ; floppy dev number
53 HDISK EQU 0x80 ; hard drive dev number
54 DEBUG EQU 0 ; enable debugging output
56 BOOTSEG EQU 0x0 ; our sole segment
57 BOOTSP EQU 0xFFF0 ; stack pointer
58 BOOTLOAD EQU 0x7C00 ; booter load address
59 BOOTRELOC EQU 0xE000 ; booter is relocated here
60 BOOTSIG EQU 0xAA55 ; booter signature
62 BOOT2_SIZE EQU 88 ; load this many blocks for boot2
63 BOOT2_ADDR EQU 0x3000 ; where to load boot2
66 DRIVE_NUM EQU FLOPPY ; floppy drive
68 DRIVE_NUM EQU HDISK ; "C" drive
70 SECTOR_BYTES EQU 512 ; sector size in bytes
72 BUF_MBR EQU 0x1000 ; memory buffer for MBR
73 BUF_EXT EQU 0x1200 ; memory buffer for extended partition
75 TABLE_MAIN EQU BUF_MBR + 0x1be ; location of main partition table
76 TABLE_EXT EQU BUF_EXT + 0x1be ; location of ext partition table
77 ENTRY_SIZE EQU 16 ; size of each fdisk partition entry
78 TYPE_BOOT EQU 0xab ; partition type we are looking for
79 TYPE_EXT EQU 0x05 ; extended partition type
80 TYPE_EXT_1 EQU 0x0f ; Windows extended partition
81 TYPE_EXT_2 EQU 0x85 ; Linux extended partition
82 EXT_LEVELS_MAX EQU 128 ; max extended partition levels
85 ;--------------------------------------------------------------------------
86 ; Start of text segment.
90 ORG 0xE000 ; must match BOOTRELOC
92 ;--------------------------------------------------------------------------
96 ; Set up the stack to grow down from BOOTSEG:BOOTSP.
97 ; Interrupts should be off while the stack is being manipulated.
101 mov ss, ax ; ss <- BOOTSEG
102 mov sp, BOOTSP ; sp <- BOOTSP
103 sti ; reenable interrupts
105 ; Relocate the booter code from DS:SI to ES:DI,
106 ; or from 0:7C00h to BOOTSEG:BOOTRELOC.
108 mov es, ax ; es <- BOOTSEG
111 mov si, BOOTLOAD ; si <- BOOTLOAD (source)
112 mov di, BOOTRELOC ; di <- BOOTRELOC (destination)
114 cld ; auto-increment SI and/or DI registers
115 mov cx, 256 ; copy 256 words (512 bytes)
116 repnz movsw ; repeat string move (word) operation
118 ; Code relocated, jump to start_reloc in relocated location.
120 jmp BOOTSEG:start_reloc
122 ;--------------------------------------------------------------------------
123 ; Start execution from the relocated location.
127 mov ds, ax ; ds <- BOOTSEG
129 mov al, '=' ; indicate execution start
132 ; Get disk parameters (CHS) using INT13/F8 call.
134 mov dl, DRIVE_NUM ; boot drive is drive C
135 mov ah, 8 ; Read Disk Driver Parameter function
137 and cl, 0x3f ; sectors/track
138 mov [max_sectors], cl
142 mov al, '>' ; indicate INT13/F8 success
145 mov ax, BOOTSEG ; es <- BOOTSEG
148 ; Since this code may not always reside in the MBR, we will always
149 ; start by loading the MBR to BUF_MBR.
151 mov WORD [chs_cx], 0x0001 ; cyl = 0, sect = 1
152 mov BYTE [chs_dx + 1], 0 ; head = 0
153 xor cx, cx ; skip 0 sectors
154 mov ax, 1 ; read 1 sector
155 mov bx, BUF_MBR ; load buffer
159 mov di, TABLE_MAIN ; argument for find_booter
160 cmp WORD [di + 64], BOOTSIG ; correct signature found?
161 jne error ; Oops! no signature!
162 mov bl, TYPE_BOOT ; look for this partition type
163 mov bh, 0 ; initial nesting level is 0
172 ;--------------------------------------------------------------------------
173 ; Locate the booter partition and load the booter.
176 ; di - pointer to fdisk partition table.
177 ; bl - partition type to look for.
179 ; The following registers are modified:
186 mov si, di ; si <- pointer to partition table
187 mov cx, 4 ; 4 partition entries per table
191 ; Hunt for a fdisk partition type that matches the value in bl.
194 mov al, bh ; log partition type seen
200 cmp BYTE [si + 4], bl ; Is this the booter partition?
201 je load_booter ; yes, load the booter
203 add si, ENTRY_SIZE ; si <- next partition entry
204 loop find_booter_pri ; loop while cx is not zero
206 ; No primary (or perhaps logical) booter partition found in the
207 ; current partition table. Restart and look for extended partitions.
209 mov si, di ; si <- pointer to partition table
210 mov cx, 4 ; 4 partition entries per table
214 ; Look for extended partition entries in the partition table.
217 mov al, bh ; log partition type seen
225 cmp BYTE [si + 4], TYPE_EXT ; Is this an extended partition?
226 je find_booter_ext_2 ; yes, load its partition table
228 cmp BYTE [si + 4], TYPE_EXT_1 ; Is this an extended partition?
229 je find_booter_ext_2 ; yes, load its partition table
231 cmp BYTE [si + 4], TYPE_EXT_2 ; Is this an extended partition?
232 je find_booter_ext_2 ; yes, load its partition table
236 ; si is not pointing to an extended partition entry,
237 ; try the next entry in the partition table.
239 add si, ENTRY_SIZE ; si <- next partition entry
240 loop find_booter_ext ; loop while cx is not zero
242 jmp find_booter_end ; give up
245 cmp bh, EXT_LEVELS_MAX
246 ja find_booter_end ; in too deep!
248 inc bh ; increment nesting level counter
250 ; Prepare the arguments for the load function call to
251 ; load the extended partition table into memory.
252 ; Note that si points to the extended partition entry.
256 mov ax, [si + 2] ; CH/CL
259 xor cx, cx ; skip 0 sectors
260 mov ax, 1 ; read 1 sector
261 mov bx, BUF_EXT ; load to BUF_EXT
265 jc find_booter_ext_3 ; bail out if load failed
267 mov di, TABLE_EXT ; di <- pointer to new partition table
268 cmp WORD [di + 64], BOOTSIG
269 jne find_booter_ext_3 ; OhOh! no signature!
271 call find_booter ; recursion...
274 dec bh ; decrement nesting level counter
276 ; If we got here, then we know there isn't a booter
277 ; partition linked from this partition entry.
279 test bh, bh ; if we are at level 0, then
280 jz find_booter_ext_1 ; look for next extended partition entry
287 ;--------------------------------------------------------------------------
288 ; Yeah! Found the booter partition. The first sector in this partition
289 ; is reserved for the boot sector code (us). So load the booter
290 ; starting from the second sector in the partition, then jump to the
291 ; start of the booter.
296 mov ax, [si + 2] ; CH/CL
299 mov cx, 1 ; skip the initial boot sector
300 mov ax, BOOT2_SIZE ; read BOOT2_SIZE sectors
301 mov bx, BOOT2_ADDR ; where to place boot2 code
302 call load ; load it...
304 xor edx, edx ; argument for boot2 (hard drive boot)
306 inc edx ; floppy is dev 1
308 jmp BOOTSEG:BOOT2_ADDR ; there is no going back now!
310 ;--------------------------------------------------------------------------
311 ; Load sectors from disk using INT13/F2 call. The sectors are loaded
312 ; one sector at a time to avoid any BIOS bugs, and eliminate
313 ; complexities with crossing track boundaries, and other gotchas.
316 ; cx - number of sectors to skip
317 ; ax - number of sectors to read
318 ; bx - pointer to the memory buffer (must not cross a segment boundary)
319 ; [chs_cx][chs_dx] - CHS starting position
325 ; The caller must save any registers it needs.
329 call next_sector ; [chs_cx][chs_dx] <- next sector
333 mov cx, ax ; cx <- number of sectors to read
336 call read_sector ; load a single sector
337 jc load_exit ; abort if carry flag is set
338 add bx, SECTOR_BYTES ; increment buffer pointer
339 call next_sector ; [chs_cx][chs_dx] <- next sector
341 clc ; successful exit
345 ;--------------------------------------------------------------------------
346 ; Read a single sector from the hard disk.
349 ; [chs_cx][chs_dx] - CHS starting position
350 ; bx - pointer to the sector memory buffer
351 ; (must not cross a segment boundary)
357 ; Caller's cx register is preserved.
361 mov cx, 5 ; try 5 times to read the sector
368 mov dl, DRIVE_NUM ; drive number
369 mov ax, 0x0201 ; Func 2, read 1 sector
370 int 0x13 ; read sector
371 jnc read_sector_ok ; CF = 0 indicates success
373 mov al, '*' ; sector read error indicator
376 xor ax, ax ; Reset the drive and retry the read
380 loop read_sector_1 ; retry while cx is not zero
382 stc ; set carry flag to indicate error
387 mov al, '.' ; successful sector read indicator
389 clc ; success, clear carry flag
393 ;--------------------------------------------------------------------------
394 ; Given the current CHS position stored in [chs_cx][chs_dx], update
395 ; it so that the value in [chs_cx][chs_dx] points to the following
399 ; [chs_cx][chs_dx] - CHS position
401 ; [max_sectors] and [max_heads] must be valid.
403 ; Caller's ax and bx registers are preserved.
409 ; Extract the CHS values from the packed register values in memory.
412 and al, 0x3f ; al <- sector number (1-63)
417 and bx, 0x03ff ; bx <- cylinder number
419 mov ah, [chs_dx + 1] ; ah <- head number
421 inc al ; Increment CHS by one sector.
422 cmp al, [max_sectors]
427 jbe next_sector_new_head
432 xor ah, ah ; head number starts at 0
435 mov al, 1 ; sector number starts at 1
438 ; Reassemble the CHS values back into the packed representation
441 mov [chs_cx + 1], bl ; lower 8-bits of the 10-bit cylinder
444 mov [chs_cx], bh ; cylinder & sector number
445 mov [chs_dx + 1], ah ; head number
451 ;--------------------------------------------------------------------------
452 ; Write a string to the console.
455 ; ds:si pointer to a NULL terminated string.
457 ; The following registers are modified:
461 mov bx, 1 ; bh=0, bl=1 (blue)
462 cld ; increment SI after each lodsb call
464 lodsb ; load a byte from DS:SI into al
465 cmp al, 0 ; Is it a NULL?
466 je message_done ; yes, all done
467 mov ah, 0xE ; bios INT10 Func 0xE
468 int 0x10 ; bios display a byte in tty mode
469 jmp short message_loop
473 ;--------------------------------------------------------------------------
474 ; Write a ASCII character to the console.
477 ; al contains the ASCII character printed.
481 mov bx, 1 ; bh=0, bl=1 (blue)
482 mov ah, 0x0e ; bios int 10, function 0xe
483 int 0x10 ; bios display a byte in tty mode
488 ;==========================================================================
489 ; DEBUG FUNCTION START
491 ; If DEBUG is set to 1, this booter will become too large for the MBR,
492 ; but it will still be less than 510 bytes, which is fine for a partition's
494 ;==========================================================================
496 ;--------------------------------------------------------------------------
497 ; Write a variable number of spaces to the console.
500 ; al number to spaces to display
505 mov cl, al ; use cx as the loop counter
506 mov al, ' ' ; character to print
515 ;--------------------------------------------------------------------------
516 ; Write the hex byte value to the console.
519 ; al contains the byte to be displayed. e.g. if al is 0x3f, then 3F
520 ; will be displayed on screen.
525 call display_nibble ; display upper nibble
527 call display_nibble ; display lower nibble
529 mov ax, 10 ; display carriage return
540 add al, 'A' - '9' - 1
545 ;==========================================================================
547 ;==========================================================================
550 ; Disk parameters gathered through INT13/F8 call.
552 max_sectors db 0 ; number of sectors per track
553 max_heads db 0 ; number of heads
555 ; Parameters to our load function.
557 chs_cx dw 0x0001 ; cx register for INT13/F2 call
558 chs_dx dw 0x0000 ; dx register for INT13/F2 call
560 ;--------------------------------------------------------------------------
561 ; NULL terminated strings.
563 load_error db 10, 13, 'Load Error', 0
565 ;--------------------------------------------------------------------------
566 ; Pad the rest of the 512 byte sized booter with zeroes. The last
567 ; two bytes is the mandatory boot sector signature.
569 ; If the booter code becomes too large, then nasm will complain
570 ; that the 'times' argument is negative.
573 times 446-($-$$) db 0
576 ;--------------------------------------------------------------------------
577 ; Put fake partition entries for the bootable floppy image
579 part1bootid db 0x80 ; first partition active
580 part1head db 0x00 ; head #
581 part1sect db 0x02 ; sector # (low 6 bits)
582 part1cyl db 0x00 ; cylinder # (+ high 2 bits of above)
583 part1systid db 0xab ; Apple boot partition
584 times 3 db 0x00 ; ignore head/cyl/sect #'s
585 part1relsect dd 0x00000001 ; start at sector 1
586 part1numsect dd 0x00000058 ; 44K for booter
587 part2bootid db 0x00 ; not active
588 times 3 db 0x00 ; ignore head/cyl/sect #'s
589 part2systid db 0xa8 ; Apple UFS partition
590 times 3 db 0x00 ; ignore head/cyl/sect #'s
591 part2relsect dd 0x0000005a ; start after booter
592 ; part2numsect dd 0x00000ae6 ; 1.44MB - 45K
593 part2numsect dd 0x00001626 ; 2.88MB - 45K
597 times 510-($-$$) db 0