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