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 $(firstlist - GRUB_BOOT_MACHINE_LIST_SIZE), %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
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)
380
firstlist: /* this label has to be after the list data!!! */