|  | /* | 
|  | *  GRUB  --  GRand Unified Bootloader | 
|  | *  Copyright (C) 1999,2000,2001,2002,2004 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. | 
|  | */ | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Note: These functions defined in this file may be called from C. | 
|  | *       Be careful of that you must not modify some registers. Quote | 
|  | *       from gcc-2.95.2/gcc/config/i386/i386.h: | 
|  |  | 
|  | 1 for registers not available across function calls. | 
|  | These must include the FIXED_REGISTERS and also any | 
|  | registers that can be used without being saved. | 
|  | The latter must include the registers where values are returned | 
|  | and the register where structure-value addresses are passed. | 
|  | Aside from that, you can include as many other registers as you like. | 
|  |  | 
|  | ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg | 
|  | {  1, 1, 1, 0, 0, 0, 0, 1, 1,  1,  1,  1,  1,  1,  1,  1,  1 } | 
|  | */ | 
|  |  | 
|  | #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	0xFFFFFF | 
|  | /* This variable is here only because of a historical reason.  */ | 
|  | VARIABLE(saved_entryno) | 
|  | .long	0 | 
|  | VARIABLE(stage2_id) | 
|  | .byte	STAGE2_ID | 
|  | VARIABLE(force_lba) | 
|  | .byte	0 | 
|  | 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 | 
|  |  | 
|  | #ifndef SUPPORT_DISKLESS | 
|  | /* | 
|  | * Save the sector number of the second sector (i.e. this sector) | 
|  | * in INSTALL_SECOND_SECTOR. See also "stage2/start.S". | 
|  | */ | 
|  | ADDR32	movl	%ebp, EXT_C(install_second_sector) | 
|  | #endif | 
|  |  | 
|  | /* set up the real mode/BIOS stack */ | 
|  | movl	$STACKOFF, %ebp | 
|  | movl	%ebp, %esp | 
|  |  | 
|  | sti		/* we're safe again */ | 
|  |  | 
|  | #ifndef SUPPORT_DISKLESS | 
|  | /* save boot drive reference */ | 
|  | ADDR32	movb	%dl, EXT_C(boot_drive) | 
|  |  | 
|  | /* reset disk system (%ah = 0) */ | 
|  | int	$0x13 | 
|  | #endif | 
|  |  | 
|  | /* 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) | 
|  | pusha | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  | xorb	%dl, %dl | 
|  | int	$0x13 | 
|  | DATA32  call EXT_C(real_to_prot) | 
|  | .code32 | 
|  | popa | 
|  | ret | 
|  |  | 
|  | /* | 
|  | * grub_reboot() | 
|  | * | 
|  | * Reboot the system. At the moment, rely on BIOS. | 
|  | */ | 
|  | ENTRY(grub_reboot) | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  | /* cold boot */ | 
|  | movw	$0x0472, %di | 
|  | movw	%ax, (%di) | 
|  | ljmp	$0xFFFF, $0x0000 | 
|  | .code32 | 
|  |  | 
|  | /* | 
|  | * grub_halt(int no_apm) | 
|  | * | 
|  | * Halt the system, using APM if possible. If NO_APM is true, don't use | 
|  | * APM even if it is available. | 
|  | */ | 
|  | ENTRY(grub_halt) | 
|  | /* get the argument */ | 
|  | movl	4(%esp), %eax | 
|  |  | 
|  | /* see if zero */ | 
|  | testl	%eax, %eax | 
|  | jnz	EXT_C(stop) | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | /* detect APM */ | 
|  | movw	$0x5300, %ax | 
|  | xorw	%bx, %bx | 
|  | int	$0x15 | 
|  | jc	EXT_C(hard_stop) | 
|  | /* don't check %bx for buggy BIOSes... */ | 
|  |  | 
|  | /* disconnect APM first */ | 
|  | movw	$0x5304, %ax | 
|  | xorw	%bx, %bx | 
|  | int	$0x15 | 
|  |  | 
|  | /* connect APM */ | 
|  | movw	$0x5301, %ax | 
|  | xorw	%bx, %bx | 
|  | int	$0x15 | 
|  | jc	EXT_C(hard_stop) | 
|  |  | 
|  | /* set APM protocol level - 1.1 or bust. (this covers APM 1.2 also) */ | 
|  | movw	$0x530E, %ax | 
|  | xorw	%bx, %bx | 
|  | movw	$0x0101, %cx | 
|  | int	$0x15 | 
|  | jc	EXT_C(hard_stop) | 
|  |  | 
|  | /* set the power state to off */ | 
|  | movw	$0x5307, %ax | 
|  | movw	$1, %bx | 
|  | movw	$3, %cx | 
|  | int	$0x15 | 
|  |  | 
|  | /* shouldn't reach here */ | 
|  | jmp	EXT_C(hard_stop) | 
|  | .code32 | 
|  |  | 
|  | /* | 
|  | * track_int13(int drive) | 
|  | * | 
|  | * Track the int13 handler to probe I/O address space. | 
|  | */ | 
|  | ENTRY(track_int13) | 
|  | pushl	%ebp | 
|  | movl	%esp, %ebp | 
|  |  | 
|  | pushl	%ebx | 
|  | pushl	%edi | 
|  |  | 
|  | /* copy the original int13 handler segment:offset */ | 
|  | movl	$0x4c, %edi | 
|  | movl	(%edi), %eax | 
|  | movl	%eax, track_int13_addr | 
|  |  | 
|  | /* 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 | 
|  |  | 
|  | /* save FLAGS on the stack to emulate int13 */ | 
|  | pushfw | 
|  |  | 
|  | /* set the TF flag */ | 
|  | /* FIXME: this can be simplified not to use AX */ | 
|  | pushfw | 
|  | popw	%ax | 
|  | orw	$0x100, %ax | 
|  | pushw	%ax | 
|  | popfw | 
|  |  | 
|  | movw	$0x0201, %ax | 
|  |  | 
|  | .byte	0x9a		/* lcall */ | 
|  | track_int13_addr: | 
|  | .word	0		/* offset */ | 
|  | .word	0		/* segment */ | 
|  |  | 
|  | /* TF is cleared here automatically */ | 
|  |  | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | /* restore the int1 handler */ | 
|  | movl	$0x4, %edi | 
|  | popl	(%edi) | 
|  |  | 
|  | popl	%edi | 
|  | popl	%ebx | 
|  | 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 */ | 
|  | lodsb	(%si), %al | 
|  | movzbw	%al, %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 | 
|  |  | 
|  | .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	%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) | 
|  |  | 
|  | /* decrease the lower memory size and set it to the BIOS memory */ | 
|  | movl	$0x413, %edi | 
|  | decw	(%edi) | 
|  | xorl	%eax, %eax | 
|  | movw	(%edi), %ax | 
|  |  | 
|  | /* 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	%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 | 
|  |  | 
|  | #ifdef ABSOLUTE_WITHOUT_ASTERISK | 
|  | DATA32	ADDR32	ljmp	(offset) | 
|  | #else | 
|  | DATA32	ADDR32	ljmp	*(offset) | 
|  | #endif | 
|  | .code32 | 
|  | #endif /* STAGE1_5 */ | 
|  |  | 
|  |  | 
|  | #ifdef STAGE1_5 | 
|  | /* | 
|  | * chain_stage2(segment, offset, second_sector) | 
|  | * | 
|  | *  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 | 
|  |  | 
|  | /* set up to pass the second sector of stage2 */ | 
|  | movl	0xc(%esp), %ecx | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | movl	%ecx, %ebp | 
|  |  | 
|  | #ifdef ABSOLUTE_WITHOUT_ASTERISK | 
|  | DATA32	ADDR32	ljmp	(offset) | 
|  | #else | 
|  | DATA32	ADDR32	ljmp	*(offset) | 
|  | #endif | 
|  |  | 
|  | .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 ax, int drive, void *dap) | 
|  | * | 
|  | *   Call IBM/MS INT13 Extensions (int 13 %ax=AX) 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	%esi | 
|  | pushl	%ebx | 
|  |  | 
|  | /* 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 | 
|  | /* ax */ | 
|  | movw	0x8(%ebp), %bx | 
|  | /* enter real mode */ | 
|  | call	EXT_C(prot_to_real) | 
|  |  | 
|  | .code16 | 
|  | movw	%bx, %ax | 
|  | 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	%ebx | 
|  | popl	%esi | 
|  | 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	%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	%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 | 
|  |  | 
|  | /* 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 | 
|  |  | 
|  | movb	%ah, %bl	/* save the major version into %bl */ | 
|  |  | 
|  | /* check if AH=0x42 is supported if FORCE_LBA is zero */ | 
|  | movb	EXT_C(force_lba), %al | 
|  | testb	%al, %al | 
|  | jnz	2f | 
|  | andw	$1, %cx | 
|  | jnz	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	%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	%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	0x8(%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	%ebx | 
|  | popl	%ebp | 
|  |  | 
|  | ret | 
|  |  | 
|  |  | 
|  | #if 0 | 
|  | /* | 
|  | *   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	%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	0x8(%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	%ebx | 
|  | popl	%ebp | 
|  |  | 
|  | ret | 
|  | #endif | 
|  |  | 
|  |  | 
|  | /* Source files are splitted, as they have different copyrights.  */ | 
|  | #ifndef STAGE1_5 | 
|  | # include "setjmp.S" | 
|  | # include "apm.S" | 
|  | #endif /* ! STAGE1_5 */ | 
|  |  | 
|  |  | 
|  |  | 
|  | #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 | 
|  |  | 
|  | 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	%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	%edi | 
|  | push	%esi | 
|  |  | 
|  | /* place address (+4) in ES:DI */ | 
|  | movl	0x14(%esp), %eax | 
|  | addl	$4, %eax | 
|  | movl	%eax, %edi | 
|  | andl	$0xf, %edi | 
|  | shrl	$4, %eax | 
|  | movl	%eax, %esi | 
|  |  | 
|  | /* set continuation value */ | 
|  | movl	0x18(%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	0x14(%esp), %eax | 
|  | movl	%ecx, (%eax) | 
|  |  | 
|  | /* set return value to continuation */ | 
|  | movl	%ebx, %eax | 
|  |  | 
|  | pop	%esi | 
|  | pop	%edi | 
|  | pop	%ebx | 
|  | pop	%ebp | 
|  | ret | 
|  |  | 
|  | /* | 
|  | * get_rom_config_table() | 
|  | * | 
|  | * Get the linear address of a ROM configuration table. Return zero, | 
|  | * if fails. | 
|  | */ | 
|  |  | 
|  | ENTRY(get_rom_config_table) | 
|  | pushl	%ebp | 
|  | pushl	%ebx | 
|  |  | 
|  | /* zero %ebx for simplicity */ | 
|  | xorl	%ebx, %ebx | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | movw	$0xc0, %ax | 
|  | int	$0x15 | 
|  |  | 
|  | jc	no_rom_table | 
|  | testb	%ah, %ah | 
|  | jnz	no_rom_table | 
|  |  | 
|  | movw	%es, %dx | 
|  | jmp	found_rom_table | 
|  |  | 
|  | no_rom_table: | 
|  | xorw	%dx, %dx | 
|  | xorw	%bx, %bx | 
|  |  | 
|  | found_rom_table: | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | /* compute the linear address */ | 
|  | movw	%dx, %ax | 
|  | shll	$4, %eax | 
|  | addl	%ebx, %eax | 
|  |  | 
|  | popl	%ebx | 
|  | popl	%ebp | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * int get_vbe_controller_info (struct vbe_controller *controller_ptr) | 
|  | * | 
|  | * Get VBE controller information. | 
|  | */ | 
|  |  | 
|  | ENTRY(get_vbe_controller_info) | 
|  | pushl	%ebp | 
|  | movl	%esp, %ebp | 
|  |  | 
|  | pushl	%edi | 
|  | pushl	%ebx | 
|  |  | 
|  | /* Convert the linear address to segment:offset */ | 
|  | movl	8(%ebp), %eax | 
|  | movl	%eax, %edi | 
|  | andl	$0x0000000f, %edi | 
|  | shrl	$4, %eax | 
|  | movl	%eax, %ebx | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | movw	%bx, %es | 
|  | movw	$0x4F00, %ax | 
|  | int	$0x10 | 
|  |  | 
|  | movw	%ax, %bx | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | movzwl	%bx, %eax | 
|  |  | 
|  | popl	%ebx | 
|  | popl	%edi | 
|  | popl	%ebp | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * int get_vbe_mode_info (int mode_number, struct vbe_mode *mode_ptr) | 
|  | * | 
|  | * Get VBE mode information. | 
|  | */ | 
|  |  | 
|  | ENTRY(get_vbe_mode_info) | 
|  | pushl	%ebp | 
|  | movl	%esp, %ebp | 
|  |  | 
|  | pushl	%edi | 
|  | pushl	%ebx | 
|  |  | 
|  | /* Convert the linear address to segment:offset */ | 
|  | movl	0xc(%ebp), %eax | 
|  | movl	%eax, %edi | 
|  | andl	$0x0000000f, %edi | 
|  | shrl	$4, %eax | 
|  | movl	%eax, %ebx | 
|  |  | 
|  | /* Save the mode number in %cx */ | 
|  | movl	0x8(%ebp), %ecx | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | movw	%bx, %es | 
|  | movw	$0x4F01, %ax | 
|  | int	$0x10 | 
|  |  | 
|  | movw	%ax, %bx | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | movzwl	%bx, %eax | 
|  |  | 
|  | popl	%ebx | 
|  | popl	%edi | 
|  | popl	%ebp | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * int set_vbe_mode (int mode_number) | 
|  | * | 
|  | * Set VBE mode. Don't support user-specified CRTC information. | 
|  | */ | 
|  |  | 
|  | ENTRY(set_vbe_mode) | 
|  | pushl	%ebp | 
|  | movl	%esp, %ebp | 
|  |  | 
|  | pushl	%ebx | 
|  |  | 
|  | /* Save the mode number in %bx */ | 
|  | movl	0x8(%ebp), %ebx | 
|  | /* Clear bit D11 */ | 
|  | andl	$0xF7FF, %ebx | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | movw	$0x4F02, %ax | 
|  | int	$0x10 | 
|  |  | 
|  | movw	%ax, %bx | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | movzwl	%bx, %eax | 
|  |  | 
|  | popl	%ebx | 
|  | popl	%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) | 
|  | /* first, try a BIOS call */ | 
|  | pushl	%ebp | 
|  | movl	8(%esp), %edx | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  |  | 
|  | .code16 | 
|  | movw	$0x2400, %ax | 
|  | testw	%dx, %dx | 
|  | jz	1f | 
|  | incw	%ax | 
|  | 1:	stc | 
|  | int	$0x15 | 
|  | jnc	2f | 
|  |  | 
|  | /* set non-zero if failed */ | 
|  | movb	$1, %ah | 
|  |  | 
|  | /* save the status */ | 
|  | 2:	movb	%ah, %dl | 
|  |  | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | popl	%ebp | 
|  | testb	%dl, %dl | 
|  | jnz	3f | 
|  | ret | 
|  |  | 
|  | 3:	/* use keyboard controller */ | 
|  | 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 | 
|  |  | 
|  | /* output a dummy command (USB keyboard hack) */ | 
|  | movb	$0xff, %al | 
|  | outb	$K_CMD | 
|  | 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. | 
|  | */ | 
|  |  | 
|  | VARIABLE(linux_text_len) | 
|  | .long	0 | 
|  |  | 
|  | VARIABLE(linux_data_tmp_addr) | 
|  | .long	0 | 
|  |  | 
|  | VARIABLE(linux_data_real_addr) | 
|  | .long	0 | 
|  |  | 
|  | ENTRY(linux_boot) | 
|  | /* don't worry about saving anything, we're committed at this point */ | 
|  | cld	/* forward copying */ | 
|  |  | 
|  | /* copy kernel */ | 
|  | movl	EXT_C(linux_text_len), %ecx | 
|  | addl	$3, %ecx | 
|  | shrl	$2, %ecx | 
|  | movl	$LINUX_BZIMAGE_ADDR, %esi | 
|  | movl	$LINUX_ZIMAGE_ADDR, %edi | 
|  |  | 
|  | rep | 
|  | movsl | 
|  |  | 
|  | ENTRY(big_linux_boot) | 
|  | movl	EXT_C(linux_data_real_addr), %ebx | 
|  |  | 
|  | /* copy the real mode part */ | 
|  | movl	EXT_C(linux_data_tmp_addr), %esi | 
|  | movl	%ebx, %edi | 
|  | movl	$LINUX_SETUP_MOVE_SIZE, %ecx | 
|  | cld | 
|  | rep | 
|  | movsb | 
|  |  | 
|  | /* change %ebx to the segment address */ | 
|  | shrl	$4, %ebx | 
|  | movl	%ebx, %eax | 
|  | addl	$0x20, %eax | 
|  | movl	%eax, linux_setup_seg | 
|  |  | 
|  | /* XXX new stack pointer in safe area for calling functions */ | 
|  | movl	$0x4000, %esp | 
|  | call	EXT_C(stop_floppy) | 
|  |  | 
|  | /* final setup for linux boot */ | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | /* final setup for linux boot */ | 
|  | cli | 
|  | movw	%bx, %ss | 
|  | movw	$LINUX_SETUP_STACK, %sp | 
|  |  | 
|  | movw	%bx, %ds | 
|  | movw	%bx, %es | 
|  | movw	%bx, %fs | 
|  | movw	%bx, %gs | 
|  |  | 
|  | /* jump to start */ | 
|  | /* ljmp */ | 
|  | .byte	0xea | 
|  | .word	0 | 
|  | linux_setup_seg: | 
|  | .word	0 | 
|  | .code32 | 
|  |  | 
|  |  | 
|  | /* | 
|  | * multi_boot(int start, int mb_info) | 
|  | * | 
|  | *  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) | 
|  |  | 
|  | #endif /* ! STAGE1_5 */ | 
|  |  | 
|  | /* | 
|  | * void console_putchar (int c) | 
|  | * | 
|  | * Put the character C on the console. Because GRUB wants to write a | 
|  | * character with an attribute, this implementation is a bit tricky. | 
|  | * If C is a control character (CR, LF, BEL, BS), use INT 10, AH = 0Eh | 
|  | * (TELETYPE OUTPUT). Otherwise, save the original position, put a space, | 
|  | * save the current position, restore the original position, write the | 
|  | * character and the attribute, and restore the current position. | 
|  | * | 
|  | * The reason why this is so complicated is that there is no easy way to | 
|  | * get the height of the screen, and the TELETYPE OUPUT BIOS call doesn't | 
|  | * support setting a background attribute. | 
|  | */ | 
|  | ENTRY(console_putchar) | 
|  | movl	0x4(%esp), %edx | 
|  | pusha | 
|  | #ifdef STAGE1_5 | 
|  | movb	$0x07, %bl | 
|  | #else | 
|  | movl	EXT_C(console_current_color), %ebx | 
|  | #endif | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  | movb	%dl, %al | 
|  | xorb	%bh, %bh | 
|  |  | 
|  | #ifndef STAGE1_5 | 
|  | /* use teletype output if control character */ | 
|  | cmpb	$0x7, %al | 
|  | je	1f | 
|  | cmpb	$0x8, %al | 
|  | je	1f | 
|  | cmpb	$0xa, %al | 
|  | je	1f | 
|  | cmpb	$0xd, %al | 
|  | je	1f | 
|  |  | 
|  | /* save the character and the attribute on the stack */ | 
|  | pushw	%ax | 
|  | pushw	%bx | 
|  |  | 
|  | /* get the current position */ | 
|  | movb	$0x3, %ah | 
|  | int	$0x10 | 
|  |  | 
|  | /* check the column with the width */ | 
|  | cmpb	$79, %dl | 
|  | jl	2f | 
|  |  | 
|  | /* print CR and LF, if next write will exceed the width */ | 
|  | movw	$0x0e0d, %ax | 
|  | int	$0x10 | 
|  | movb	$0x0a, %al | 
|  | int	$0x10 | 
|  |  | 
|  | /* get the current position */ | 
|  | movb	$0x3, %ah | 
|  | int	$0x10 | 
|  |  | 
|  | 2: | 
|  | /* restore the character and the attribute */ | 
|  | popw	%bx | 
|  | popw	%ax | 
|  |  | 
|  | /* write the character with the attribute */ | 
|  | movb	$0x9, %ah | 
|  | movw	$1, %cx | 
|  | int	$0x10 | 
|  |  | 
|  | /* move the cursor forward */ | 
|  | incb	%dl | 
|  | movb	$0x2, %ah | 
|  | int	$0x10 | 
|  |  | 
|  | jmp	3f | 
|  | #endif /* ! STAGE1_5 */ | 
|  |  | 
|  | 1:	movb	$0xe, %ah | 
|  | int	$0x10 | 
|  |  | 
|  | 3:	DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | popa | 
|  | ret | 
|  |  | 
|  |  | 
|  | #ifndef STAGE1_5 | 
|  |  | 
|  | /* this table is used in translate_keycode below */ | 
|  | translation_table: | 
|  | .word	KEY_LEFT, 2 | 
|  | .word	KEY_RIGHT, 6 | 
|  | .word	KEY_UP, 16 | 
|  | .word	KEY_DOWN, 14 | 
|  | .word	KEY_HOME, 1 | 
|  | .word	KEY_END, 5 | 
|  | .word	KEY_DC, 4 | 
|  | .word	KEY_BACKSPACE, 8 | 
|  | .word	KEY_PPAGE, 7 | 
|  | .word	KEY_NPAGE, 3 | 
|  | .word	0 | 
|  |  | 
|  | /* | 
|  | * translate_keycode translates the key code %dx to an ascii code. | 
|  | */ | 
|  | .code16 | 
|  |  | 
|  | translate_keycode: | 
|  | pushw	%bx | 
|  | pushw	%si | 
|  |  | 
|  | movw	$ABS(translation_table), %si | 
|  |  | 
|  | 1:	lodsw | 
|  | /* check if this is the end */ | 
|  | testw	%ax, %ax | 
|  | jz	2f | 
|  | /* load the ascii code into %ax */ | 
|  | movw	%ax, %bx | 
|  | lodsw | 
|  | /* check if this matches the key code */ | 
|  | cmpw	%bx, %dx | 
|  | jne	1b | 
|  | /* translate %dx, if successful */ | 
|  | movw	%ax, %dx | 
|  |  | 
|  | 2:	popw	%si | 
|  | popw	%bx | 
|  | ret | 
|  |  | 
|  | .code32 | 
|  |  | 
|  |  | 
|  | /* | 
|  | * remap_ascii_char remaps the ascii code %dl 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, %dl | 
|  | jne	1b | 
|  | /* if so, perform the mapping */ | 
|  | movb	%ah, %dl | 
|  | 2: | 
|  | /* restore %si */ | 
|  | popw	%si | 
|  |  | 
|  | ret | 
|  |  | 
|  | .code32 | 
|  |  | 
|  | .align	4 | 
|  | ENTRY(ascii_key_map) | 
|  | .space	(KEY_MAP_SIZE + 1) * 2 | 
|  |  | 
|  |  | 
|  | /* | 
|  | * int console_getkey (void) | 
|  | * BIOS call "INT 16H Function 00H" to read character from keyboard | 
|  | *	Call with	%ah = 0x0 | 
|  | *	Return:		%ah = keyboard scan code | 
|  | *			%al = ASCII character | 
|  | */ | 
|  |  | 
|  | ENTRY(console_getkey) | 
|  | push	%ebp | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | int	$0x16 | 
|  |  | 
|  | movw	%ax, %dx		/* real_to_prot uses %eax */ | 
|  | call	translate_keycode | 
|  | call	remap_ascii_char | 
|  |  | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | movw	%dx, %ax | 
|  |  | 
|  | pop	%ebp | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * int console_checkkey (void) | 
|  | *	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(console_checkkey) | 
|  | push	%ebp | 
|  | xorl	%edx, %edx | 
|  |  | 
|  | call	EXT_C(prot_to_real)	/* enter real mode */ | 
|  | .code16 | 
|  |  | 
|  | movb	$0x1, %ah | 
|  | int	$0x16 | 
|  |  | 
|  | DATA32	jz	notpending | 
|  |  | 
|  | movw	%ax, %dx | 
|  | call	translate_keycode | 
|  | call	remap_ascii_char | 
|  | DATA32	jmp	pending | 
|  |  | 
|  | notpending: | 
|  | movl	$0xFFFFFFFF, %edx | 
|  |  | 
|  | pending: | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | mov	%edx, %eax | 
|  |  | 
|  | pop	%ebp | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * int console_getxy (void) | 
|  | * 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(console_getxy) | 
|  | push	%ebp | 
|  | push	%ebx                    /* save EBX */ | 
|  |  | 
|  | 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	%ebx | 
|  | pop	%ebp | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * void console_gotoxy(int x, int 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(console_gotoxy) | 
|  | push	%ebp | 
|  | push	%ebx                    /* save EBX */ | 
|  |  | 
|  | movb	0xc(%esp), %dl           /* %dl = x */ | 
|  | movb	0x10(%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	%ebx | 
|  | pop	%ebp | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * void console_cls (void) | 
|  | * BIOS call "INT 10H Function 09h" to write character and attribute | 
|  | *	Call with	%ah = 0x09 | 
|  | *                      %al = (character) | 
|  | *                      %bh = (page number) | 
|  | *                      %bl = (attribute) | 
|  | *                      %cx = (number of times) | 
|  | */ | 
|  |  | 
|  |  | 
|  | ENTRY(console_cls) | 
|  | push	%ebp | 
|  | push	%ebx                    /* save EBX */ | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | /* move the cursor to the beginning */ | 
|  | movb	$0x02, %ah | 
|  | xorb	%bh, %bh | 
|  | xorw	%dx, %dx | 
|  | int	$0x10 | 
|  |  | 
|  | /* write spaces to the entire screen */ | 
|  | movw	$0x0920, %ax | 
|  | movw	$0x07, %bx | 
|  | movw	$(80 * 25), %cx | 
|  | int	$0x10 | 
|  |  | 
|  | /* move back the cursor */ | 
|  | movb	$0x02, %ah | 
|  | int	$0x10 | 
|  |  | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | pop	%ebx | 
|  | pop	%ebp | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * int console_setcursor (int on) | 
|  | * BIOS call "INT 10H Function 01h" to set cursor type | 
|  | *      Call with       %ah = 0x01 | 
|  | *                      %ch = cursor starting scanline | 
|  | *                      %cl = cursor ending scanline | 
|  | */ | 
|  |  | 
|  | console_cursor_state: | 
|  | .byte	1 | 
|  | console_cursor_shape: | 
|  | .word	0 | 
|  |  | 
|  | ENTRY(console_setcursor) | 
|  | push	%ebp | 
|  | push	%ebx | 
|  |  | 
|  | /* check if the standard cursor shape has already been saved */ | 
|  | movw	console_cursor_shape, %ax | 
|  | testw	%ax, %ax | 
|  | jne	1f | 
|  |  | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | movb	$0x03, %ah | 
|  | xorb	%bh, %bh | 
|  | int	$0x10 | 
|  |  | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | movw	%cx, console_cursor_shape | 
|  | 1: | 
|  | /* set %cx to the designated cursor shape */ | 
|  | movw	$0x2000, %cx | 
|  | movl	0xc(%esp), %ebx | 
|  | testl	%ebx, %ebx | 
|  | jz	2f | 
|  | movw	console_cursor_shape, %cx | 
|  | 2: | 
|  | call	EXT_C(prot_to_real) | 
|  | .code16 | 
|  |  | 
|  | movb    $0x1, %ah | 
|  | int     $0x10 | 
|  |  | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | movzbl	console_cursor_state, %eax | 
|  | movb	%bl, console_cursor_state | 
|  |  | 
|  | pop	%ebx | 
|  | 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 | 
|  |  | 
|  | 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	%ebp | 
|  | ret | 
|  |  | 
|  |  | 
|  | /* | 
|  | * currticks() | 
|  | *	return the real time in ticks, of which there are about | 
|  | *	18-20 per second | 
|  | */ | 
|  | ENTRY(currticks) | 
|  | pushl	%ebp | 
|  |  | 
|  | call	EXT_C(prot_to_real)	/* enter real mode */ | 
|  | .code16 | 
|  |  | 
|  | /* %ax is already zero */ | 
|  | int	$0x1a | 
|  |  | 
|  | DATA32	call	EXT_C(real_to_prot) | 
|  | .code32 | 
|  |  | 
|  | movl	%ecx, %eax | 
|  | shll	$16, %eax | 
|  | movw	%dx, %ax | 
|  |  | 
|  | popl	%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) | 
|  | #ifdef SUPPORT_DISKLESS | 
|  | .long	NETWORK_DRIVE | 
|  | #else | 
|  | .long	0 | 
|  | #endif | 
|  |  | 
|  | VARIABLE(install_second_sector) | 
|  | .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 | 
|  |  | 
|  | VARIABLE(apm_bios_info) | 
|  | .word	0	/* version */ | 
|  | .word	0	/* cseg */ | 
|  | .long	0	/* offset */ | 
|  | .word	0	/* cseg_16 */ | 
|  | .word	0	/* dseg_16 */ | 
|  | .word	0	/* cseg_len */ | 
|  | .word	0	/* cseg_16_len */ | 
|  | .word	0	/* dseg_16_len */ | 
|  |  | 
|  | /* | 
|  | * 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 */ |