]> git.saurik.com Git - apple/boot.git/blob - i386/boot1u/boot1u0.s
454a546341bf9bd09b0826747eb54ff179348f90
[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 ; Portions Copyright (c) 1999-2002 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.2 (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 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 ;
30 ; This boot loader can be placed at any of the following places:
31 ; 1. Master Boot Record (MBR)
32 ; 2. Boot sector of an extended partition
33 ; 3. Boot sector of a primary partition
34 ; 4. Boot sector of a logical partition
35 ;
36 ; In order to coexist with a fdisk partition table (64 bytes), and
37 ; leave room for a two byte signature (0xAA55) in the end, boot0 is
38 ; restricted to 446 bytes (512 - 64 - 2). If boot0 did not have to
39 ; live in the MBR, then we would have 510 bytes to work with.
40 ;
41 ; boot0 is always loaded by the BIOS or another booter to 0:7C00h.
42 ;
43 ; This code is written for the NASM assembler.
44 ; nasm boot0.s -o boot0
45
46
47 ;
48 ; Set to 1 to enable obscure debug messages.
49 ;
50 DEBUG EQU 0
51
52 ;
53 ; Set to 1 to support loading the booter (boot2) from a
54 ; logical partition.
55 ;
56 EXT_PART_SUPPORT EQU 1
57
58 ;
59 ; Various constants.
60 ;
61 kBoot0Segment EQU 0x0000
62 kBoot0Stack EQU 0xFFF0 ; boot0 stack pointer
63 kBoot0LoadAddr EQU 0x7C00 ; boot0 load address
64 kBoot0RelocAddr EQU 0xE000 ; boot0 relocated address
65
66 kMBRBuffer EQU 0x1000 ; MBR buffer address
67 kExtBuffer EQU 0x1200 ; EXT boot block buffer address
68
69 kPartTableOffset EQU 0x1be
70 kMBRPartTable EQU kMBRBuffer + kPartTableOffset
71 kExtPartTable EQU kExtBuffer + kPartTableOffset
72
73 kBoot1uSectors EQU 16 ; sectors to load for boot2
74 kBoot1uAddress EQU 0x0000 ; boot2 load address
75 kBoot1uSegment EQU 0x1000 ; boot2 load segment
76
77 kSectorBytes EQU 512 ; sector size in bytes
78 kBootSignature EQU 0xAA55 ; boot sector signature
79
80 kPartCount EQU 4 ; number of paritions per table
81 kPartTypeBoot EQU 0xab ; boot2 partition type
82 kPartTypeUFS EQU 0xa8 ;
83 kPartTypeExtDOS EQU 0x05 ; DOS extended partition type
84 kPartTypeExtWin EQU 0x0f ; Windows extended partition type
85 kPartTypeExtLinux EQU 0x85 ; Linux extended partition type
86 kPartActive EQU 0x80
87
88 %ifdef FLOPPY
89 kDriveNumber EQU 0x00
90 %else
91 kDriveNumber EQU 0x80
92 %endif
93
94 ;
95 ; In memory variables.
96 ;
97 ebios_lba dd 0 ; starting LBA of the intial extended partition.
98 ebios_present db 0 ; 1 if EBIOS is supported, 0 otherwise.
99
100 ;
101 ; Format of fdisk partition entry.
102 ;
103 ; The symbol 'part_size' is automatically defined as an `EQU'
104 ; giving the size of the structure.
105 ;
106 struc part
107 .bootid: resb 1 ; bootable or not
108 .head: resb 1 ; starting head, sector, cylinder
109 .sect: resb 1 ;
110 .cyl: resb 1 ;
111 .type: resb 1 ; partition type
112 .endhead resb 1 ; ending head, sector, cylinder
113 .endsect: resb 1 ;
114 .endcyl: resb 1 ;
115 .lba: resd 1 ; starting lba
116 .sectors resd 1 ; size in sectors
117 endstruc
118
119 ;
120 ; Macros.
121 ;
122 %macro DebugCharMacro 1
123 mov al, %1
124 call print_char
125 %endmacro
126
127 %if DEBUG
128 %define DebugChar(x) DebugCharMacro x
129 %else
130 %define DebugChar(x)
131 %endif
132
133 ;--------------------------------------------------------------------------
134 ; Start of text segment.
135
136 SEGMENT .text
137
138 ORG 0xE000 ; must match kBoot0RelocAddr
139
140 ;--------------------------------------------------------------------------
141 ; Boot code is loaded at 0:7C00h.
142 ;
143 start
144 ;
145 ; Set up the stack to grow down from kBoot0Segment:kBoot0Stack.
146 ; Interrupts should be off while the stack is being manipulated.
147 ;
148 cli ; interrupts off
149 xor ax, ax ; zero ax
150 mov ss, ax ; ss <- 0
151 mov sp, kBoot0Stack ; sp <- top of stack
152 sti ; reenable interrupts
153
154 mov es, ax ; es <- 0
155 mov ds, ax ; ds <- 0
156
157 ;
158 ; Relocate boot0 code.
159 ;
160 mov si, kBoot0LoadAddr ; si <- source
161 mov di, kBoot0RelocAddr ; di <- destination
162 ;
163 cld ; auto-increment SI and/or DI registers
164 mov cx, kSectorBytes/2 ; copy 256 words
165 repnz movsw ; repeat string move (word) operation
166
167 ; Code relocated, jump to start_reloc in relocated location.
168 ;
169 jmp 0:start_reloc
170
171 ;--------------------------------------------------------------------------
172 ; Start execution from the relocated location.
173 ;
174 start_reloc:
175
176 DebugChar('*')
177
178 mov dl, kDriveNumber ; starting BIOS drive number
179
180 .loop:
181
182 %if DEBUG
183 mov al, dl
184 call print_hex
185 %endif
186
187 ;
188 ; Clear various flags in memory.
189 ;
190 xor eax, eax
191 mov [ebios_lba], eax ; clear EBIOS LBA offset
192 mov [ebios_present], al ; clear EBIOS support flag
193
194 ;
195 ; Since this code may not always reside in the MBR, always start by
196 ; loading the MBR to kMBRBuffer.
197 ;
198 mov al, 1 ; load one sector
199 xor bx, bx
200 mov es, bx ; MBR load segment = 0
201 mov bx, kMBRBuffer ; MBR load address
202 mov si, bx ; pointer to fake partition entry
203 mov WORD [si], 0x0000 ; CHS DX: head = 0
204 mov WORD [si + 2], 0x0001 ; CHS CX: cylinder = 0, sector = 1
205
206 call load
207 jc .next_drive ; MBR load error
208
209 ;
210 ; Check if EBIOS is supported for this hard drive.
211 ;
212 mov ah, 0x41 ; Function 0x41
213 mov bx, 0x55AA ; check signature
214 ; mov dl, kDriveNumber ; Drive number
215 int 0x13
216
217 ;
218 ; If successful, the return values are as follows:
219 ;
220 ; carry = 0
221 ; ah = major version of EBIOS extensions (0x21 = version 1.1)
222 ; al = altered
223 ; bx = 0xAA55
224 ; cx = support bits. bit 0 must be set for function 0x42.
225 ;
226 jc .ebios_check_done
227 cmp bx, 0xAA55 ; check BX = 0xAA55
228 jnz .ebios_check_done
229 test cl, 0x01 ; check enhanced drive read support
230 setnz [ebios_present] ; EBIOS supported, set flag
231 DebugChar('E') ; EBIOS supported
232 .ebios_check_done:
233
234 ;
235 ; Look for the booter partition in the MBR partition table,
236 ; which is at offset kMBRPartTable.
237 ;
238 mov di, kMBRPartTable ; pointer to partition table
239 mov ah, 0 ; initial nesting level is 0
240 call find_boot ; will not return on success
241
242 .next_drive:
243 ;; inc dl ; next drive number
244 ;; test dl, 0x84 ; went through all 4 drives?
245 ;; jz .loop ; not yet, loop again
246
247 mov si, boot_error_str
248 call print_string
249
250 hang:
251 hlt
252 jmp SHORT hang
253
254 ;--------------------------------------------------------------------------
255 ; Find the boot partition and load the booter from the partition.
256 ;
257 ; Arguments:
258 ; AH = recursion nesting level
259 ; DL = drive number (0x80 + unit number)
260 ; DI = pointer to fdisk partition table.
261 ;
262 ; Clobber list:
263 ; AX, BX, EBP
264 ;
265 find_boot:
266 push cx ; preserve CX and SI
267 push si
268
269 ;
270 ; Check for boot block signature 0xAA55 following the 4 partition
271 ; entries.
272 ;
273 cmp WORD [di + part_size * kPartCount], kBootSignature
274 jne .exit ; boot signature not found
275
276 mov si, di ; make SI a pointer to partition table
277 mov cx, kPartCount ; number of partition entries per table
278
279 .loop:
280 ;
281 ; First scan through the partition table looking for the boot
282 ; partition. Postpone walking the extended partition chain for
283 ; the second pass. Do not merge the two without changing the
284 ; buffering scheme used to store extended partition tables.
285 ;
286 %if DEBUG
287 mov al, ah ; indent based on nesting level
288 call print_spaces
289 mov al, [si + part.type] ; print partition type
290 call print_hex
291 %endif
292
293 cmp BYTE [si + part.type], kPartTypeUFS
294 jne .continue
295 cmp BYTE [si + part.bootid], kPartActive
296 jne .continue
297
298 ;
299 ; Found boot partition, read boot1u image to memory.
300 ;
301 mov al, kBoot1uSectors
302 mov bx, kBoot1uSegment
303 mov es, bx
304 mov bx, kBoot1uAddress
305 call load ;
306 jc .continue ; load error, keep looking?
307
308 DebugChar('^')
309 ;
310 ; Jump to boot1u. The drive number is already in register DL.
311 ;
312 ; The first sector loaded from the disk is reserved for the boot
313 ; block (boot1), adjust the jump location by adding a sector offset.
314 ;
315 jmp kBoot1uSegment:kBoot1uAddress + kSectorBytes
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 pop si
400 pop cx
401 ret
402
403 ;--------------------------------------------------------------------------
404 ; load - Load one or more sectors from a partition.
405 ;
406 ; Arguments:
407 ; AL = number of 512-byte sectors to read.
408 ; ES:BX = pointer to where the sectors should be stored.
409 ; DL = drive number (0x80 + unit number)
410 ; SI = pointer to the partition entry.
411 ;
412 ; Returns:
413 ; CF = 0 success
414 ; 1 error
415 ;
416 load:
417 push cx
418 test BYTE [ebios_present], 1
419 jz .chs
420
421 .ebios:
422 mov cx, 5 ; load retry count
423 .ebios_loop:
424 call read_lba ; use INT13/F42
425 jnc .exit
426 loop .ebios_loop
427
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 pop cx
437 ret
438
439 ;--------------------------------------------------------------------------
440 ; read_chs - Read sectors from a partition using CHS addressing.
441 ;
442 ; Arguments:
443 ; AL = number of 512-byte sectors to read.
444 ; ES:BX = pointer to where the sectors should be stored.
445 ; DL = drive number (0x80 + unit number)
446 ; SI = pointer to the partition entry.
447 ;
448 ; Returns:
449 ; CF = 0 success
450 ; 1 error
451 ;
452 read_chs:
453 pusha ; save all registers
454
455 ;
456 ; Read the CHS start values from the partition entry.
457 ;
458 mov dh, [ si + part.head ] ; drive head
459 mov cx, [ si + part.sect ] ; drive sector + cylinder
460
461 ;
462 ; INT13 Func 2 - Read Disk Sectors
463 ;
464 ; Arguments:
465 ; AH = 2
466 ; AL = number of sectors to read
467 ; CH = lowest 8 bits of the 10-bit cylinder number
468 ; CL = bits 6 & 7: cylinder number bits 8 and 9
469 ; bits 0 - 5: starting sector number (1-63)
470 ; DH = starting head number (0 to 255)
471 ; DL = drive number (80h + drive unit)
472 ; es:bx = pointer where to place sectors read from disk
473 ;
474 ; Returns:
475 ; AH = return status (sucess is 0)
476 ; AL = burst error length if ah=0x11 (ECC corrected)
477 ; carry = 0 success
478 ; 1 error
479 ;
480 ; mov dl, kDriveNumber
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 ;
513 read_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 dl, kDriveNumber
556 mov si, sp
557 mov ah, 0x42
558 int 0x13
559
560 jnc .exit
561
562 DebugChar('R') ; indicate INT13/F42 error
563
564 ;
565 ; Issue a disk reset on error.
566 ; Should this be changed to Func 0xD to skip the diskette controller
567 ; reset?
568 ;
569 xor ax, ax ; Func 0
570 int 0x13 ; INT 13
571 stc ; set carry to indicate error
572
573 .exit:
574 mov sp, bp ; restore SP
575 popa
576 ret
577
578 ;--------------------------------------------------------------------------
579 ; Write a string to the console.
580 ;
581 ; Arguments:
582 ; DS:SI pointer to a NULL terminated string.
583 ;
584 ; Clobber list:
585 ; AX, BX, SI
586 ;
587 print_string
588 mov bx, 1 ; BH=0, BL=1 (blue)
589 cld ; increment SI after each lodsb call
590 .loop
591 lodsb ; load a byte from DS:SI into AL
592 cmp al, 0 ; Is it a NULL?
593 je .exit ; yes, all done
594 mov ah, 0xE ; INT10 Func 0xE
595 int 0x10 ; display byte in tty mode
596 jmp short .loop
597 .exit
598 ret
599
600 %if DEBUG
601
602 ;--------------------------------------------------------------------------
603 ; Write a ASCII character to the console.
604 ;
605 ; Arguments:
606 ; AL = ASCII character.
607 ;
608 print_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 ;--------------------------------------------------------------------------
617 ; Write a variable number of spaces to the console.
618 ;
619 ; Arguments:
620 ; AL = number to spaces.
621 ;
622 print_spaces:
623 pusha
624 xor cx, cx
625 mov cl, al ; use CX as the loop counter
626 mov al, ' ' ; character to print
627 .loop:
628 jcxz .exit
629 call print_char
630 loop .loop
631 .exit:
632 popa
633 ret
634
635 ;--------------------------------------------------------------------------
636 ; Write the byte value to the console in hex.
637 ;
638 ; Arguments:
639 ; AL = Value to be displayed in hex.
640 ;
641 print_hex:
642 push ax
643 ror al, 4
644 call print_nibble ; display upper nibble
645 pop ax
646 call print_nibble ; display lower nibble
647
648 mov al, 10 ; carriage return
649 call print_char
650 mov al, 13
651 call print_char
652 ret
653
654 print_nibble:
655 and al, 0x0f
656 add al, '0'
657 cmp al, '9'
658 jna .print_ascii
659 add al, 'A' - '9' - 1
660 .print_ascii:
661 call print_char
662 ret
663
664 %endif ; DEBUG
665
666 ;--------------------------------------------------------------------------
667 ; NULL terminated strings.
668 ;
669 boot_error_str db 10, 13, 'Error', 0
670
671 ;--------------------------------------------------------------------------
672 ; Pad the rest of the 512 byte sized booter with zeroes. The last
673 ; two bytes is the mandatory boot sector signature.
674 ;
675 ; If the booter code becomes too large, then nasm will complain
676 ; that the 'times' argument is negative.
677
678 ;;pad_boot
679 ;; times 446-($-$$) db 0
680
681 %ifdef FLOPPY
682 ;--------------------------------------------------------------------------
683 ; Put fake partition entries for the bootable floppy image
684 ;
685 part1bootid db 0x80 ; first partition active
686 part1head db 0x00 ; head #
687 part1sect db 0x02 ; sector # (low 6 bits)
688 part1cyl db 0x00 ; cylinder # (+ high 2 bits of above)
689 part1systid db 0xab ; Apple boot partition
690 times 3 db 0x00 ; ignore head/cyl/sect #'s
691 part1relsect dd 0x00000001 ; start at sector 1
692 part1numsect dd 0x00000080 ; 64K for booter
693 part2bootid db 0x00 ; not active
694 times 3 db 0x00 ; ignore head/cyl/sect #'s
695 part2systid db 0xa8 ; Apple UFS partition
696 times 3 db 0x00 ; ignore head/cyl/sect #'s
697 part2relsect dd 0x00000082 ; start after booter
698 ; part2numsect dd 0x00000abe ; 1.44MB - 65K
699 part2numsect dd 0x000015fe ; 2.88MB - 65K
700 %endif
701
702 pad_table_and_sig
703 times 510-($-$$) db 0
704 dw kBootSignature
705
706 END