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 DEBUG EQU 0 ; enable debugging output
54 BOOTSEG EQU 0x0 ; our sole segment
55 BOOTSP EQU 0xFFF0 ; stack pointer
56 BOOTLOAD EQU 0x7C00 ; booter load address
57 BOOTRELOC EQU 0xE000 ; booter is relocated here
58 BOOTSIG EQU 0xAA55 ; booter signature
60 BOOT2_SIZE EQU 88 ; load this many blocks for boot2
61 BOOT2_ADDR EQU 0x3000 ; where to load boot2
63 DRIVE_NUM EQU 0x80 ; "C" drive
64 SECTOR_BYTES EQU 512 ; sector size in bytes
66 BUF_MBR EQU 0x1000 ; memory buffer for MBR
67 BUF_EXT EQU 0x1200 ; memory buffer for extended partition
69 TABLE_MAIN EQU BUF_MBR + 0x1be ; location of main partition table
70 TABLE_EXT EQU BUF_EXT + 0x1be ; location of ext partition table
71 ENTRY_SIZE EQU 16 ; size of each fdisk partition entry
72 TYPE_BOOT EQU 0xab ; partition type we are looking for
73 TYPE_EXT EQU 0x05 ; extended partition type
74 TYPE_EXT_1 EQU 0x0f ; Windows extended partition
75 TYPE_EXT_2 EQU 0x85 ; Linux extended partition
76 EXT_LEVELS_MAX EQU 128 ; max extended partition levels
78 ; Disk parameters gathered through INT13/F8 call.
80 max_sectors db 0 ; number of sectors per track
81 max_heads db 0 ; number of heads
83 ; Parameters to our load function.
85 chs_cx dw 0 ; cx register for INT13/F2 call
86 chs_dx dw 0 ; dx register for INT13/F2 call
89 ;--------------------------------------------------------------------------
90 ; Start of text segment.
94 ORG 0xE000 ; must match BOOTRELOC
96 ;--------------------------------------------------------------------------
100 ; Set up the stack to grow down from BOOTSEG:BOOTSP.
101 ; Interrupts should be off while the stack is being manipulated.
105 mov ss, ax ; ss <- BOOTSEG
106 mov sp, BOOTSP ; sp <- BOOTSP
107 sti ; reenable interrupts
109 ; Relocate the booter code from DS:SI to ES:DI,
110 ; or from 0:7C00h to BOOTSEG:BOOTRELOC.
112 mov es, ax ; es <- BOOTSEG
115 mov si, BOOTLOAD ; si <- BOOTLOAD (source)
116 mov di, BOOTRELOC ; di <- BOOTRELOC (destination)
118 cld ; auto-increment SI and/or DI registers
119 mov cx, 256 ; copy 256 words (512 bytes)
120 repnz movsw ; repeat string move (word) operation
122 ; Code relocated, jump to start_reloc in relocated location.
124 jmp BOOTSEG:start_reloc
126 ;--------------------------------------------------------------------------
127 ; Start execution from the relocated location.
131 mov ds, ax ; ds <- BOOTSEG
133 mov al, '=' ; indicate execution start
136 ; Get disk parameters (CHS) using INT13/F8 call.
138 mov dl, DRIVE_NUM ; boot drive is drive C
139 mov ah, 8 ; Read Disk Driver Parameter function
141 and cl, 0x3f ; sectors/track
142 mov [max_sectors], cl
146 mov al, '>' ; indicate INT13/F8 success
149 ; Since this code may not always reside in the MBR, we will always
150 ; start by loading the MBR to BUF_MBR.
152 mov WORD [chs_cx], 0x0001 ; cyl = 0, sect = 1
153 mov BYTE [chs_dx + 1], 0 ; head = 0
154 xor cx, cx ; skip 0 sectors
155 mov ax, 1 ; read 1 sector
156 mov bx, BUF_MBR ; load buffer
160 mov di, TABLE_MAIN ; argument for find_booter
161 cmp WORD [di + 64], BOOTSIG ; correct signature found?
162 jne error ; Oops! no signature!
163 mov bl, TYPE_BOOT ; look for this partition type
164 mov bh, 0 ; initial nesting level is 0
173 ;--------------------------------------------------------------------------
174 ; Locate the booter partition and load the booter.
177 ; di - pointer to fdisk partition table.
178 ; bl - partition type to look for.
180 ; The following registers are modified:
187 mov si, di ; si <- pointer to partition table
188 mov cx, 4 ; 4 partition entries per table
192 ; Hunt for a fdisk partition type that matches the value in bl.
195 mov al, bh ; log partition type seen
201 cmp BYTE [si + 4], bl ; Is this the booter partition?
202 je load_booter ; yes, load the booter
204 add si, ENTRY_SIZE ; si <- next partition entry
205 loop find_booter_pri ; loop while cx is not zero
207 ; No primary (or perhaps logical) booter partition found in the
208 ; current partition table. Restart and look for extended partitions.
210 mov si, di ; si <- pointer to partition table
211 mov cx, 4 ; 4 partition entries per table
215 ; Look for extended partition entries in the partition table.
218 mov al, bh ; log partition type seen
226 cmp BYTE [si + 4], TYPE_EXT ; Is this an extended partition?
227 je find_booter_ext_2 ; yes, load its partition table
229 cmp BYTE [si + 4], TYPE_EXT_1 ; Is this an extended partition?
230 je find_booter_ext_2 ; yes, load its partition table
232 cmp BYTE [si + 4], TYPE_EXT_2 ; Is this an extended partition?
233 je find_booter_ext_2 ; yes, load its partition table
237 ; si is not pointing to an extended partition entry,
238 ; try the next entry in the partition table.
240 add si, ENTRY_SIZE ; si <- next partition entry
241 loop find_booter_ext ; loop while cx is not zero
243 jmp find_booter_end ; give up
246 cmp bh, EXT_LEVELS_MAX
247 ja find_booter_end ; in too deep!
249 inc bh ; increment nesting level counter
251 ; Prepare the arguments for the load function call to
252 ; load the extended partition table into memory.
253 ; Note that si points to the extended partition entry.
257 mov ax, [si + 2] ; CH/CL
260 xor cx, cx ; skip 0 sectors
261 mov ax, 1 ; read 1 sector
262 mov bx, BUF_EXT ; load to BUF_EXT
266 jc find_booter_ext_3 ; bail out if load failed
268 mov di, TABLE_EXT ; di <- pointer to new partition table
269 cmp WORD [di + 64], BOOTSIG
270 jne find_booter_ext_3 ; OhOh! no signature!
272 call find_booter ; recursion...
275 dec bh ; decrement nesting level counter
277 ; If we got here, then we know there isn't a booter
278 ; partition linked from this partition entry.
280 test bh, bh ; if we are at level 0, then
281 jz find_booter_ext_1 ; look for next extended partition entry
288 ;--------------------------------------------------------------------------
289 ; Yeah! Found the booter partition. The first sector in this partition
290 ; is reserved for the boot sector code (us). So load the booter
291 ; starting from the second sector in the partition, then jump to the
292 ; start of the booter.
297 mov ax, [si + 2] ; CH/CL
300 mov cx, 1 ; skip the initial boot sector
301 mov ax, BOOT2_SIZE ; read BOOT2_SIZE sectors
302 mov bx, BOOT2_ADDR ; where to place boot2 code
303 call load ; load it...
305 xor edx, edx ; argument for boot2 (hard drive boot)
306 jmp BOOTSEG:BOOT2_ADDR ; there is no going back now!
308 ;--------------------------------------------------------------------------
309 ; Load sectors from disk using INT13/F2 call. The sectors are loaded
310 ; one sector at a time to avoid any BIOS bugs, and eliminate
311 ; complexities with crossing track boundaries, and other gotchas.
314 ; cx - number of sectors to skip
315 ; ax - number of sectors to read
316 ; bx - pointer to the memory buffer (must not cross a segment boundary)
317 ; [chs_cx][chs_dx] - CHS starting position
323 ; The caller must save any registers it needs.
327 call next_sector ; [chs_cx][chs_dx] <- next sector
331 mov cx, ax ; cx <- number of sectors to read
334 call read_sector ; load a single sector
335 jc load_exit ; abort if carry flag is set
336 add bx, SECTOR_BYTES ; increment buffer pointer
337 call next_sector ; [chs_cx][chs_dx] <- next sector
339 clc ; successful exit
343 ;--------------------------------------------------------------------------
344 ; Read a single sector from the hard disk.
347 ; [chs_cx][chs_dx] - CHS starting position
348 ; bx - pointer to the sector memory buffer
349 ; (must not cross a segment boundary)
355 ; Caller's cx register is preserved.
359 mov cx, 5 ; try 5 times to read the sector
366 mov dl, DRIVE_NUM ; drive number
367 mov ax, 0x0201 ; Func 2, read 1 sector
368 int 0x13 ; read sector
369 jnc read_sector_ok ; CF = 0 indicates success
371 mov al, '*' ; sector read error indicator
374 xor ax, ax ; Reset the drive and retry the read
378 loop read_sector_1 ; retry while cx is not zero
380 stc ; set carry flag to indicate error
385 mov al, '.' ; successful sector read indicator
387 clc ; success, clear carry flag
391 ;--------------------------------------------------------------------------
392 ; Given the current CHS position stored in [chs_cx][chs_dx], update
393 ; it so that the value in [chs_cx][chs_dx] points to the following
397 ; [chs_cx][chs_dx] - CHS position
399 ; [max_sectors] and [max_heads] must be valid.
401 ; Caller's ax and bx registers are preserved.
407 ; Extract the CHS values from the packed register values in memory.
410 and al, 0x3f ; al <- sector number (1-63)
415 and bx, 0x03ff ; bx <- cylinder number
417 mov ah, [chs_dx + 1] ; ah <- head number
419 inc al ; Increment CHS by one sector.
420 cmp al, [max_sectors]
425 jbe next_sector_new_head
430 xor ah, ah ; head number starts at 0
433 mov al, 1 ; sector number starts at 1
436 ; Reassemble the CHS values back into the packed representation
439 mov [chs_cx + 1], bl ; lower 8-bits of the 10-bit cylinder
442 mov [chs_cx], bh ; cylinder & sector number
443 mov [chs_dx + 1], ah ; head number
449 ;--------------------------------------------------------------------------
450 ; Write a string to the console.
453 ; ds:si pointer to a NULL terminated string.
455 ; The following registers are modified:
459 mov bx, 1 ; bh=0, bl=1 (blue)
460 cld ; increment SI after each lodsb call
462 lodsb ; load a byte from DS:SI into al
463 cmp al, 0 ; Is it a NULL?
464 je message_done ; yes, all done
465 mov ah, 0xE ; bios INT10 Func 0xE
466 int 0x10 ; bios display a byte in tty mode
467 jmp short message_loop
471 ;--------------------------------------------------------------------------
472 ; Write a ASCII character to the console.
475 ; al contains the ASCII character printed.
479 mov bx, 1 ; bh=0, bl=1 (blue)
480 mov ah, 0x0e ; bios int 10, function 0xe
481 int 0x10 ; bios display a byte in tty mode
486 ;==========================================================================
487 ; DEBUG FUNCTION START
489 ; If DEBUG is set to 1, this booter will become too large for the MBR,
490 ; but it will still be less than 510 bytes, which is fine for a partition's
492 ;==========================================================================
494 ;--------------------------------------------------------------------------
495 ; Write a variable number of spaces to the console.
498 ; al number to spaces to display
503 mov cl, al ; use cx as the loop counter
504 mov al, ' ' ; character to print
513 ;--------------------------------------------------------------------------
514 ; Write the hex byte value to the console.
517 ; al contains the byte to be displayed. e.g. if al is 0x3f, then 3F
518 ; will be displayed on screen.
523 call display_nibble ; display upper nibble
525 call display_nibble ; display lower nibble
527 mov ax, 10 ; display carriage return
538 add al, 'A' - '9' - 1
543 ;==========================================================================
545 ;==========================================================================
548 ;--------------------------------------------------------------------------
549 ; NULL terminated strings.
551 load_error db 10, 13, 'Load Error', 0
553 ;--------------------------------------------------------------------------
554 ; Pad the rest of the 512 byte sized booter with zeroes. The last
555 ; two bytes is the mandatory boot sector signature.
557 ; If the booter code becomes too large, then nasm will complain
558 ; that the 'times' argument is negative.
561 times 446-($-$$) db 0
564 times 510-($-$$) db 0