]>
Commit | Line | Data |
---|---|---|
57c72a9a | 1 | ; Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved. |
f083c6c3 A |
2 | ; |
3 | ; @APPLE_LICENSE_HEADER_START@ | |
4 | ; | |
57c72a9a | 5 | ; Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights |
4f6e3300 A |
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 | |
57c72a9a | 8 | ; Source License Version 2.0 (the "License"). You may not use this file |
4f6e3300 A |
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. | |
f083c6c3 A |
12 | ; |
13 | ; The Original Code and all software distributed under the License are | |
4f6e3300 | 14 | ; distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
f083c6c3 A |
15 | ; EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
16 | ; INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
4f6e3300 A |
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. | |
f083c6c3 A |
20 | ; |
21 | ; @APPLE_LICENSE_HEADER_END@ | |
22 | ; | |
23 | ; Boot Loader: boot0 | |
24 | ; | |
25 | ; A small boot sector program written in x86 assembly whose only | |
57c72a9a A |
26 | ; responsibility is to load the booter into memory |
27 | ; and jump to the booter's entry point. | |
f083c6c3 A |
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: | |
57c72a9a A |
31 | ; 1. Boot sector of an extended partition |
32 | ; 2. Boot sector of a primary partition | |
33 | ; 3. Boot sector of a logical partition | |
34 | ; | |
35 | ; It expects that the MBR has left the drive number in DL | |
36 | ; and a pointer to the partition entry in SI. | |
f083c6c3 A |
37 | ; |
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 work with. | |
42 | ; | |
43 | ; boot0 is always loaded by the BIOS or another booter to 0:7C00h. | |
44 | ; | |
45 | ; This code is written for the NASM assembler. | |
46 | ; nasm boot0.s -o boot0 | |
47 | ||
48 | ||
49 | ; | |
50 | ; Set to 1 to enable obscure debug messages. | |
51 | ; | |
52 | DEBUG EQU 0 | |
53 | ||
54 | ; | |
55 | ; Set to 1 to support loading the booter (boot2) from a | |
56 | ; logical partition. | |
57 | ; | |
58 | EXT_PART_SUPPORT EQU 1 | |
59 | ||
60 | ; | |
61 | ; Various constants. | |
62 | ; | |
63 | kBoot0Segment EQU 0x0000 | |
64 | kBoot0Stack EQU 0xFFF0 ; boot0 stack pointer | |
65 | kBoot0LoadAddr EQU 0x7C00 ; boot0 load address | |
66 | kBoot0RelocAddr EQU 0xE000 ; boot0 relocated address | |
67 | ||
68 | kMBRBuffer EQU 0x1000 ; MBR buffer address | |
69 | kExtBuffer EQU 0x1200 ; EXT boot block buffer address | |
70 | ||
71 | kPartTableOffset EQU 0x1be | |
72 | kMBRPartTable EQU kMBRBuffer + kPartTableOffset | |
73 | kExtPartTable EQU kExtBuffer + kPartTableOffset | |
74 | ||
75 | kBoot1uSectors EQU 16 ; sectors to load for boot2 | |
76 | kBoot1uAddress EQU 0x0000 ; boot2 load address | |
77 | kBoot1uSegment EQU 0x1000 ; boot2 load segment | |
78 | ||
79 | kSectorBytes EQU 512 ; sector size in bytes | |
80 | kBootSignature EQU 0xAA55 ; boot sector signature | |
81 | ||
82 | kPartCount EQU 4 ; number of paritions per table | |
83 | kPartTypeBoot EQU 0xab ; boot2 partition type | |
84 | kPartTypeUFS EQU 0xa8 ; | |
85 | kPartTypeExtDOS EQU 0x05 ; DOS extended partition type | |
86 | kPartTypeExtWin EQU 0x0f ; Windows extended partition type | |
87 | kPartTypeExtLinux EQU 0x85 ; Linux extended partition type | |
88 | kPartActive EQU 0x80 | |
89 | ||
90 | %ifdef FLOPPY | |
91 | kDriveNumber EQU 0x00 | |
92 | %else | |
93 | kDriveNumber EQU 0x80 | |
94 | %endif | |
95 | ||
f083c6c3 A |
96 | ; |
97 | ; Format of fdisk partition entry. | |
98 | ; | |
99 | ; The symbol 'part_size' is automatically defined as an `EQU' | |
100 | ; giving the size of the structure. | |
101 | ; | |
102 | struc part | |
103 | .bootid: resb 1 ; bootable or not | |
104 | .head: resb 1 ; starting head, sector, cylinder | |
105 | .sect: resb 1 ; | |
106 | .cyl: resb 1 ; | |
107 | .type: resb 1 ; partition type | |
108 | .endhead resb 1 ; ending head, sector, cylinder | |
109 | .endsect: resb 1 ; | |
110 | .endcyl: resb 1 ; | |
111 | .lba: resd 1 ; starting lba | |
112 | .sectors resd 1 ; size in sectors | |
113 | endstruc | |
114 | ||
115 | ; | |
116 | ; Macros. | |
117 | ; | |
118 | %macro DebugCharMacro 1 | |
119 | mov al, %1 | |
120 | call print_char | |
121 | %endmacro | |
122 | ||
123 | %if DEBUG | |
124 | %define DebugChar(x) DebugCharMacro x | |
125 | %else | |
126 | %define DebugChar(x) | |
127 | %endif | |
128 | ||
129 | ;-------------------------------------------------------------------------- | |
130 | ; Start of text segment. | |
131 | ||
132 | SEGMENT .text | |
133 | ||
57c72a9a | 134 | ORG 0x7C00 |
f083c6c3 A |
135 | |
136 | ;-------------------------------------------------------------------------- | |
137 | ; Boot code is loaded at 0:7C00h. | |
138 | ; | |
139 | start | |
57c72a9a A |
140 | DebugChar('!') |
141 | %if DEBUG | |
142 | mov al, dl | |
143 | call print_hex | |
144 | %endif | |
f083c6c3 A |
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 | ||
57c72a9a A |
158 | %if 0 |
159 | ; | |
160 | ; Save SI register. | |
161 | ; | |
162 | mov bx, si | |
163 | ||
f083c6c3 | 164 | ; |
57c72a9a | 165 | ; Relocate ourselves. |
f083c6c3 A |
166 | ; |
167 | mov si, kBoot0LoadAddr ; si <- source | |
168 | mov di, kBoot0RelocAddr ; di <- destination | |
169 | ; | |
170 | cld ; auto-increment SI and/or DI registers | |
171 | mov cx, kSectorBytes/2 ; copy 256 words | |
172 | repnz movsw ; repeat string move (word) operation | |
173 | ||
174 | ; Code relocated, jump to start_reloc in relocated location. | |
175 | ; | |
176 | jmp 0:start_reloc | |
177 | ||
178 | ;-------------------------------------------------------------------------- | |
179 | ; Start execution from the relocated location. | |
180 | ; | |
181 | start_reloc: | |
57c72a9a A |
182 | %endif |
183 | ||
f083c6c3 A |
184 | DebugChar('*') |
185 | ||
f083c6c3 A |
186 | .loop: |
187 | ||
188 | %if DEBUG | |
189 | mov al, dl | |
190 | call print_hex | |
191 | %endif | |
192 | ||
193 | ; | |
194 | ; Clear various flags in memory. | |
195 | ; | |
196 | xor eax, eax | |
197 | mov [ebios_lba], eax ; clear EBIOS LBA offset | |
198 | mov [ebios_present], al ; clear EBIOS support flag | |
199 | ||
f083c6c3 A |
200 | ; |
201 | ; Check if EBIOS is supported for this hard drive. | |
202 | ; | |
203 | mov ah, 0x41 ; Function 0x41 | |
204 | mov bx, 0x55AA ; check signature | |
205 | ; mov dl, kDriveNumber ; Drive number | |
206 | int 0x13 | |
207 | ||
208 | ; | |
209 | ; If successful, the return values are as follows: | |
210 | ; | |
211 | ; carry = 0 | |
212 | ; ah = major version of EBIOS extensions (0x21 = version 1.1) | |
213 | ; al = altered | |
214 | ; bx = 0xAA55 | |
215 | ; cx = support bits. bit 0 must be set for function 0x42. | |
216 | ; | |
217 | jc .ebios_check_done | |
218 | cmp bx, 0xAA55 ; check BX = 0xAA55 | |
219 | jnz .ebios_check_done | |
220 | test cl, 0x01 ; check enhanced drive read support | |
221 | setnz [ebios_present] ; EBIOS supported, set flag | |
222 | DebugChar('E') ; EBIOS supported | |
223 | .ebios_check_done: | |
224 | ||
57c72a9a | 225 | DebugChar('L') |
f083c6c3 | 226 | |
57c72a9a A |
227 | %if DEBUG |
228 | mov al, BYTE [si + part.type] | |
229 | call print_hex | |
230 | %endif | |
231 | ||
232 | ; Check to make sure our partition is the correct type. | |
233 | cmp BYTE [si + part.type], kPartTypeUFS | |
234 | je load_boot | |
f083c6c3 | 235 | |
57c72a9a A |
236 | part_error: |
237 | ; Drat, the partition entry was the wrong type. | |
238 | ; Print an error and hang. | |
239 | mov si, part_error_str | |
f083c6c3 A |
240 | call print_string |
241 | ||
242 | hang: | |
243 | hlt | |
244 | jmp SHORT hang | |
245 | ||
57c72a9a A |
246 | ;-------------------------------------------------------------------------- |
247 | ; Load the booter from the partition. | |
f083c6c3 | 248 | |
57c72a9a | 249 | load_boot: |
f083c6c3 A |
250 | |
251 | ; | |
252 | ; Found boot partition, read boot1u image to memory. | |
253 | ; | |
254 | mov al, kBoot1uSectors | |
255 | mov bx, kBoot1uSegment | |
256 | mov es, bx | |
257 | mov bx, kBoot1uAddress | |
258 | call load ; | |
57c72a9a A |
259 | jc part_error ; load error, keep looking? |
260 | ||
261 | DebugChar('^') | |
f083c6c3 | 262 | |
f083c6c3 A |
263 | ; |
264 | ; Jump to boot1u. The drive number is already in register DL. | |
265 | ; | |
266 | ; The first sector loaded from the disk is reserved for the boot | |
267 | ; block (boot1), adjust the jump location by adding a sector offset. | |
268 | ; | |
269 | jmp kBoot1uSegment:kBoot1uAddress + kSectorBytes | |
270 | ||
f083c6c3 A |
271 | .exit: |
272 | ; | |
273 | ; Boot partition not found. Giving up. | |
274 | ; | |
275 | pop si | |
276 | pop cx | |
277 | ret | |
278 | ||
279 | ;-------------------------------------------------------------------------- | |
280 | ; load - Load one or more sectors from a partition. | |
281 | ; | |
282 | ; Arguments: | |
283 | ; AL = number of 512-byte sectors to read. | |
284 | ; ES:BX = pointer to where the sectors should be stored. | |
285 | ; DL = drive number (0x80 + unit number) | |
286 | ; SI = pointer to the partition entry. | |
287 | ; | |
288 | ; Returns: | |
289 | ; CF = 0 success | |
290 | ; 1 error | |
291 | ; | |
292 | load: | |
293 | push cx | |
294 | test BYTE [ebios_present], 1 | |
295 | jz .chs | |
296 | ||
297 | .ebios: | |
298 | mov cx, 5 ; load retry count | |
299 | .ebios_loop: | |
300 | call read_lba ; use INT13/F42 | |
301 | jnc .exit | |
302 | loop .ebios_loop | |
303 | ||
304 | .chs: | |
305 | mov cx, 5 ; load retry count | |
306 | .chs_loop: | |
307 | call read_chs ; use INT13/F2 | |
308 | jnc .exit | |
309 | loop .chs_loop | |
310 | ||
311 | .exit | |
312 | pop cx | |
313 | ret | |
314 | ||
315 | ;-------------------------------------------------------------------------- | |
316 | ; read_chs - Read sectors from a partition using CHS addressing. | |
317 | ; | |
318 | ; Arguments: | |
319 | ; AL = number of 512-byte sectors to read. | |
320 | ; ES:BX = pointer to where the sectors should be stored. | |
321 | ; DL = drive number (0x80 + unit number) | |
322 | ; SI = pointer to the partition entry. | |
323 | ; | |
324 | ; Returns: | |
325 | ; CF = 0 success | |
326 | ; 1 error | |
327 | ; | |
328 | read_chs: | |
329 | pusha ; save all registers | |
330 | ||
331 | ; | |
332 | ; Read the CHS start values from the partition entry. | |
333 | ; | |
334 | mov dh, [ si + part.head ] ; drive head | |
335 | mov cx, [ si + part.sect ] ; drive sector + cylinder | |
336 | ||
337 | ; | |
338 | ; INT13 Func 2 - Read Disk Sectors | |
339 | ; | |
340 | ; Arguments: | |
341 | ; AH = 2 | |
342 | ; AL = number of sectors to read | |
343 | ; CH = lowest 8 bits of the 10-bit cylinder number | |
344 | ; CL = bits 6 & 7: cylinder number bits 8 and 9 | |
345 | ; bits 0 - 5: starting sector number (1-63) | |
346 | ; DH = starting head number (0 to 255) | |
347 | ; DL = drive number (80h + drive unit) | |
348 | ; es:bx = pointer where to place sectors read from disk | |
349 | ; | |
350 | ; Returns: | |
351 | ; AH = return status (sucess is 0) | |
352 | ; AL = burst error length if ah=0x11 (ECC corrected) | |
353 | ; carry = 0 success | |
354 | ; 1 error | |
355 | ; | |
356 | ; mov dl, kDriveNumber | |
357 | mov ah, 0x02 ; Func 2 | |
358 | int 0x13 ; INT 13 | |
359 | jnc .exit | |
360 | ||
361 | DebugChar('r') ; indicate INT13/F2 error | |
362 | ||
363 | ; | |
364 | ; Issue a disk reset on error. | |
365 | ; Should this be changed to Func 0xD to skip the diskette controller | |
366 | ; reset? | |
367 | ; | |
368 | xor ax, ax ; Func 0 | |
369 | int 0x13 ; INT 13 | |
370 | stc ; set carry to indicate error | |
371 | ||
372 | .exit: | |
373 | popa | |
374 | ret | |
375 | ||
376 | ;-------------------------------------------------------------------------- | |
377 | ; read_lba - Read sectors from a partition using LBA addressing. | |
378 | ; | |
379 | ; Arguments: | |
380 | ; AL = number of 512-byte sectors to read (valid from 1-127). | |
381 | ; ES:BX = pointer to where the sectors should be stored. | |
382 | ; DL = drive number (0x80 + unit number) | |
383 | ; SI = pointer to the partition entry. | |
384 | ; | |
385 | ; Returns: | |
386 | ; CF = 0 success | |
387 | ; 1 error | |
388 | ; | |
389 | read_lba: | |
390 | pusha ; save all registers | |
391 | mov bp, sp ; save current SP | |
392 | ||
393 | ; | |
394 | ; Create the Disk Address Packet structure for the | |
395 | ; INT13/F42 (Extended Read Sectors) on the stack. | |
396 | ; | |
397 | ||
398 | ; push DWORD 0 ; offset 12, upper 32-bit LBA | |
399 | push ds ; For sake of saving memory, | |
400 | push ds ; push DS register, which is 0. | |
401 | ||
402 | mov ecx, [ebios_lba] ; offset 8, lower 32-bit LBA | |
403 | add ecx, [si + part.lba] | |
404 | push ecx | |
405 | ||
406 | push es ; offset 6, memory segment | |
407 | ||
408 | push bx ; offset 4, memory offset | |
409 | ||
410 | xor ah, ah ; offset 3, must be 0 | |
411 | push ax ; offset 2, number of sectors | |
412 | ||
413 | push WORD 16 ; offset 0-1, packet size | |
414 | ||
415 | ; | |
416 | ; INT13 Func 42 - Extended Read Sectors | |
417 | ; | |
418 | ; Arguments: | |
419 | ; AH = 0x42 | |
420 | ; DL = drive number (80h + drive unit) | |
421 | ; DS:SI = pointer to Disk Address Packet | |
422 | ; | |
423 | ; Returns: | |
424 | ; AH = return status (sucess is 0) | |
425 | ; carry = 0 success | |
426 | ; 1 error | |
427 | ; | |
428 | ; Packet offset 2 indicates the number of sectors read | |
429 | ; successfully. | |
430 | ; | |
431 | ; mov dl, kDriveNumber | |
432 | mov si, sp | |
433 | mov ah, 0x42 | |
434 | int 0x13 | |
435 | ||
436 | jnc .exit | |
437 | ||
438 | DebugChar('R') ; indicate INT13/F42 error | |
439 | ||
440 | ; | |
441 | ; Issue a disk reset on error. | |
442 | ; Should this be changed to Func 0xD to skip the diskette controller | |
443 | ; reset? | |
444 | ; | |
445 | xor ax, ax ; Func 0 | |
446 | int 0x13 ; INT 13 | |
447 | stc ; set carry to indicate error | |
448 | ||
449 | .exit: | |
450 | mov sp, bp ; restore SP | |
451 | popa | |
452 | ret | |
453 | ||
454 | ;-------------------------------------------------------------------------- | |
455 | ; Write a string to the console. | |
456 | ; | |
457 | ; Arguments: | |
458 | ; DS:SI pointer to a NULL terminated string. | |
459 | ; | |
460 | ; Clobber list: | |
461 | ; AX, BX, SI | |
462 | ; | |
463 | print_string | |
464 | mov bx, 1 ; BH=0, BL=1 (blue) | |
465 | cld ; increment SI after each lodsb call | |
466 | .loop | |
467 | lodsb ; load a byte from DS:SI into AL | |
468 | cmp al, 0 ; Is it a NULL? | |
469 | je .exit ; yes, all done | |
470 | mov ah, 0xE ; INT10 Func 0xE | |
471 | int 0x10 ; display byte in tty mode | |
472 | jmp short .loop | |
473 | .exit | |
474 | ret | |
475 | ||
476 | %if DEBUG | |
477 | ||
478 | ;-------------------------------------------------------------------------- | |
479 | ; Write a ASCII character to the console. | |
480 | ; | |
481 | ; Arguments: | |
482 | ; AL = ASCII character. | |
483 | ; | |
484 | print_char | |
485 | pusha | |
486 | mov bx, 1 ; BH=0, BL=1 (blue) | |
487 | mov ah, 0x0e ; bios INT 10, Function 0xE | |
488 | int 0x10 ; display byte in tty mode | |
489 | popa | |
490 | ret | |
491 | ||
f083c6c3 A |
492 | ;-------------------------------------------------------------------------- |
493 | ; Write the byte value to the console in hex. | |
494 | ; | |
495 | ; Arguments: | |
496 | ; AL = Value to be displayed in hex. | |
497 | ; | |
498 | print_hex: | |
499 | push ax | |
500 | ror al, 4 | |
501 | call print_nibble ; display upper nibble | |
502 | pop ax | |
503 | call print_nibble ; display lower nibble | |
504 | ||
505 | mov al, 10 ; carriage return | |
506 | call print_char | |
507 | mov al, 13 | |
508 | call print_char | |
509 | ret | |
510 | ||
511 | print_nibble: | |
512 | and al, 0x0f | |
513 | add al, '0' | |
514 | cmp al, '9' | |
515 | jna .print_ascii | |
516 | add al, 'A' - '9' - 1 | |
517 | .print_ascii: | |
518 | call print_char | |
519 | ret | |
520 | ||
521 | %endif ; DEBUG | |
522 | ||
523 | ;-------------------------------------------------------------------------- | |
524 | ; NULL terminated strings. | |
525 | ; | |
57c72a9a | 526 | part_error_str db 10, 13, 'Error loading UFS partition', 0 |
f083c6c3 A |
527 | |
528 | ;-------------------------------------------------------------------------- | |
529 | ; Pad the rest of the 512 byte sized booter with zeroes. The last | |
530 | ; two bytes is the mandatory boot sector signature. | |
531 | ; | |
532 | ; If the booter code becomes too large, then nasm will complain | |
533 | ; that the 'times' argument is negative. | |
534 | ||
535 | ;;pad_boot | |
536 | ;; times 446-($-$$) db 0 | |
537 | ||
538 | %ifdef FLOPPY | |
539 | ;-------------------------------------------------------------------------- | |
540 | ; Put fake partition entries for the bootable floppy image | |
541 | ; | |
542 | part1bootid db 0x80 ; first partition active | |
543 | part1head db 0x00 ; head # | |
544 | part1sect db 0x02 ; sector # (low 6 bits) | |
545 | part1cyl db 0x00 ; cylinder # (+ high 2 bits of above) | |
546 | part1systid db 0xab ; Apple boot partition | |
547 | times 3 db 0x00 ; ignore head/cyl/sect #'s | |
548 | part1relsect dd 0x00000001 ; start at sector 1 | |
549 | part1numsect dd 0x00000080 ; 64K for booter | |
550 | part2bootid db 0x00 ; not active | |
551 | times 3 db 0x00 ; ignore head/cyl/sect #'s | |
552 | part2systid db 0xa8 ; Apple UFS partition | |
553 | times 3 db 0x00 ; ignore head/cyl/sect #'s | |
554 | part2relsect dd 0x00000082 ; start after booter | |
555 | ; part2numsect dd 0x00000abe ; 1.44MB - 65K | |
556 | part2numsect dd 0x000015fe ; 2.88MB - 65K | |
557 | %endif | |
558 | ||
559 | pad_table_and_sig | |
560 | times 510-($-$$) db 0 | |
561 | dw kBootSignature | |
562 | ||
57c72a9a A |
563 | ABSOLUTE 0xE400 |
564 | ||
565 | ; | |
566 | ; In memory variables. | |
567 | ; | |
568 | ebios_lba resd 1 ; starting LBA of the intial extended partition. | |
569 | ebios_present resb 1 ; 1 if EBIOS is supported, 0 otherwise. | |
570 | ||
f083c6c3 | 571 | END |