remove all trailing whitespace
[grub2/phcoder/solaris.git] / boot / i386 / pc / diskboot.S
blob1e817df27598f7dc0c31ed5020579bdaa16c14fa
1 /*
2  *  GRUB  --  GRand Unified Bootloader
3  *  Copyright (C) 1999,2000,2001,2002,2006,2007   Free Software Foundation, Inc.
4  *
5  *  GRUB is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  GRUB is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
17  */
19 #include <grub/machine/boot.h>
22  *  defines for the code go here
23  */
25         /* Absolute addresses
26            This makes the assembler generate the address without support
27            from the linker. (ELF can't relocate 16-bit addresses!) */
28 #define ABS(x) (x-_start+GRUB_BOOT_MACHINE_KERNEL_ADDR)
30         /* Print message string */
31 #ifdef APPLE_CC
32 #define MSG(x)  x ## _abs = ABS(x); mov $x ## _abs, %esi; call message
33 #else
34 #define MSG(x)  movw $ABS(x), %si; call message
35 #endif
37         .file   "diskboot.S"
39         .text
41         /* Tell GAS to generate 16-bit instructions so that this code works
42            in real mode. */
43         .code16
45         .globl  start, _start
46 start:
47 _start:
48         /*
49          * _start is loaded at 0x2000 and is jumped to with
50          * CS:IP 0:0x2000 in kernel.
51          */
53         /*
54          * we continue to use the stack for boot.img and assume that
55          * some registers are set to correct values. See boot.S
56          * for more information.
57          */
59         /* save drive reference first thing! */
60         pushw   %dx
62         /* print a notification message on the screen */
63         pushw   %si
64         MSG(notification_string)
65         popw    %si
67         /* this sets up for the first run through "bootloop" */
68 #ifdef APPLE_CC
69         firstlist_off_abs = ABS (firstlist - GRUB_BOOT_MACHINE_LIST_SIZE)
70         movl    $firstlist_off_abs, %edi
71 #else
72         movw    $ABS(firstlist - GRUB_BOOT_MACHINE_LIST_SIZE), %di
73 #endif
75         /* save the sector number of the second sector in %ebp */
76         movl    (%di), %ebp
78         /* this is the loop for reading the rest of the kernel in */
79 bootloop:
81         /* check the number of sectors to read */
82         cmpw    $0, 8(%di)
84         /* if zero, go to the start function */
85         je      bootit
87 setup_sectors:
88         /* check if we use LBA or CHS */
89         cmpb    $0, -1(%si)
91         /* jump to chs_mode if zero */
92         je      chs_mode
94 lba_mode:
95         /* load logical sector start */
96         movl    (%di), %ebx
97         movl    4(%di), %ecx
99         /* the maximum is limited to 0x7f because of Phoenix EDD */
100         xorl    %eax, %eax
101         movb    $0x7f, %al
103         /* how many do we really want to read? */
104         cmpw    %ax, 8(%di)     /* compare against total number of sectors */
106         /* which is greater? */
107         jg      1f
109         /* if less than, set to total */
110         movw    8(%di), %ax
113         /* subtract from total */
114         subw    %ax, 8(%di)
116         /* add into logical sector start */
117         addl    %eax, (%di)
118         adcl    $0, 4(%di)
120         /* set up disk address packet */
122         /* the size and the reserved byte */
123         movw    $0x0010, (%si)
125         /* the number of sectors */
126         movw    %ax, 2(%si)
128         /* the absolute address */
129         movl    %ebx, 8(%si)
130         movl    %ecx, 12(%si)
132         /* the segment of buffer address */
133         movw    $GRUB_BOOT_MACHINE_BUFFER_SEG, 6(%si)
135         /* save %ax from destruction! */
136         pushw   %ax
138         /* the offset of buffer address */
139         movw    $0, 4(%si)
142  * BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory
143  *      Call with       %ah = 0x42
144  *                      %dl = drive number
145  *                      %ds:%si = segment:offset of disk address packet
146  *      Return:
147  *                      %al = 0x0 on success; err code on failure
148  */
150         movb    $0x42, %ah
151         int     $0x13
153         jc      read_error
155         movw    $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
156         jmp     copy_buffer
158 chs_mode:
159         /* load logical sector start (top half) */
160         movl    4(%di), %eax
161         orl     %eax, %eax
162         jnz     geometry_error
164         /* load logical sector start (bottom half) */
165         movl    (%di), %eax
167         /* zero %edx */
168         xorl    %edx, %edx
170         /* divide by number of sectors */
171         divl    (%si)
173         /* save sector start */
174         movb    %dl, 10(%si)
176         xorl    %edx, %edx      /* zero %edx */
177         divl    4(%si)          /* divide by number of heads */
179         /* save head start */
180         movb    %dl, 11(%si)
182         /* save cylinder start */
183         movw    %ax, 12(%si)
185         /* do we need too many cylinders? */
186         cmpw    8(%si), %ax
187         jge     geometry_error
189         /* determine the maximum sector length of this read */
190         movw    (%si), %ax      /* get number of sectors per track/head */
192         /* subtract sector start */
193         subb    10(%si), %al
195         /* how many do we really want to read? */
196         cmpw    %ax, 8(%di)     /* compare against total number of sectors */
199         /* which is greater? */
200         jg      2f
202         /* if less than, set to total */
203         movw    8(%di), %ax
206         /* subtract from total */
207         subw    %ax, 8(%di)
209         /* add into logical sector start */
210         addl    %eax, (%di)
211         adcl    $0, 4(%di)
214  *  This is the loop for taking care of BIOS geometry translation (ugh!)
215  */
217         /* get high bits of cylinder */
218         movb    13(%si), %dl
220         shlb    $6, %dl         /* shift left by 6 bits */
221         movb    10(%si), %cl    /* get sector */
223         incb    %cl             /* normalize sector (sectors go
224                                         from 1-N, not 0-(N-1) ) */
225         orb     %dl, %cl        /* composite together */
226         movb    12(%si), %ch    /* sector+hcyl in cl, cylinder in ch */
228         /* restore %dx */
229         popw    %dx
230         pushw   %dx
232         /* head number */
233         movb    11(%si), %dh
235         pushw   %ax     /* save %ax from destruction! */
238  * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
239  *      Call with       %ah = 0x2
240  *                      %al = number of sectors
241  *                      %ch = cylinder
242  *                      %cl = sector (bits 6-7 are high bits of "cylinder")
243  *                      %dh = head
244  *                      %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
245  *                      %es:%bx = segment:offset of buffer
246  *      Return:
247  *                      %al = 0x0 on success; err code on failure
248  */
250         movw    $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
251         movw    %bx, %es        /* load %es segment with disk buffer */
253         xorw    %bx, %bx        /* %bx = 0, put it at 0 in the segment */
254         movb    $0x2, %ah       /* function 2 */
255         int     $0x13
257         jc      read_error
259         /* save source segment */
260         movw    %es, %bx
262 copy_buffer:
264         /* load addresses for copy from disk buffer to destination */
265         movw    10(%di), %es    /* load destination segment */
267         /* restore %ax */
268         popw    %ax
270         /* determine the next possible destination address (presuming
271                 512 byte sectors!) */
272         shlw    $5, %ax         /* shift %ax five bits to the left */
273         addw    %ax, 10(%di)    /* add the corrected value to the destination
274                                    address for next time */
276         /* save addressing regs */
277         pusha
278         pushw   %ds
280         /* get the copy length */
281         shlw    $3, %ax
282         movw    %ax, %cx
284         xorw    %di, %di        /* zero offset of destination addresses */
285         xorw    %si, %si        /* zero offset of source addresses */
286         movw    %bx, %ds        /* restore the source segment */
288         cld             /* sets the copy direction to forward */
290         /* perform copy */
291         rep             /* sets a repeat */
292         movsw           /* this runs the actual copy */
294         /* restore addressing regs and print a dot with correct DS
295            (MSG modifies SI, which is saved, and unused AX and BX) */
296         popw    %ds
297         MSG(notification_step)
298         popa
300         /* check if finished with this dataset */
301         cmpw    $0, 8(%di)
302         jne     setup_sectors
304         /* update position to load from */
305         subw    $GRUB_BOOT_MACHINE_LIST_SIZE, %di
307         /* jump to bootloop */
308         jmp     bootloop
310 /* END OF MAIN LOOP */
312 bootit:
313         /* print a newline */
314         MSG(notification_done)
315         popw    %dx     /* this makes sure %dl is our "boot" drive */
316         ljmp    $0, $(GRUB_BOOT_MACHINE_KERNEL_ADDR + 0x200)
320  * BIOS Geometry translation error (past the end of the disk geometry!).
321  */
322 geometry_error:
323         MSG(geometry_error_string)
324         jmp     general_error
327  * Read error on the disk.
328  */
329 read_error:
330         MSG(read_error_string)
332 general_error:
333         MSG(general_error_string)
335 /* go here when you need to stop the machine hard after an error condition */
336 stop:   jmp     stop
338 notification_string:    .asciz "loading"
340 notification_step:      .asciz "."
341 notification_done:      .asciz "\r\n"
343 geometry_error_string:  .asciz "Geom"
344 read_error_string:      .asciz "Read"
345 general_error_string:   .asciz " Error"
348  * message: write the string pointed to by %si
350  *   WARNING: trashes %si, %ax, and %bx
351  */
353         /*
354          * Use BIOS "int 10H Function 0Eh" to write character in teletype mode
355          *      %ah = 0xe       %al = character
356          *      %bh = page      %bl = foreground color (graphics modes)
357          */
359         movw    $0x0001, %bx
360         movb    $0xe, %ah
361         int     $0x10           /* display a byte */
363         incw    %si
364 message:
365         movb    (%si), %al
366         cmpb    $0, %al
367         jne     1b      /* if not end of string, jmp to display */
368         ret
369 lastlist:
372  *  This area is an empty space between the main body of code below which
373  *  grows up (fixed after compilation, but between releases it may change
374  *  in size easily), and the lists of sectors to read, which grows down
375  *  from a fixed top location.
376  */
378         .word 0
379         .word 0
381         . = _start + 0x200 - GRUB_BOOT_MACHINE_LIST_SIZE
383         /* fill the first data listing with the default */
384 blocklist_default_start:
385         /* this is the sector start parameter, in logical sectors from
386            the start of the disk, sector 0 */
387         .long 2, 0
388 blocklist_default_len:
389         /* this is the number of sectors to read the command "install"
390            will fill this up */
391         .word 0
392 blocklist_default_seg:
393         /* this is the segment of the starting address to load the data into */
394         .word (GRUB_BOOT_MACHINE_KERNEL_SEG + 0x20)
396 firstlist:      /* this label has to be after the list data!!! */