2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 1999,2000,2001,2002,2006,2007,2009,2010 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/symbol.h>
20 #include <grub/machine/boot.h>
23 * defines for the code go here
26 #define MSG(x) movw $x, %si; call LOCAL(message)
32 /* Tell GAS to generate 16-bit instructions so that this code works
40 * _start is loaded at 0x2000 and is jumped to with
41 * CS:IP 0:0x2000 in kernel.
45 * we continue to use the stack for boot.img and assume that
46 * some registers are set to correct values. See boot.S
47 * for more information.
50 /* save drive reference first thing! */
53 /* print a notification message on the screen */
55 MSG(notification_string)
58 /* this sets up for the first run through "bootloop" */
59 movw $LOCAL(firstlist), %di
61 /* save the sector number of the second sector in %ebp */
64 /* this is the loop for reading the rest of the kernel in */
67 /* check the number of sectors to read */
70 /* if zero, go to the start function */
74 /* check if we use LBA or CHS */
77 /* use CHS if zero, LBA otherwise */
80 /* load logical sector start */
84 /* the maximum is limited to 0x7f because of Phoenix EDD */
88 /* how many do we really want to read? */
89 cmpw %ax, 8(%di) /* compare against total number of sectors */
91 /* which is greater? */
94 /* if less than, set to total */
98 /* subtract from total */
101 /* add into logical sector start */
105 /* set up disk address packet */
107 /* the size and the reserved byte */
110 /* the number of sectors */
113 /* the absolute address */
117 /* the segment of buffer address */
118 movw $GRUB_BOOT_MACHINE_BUFFER_SEG, 6(%si)
120 /* save %ax from destruction! */
123 /* the offset of buffer address */
127 * BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory
128 * Call with %ah = 0x42
130 * %ds:%si = segment:offset of disk address packet
132 * %al = 0x0 on success; err code on failure
140 movw $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
141 jmp LOCAL(copy_buffer)
144 /* load logical sector start (top half) */
147 jnz LOCAL(geometry_error)
149 /* load logical sector start (bottom half) */
155 /* divide by number of sectors */
158 /* save sector start */
161 xorl %edx, %edx /* zero %edx */
162 divl 4(%si) /* divide by number of heads */
164 /* save head start */
167 /* save cylinder start */
170 /* do we need too many cylinders? */
172 jge LOCAL(geometry_error)
174 /* determine the maximum sector length of this read */
175 movw (%si), %ax /* get number of sectors per track/head */
177 /* subtract sector start */
180 /* how many do we really want to read? */
181 cmpw %ax, 8(%di) /* compare against total number of sectors */
184 /* which is greater? */
187 /* if less than, set to total */
191 /* subtract from total */
194 /* add into logical sector start */
199 * This is the loop for taking care of BIOS geometry translation (ugh!)
202 /* get high bits of cylinder */
205 shlb $6, %dl /* shift left by 6 bits */
206 movb 10(%si), %cl /* get sector */
208 incb %cl /* normalize sector (sectors go
209 from 1-N, not 0-(N-1) ) */
210 orb %dl, %cl /* composite together */
211 movb 12(%si), %ch /* sector+hcyl in cl, cylinder in ch */
220 pushw %ax /* save %ax from destruction! */
223 * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
224 * Call with %ah = 0x2
225 * %al = number of sectors
227 * %cl = sector (bits 6-7 are high bits of "cylinder")
229 * %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
230 * %es:%bx = segment:offset of buffer
232 * %al = 0x0 on success; err code on failure
235 movw $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
236 movw %bx, %es /* load %es segment with disk buffer */
238 xorw %bx, %bx /* %bx = 0, put it at 0 in the segment */
239 movb $0x2, %ah /* function 2 */
244 /* save source segment */
249 /* load addresses for copy from disk buffer to destination */
250 movw 10(%di), %es /* load destination segment */
255 /* determine the next possible destination address (presuming
256 512 byte sectors!) */
257 shlw $5, %ax /* shift %ax five bits to the left */
258 addw %ax, 10(%di) /* add the corrected value to the destination
259 address for next time */
261 /* save addressing regs */
265 /* get the copy length */
269 xorw %di, %di /* zero offset of destination addresses */
270 xorw %si, %si /* zero offset of source addresses */
271 movw %bx, %ds /* restore the source segment */
273 cld /* sets the copy direction to forward */
276 rep /* sets a repeat */
277 movsw /* this runs the actual copy */
279 /* restore addressing regs and print a dot with correct DS
280 (MSG modifies SI, which is saved, and unused AX and BX) */
282 MSG(notification_step)
285 /* check if finished with this dataset */
287 jne LOCAL(setup_sectors)
289 /* update position to load from */
290 subw $GRUB_BOOT_MACHINE_LIST_SIZE, %di
292 /* jump to bootloop */
295 /* END OF MAIN LOOP */
298 /* print a newline */
299 MSG(notification_done)
300 popw %dx /* this makes sure %dl is our "boot" drive */
301 ljmp $0, $(GRUB_BOOT_MACHINE_KERNEL_ADDR + 0x200)
305 * BIOS Geometry translation error (past the end of the disk geometry!).
307 LOCAL(geometry_error):
308 MSG(geometry_error_string)
309 jmp LOCAL(general_error)
312 * Read error on the disk.
315 MSG(read_error_string)
317 LOCAL(general_error):
318 MSG(general_error_string)
320 /* go here when you need to stop the machine hard after an error condition */
321 LOCAL(stop): jmp LOCAL(stop)
323 notification_string: .asciz "loading"
325 notification_step: .asciz "."
326 notification_done: .asciz "\r\n"
328 geometry_error_string: .asciz "Geom"
329 read_error_string: .asciz "Read"
330 general_error_string: .asciz " Error"
333 * message: write the string pointed to by %si
335 * WARNING: trashes %si, %ax, and %bx
339 * Use BIOS "int 10H Function 0Eh" to write character in teletype mode
340 * %ah = 0xe %al = character
341 * %bh = page %bl = foreground color (graphics modes)
346 int $0x10 /* display a byte */
352 jne 1b /* if not end of string, jmp to display */
356 * This area is an empty space between the main body of code below which
357 * grows up (fixed after compilation, but between releases it may change
358 * in size easily), and the lists of sectors to read, which grows down
359 * from a fixed top location.
365 . = _start + 0x200 - GRUB_BOOT_MACHINE_LIST_SIZE
366 LOCAL(firstlist): /* this label has to be before the first list entry!!! */
367 /* fill the first data listing with the default */
368 blocklist_default_start:
369 /* this is the sector start parameter, in logical sectors from
370 the start of the disk, sector 0 */
372 blocklist_default_len:
373 /* this is the number of sectors to read. grub-mkimage
376 blocklist_default_seg:
377 /* this is the segment of the starting address to load the data into */
378 .word (GRUB_BOOT_MACHINE_KERNEL_SEG + 0x20)