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