]>
Commit | Line | Data |
---|---|---|
57c72a9a | 1 | ; Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved. |
14c7c974 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. | |
14c7c974 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 |
14c7c974 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. | |
14c7c974 A |
20 | ; |
21 | ; @APPLE_LICENSE_HEADER_END@ | |
22 | ; | |
f083c6c3 | 23 | ; Partition Boot Loader: boot1h |
14c7c974 | 24 | ; |
f083c6c3 A |
25 | ; This program is designed to reside in sector 0 of an HFS+ partition. |
26 | ; The HFS+ partition can be a primary or a logical partition. | |
27 | ; It expects that the MBR has left the drive number in DL | |
28 | ; and a pointer to the partition entry in SI. | |
14c7c974 | 29 | ; |
f083c6c3 | 30 | ; This version requires a BIOS with EBIOS (LBA) support. |
14c7c974 A |
31 | ; |
32 | ; This code is written for the NASM assembler. | |
33 | ; nasm boot0.s -o boot0 | |
34 | ||
75b89a82 | 35 | |
f083c6c3 A |
36 | ; |
37 | ; Set to 1 to enable obscure debug messages. | |
38 | ; | |
39 | DEBUG EQU 0 | |
14c7c974 | 40 | |
f083c6c3 A |
41 | ; |
42 | ; Various constants. | |
43 | ; | |
44 | kBoot0Segment EQU 0x0000 | |
45 | kBoot0Stack EQU 0xFFF0 ; boot0 stack pointer | |
46 | kBoot0LoadAddr EQU 0x7C00 ; boot0 load address | |
47 | kBoot0RelocAddr EQU 0xE000 ; boot0 relocated address | |
14c7c974 | 48 | |
f083c6c3 A |
49 | kMBRBuffer EQU 0x1000 ; MBR buffer address |
50 | kExtBuffer EQU 0x1200 ; EXT boot block buffer address | |
14c7c974 | 51 | |
f083c6c3 A |
52 | kPartTableOffset EQU 0x1be |
53 | kMBRPartTable EQU kMBRBuffer + kPartTableOffset | |
54 | kExtPartTable EQU kExtBuffer + kPartTableOffset | |
14c7c974 | 55 | |
57c72a9a | 56 | kBoot2Sectors EQU 126 ; sectors to load for boot2 |
f083c6c3 A |
57 | kBoot2Address EQU 0x0000 ; boot2 load address |
58 | kBoot2Segment EQU 0x2000 ; boot2 load segment | |
75b89a82 | 59 | |
f083c6c3 A |
60 | kSectorBytes EQU 512 ; sector size in bytes |
61 | kBootSignature EQU 0xAA55 ; boot sector signature | |
14c7c974 | 62 | |
f083c6c3 A |
63 | kPartCount EQU 4 ; number of paritions per table |
64 | kPartTypeBoot EQU 0xab ; boot2 partition type | |
65 | kPartTypeHFS EQU 0xaf | |
66 | kPartTypeExtDOS EQU 0x05 ; DOS extended partition type | |
67 | kPartTypeExtWin EQU 0x0f ; Windows extended partition type | |
68 | kPartTypeExtLinux EQU 0x85 ; Linux extended partition type | |
69 | ||
70 | kPartActive EQU 0x80 | |
14c7c974 | 71 | |
f083c6c3 A |
72 | ;; |
73 | ;; HFS constants | |
74 | ;; | |
75 | kHFSSig EQU 0x4442 ; HFS volume signature | |
76 | kAlBlStOffset EQU 0x1c | |
77 | kEmbedStartOffset EQU 0x7e | |
78 | kAlBlkSizOffset EQU 0x14 | |
14c7c974 | 79 | |
f083c6c3 A |
80 | ;; |
81 | ;; HFS+ constants | |
82 | ;; | |
83 | kHFSPlusSig EQU 0x2B48 ; HFS+ volume signature | |
57c72a9a | 84 | kHFSXSig EQU 0x5848 ; HFSX volume signature |
f083c6c3 A |
85 | kBlockSizeOffset EQU 0x28 |
86 | kExtentOffset EQU 0x1c0 | |
87 | ||
88 | kHFSBuffer EQU 0x1400 ; HFS volume header address | |
14c7c974 | 89 | |
f083c6c3 A |
90 | kHFSSigAddr EQU kHFSBuffer |
91 | kHFSAlBlSt EQU kHFSBuffer + kAlBlStOffset | |
92 | kHFSEmbedStart EQU kHFSBuffer + kEmbedStartOffset | |
93 | kHFSAlBlkSiz EQU kHFSBuffer + kAlBlkSizOffset | |
14c7c974 | 94 | |
f083c6c3 A |
95 | kHFSPlusSigAddr EQU kHFSBuffer |
96 | kHFSPlusBlockSize EQU kHFSBuffer + kBlockSizeOffset | |
97 | kHFSPlusExtent EQU kHFSBuffer + kExtentOffset | |
14c7c974 | 98 | |
14c7c974 | 99 | |
f083c6c3 | 100 | kDriveNumber EQU 0x80 |
14c7c974 | 101 | |
14c7c974 | 102 | ; |
f083c6c3 | 103 | ; Format of fdisk partition entry. |
14c7c974 | 104 | ; |
f083c6c3 A |
105 | ; The symbol 'part_size' is automatically defined as an `EQU' |
106 | ; giving the size of the structure. | |
14c7c974 | 107 | ; |
f083c6c3 A |
108 | struc part |
109 | .bootid: resb 1 ; bootable or not | |
110 | .head: resb 1 ; starting head, sector, cylinder | |
111 | .sect: resb 1 ; | |
112 | .cyl: resb 1 ; | |
113 | .type: resb 1 ; partition type | |
114 | .endhead resb 1 ; ending head, sector, cylinder | |
115 | .endsect: resb 1 ; | |
116 | .endcyl: resb 1 ; | |
117 | .lba: resd 1 ; starting lba | |
118 | .sectors resd 1 ; size in sectors | |
119 | endstruc | |
14c7c974 | 120 | |
f083c6c3 A |
121 | ; |
122 | ; Macros. | |
123 | ; | |
124 | %macro DebugCharMacro 1 | |
125 | mov al, %1 | |
126 | call print_char | |
127 | call getc | |
128 | %endmacro | |
14c7c974 | 129 | |
57c72a9a A |
130 | %macro PrintString 1 |
131 | mov si, %1 | |
132 | call print_string | |
133 | %endmacro | |
134 | ||
f083c6c3 A |
135 | %if DEBUG |
136 | %define DebugChar(x) DebugCharMacro x | |
137 | %else | |
138 | %define DebugChar(x) | |
139 | %endif | |
140 | ||
141 | ||
142 | ;-------------------------------------------------------------------------- | |
143 | ; Start of text segment. | |
14c7c974 | 144 | |
f083c6c3 | 145 | SEGMENT .text |
14c7c974 | 146 | |
57c72a9a | 147 | ORG 0x7C00 ; must match kBoot0RelocAddr |
14c7c974 | 148 | |
f083c6c3 A |
149 | ;-------------------------------------------------------------------------- |
150 | ; Boot code is loaded at 0:7C00h. | |
151 | ; | |
152 | start | |
14c7c974 | 153 | ; |
f083c6c3 A |
154 | ; Set up the stack to grow down from kBoot0Segment:kBoot0Stack. |
155 | ; Interrupts should be off while the stack is being manipulated. | |
14c7c974 | 156 | ; |
f083c6c3 A |
157 | cli ; interrupts off |
158 | xor ax, ax ; zero ax | |
159 | mov ss, ax ; ss <- 0 | |
160 | mov sp, kBoot0Stack ; sp <- top of stack | |
161 | sti ; reenable interrupts | |
14c7c974 | 162 | |
f083c6c3 A |
163 | mov es, ax ; es <- 0 |
164 | mov ds, ax ; ds <- 0 | |
14c7c974 | 165 | |
f083c6c3 | 166 | DebugChar('H') |
75b89a82 | 167 | |
f083c6c3 A |
168 | %if DEBUG |
169 | mov eax, [si + part.lba] | |
170 | call print_hex | |
171 | %endif | |
14c7c974 | 172 | |
14c7c974 | 173 | ; |
f083c6c3 | 174 | ; Clear various flags in memory. |
14c7c974 | 175 | ; |
f083c6c3 A |
176 | xor eax, eax |
177 | mov [ebios_lba], eax ; clear EBIOS LBA offset | |
178 | ||
179 | cmp BYTE [si + part.type], kPartTypeHFS | |
180 | jne .part_err | |
f083c6c3 A |
181 | |
182 | jmp find_startup | |
183 | ||
184 | .part_err: | |
57c72a9a | 185 | PrintString(part_error_str) |
f083c6c3 A |
186 | jmp hang |
187 | ||
188 | ;;; --------------------------------------- | |
189 | ;;; | |
190 | ;;; find_startup - Find HFS+ startup file in a partition. | |
191 | ;;; | |
192 | ;;; Arguments: | |
193 | ;;; DL = drive number (0x80 + unit number) | |
194 | ;;; SI = pointer to the partition entry. | |
195 | ;;; | |
196 | ;;; On success, loads booter and jumps to it. | |
197 | ;;; | |
198 | find_startup: | |
199 | DebugChar(']') | |
200 | ||
201 | mov al, 1 ; read 1 sector | |
202 | xor bx, bx | |
203 | mov es, bx ; es = 0 | |
204 | mov bx, kHFSBuffer ; load volume header | |
205 | mov ecx, DWORD 2 | |
14c7c974 | 206 | call load |
f083c6c3 A |
207 | jnc .ok ; load error |
208 | ||
209 | jmp startup_err | |
210 | ||
211 | .ok | |
212 | mov ax, [kHFSSigAddr] | |
213 | cmp ax, kHFSSig | |
214 | jne .hfs_plus | |
215 | ||
216 | DebugChar('|') | |
217 | mov ebx, [kHFSAlBlkSiz] | |
218 | bswap ebx | |
219 | sar ebx, 9 | |
220 | ||
221 | xor eax, eax | |
222 | mov ax, [kHFSEmbedStart] | |
223 | xchg al, ah ; byte-swap | |
224 | push dx | |
225 | mul ebx ; result in eax | |
226 | pop dx | |
227 | ||
228 | xor ebx, ebx | |
229 | mov bx, [kHFSAlBlSt] | |
230 | xchg bl, bh ; byte-swap | |
231 | add eax, ebx | |
232 | ||
233 | ;; now eax has sector of HFS+ partition | |
234 | inc eax | |
235 | inc eax | |
236 | mov ecx, eax | |
237 | ||
238 | mov al, 1 ; read 1 sector | |
239 | xor bx, bx | |
240 | mov es, bx ; es = 0 | |
241 | mov bx, kHFSBuffer ; load volume header | |
242 | call load | |
243 | jc startup_err ; load error | |
244 | ||
245 | .hfs_plus | |
246 | DebugChar('}') | |
247 | mov ax, [kHFSPlusSigAddr] | |
248 | cmp ax, kHFSPlusSig | |
57c72a9a A |
249 | je .hfs_plus2 |
250 | cmp ax, kHFSXSig | |
f083c6c3 A |
251 | jne startup_err |
252 | ||
253 | ;;; Now the HFS+ volume header is in our buffer. | |
254 | ||
57c72a9a | 255 | .hfs_plus2 |
f083c6c3 A |
256 | DebugChar('*') |
257 | mov eax, [kHFSPlusBlockSize] | |
258 | bswap eax | |
259 | sar eax, 9 | |
260 | ||
261 | mov ebx, [kHFSPlusExtent] | |
262 | bswap ebx | |
263 | push dx | |
264 | mul ebx ; result in eax | |
265 | pop dx | |
266 | ||
267 | dec eax | |
268 | dec eax | |
f083c6c3 A |
269 | add ecx, eax |
270 | ||
271 | DebugChar('!') | |
272 | ||
273 | mov al, kBoot2Sectors | |
274 | mov bx, kBoot2Segment | |
75b89a82 | 275 | mov es, bx |
f083c6c3 A |
276 | mov bx, kBoot2Address + kSectorBytes |
277 | call load | |
278 | jc startup_err | |
14c7c974 | 279 | |
f083c6c3 A |
280 | DebugChar('Y') |
281 | ; | |
282 | ; Jump to boot2. The drive number is already in register DL. | |
283 | ; | |
284 | jmp kBoot2Segment:kBoot2Address + kSectorBytes | |
285 | ||
286 | startup_err: | |
75b89a82 | 287 | |
57c72a9a | 288 | PrintString(boot2_error_str) |
f083c6c3 A |
289 | DebugChar('X') |
290 | ||
291 | hang: | |
292 | hlt | |
293 | jmp SHORT hang | |
14c7c974 | 294 | |
14c7c974 A |
295 | |
296 | ;-------------------------------------------------------------------------- | |
f083c6c3 | 297 | ; read_lba - Read sectors from a partition using LBA addressing. |
14c7c974 A |
298 | ; |
299 | ; Arguments: | |
f083c6c3 A |
300 | ; AL = number of 512-byte sectors to read (valid from 1-127). |
301 | ; ES:BX = pointer to where the sectors should be stored. | |
302 | ; ECX = sector offset in partition | |
303 | ; DL = drive number (0x80 + unit number) | |
304 | ; SI = pointer to the partition entry. | |
14c7c974 A |
305 | ; |
306 | ; Returns: | |
307 | ; CF = 0 success | |
f083c6c3 | 308 | ; 1 error |
14c7c974 | 309 | ; |
f083c6c3 A |
310 | read_lba: |
311 | load: | |
312 | pushad ; save all registers | |
313 | mov bp, sp ; save current SP | |
314 | ||
57c72a9a | 315 | ; |
f083c6c3 A |
316 | ; Create the Disk Address Packet structure for the |
317 | ; INT13/F42 (Extended Read Sectors) on the stack. | |
318 | ; | |
14c7c974 | 319 | |
f083c6c3 A |
320 | ; push DWORD 0 ; offset 12, upper 32-bit LBA |
321 | push ds ; For sake of saving memory, | |
322 | push ds ; push DS register, which is 0. | |
14c7c974 | 323 | |
f083c6c3 A |
324 | add ecx, [ebios_lba] ; offset 8, lower 32-bit LBA |
325 | add ecx, [si + part.lba] | |
14c7c974 | 326 | |
f083c6c3 | 327 | push ecx |
14c7c974 | 328 | |
f083c6c3 | 329 | push es ; offset 6, memory segment |
14c7c974 | 330 | |
f083c6c3 | 331 | push bx ; offset 4, memory offset |
14c7c974 | 332 | |
f083c6c3 A |
333 | xor ah, ah ; offset 3, must be 0 |
334 | push ax ; offset 2, number of sectors | |
14c7c974 | 335 | |
f083c6c3 A |
336 | %if DEBUG |
337 | push ax | |
338 | DebugChar('-') ; absolute sector offset | |
339 | mov eax, ecx | |
340 | call print_hex | |
341 | DebugChar('=') ; count | |
342 | pop ax | |
343 | call print_hex | |
344 | %endif | |
345 | ||
346 | push WORD 16 ; offset 0-1, packet size | |
14c7c974 | 347 | |
57c72a9a | 348 | ; |
f083c6c3 | 349 | ; INT13 Func 42 - Extended Read Sectors |
57c72a9a | 350 | ; |
f083c6c3 A |
351 | ; Arguments: |
352 | ; AH = 0x42 | |
353 | ; DL = drive number (80h + drive unit) | |
354 | ; DS:SI = pointer to Disk Address Packet | |
57c72a9a | 355 | ; |
f083c6c3 A |
356 | ; Returns: |
357 | ; AH = return status (sucess is 0) | |
358 | ; carry = 0 success | |
359 | ; 1 error | |
57c72a9a | 360 | ; |
f083c6c3 A |
361 | ; Packet offset 2 indicates the number of sectors read |
362 | ; successfully. | |
14c7c974 | 363 | ; |
57c72a9a | 364 | ; mov dl, kDriveNumber |
f083c6c3 A |
365 | mov si, sp |
366 | mov ah, 0x42 | |
367 | int 0x13 | |
14c7c974 | 368 | |
f083c6c3 | 369 | jnc .exit |
14c7c974 | 370 | |
f083c6c3 A |
371 | %if DEBUG |
372 | call print_hex | |
373 | %endif | |
374 | DebugChar('R') ; indicate INT13/F42 error | |
14c7c974 | 375 | |
14c7c974 | 376 | ; |
f083c6c3 A |
377 | ; Issue a disk reset on error. |
378 | ; Should this be changed to Func 0xD to skip the diskette controller | |
379 | ; reset? | |
380 | ; | |
381 | xor ax, ax ; Func 0 | |
382 | int 0x13 ; INT 13 | |
383 | stc ; set carry to indicate error | |
14c7c974 | 384 | |
f083c6c3 A |
385 | .exit: |
386 | mov sp, bp ; restore SP | |
387 | popad | |
14c7c974 A |
388 | ret |
389 | ||
f083c6c3 | 390 | ;------------------------------------------------------------------------- |
14c7c974 A |
391 | ; Write a string to the console. |
392 | ; | |
393 | ; Arguments: | |
f083c6c3 | 394 | ; DS:SI pointer to a NULL terminated string. |
14c7c974 | 395 | ; |
f083c6c3 A |
396 | ; Clobber list: |
397 | ; AX, BX, SI | |
14c7c974 | 398 | ; |
f083c6c3 A |
399 | print_string |
400 | mov bx, 1 ; BH=0, BL=1 (blue) | |
14c7c974 | 401 | cld ; increment SI after each lodsb call |
f083c6c3 A |
402 | .loop |
403 | lodsb ; load a byte from DS:SI into AL | |
14c7c974 | 404 | cmp al, 0 ; Is it a NULL? |
f083c6c3 A |
405 | je .exit ; yes, all done |
406 | mov ah, 0xE ; INT10 Func 0xE | |
407 | int 0x10 ; display byte in tty mode | |
408 | jmp short .loop | |
409 | .exit | |
14c7c974 | 410 | ret |
f083c6c3 A |
411 | |
412 | %if DEBUG | |
14c7c974 A |
413 | |
414 | ;-------------------------------------------------------------------------- | |
415 | ; Write a ASCII character to the console. | |
416 | ; | |
417 | ; Arguments: | |
f083c6c3 A |
418 | ; AL = ASCII character. |
419 | ; | |
420 | print_char | |
421 | pushad | |
422 | mov bx, 1 ; BH=0, BL=1 (blue) | |
423 | mov ah, 0x0e ; bios INT 10, Function 0xE | |
424 | int 0x10 ; display byte in tty mode | |
425 | popad | |
14c7c974 A |
426 | ret |
427 | ||
14c7c974 A |
428 | ;-------------------------------------------------------------------------- |
429 | ; Write a variable number of spaces to the console. | |
430 | ; | |
431 | ; Arguments: | |
f083c6c3 | 432 | ; AL = number to spaces. |
14c7c974 | 433 | ; |
f083c6c3 A |
434 | print_spaces: |
435 | pushad | |
14c7c974 | 436 | xor cx, cx |
f083c6c3 | 437 | mov cl, al ; use CX as the loop counter |
14c7c974 | 438 | mov al, ' ' ; character to print |
f083c6c3 A |
439 | .loop: |
440 | jcxz .exit | |
441 | call print_char | |
442 | loop .loop | |
443 | .exit: | |
444 | popad | |
14c7c974 A |
445 | ret |
446 | ||
447 | ;-------------------------------------------------------------------------- | |
f083c6c3 | 448 | ; Write the byte value to the console in hex. |
14c7c974 A |
449 | ; |
450 | ; Arguments: | |
f083c6c3 | 451 | ; AL = Value to be displayed in hex. |
14c7c974 | 452 | ; |
f083c6c3 A |
453 | print_hex: |
454 | pushad | |
455 | mov cx, WORD 4 | |
456 | bswap eax | |
457 | .loop | |
14c7c974 A |
458 | push ax |
459 | ror al, 4 | |
f083c6c3 | 460 | call print_nibble ; display upper nibble |
14c7c974 | 461 | pop ax |
f083c6c3 A |
462 | call print_nibble ; display lower nibble |
463 | ror eax, 8 | |
464 | loop .loop | |
465 | ||
466 | mov al, 10 ; carriage return | |
467 | call print_char | |
468 | mov al, 13 | |
469 | call print_char | |
470 | popad | |
14c7c974 A |
471 | ret |
472 | ||
f083c6c3 | 473 | print_nibble: |
14c7c974 A |
474 | and al, 0x0f |
475 | add al, '0' | |
476 | cmp al, '9' | |
f083c6c3 | 477 | jna .print_ascii |
14c7c974 | 478 | add al, 'A' - '9' - 1 |
f083c6c3 A |
479 | .print_ascii: |
480 | call print_char | |
14c7c974 A |
481 | ret |
482 | ||
f083c6c3 A |
483 | getc: |
484 | mov ah, 0 | |
485 | int 0x16 | |
486 | ret | |
14c7c974 | 487 | |
f083c6c3 | 488 | %endif ; DEBUG |
14c7c974 A |
489 | |
490 | ;-------------------------------------------------------------------------- | |
491 | ; NULL terminated strings. | |
492 | ; | |
f083c6c3 | 493 | ; boot_error_str db 10, 13, 'Error', 0 |
57c72a9a A |
494 | part_error_str db 10, 13, 'HFS+ partition error', 0 |
495 | boot2_error_str db 10, 13, 'Error loading booter', 0 | |
14c7c974 A |
496 | |
497 | ;-------------------------------------------------------------------------- | |
498 | ; Pad the rest of the 512 byte sized booter with zeroes. The last | |
499 | ; two bytes is the mandatory boot sector signature. | |
500 | ; | |
501 | ; If the booter code becomes too large, then nasm will complain | |
502 | ; that the 'times' argument is negative. | |
503 | ||
14c7c974 A |
504 | pad_table_and_sig |
505 | times 510-($-$$) db 0 | |
f083c6c3 | 506 | dw kBootSignature |
14c7c974 | 507 | |
57c72a9a A |
508 | |
509 | ABSOLUTE 0xE400 | |
510 | ||
511 | ; | |
512 | ; Variable storage area | |
513 | ; | |
514 | ebios_lba resd 1 | |
515 | ||
14c7c974 | 516 | END |
57c72a9a | 517 |