2009-01-10 Robert Millan <rmh@aybabtu.com>
[grub2/phcoder.git] / boot / i386 / pc / boot.S
blob8056731213922d3dc03e2b6806abf804bf16ba4b
1 /* -*-Asm-*- */
2 /*
3  *  GRUB  --  GRand Unified Bootloader
4  *  Copyright (C) 1999,2000,2001,2002,2005,2006,2007,2008  Free Software Foundation, Inc.
5  *
6  *  GRUB is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  GRUB is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
18  */
20 #include <grub/boot.h>
21 #include <grub/machine/boot.h>
22         
24  *  defines for the code go here
25  */
27         /* Absolute addresses
28            This makes the assembler generate the address without support
29            from the linker. (ELF can't relocate 16-bit addresses!) */
30 #define ABS(x) (x-_start+0x7c00)
32         /* Print message string */
33 #define MSG(x)  movw $ABS(x), %si; call message
35         .file   "boot.S"
37         .text
39         /* Tell GAS to generate 16-bit instructions so that this code works
40            in real mode. */
41         .code16
43 .globl _start; _start:
44         /*
45          * _start is loaded at 0x7c00 and is jumped to with CS:IP 0:0x7c00
46          */
48         /*
49          * Beginning of the sector is compatible with the FAT/HPFS BIOS
50          * parameter block.
51          */
53         jmp     after_BPB
54         nop     /* do I care about this ??? */
56         /*
57          * This space is for the BIOS parameter block!!!!  Don't change
58          * the first jump, nor start the code anywhere but right after
59          * this area.
60          */
62         . = _start + 4
64         /* scratch space */
65 mode:
66         .byte   0
67 disk_address_packet:    
68 sectors:
69         .long   0
70 heads:
71         .long   0
72 cylinders:
73         .word   0
74 sector_start:
75         .byte   0
76 head_start:
77         .byte   0
78 cylinder_start:
79         .word   0
80         /* more space... */
82         . = _start + GRUB_BOOT_MACHINE_BPB_END
84         /*
85          * End of BIOS parameter block.
86          */
88 boot_version:   
89         .byte   GRUB_BOOT_VERSION_MAJOR, GRUB_BOOT_VERSION_MINOR
90 kernel_address:
91         .word   GRUB_BOOT_MACHINE_KERNEL_ADDR
92 kernel_segment:
93         .word   GRUB_BOOT_MACHINE_KERNEL_SEG
94 kernel_sector:
95         .long   1, 0
96 boot_drive:     
97         .byte 0xff      /* the disk to load kernel from */
98                         /* 0xff means use the boot drive */
99 root_drive:
100         .byte 0xff
102 after_BPB:
104 /* general setup */
105         cli             /* we're not safe here! */
107         /*
108          * This is a workaround for buggy BIOSes which don't pass boot
109          * drive correctly. If GRUB is installed into a HDD, check if
110          * DL is masked correctly. If not, assume that the BIOS passed
111          * a bogus value and set DL to 0x80, since this is the only
112          * possible boot drive. If GRUB is installed into a floppy,
113          * this does nothing (only jump).
114          */
115 boot_drive_check:
116         jmp     1f      /* grub-setup may overwrite this jump */
117         testb   $0x80, %dl
118         jnz     1f
119         movb    $0x80, %dl
121         
122         /*
123          * ljmp to the next instruction because some bogus BIOSes
124          * jump to 07C0:0000 instead of 0000:7C00.
125          */
126         ljmp    $0, $ABS(real_start)
128 real_start:     
130         /* set up %ds and %ss as offset from 0 */
131         xorw    %ax, %ax
132         movw    %ax, %ds
133         movw    %ax, %ss
135         /* set up the REAL stack */
136         movw    $GRUB_BOOT_MACHINE_STACK_SEG, %sp
138         sti             /* we're safe again */
140         /*
141          *  Check if we have a forced disk reference here
142          */
143         /* assign root_drive at the same time */
144         movw    ABS(boot_drive), %ax
145         movb    %ah, %dh
146         cmpb    $0xff, %al
147         je      1f
148         movb    %al, %dl
150         /* save drive reference first thing! */
151         pushw   %dx
153         /* print a notification message on the screen */
154         MSG(notification_string)
156         /* set %si to the disk address packet */
157         movw    $ABS(disk_address_packet), %si
158         
159         /* do not probe LBA if the drive is a floppy */
160         testb   $GRUB_BOOT_MACHINE_BIOS_HD_FLAG, %dl
161         jz      chs_mode
162                         
163         /* check if LBA is supported */
164         movb    $0x41, %ah
165         movw    $0x55aa, %bx
166         int     $0x13
168         /* 
169          *  %dl may have been clobbered by INT 13, AH=41H.
170          *  This happens, for example, with AST BIOS 1.04.
171          */
172         popw    %dx
173         pushw   %dx
175         /* use CHS if fails */
176         jc      chs_mode
177         cmpw    $0xaa55, %bx
178         jne     chs_mode
180         andw    $1, %cx
181         jz      chs_mode
182         
183 lba_mode:
184         xorw    %ax, %ax
185         movw    %ax, 4(%si)
187         incw    %ax     
188         /* set the mode to non-zero */
189         movb    %al, -1(%si)
190         
191         /* the blocks */
192         movw    %ax, 2(%si)
194         /* the size and the reserved byte */
195         movw    $0x0010, (%si)
197         /* the absolute address */
198         movl    ABS(kernel_sector), %ebx
199         movl    %ebx, 8(%si)
200         movl    ABS(kernel_sector + 4), %ebx
201         movl    %ebx, 12(%si)
203         /* the segment of buffer address */
204         movw    $GRUB_BOOT_MACHINE_BUFFER_SEG, 6(%si)
207  * BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory
208  *      Call with       %ah = 0x42
209  *                      %dl = drive number
210  *                      %ds:%si = segment:offset of disk address packet
211  *      Return:
212  *                      %al = 0x0 on success; err code on failure
213  */
215         movb    $0x42, %ah
216         int     $0x13
218         /* LBA read is not supported, so fallback to CHS.  */
219         jc      chs_mode
221         movw    $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
222         jmp     copy_buffer
223                 
224 chs_mode:       
225         /*
226          *  Determine the hard disk geometry from the BIOS!
227          *  We do this first, so that LS-120 IDE floppies work correctly.
228          */
229         movb    $8, %ah
230         int     $0x13
231         jnc     final_init
233         /*
234          *  The call failed, so maybe use the floppy probe instead.
235          */
236         testb   $GRUB_BOOT_MACHINE_BIOS_HD_FLAG, %dl
237         jz      floppy_probe
239         /* Nope, we definitely have a hard disk, and we're screwed. */
240         jmp     hd_probe_error
242 final_init:
243         /* set the mode to zero */
244         movzbl  %dh, %eax
245         movb    %ah, -1(%si)
246         
247         /* save number of heads */
248         incw    %ax
249         movl    %eax, 4(%si)
251         movzbw  %cl, %dx
252         shlw    $2, %dx
253         movb    %ch, %al
254         movb    %dh, %ah
256         /* save number of cylinders */
257         incw    %ax
258         movw    %ax, 8(%si)
260         movzbw  %dl, %ax
261         shrb    $2, %al
263         /* save number of sectors */
264         movl    %eax, (%si)
266 setup_sectors:
267         /* load logical sector start (top half) */
268         movl    ABS(kernel_sector + 4), %eax
269         orl     %eax, %eax
270         jnz     geometry_error
271         
272         /* load logical sector start (bottom half) */
273         movl    ABS(kernel_sector), %eax
275         /* zero %edx */
276         xorl    %edx, %edx
278         /* divide by number of sectors */
279         divl    (%si)
281         /* save sector start */
282         movb    %dl, %cl
284         xorw    %dx, %dx        /* zero %edx */
285         divl    4(%si)          /* divide by number of heads */
287         /* do we need too many cylinders? */
288         cmpw    8(%si), %ax
289         jge     geometry_error
291         /* normalize sector start (1-based) */
292         incb    %cl
294         /* low bits of cylinder start */
295         movb    %al, %ch
297         /* high bits of cylinder start */
298         xorb    %al, %al
299         shrw    $2, %ax
300         orb     %al, %cl
302         /* save head start */
303         movb    %dl, %al
305         /* restore %dl */
306         popw    %dx
307         pushw   %dx
309         /* head start */
310         movb    %al, %dh
313  * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
314  *      Call with       %ah = 0x2
315  *                      %al = number of sectors
316  *                      %ch = cylinder
317  *                      %cl = sector (bits 6-7 are high bits of "cylinder")
318  *                      %dh = head
319  *                      %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
320  *                      %es:%bx = segment:offset of buffer
321  *      Return:
322  *                      %al = 0x0 on success; err code on failure
323  */
325         movw    $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
326         movw    %bx, %es        /* load %es segment with disk buffer */
328         xorw    %bx, %bx        /* %bx = 0, put it at 0 in the segment */
329         movw    $0x0201, %ax    /* function 2 */
330         int     $0x13
332         jc      read_error
334         movw    %es, %bx
335         
336 copy_buffer:
337         movw    ABS(kernel_segment), %es
339         /*
340          * We need to save %cx and %si because the startup code in
341          * kernel uses them without initializing them.
342          */
343         pusha
344         pushw   %ds
345         
346         movw    $0x100, %cx
347         movw    %bx, %ds
348         xorw    %si, %si
349         xorw    %di, %di
350         
351         cld
352         
353         rep
354         movsw
356         popw    %ds
357         popa
358         popw    %dx
360         /* boot kernel */
361         jmp     *(kernel_address)
363 /* END OF MAIN LOOP */
366  * BIOS Geometry translation error (past the end of the disk geometry!).
367  */
368 geometry_error:
369         MSG(geometry_error_string)
370         jmp     general_error
373  * Disk probe failure.
374  */
375 hd_probe_error:
376         MSG(hd_probe_error_string)
377         jmp     general_error
380  * Read error on the disk.
381  */
382 read_error:
383         MSG(read_error_string)
385 general_error:
386         MSG(general_error_string)
388 /* go here when you need to stop the machine hard after an error condition */
389         /* tell the BIOS a boot failure, which may result in no effect */
390         int     $0x18
391 stop:   jmp     stop
393 notification_string:    .string "GRUB "
394 geometry_error_string:  .string "Geom"
395 hd_probe_error_string:  .string "Hard Disk"
396 read_error_string:      .string "Read"
397 general_error_string:   .string " Error"
400  * message: write the string pointed to by %si
402  *   WARNING: trashes %si, %ax, and %bx
403  */
405         /*
406          * Use BIOS "int 10H Function 0Eh" to write character in teletype mode
407          *      %ah = 0xe       %al = character
408          *      %bh = page      %bl = foreground color (graphics modes)
409          */
411         movw    $0x0001, %bx
412         movb    $0xe, %ah
413         int     $0x10           /* display a byte */
414 message:
415         lodsb
416         cmpb    $0, %al
417         jne     1b      /* if not end of string, jmp to display */
418         ret
420         /*
421          *  Windows NT breaks compatibility by embedding a magic
422          *  number here.
423          */
425         . = _start + GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC
426 nt_magic:       
427         .long 0
428         .word 0
430         /*
431          *  This is where an MBR would go if on a hard disk.  The code
432          *  here isn't even referenced unless we're on a floppy.  Kinda
433          *  sneaky, huh?
434          */
436 part_start:     
437         . = _start + GRUB_BOOT_MACHINE_PART_START
439 probe_values:
440         .byte   36, 18, 15, 9, 0
442 floppy_probe:
444  *  Perform floppy probe.
445  */
447         movw    $ABS(probe_values-1), %si
449 probe_loop:
450         /* reset floppy controller INT 13h AH=0 */
451         xorw    %ax, %ax
452         int     $0x13
454         incw    %si
455         movb    (%si), %cl
457         /* if number of sectors is 0, display error and die */
458         cmpb    $0, %cl
459         jne     1f
462  * Floppy disk probe failure.
463  */
464         MSG(fd_probe_error_string)
465         jmp     general_error
467 fd_probe_error_string:  .string "Floppy"
470         /* perform read */
471         movw    $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
472         movw    $0x201, %ax
473         movb    $0, %ch
474         movb    $0, %dh
475         int     $0x13
477         /* if error, jump to "probe_loop" */
478         jc      probe_loop
480         /* %cl is already the correct value! */
481         movb    $1, %dh
482         movb    $79, %ch
484         jmp     final_init
486         . = _start + GRUB_BOOT_MACHINE_PART_END
488 /* the last 2 bytes in the sector 0 contain the signature */
489         .word   GRUB_BOOT_MACHINE_SIGNATURE