2
* GRUB -- GRand Unified Bootloader
3
* Copyright (C) 1999,2000,2001,2002 Free Software Foundation, Inc.
5
* This program 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 2 of the License, or
8
* (at your option) any later version.
10
* This program 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 this program; if not, write to the Free Software
17
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
#include <grub/machine/boot.h>
23
* defines for the code go here
27
This makes the assembler generate the address without support
28
from the linker. (ELF can't relocate 16-bit addresses!) */
29
#define ABS(x) (x-_start+GRUB_BOOT_MACHINE_KERNEL_ADDR)
31
/* Print message string */
32
#define MSG(x) movw $ABS(x), %si; call message
38
/* Tell GAS to generate 16-bit instructions so that this code works
46
* _start is loaded at 0x2000 and is jumped to with
47
* CS:IP 0:0x2000 in kernel.
51
* we continue to use the stack for boot.img and assume that
52
* some registers are set to correct values. See boot.S
53
* for more information.
56
/* save drive reference first thing! */
59
/* print a notification message on the screen */
61
MSG(notification_string)
64
/* this sets up for the first run through "bootloop" */
65
movw $ABS(firstlist - GRUB_BOOT_MACHINE_LIST_SIZE), %di
67
/* save the sector number of the second sector in %ebp */
70
/* this is the loop for reading the rest of the kernel in */
73
/* check the number of sectors to read */
76
/* if zero, go to the start function */
80
/* check if we use LBA or CHS */
83
/* jump to chs_mode if zero */
87
/* load logical sector start */
90
/* the maximum is limited to 0x7f because of Phoenix EDD */
94
/* how many do we really want to read? */
95
cmpw %ax, 4(%di) /* compare against total number of sectors */
97
/* which is greater? */
100
/* if less than, set to total */
104
/* subtract from total */
107
/* add into logical sector start */
110
/* set up disk address packet */
112
/* the size and the reserved byte */
115
/* the number of sectors */
118
/* the absolute address (low 32 bits) */
121
/* the segment of buffer address */
122
movw $GRUB_BOOT_MACHINE_BUFFER_SEG, 6(%si)
124
/* save %ax from destruction! */
130
/* the offset of buffer address */
133
/* the absolute address (high 32 bits) */
138
* BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory
139
* Call with %ah = 0x42
141
* %ds:%si = segment:offset of disk address packet
143
* %al = 0x0 on success; err code on failure
151
movw $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
155
/* load logical sector start (bottom half) */
161
/* divide by number of sectors */
164
/* save sector start */
167
xorl %edx, %edx /* zero %edx */
168
divl 4(%si) /* divide by number of heads */
170
/* save head start */
173
/* save cylinder start */
176
/* do we need too many cylinders? */
180
/* determine the maximum sector length of this read */
181
movw (%si), %ax /* get number of sectors per track/head */
183
/* subtract sector start */
186
/* how many do we really want to read? */
187
cmpw %ax, 4(%di) /* compare against total number of sectors */
190
/* which is greater? */
193
/* if less than, set to total */
197
/* subtract from total */
200
/* add into logical sector start */
204
* This is the loop for taking care of BIOS geometry translation (ugh!)
207
/* get high bits of cylinder */
210
shlb $6, %dl /* shift left by 6 bits */
211
movb 10(%si), %cl /* get sector */
213
incb %cl /* normalize sector (sectors go
214
from 1-N, not 0-(N-1) ) */
215
orb %dl, %cl /* composite together */
216
movb 12(%si), %ch /* sector+hcyl in cl, cylinder in ch */
225
pushw %ax /* save %ax from destruction! */
228
* BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
229
* Call with %ah = 0x2
230
* %al = number of sectors
232
* %cl = sector (bits 6-7 are high bits of "cylinder")
234
* %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
235
* %es:%bx = segment:offset of buffer
237
* %al = 0x0 on success; err code on failure
240
movw $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
241
movw %bx, %es /* load %es segment with disk buffer */
243
xorw %bx, %bx /* %bx = 0, put it at 0 in the segment */
244
movb $0x2, %ah /* function 2 */
249
/* save source segment */
254
/* load addresses for copy from disk buffer to destination */
255
movw 6(%di), %es /* load destination segment */
260
/* determine the next possible destination address (presuming
261
512 byte sectors!) */
262
shlw $5, %ax /* shift %ax five bits to the left */
263
addw %ax, 6(%di) /* add the corrected value to the destination
264
address for next time */
266
/* save addressing regs */
270
/* get the copy length */
274
xorw %di, %di /* zero offset of destination addresses */
275
xorw %si, %si /* zero offset of source addresses */
276
movw %bx, %ds /* restore the source segment */
278
cld /* sets the copy direction to forward */
281
rep /* sets a repeat */
282
movsw /* this runs the actual copy */
284
/* restore addressing regs and print a dot with correct DS
285
(MSG modifies SI, which is saved, and unused AX and BX) */
287
MSG(notification_step)
290
/* check if finished with this dataset */
294
/* update position to load from */
295
subw $GRUB_BOOT_MACHINE_LIST_SIZE, %di
297
/* jump to bootloop */
300
/* END OF MAIN LOOP */
303
/* print a newline */
304
MSG(notification_done)
305
popw %dx /* this makes sure %dl is our "boot" drive */
306
ljmp $0, $(GRUB_BOOT_MACHINE_KERNEL_ADDR + 0x200)
310
* BIOS Geometry translation error (past the end of the disk geometry!).
313
MSG(geometry_error_string)
317
* Read error on the disk.
320
MSG(read_error_string)
323
MSG(general_error_string)
325
/* go here when you need to stop the machine hard after an error condition */
328
notification_string: .string "Loading kernel"
330
notification_step: .string "."
331
notification_done: .string "\r\n"
333
geometry_error_string: .string "Geom"
334
read_error_string: .string "Read"
335
general_error_string: .string " Error"
338
* message: write the string pointed to by %si
340
* WARNING: trashes %si, %ax, and %bx
344
* Use BIOS "int 10H Function 0Eh" to write character in teletype mode
345
* %ah = 0xe %al = character
346
* %bh = page %bl = foreground color (graphics modes)
351
int $0x10 /* display a byte */
357
jne 1b /* if not end of string, jmp to display */
362
* This area is an empty space between the main body of code below which
363
* grows up (fixed after compilation, but between releases it may change
364
* in size easily), and the lists of sectors to read, which grows down
365
* from a fixed top location.
371
. = _start + 0x200 - GRUB_BOOT_MACHINE_LIST_SIZE
373
/* fill the first data listing with the default */
374
blocklist_default_start:
375
/* this is the sector start parameter, in logical sectors from
376
the start of the disk, sector 0 */
378
blocklist_default_len:
379
/* this is the number of sectors to read the command "install"
382
blocklist_default_seg:
383
/* this is the segment of the starting address to load the data into */
384
.word (GRUB_BOOT_MACHINE_KERNEL_SEG + 0x20)
386
firstlist: /* this label has to be after the list data!!! */