|  | /* | 
|  | *  GRUB  --  GRand Unified Bootloader | 
|  | *  Copyright (C) 1996   Erich Boleyn  <erich@uruk.org> | 
|  | *  Copyright (C) 1999, 2000 Free Software Foundation, Inc. | 
|  | * | 
|  | *  This program is free software; you can redistribute it and/or modify | 
|  | *  it under the terms of the GNU General Public License as published by | 
|  | *  the Free Software Foundation; either version 2 of the License, or | 
|  | *  (at your option) any later version. | 
|  | * | 
|  | *  This program is distributed in the hope that it will be useful, | 
|  | *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | *  GNU General Public License for more details. | 
|  | * | 
|  | *  You should have received a copy of the GNU General Public License | 
|  | *  along with this program; if not, write to the Free Software | 
|  | *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 
|  | */ | 
|  |  | 
|  | #define ASM_FILE | 
|  |  | 
|  | #include "shared.h" | 
|  |  | 
|  | #ifdef STAGE1_5 | 
|  | #define	ABS(x)	((x) - EXT_C(main) + 0x2200) | 
|  | #else | 
|  | #define	ABS(x)	((x) - EXT_C(main) + 0x8200) | 
|  | #endif | 
|  |  | 
|  | .file	"asm.S" | 
|  |  | 
|  | .text | 
|  |  | 
|  | /* Tell GAS to generate 16-bit instructions so that this code works | 
|  | in real mode. */ | 
|  | .code16 | 
|  |  | 
|  | #ifndef STAGE1_5 | 
|  | /* | 
|  | * In stage2, do not link start.S with the rest of the source | 
|  | * files directly, so define the start symbols here just to | 
|  | * force ld quiet. These are not referred anyway. | 
|  | */ | 
|  | .globl	start, _start | 
|  | start: | 
|  | _start: | 
|  | #endif /* ! STAGE1_5 */ | 
|  |  | 
|  | ENTRY(main) | 
|  | /* | 
|  | *  Guarantee that "main" is loaded at 0x0:0x8200 in stage2 and | 
|  | *  at 0x0:0x2200 in stage1.5. | 
|  | */ | 
|  | ljmp $0, $ABS(codestart) | 
|  |  | 
|  | /* | 
|  | *  Compatibility version number | 
|  | * | 
|  | *  These MUST be at byte offset 6 and 7 of the executable | 
|  | *  DO NOT MOVE !!! | 
|  | */ | 
|  | . = EXT_C(main) + 0x6 | 
|  | .byte	COMPAT_VERSION_MAJOR, COMPAT_VERSION_MINOR | 
|  |  | 
|  | /* | 
|  | *  This is a special data area 8 bytes from the beginning. | 
|  | */ | 
|  |  | 
|  | . = EXT_C(main) + 0x8 | 
|  |  | 
|  | VARIABLE(install_partition) | 
|  | .long	0x020000 | 
|  | VARIABLE(stage2_id) | 
|  | .byte	STAGE2_ID | 
|  | VARIABLE(version_string) | 
|  | .string VERSION | 
|  | VARIABLE(config_file) | 
|  | #ifndef STAGE1_5 | 
|  | .string "/boot/grub/menu.lst" | 
|  | #else   /* STAGE1_5 */ | 
|  | .long	0xffffffff | 
|  | .string "/boot/grub/stage2" | 
|  | #endif  /* STAGE1_5 */ | 
|  |  | 
|  | /* | 
|  | *  Leave some breathing room for the config file name. | 
|  | */ | 
|  |  | 
|  | . = EXT_C(main) + 0x70 | 
|  |  | 
|  | /* the real mode code continues... */ | 
|  | codestart: | 
|  | cli		/* we're not safe here! */ | 
|  |  | 
|  | /* set up %ds, %ss, and %es */ | 
|  | xorw	%ax, %ax | 
|  | movw	%ax, %ds | 
|  | movw	%ax, %ss | 
|  | movw	%ax, %es | 
|  |  | 
|  | /* set up the real mode/BIOS stack */ | 
|  | movl	$STACKOFF, %ebp | 
|  | movl	%ebp, %esp | 
|  |  | 
|  | sti		/* we're safe again */ | 
|  |  | 
|  | /* save boot drive reference */ | 
|  | ADDR32	movb	%dl, EXT_C(boot_drive) | 
|  |  | 
|  | /* reset disk system (%ah = 0) */ | 
|  | int	$0x13 | 
|  |  | 
|  | /* transition to protected mode */ | 
|  | DATA32	call EXT_C(real_to_prot) | 
|  |  | 
|  | /* The ".code32" directive takes GAS out of 16-bit mode. */ | 
|  | .code32 | 
|  |  | 
|  | /* clean out the bss */ | 
|  |  | 
|  | /* set %edi to the bss starting address */ | 
|  | #if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL) | 
|  | movl	$__bss_start, %edi | 
|  | #elif defined(HAVE_USCORE_EDATA_SYMBOL) | 
|  | movl	$_edata, %edi | 
|  | #elif defined(HAVE_EDATA_SYMBOL) | 
|  | movl	$edata, %edi | 
|  | #endif | 
|  |  | 
|  | /* set %ecx to the bss end */ | 
|  | #if defined(HAVE_END_SYMBOL) | 
|  | movl	$end, %ecx | 
|  | #elif defined(HAVE_USCORE_END_SYMBOL) | 
|  | movl	$_end, %ecx | 
|  | #endif | 
|  |  | 
|  | /* compute the bss length */ | 
|  | subl	%edi, %ecx | 
|  |  | 
|  | /* zero %al */ | 
|  | xorb	%al, %al | 
|  |  | 
|  | /* set the direction */ | 
|  | cld | 
|  |  | 
|  | /* clean out */ | 
|  | rep | 
|  | stosb | 
|  |  | 
|  | /* | 
|  | *  Call the start of main body of C code, which does some | 
|  | *  of it's own initialization before transferring to "cmain". | 
|  | */ | 
|  | call EXT_C(init_bios_info) | 
|  |  | 
|  |  | 
|  | /* | 
|  | *  This call is special...  it never returns...  in fact it should simply | 
|  | *  hang at this point! | 
|  | */ | 
|  |  | 
|  | ENTRY(stop) | 
|  | call	EXT_C(prot_to_real) | 
|  |  | 
|  | /* | 
|  | * This next part is sort of evil.  It takes advantage of the | 
|  | * byte ordering on the x86 to work in either 16-bit or 32-bit | 
|  | * mode, so think about it before changing it. | 
|  | */ | 
|  |  | 
|  | ENTRY(hard_stop) | 
|  | hlt | 
|  | jmp EXT_C(hard_stop) | 
|  |  | 
|  | #ifndef STAGE1_5 | 
|  | /* | 
|  | * stop_floppy() | 
|  | * | 
|  | * Stops the floppy drive from spinning, so that other software is | 
|  | * jumped to with a known state. | 
|  | */ | 
|  | ENTRY(stop_floppy) | 
|  | movw	$0x3F2, %dx | 
|  | xorb	%al, %al | 
|  | outb	%al, %dx | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * track_int13(int drive) | 
|  | * | 
|  | * Track the int13 handler to probe I/O address space. | 
|  | */ | 
|  | ENTRY(track_int13) | 
|  | pushl	%ebp | 
|  | movl	%esp, %ebp | 
|  |  | 
|  | pushl	%edx | 
|  | pushl	%ebx | 
|  | pushl	%edi | 
|  |  | 
|  | /* save the original int13 handler */ | 
|  | movl	$0x4c, %edi | 
|  | movw	(%edi), %ax | 
|  | movw	%ax, ABS(set_tf_int13_offset) | 
|  | movw	2(%edi), %ax | 
|  | movw	%ax, ABS(set_tf_int13_segment) | 
|  |  | 
|  | /* save the new int13 handler */ | 
|  | movl	$ABS(set_tf_int13_handler), %eax | 
|  | movl	%eax, (%edi) | 
|  |  | 
|  | /* replace the int1 handler */ | 
|  | movl	$0x4, %edi | 
|  | pushl	(%edi) | 
|  | movl	$ABS(int1_handler), %eax | 
|  | movl	%eax, (%edi) | 
|  |  | 
|  | /* read the MBR to call int13 successfully */ | 
|  | movb	8(%ebp), %dl | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | movw	$SCRATCHSEG, %ax | 
|  | movw	%ax, %es | 
|  | xorw	%bx, %bx | 
|  | movw	$1, %cx | 
|  | xorb	%dh, %dh | 
|  |  | 
|  | movw	$0x201, %ax | 
|  | int	$0x13 | 
|  |  | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | /* restore the int1 handler */ | 
|  | movl	$0x4, %edi | 
|  | popl	(%edi) | 
|  |  | 
|  | /* restore the int13 handler */ | 
|  | movl	$0x4c, %edi | 
|  | movw	ABS(set_tf_int13_offset), %ax | 
|  | movw	%ax, (%edi) | 
|  | movw	ABS(set_tf_int13_segment), %ax | 
|  | movw	%ax, 2(%edi) | 
|  |  | 
|  | popl	%edi | 
|  | popl	%ebx | 
|  | popl	%edx | 
|  | popl	%ebp | 
|  |  | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Check if the next instruction is I/O, and if this is true, add the | 
|  | * port into the io map. | 
|  | * | 
|  | * Note: Probably this will make the execution of int13 very slow. | 
|  | * | 
|  | * Note2: In this implementation, all we can know is I/O-mapped I/O. It | 
|  | * is impossible to detect memory-mapped I/O. | 
|  | */ | 
|  | int1_handler: | 
|  | .code16 | 
|  |  | 
|  | pushw	%bp | 
|  | movw	%sp, %bp | 
|  | pushw	%ds | 
|  | pushw	%ax | 
|  | pushw	%si | 
|  | pushw	%dx | 
|  |  | 
|  | /* IP */ | 
|  | movw	2(%bp), %si | 
|  | /* CS */ | 
|  | movw	4(%bp), %ax | 
|  | movw	%ax, %ds | 
|  |  | 
|  | /* examine the next instruction */ | 
|  | 1:	lodsb	(%si), %al | 
|  | /* skip this code if it is a prefix */ | 
|  | cmpb	$0x2E, %al | 
|  | je	1b | 
|  | cmpb	$0x36, %al | 
|  | je	1b | 
|  | cmpb	$0x3E, %al | 
|  | je	1b | 
|  | cmpb	$0x26, %al | 
|  | je	1b | 
|  | cmpb	$0x64, %al | 
|  | jl	2f | 
|  | cmpb	$0x67, %al | 
|  | jle	1b | 
|  | 2:	cmpb	$0xF0, %al | 
|  | jl	3f | 
|  | cmpb	$0xF3, %al | 
|  | jle	1b | 
|  |  | 
|  | 3:	/* check if this code is out* or in* */ | 
|  |  | 
|  | /* ins? or outs? */ | 
|  | cmpb	$0x6C, %al | 
|  | jl	4f | 
|  | cmpb	$0x6F, %al | 
|  | jle	5f | 
|  |  | 
|  | 4:	/* in? or out? (register operand version) */ | 
|  | cmpb	$0xEC, %al | 
|  | jl	6f | 
|  | cmpb	$0xEF, %al | 
|  | jle	5f | 
|  |  | 
|  | 6:	/* in? or out? (immediate operand version) */ | 
|  | cmpb	$0xE4, %al | 
|  | jl	8f | 
|  | cmpb	$0xE7, %al | 
|  | jg	8f | 
|  |  | 
|  | 7:	/* immediate has a port */ | 
|  | xorb	%ah, %ah | 
|  | lodsb	(%si), %al | 
|  | movw	%ax, %dx | 
|  |  | 
|  | 5:	/* %dx has a port */ | 
|  |  | 
|  | /* set %ds to zero */ | 
|  | xorw	%ax, %ax | 
|  | movw	%ax, %ds | 
|  |  | 
|  | /* set %si to the io map */ | 
|  | movw	$ABS(EXT_C(io_map)), %si | 
|  |  | 
|  |  | 
|  | 9:	/* check if the io map already has the port */ | 
|  | lodsw	(%si), %ax | 
|  | /* check if this is the end */ | 
|  | testw	%ax, %ax | 
|  | jz	1f | 
|  | /* check if this matches the port */ | 
|  | cmpw	%ax, %dx | 
|  | jne	9b | 
|  | /* if so, leave from this handler */ | 
|  | jmp	8f | 
|  |  | 
|  | 1:	/* check for the buffer overrun */ | 
|  | cmpw	$(ABS(EXT_C(io_map)) + (IO_MAP_SIZE + 1) * 2), %si | 
|  | je	8f | 
|  | /* add the port into the io map */ | 
|  | movw	%dx, -2(%si) | 
|  |  | 
|  | 8:	/* restore registers */ | 
|  | popw	%dx | 
|  | popw	%si | 
|  | popw	%ax | 
|  | popw	%ds | 
|  | popw	%bp | 
|  |  | 
|  | iret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Just set the TF flag. This handler is necessary because any interrupt | 
|  | * call clears the flag automatically. | 
|  | * | 
|  | * Note: we need not to clear this flag after the tracking explicitly, | 
|  | * because iret restores the original FLAGS. | 
|  | */ | 
|  | set_tf_int13_handler: | 
|  | /* save %ax int the stack */ | 
|  | pushw	%ax | 
|  |  | 
|  | /* set the TF flag */ | 
|  | pushf | 
|  | popw	%ax | 
|  | orw	$0x100, %ax | 
|  | pushw	%ax | 
|  | popf | 
|  |  | 
|  | /* restore %ax */ | 
|  | popw	%ax | 
|  |  | 
|  | /* simulate the interrupt call */ | 
|  | pushf | 
|  | /* lcall */ | 
|  | .byte	0x9a | 
|  | set_tf_int13_offset:	.word	0 | 
|  | set_tf_int13_segment:	.word	0 | 
|  |  | 
|  | iret | 
|  |  | 
|  | .code32 | 
|  |  | 
|  | ENTRY(io_map) | 
|  | .space	(IO_MAP_SIZE + 1) * 2 | 
|  |  | 
|  |  | 
|  | /* | 
|  | * set_int15_handler(void) | 
|  | * | 
|  | * Set up int15_handler. | 
|  | */ | 
|  | ENTRY(set_int15_handler) | 
|  | pushl	%edi | 
|  |  | 
|  | /* save the original int15 handler */ | 
|  | movl	$0x54, %edi | 
|  | movw	(%edi), %ax | 
|  | movw	%ax, ABS(int15_offset) | 
|  | movw	2(%edi), %ax | 
|  | movw	%ax, ABS(int15_segment) | 
|  |  | 
|  | /* save the new int15 handler */ | 
|  | movw	$ABS(int15_handler), %ax | 
|  | movw	%ax, (%edi) | 
|  | xorw	%ax, %ax | 
|  | movw	%ax, 2(%edi) | 
|  |  | 
|  | popl	%edi | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * unset_int15_handler(void) | 
|  | * | 
|  | * Restore the original int15 handler | 
|  | */ | 
|  | ENTRY(unset_int15_handler) | 
|  | pushl	%edi | 
|  |  | 
|  | /* check if int15_handler is set */ | 
|  | movl	$0x54, %edi | 
|  | movw	$ABS(int15_handler), %ax | 
|  | cmpw	%ax, (%edi) | 
|  | jne	1f | 
|  | xorw	%ax, %ax | 
|  | cmpw	%ax, 2(%edi) | 
|  | jne	1f | 
|  |  | 
|  | /* restore the original */ | 
|  | movw	ABS(int15_offset), %ax | 
|  | movw	%ax, (%edi) | 
|  | movw	ABS(int15_segment), %ax | 
|  | movw	%ax, 2(%edi) | 
|  |  | 
|  | 1: | 
|  | popl	%edi | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Translate a key code to another. | 
|  | * | 
|  | * Note: This implementation cannot handle more than one length | 
|  | * scancodes (such as Right Ctrl). | 
|  | */ | 
|  | .code16 | 
|  | int15_handler: | 
|  | /* if non-carrier, ignore it */ | 
|  | jnc	1f | 
|  | /* check if AH=4F */ | 
|  | cmpb	$0x4F, %ah | 
|  | jne	1f | 
|  |  | 
|  | /* E0 and E1 are special */ | 
|  | cmpb	$0xE1, %al | 
|  | je	4f | 
|  | cmpb	$0xE0, %al | 
|  | /* this flag is actually the machine code (je or jmp) */ | 
|  | int15_skip_flag: | 
|  | je	4f | 
|  |  | 
|  | pushw	%bp | 
|  | movw	%sp, %bp | 
|  |  | 
|  | pushw	%bx | 
|  | pushw	%dx | 
|  | pushw	%ds | 
|  | pushw	%si | 
|  |  | 
|  | /* save bits 0-6 of %al in %dl */ | 
|  | movw	%ax, %dx | 
|  | andb	$0x7f, %dl | 
|  | /* save the highest bit in %bl */ | 
|  | movb	%al, %bl | 
|  | xorb	%dl, %bl | 
|  | /* set %ds to 0 */ | 
|  | xorw	%ax, %ax | 
|  | movw	%ax, %ds | 
|  | /* set %si to the key map */ | 
|  | movw	$ABS(EXT_C(bios_key_map)), %si | 
|  |  | 
|  | /* find the key code from the key map */ | 
|  | 2: | 
|  | lodsw | 
|  | /* check if this is the end */ | 
|  | testw	%ax, %ax | 
|  | jz	3f | 
|  | /* check if this matches the key code */ | 
|  | cmpb	%al, %dl | 
|  | jne	2b | 
|  | /* if so, perform the mapping */ | 
|  | movb	%ah, %dl | 
|  | 3: | 
|  | /* restore %ax */ | 
|  | movw	%dx, %ax | 
|  | orb	%bl, %al | 
|  | /* make sure that CF is set */ | 
|  | orw	$1, 6(%bp) | 
|  | /* restore other registers */ | 
|  | popw	%si | 
|  | popw	%ds | 
|  | popw	%dx | 
|  | popw	%bx | 
|  | popw	%bp | 
|  | iret | 
|  |  | 
|  | 4: | 
|  | /* tricky: jmp (0x74) <-> je (0xeb) */ | 
|  | xorb	$(0x74 ^ 0xeb), ABS(int15_skip_flag) | 
|  | 1: | 
|  | /* just cascade to the original */ | 
|  | /* ljmp */ | 
|  | .byte	0xea | 
|  | int15_offset:	.word	0 | 
|  | int15_segment:	.word	0 | 
|  |  | 
|  | .code32 | 
|  |  | 
|  | .align	4 | 
|  | ENTRY(bios_key_map) | 
|  | .space	(KEY_MAP_SIZE + 1) * 2 | 
|  |  | 
|  |  | 
|  | /* | 
|  | * set_int13_handler(map) | 
|  | * | 
|  | * Copy MAP to the drive map and set up int13_handler. | 
|  | */ | 
|  | ENTRY(set_int13_handler) | 
|  | pushl	%ebp | 
|  | movl	%esp, %ebp | 
|  |  | 
|  | pushl	%ecx | 
|  | pushl	%edi | 
|  | pushl	%esi | 
|  |  | 
|  | /* copy MAP to the drive map */ | 
|  | movl	$(DRIVE_MAP_SIZE * 2), %ecx | 
|  | movl	$ABS(drive_map), %edi | 
|  | movl	8(%ebp), %esi | 
|  | cld | 
|  | rep | 
|  | movsb | 
|  |  | 
|  | /* save the original int13 handler */ | 
|  | movl	$0x4c, %edi | 
|  | movw	(%edi), %ax | 
|  | movw	%ax, ABS(int13_offset) | 
|  | movw	2(%edi), %ax | 
|  | movw	%ax, ABS(int13_segment) | 
|  |  | 
|  | /* get the lower memory size */ | 
|  | movl	$EXT_C(mbi), %edi | 
|  | movl	4(%edi), %eax | 
|  | /* decrease the lower memory size and set it to the BIOS memory */ | 
|  | decl	%eax | 
|  | movl	$0x413, %edi | 
|  | movw	%ax, (%edi) | 
|  | /* compute the segment */ | 
|  | shll	$6, %eax | 
|  |  | 
|  | /* save the new int13 handler */ | 
|  | movl	$0x4c, %edi | 
|  | movw	%ax, 2(%edi) | 
|  | xorw	%cx, %cx | 
|  | movw	%cx, (%edi) | 
|  |  | 
|  | /* copy int13_handler to the reserved area */ | 
|  | shll	$4, %eax | 
|  | movl	%eax, %edi | 
|  | movl	$ABS(int13_handler), %esi | 
|  | movl	$(int13_handler_end - int13_handler), %ecx | 
|  | rep | 
|  | movsb | 
|  |  | 
|  | popl	%esi | 
|  | popl	%edi | 
|  | popl	%ecx | 
|  | popl	%ebp | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Map a drive to another drive. | 
|  | */ | 
|  |  | 
|  | .code16 | 
|  |  | 
|  | int13_handler: | 
|  | pushw	%ax | 
|  | pushw	%bp | 
|  | movw	%sp, %bp | 
|  |  | 
|  | pushw	%si | 
|  |  | 
|  | /* set %si to the drive map */ | 
|  | movw	$(drive_map - int13_handler), %si | 
|  | /* find the drive number from the drive map */ | 
|  | cld | 
|  | 1: | 
|  | lodsw	%cs:(%si), %ax | 
|  | /* check if this is the end */ | 
|  | testw	%ax, %ax | 
|  | jz	2f | 
|  | /* check if this matches the drive number */ | 
|  | cmpb	%al, %dl | 
|  | jne	1b | 
|  | /* if so, perform the mapping */ | 
|  | movb	%ah, %dl | 
|  | 2: | 
|  | /* restore %si */ | 
|  | popw	%si | 
|  | /* save %ax in the stack */ | 
|  | pushw	%ax | 
|  | /* simulate the interrupt call */ | 
|  | pushw	8(%bp) | 
|  | /* set %ax and %bp to the original values */ | 
|  | movw	2(%bp), %ax | 
|  | movw	(%bp), %bp | 
|  | /* lcall */ | 
|  | .byte	0x9a | 
|  | int13_offset:	.word	0 | 
|  | int13_segment:	.word	0 | 
|  | /* save flags */ | 
|  | pushf | 
|  | /* restore %bp */ | 
|  | movw	%sp, %bp | 
|  | /* save %ax */ | 
|  | pushw	%ax | 
|  | /* set the flags in the stack to the value returned by int13 */ | 
|  | movw	(%bp), %ax | 
|  | movw	%ax, 0xc(%bp) | 
|  | /* check if should map the drive number */ | 
|  | movw	6(%bp), %ax | 
|  | cmpw	$0x8, %ax | 
|  | jne	3f | 
|  | cmpw	$0x15, %ax | 
|  | jne	3f | 
|  | /* check if the mapping was performed */ | 
|  | movw	2(%bp), %ax | 
|  | testw	%ax, %ax | 
|  | jz	3f | 
|  | /* perform the mapping */ | 
|  | movb	%al, %dl | 
|  | 3: | 
|  | popw	%ax | 
|  | movw	4(%bp), %bp | 
|  | addw	$8, %sp | 
|  | iret | 
|  |  | 
|  | .align	4 | 
|  | drive_map:	.space	(DRIVE_MAP_SIZE + 1) * 2 | 
|  | int13_handler_end: | 
|  |  | 
|  | .code32 | 
|  |  | 
|  |  | 
|  | /* | 
|  | * chain_stage1(segment, offset, part_table_addr) | 
|  | * | 
|  | *  This starts another stage1 loader, at segment:offset. | 
|  | */ | 
|  |  | 
|  | ENTRY(chain_stage1) | 
|  | /* no need to save anything, just use %esp */ | 
|  |  | 
|  | /* store %ESI, presuming %ES is 0 */ | 
|  | movl	0xc(%esp), %esi | 
|  |  | 
|  | /* store new offset */ | 
|  | movl	0x8(%esp), %eax | 
|  | movl	%eax, offset | 
|  |  | 
|  | /* store new segment */ | 
|  | movw	0x4(%esp), %ax | 
|  | movw	%ax, segment | 
|  |  | 
|  | /* set up to pass boot drive */ | 
|  | movb	EXT_C(boot_drive), %dl | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | DATA32	ADDR32	ljmp	(offset) | 
|  | .code32 | 
|  | #endif /* STAGE1_5 */ | 
|  |  | 
|  |  | 
|  | #ifdef STAGE1_5 | 
|  | /* | 
|  | * chain_stage2(segment, offset) | 
|  | * | 
|  | *  This starts another stage2 loader, at segment:offset.  It presumes | 
|  | *  that the other one starts with this same "asm.S" file, and passes | 
|  | *  parameters by writing the embedded install variables. | 
|  | */ | 
|  |  | 
|  | ENTRY(chain_stage2) | 
|  | /* no need to save anything, just use %esp */ | 
|  |  | 
|  | /* store new offset */ | 
|  | movl	0x8(%esp), %eax | 
|  | movl	%eax, offset | 
|  | movl	%eax, %ebx | 
|  |  | 
|  | /* store new segment */ | 
|  | movw	0x4(%esp), %ax | 
|  | movw	%ax, segment | 
|  | shll	$4, %eax | 
|  |  | 
|  | /* generate linear address */ | 
|  | addl	%eax, %ebx | 
|  |  | 
|  | /* set up to pass the partition where stage2 is located in */ | 
|  | movl	EXT_C(current_partition), %eax | 
|  | movl	%eax, (EXT_C(install_partition)-EXT_C(main))(%ebx) | 
|  |  | 
|  | /* set up to pass the drive where stage2 is located in */ | 
|  | movb	EXT_C(current_drive), %dl | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | DATA32	ADDR32	ljmp	(offset) | 
|  |  | 
|  | .code32 | 
|  | #endif /* STAGE1_5 */ | 
|  |  | 
|  | /* | 
|  | *  These next two routines, "real_to_prot" and "prot_to_real" are structured | 
|  | *  in a very specific way.  Be very careful when changing them. | 
|  | * | 
|  | *  NOTE:  Use of either one messes up %eax and %ebp. | 
|  | */ | 
|  |  | 
|  | ENTRY(real_to_prot) | 
|  | .code16 | 
|  | cli | 
|  |  | 
|  | /* load the GDT register */ | 
|  | DATA32	ADDR32	lgdt	gdtdesc | 
|  |  | 
|  | /* turn on protected mode */ | 
|  | movl	%cr0, %eax | 
|  | orl	$CR0_PE_ON, %eax | 
|  | movl	%eax, %cr0 | 
|  |  | 
|  | /* jump to relocation, flush prefetch queue, and reload %cs */ | 
|  | DATA32	ljmp	$PROT_MODE_CSEG, $protcseg | 
|  |  | 
|  | /* | 
|  | *  The ".code32" directive only works in GAS, the GNU assembler! | 
|  | *  This gets out of "16-bit" mode. | 
|  | */ | 
|  | .code32 | 
|  |  | 
|  | protcseg: | 
|  | /* reload other segment registers */ | 
|  | movw	$PROT_MODE_DSEG, %ax | 
|  | movw	%ax, %ds | 
|  | movw	%ax, %es | 
|  | movw	%ax, %fs | 
|  | movw	%ax, %gs | 
|  | movw	%ax, %ss | 
|  |  | 
|  | /* put the return address in a known safe location */ | 
|  | movl	(%esp), %eax | 
|  | movl	%eax, STACKOFF | 
|  |  | 
|  | /* get protected mode stack */ | 
|  | movl	protstack, %eax | 
|  | movl	%eax, %esp | 
|  | movl	%eax, %ebp | 
|  |  | 
|  | /* get return address onto the right stack */ | 
|  | movl	STACKOFF, %eax | 
|  | movl	%eax, (%esp) | 
|  |  | 
|  | /* zero %eax */ | 
|  | xorl	%eax, %eax | 
|  |  | 
|  | /* return on the old (or initialized) stack! */ | 
|  | ret | 
|  |  | 
|  |  | 
|  | ENTRY(prot_to_real) | 
|  | /* just in case, set GDT */ | 
|  | lgdt	gdtdesc | 
|  |  | 
|  | /* save the protected mode stack */ | 
|  | movl	%esp, %eax | 
|  | movl	%eax, protstack | 
|  |  | 
|  | /* get the return address */ | 
|  | movl	(%esp), %eax | 
|  | movl	%eax, STACKOFF | 
|  |  | 
|  | /* set up new stack */ | 
|  | movl	$STACKOFF, %eax | 
|  | movl	%eax, %esp | 
|  | movl	%eax, %ebp | 
|  |  | 
|  | /* set up segment limits */ | 
|  | movw	$PSEUDO_RM_DSEG, %ax | 
|  | movw	%ax, %ds | 
|  | movw	%ax, %es | 
|  | movw	%ax, %fs | 
|  | movw	%ax, %gs | 
|  | movw	%ax, %ss | 
|  |  | 
|  | /* this might be an extra step */ | 
|  | ljmp	$PSEUDO_RM_CSEG, $tmpcseg	/* jump to a 16 bit segment */ | 
|  |  | 
|  | tmpcseg: | 
|  | .code16 | 
|  |  | 
|  | /* clear the PE bit of CR0 */ | 
|  | movl	%cr0, %eax | 
|  | andl 	$CR0_PE_OFF, %eax | 
|  | movl	%eax, %cr0 | 
|  |  | 
|  | /* flush prefetch queue, reload %cs */ | 
|  | DATA32	ljmp	$0, $realcseg | 
|  |  | 
|  | realcseg: | 
|  | /* we are in real mode now | 
|  | * set up the real mode segment registers : DS, SS, ES | 
|  | */ | 
|  | /* zero %eax */ | 
|  | xorl	%eax, %eax | 
|  |  | 
|  | movw	%ax, %ds | 
|  | movw	%ax, %es | 
|  | movw	%ax, %fs | 
|  | movw	%ax, %gs | 
|  | movw	%ax, %ss | 
|  |  | 
|  | /* restore interrupts */ | 
|  | sti | 
|  |  | 
|  | /* return on new stack! */ | 
|  | DATA32	ret | 
|  |  | 
|  | .code32 | 
|  |  | 
|  |  | 
|  | /* | 
|  | *   int biosdisk_int13_extensions (int ah, int drive, void *dap) | 
|  | * | 
|  | *   Call IBM/MS INT13 Extensions (int 13 %ah=AH) for DRIVE. DAP | 
|  | *   is passed for disk address packet. If an error occurs, return | 
|  | *   non-zero, otherwise zero. | 
|  | */ | 
|  |  | 
|  | ENTRY(biosdisk_int13_extensions) | 
|  | pushl	%ebp | 
|  | movl	%esp, %ebp | 
|  |  | 
|  | pushl	%ecx | 
|  | pushl	%edx | 
|  | pushl	%esi | 
|  |  | 
|  | /* compute the address of disk_address_packet */ | 
|  | movl	0x10(%ebp), %eax | 
|  | movw	%ax, %si | 
|  | xorw	%ax, %ax | 
|  | shrl	$4, %eax | 
|  | movw	%ax, %cx	/* save the segment to cx */ | 
|  |  | 
|  | /* drive */ | 
|  | movb	0xc(%ebp), %dl | 
|  | /* ah */ | 
|  | movb	0x8(%ebp), %dh | 
|  | /* enter real mode */ | 
|  | call	EXT_C(prot_to_real) | 
|  |  | 
|  | .code16 | 
|  | movb	%dh, %ah | 
|  | movw	%cx, %ds | 
|  | int	$0x13		/* do the operation */ | 
|  | movb	%ah, %dl	/* save return value */ | 
|  | /* clear the data segment */ | 
|  | xorw	%ax, %ax | 
|  | movw	%ax, %ds | 
|  | /* back to protected mode */ | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | movb	%dl, %al	/* return value in %eax */ | 
|  |  | 
|  | popl	%esi | 
|  | popl	%edx | 
|  | popl	%ecx | 
|  | popl	%ebp | 
|  |  | 
|  | ret | 
|  |  | 
|  | /* | 
|  | *   int biosdisk_standard (int ah, int drive, int coff, int hoff, int soff, | 
|  | *                          int nsec, int segment) | 
|  | * | 
|  | *   Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write | 
|  | *   NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs, | 
|  | *   return non-zero, otherwise zero. | 
|  | */ | 
|  |  | 
|  | ENTRY(biosdisk_standard) | 
|  | pushl	%ebp | 
|  | movl	%esp, %ebp | 
|  |  | 
|  | pushl	%ebx | 
|  | pushl	%ecx | 
|  | pushl	%edx | 
|  | pushl	%edi | 
|  | pushl	%esi | 
|  |  | 
|  | /* set up CHS information */ | 
|  | movl	0x10(%ebp), %eax | 
|  | movb	%al, %ch | 
|  | movb	0x18(%ebp), %al | 
|  | shlb	$2, %al | 
|  | shrw	$2, %ax | 
|  | movb	%al, %cl | 
|  | movb	0x14(%ebp), %dh | 
|  | /* drive */ | 
|  | movb	0xc(%ebp), %dl | 
|  | /* segment */ | 
|  | movw	0x20(%ebp), %bx | 
|  | /* save nsec and ah to %di */ | 
|  | movb	0x8(%ebp), %ah | 
|  | movb	0x1c(%ebp), %al | 
|  | movw	%ax, %di | 
|  | /* enter real mode */ | 
|  | call	EXT_C(prot_to_real) | 
|  |  | 
|  | .code16 | 
|  | movw	%bx, %es | 
|  | xorw	%bx, %bx | 
|  | movw	$3, %si		/* attempt at least three times */ | 
|  |  | 
|  | 1: | 
|  | movw	%di, %ax | 
|  | int	$0x13		/* do the operation */ | 
|  | jnc	2f		/* check if successful */ | 
|  |  | 
|  | movb	%ah, %bl	/* save return value */ | 
|  | /* if fail, reset the disk system */ | 
|  | xorw	%ax, %ax | 
|  | int	$0x13 | 
|  |  | 
|  | decw	%si | 
|  | cmpw	$0, %si | 
|  | je	2f | 
|  | xorb	%bl, %bl | 
|  | jmp	1b		/* retry */ | 
|  | 2: | 
|  | /* back to protected mode */ | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | movb	%bl, %al	/* return value in %eax */ | 
|  |  | 
|  | popl	%esi | 
|  | popl	%edi | 
|  | popl	%edx | 
|  | popl	%ecx | 
|  | popl	%ebx | 
|  | popl	%ebp | 
|  |  | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | *   int check_int13_extensions (int drive) | 
|  | * | 
|  | *   Check if LBA is supported for DRIVE. If it is supported, then return | 
|  | *   the major version of extensions, otherwise zero. | 
|  | */ | 
|  |  | 
|  | ENTRY(check_int13_extensions) | 
|  | pushl	%ebp | 
|  | movl	%esp, %ebp | 
|  |  | 
|  | pushl	%ebx | 
|  | pushl	%ecx | 
|  | pushl	%edx | 
|  |  | 
|  | /* drive */ | 
|  | movb	0x8(%ebp), %dl | 
|  | /* enter real mode */ | 
|  | call	EXT_C(prot_to_real) | 
|  |  | 
|  | .code16 | 
|  | movb	$0x41, %ah | 
|  | movw	$0x55aa, %bx | 
|  | int	$0x13		/* do the operation */ | 
|  |  | 
|  | /* check the result */ | 
|  | jc	1f | 
|  | cmpw	$0xaa55, %bx | 
|  | jne	1f | 
|  | andw	$1, %cx | 
|  | jz	1f | 
|  |  | 
|  | movb	%ah, %bl	/* save the major version into %bl */ | 
|  | jmp	2f | 
|  | 1: | 
|  | xorb	%bl, %bl | 
|  | 2: | 
|  | /* back to protected mode */ | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | movb	%bl, %al	/* return value in %eax */ | 
|  |  | 
|  | popl	%edx | 
|  | popl	%ecx | 
|  | popl	%ebx | 
|  | popl	%ebp | 
|  |  | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | *   int get_diskinfo_int13_extensions (int drive, void *drp) | 
|  | * | 
|  | *   Return the geometry of DRIVE in a drive parameters, DRP. If an error | 
|  | *   occurs, then return non-zero, otherwise zero. | 
|  | */ | 
|  |  | 
|  | ENTRY(get_diskinfo_int13_extensions) | 
|  | pushl	%ebp | 
|  | movl	%esp, %ebp | 
|  |  | 
|  | pushl	%ebx | 
|  | pushl	%edx | 
|  | pushl	%esi | 
|  |  | 
|  | /* compute the address of drive parameters */ | 
|  | movl	0xc(%ebp), %eax | 
|  | movw	%ax, %si | 
|  | xorw	%ax, %ax | 
|  | shrl	$4, %eax | 
|  | movw	%ax, %bx	/* save the segment into %bx */ | 
|  | /* drive */ | 
|  | movb	0x8(%ebp), %dl | 
|  | /* enter real mode */ | 
|  | call	EXT_C(prot_to_real) | 
|  |  | 
|  | .code16 | 
|  | movb	$0x48, %ah | 
|  | movw	%bx, %ds | 
|  | int	$0x13		/* do the operation */ | 
|  | movb	%ah, %bl	/* save return value in %bl */ | 
|  | /* clear the data segment */ | 
|  | xorw	%ax, %ax | 
|  | movw	%ax, %ds | 
|  | /* back to protected mode */ | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | movb	%bl, %al	/* return value in %eax */ | 
|  |  | 
|  | popl	%esi | 
|  | popl	%edx | 
|  | popl	%ebx | 
|  | popl	%ebp | 
|  |  | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | *   int get_diskinfo_standard (int drive, unsigned long *cylinders, | 
|  | *                              unsigned long *heads, unsigned long *sectors) | 
|  | * | 
|  | *   Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an | 
|  | *   error occurs, then return non-zero, otherwise zero. | 
|  | */ | 
|  |  | 
|  | ENTRY(get_diskinfo_standard) | 
|  | pushl	%ebp | 
|  | movl	%esp, %ebp | 
|  |  | 
|  | pushl	%ebx | 
|  | pushl	%ecx | 
|  | pushl	%edx | 
|  | pushl	%edi | 
|  |  | 
|  | /* drive */ | 
|  | movb	0x8(%ebp), %dl | 
|  | /* enter real mode */ | 
|  | call	EXT_C(prot_to_real) | 
|  |  | 
|  | .code16 | 
|  | movb	$0x8, %ah | 
|  | int	$0x13		/* do the operation */ | 
|  | /* check if successful */ | 
|  | testb	%ah, %ah | 
|  | jnz	1f | 
|  | /* bogus BIOSes may not return an error number */ | 
|  | testb	$0x3f, %cl	/* 0 sectors means no disk */ | 
|  | jnz	1f		/* if non-zero, then succeed */ | 
|  | /* XXX 0x60 is one of the unused error numbers */ | 
|  | movb	$0x60, %ah | 
|  | 1: | 
|  | movb	%ah, %bl	/* save return value in %bl */ | 
|  | /* back to protected mode */ | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | /* restore %ebp */ | 
|  | leal	0x10(%esp), %ebp | 
|  |  | 
|  | /* heads */ | 
|  | movb	%dh, %al | 
|  | incl	%eax		/* the number of heads is counted from zero */ | 
|  | movl	0x10(%ebp), %edi | 
|  | movl	%eax, (%edi) | 
|  |  | 
|  | /* sectors */ | 
|  | xorl	%eax, %eax | 
|  | movb	%cl, %al | 
|  | andb	$0x3f, %al | 
|  | movl	0x14(%ebp), %edi | 
|  | movl	%eax, (%edi) | 
|  |  | 
|  | /* cylinders */ | 
|  | shrb	$6, %cl | 
|  | movb	%cl, %ah | 
|  | movb	%ch, %al | 
|  | incl	%eax		/* the number of cylinders is | 
|  | counted from zero */ | 
|  | movl	0xc(%ebp), %edi | 
|  | movl	%eax, (%edi) | 
|  |  | 
|  | xorl	%eax, %eax | 
|  | movb	%bl, %al	/* return value in %eax */ | 
|  |  | 
|  | popl	%edi | 
|  | popl	%edx | 
|  | popl	%ecx | 
|  | popl	%ebx | 
|  | popl	%ebp | 
|  |  | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | *   int get_diskinfo_floppy (int drive, unsigned long *cylinders, | 
|  | *                            unsigned long *heads, unsigned long *sectors) | 
|  | * | 
|  | *   Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an | 
|  | *   error occurs, then return non-zero, otherwise zero. | 
|  | */ | 
|  |  | 
|  | ENTRY(get_diskinfo_floppy) | 
|  | pushl	%ebp | 
|  | movl	%esp, %ebp | 
|  |  | 
|  | pushl	%ebx | 
|  | pushl	%ecx | 
|  | pushl	%edx | 
|  | pushl	%esi | 
|  |  | 
|  | /* drive */ | 
|  | movb	0x8(%ebp), %dl | 
|  | /* enter real mode */ | 
|  | call	EXT_C(prot_to_real) | 
|  |  | 
|  | .code16 | 
|  | /* init probe value */ | 
|  | movl	$probe_values-1, %esi | 
|  | 1: | 
|  | xorw	%ax, %ax | 
|  | int	$0x13		/* reset floppy controller */ | 
|  |  | 
|  | incw	%si | 
|  | movb	(%si), %cl | 
|  | cmpb	$0, %cl		/* probe failed if zero */ | 
|  | je	2f | 
|  |  | 
|  | /* perform read */ | 
|  | movw	$SCRATCHSEG, %ax | 
|  | movw	%ax, %es | 
|  | xorw	%bx, %bx | 
|  | movw	$0x0201, %ax | 
|  | movb	$0, %ch | 
|  | movb	$0, %dh | 
|  | int	$0x13 | 
|  |  | 
|  | /* FIXME: Read from floppy may fail even if the geometry is correct. | 
|  | So should retry at least three times.  */ | 
|  | jc	1b		/* next value */ | 
|  |  | 
|  | /* succeed */ | 
|  | jmp	2f | 
|  |  | 
|  | probe_values: | 
|  | .byte	36, 18, 15, 9, 0 | 
|  |  | 
|  | 2: | 
|  | /* back to protected mode */ | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | /* restore %ebp */ | 
|  | leal	0x10(%esp), %ebp | 
|  |  | 
|  | /* cylinders */ | 
|  | movl	0xc(%ebp), %eax | 
|  | movl	$80, %ebx | 
|  | movl	%ebx, (%eax) | 
|  | /* heads */ | 
|  | movl	0x10(%ebp), %eax | 
|  | movl	$2, %ebx | 
|  | movl	%ebx, (%eax) | 
|  | /* sectors */ | 
|  | movl	0x14(%ebp), %eax | 
|  | movzbl	%cl, %ebx | 
|  | movl	%ebx, (%eax) | 
|  |  | 
|  | /* return value in %eax */ | 
|  | xorl	%eax, %eax | 
|  | cmpb	$0, %cl | 
|  | jne	3f | 
|  | incl	%eax		/* %eax = 1 (non-zero) */ | 
|  | 3: | 
|  | popl	%esi | 
|  | popl	%edx | 
|  | popl	%ecx | 
|  | popl	%ebx | 
|  | popl	%ebp | 
|  |  | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* This is stolen from libc/x86/setjmp.S in the OSKit */ | 
|  | /* | 
|  | * Mach Operating System | 
|  | * Copyright (c) 1991,1990,1989 Carnegie Mellon University | 
|  | * All Rights Reserved. | 
|  | * | 
|  | * Permission to use, copy, modify and distribute this software and its | 
|  | * documentation is hereby granted, provided that both the copyright | 
|  | * notice and this permission notice appear in all copies of the | 
|  | * software, derivative works or modified versions, and any portions | 
|  | * thereof, and that both notices appear in supporting documentation. | 
|  | * | 
|  | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" | 
|  | * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR | 
|  | * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. | 
|  | * | 
|  | * Carnegie Mellon requests users of this software to return to | 
|  | * | 
|  | *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU | 
|  | *  School of Computer Science | 
|  | *  Carnegie Mellon University | 
|  | *  Pittsburgh PA 15213-3890 | 
|  | * | 
|  | * any improvements or extensions that they make and grant Carnegie Mellon | 
|  | * the rights to redistribute these changes. | 
|  | */ | 
|  | /* | 
|  | * C library -- _setjmp, _longjmp | 
|  | * | 
|  | *      _longjmp(a,v) | 
|  | * will generate a "return(v)" from | 
|  | * the last call to | 
|  | *      _setjmp(a) | 
|  | * by restoring registers from the stack, | 
|  | * The previous signal state is NOT restored. | 
|  | * | 
|  | */ | 
|  |  | 
|  | ENTRY(grub_setjmp) | 
|  | movl	4(%esp), %ecx		/* fetch buffer */ | 
|  | movl	%ebx, 0(%ecx) | 
|  | movl	%esi, 4(%ecx) | 
|  | movl	%edi, 8(%ecx) | 
|  | movl	%ebp, 12(%ecx)		/* save frame pointer of caller */ | 
|  | popl	%edx | 
|  | movl	%esp, 16(%ecx)		/* save stack pointer of caller */ | 
|  | movl	%edx, 20(%ecx)		/* save pc of caller */ | 
|  | xorl	%eax, %eax | 
|  | jmp     *%edx | 
|  |  | 
|  | ENTRY(grub_longjmp) | 
|  | movl	8(%esp), %eax		/* return(v) */ | 
|  | movl	4(%esp), %ecx		/* fetch buffer */ | 
|  | movl	0(%ecx), %ebx | 
|  | movl	4(%ecx), %esi | 
|  | movl	8(%ecx), %edi | 
|  | movl	12(%ecx), %ebp | 
|  | movl	16(%ecx), %esp | 
|  | orl	%eax, %eax | 
|  | jnz	0f | 
|  | incl	%eax | 
|  | 0:	jmp	*20(%ecx)		/* done, return.... */ | 
|  |  | 
|  |  | 
|  | /* | 
|  | * putchar(c)   : Puts character on the screen, interpreting '\n' as in the | 
|  | *                UNIX fashion. | 
|  | * | 
|  | * BIOS call "INT 10H Function 0Eh" to write character to console | 
|  | *      Call with       %ah = 0x0e | 
|  | *                      %al = character | 
|  | *                      %bh = page | 
|  | *                      %bl = foreground color ( graphics modes) | 
|  | */ | 
|  |  | 
|  |  | 
|  | ENTRY(grub_putchar) | 
|  | push	%ebp | 
|  | push	%eax | 
|  | push	%ebx | 
|  |  | 
|  | movb	0x10(%esp), %bl | 
|  |  | 
|  | /* if not '\n', just print the character */ | 
|  | cmpb	$0xa, %bl | 
|  | jne	pc_notnewline | 
|  |  | 
|  | /* if newline, print CR as well */ | 
|  | pushl	$0xd | 
|  | call	EXT_C(grub_putchar) | 
|  | popl	%eax | 
|  |  | 
|  | pc_notnewline: | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | movb	%bl, %al | 
|  | movb	$0xe, %ah | 
|  | movw	$1, %bx | 
|  | int	$0x10 | 
|  |  | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | pop	%ebx | 
|  | pop	%eax | 
|  | pop	%ebp | 
|  | ret | 
|  |  | 
|  | #ifndef STAGE1_5 | 
|  | /* get_code_end() :  return the address of the end of the code | 
|  | * This is here so that it can be replaced by asmstub.c. | 
|  | */ | 
|  | ENTRY(get_code_end) | 
|  | /* will be the end of the bss */ | 
|  | # if defined(HAVE_END_SYMBOL) | 
|  | movl	$end, %eax | 
|  | # elif defined(HAVE_USCORE_END_SYMBOL) | 
|  | movl	$_end, %eax | 
|  | # endif | 
|  | shrl	$2, %eax		/* Round up to the next word. */ | 
|  | incl	%eax | 
|  | shll	$2, %eax | 
|  | ret | 
|  | #endif /* ! STAGE1_5 */ | 
|  |  | 
|  | /* | 
|  | * | 
|  | * get_memsize(i) :  return the memory size in KB. i == 0 for conventional | 
|  | *		memory, i == 1 for extended memory | 
|  | *	BIOS call "INT 12H" to get conventional memory size | 
|  | *	BIOS call "INT 15H, AH=88H" to get extended memory size | 
|  | *		Both have the return value in AX. | 
|  | * | 
|  | */ | 
|  |  | 
|  | ENTRY(get_memsize) | 
|  | push	%ebp | 
|  | push	%ebx | 
|  |  | 
|  | mov	0xc(%esp), %ebx | 
|  |  | 
|  | call	EXT_C(prot_to_real)	/* enter real mode */ | 
|  | .code16 | 
|  |  | 
|  | cmpb	$0x1, %bl | 
|  | DATA32	je	xext | 
|  |  | 
|  | int	$0x12 | 
|  | DATA32	jmp	xdone | 
|  |  | 
|  | xext: | 
|  | movb	$0x88, %ah | 
|  | int	$0x15 | 
|  |  | 
|  | xdone: | 
|  | movw	%ax, %bx | 
|  |  | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | movw	%bx, %ax | 
|  | pop	%ebx | 
|  | pop	%ebp | 
|  | ret | 
|  |  | 
|  |  | 
|  | #ifndef STAGE1_5 | 
|  |  | 
|  | /* | 
|  | * | 
|  | * get_eisamemsize() :  return packed EISA memory map, lower 16 bits is | 
|  | *		memory between 1M and 16M in 1K parts, upper 16 bits is | 
|  | *		memory above 16M in 64K parts.  If error, return -1. | 
|  | *	BIOS call "INT 15H, AH=E801H" to get EISA memory map, | 
|  | *		AX = memory between 1M and 16M in 1K parts. | 
|  | *		BX = memory above 16M in 64K parts. | 
|  | * | 
|  | */ | 
|  |  | 
|  | ENTRY(get_eisamemsize) | 
|  | push	%ebp | 
|  | push	%ebx | 
|  | push	%ecx | 
|  | push	%edx | 
|  |  | 
|  | call	EXT_C(prot_to_real)	/* enter real mode */ | 
|  | .code16 | 
|  |  | 
|  | movw	$0xe801, %ax | 
|  | int	$0x15 | 
|  |  | 
|  | shll	$16, %ebx | 
|  | movw	%ax, %bx | 
|  |  | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | movl	$0xFFFFFFFF, %eax | 
|  | cmpb	$0x86, %bh | 
|  | je	xnoteisa | 
|  |  | 
|  | movl	%ebx, %eax | 
|  |  | 
|  | xnoteisa: | 
|  | pop	%edx | 
|  | pop	%ecx | 
|  | pop	%ebx | 
|  | pop	%ebp | 
|  | ret | 
|  |  | 
|  | /* | 
|  | * | 
|  | * get_mmap_entry(addr, cont) :  address and old continuation value (zero to | 
|  | *		start), for the Query System Address Map BIOS call. | 
|  | * | 
|  | *  Sets the first 4-byte int value of "addr" to the size returned by | 
|  | *  the call.  If the call fails, sets it to zero. | 
|  | * | 
|  | *	Returns:  new (non-zero) continuation value, 0 if done. | 
|  | * | 
|  | * NOTE: Currently hard-coded for a maximum buffer length of 1024. | 
|  | */ | 
|  |  | 
|  | ENTRY(get_mmap_entry) | 
|  | push	%ebp | 
|  | push	%ebx | 
|  | push	%ecx | 
|  | push	%edx | 
|  | push	%edi | 
|  | push	%esi | 
|  |  | 
|  | /* place address (+4) in ES:DI */ | 
|  | movl	0x1c(%esp), %eax | 
|  | addl	$4, %eax | 
|  | movl	%eax, %edi | 
|  | andl	$0xf, %edi | 
|  | shrl	$4, %eax | 
|  | movl	%eax, %esi | 
|  |  | 
|  | /* set continuation value */ | 
|  | movl	0x20(%esp), %ebx | 
|  |  | 
|  | /* set default maximum buffer size */ | 
|  | movl	$0x14, %ecx | 
|  |  | 
|  | /* set EDX to 'SMAP' */ | 
|  | movl	$0x534d4150, %edx | 
|  |  | 
|  | call	EXT_C(prot_to_real)	/* enter real mode */ | 
|  | .code16 | 
|  |  | 
|  | movw	%si, %es | 
|  | movl	$0xe820, %eax | 
|  | int	$0x15 | 
|  |  | 
|  | DATA32	jc	xnosmap | 
|  |  | 
|  | cmpl	$0x534d4150, %eax | 
|  | DATA32	jne	xnosmap | 
|  |  | 
|  | cmpl	$0x14, %ecx | 
|  | DATA32	jl	xnosmap | 
|  |  | 
|  | cmpl	$0x400, %ecx | 
|  | DATA32	jg	xnosmap | 
|  |  | 
|  | DATA32	jmp	xsmap | 
|  |  | 
|  | xnosmap: | 
|  | movl	$0, %ecx | 
|  |  | 
|  | xsmap: | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | /* write length of buffer (zero if error) into "addr" */ | 
|  | movl	0x1c(%esp), %eax | 
|  | movl	%ecx, (%eax) | 
|  |  | 
|  | /* set return value to continuation */ | 
|  | movl	%ebx, %eax | 
|  |  | 
|  | pop	%esi | 
|  | pop	%edi | 
|  | pop	%edx | 
|  | pop	%ecx | 
|  | pop	%ebx | 
|  | pop	%ebp | 
|  | ret | 
|  |  | 
|  | /* | 
|  | * gateA20(int linear) | 
|  | * | 
|  | * Gate address-line 20 for high memory. | 
|  | * | 
|  | * This routine is probably overconservative in what it does, but so what? | 
|  | * | 
|  | * It also eats any keystrokes in the keyboard buffer.  :-( | 
|  | */ | 
|  |  | 
|  | ENTRY(gateA20) | 
|  | pushl	%eax | 
|  |  | 
|  | call    gloop1 | 
|  |  | 
|  | movb	$KC_CMD_WOUT, %al | 
|  | outb	$K_CMD | 
|  |  | 
|  | gloopint1: | 
|  | inb	$K_STATUS | 
|  | andb	$K_IBUF_FUL, %al | 
|  | jnz	gloopint1 | 
|  |  | 
|  | movb	$KB_OUTPUT_MASK, %al | 
|  | cmpb	$0, 0x8(%esp) | 
|  | jz	gdoit | 
|  |  | 
|  | orb	$KB_A20_ENABLE, %al | 
|  | gdoit: | 
|  | outb	$K_RDWR | 
|  |  | 
|  | call	gloop1 | 
|  |  | 
|  | popl	%eax | 
|  | ret | 
|  |  | 
|  | gloop1: | 
|  | inb	$K_STATUS | 
|  | andb	$K_IBUF_FUL, %al | 
|  | jnz	gloop1 | 
|  |  | 
|  | gloop2: | 
|  | inb	$K_STATUS | 
|  | andb	$K_OBUF_FUL, %al | 
|  | jz	gloop2ret | 
|  | inb	$K_RDWR | 
|  | jmp	gloop2 | 
|  |  | 
|  | gloop2ret: | 
|  | ret | 
|  |  | 
|  |  | 
|  | ENTRY(patch_code)	/* labels start with "pc_" */ | 
|  | .code16 | 
|  |  | 
|  | mov	%cs, %ax | 
|  | mov	%ax, %ds | 
|  | mov	%ax, %es | 
|  | mov	%ax, %fs | 
|  | mov	%ax, %gs | 
|  | ADDR32	movl	$0, 0 | 
|  | pc_stop: | 
|  | hlt | 
|  | DATA32	jmp	pc_stop | 
|  | ENTRY(patch_code_end) | 
|  |  | 
|  | .code32 | 
|  |  | 
|  |  | 
|  | /* | 
|  | * linux_boot() | 
|  | * | 
|  | * Does some funky things (including on the stack!), then jumps to the | 
|  | * entry point of the Linux setup code. | 
|  | */ | 
|  |  | 
|  | ENTRY(linux_boot) | 
|  | /* don't worry about saving anything, we're committed at this point */ | 
|  | cld	/* forward copying */ | 
|  |  | 
|  | /* copy kernel */ | 
|  | movl	$LINUX_SETUP, %eax | 
|  | movl	LINUX_KERNEL_LEN_OFFSET(%eax), %ecx | 
|  | shll	$2, %ecx | 
|  | movl	$LINUX_STAGING_AREA, %esi | 
|  | movl	$LINUX_KERNEL, %edi | 
|  |  | 
|  | rep | 
|  | movsl | 
|  |  | 
|  | ENTRY(big_linux_boot) | 
|  | /* XXX new stack pointer in safe area for calling functions */ | 
|  | movl	$0x4000, %esp | 
|  | call	EXT_C(stop_floppy) | 
|  |  | 
|  | /* final setup for linux boot */ | 
|  |  | 
|  | movw	$LINUX_SETUP_SEG, %ax | 
|  | movw	%ax, segment | 
|  |  | 
|  | xorl	%eax, %eax | 
|  | movl	%eax, offset | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | /* final setup for linux boot */ | 
|  | movw	$LINUX_SETUP_STACK, %sp | 
|  |  | 
|  | movw	$LINUX_INIT_SEG, %ax | 
|  | movw	%ax, %ss | 
|  |  | 
|  | /* jump to start */ | 
|  | DATA32	ADDR32	ljmp	(offset) | 
|  | .code32 | 
|  |  | 
|  |  | 
|  | /* | 
|  | * multi_boot(int start, int mbi) | 
|  | * | 
|  | *  This starts a kernel in the manner expected of the multiboot standard. | 
|  | */ | 
|  |  | 
|  | ENTRY(multi_boot) | 
|  | /* no need to save anything */ | 
|  | call	EXT_C(stop_floppy) | 
|  |  | 
|  | movl	$0x2BADB002, %eax | 
|  | movl	0x8(%esp), %ebx | 
|  |  | 
|  | /* boot kernel here (absolute address call) */ | 
|  | call	*0x4(%esp) | 
|  |  | 
|  | /* error */ | 
|  | call	EXT_C(stop) | 
|  |  | 
|  |  | 
|  | /* | 
|  | * cls() | 
|  | * BIOS call "INT 10H Function 0Fh" to get current video mode | 
|  | *	Call with	%ah = 0x0f | 
|  | *      Returns         %al = (video mode) | 
|  | *                      %bh = (page number) | 
|  | * BIOS call "INT 10H Function 00h" to set the video mode (clears screen) | 
|  | *	Call with	%ah = 0x00 | 
|  | *                      %al = (video mode) | 
|  | */ | 
|  |  | 
|  |  | 
|  | ENTRY(cls) | 
|  | push	%ebp | 
|  | push	%eax | 
|  | push	%ebx                    /* save EBX */ | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | movb	$0xf, %ah | 
|  | int	$0x10			/* Get Current Video mode */ | 
|  | xorb	%ah, %ah | 
|  | int	$0x10                   /* Set Video mode (clears screen) */ | 
|  |  | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | pop	%ebx | 
|  | pop	%eax | 
|  | pop	%ebp | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * nocursor() | 
|  | * BIOS call "INT 10H Function 01h" to set cursor type | 
|  | *      Call with       %ah = 0x01 | 
|  | *                      %ch = cursor starting scanline | 
|  | *                      %cl = cursor ending scanline | 
|  | */ | 
|  |  | 
|  | ENTRY(nocursor) | 
|  | push	%ebp | 
|  | push	%eax | 
|  | push	%ebx                    /* save EBX */ | 
|  | push	%edx | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | movw    $0x2000, %cx | 
|  | movb    $0x1, %ah | 
|  | int     $0x10 | 
|  |  | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | pop	%edx | 
|  | pop	%ebx | 
|  | pop	%eax | 
|  | pop	%ebp | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * getxy() | 
|  | * BIOS call "INT 10H Function 03h" to get cursor position | 
|  | *	Call with	%ah = 0x03 | 
|  | *			%bh = page | 
|  | *      Returns         %ch = starting scan line | 
|  | *                      %cl = ending scan line | 
|  | *                      %dh = row (0 is top) | 
|  | *                      %dl = column (0 is left) | 
|  | */ | 
|  |  | 
|  |  | 
|  | ENTRY(getxy) | 
|  | push	%ebp | 
|  | push	%ebx                    /* save EBX */ | 
|  | push	%ecx                    /* save ECX */ | 
|  | push	%edx | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | xorb	%bh, %bh                /* set page to 0 */ | 
|  | movb	$0x3, %ah | 
|  | int	$0x10			/* get cursor position */ | 
|  |  | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | movb	%dl, %ah | 
|  | movb	%dh, %al | 
|  |  | 
|  | pop	%edx | 
|  | pop	%ecx | 
|  | pop	%ebx | 
|  | pop	%ebp | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * gotoxy(x,y) | 
|  | * BIOS call "INT 10H Function 02h" to set cursor position | 
|  | *	Call with	%ah = 0x02 | 
|  | *			%bh = page | 
|  | *                      %dh = row (0 is top) | 
|  | *                      %dl = column (0 is left) | 
|  | */ | 
|  |  | 
|  |  | 
|  | ENTRY(gotoxy) | 
|  | push	%ebp | 
|  | push	%eax | 
|  | push	%ebx                    /* save EBX */ | 
|  | push	%edx | 
|  |  | 
|  | movb	0x14(%esp), %dl          /* %dl = x */ | 
|  | movb	0x18(%esp), %dh          /* %dh = y */ | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | xorb	%bh, %bh                /* set page to 0 */ | 
|  | movb	$0x2, %ah | 
|  | int	$0x10			/* set cursor position */ | 
|  |  | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | pop	%edx | 
|  | pop	%ebx | 
|  | pop	%eax | 
|  | pop	%ebp | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * set_attrib(attr) :  Sets the character attributes for character at | 
|  | *		current cursor position. | 
|  | * | 
|  | *  Bitfields for character's display attribute: | 
|  | *  Bit(s)	Description | 
|  | *   7		foreground blink | 
|  | *   6-4	background color | 
|  | *   3		foreground bright | 
|  | *   2-0	foreground color | 
|  | * | 
|  | *  Values for character color: | 
|  | *		Normal		Bright | 
|  | *   000b	black		dark gray | 
|  | *   001b	blue		light blue | 
|  | *   010b	green		light green | 
|  | *   011b	cyan		light cyan | 
|  | *   100b	red		light red | 
|  | *   101b	magenta		light magenta | 
|  | *   110b	brown		yellow | 
|  | *   111b	light gray	white | 
|  | * | 
|  | * BIOS call "INT 10H Function 08h" to read character and attribute data | 
|  | *	Call with	%ah = 0x08 | 
|  | *			%bh = page | 
|  | *	Returns		%ah = character attribute | 
|  | *			%al = character value | 
|  | * BIOS call "INT 10H Function 09h" to write character and attribute data | 
|  | *	Call with	%ah = 0x09 | 
|  | *			%al = character value | 
|  | *			%bh = page | 
|  | *			%bl = character attribute | 
|  | *			%cx = count to display (???, possible side-effects!!) | 
|  | */ | 
|  |  | 
|  | ENTRY(set_attrib) | 
|  | push	%ebp | 
|  | push	%eax | 
|  | push	%ebx | 
|  | push	%ecx | 
|  |  | 
|  | movl	0x14(%esp), %ecx | 
|  | xorl	%ebx, %ebx | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | movb	$0x8, %ah | 
|  | int	$0x10 | 
|  | movb	$0x9, %ah | 
|  | movb	%cl, %bl | 
|  | movw	$1, %cx | 
|  | int	$0x10 | 
|  |  | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | pop	%ecx | 
|  | pop	%ebx | 
|  | pop	%eax | 
|  | pop	%ebp | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * getrtsecs() | 
|  | *	if a seconds value can be read, read it and return it (BCD), | 
|  | *      otherwise return 0xFF | 
|  | * BIOS call "INT 1AH Function 02H" to check whether a character is pending | 
|  | *	Call with	%ah = 0x2 | 
|  | *	Return: | 
|  | *		If RT Clock can give correct values | 
|  | *			%ch = hour (BCD) | 
|  | *			%cl = minutes (BCD) | 
|  | *                      %dh = seconds (BCD) | 
|  | *                      %dl = daylight savings time (00h std, 01h daylight) | 
|  | *			Carry flag = clear | 
|  | *		else | 
|  | *			Carry flag = set | 
|  | *                         (this indicates that the clock is updating, or | 
|  | *                          that it isn't running) | 
|  | */ | 
|  | ENTRY(getrtsecs) | 
|  | push	%ebp | 
|  | push	%ecx | 
|  | push	%edx | 
|  |  | 
|  | call	EXT_C(prot_to_real)	/* enter real mode */ | 
|  | .code16 | 
|  |  | 
|  | movb	$0x2, %ah | 
|  | int	$0x1a | 
|  |  | 
|  | DATA32	jnc	gottime | 
|  | movb	$0xff, %dh | 
|  |  | 
|  | gottime: | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | movb	%dh, %al | 
|  |  | 
|  | pop	%edx | 
|  | pop	%ecx | 
|  | pop	%ebp | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * currticks() | 
|  | *	return the real time in ticks, of which there are about | 
|  | *	18-20 per second | 
|  | */ | 
|  | ENTRY(currticks) | 
|  | pushl	%ebp | 
|  | pushl	%ecx | 
|  | pushl	%edx | 
|  |  | 
|  | call	EXT_C(prot_to_real)	/* enter real mode */ | 
|  | .code16 | 
|  |  | 
|  | xorl	%eax, %eax | 
|  | int	$0x1a | 
|  |  | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | movl	%ecx, %eax | 
|  | shll	$16, %eax | 
|  | movw	%dx, %ax | 
|  |  | 
|  | popl	%edx | 
|  | popl	%ecx | 
|  | popl	%ebp | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * remap_ascii_char remaps the ascii code %bl to another if the code is | 
|  | * contained in ASCII_KEY_MAP. | 
|  | */ | 
|  | .code16 | 
|  |  | 
|  | remap_ascii_char: | 
|  | pushw	%si | 
|  |  | 
|  | movw	$ABS(EXT_C(ascii_key_map)), %si | 
|  | 1: | 
|  | lodsw | 
|  | /* check if this is the end */ | 
|  | testw	%ax, %ax | 
|  | jz	2f | 
|  | /* check if this matches the ascii code */ | 
|  | cmpb	%al, %bl | 
|  | jne	1b | 
|  | /* if so, perform the mapping */ | 
|  | movb	%ah, %bl | 
|  | 2: | 
|  | /* restore %si */ | 
|  | popw	%si | 
|  |  | 
|  | ret | 
|  |  | 
|  | .code32 | 
|  |  | 
|  | .align	4 | 
|  | ENTRY(ascii_key_map) | 
|  | .space	(KEY_MAP_SIZE + 1) * 2 | 
|  |  | 
|  |  | 
|  | /* | 
|  | * getkey() | 
|  | * BIOS call "INT 16H Function 00H" to read character from keyboard | 
|  | *	Call with	%ah = 0x0 | 
|  | *	Return:		%ah = keyboard scan code | 
|  | *			%al = ASCII character | 
|  | */ | 
|  |  | 
|  | ENTRY(getkey) | 
|  | push	%ebp | 
|  | push	%ebx			/* save %ebx */ | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | int	$0x16 | 
|  |  | 
|  | movw	%ax, %bx		/* real_to_prot uses %eax */ | 
|  | call	remap_ascii_char | 
|  |  | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | movw	%bx, %ax | 
|  |  | 
|  | pop	%ebx | 
|  | pop	%ebp | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * checkkey() | 
|  | *	if there is a character pending, return it; otherwise return -1 | 
|  | * BIOS call "INT 16H Function 01H" to check whether a character is pending | 
|  | *	Call with	%ah = 0x1 | 
|  | *	Return: | 
|  | *		If key waiting to be input: | 
|  | *			%ah = keyboard scan code | 
|  | *			%al = ASCII character | 
|  | *			Zero flag = clear | 
|  | *		else | 
|  | *			Zero flag = set | 
|  | */ | 
|  | ENTRY(checkkey) | 
|  | push	%ebp | 
|  | push	%ebx | 
|  |  | 
|  | xorl	%ebx, %ebx | 
|  |  | 
|  | call	EXT_C(prot_to_real)	/* enter real mode */ | 
|  | .code16 | 
|  |  | 
|  | movb	$0x1, %ah | 
|  | int	$0x16 | 
|  |  | 
|  | DATA32	jz	notpending | 
|  |  | 
|  | movw	%ax, %bx | 
|  | call	remap_ascii_char | 
|  | DATA32	jmp	pending | 
|  |  | 
|  | notpending: | 
|  | movl	$0xFFFFFFFF, %ebx | 
|  |  | 
|  | pending: | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | mov	%ebx, %eax | 
|  |  | 
|  | pop	%ebx | 
|  | pop	%ebp | 
|  | ret | 
|  |  | 
|  | #endif /* STAGE1_5 */ | 
|  |  | 
|  | /* | 
|  | *  This is the area for all of the special variables. | 
|  | */ | 
|  |  | 
|  | .p2align	2	/* force 4-byte alignment */ | 
|  |  | 
|  | protstack: | 
|  | .long	PROTSTACKINIT | 
|  |  | 
|  | VARIABLE(boot_drive) | 
|  | .long	0 | 
|  |  | 
|  | /* an address can only be long-jumped to if it is in memory, this | 
|  | is used by multiple routines */ | 
|  | offset: | 
|  | .long	0x8000 | 
|  | segment: | 
|  | .word	0 | 
|  |  | 
|  | /* | 
|  | * This is the Global Descriptor Table | 
|  | * | 
|  | *  An entry, a "Segment Descriptor", looks like this: | 
|  | * | 
|  | * 31          24         19   16                 7           0 | 
|  | * ------------------------------------------------------------ | 
|  | * |             | |B| |A|       | |   |1|0|E|W|A|            | | 
|  | * | BASE 31..24 |G|/|0|V| LIMIT |P|DPL|  TYPE   | BASE 23:16 | | 
|  | * |             | |D| |L| 19..16| |   |1|1|C|R|A|            | | 
|  | * ------------------------------------------------------------ | 
|  | * |                             |                            | | 
|  | * |        BASE 15..0           |       LIMIT 15..0          | | 
|  | * |                             |                            | | 
|  | * ------------------------------------------------------------ | 
|  | * | 
|  | *  Note the ordering of the data items is reversed from the above | 
|  | *  description. | 
|  | */ | 
|  |  | 
|  | .p2align	2	/* force 4-byte alignment */ | 
|  | gdt: | 
|  | .word	0, 0 | 
|  | .byte	0, 0, 0, 0 | 
|  |  | 
|  | /* code segment */ | 
|  | .word	0xFFFF, 0 | 
|  | .byte	0, 0x9A, 0xCF, 0 | 
|  |  | 
|  | /* data segment */ | 
|  | .word	0xFFFF, 0 | 
|  | .byte	0, 0x92, 0xCF, 0 | 
|  |  | 
|  | /* 16 bit real mode CS */ | 
|  | .word	0xFFFF, 0 | 
|  | .byte	0, 0x9E, 0, 0 | 
|  |  | 
|  | /* 16 bit real mode DS */ | 
|  | .word	0xFFFF, 0 | 
|  | .byte	0, 0x92, 0, 0 | 
|  |  | 
|  |  | 
|  | /* this is the GDT descriptor */ | 
|  | gdtdesc: | 
|  | .word	0x27			/* limit */ | 
|  | .long	gdt			/* addr */ |