]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/boot/i386/pc/boot.S
18c99b372a53d79e11283c6cf6972e2a69c089e1
[Ventoy.git] / GRUB2 / MOD_SRC / grub-2.04 / grub-core / boot / i386 / pc / boot.S
1 /* -*-Asm-*- */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 1999,2000,2001,2002,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
5 *
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <grub/symbol.h>
21 #include <grub/machine/boot.h>
22
23 /*
24 * defines for the code go here
25 */
26
27 /* Print message string */
28 #define MSG(x) movw $x, %si; call LOCAL(message)
29 #define ERR(x) movw $x, %si; jmp LOCAL(error_message)
30
31 .macro floppy
32 part_start:
33
34 LOCAL(probe_values):
35 .byte 36, 18, 15, 9, 0
36
37 LOCAL(floppy_probe):
38 pushw %dx
39 /*
40 * Perform floppy probe.
41 */
42 #ifdef __APPLE__
43 LOCAL(probe_values_minus_one) = LOCAL(probe_values) - 1
44 movw MACRO_DOLLAR(LOCAL(probe_values_minus_one)), %si
45 #else
46 movw MACRO_DOLLAR(LOCAL(probe_values)) - 1, %si
47 #endif
48
49 LOCAL(probe_loop):
50 /* reset floppy controller INT 13h AH=0 */
51 xorw %ax, %ax
52 int MACRO_DOLLAR(0x13)
53
54 incw %si
55 movb (%si), %cl
56
57 /* if number of sectors is 0, display error and die */
58 testb %cl, %cl
59 jnz 1f
60
61 /*
62 * Floppy disk probe failure.
63 */
64 MSG(fd_probe_error_string)
65 jmp LOCAL(general_error)
66
67 /* "Floppy" */
68 fd_probe_error_string: .asciz "Floppy"
69
70 1:
71 /* perform read */
72 movw MACRO_DOLLAR(GRUB_BOOT_MACHINE_BUFFER_SEG), %bx
73 movw %bx, %es
74 xorw %bx, %bx
75 movw MACRO_DOLLAR(0x201), %ax
76 movb MACRO_DOLLAR(0), %ch
77 movb MACRO_DOLLAR(0), %dh
78 int MACRO_DOLLAR(0x13)
79
80 /* if error, jump to "LOCAL(probe_loop)" */
81 jc LOCAL(probe_loop)
82
83 /* %cl is already the correct value! */
84 movb MACRO_DOLLAR(1), %dh
85 movb MACRO_DOLLAR(79), %ch
86
87 jmp LOCAL(final_init)
88 .endm
89
90 .macro scratch
91
92 /* scratch space */
93 mode:
94 .byte 0
95 disk_address_packet:
96 sectors:
97 .long 0
98 heads:
99 .long 0
100 cylinders:
101 .word 0
102 sector_start:
103 .byte 0
104 head_start:
105 .byte 0
106 cylinder_start:
107 .word 0
108 /* more space... */
109 .endm
110
111 .file "boot.S"
112
113 .text
114
115 /* Tell GAS to generate 16-bit instructions so that this code works
116 in real mode. */
117 .code16
118
119 .globl _start, start;
120 _start:
121 start:
122 /*
123 * _start is loaded at 0x7c00 and is jumped to with CS:IP 0:0x7c00
124 */
125
126 /*
127 * Beginning of the sector is compatible with the FAT/HPFS BIOS
128 * parameter block.
129 */
130
131 jmp LOCAL(after_BPB)
132 nop /* do I care about this ??? */
133
134 #ifdef HYBRID_BOOT
135 nop
136 nop
137 nop
138 nop
139 nop
140 nop
141 nop
142 nop
143 nop
144 nop
145 nop
146 nop
147 nop
148
149 nop
150 nop
151 nop
152 nop
153 nop
154 nop
155 nop
156 nop
157
158 nop
159 nop
160 jmp LOCAL(after_BPB)
161 #else
162 /*
163 * This space is for the BIOS parameter block!!!! Don't change
164 * the first jump, nor start the code anywhere but right after
165 * this area.
166 */
167
168 .org GRUB_BOOT_MACHINE_BPB_START
169 .org 4
170 #endif
171 #ifdef HYBRID_BOOT
172 floppy
173 #else
174 scratch
175 #endif
176
177 .org GRUB_BOOT_MACHINE_BPB_END
178 /*
179 * End of BIOS parameter block.
180 */
181
182 LOCAL(kernel_address):
183 .word GRUB_BOOT_MACHINE_KERNEL_ADDR
184
185 #ifndef HYBRID_BOOT
186 .org GRUB_BOOT_MACHINE_KERNEL_SECTOR
187 LOCAL(kernel_sector):
188 .long 1
189 LOCAL(kernel_sector_high):
190 .long 0
191 #endif
192
193 .org GRUB_BOOT_MACHINE_BOOT_DRIVE
194 boot_drive:
195 .byte 0xff /* the disk to load kernel from */
196 /* 0xff means use the boot drive */
197
198 LOCAL(after_BPB):
199
200 /* general setup */
201 cli /* we're not safe here! */
202
203 /*
204 * This is a workaround for buggy BIOSes which don't pass boot
205 * drive correctly. If GRUB is installed into a HDD, check if
206 * DL is masked correctly. If not, assume that the BIOS passed
207 * a bogus value and set DL to 0x80, since this is the only
208 * possible boot drive. If GRUB is installed into a floppy,
209 * this does nothing (only jump).
210 */
211 .org GRUB_BOOT_MACHINE_DRIVE_CHECK
212 boot_drive_check:
213 jmp 3f /* grub-setup may overwrite this jump */
214 testb $0x80, %dl
215 jz 2f
216 3:
217 /* Ignore %dl different from 0-0x0f and 0x80-0x8f. */
218 testb $0x70, %dl
219 jz 1f
220 2:
221 movb $0x80, %dl
222 1:
223 /*
224 * ljmp to the next instruction because some bogus BIOSes
225 * jump to 07C0:0000 instead of 0000:7C00.
226 */
227 ljmp $0, $real_start
228
229 real_start:
230
231 /* set up %ds and %ss as offset from 0 */
232 xorw %ax, %ax
233 movw %ax, %ds
234 movw %ax, %ss
235
236 /* set up the REAL stack */
237 movw $GRUB_BOOT_MACHINE_STACK_SEG, %sp
238
239 sti /* we're safe again */
240
241 /*
242 * Check if we have a forced disk reference here
243 */
244 movb boot_drive, %al
245 cmpb $0xff, %al
246 je 1f
247 movb %al, %dl
248 1:
249 /* save drive reference first thing! */
250 pushw %dx
251
252 /* print a notification message on the screen */
253 MSG(notification_string)
254
255 /* set %si to the disk address packet */
256 movw $disk_address_packet, %si
257
258 /* check if LBA is supported */
259 movb $0x41, %ah
260 movw $0x55aa, %bx
261 int $0x13
262
263 /*
264 * %dl may have been clobbered by INT 13, AH=41H.
265 * This happens, for example, with AST BIOS 1.04.
266 */
267 popw %dx
268 pushw %dx
269
270 /* use CHS if fails */
271 jc LOCAL(chs_mode)
272 cmpw $0xaa55, %bx
273 jne LOCAL(chs_mode)
274
275 andw $1, %cx
276 jz LOCAL(chs_mode)
277
278 LOCAL(lba_mode):
279 xorw %ax, %ax
280 movw %ax, 4(%si)
281
282 incw %ax
283 /* set the mode to non-zero */
284 movb %al, -1(%si)
285
286 /* the blocks */
287 movw %ax, 2(%si)
288
289 /* the size and the reserved byte */
290 movw $0x0010, (%si)
291
292 /* the absolute address */
293 movl LOCAL(kernel_sector), %ebx
294 movl %ebx, 8(%si)
295 movl LOCAL(kernel_sector_high), %ebx
296 movl %ebx, 12(%si)
297
298 /* the segment of buffer address */
299 movw $GRUB_BOOT_MACHINE_BUFFER_SEG, 6(%si)
300
301 /*
302 * BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory
303 * Call with %ah = 0x42
304 * %dl = drive number
305 * %ds:%si = segment:offset of disk address packet
306 * Return:
307 * %al = 0x0 on success; err code on failure
308 */
309
310 movb $0x42, %ah
311 int $0x13
312
313 /* LBA read is not supported, so fallback to CHS. */
314 jc LOCAL(chs_mode)
315
316 movw $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
317 jmp LOCAL(copy_buffer)
318
319 LOCAL(chs_mode):
320 /*
321 * Determine the hard disk geometry from the BIOS!
322 * We do this first, so that LS-120 IDE floppies work correctly.
323 */
324 movb $8, %ah
325 int $0x13
326 jnc LOCAL(final_init)
327
328 popw %dx
329 /*
330 * The call failed, so maybe use the floppy probe instead.
331 */
332 testb %dl, %dl
333 jnb LOCAL(floppy_probe)
334
335 /* Nope, we definitely have a hard disk, and we're screwed. */
336 ERR(hd_probe_error_string)
337
338 LOCAL(final_init):
339 /* set the mode to zero */
340 movzbl %dh, %eax
341 movb %ah, -1(%si)
342
343 /* save number of heads */
344 incw %ax
345 movl %eax, 4(%si)
346
347 movzbw %cl, %dx
348 shlw $2, %dx
349 movb %ch, %al
350 movb %dh, %ah
351
352 /* save number of cylinders */
353 incw %ax
354 movw %ax, 8(%si)
355
356 movzbw %dl, %ax
357 shrb $2, %al
358
359 /* save number of sectors */
360 movl %eax, (%si)
361
362 setup_sectors:
363 /* load logical sector start (top half) */
364 movl LOCAL(kernel_sector_high), %eax
365
366 orl %eax, %eax
367 jnz LOCAL(geometry_error)
368
369 /* load logical sector start (bottom half) */
370 movl LOCAL(kernel_sector), %eax
371
372 /* zero %edx */
373 xorl %edx, %edx
374
375 /* divide by number of sectors */
376 divl (%si)
377
378 /* save sector start */
379 movb %dl, %cl
380
381 xorw %dx, %dx /* zero %edx */
382 divl 4(%si) /* divide by number of heads */
383
384 /* do we need too many cylinders? */
385 cmpw 8(%si), %ax
386 jge LOCAL(geometry_error)
387
388 /* normalize sector start (1-based) */
389 incb %cl
390
391 /* low bits of cylinder start */
392 movb %al, %ch
393
394 /* high bits of cylinder start */
395 xorb %al, %al
396 shrw $2, %ax
397 orb %al, %cl
398
399 /* save head start */
400 movb %dl, %al
401
402 /* restore %dl */
403 popw %dx
404
405 /* head start */
406 movb %al, %dh
407
408 /*
409 * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
410 * Call with %ah = 0x2
411 * %al = number of sectors
412 * %ch = cylinder
413 * %cl = sector (bits 6-7 are high bits of "cylinder")
414 * %dh = head
415 * %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
416 * %es:%bx = segment:offset of buffer
417 * Return:
418 * %al = 0x0 on success; err code on failure
419 */
420
421 movw $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
422 movw %bx, %es /* load %es segment with disk buffer */
423
424 xorw %bx, %bx /* %bx = 0, put it at 0 in the segment */
425 movw $0x0201, %ax /* function 2 */
426 int $0x13
427
428 jc LOCAL(read_error)
429
430 movw %es, %bx
431
432 LOCAL(copy_buffer):
433 /*
434 * We need to save %cx and %si because the startup code in
435 * kernel uses them without initializing them.
436 */
437 pusha
438 pushw %ds
439
440 movw $0x100, %cx
441 movw %bx, %ds
442 xorw %si, %si
443 movw $GRUB_BOOT_MACHINE_KERNEL_ADDR, %di
444 movw %si, %es
445
446 cld
447
448 rep
449 movsw
450
451 popw %ds
452 popa
453
454 /* boot kernel */
455 jmp *(LOCAL(kernel_address))
456
457 /* END OF MAIN LOOP */
458
459 /*
460 * BIOS Geometry translation error (past the end of the disk geometry!).
461 */
462 LOCAL(geometry_error):
463 ERR(geometry_error_string)
464
465 /*
466 * Read error on the disk.
467 */
468 LOCAL(read_error):
469 movw $read_error_string, %si
470 LOCAL(error_message):
471 call LOCAL(message)
472 LOCAL(general_error):
473 MSG(general_error_string)
474
475 /* go here when you need to stop the machine hard after an error condition */
476 /* tell the BIOS a boot failure, which may result in no effect */
477 int $0x18
478 LOCAL(stop):
479 jmp LOCAL(stop)
480
481 ventoy_uuid: .ascii "XXXXXXXXXXXXXXXX"
482 notification_string: .asciz "GR"
483 geometry_error_string: .asciz "Ge"
484 hd_probe_error_string: .asciz "HD"
485 read_error_string: .asciz "Rd"
486 general_error_string: .asciz " Er\r\n"
487
488
489
490 /*
491 * message: write the string pointed to by %si
492 *
493 * WARNING: trashes %si, %ax, and %bx
494 */
495
496 /*
497 * Use BIOS "int 10H Function 0Eh" to write character in teletype mode
498 * %ah = 0xe %al = character
499 * %bh = page %bl = foreground color (graphics modes)
500 */
501 1:
502 movw $0x0001, %bx
503 movb $0xe, %ah
504 int $0x10 /* display a byte */
505 LOCAL(message):
506 lodsb
507 cmpb $0, %al
508 jne 1b /* if not end of string, jmp to display */
509 ret
510
511 /*
512 * Windows NT breaks compatibility by embedding a magic
513 * number here.
514 */
515
516 #ifdef HYBRID_BOOT
517 .org 0x1b0
518 LOCAL(kernel_sector):
519 .long 1
520 LOCAL(kernel_sector_high):
521 .long 0
522 #endif
523 .org GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC
524 nt_magic:
525 .long 0
526 .word 0
527
528 /*
529 * This is where an MBR would go if on a hard disk. The code
530 * here isn't even referenced unless we're on a floppy. Kinda
531 * sneaky, huh?
532 */
533
534 .org GRUB_BOOT_MACHINE_PART_START
535
536 #ifndef HYBRID_BOOT
537 floppy
538 #else
539 scratch
540 #endif
541
542 .org GRUB_BOOT_MACHINE_PART_END
543
544 /* the last 2 bytes in the sector 0 contain the signature */
545 .word GRUB_BOOT_MACHINE_SIGNATURE