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