1 ; Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved.
3 ; @APPLE_LICENSE_HEADER_START@
5 ; Portions Copyright (c) 1999-2003 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 2.0 (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 active partition, load the
27 ; partition booter into memory, and jump to the booter's entry point.
28 ; It leaves the boot drive in DL and a pointer to the partition entry in SI.
30 ; This boot loader must be placed in the Master Boot Record.
32 ; In order to coexist with a fdisk partition table (64 bytes), and
33 ; leave room for a two byte signature (0xAA55) in the end, boot0 is
34 ; restricted to 446 bytes (512 - 64 - 2). If boot0 did not have to
35 ; live in the MBR, then we would have 510 bytes to work with.
37 ; boot0 is always loaded by the BIOS or another booter to 0:7C00h.
39 ; This code is written for the NASM assembler.
40 ; nasm boot0.s -o boot0
44 ; Set to 1 to enable obscure debug messages.
49 ; Set to 1 to support loading the partition booter (boot1) from a
52 EXT_PART_SUPPORT EQU 1
59 kBoot0Segment EQU 0x0000
60 kBoot0Stack EQU 0xFFF0 ; boot0 stack pointer
61 kBoot0LoadAddr EQU 0x7C00 ; boot0 load address
62 kBoot0RelocAddr EQU 0xE000 ; boot0 relocated address
64 kMBRBuffer EQU 0x1000 ; MBR buffer address
65 kExtBuffer EQU 0x1200 ; EXT boot block buffer address
67 kPartTableOffset EQU 0x1be
68 kMBRPartTable EQU kMBRBuffer + kPartTableOffset
69 kExtPartTable EQU kExtBuffer + kPartTableOffset
71 kSectorBytes EQU 512 ; sector size in bytes
72 kBootSignature EQU 0xAA55 ; boot sector signature
74 kPartCount EQU 4 ; number of paritions per table
75 kPartTypeBoot EQU 0xab ; boot2 partition type
76 kPartTypeExtDOS EQU 0x05 ; DOS extended partition type
77 kPartTypeExtWin EQU 0x0f ; Windows extended partition type
78 kPartTypeExtLinux EQU 0x85 ; Linux extended partition type
89 ; Format of fdisk partition entry.
91 ; The symbol 'part_size' is automatically defined as an `EQU'
92 ; giving the size of the structure.
95 .bootid: resb 1 ; bootable or not
96 .head: resb 1 ; starting head, sector, cylinder
99 .type: resb 1 ; partition type
100 .endhead resb 1 ; ending head, sector, cylinder
103 .lba: resd 1 ; starting lba
104 .sectors resd 1 ; size in sectors
110 %macro DebugCharMacro 1
116 %define DebugChar(x) DebugCharMacro x
121 ;--------------------------------------------------------------------------
122 ; Start of text segment.
126 ORG 0xE000 ; must match kBoot0RelocAddr
128 ;--------------------------------------------------------------------------
129 ; Boot code is loaded at 0:7C00h.
133 ; Set up the stack to grow down from kBoot0Segment:kBoot0Stack.
134 ; Interrupts should be off while the stack is being manipulated.
139 mov sp, kBoot0Stack ; sp <- top of stack
140 sti ; reenable interrupts
146 ; Relocate boot0 code.
148 mov si, kBoot0LoadAddr ; si <- source
149 mov di, kBoot0RelocAddr ; di <- destination
151 cld ; auto-increment SI and/or DI registers
152 mov cx, kSectorBytes/2 ; copy 256 words
153 repnz movsw ; repeat string move (word) operation
155 ; Code relocated, jump to start_reloc in relocated location.
159 ;--------------------------------------------------------------------------
160 ; Start execution from the relocated location.
174 ; Clear various flags in memory.
177 mov [first_part], eax ; clear EBIOS LBA offset
181 mov [ebios_present], al ; clear EBIOS support flag
184 ; Check if EBIOS is supported for this hard drive.
186 mov ah, 0x41 ; Function 0x41
187 mov bx, 0x55AA ; check signature
191 ; If successful, the return values are as follows:
194 ; ah = major version of EBIOS extensions (0x21 = version 1.1)
197 ; cx = support bits. bit 0 must be set for function 0x42.
200 cmp bx, 0xAA55 ; check BX = 0xAA55
201 jnz .ebios_check_done
202 test cl, 0x01 ; check enhanced drive read support
203 setnz [ebios_present] ; EBIOS supported, set flag
204 DebugChar('E') ; EBIOS supported
208 mov [ebios_present], al
212 ; Since this code may not always reside in the MBR, always start by
213 ; loading the MBR to kMBRBuffer.
215 mov al, 1 ; load one sector
217 mov es, bx ; MBR load segment = 0
218 mov bx, kMBRBuffer ; MBR load address
219 mov si, bx ; pointer to fake partition entry
221 mov WORD [si], 0x0000 ; CHS DX: head = 0
222 mov WORD [si + 2], 0x0001 ; CHS CX: cylinder = 0, sector = 1
224 mov DWORD [si + part.lba], 0x00000000 ; LBA sector 0
227 jc .next_drive ; MBR load error
230 ; Look for the booter partition in the MBR partition table,
231 ; which is at offset kMBRPartTable.
233 mov di, kMBRPartTable ; pointer to partition table
234 call find_boot ; will not return on success
237 inc dl ; next drive number
238 test dl, 0x4 ; went through all 4 drives?
239 jz .loop ; not yet, loop again
242 mov si, boot_error_str
249 ;--------------------------------------------------------------------------
250 ; Find the active (boot) partition and load the booter from the partition.
253 ; DL = drive number (0x80 + unit number)
254 ; DI = pointer to fdisk partition table.
260 push cx ; preserve CX and SI
264 ; Check for boot block signature 0xAA55 following the 4 partition
267 cmp WORD [di + part_size * kPartCount], kBootSignature
268 jne NEAR .exit ; boot signature not found
270 mov si, di ; make SI a pointer to partition table
271 mov cx, kPartCount ; number of partition entries per table
275 ; First scan through the partition table looking for the active
276 ; partition. Postpone walking the extended partition chain for
277 ; the second pass. Do not merge the two without changing the
278 ; buffering scheme used to store extended partition tables.
281 mov al, [si + part.type] ; print partition type
285 cmp BYTE [si + part.type], 0
287 cmp BYTE [si + part.bootid], kPartActive
292 ; fix offset for load
294 mov [first_part], eax
297 ; Found boot partition, read boot sector to memory.
300 mov bx, kBoot0Segment
302 mov bx, kBoot0LoadAddr
303 call load ; will not return on success
304 jc error ; load error, keep looking?
307 ; Fix up absolute block location in partition record.
309 mov eax, [si + part.lba]
311 mov [si + part.lba], eax
315 ; Jump to partition booter. The drive number is already in register DL.
316 ; SI is pointing to the modified partition entry.
318 jmp kBoot0Segment:kBoot0LoadAddr
321 add si, part_size ; advance SI to next partition entry
322 loop .loop ; loop through all partition entries
326 ; No primary (or logical) boot partition found in the current
327 ; partition table. Restart and look for extended partitions.
329 mov si, di ; make SI a pointer to partition table
330 mov cx, kPartCount ; number of partition entries per table
334 mov al, [si + part.type] ; AL <- partition type
336 cmp al, kPartTypeExtDOS ; Extended DOS
339 cmp al, kPartTypeExtWin ; Extended Windows(95)
342 cmp al, kPartTypeExtLinux ; Extended Linux
347 ; Advance si to the next partition entry in the extended
350 add si, part_size ; advance SI to next partition entry
351 loop .ext_loop ; loop through all partition entries
352 jmp .exit ; boot partition not found
357 ; Save current partition offset, since for extended
358 ; partitions we will overwrite it when loading the new
361 mov ebp, [si + part.lba]
364 ; Setup the arguments for the load function call to bring the
365 ; extended partition table into memory.
366 ; Remember that SI points to the extended partition entry.
368 mov al, 1 ; read 1 sector
371 mov bx, kExtBuffer ; load extended boot sector
373 jc .ext_continue ; load error
376 ; The LBA address of all extended partitions is relative based
377 ; on the LBA address of the extended partition in the MBR, or
378 ; the extended partition at the head of the chain. Thus it is
379 ; necessary to save the LBA address of the first extended partition.
384 add ebp, [first_part]
390 mov [first_part], ebp
401 ; Call find_boot recursively to scan through the extended partition
402 ; table. Load DI with a pointer to the extended table in memory.
404 mov di, kExtPartTable ; partition table pointer
405 call find_boot ; recursion...
408 ; Since there is an "unwritten" rule that limits each partition table
409 ; to have 0 or 1 extended partitions, there is no point in looking for
410 ; any additional extended partition entries at this point. There is no
411 ; boot partition linked beyond the extended partition that was loaded
415 %endif ; EXT_PART_SUPPORT
419 ; Boot partition not found. Giving up.
425 ;--------------------------------------------------------------------------
426 ; load - Load one or more sectors from a partition.
429 ; AL = number of 512-byte sectors to read.
430 ; ES:BX = pointer to where the sectors should be stored.
431 ; DL = drive number (0x80 + unit number)
432 ; SI = pointer to the partition entry.
441 test BYTE [ebios_present], 1
446 mov cx, 5 ; load retry count
448 call read_lba ; use INT13/F42
454 mov cx, 5 ; load retry count
456 call read_chs ; use INT13/F2
466 ;--------------------------------------------------------------------------
467 ; read_chs - Read sectors from a partition using CHS addressing.
470 ; AL = number of 512-byte sectors to read.
471 ; ES:BX = pointer to where the sectors should be stored.
472 ; DL = drive number (0x80 + unit number)
473 ; SI = pointer to the partition entry.
480 pusha ; save all registers
483 ; Read the CHS start values from the partition entry.
485 mov dh, [ si + part.head ] ; drive head
486 mov cx, [ si + part.sect ] ; drive sector + cylinder
489 ; INT13 Func 2 - Read Disk Sectors
493 ; AL = number of sectors to read
494 ; CH = lowest 8 bits of the 10-bit cylinder number
495 ; CL = bits 6 & 7: cylinder number bits 8 and 9
496 ; bits 0 - 5: starting sector number (1-63)
497 ; DH = starting head number (0 to 255)
498 ; DL = drive number (80h + drive unit)
499 ; es:bx = pointer where to place sectors read from disk
502 ; AH = return status (sucess is 0)
503 ; AL = burst error length if ah=0x11 (ECC corrected)
507 mov ah, 0x02 ; Func 2
511 DebugChar('r') ; indicate INT13/F2 error
514 ; Issue a disk reset on error.
515 ; Should this be changed to Func 0xD to skip the diskette controller
520 stc ; set carry to indicate error
527 ;--------------------------------------------------------------------------
528 ; read_lba - Read sectors from a partition using LBA addressing.
531 ; AL = number of 512-byte sectors to read (valid from 1-127).
532 ; ES:BX = pointer to where the sectors should be stored.
533 ; DL = drive number (0x80 + unit number)
534 ; SI = pointer to the partition entry.
541 pusha ; save all registers
542 mov bp, sp ; save current SP
545 ; Create the Disk Address Packet structure for the
546 ; INT13/F42 (Extended Read Sectors) on the stack.
549 ; push DWORD 0 ; offset 12, upper 32-bit LBA
550 push ds ; For sake of saving memory,
551 push ds ; push DS register, which is 0.
553 mov ecx, [first_part] ; offset 8, lower 32-bit LBA
554 add ecx, [si + part.lba]
557 push es ; offset 6, memory segment
559 push bx ; offset 4, memory offset
561 xor ah, ah ; offset 3, must be 0
562 push ax ; offset 2, number of sectors
564 push WORD 16 ; offset 0-1, packet size
573 ; INT13 Func 42 - Extended Read Sectors
577 ; DL = drive number (80h + drive unit)
578 ; DS:SI = pointer to Disk Address Packet
581 ; AH = return status (sucess is 0)
585 ; Packet offset 2 indicates the number of sectors read
594 DebugChar('R') ; indicate INT13/F42 error
597 ; Issue a disk reset on error.
598 ; Should this be changed to Func 0xD to skip the diskette controller
603 stc ; set carry to indicate error
606 mov sp, bp ; restore SP
610 ;--------------------------------------------------------------------------
611 ; Write a string to the console.
614 ; DS:SI pointer to a NULL terminated string.
620 mov bx, 1 ; BH=0, BL=1 (blue)
621 cld ; increment SI after each lodsb call
623 lodsb ; load a byte from DS:SI into AL
624 cmp al, 0 ; Is it a NULL?
625 je .exit ; yes, all done
626 mov ah, 0xE ; INT10 Func 0xE
627 int 0x10 ; display byte in tty mode
633 ;--------------------------------------------------------------------------
634 ; Write a ASCII character to the console.
637 ; AL = ASCII character.
641 mov bx, 1 ; BH=0, BL=1 (blue)
642 mov ah, 0x0e ; bios INT 10, Function 0xE
643 int 0x10 ; display byte in tty mode
649 ;--------------------------------------------------------------------------
650 ; Write the 4-byte value to the console in hex.
653 ; EAX = Value to be displayed in hex.
662 call print_nibble ; display upper nibble
664 call print_nibble ; display lower nibble
668 mov al, 10 ; carriage return
680 add al, 'A' - '9' - 1
694 ;--------------------------------------------------------------------------
695 ; NULL terminated strings.
697 boot_error_str db 10, 13, 'b0 error', 0
699 ;--------------------------------------------------------------------------
700 ; Pad the rest of the 512 byte sized booter with zeroes. The last
701 ; two bytes is the mandatory boot sector signature.
703 ; If the booter code becomes too large, then nasm will complain
704 ; that the 'times' argument is negative.
707 times 446-($-$$) db 0
710 ;--------------------------------------------------------------------------
711 ; Put fake partition entries for the bootable floppy image
713 part1bootid db 0x80 ; first partition active
714 part1head db 0x00 ; head #
715 part1sect db 0x02 ; sector # (low 6 bits)
716 part1cyl db 0x00 ; cylinder # (+ high 2 bits of above)
717 part1systid db 0xab ; Apple boot partition
718 times 3 db 0x00 ; ignore head/cyl/sect #'s
719 part1relsect dd 0x00000001 ; start at sector 1
720 part1numsect dd 0x00000080 ; 64K for booter
721 part2bootid db 0x00 ; not active
722 times 3 db 0x00 ; ignore head/cyl/sect #'s
723 part2systid db 0xa8 ; Apple UFS partition
724 times 3 db 0x00 ; ignore head/cyl/sect #'s
725 part2relsect dd 0x00000082 ; start after booter
726 ; part2numsect dd 0x00000abe ; 1.44MB - 65K
727 part2numsect dd 0x000015fe ; 2.88MB - 65K
731 times 510-($-$$) db 0
738 ; In memory variables.
740 first_part resd 1 ; starting LBA of the intial extended partition.
741 this_part resd 1 ; starting LBA of the current extended partition.
742 ebios_present resb 1 ; 1 if EBIOS is supported, 0 otherwise.