2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 1999,2000,2001,2002,2006,2007 Free Software Foundation, Inc.
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.
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.
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/>.
19 #include <grub/machine/boot.h>
22 * defines for the code go here
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 */
32 #define MSG(x) x ## _abs = ABS(x); mov $x ## _abs, %esi; call message
34 #define MSG(x) movw $ABS(x), %si; call message
41 /* Tell GAS to generate 16-bit instructions so that this code works
49 * _start is loaded at 0x2000 and is jumped to with
50 * CS:IP 0:0x2000 in kernel.
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.
59 /* save drive reference first thing! */
62 /* print a notification message on the screen */
64 MSG(notification_string)
67 /* this sets up for the first run through "bootloop" */
69 firstlist_off_abs = ABS (firstlist - GRUB_BOOT_MACHINE_LIST_SIZE)
70 movl $firstlist_off_abs, %edi
72 movw $ABS(firstlist - GRUB_BOOT_MACHINE_LIST_SIZE), %di
75 /* save the sector number of the second sector in %ebp */
78 /* this is the loop for reading the rest of the kernel in */
81 /* check the number of sectors to read */
84 /* if zero, go to the start function */
88 /* check if we use LBA or CHS */
91 /* jump to chs_mode if zero */
95 /* load logical sector start */
99 /* the maximum is limited to 0x7f because of Phoenix EDD */
103 /* how many do we really want to read? */
104 cmpw %ax, 8(%di) /* compare against total number of sectors */
106 /* which is greater? */
109 /* if less than, set to total */
113 /* subtract from total */
116 /* add into logical sector start */
120 /* set up disk address packet */
122 /* the size and the reserved byte */
125 /* the number of sectors */
128 /* the absolute address */
132 /* the segment of buffer address */
133 movw $GRUB_BOOT_MACHINE_BUFFER_SEG, 6(%si)
135 /* save %ax from destruction! */
138 /* the offset of buffer address */
142 * BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory
143 * Call with %ah = 0x42
145 * %ds:%si = segment:offset of disk address packet
147 * %al = 0x0 on success; err code on failure
155 movw $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
159 /* load logical sector start (top half) */
164 /* load logical sector start (bottom half) */
170 /* divide by number of sectors */
173 /* save sector start */
176 xorl %edx, %edx /* zero %edx */
177 divl 4(%si) /* divide by number of heads */
179 /* save head start */
182 /* save cylinder start */
185 /* do we need too many cylinders? */
189 /* determine the maximum sector length of this read */
190 movw (%si), %ax /* get number of sectors per track/head */
192 /* subtract sector start */
195 /* how many do we really want to read? */
196 cmpw %ax, 8(%di) /* compare against total number of sectors */
199 /* which is greater? */
202 /* if less than, set to total */
206 /* subtract from total */
209 /* add into logical sector start */
214 * This is the loop for taking care of BIOS geometry translation (ugh!)
217 /* get high bits of cylinder */
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 */
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
242 * %cl = sector (bits 6-7 are high bits of "cylinder")
244 * %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
245 * %es:%bx = segment:offset of buffer
247 * %al = 0x0 on success; err code on failure
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 */
259 /* save source segment */
264 /* load addresses for copy from disk buffer to destination */
265 movw 10(%di), %es /* load destination segment */
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 */
280 /* get the copy length */
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 */
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) */
297 MSG(notification_step)
300 /* check if finished with this dataset */
304 /* update position to load from */
305 subw $GRUB_BOOT_MACHINE_LIST_SIZE, %di
307 /* jump to bootloop */
310 /* END OF MAIN LOOP */
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!).
323 MSG(geometry_error_string)
327 * Read error on the disk.
330 MSG(read_error_string)
333 MSG(general_error_string)
335 /* go here when you need to stop the machine hard after an error condition */
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
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)
361 int $0x10 /* display a byte */
367 jne 1b /* if not end of string, jmp to display */
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.
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 */
388 blocklist_default_len:
389 /* this is the number of sectors to read the command "install"
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!!! */