2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 1999,2000,2001,2002,2004 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.
22 * Note: These functions defined in this file may be called from C.
23 * Be careful of that you must not modify some registers. Quote
24 * from gcc-2.95.2/gcc/config/i386/i386.h:
26 1 for registers not available across function calls.
27 These must include the FIXED_REGISTERS and also any
28 registers that can be used without being saved.
29 The latter must include the registers where values are returned
30 and the register where structure-value addresses are passed.
31 Aside from that, you can include as many other registers as you like.
33 ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg
34 { 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
42 # define ABS(x) ((x) - EXT_C(main) + 0x2200)
44 # define ABS(x) ((x) - EXT_C(main) + 0x8200)
51 /* Tell GAS to generate 16-bit instructions so that this code works
57 * In stage2, do not link start.S with the rest of the source
58 * files directly, so define the start symbols here just to
59 * force ld quiet. These are not referred anyway.
64 #endif /* ! STAGE1_5 */
68 * Guarantee that "main" is loaded at 0x0:0x8200 in stage2 and
69 * at 0x0:0x2200 in stage1.5.
71 ljmp $0, $ABS(codestart)
74 * Compatibility version number
76 * These MUST be at byte offset 6 and 7 of the executable
80 .byte COMPAT_VERSION_MAJOR, COMPAT_VERSION_MINOR
83 * This is a special data area 8 bytes from the beginning.
88 VARIABLE(install_partition)
90 /* This variable is here only because of a historical reason. */
91 VARIABLE(saved_entryno)
97 VARIABLE(version_string)
101 .string "/boot/grub/menu.lst"
104 .string "/boot/grub/stage2"
105 #endif /* STAGE1_5 */
108 * Leave some breathing room for the config file name.
111 . = EXT_C(main) + 0x60
117 * installgrub will place the rest of the fake
118 * multiboot header here.
120 .= EXT_C(main) + 0x140
121 /* the real mode code continues... */
123 cli /* we're not safe here! */
125 /* set up %ds, %ss, and %es */
131 #ifndef SUPPORT_DISKLESS
133 * Save the sector number of the second sector (i.e. this sector)
134 * in INSTALL_SECOND_SECTOR. See also "stage2/start.S".
136 ADDR32 movl %ebp, EXT_C(install_second_sector)
139 /* set up the real mode/BIOS stack */
143 sti /* we're safe again */
145 #ifndef SUPPORT_DISKLESS
146 /* save boot drive reference */
147 ADDR32 movb %dl, EXT_C(boot_drive)
149 /* reset disk system (%ah = 0) */
153 /* transition to protected mode */
154 DATA32 call EXT_C(real_to_prot)
156 /* The ".code32" directive takes GAS out of 16-bit mode. */
159 /* clean out the bss */
161 /* set %edi to the bss starting address */
162 #if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL)
163 movl $__bss_start, %edi
164 #elif defined(HAVE_USCORE_EDATA_SYMBOL)
166 #elif defined(HAVE_EDATA_SYMBOL)
170 /* set %ecx to the bss end */
171 #if defined(HAVE_END_SYMBOL)
173 #elif defined(HAVE_USCORE_END_SYMBOL)
177 /* compute the bss length */
183 /* set the direction */
191 * Call the start of main body of C code, which does some
192 * of it's own initialization before transferring to "cmain".
194 call EXT_C(init_bios_info)
198 * This call is special... it never returns... in fact it should simply
199 * hang at this point!
203 call EXT_C(prot_to_real)
206 * This next part is sort of evil. It takes advantage of the
207 * byte ordering on the x86 to work in either 16-bit or 32-bit
208 * mode, so think about it before changing it.
217 /**************************************************************************
218 UNDI_CALL - wrapper around real-mode UNDI API calls
219 **************************************************************************/
227 movw 8(%ebp),%cx /* Seg:off addr of undi_call_info_t struct */
228 movw 12(%ebp),%dx /* Pass to 16-bit code in %cx:%dx */
230 call EXT_C(prot_to_real)
233 movw %cx,%es /* Seg:off addr of undi_call_info_t struct */
234 movw %dx,%bx /* into %es:%bx */
236 movw %es:8(%bx),%ax /* Transfer contents of undi_call_info_t */
237 pushw %ax /* structure to the real-mode stack */
243 lcall *%es:0(%bx) /* Do the UNDI call */
244 cld /* Don't know whether or not we need this */
245 /* but pxelinux includes it for some reason, */
246 /* so we put it in just in case. */
248 popw %cx /* Tidy up the stack */
251 movw %ax,%cx /* Return %ax via %cx */
253 DATA32 call EXT_C(real_to_prot)
256 xorl %eax,%eax /* %ax is returned via %cx */
265 /**************************************************************************
266 UNDI_IRQ_HANDLER - UNDI IRQ handler: calls PXENV_UNDI_ISR and send EOI
267 NOTE: For some reason, this handler needs to be aligned. Else, the
268 undi driver won't get the trigger count on some platforms.
269 **************************************************************************/
271 ENTRY(_undi_irq_handler)
276 call 1f /* Position-independent access to */
277 1: popw %bx /* various locations. */
278 pushw %bx /* save for after UNDI call */
280 /* set funcflag to PXENV_UNDI_ISR_IN_START */
281 movw $1,%cs:(pxenv_undi_isr-1b+2)(%bx)
283 /* push pxenv_undi_isr struct on stack */
284 movl $(ABS(pxenv_undi_isr)),%eax
286 shrl $4,%eax /* get segment */
288 andw $0xf,%cx /* get offset */
290 movw $0x14,%ax /* opcode PXENV_UNDI_ISR */
293 lcall *%cs:(pxenv_entrypointsp-1b)(%bx) /* Do the UNDI call */
294 cld /* Don't know whether or not we need this */
295 /* but pxelinux includes it for some reason, */
296 /* so we put it in just in case. */
297 popw %cx /* Tidy up the stack */
301 popw %bx /* restore old position reg */
303 cmpw $0,%ax /* did the UNDI call succeed? */
305 movw %cs:(pxenv_undi_isr-1b+2)(%bx),%ax
306 cmpw $0,%ax /* is this our interrupt? */
309 /* send EOI -- non specific for now */
310 movw $0x20,%ax /* ICR_EOI_NON_SPECIFIC */
311 movb %cs:(pxenv_undi_irq-1b),%cl
314 outb $0xa0 /* PIC2_ICR */
315 2: outb $0x20 /* PIC1_ICR */
317 /* increment trigger count */
318 incw %cs:(EXT_C(_undi_irq_trigger_count)-1b)(%bx)
320 /* restore other registers */
325 ENTRY(_undi_irq_trigger_count)
326 undi_irq_trigger_count:
328 ENTRY(_undi_irq_chain_to)
330 ENTRY(_undi_irq_chain)
332 ENTRY(_pxenv_undi_irq)
335 ENTRY(_pxenv_undi_entrypointsp)
338 .word 0 /* segment */
341 .word 0 /* funcflag */
342 .long 0 /* struct padding not used by ISR */
351 * Stops the floppy drive from spinning, so that other software is
352 * jumped to with a known state.
356 call EXT_C(prot_to_real)
360 DATA32 call EXT_C(real_to_prot)
368 * Reboot the system. At the moment, rely on BIOS.
371 call EXT_C(prot_to_real)
376 ljmp $0xFFFF, $0x0000
380 * grub_halt(int no_apm)
382 * Halt the system, using APM if possible. If NO_APM is true, don't use
383 * APM even if it is available.
386 /* get the argument */
393 call EXT_C(prot_to_real)
401 /* don't check %bx for buggy BIOSes... */
403 /* disconnect APM first */
414 /* set APM protocol level - 1.1 or bust. (this covers APM 1.2 also) */
421 /* set the power state to off */
427 /* shouldn't reach here */
432 * track_int13(int drive)
434 * Track the int13 handler to probe I/O address space.
443 /* copy the original int13 handler segment:offset */
446 movl %eax, track_int13_addr
448 /* replace the int1 handler */
451 movl $ABS(int1_handler), %eax
454 /* read the MBR to call int13 successfully */
457 call EXT_C(prot_to_real)
460 movw $SCRATCHSEG, %ax
466 /* save FLAGS on the stack to emulate int13 */
469 /* set the TF flag */
470 /* FIXME: this can be simplified not to use AX */
479 .byte 0x9a /* lcall */
482 .word 0 /* segment */
484 /* TF is cleared here automatically */
486 DATA32 call EXT_C(real_to_prot)
489 /* restore the int1 handler */
501 * Check if the next instruction is I/O, and if this is true, add the
502 * port into the io map.
504 * Note: Probably this will make the execution of int13 very slow.
506 * Note2: In this implementation, all we can know is I/O-mapped I/O. It
507 * is impossible to detect memory-mapped I/O.
525 /* examine the next instruction */
527 /* skip this code if it is a prefix */
545 3: /* check if this code is out* or in* */
553 4: /* in? or out? (register operand version) */
559 6: /* in? or out? (immediate operand version) */
565 7: /* immediate has a port */
569 5: /* %dx has a port */
571 /* set %ds to zero */
575 /* set %si to the io map */
576 movw $ABS(EXT_C(io_map)), %si
579 9: /* check if the io map already has the port */
581 /* check if this is the end */
584 /* check if this matches the port */
587 /* if so, leave from this handler */
590 1: /* check for the buffer overrun */
591 cmpw $(ABS(EXT_C(io_map)) + (IO_MAP_SIZE + 1) * 2), %si
593 /* add the port into the io map */
596 8: /* restore registers */
608 .space (IO_MAP_SIZE + 1) * 2
612 * set_int15_handler(void)
614 * Set up int15_handler.
616 ENTRY(set_int15_handler)
619 /* save the original int15 handler */
622 movw %ax, ABS(int15_offset)
624 movw %ax, ABS(int15_segment)
626 /* save the new int15 handler */
627 movw $ABS(int15_handler), %ax
637 * unset_int15_handler(void)
639 * Restore the original int15 handler
641 ENTRY(unset_int15_handler)
644 /* check if int15_handler is set */
646 movw $ABS(int15_handler), %ax
653 /* restore the original */
654 movw ABS(int15_offset), %ax
656 movw ABS(int15_segment), %ax
665 * Translate a key code to another.
667 * Note: This implementation cannot handle more than one length
668 * scancodes (such as Right Ctrl).
672 /* if non-carrier, ignore it */
678 /* E0 and E1 are special */
682 /* this flag is actually the machine code (je or jmp) */
694 /* save bits 0-6 of %al in %dl */
697 /* save the highest bit in %bl */
703 /* set %si to the key map */
704 movw $ABS(EXT_C(bios_key_map)), %si
706 /* find the key code from the key map */
709 /* check if this is the end */
712 /* check if this matches the key code */
715 /* if so, perform the mapping */
721 /* make sure that CF is set */
723 /* restore other registers */
732 /* tricky: jmp (0x74) <-> je (0xeb) */
733 xorb $(0x74 ^ 0xeb), ABS(int15_skip_flag)
735 /* just cascade to the original */
738 int15_offset: .word 0
739 int15_segment: .word 0
745 .space (KEY_MAP_SIZE + 1) * 2
749 * set_int13_handler(map)
751 * Copy MAP to the drive map and set up int13_handler.
753 ENTRY(set_int13_handler)
760 /* copy MAP to the drive map */
761 movl $(DRIVE_MAP_SIZE * 2), %ecx
762 movl $ABS(drive_map), %edi
768 /* save the original int13 handler */
771 movw %ax, ABS(int13_offset)
773 movw %ax, ABS(int13_segment)
775 /* decrease the lower memory size and set it to the BIOS memory */
781 /* compute the segment */
784 /* save the new int13 handler */
790 /* copy int13_handler to the reserved area */
793 movl $ABS(int13_handler), %esi
794 movl $(int13_handler_end - int13_handler), %ecx
805 * Map a drive to another drive.
817 /* set %si to the drive map */
818 movw $(drive_map - int13_handler), %si
819 /* find the drive number from the drive map */
823 /* check if this is the end */
826 /* check if this matches the drive number */
829 /* if so, perform the mapping */
834 /* save %ax in the stack */
836 /* simulate the interrupt call */
838 /* set %ax and %bp to the original values */
843 int13_offset: .word 0
844 int13_segment: .word 0
851 /* set the flags in the stack to the value returned by int13 */
854 /* check if should map the drive number */
860 /* check if the mapping was performed */
864 /* perform the mapping */
873 drive_map: .space (DRIVE_MAP_SIZE + 1) * 2
880 * chain_stage1(segment, offset, part_table_addr)
882 * This starts another stage1 loader, at segment:offset.
886 /* no need to save anything, just use %esp */
888 /* store %ESI, presuming %ES is 0 */
891 /* store new offset */
895 /* store new segment */
899 /* set up to pass boot drive */
900 movb EXT_C(boot_drive), %dl
902 call EXT_C(prot_to_real)
905 #ifdef ABSOLUTE_WITHOUT_ASTERISK
906 DATA32 ADDR32 ljmp (offset)
908 DATA32 ADDR32 ljmp *(offset)
911 #endif /* STAGE1_5 */
916 * chain_stage2(segment, offset, second_sector)
918 * This starts another stage2 loader, at segment:offset. It presumes
919 * that the other one starts with this same "asm.S" file, and passes
920 * parameters by writing the embedded install variables.
924 /* no need to save anything, just use %esp */
926 /* store new offset */
931 /* store new segment */
936 /* generate linear address */
939 /* set up to pass the partition where stage2 is located in */
940 movl EXT_C(current_partition), %eax
941 movl %eax, (EXT_C(install_partition)-EXT_C(main))(%ebx)
943 /* set up to pass the drive where stage2 is located in */
944 movb EXT_C(current_drive), %dl
946 /* set up to pass the second sector of stage2 */
949 call EXT_C(prot_to_real)
954 #ifdef ABSOLUTE_WITHOUT_ASTERISK
955 DATA32 ADDR32 ljmp (offset)
957 DATA32 ADDR32 ljmp *(offset)
961 #endif /* STAGE1_5 */
964 * These next two routines, "real_to_prot" and "prot_to_real" are structured
965 * in a very specific way. Be very careful when changing them.
967 * NOTE: Use of either one messes up %eax and %ebp.
974 /* load the GDT register */
975 DATA32 ADDR32 lgdt gdtdesc
977 /* turn on protected mode */
982 /* jump to relocation, flush prefetch queue, and reload %cs */
983 DATA32 ljmp $PROT_MODE_CSEG, $protcseg
986 * The ".code32" directive only works in GAS, the GNU assembler!
987 * This gets out of "16-bit" mode.
992 /* reload other segment registers */
993 movw $PROT_MODE_DSEG, %ax
1000 /* put the return address in a known safe location */
1004 /* get protected mode stack */
1005 movl protstack, %eax
1009 /* get return address onto the right stack */
1016 /* return on the old (or initialized) stack! */
1021 /* just in case, set GDT */
1024 /* save the protected mode stack */
1026 movl %eax, protstack
1028 /* get the return address */
1032 /* set up new stack */
1033 movl $STACKOFF, %eax
1037 /* set up segment limits */
1038 movw $PSEUDO_RM_DSEG, %ax
1045 /* this might be an extra step */
1046 ljmp $PSEUDO_RM_CSEG, $tmpcseg /* jump to a 16 bit segment */
1051 /* clear the PE bit of CR0 */
1053 andl $CR0_PE_OFF, %eax
1056 /* flush prefetch queue, reload %cs */
1057 DATA32 ljmp $0, $realcseg
1060 /* we are in real mode now
1061 * set up the real mode segment registers : DS, SS, ES
1072 /* restore interrupts */
1075 /* return on new stack! */
1082 * int biosdisk_int13_extensions (int ax, int drive, void *dap)
1084 * Call IBM/MS INT13 Extensions (int 13 %ax=AX) for DRIVE. DAP
1085 * is passed for disk address packet. If an error occurs, return
1086 * non-zero, otherwise zero.
1089 ENTRY(biosdisk_int13_extensions)
1096 /* compute the address of disk_address_packet */
1097 movl 0x10(%ebp), %eax
1101 movw %ax, %cx /* save the segment to cx */
1107 /* enter real mode */
1108 call EXT_C(prot_to_real)
1113 int $0x13 /* do the operation */
1114 movb %ah, %dl /* save return value */
1115 /* clear the data segment */
1118 /* back to protected mode */
1119 DATA32 call EXT_C(real_to_prot)
1122 movb %dl, %al /* return value in %eax */
1131 * int biosdisk_standard (int ah, int drive, int coff, int hoff, int soff,
1132 * int nsec, int segment)
1134 * Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write
1135 * NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs,
1136 * return non-zero, otherwise zero.
1139 ENTRY(biosdisk_standard)
1147 /* set up CHS information */
1148 movl 0x10(%ebp), %eax
1150 movb 0x18(%ebp), %al
1154 movb 0x14(%ebp), %dh
1158 movw 0x20(%ebp), %bx
1159 /* save nsec and ah to %di */
1161 movb 0x1c(%ebp), %al
1163 /* enter real mode */
1164 call EXT_C(prot_to_real)
1169 movw $3, %si /* attempt at least three times */
1173 int $0x13 /* do the operation */
1174 jnc 2f /* check if successful */
1176 movb %ah, %bl /* save return value */
1177 /* if fail, reset the disk system */
1187 /* back to protected mode */
1188 DATA32 call EXT_C(real_to_prot)
1191 movb %bl, %al /* return value in %eax */
1202 * int check_int13_extensions (int drive)
1204 * Check if LBA is supported for DRIVE. If it is supported, then return
1205 * the major version of extensions, otherwise zero.
1208 ENTRY(check_int13_extensions)
1216 /* enter real mode */
1217 call EXT_C(prot_to_real)
1222 int $0x13 /* do the operation */
1224 /* check the result */
1229 movb %ah, %bl /* save the major version into %bl */
1231 /* check if AH=0x42 is supported if FORCE_LBA is zero */
1232 movb EXT_C(force_lba), %al
1241 /* back to protected mode */
1242 DATA32 call EXT_C(real_to_prot)
1245 movb %bl, %al /* return value in %eax */
1254 * int get_diskinfo_standard (int drive, unsigned long *cylinders,
1255 * unsigned long *heads, unsigned long *sectors)
1257 * Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
1258 * error occurs, then return non-zero, otherwise zero.
1261 ENTRY(get_diskinfo_standard)
1270 /* enter real mode */
1271 call EXT_C(prot_to_real)
1275 int $0x13 /* do the operation */
1276 /* check if successful */
1279 /* bogus BIOSes may not return an error number */
1280 testb $0x3f, %cl /* 0 sectors means no disk */
1281 jnz 1f /* if non-zero, then succeed */
1282 /* XXX 0x60 is one of the unused error numbers */
1285 movb %ah, %bl /* save return value in %bl */
1286 /* back to protected mode */
1287 DATA32 call EXT_C(real_to_prot)
1291 leal 0x8(%esp), %ebp
1295 incl %eax /* the number of heads is counted from zero */
1296 movl 0x10(%ebp), %edi
1303 movl 0x14(%ebp), %edi
1310 incl %eax /* the number of cylinders is
1311 counted from zero */
1312 movl 0xc(%ebp), %edi
1316 movb %bl, %al /* return value in %eax */
1327 * int get_diskinfo_floppy (int drive, unsigned long *cylinders,
1328 * unsigned long *heads, unsigned long *sectors)
1330 * Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
1331 * error occurs, then return non-zero, otherwise zero.
1334 ENTRY(get_diskinfo_floppy)
1343 /* enter real mode */
1344 call EXT_C(prot_to_real)
1347 /* init probe value */
1348 movl $probe_values-1, %esi
1351 int $0x13 /* reset floppy controller */
1355 cmpb $0, %cl /* probe failed if zero */
1359 movw $SCRATCHSEG, %ax
1367 /* FIXME: Read from floppy may fail even if the geometry is correct.
1368 So should retry at least three times. */
1369 jc 1b /* next value */
1375 .byte 36, 18, 15, 9, 0
1378 /* back to protected mode */
1379 DATA32 call EXT_C(real_to_prot)
1383 leal 0x8(%esp), %ebp
1386 movl 0xc(%ebp), %eax
1390 movl 0x10(%ebp), %eax
1394 movl 0x14(%ebp), %eax
1398 /* return value in %eax */
1402 incl %eax /* %eax = 1 (non-zero) */
1412 /* Source files are splitted, as they have different copyrights. */
1414 # include "setjmp.S"
1416 #endif /* ! STAGE1_5 */
1421 /* get_code_end() : return the address of the end of the code
1422 * This is here so that it can be replaced by asmstub.c.
1425 /* will be the end of the bss */
1426 # if defined(HAVE_END_SYMBOL)
1428 # elif defined(HAVE_USCORE_END_SYMBOL)
1431 shrl $2, %eax /* Round up to the next word. */
1435 #endif /* ! STAGE1_5 */
1439 * get_memsize(i) : return the memory size in KB. i == 0 for conventional
1440 * memory, i == 1 for extended memory
1441 * BIOS call "INT 12H" to get conventional memory size
1442 * BIOS call "INT 15H, AH=88H" to get extended memory size
1443 * Both have the return value in AX.
1453 call EXT_C(prot_to_real) /* enter real mode */
1469 DATA32 call EXT_C(real_to_prot)
1482 * get_eisamemsize() : return packed EISA memory map, lower 16 bits is
1483 * memory between 1M and 16M in 1K parts, upper 16 bits is
1484 * memory above 16M in 64K parts. If error, return -1.
1485 * BIOS call "INT 15H, AH=E801H" to get EISA memory map,
1486 * AX = memory between 1M and 16M in 1K parts.
1487 * BX = memory above 16M in 64K parts.
1491 ENTRY(get_eisamemsize)
1495 call EXT_C(prot_to_real) /* enter real mode */
1504 DATA32 call EXT_C(real_to_prot)
1507 movl $0xFFFFFFFF, %eax
1520 * get_mmap_entry(addr, cont) : address and old continuation value (zero to
1521 * start), for the Query System Address Map BIOS call.
1523 * Sets the first 4-byte int value of "addr" to the size returned by
1524 * the call. If the call fails, sets it to zero.
1526 * Returns: new (non-zero) continuation value, 0 if done.
1528 * NOTE: Currently hard-coded for a maximum buffer length of 1024.
1531 ENTRY(get_mmap_entry)
1537 /* place address (+4) in ES:DI */
1538 movl 0x14(%esp), %eax
1545 /* set continuation value */
1546 movl 0x18(%esp), %ebx
1548 /* set default maximum buffer size */
1551 /* set EDX to 'SMAP' */
1552 movl $0x534d4150, %edx
1554 call EXT_C(prot_to_real) /* enter real mode */
1563 cmpl $0x534d4150, %eax
1578 DATA32 call EXT_C(real_to_prot)
1581 /* write length of buffer (zero if error) into "addr" */
1582 movl 0x14(%esp), %eax
1585 /* set return value to continuation */
1595 * get_rom_config_table()
1597 * Get the linear address of a ROM configuration table. Return zero,
1601 ENTRY(get_rom_config_table)
1605 /* zero %ebx for simplicity */
1608 call EXT_C(prot_to_real)
1626 DATA32 call EXT_C(real_to_prot)
1629 /* compute the linear address */
1640 * int get_vbe_controller_info (struct vbe_controller *controller_ptr)
1642 * Get VBE controller information.
1645 ENTRY(get_vbe_controller_info)
1652 /* Convert the linear address to segment:offset */
1655 andl $0x0000000f, %edi
1659 call EXT_C(prot_to_real)
1667 DATA32 call EXT_C(real_to_prot)
1679 * int get_vbe_mode_info (int mode_number, struct vbe_mode *mode_ptr)
1681 * Get VBE mode information.
1684 ENTRY(get_vbe_mode_info)
1691 /* Convert the linear address to segment:offset */
1692 movl 0xc(%ebp), %eax
1694 andl $0x0000000f, %edi
1698 /* Save the mode number in %cx */
1699 movl 0x8(%ebp), %ecx
1701 call EXT_C(prot_to_real)
1709 DATA32 call EXT_C(real_to_prot)
1721 * int set_vbe_mode (int mode_number)
1723 * Set VBE mode. Don't support user-specified CRTC information.
1732 /* Save the mode number in %bx */
1733 movl 0x8(%ebp), %ebx
1737 call EXT_C(prot_to_real)
1744 DATA32 call EXT_C(real_to_prot)
1755 * gateA20(int linear)
1757 * Gate address-line 20 for high memory.
1759 * This routine is probably overconservative in what it does, but so what?
1761 * It also eats any keystrokes in the keyboard buffer. :-(
1765 /* first, try a BIOS call */
1769 call EXT_C(prot_to_real)
1780 /* set non-zero if failed */
1783 /* save the status */
1786 DATA32 call EXT_C(real_to_prot)
1795 * try to switch gateA20 using PORT92, the "Fast A20 and Init"
1800 /* skip the port92 code if it's unimplemented (read returns 0xff) */
1804 /* set or clear bit1, the ALT_A20_GATE bit */
1812 /* clear the INIT_NOW bit; don't accidently reset the machine */
1816 6: /* use keyboard controller */
1821 movb $KC_CMD_WOUT, %al
1828 andb $K_IBUF_FUL, %al
1832 movb $KB_OUTPUT_MASK, %al
1836 orb $KB_A20_ENABLE, %al
1842 /* output a dummy command (USB keyboard hack) */
1854 andb $K_IBUF_FUL, %al
1859 andb $K_OBUF_FUL, %al
1868 ENTRY(patch_code) /* labels start with "pc_" */
1880 ENTRY(patch_code_end)
1888 * Does some funky things (including on the stack!), then jumps to the
1889 * entry point of the Linux setup code.
1892 VARIABLE(linux_text_len)
1895 VARIABLE(linux_data_tmp_addr)
1898 VARIABLE(linux_data_real_addr)
1902 /* don't worry about saving anything, we're committed at this point */
1903 cld /* forward copying */
1906 movl EXT_C(linux_text_len), %ecx
1909 movl $LINUX_BZIMAGE_ADDR, %esi
1910 movl $LINUX_ZIMAGE_ADDR, %edi
1915 ENTRY(big_linux_boot)
1916 movl EXT_C(linux_data_real_addr), %ebx
1918 /* copy the real mode part */
1919 movl EXT_C(linux_data_tmp_addr), %esi
1921 movl $LINUX_SETUP_MOVE_SIZE, %ecx
1926 /* change %ebx to the segment address */
1930 movl %eax, linux_setup_seg
1932 /* XXX new stack pointer in safe area for calling functions */
1934 call EXT_C(stop_floppy)
1936 /* final setup for linux boot */
1938 call EXT_C(prot_to_real)
1941 /* final setup for linux boot */
1944 movw $LINUX_SETUP_STACK, %sp
1961 * multi_boot(int start, int mb_info)
1963 * This starts a kernel in the manner expected of the multiboot standard.
1967 /* no need to save anything */
1968 call EXT_C(stop_floppy)
1970 movl $0x2BADB002, %eax
1971 movl 0x8(%esp), %ebx
1973 /* boot kernel here (absolute address call) */
1979 #endif /* ! STAGE1_5 */
1982 * void console_putchar (int c)
1984 * Put the character C on the console. Because GRUB wants to write a
1985 * character with an attribute, this implementation is a bit tricky.
1986 * If C is a control character (CR, LF, BEL, BS), use INT 10, AH = 0Eh
1987 * (TELETYPE OUTPUT). Otherwise, save the original position, put a space,
1988 * save the current position, restore the original position, write the
1989 * character and the attribute, and restore the current position.
1991 * The reason why this is so complicated is that there is no easy way to
1992 * get the height of the screen, and the TELETYPE OUPUT BIOS call doesn't
1993 * support setting a background attribute.
1995 ENTRY(console_putchar)
1996 movl 0x4(%esp), %edx
2001 movl EXT_C(console_current_color), %ebx
2004 call EXT_C(prot_to_real)
2010 /* use teletype output if control character */
2020 /* save the character and the attribute on the stack */
2024 /* get the current position */
2028 /* check the column with the width */
2032 /* print CR and LF, if next write will exceed the width */
2038 /* get the current position */
2043 /* restore the character and the attribute */
2047 /* write the character with the attribute */
2052 /* move the cursor forward */
2058 #endif /* ! STAGE1_5 */
2063 3: DATA32 call EXT_C(real_to_prot)
2072 /* this table is used in translate_keycode below */
2081 .word KEY_BACKSPACE, 8
2087 * translate_keycode translates the key code %dx to an ascii code.
2095 movw $ABS(translation_table), %si
2098 /* check if this is the end */
2101 /* load the ascii code into %ax */
2104 /* check if this matches the key code */
2107 /* translate %dx, if successful */
2118 * remap_ascii_char remaps the ascii code %dl to another if the code is
2119 * contained in ASCII_KEY_MAP.
2126 movw $ABS(EXT_C(ascii_key_map)), %si
2129 /* check if this is the end */
2132 /* check if this matches the ascii code */
2135 /* if so, perform the mapping */
2146 ENTRY(ascii_key_map)
2147 .space (KEY_MAP_SIZE + 1) * 2
2151 * int console_getkey (void)
2152 * BIOS call "INT 16H Function 00H" to read character from keyboard
2153 * Call with %ah = 0x0
2154 * Return: %ah = keyboard scan code
2155 * %al = ASCII character
2158 ENTRY(console_getkey)
2162 call EXT_C(console_checkkey)
2166 call EXT_C(prot_to_real)
2171 movw %ax, %dx /* real_to_prot uses %eax */
2172 call translate_keycode
2173 call remap_ascii_char
2175 DATA32 call EXT_C(real_to_prot)
2185 * int console_checkkey (void)
2186 * if there is a character pending, return it; otherwise return -1
2187 * BIOS call "INT 16H Function 01H" to check whether a character is pending
2188 * Call with %ah = 0x1
2190 * If key waiting to be input:
2191 * %ah = keyboard scan code
2192 * %al = ASCII character
2197 ENTRY(console_checkkey)
2201 call EXT_C(prot_to_real) /* enter real mode */
2207 DATA32 jz notpending
2210 call translate_keycode
2211 call remap_ascii_char
2215 movl $0xFFFFFFFF, %edx
2218 DATA32 call EXT_C(real_to_prot)
2228 * int console_getxy (void)
2229 * BIOS call "INT 10H Function 03h" to get cursor position
2230 * Call with %ah = 0x03
2232 * Returns %ch = starting scan line
2233 * %cl = ending scan line
2234 * %dh = row (0 is top)
2235 * %dl = column (0 is left)
2239 ENTRY(console_getxy)
2241 push %ebx /* save EBX */
2243 call EXT_C(prot_to_real)
2246 xorb %bh, %bh /* set page to 0 */
2248 int $0x10 /* get cursor position */
2250 DATA32 call EXT_C(real_to_prot)
2262 * void console_gotoxy(int x, int y)
2263 * BIOS call "INT 10H Function 02h" to set cursor position
2264 * Call with %ah = 0x02
2266 * %dh = row (0 is top)
2267 * %dl = column (0 is left)
2271 ENTRY(console_gotoxy)
2273 push %ebx /* save EBX */
2275 movb 0xc(%esp), %dl /* %dl = x */
2276 movb 0x10(%esp), %dh /* %dh = y */
2278 call EXT_C(prot_to_real)
2281 xorb %bh, %bh /* set page to 0 */
2283 int $0x10 /* set cursor position */
2285 DATA32 call EXT_C(real_to_prot)
2294 * void console_cls (void)
2295 * BIOS call "INT 10H Function 09h" to write character and attribute
2296 * Call with %ah = 0x09
2298 * %bh = (page number)
2300 * %cx = (number of times)
2306 push %ebx /* save EBX */
2308 call EXT_C(prot_to_real)
2311 /* move the cursor to the beginning */
2317 /* write spaces to the entire screen */
2320 movw $(80 * 25), %cx
2323 /* move back the cursor */
2327 DATA32 call EXT_C(real_to_prot)
2336 * int console_setcursor (int on)
2337 * BIOS call "INT 10H Function 01h" to set cursor type
2338 * Call with %ah = 0x01
2339 * %ch = cursor starting scanline
2340 * %cl = cursor ending scanline
2343 console_cursor_state:
2345 console_cursor_shape:
2348 ENTRY(console_setcursor)
2352 /* check if the standard cursor shape has already been saved */
2353 movw console_cursor_shape, %ax
2357 call EXT_C(prot_to_real)
2364 DATA32 call EXT_C(real_to_prot)
2367 movw %cx, console_cursor_shape
2369 /* set %cx to the designated cursor shape */
2371 movl 0xc(%esp), %ebx
2374 movw console_cursor_shape, %cx
2376 call EXT_C(prot_to_real)
2382 DATA32 call EXT_C(real_to_prot)
2385 movzbl console_cursor_state, %eax
2386 movb %bl, console_cursor_state
2392 /* graphics mode functions */
2393 #ifdef SUPPORT_GRAPHICS
2398 VARIABLE(cursorCount)
2401 .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
2405 * int set_videomode(mode)
2406 * BIOS call "INT 10H Function 0h" to set video mode
2407 * Call with %ah = 0x0
2409 * Returns old videomode.
2411 ENTRY(set_videomode)
2416 movb 0x10(%esp), %cl
2418 call EXT_C(prot_to_real)
2423 int $0x10 /* Get Current Video mode */
2427 int $0x10 /* Set Video mode */
2429 DATA32 call EXT_C(real_to_prot)
2442 * unsigned char * graphics_get_font()
2443 * BIOS call "INT 10H Function 11h" to set font
2444 * Call with %ah = 0x11
2446 ENTRY(graphics_get_font)
2452 call EXT_C(prot_to_real)
2456 movb $6, %bh /* font 8x16 */
2461 DATA32 call EXT_C(real_to_prot)
2478 * graphics_set_palette(index, red, green, blue)
2479 * BIOS call "INT 10H Function 10h" to set individual dac register
2480 * Call with %ah = 0x10
2481 * %bx = register number
2482 * %ch = new value for green (0-63)
2483 * %cl = new value for blue (0-63)
2484 * %dh = new value for red (0-63)
2487 ENTRY(graphics_set_palette)
2494 movw $0x3c8, %bx /* address write mode register */
2496 /* wait vertical retrace */
2499 l1b: inb %dx, %al /* wait vertical active display */
2503 l2b: inb %dx, %al /* wait vertical retrace */
2508 movb 0x18(%esp), %al /* index */
2512 movb 0x1c(%esp), %al /* red */
2515 movb 0x20(%esp), %al /* green */
2518 movb 0x24(%esp), %al /* blue */
2521 movw 0x18(%esp), %bx
2523 call EXT_C(prot_to_real)
2530 DATA32 call EXT_C(real_to_prot)
2540 #endif /* SUPPORT_GRAPHICS */
2544 * if a seconds value can be read, read it and return it (BCD),
2545 * otherwise return 0xFF
2546 * BIOS call "INT 1AH Function 02H" to check whether a character is pending
2547 * Call with %ah = 0x2
2549 * If RT Clock can give correct values
2551 * %cl = minutes (BCD)
2552 * %dh = seconds (BCD)
2553 * %dl = daylight savings time (00h std, 01h daylight)
2554 * Carry flag = clear
2557 * (this indicates that the clock is updating, or
2558 * that it isn't running)
2563 call EXT_C(prot_to_real) /* enter real mode */
2573 DATA32 call EXT_C(real_to_prot)
2584 * return the real time in ticks, of which there are about
2590 call EXT_C(prot_to_real) /* enter real mode */
2593 /* %ax is already zero */
2596 DATA32 call EXT_C(real_to_prot)
2622 ENTRY(amd64_cpuid_insn)
2627 movl 0x8(%ebp), %eax
2628 movl 0xc(%ebp), %esi
2630 movl %eax, 0x0(%esi)
2631 movl %ebx, 0x4(%esi)
2632 movl %ecx, 0x8(%esi)
2633 movl %edx, 0xc(%esi)
2640 * Based on code from AMD64 Volume 3
2642 ENTRY(amd64_cpuid_supported)
2645 mov %eax, %edx /* save %eax for later */
2646 xorl %eax, 0x200000 /* toggle bit 21 */
2648 popf /* save new %eax to EFLAGS */
2649 pushf /* save new EFLAGS */
2650 popl %ecx /* copy EFLAGS to %eax */
2652 cmpl %ecx, %edx /* see if bit 21 has changes */
2658 ENTRY(get_target_operating_mode)
2661 call EXT_C(prot_to_real)
2671 DATA32 call EXT_C(real_to_prot)
2676 movl %eax, 0x1c(%esp)
2681 #endif /* ! STAGE1_5 */
2684 * This is the area for all of the special variables.
2687 .p2align 2 /* force 4-byte alignment */
2692 VARIABLE(boot_drive)
2693 #ifdef SUPPORT_DISKLESS
2699 VARIABLE(install_second_sector)
2702 /* an address can only be long-jumped to if it is in memory, this
2703 is used by multiple routines */
2709 VARIABLE(apm_bios_info)
2710 .word 0 /* version */
2712 .long 0 /* offset */
2713 .word 0 /* cseg_16 */
2714 .word 0 /* dseg_16 */
2715 .word 0 /* cseg_len */
2716 .word 0 /* cseg_16_len */
2717 .word 0 /* dseg_16_len */
2720 * This is the Global Descriptor Table
2722 * An entry, a "Segment Descriptor", looks like this:
2725 * ------------------------------------------------------------
2726 * | | |B| |A| | | |1|0|E|W|A| |
2727 * | BASE 31..24 |G|/|0|V| LIMIT |P|DPL| TYPE | BASE 23:16 |
2728 * | | |D| |L| 19..16| | |1|1|C|R|A| |
2729 * ------------------------------------------------------------
2731 * | BASE 15..0 | LIMIT 15..0 |
2733 * ------------------------------------------------------------
2735 * Note the ordering of the data items is reversed from the above
2739 .p2align 2 /* force 4-byte alignment */
2746 .byte 0, 0x9A, 0xCF, 0
2750 .byte 0, 0x92, 0xCF, 0
2752 /* 16 bit real mode CS */
2756 /* 16 bit real mode DS */
2761 /* this is the GDT descriptor */
2763 .word 0x27 /* limit */
2764 .long gdt /* addr */