]> git.saurik.com Git - apple/boot.git/blame - i386/boot0/chain0.s
boot-122.tar.gz
[apple/boot.git] / i386 / boot0 / chain0.s
CommitLineData
57c72a9a
A
1; Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved.
2;
3; @APPLE_LICENSE_HEADER_START@
4;
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
11; this file.
12;
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
19; under the License.
20;
21; @APPLE_LICENSE_HEADER_END@
22;
23; Boot Loader: boot0
24;
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.
29;
30; This boot loader must be placed in the Master Boot Record.
31;
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.
36;
37; boot0 is always loaded by the BIOS or another booter to 0:7C00h.
38;
39; This code is written for the NASM assembler.
40; nasm boot0.s -o boot0
41
42
43;
44; Set to 1 to enable obscure debug messages.
45;
46DEBUG EQU 0
47
48;
49; Set to 1 to support loading the partition booter (boot1) from a
50; logical partition.
51;
52EXT_PART_SUPPORT EQU 1
53
54;
55; Various constants.
56;
57kBoot0Segment EQU 0x0000
58kBoot0Stack EQU 0xFFF0 ; boot0 stack pointer
59kBoot0LoadAddr EQU 0x7C00 ; boot0 load address
60kBoot0RelocAddr EQU 0xE000 ; boot0 relocated address
61
62kMBRBuffer EQU 0x1000 ; MBR buffer address
63kExtBuffer EQU 0x1200 ; EXT boot block buffer address
64
65kPartTableOffset EQU 0x1be
66kMBRPartTable EQU kMBRBuffer + kPartTableOffset
67kExtPartTable EQU kExtBuffer + kPartTableOffset
68
69kSectorBytes EQU 512 ; sector size in bytes
70kBootSignature EQU 0xAA55 ; boot sector signature
71
72kPartCount EQU 4 ; number of paritions per table
73kPartTypeBoot EQU 0xab ; boot2 partition type
74kPartTypeUFS EQU 0xa8 ; UFS partition type
75kPartTypeHFS EQU 0xaf ; HFS partition type
76kPartTypeExtDOS EQU 0x05 ; DOS extended partition type
77kPartTypeExtWin EQU 0x0f ; Windows extended partition type
78kPartTypeExtLinux EQU 0x85 ; Linux extended partition type
79
80kPartActive EQU 0x80
81
82;
83; Format of fdisk partition entry.
84;
85; The symbol 'part_size' is automatically defined as an `EQU'
86; giving the size of the structure.
87;
88 struc part
89.bootid: resb 1 ; bootable or not
90.head: resb 1 ; starting head, sector, cylinder
91.sect: resb 1 ;
92.cyl: resb 1 ;
93.type: resb 1 ; partition type
94.endhead resb 1 ; ending head, sector, cylinder
95.endsect: resb 1 ;
96.endcyl: resb 1 ;
97.lba: resd 1 ; starting lba
98.sectors resd 1 ; size in sectors
99 endstruc
100
101;
102; Macros.
103;
104%macro DebugCharMacro 1
105 mov al, %1
106 call print_char
107%endmacro
108
109%if DEBUG
110%define DebugChar(x) DebugCharMacro x
111%else
112%define DebugChar(x)
113%endif
114
115;--------------------------------------------------------------------------
116; Start of text segment.
117
118 SEGMENT .text
119
120 ORG 0xE000 ; must match kBoot0RelocAddr
121
122;--------------------------------------------------------------------------
123; Boot code is loaded at 0:7C00h.
124;
125start
126 ;
127 ; Set up the stack to grow down from kBoot0Segment:kBoot0Stack.
128 ; Interrupts should be off while the stack is being manipulated.
129 ;
130 cli ; interrupts off
131 xor ax, ax ; zero ax
132 mov ss, ax ; ss <- 0
133 mov sp, kBoot0Stack ; sp <- top of stack
134 sti ; reenable interrupts
135
136 mov es, ax ; es <- 0
137 mov ds, ax ; ds <- 0
138
139 ;
140 ; Relocate boot0 code.
141 ;
142 mov si, kBoot0LoadAddr ; si <- source
143 mov di, kBoot0RelocAddr ; di <- destination
144 ;
145 cld ; auto-increment SI and/or DI registers
146 mov cx, kSectorBytes/2 ; copy 256 words
147 repnz movsw ; repeat string move (word) operation
148
149 ; Code relocated, jump to start_reloc in relocated location.
150 ;
151 jmp 0:start_reloc
152
153;--------------------------------------------------------------------------
154; Start execution from the relocated location.
155;
156start_reloc:
157
158 DebugChar('>')
159
160.loop:
161
162%if DEBUG
163 mov al, dl
164 call print_hex
165%endif
166
167 ;
168 ; Clear various flags in memory.
169 ;
170 xor eax, eax
171 mov [ebios_lba], eax ; clear EBIOS LBA offset
172 mov [ebios_present], al ; clear EBIOS support flag
173
174 ;
175 ; Check if EBIOS is supported for this hard drive.
176 ;
177 mov ah, 0x41 ; Function 0x41
178 mov bx, 0x55AA ; check signature
179 int 0x13
180
181 ;
182 ; If successful, the return values are as follows:
183 ;
184 ; carry = 0
185 ; ah = major version of EBIOS extensions (0x21 = version 1.1)
186 ; al = altered
187 ; bx = 0xAA55
188 ; cx = support bits. bit 0 must be set for function 0x42.
189 ;
190 jc .ebios_check_done
191 cmp bx, 0xAA55 ; check BX = 0xAA55
192 jnz .ebios_check_done
193 test cl, 0x01 ; check enhanced drive read support
194 setnz [ebios_present] ; EBIOS supported, set flag
195 DebugChar('E') ; EBIOS supported
196.ebios_check_done:
197
198 ;
199 ; Since this code may not always reside in the MBR, always start by
200 ; loading the MBR to kMBRBuffer.
201 ;
202 mov al, 1 ; load one sector
203 xor bx, bx
204 mov es, bx ; MBR load segment = 0
205 mov bx, kMBRBuffer ; MBR load address
206 mov si, bx ; pointer to fake partition entry
207 mov WORD [si], 0x0000 ; CHS DX: head = 0
208 mov WORD [si + 2], 0x0001 ; CHS CX: cylinder = 0, sector = 1
209 mov DWORD [si + part.lba], 0x00000000 ; LBA sector 0
210
211 call load
212 jc .next_drive ; MBR load error
213
214 ;
215 ; Look for the booter partition in the MBR partition table,
216 ; which is at offset kMBRPartTable.
217 ;
218 mov di, kMBRPartTable ; pointer to partition table
219 mov ah, 0 ; initial nesting level is 0
220 call find_boot ; will not return on success
221
222.next_drive:
223 inc dl ; next drive number
224 test dl, 0x4 ; went through all 4 drives?
225 jz .loop ; not yet, loop again
226
227 mov si, boot_error_str
228 call print_string
229
230hang:
231 jmp SHORT hang
232
233;--------------------------------------------------------------------------
234; Find the active (boot) partition and load the booter from the partition.
235;
236; Arguments:
237; AH = recursion nesting level
238; DL = drive number (0x80 + unit number)
239; DI = pointer to fdisk partition table.
240;
241; Clobber list:
242; AX, BX, EBP
243;
244find_boot:
245 push cx ; preserve CX and SI
246 push si
247
248 ;
249 ; Check for boot block signature 0xAA55 following the 4 partition
250 ; entries.
251 ;
252 cmp WORD [di + part_size * kPartCount], kBootSignature
253 jne NEAR .exit ; boot signature not found
254
255 mov si, di ; make SI a pointer to partition table
256 mov cx, kPartCount ; number of partition entries per table
257
258.loop:
259 ;
260 ; First scan through the partition table looking for the active
261 ; partition. Postpone walking the extended partition chain for
262 ; the second pass. Do not merge the two without changing the
263 ; buffering scheme used to store extended partition tables.
264 ;
265%if DEBUG
266 mov al, ah ; indent based on nesting level
267 call print_spaces
268 mov al, [si + part.type] ; print partition type
269 call print_hex
270%endif
271
272 cmp BYTE [si + part.type], kPartTypeBoot
273 je .found
274 cmp BYTE [si + part.type], kPartTypeUFS
275 je .found
276 cmp BYTE [si + part.type], kPartTypeHFS
277 je .found
278
279 jmp .continue
280
281.found
282 DebugChar('*')
283
284 ;
285 ; Found boot partition, read boot sector to memory.
286 ;
287 mov al, 1
288 mov bx, kBoot0Segment
289 mov es, bx
290 mov bx, kBoot0LoadAddr
291 call load
292 jc .continue ; load error, keep looking?
293
294 ;
295 ; Check signature
296 ;
297 cmp WORD [bx + 510], kBootSignature
298 jne NEAR .exit ; boot signature not found
299
300 DebugChar('&')
301
302 ;
303 ; Fix up absolute block location in partition record.
304 ;
305 mov eax, [si + part.lba]
306 add eax, [ebios_lba]
307 mov [si + part.lba], eax
308
309 DebugChar('%')
310
311 ;
312 ; Jump to partition booter. The drive number is already in register DL.
313 ; SI is pointing to the modified partition entry.
314 ;
315 jmp kBoot0Segment:kBoot0LoadAddr
316
317.continue:
318 add si, part_size ; advance SI to next partition entry
319 loop .loop ; loop through all partition entries
320
321%if EXT_PART_SUPPORT
322 ;
323 ; No primary (or logical) boot partition found in the current
324 ; partition table. Restart and look for extended partitions.
325 ;
326 mov si, di ; make SI a pointer to partition table
327 mov cx, kPartCount ; number of partition entries per table
328
329.ext_loop:
330
331 mov al, [si + part.type] ; AL <- partition type
332
333 cmp al, kPartTypeExtDOS ; Extended DOS
334 je .ext_load
335
336 cmp al, kPartTypeExtWin ; Extended Windows(95)
337 je .ext_load
338
339 cmp al, kPartTypeExtLinux ; Extended Linux
340 je .ext_load
341
342.ext_continue:
343 ;
344 ; Advance si to the next partition entry in the extended
345 ; partition table.
346 ;
347 add si, part_size ; advance SI to next partition entry
348 loop .ext_loop ; loop through all partition entries
349 jmp .exit ; boot partition not found
350
351.ext_load:
352 ;
353 ; Setup the arguments for the load function call to bring the
354 ; extended partition table into memory.
355 ; Remember that SI points to the extended partition entry.
356 ;
357 mov al, 1 ; read 1 sector
358 xor bx, bx
359 mov es, bx ; es = 0
360 mov bx, kExtBuffer ; load extended boot sector
361 call load
362 jc .ext_continue ; load error
363
364 ;
365 ; The LBA address of all extended partitions is relative based
366 ; on the LBA address of the extended partition in the MBR, or
367 ; the extended partition at the head of the chain. Thus it is
368 ; necessary to save the LBA address of the first extended partition.
369 ;
370 or ah, ah
371 jnz .ext_find_boot
372 mov ebp, [si + part.lba]
373 mov [ebios_lba], ebp
374
375.ext_find_boot:
376 ;
377 ; Call find_boot recursively to scan through the extended partition
378 ; table. Load DI with a pointer to the extended table in memory.
379 ;
380 inc ah ; increment recursion level
381 mov di, kExtPartTable ; partition table pointer
382 call find_boot ; recursion...
383 ;dec ah
384
385 ;
386 ; Since there is an "unwritten" rule that limits each partition table
387 ; to have 0 or 1 extended partitions, there is no point in looking for
388 ; any additional extended partition entries at this point. There is no
389 ; boot partition linked beyond the extended partition that was loaded
390 ; above.
391 ;
392
393%endif ; EXT_PART_SUPPORT
394
395.exit:
396 ;
397 ; Boot partition not found. Giving up.
398 ;
399 DebugChar('X')
400 pop si
401 pop cx
402 ret
403
404;--------------------------------------------------------------------------
405; load - Load one or more sectors from a partition.
406;
407; Arguments:
408; AL = number of 512-byte sectors to read.
409; ES:BX = pointer to where the sectors should be stored.
410; DL = drive number (0x80 + unit number)
411; SI = pointer to the partition entry.
412;
413; Returns:
414; CF = 0 success
415; 1 error
416;
417load:
418 push cx
419 test BYTE [ebios_present], 1
420 jz .chs
421
422.ebios:
423 mov cx, 5 ; load retry count
424.ebios_loop:
425 call read_lba ; use INT13/F42
426 jnc .exit
427 loop .ebios_loop
428.chs:
429 mov cx, 5 ; load retry count
430.chs_loop:
431 call read_chs ; use INT13/F2
432 jnc .exit
433 loop .chs_loop
434
435.exit
436 DebugChar('R')
437 pop cx
438 ret
439
440;--------------------------------------------------------------------------
441; read_chs - Read sectors from a partition using CHS addressing.
442;
443; Arguments:
444; AL = number of 512-byte sectors to read.
445; ES:BX = pointer to where the sectors should be stored.
446; DL = drive number (0x80 + unit number)
447; SI = pointer to the partition entry.
448;
449; Returns:
450; CF = 0 success
451; 1 error
452;
453read_chs:
454 pusha ; save all registers
455
456 ;
457 ; Read the CHS start values from the partition entry.
458 ;
459 mov dh, [ si + part.head ] ; drive head
460 mov cx, [ si + part.sect ] ; drive sector + cylinder
461
462 ;
463 ; INT13 Func 2 - Read Disk Sectors
464 ;
465 ; Arguments:
466 ; AH = 2
467 ; AL = number of sectors to read
468 ; CH = lowest 8 bits of the 10-bit cylinder number
469 ; CL = bits 6 & 7: cylinder number bits 8 and 9
470 ; bits 0 - 5: starting sector number (1-63)
471 ; DH = starting head number (0 to 255)
472 ; DL = drive number (80h + drive unit)
473 ; es:bx = pointer where to place sectors read from disk
474 ;
475 ; Returns:
476 ; AH = return status (sucess is 0)
477 ; AL = burst error length if ah=0x11 (ECC corrected)
478 ; carry = 0 success
479 ; 1 error
480 ;
481 mov ah, 0x02 ; Func 2
482 int 0x13 ; INT 13
483 jnc .exit
484
485 DebugChar('r') ; indicate INT13/F2 error
486
487 ;
488 ; Issue a disk reset on error.
489 ; Should this be changed to Func 0xD to skip the diskette controller
490 ; reset?
491 ;
492 xor ax, ax ; Func 0
493 int 0x13 ; INT 13
494 stc ; set carry to indicate error
495
496.exit:
497 popa
498 ret
499
500;--------------------------------------------------------------------------
501; read_lba - Read sectors from a partition using LBA addressing.
502;
503; Arguments:
504; AL = number of 512-byte sectors to read (valid from 1-127).
505; ES:BX = pointer to where the sectors should be stored.
506; DL = drive number (0x80 + unit number)
507; SI = pointer to the partition entry.
508;
509; Returns:
510; CF = 0 success
511; 1 error
512;
513read_lba:
514 pusha ; save all registers
515 mov bp, sp ; save current SP
516
517 ;
518 ; Create the Disk Address Packet structure for the
519 ; INT13/F42 (Extended Read Sectors) on the stack.
520 ;
521
522 ; push DWORD 0 ; offset 12, upper 32-bit LBA
523 push ds ; For sake of saving memory,
524 push ds ; push DS register, which is 0.
525
526 mov ecx, [ebios_lba] ; offset 8, lower 32-bit LBA
527 add ecx, [si + part.lba]
528 push ecx
529
530 push es ; offset 6, memory segment
531
532 push bx ; offset 4, memory offset
533
534 xor ah, ah ; offset 3, must be 0
535 push ax ; offset 2, number of sectors
536
537 push WORD 16 ; offset 0-1, packet size
538
539 ;
540 ; INT13 Func 42 - Extended Read Sectors
541 ;
542 ; Arguments:
543 ; AH = 0x42
544 ; DL = drive number (80h + drive unit)
545 ; DS:SI = pointer to Disk Address Packet
546 ;
547 ; Returns:
548 ; AH = return status (sucess is 0)
549 ; carry = 0 success
550 ; 1 error
551 ;
552 ; Packet offset 2 indicates the number of sectors read
553 ; successfully.
554 ;
555 mov si, sp
556 mov ah, 0x42
557 int 0x13
558
559 jnc .exit
560
561 DebugChar('R') ; indicate INT13/F42 error
562
563 ;
564 ; Issue a disk reset on error.
565 ; Should this be changed to Func 0xD to skip the diskette controller
566 ; reset?
567 ;
568 xor ax, ax ; Func 0
569 int 0x13 ; INT 13
570 stc ; set carry to indicate error
571
572.exit:
573 mov sp, bp ; restore SP
574 popa
575 ret
576
577;--------------------------------------------------------------------------
578; Write a string to the console.
579;
580; Arguments:
581; DS:SI pointer to a NULL terminated string.
582;
583; Clobber list:
584; AX, BX, SI
585;
586print_string
587 mov bx, 1 ; BH=0, BL=1 (blue)
588 cld ; increment SI after each lodsb call
589.loop
590 lodsb ; load a byte from DS:SI into AL
591 cmp al, 0 ; Is it a NULL?
592 je .exit ; yes, all done
593 mov ah, 0xE ; INT10 Func 0xE
594 int 0x10 ; display byte in tty mode
595 jmp short .loop
596.exit
597 ret
598
599
600%if DEBUG
601
602;--------------------------------------------------------------------------
603; Write a ASCII character to the console.
604;
605; Arguments:
606; AL = ASCII character.
607;
608print_char
609 pusha
610 mov bx, 1 ; BH=0, BL=1 (blue)
611 mov ah, 0x0e ; bios INT 10, Function 0xE
612 int 0x10 ; display byte in tty mode
613 popa
614 ret
615
616%if DEBUG
617;--------------------------------------------------------------------------
618; Write a variable number of spaces to the console.
619;
620; Arguments:
621; AL = number to spaces.
622;
623print_spaces:
624 pusha
625 xor cx, cx
626 mov cl, al ; use CX as the loop counter
627 mov al, ' ' ; character to print
628.loop:
629 jcxz .exit
630 call print_char
631 loop .loop
632.exit:
633 popa
634 ret
635%endif
636
637;--------------------------------------------------------------------------
638; Write the byte value to the console in hex.
639;
640; Arguments:
641; AL = Value to be displayed in hex.
642;
643print_hex:
644 push ax
645 ror al, 4
646 call print_nibble ; display upper nibble
647 pop ax
648 call print_nibble ; display lower nibble
649
650 mov al, 10 ; carriage return
651 call print_char
652 mov al, 13
653 call print_char
654 ret
655
656print_nibble:
657 and al, 0x0f
658 add al, '0'
659 cmp al, '9'
660 jna .print_ascii
661 add al, 'A' - '9' - 1
662.print_ascii:
663 call print_char
664 ret
665
666getc:
667 pusha
668 mov ah, 0
669 int 0x16
670 popa
671 ret
672
673%endif ; DEBUG
674
675;--------------------------------------------------------------------------
676; NULL terminated strings.
677;
678boot_error_str db 10, 13, 'Chain booting error', 0
679
680;--------------------------------------------------------------------------
681; Pad the rest of the 512 byte sized booter with zeroes. The last
682; two bytes is the mandatory boot sector signature.
683;
684; If the booter code becomes too large, then nasm will complain
685; that the 'times' argument is negative.
686
687;
688; In memory variables.
689;
690ebios_lba dd 0 ; starting LBA of the intial extended partition.
691ebios_present db 0 ; 1 if EBIOS is supported, 0 otherwise.
692
693pad_boot
694 times 446-($-$$) db 0
695
696pad_table_and_sig
697 times 510-($-$$) db 0
698 dw kBootSignature
699
700 END