]> git.saurik.com Git - apple/boot.git/blob - i386/boot0/boot0.s
boot-83.2.tar.gz
[apple/boot.git] / i386 / boot0 / boot0.s
1 ; Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
2 ;
3 ; @APPLE_LICENSE_HEADER_START@
4 ;
5 ; Portions Copyright (c) 1999 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.1 (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 ; But the booter partition must reside within the 8GB limit
30 ; imposed by CHS addressing + translation.
31 ;
32 ; This boot loader can be placed at any of the following places:
33 ; * Master Boot Record (MBR)
34 ; * Boot sector of an extended partition
35 ; * Boot sector of a primary partition
36 ; * Boot sector of a logical partition
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 play with.
42 ;
43 ; boot0 is always loaded by the BIOS or another first level booter
44 ; to 0:7C00h.
45 ;
46 ; This code is written for the NASM assembler.
47 ; nasm boot0.s -o boot0
48
49 ;--------------------------------------------------------------------------
50 ; Constants.
51
52 DEBUG EQU 0 ; enable debugging output
53
54 BOOTSEG EQU 0x0 ; our sole segment
55 BOOTSP EQU 0xFFF0 ; stack pointer
56 BOOTLOAD EQU 0x7C00 ; booter load address
57 BOOTRELOC EQU 0xE000 ; booter is relocated here
58 BOOTSIG EQU 0xAA55 ; booter signature
59
60 BOOT2_SIZE EQU 88 ; load this many blocks for boot2
61 BOOT2_ADDR EQU 0x3000 ; where to load boot2
62
63 DRIVE_NUM EQU 0x80 ; "C" drive
64 SECTOR_BYTES EQU 512 ; sector size in bytes
65
66 BUF_MBR EQU 0x1000 ; memory buffer for MBR
67 BUF_EXT EQU 0x1200 ; memory buffer for extended partition
68
69 TABLE_MAIN EQU BUF_MBR + 0x1be ; location of main partition table
70 TABLE_EXT EQU BUF_EXT + 0x1be ; location of ext partition table
71 ENTRY_SIZE EQU 16 ; size of each fdisk partition entry
72 TYPE_BOOT EQU 0xab ; partition type we are looking for
73 TYPE_EXT EQU 0x05 ; extended partition type
74 TYPE_EXT_1 EQU 0x0f ; Windows extended partition
75 TYPE_EXT_2 EQU 0x85 ; Linux extended partition
76 EXT_LEVELS_MAX EQU 128 ; max extended partition levels
77
78 ; Disk parameters gathered through INT13/F8 call.
79 ;
80 max_sectors db 0 ; number of sectors per track
81 max_heads db 0 ; number of heads
82
83 ; Parameters to our load function.
84 ;
85 chs_cx dw 0 ; cx register for INT13/F2 call
86 chs_dx dw 0 ; dx register for INT13/F2 call
87
88
89 ;--------------------------------------------------------------------------
90 ; Start of text segment.
91
92 SEGMENT .text
93
94 ORG 0xE000 ; must match BOOTRELOC
95
96 ;--------------------------------------------------------------------------
97 ; Loaded at 0:7c00h.
98 ;
99 start
100 ; Set up the stack to grow down from BOOTSEG:BOOTSP.
101 ; Interrupts should be off while the stack is being manipulated.
102 ;
103 cli ; interrupts off
104 mov ax, BOOTSEG ;
105 mov ss, ax ; ss <- BOOTSEG
106 mov sp, BOOTSP ; sp <- BOOTSP
107 sti ; reenable interrupts
108
109 ; Relocate the booter code from DS:SI to ES:DI,
110 ; or from 0:7C00h to BOOTSEG:BOOTRELOC.
111 ;
112 mov es, ax ; es <- BOOTSEG
113 xor ax, ax
114 mov ds, ax ; ds <- 0
115 mov si, BOOTLOAD ; si <- BOOTLOAD (source)
116 mov di, BOOTRELOC ; di <- BOOTRELOC (destination)
117 ;
118 cld ; auto-increment SI and/or DI registers
119 mov cx, 256 ; copy 256 words (512 bytes)
120 repnz movsw ; repeat string move (word) operation
121
122 ; Code relocated, jump to start_reloc in relocated location.
123 ;
124 jmp BOOTSEG:start_reloc
125
126 ;--------------------------------------------------------------------------
127 ; Start execution from the relocated location.
128 ;
129 start_reloc
130 mov ax, BOOTSEG
131 mov ds, ax ; ds <- BOOTSEG
132
133 mov al, '=' ; indicate execution start
134 call putchar
135
136 ; Get disk parameters (CHS) using INT13/F8 call.
137 ;
138 mov dl, DRIVE_NUM ; boot drive is drive C
139 mov ah, 8 ; Read Disk Driver Parameter function
140 int 0x13
141 and cl, 0x3f ; sectors/track
142 mov [max_sectors], cl
143 mov [max_heads], dh
144 jc error
145
146 mov al, '>' ; indicate INT13/F8 success
147 call putchar
148
149 ; Since this code may not always reside in the MBR, we will always
150 ; start by loading the MBR to BUF_MBR.
151 ;
152 mov WORD [chs_cx], 0x0001 ; cyl = 0, sect = 1
153 mov BYTE [chs_dx + 1], 0 ; head = 0
154 xor cx, cx ; skip 0 sectors
155 mov ax, 1 ; read 1 sector
156 mov bx, BUF_MBR ; load buffer
157 call load
158 jc error
159
160 mov di, TABLE_MAIN ; argument for find_booter
161 cmp WORD [di + 64], BOOTSIG ; correct signature found?
162 jne error ; Oops! no signature!
163 mov bl, TYPE_BOOT ; look for this partition type
164 mov bh, 0 ; initial nesting level is 0
165 call find_booter
166
167 error
168 mov si, load_error
169 call message
170 hang_1
171 jmp hang_1
172
173 ;--------------------------------------------------------------------------
174 ; Locate the booter partition and load the booter.
175 ;
176 ; Arguments:
177 ; di - pointer to fdisk partition table.
178 ; bl - partition type to look for.
179 ;
180 ; The following registers are modified:
181 ; ax, bh
182 ;
183 find_booter
184 push cx
185 push si
186
187 mov si, di ; si <- pointer to partition table
188 mov cx, 4 ; 4 partition entries per table
189
190 find_booter_pri
191 ;
192 ; Hunt for a fdisk partition type that matches the value in bl.
193 ;
194 %IF DEBUG
195 mov al, bh ; log partition type seen
196 call putspace
197 mov al, [si + 4]
198 call display_byte
199 %ENDIF
200
201 cmp BYTE [si + 4], bl ; Is this the booter partition?
202 je load_booter ; yes, load the booter
203
204 add si, ENTRY_SIZE ; si <- next partition entry
205 loop find_booter_pri ; loop while cx is not zero
206
207 ; No primary (or perhaps logical) booter partition found in the
208 ; current partition table. Restart and look for extended partitions.
209 ;
210 mov si, di ; si <- pointer to partition table
211 mov cx, 4 ; 4 partition entries per table
212
213 find_booter_ext
214 ;
215 ; Look for extended partition entries in the partition table.
216 ;
217 %IF DEBUG
218 mov al, bh ; log partition type seen
219 call putspace
220 mov al, 'E'
221 call putchar
222 mov al, [si + 4]
223 call display_byte
224 %ENDIF
225
226 cmp BYTE [si + 4], TYPE_EXT ; Is this an extended partition?
227 je find_booter_ext_2 ; yes, load its partition table
228
229 cmp BYTE [si + 4], TYPE_EXT_1 ; Is this an extended partition?
230 je find_booter_ext_2 ; yes, load its partition table
231
232 cmp BYTE [si + 4], TYPE_EXT_2 ; Is this an extended partition?
233 je find_booter_ext_2 ; yes, load its partition table
234
235 find_booter_ext_1
236 ;
237 ; si is not pointing to an extended partition entry,
238 ; try the next entry in the partition table.
239 ;
240 add si, ENTRY_SIZE ; si <- next partition entry
241 loop find_booter_ext ; loop while cx is not zero
242
243 jmp find_booter_end ; give up
244
245 find_booter_ext_2
246 cmp bh, EXT_LEVELS_MAX
247 ja find_booter_end ; in too deep!
248
249 inc bh ; increment nesting level counter
250
251 ; Prepare the arguments for the load function call to
252 ; load the extended partition table into memory.
253 ; Note that si points to the extended partition entry.
254 ;
255 mov ax, [si] ; DH/DL
256 mov [chs_dx], ax
257 mov ax, [si + 2] ; CH/CL
258 mov [chs_cx], ax
259 pusha
260 xor cx, cx ; skip 0 sectors
261 mov ax, 1 ; read 1 sector
262 mov bx, BUF_EXT ; load to BUF_EXT
263 call load
264 popa
265
266 jc find_booter_ext_3 ; bail out if load failed
267
268 mov di, TABLE_EXT ; di <- pointer to new partition table
269 cmp WORD [di + 64], BOOTSIG
270 jne find_booter_ext_3 ; OhOh! no signature!
271
272 call find_booter ; recursion...
273
274 find_booter_ext_3
275 dec bh ; decrement nesting level counter
276
277 ; If we got here, then we know there isn't a booter
278 ; partition linked from this partition entry.
279
280 test bh, bh ; if we are at level 0, then
281 jz find_booter_ext_1 ; look for next extended partition entry
282
283 find_booter_end
284 pop si
285 pop cx
286 ret
287
288 ;--------------------------------------------------------------------------
289 ; Yeah! Found the booter partition. The first sector in this partition
290 ; is reserved for the boot sector code (us). So load the booter
291 ; starting from the second sector in the partition, then jump to the
292 ; start of the booter.
293 ;
294 load_booter
295 mov ax, [si] ; DH/DL
296 mov [chs_dx], ax
297 mov ax, [si + 2] ; CH/CL
298 mov [chs_cx], ax
299
300 mov cx, 1 ; skip the initial boot sector
301 mov ax, BOOT2_SIZE ; read BOOT2_SIZE sectors
302 mov bx, BOOT2_ADDR ; where to place boot2 code
303 call load ; load it...
304
305 xor edx, edx ; argument for boot2 (hard drive boot)
306 jmp BOOTSEG:BOOT2_ADDR ; there is no going back now!
307
308 ;--------------------------------------------------------------------------
309 ; Load sectors from disk using INT13/F2 call. The sectors are loaded
310 ; one sector at a time to avoid any BIOS bugs, and eliminate
311 ; complexities with crossing track boundaries, and other gotchas.
312 ;
313 ; Arguments:
314 ; cx - number of sectors to skip
315 ; ax - number of sectors to read
316 ; bx - pointer to the memory buffer (must not cross a segment boundary)
317 ; [chs_cx][chs_dx] - CHS starting position
318 ;
319 ; Returns:
320 ; CF = 0 success
321 ; CF = 1 error
322 ;
323 ; The caller must save any registers it needs.
324 ;
325 load
326 jcxz load_sectors
327 call next_sector ; [chs_cx][chs_dx] <- next sector
328 loop load
329
330 load_sectors
331 mov cx, ax ; cx <- number of sectors to read
332
333 load_loop
334 call read_sector ; load a single sector
335 jc load_exit ; abort if carry flag is set
336 add bx, SECTOR_BYTES ; increment buffer pointer
337 call next_sector ; [chs_cx][chs_dx] <- next sector
338 loop load_loop
339 clc ; successful exit
340 load_exit
341 ret
342
343 ;--------------------------------------------------------------------------
344 ; Read a single sector from the hard disk.
345 ;
346 ; Arguments:
347 ; [chs_cx][chs_dx] - CHS starting position
348 ; bx - pointer to the sector memory buffer
349 ; (must not cross a segment boundary)
350 ;
351 ; Returns:
352 ; CF = 0 success
353 ; CF = 1 error
354 ;
355 ; Caller's cx register is preserved.
356 ;
357 read_sector
358 push cx
359 mov cx, 5 ; try 5 times to read the sector
360
361 read_sector_1
362 mov bp, cx ; save cx
363
364 mov cx, [chs_cx]
365 mov dx, [chs_dx]
366 mov dl, DRIVE_NUM ; drive number
367 mov ax, 0x0201 ; Func 2, read 1 sector
368 int 0x13 ; read sector
369 jnc read_sector_ok ; CF = 0 indicates success
370
371 mov al, '*' ; sector read error indicator
372 call putchar
373
374 xor ax, ax ; Reset the drive and retry the read
375 int 0x13
376
377 mov cx, bp
378 loop read_sector_1 ; retry while cx is not zero
379
380 stc ; set carry flag to indicate error
381 pop cx
382 ret
383
384 read_sector_ok
385 mov al, '.' ; successful sector read indicator
386 call putchar
387 clc ; success, clear carry flag
388 pop cx
389 ret
390
391 ;--------------------------------------------------------------------------
392 ; Given the current CHS position stored in [chs_cx][chs_dx], update
393 ; it so that the value in [chs_cx][chs_dx] points to the following
394 ; sector.
395 ;
396 ; Arguments:
397 ; [chs_cx][chs_dx] - CHS position
398 ;
399 ; [max_sectors] and [max_heads] must be valid.
400 ;
401 ; Caller's ax and bx registers are preserved.
402 ;
403 next_sector
404 push ax
405 push bx
406
407 ; Extract the CHS values from the packed register values in memory.
408 ;
409 mov al, [chs_cx]
410 and al, 0x3f ; al <- sector number (1-63)
411
412 mov bx, [chs_cx]
413 rol bl, 2
414 ror bx, 8
415 and bx, 0x03ff ; bx <- cylinder number
416
417 mov ah, [chs_dx + 1] ; ah <- head number
418
419 inc al ; Increment CHS by one sector.
420 cmp al, [max_sectors]
421 jbe next_sector_done
422
423 inc ah
424 cmp ah, [max_heads]
425 jbe next_sector_new_head
426
427 inc bx
428
429 next_sector_new_cyl
430 xor ah, ah ; head number starts at 0
431
432 next_sector_new_head
433 mov al, 1 ; sector number starts at 1
434
435 next_sector_done
436 ; Reassemble the CHS values back into the packed representation
437 ; in memory.
438 ;
439 mov [chs_cx + 1], bl ; lower 8-bits of the 10-bit cylinder
440 ror bh, 2
441 or bh, al
442 mov [chs_cx], bh ; cylinder & sector number
443 mov [chs_dx + 1], ah ; head number
444
445 pop bx
446 pop ax
447 ret
448
449 ;--------------------------------------------------------------------------
450 ; Write a string to the console.
451 ;
452 ; Arguments:
453 ; ds:si pointer to a NULL terminated string.
454 ;
455 ; The following registers are modified:
456 ; ax, bx, si
457 ;
458 message
459 mov bx, 1 ; bh=0, bl=1 (blue)
460 cld ; increment SI after each lodsb call
461 message_loop
462 lodsb ; load a byte from DS:SI into al
463 cmp al, 0 ; Is it a NULL?
464 je message_done ; yes, all done
465 mov ah, 0xE ; bios INT10 Func 0xE
466 int 0x10 ; bios display a byte in tty mode
467 jmp short message_loop
468 message_done
469 ret
470
471 ;--------------------------------------------------------------------------
472 ; Write a ASCII character to the console.
473 ;
474 ; Arguments:
475 ; al contains the ASCII character printed.
476 ;
477 putchar
478 push bx
479 mov bx, 1 ; bh=0, bl=1 (blue)
480 mov ah, 0x0e ; bios int 10, function 0xe
481 int 0x10 ; bios display a byte in tty mode
482 pop bx
483 ret
484
485 %IF DEBUG
486 ;==========================================================================
487 ; DEBUG FUNCTION START
488 ;
489 ; If DEBUG is set to 1, this booter will become too large for the MBR,
490 ; but it will still be less than 510 bytes, which is fine for a partition's
491 ; boot sector.
492 ;==========================================================================
493
494 ;--------------------------------------------------------------------------
495 ; Write a variable number of spaces to the console.
496 ;
497 ; Arguments:
498 ; al number to spaces to display
499 ;
500 putspace
501 push cx
502 xor cx, cx
503 mov cl, al ; use cx as the loop counter
504 mov al, ' ' ; character to print
505 putspace_loop
506 jcxz putspace_done
507 call putchar
508 loop putspace_loop
509 putspace_done
510 pop cx
511 ret
512
513 ;--------------------------------------------------------------------------
514 ; Write the hex byte value to the console.
515 ;
516 ; Arguments:
517 ; al contains the byte to be displayed. e.g. if al is 0x3f, then 3F
518 ; will be displayed on screen.
519 ;
520 display_byte
521 push ax
522 ror al, 4
523 call display_nibble ; display upper nibble
524 pop ax
525 call display_nibble ; display lower nibble
526 ;
527 mov ax, 10 ; display carriage return
528 call putchar
529 mov ax, 13
530 call putchar
531 ret
532
533 display_nibble
534 and al, 0x0f
535 add al, '0'
536 cmp al, '9'
537 jna display_nibble_1
538 add al, 'A' - '9' - 1
539 display_nibble_1
540 call putchar
541 ret
542
543 ;==========================================================================
544 ; DEBUG FUNCTION END
545 ;==========================================================================
546 %ENDIF
547
548 ;--------------------------------------------------------------------------
549 ; NULL terminated strings.
550 ;
551 load_error db 10, 13, 'Load Error', 0
552
553 ;--------------------------------------------------------------------------
554 ; Pad the rest of the 512 byte sized booter with zeroes. The last
555 ; two bytes is the mandatory boot sector signature.
556 ;
557 ; If the booter code becomes too large, then nasm will complain
558 ; that the 'times' argument is negative.
559
560 pad_boot
561 times 446-($-$$) db 0
562
563 pad_table_and_sig
564 times 510-($-$$) db 0
565 dw BOOTSIG
566
567 END