Home | History | Annotate | Download | only in stage2
      1 /*
      2  *  GRUB  --  GRand Unified Bootloader
      3  *  Copyright (C) 1999,2000,2001,2002,2004 Free Software Foundation, Inc.
      4  *
      5  *  This program is free software; you can redistribute it and/or modify
      6  *  it under the terms of the GNU General Public License as published by
      7  *  the Free Software Foundation; either version 2 of the License, or
      8  *  (at your option) any later version.
      9  *
     10  *  This program is distributed in the hope that it will be useful,
     11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  *  GNU General Public License for more details.
     14  *
     15  *  You should have received a copy of the GNU General Public License
     16  *  along with this program; if not, write to the Free Software
     17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     18  */
     19 
     20 
     21 /*
     22  * Note: These functions defined in this file may be called from C.
     23  *       Be careful of that you must not modify some registers. Quote
     24  *       from gcc-2.95.2/gcc/config/i386/i386.h:
     25 
     26    1 for registers not available across function calls.
     27    These must include the FIXED_REGISTERS and also any
     28    registers that can be used without being saved.
     29    The latter must include the registers where values are returned
     30    and the register where structure-value addresses are passed.
     31    Aside from that, you can include as many other registers as you like.
     32 
     33   ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg
     34 {  1, 1, 1, 0, 0, 0, 0, 1, 1,  1,  1,  1,  1,  1,  1,  1,  1 }
     35  */
     36 
     37 #define ASM_FILE
     38 
     39 #include "shared.h"
     40 
     41 #ifdef STAGE1_5
     42 # define	ABS(x)	((x) - EXT_C(main) + 0x2200)
     43 #else
     44 # define	ABS(x)	((x) - EXT_C(main) + 0x8200)
     45 #endif
     46 
     47 	.file	"asm.S"
     48 
     49 	.text
     50 
     51 	/* Tell GAS to generate 16-bit instructions so that this code works
     52 	   in real mode. */
     53 	.code16
     54 
     55 #ifndef STAGE1_5
     56 	/*
     57 	 * In stage2, do not link start.S with the rest of the source
     58 	 * files directly, so define the start symbols here just to
     59 	 * force ld quiet. These are not referred anyway.
     60 	 */
     61 	.globl	start, _start
     62 start:
     63 _start:
     64 #endif /* ! STAGE1_5 */
     65 
     66 ENTRY(main)
     67 	/*
     68 	 *  Guarantee that "main" is loaded at 0x0:0x8200 in stage2 and
     69 	 *  at 0x0:0x2200 in stage1.5.
     70 	 */
     71 	ljmp $0, $ABS(codestart)
     72 
     73 	/*
     74 	 *  Compatibility version number
     75 	 *
     76 	 *  These MUST be at byte offset 6 and 7 of the executable
     77 	 *  DO NOT MOVE !!!
     78 	 */
     79 	. = EXT_C(main) + 0x6
     80 	.byte	COMPAT_VERSION_MAJOR, COMPAT_VERSION_MINOR
     81 
     82 	/*
     83 	 *  This is a special data area 8 bytes from the beginning.
     84 	 */
     85 
     86 	. = EXT_C(main) + 0x8
     87 
     88 VARIABLE(install_partition)
     89 	.long	0xFFFFFF
     90 /* This variable is here only because of a historical reason.  */
     91 VARIABLE(saved_entryno)
     92 	.long	0
     93 VARIABLE(stage2_id)
     94 	.byte	STAGE2_ID
     95 VARIABLE(force_lba)
     96 	.byte	0
     97 VARIABLE(version_string)
     98 	.string VERSION
     99 VARIABLE(config_file)
    100 #ifndef STAGE1_5
    101 	.string "/boot/grub/menu.lst"
    102 #else   /* STAGE1_5 */
    103 	.long	0xffffffff
    104 	.string "/boot/grub/stage2"
    105 #endif  /* STAGE1_5 */
    106 
    107 	/*
    108 	 *  Leave some breathing room for the config file name.
    109 	 */
    110 
    111 	. = EXT_C(main) + 0x70
    112 
    113 /* the real mode code continues... */
    114 codestart:
    115 	cli		/* we're not safe here! */
    116 
    117 	/* set up %ds, %ss, and %es */
    118 	xorw	%ax, %ax
    119 	movw	%ax, %ds
    120 	movw	%ax, %ss
    121 	movw	%ax, %es
    122 
    123 #ifndef SUPPORT_DISKLESS
    124 	/*
    125 	 * Save the sector number of the second sector (i.e. this sector)
    126 	 * in INSTALL_SECOND_SECTOR. See also "stage2/start.S".
    127 	 */
    128 	ADDR32	movl	%ebp, EXT_C(install_second_sector)
    129 #endif
    130 
    131 	/* set up the real mode/BIOS stack */
    132 	movl	$STACKOFF, %ebp
    133 	movl	%ebp, %esp
    134 
    135 	sti		/* we're safe again */
    136 
    137 #ifndef SUPPORT_DISKLESS
    138 	/* save boot drive reference */
    139 	ADDR32	movb	%dl, EXT_C(boot_drive)
    140 
    141 	/* reset disk system (%ah = 0) */
    142 	int	$0x13
    143 #endif
    144 
    145 	/* transition to protected mode */
    146 	DATA32	call EXT_C(real_to_prot)
    147 
    148 	/* The ".code32" directive takes GAS out of 16-bit mode. */
    149 	.code32
    150 
    151 	/* clean out the bss */
    152 
    153 	/* set %edi to the bss starting address */
    154 #if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL)
    155 	movl	$__bss_start, %edi
    156 #elif defined(HAVE_USCORE_EDATA_SYMBOL)
    157 	movl	$_edata, %edi
    158 #elif defined(HAVE_EDATA_SYMBOL)
    159 	movl	$edata, %edi
    160 #endif
    161 
    162 	/* set %ecx to the bss end */
    163 #if defined(HAVE_END_SYMBOL)
    164 	movl	$end, %ecx
    165 #elif defined(HAVE_USCORE_END_SYMBOL)
    166 	movl	$_end, %ecx
    167 #endif
    168 
    169 	/* compute the bss length */
    170 	subl	%edi, %ecx
    171 
    172 	/* zero %al */
    173 	xorb	%al, %al
    174 
    175 	/* set the direction */
    176 	cld
    177 
    178 	/* clean out */
    179 	rep
    180 	stosb
    181 
    182 	/*
    183 	 *  Call the start of main body of C code, which does some
    184 	 *  of it's own initialization before transferring to "cmain".
    185 	 */
    186 	call EXT_C(init_bios_info)
    187 
    188 
    189 /*
    190  *  This call is special...  it never returns...  in fact it should simply
    191  *  hang at this point!
    192  */
    193 
    194 ENTRY(stop)
    195 	call	EXT_C(prot_to_real)
    196 
    197 	/*
    198 	 * This next part is sort of evil.  It takes advantage of the
    199 	 * byte ordering on the x86 to work in either 16-bit or 32-bit
    200 	 * mode, so think about it before changing it.
    201 	 */
    202 
    203 ENTRY(hard_stop)
    204 	hlt
    205 	jmp EXT_C(hard_stop)
    206 
    207 #ifndef STAGE1_5
    208 /*
    209  * stop_floppy()
    210  *
    211  * Stops the floppy drive from spinning, so that other software is
    212  * jumped to with a known state.
    213  */
    214 ENTRY(stop_floppy)
    215 	pusha
    216 	call	EXT_C(prot_to_real)
    217 	.code16
    218 	xorb	%dl, %dl
    219 	int	$0x13
    220 	DATA32  call EXT_C(real_to_prot)
    221 	.code32
    222 	popa
    223 	ret
    224 
    225 /*
    226  * grub_reboot()
    227  *
    228  * Reboot the system. At the moment, rely on BIOS.
    229  */
    230 ENTRY(grub_reboot)
    231 	call	EXT_C(prot_to_real)
    232 	.code16
    233 	/* cold boot */
    234 	movw	$0x0472, %di
    235 	movw	%ax, (%di)
    236 	ljmp	$0xFFFF, $0x0000
    237 	.code32
    238 
    239 /*
    240  * grub_halt(int no_apm)
    241  *
    242  * Halt the system, using APM if possible. If NO_APM is true, don't use
    243  * APM even if it is available.
    244  */
    245 ENTRY(grub_halt)
    246 	/* get the argument */
    247 	movl	4(%esp), %eax
    248 
    249 	/* see if zero */
    250 	testl	%eax, %eax
    251 	jnz	EXT_C(stop)
    252 
    253 	call	EXT_C(prot_to_real)
    254 	.code16
    255 
    256 	/* detect APM */
    257 	movw	$0x5300, %ax
    258 	xorw	%bx, %bx
    259 	int	$0x15
    260 	jc	EXT_C(hard_stop)
    261 	/* don't check %bx for buggy BIOSes... */
    262 
    263 	/* disconnect APM first */
    264 	movw	$0x5304, %ax
    265 	xorw	%bx, %bx
    266 	int	$0x15
    267 
    268 	/* connect APM */
    269 	movw	$0x5301, %ax
    270 	xorw	%bx, %bx
    271 	int	$0x15
    272 	jc	EXT_C(hard_stop)
    273 
    274 	/* set APM protocol level - 1.1 or bust. (this covers APM 1.2 also) */
    275 	movw	$0x530E, %ax
    276 	xorw	%bx, %bx
    277 	movw	$0x0101, %cx
    278 	int	$0x15
    279 	jc	EXT_C(hard_stop)
    280 
    281 	/* set the power state to off */
    282 	movw	$0x5307, %ax
    283 	movw	$1, %bx
    284 	movw	$3, %cx
    285 	int	$0x15
    286 
    287 	/* shouldn't reach here */
    288 	jmp	EXT_C(hard_stop)
    289 	.code32
    290 
    291 /*
    292  * track_int13(int drive)
    293  *
    294  * Track the int13 handler to probe I/O address space.
    295  */
    296 ENTRY(track_int13)
    297 	pushl	%ebp
    298 	movl	%esp, %ebp
    299 
    300 	pushl	%ebx
    301 	pushl	%edi
    302 
    303 	/* copy the original int13 handler segment:offset */
    304 	movl	$0x4c, %edi
    305 	movl	(%edi), %eax
    306 	movl	%eax, track_int13_addr
    307 
    308 	/* replace the int1 handler */
    309 	movl	$0x4, %edi
    310 	pushl	(%edi)
    311 	movl	$ABS(int1_handler), %eax
    312 	movl	%eax, (%edi)
    313 
    314 	/* read the MBR to call int13 successfully */
    315 	movb	8(%ebp), %dl
    316 
    317 	call	EXT_C(prot_to_real)
    318 	.code16
    319 
    320 	movw	$SCRATCHSEG, %ax
    321 	movw	%ax, %es
    322 	xorw	%bx, %bx
    323 	movw	$1, %cx
    324 	xorb	%dh, %dh
    325 
    326 	/* save FLAGS on the stack to emulate int13 */
    327 	pushfw
    328 
    329 	/* set the TF flag */
    330 	/* FIXME: this can be simplified not to use AX */
    331 	pushfw
    332 	popw	%ax
    333 	orw	$0x100, %ax
    334 	pushw	%ax
    335 	popfw
    336 
    337 	movw	$0x0201, %ax
    338 
    339 	.byte	0x9a		/* lcall */
    340 track_int13_addr:
    341 	.word	0		/* offset */
    342 	.word	0		/* segment */
    343 
    344 	/* TF is cleared here automatically */
    345 
    346 	DATA32	call	EXT_C(real_to_prot)
    347 	.code32
    348 
    349 	/* restore the int1 handler */
    350 	movl	$0x4, %edi
    351 	popl	(%edi)
    352 
    353 	popl	%edi
    354 	popl	%ebx
    355 	popl	%ebp
    356 
    357 	ret
    358 
    359 
    360 /*
    361  * Check if the next instruction is I/O, and if this is true, add the
    362  * port into the io map.
    363  *
    364  * Note: Probably this will make the execution of int13 very slow.
    365  *
    366  * Note2: In this implementation, all we can know is I/O-mapped I/O. It
    367  * is impossible to detect memory-mapped I/O.
    368  */
    369 int1_handler:
    370 	.code16
    371 
    372 	pushw	%bp
    373 	movw	%sp, %bp
    374 	pushw	%ds
    375 	pushw	%ax
    376 	pushw	%si
    377 	pushw	%dx
    378 
    379 	/* IP */
    380 	movw	2(%bp), %si
    381 	/* CS */
    382 	movw	4(%bp), %ax
    383 	movw	%ax, %ds
    384 
    385 	/* examine the next instruction */
    386 1:	lodsb	(%si), %al
    387 	/* skip this code if it is a prefix */
    388 	cmpb	$0x2E, %al
    389 	je	1b
    390 	cmpb	$0x36, %al
    391 	je	1b
    392 	cmpb	$0x3E, %al
    393 	je	1b
    394 	cmpb	$0x26, %al
    395 	je	1b
    396 	cmpb	$0x64, %al
    397 	jl	2f
    398 	cmpb	$0x67, %al
    399 	jle	1b
    400 2:	cmpb	$0xF0, %al
    401 	jl	3f
    402 	cmpb	$0xF3, %al
    403 	jle	1b
    404 
    405 3:	/* check if this code is out* or in* */
    406 
    407 	/* ins? or outs? */
    408 	cmpb	$0x6C, %al
    409 	jl	4f
    410 	cmpb	$0x6F, %al
    411 	jle	5f
    412 
    413 4:	/* in? or out? (register operand version) */
    414 	cmpb	$0xEC, %al
    415 	jl	6f
    416 	cmpb	$0xEF, %al
    417 	jle	5f
    418 
    419 6:	/* in? or out? (immediate operand version) */
    420 	cmpb	$0xE4, %al
    421 	jl	8f
    422 	cmpb	$0xE7, %al
    423 	jg	8f
    424 
    425 7:	/* immediate has a port */
    426 	lodsb	(%si), %al
    427 	movzbw	%al, %dx
    428 
    429 5:	/* %dx has a port */
    430 
    431 	/* set %ds to zero */
    432 	xorw	%ax, %ax
    433 	movw	%ax, %ds
    434 
    435 	/* set %si to the io map */
    436 	movw	$ABS(EXT_C(io_map)), %si
    437 
    438 
    439 9:	/* check if the io map already has the port */
    440 	lodsw	(%si), %ax
    441 	/* check if this is the end */
    442 	testw	%ax, %ax
    443 	jz	1f
    444 	/* check if this matches the port */
    445 	cmpw	%ax, %dx
    446 	jne	9b
    447 	/* if so, leave from this handler */
    448 	jmp	8f
    449 
    450 1:	/* check for the buffer overrun */
    451 	cmpw	$(ABS(EXT_C(io_map)) + (IO_MAP_SIZE + 1) * 2), %si
    452 	je	8f
    453 	/* add the port into the io map */
    454 	movw	%dx, -2(%si)
    455 
    456 8:	/* restore registers */
    457 	popw	%dx
    458 	popw	%si
    459 	popw	%ax
    460 	popw	%ds
    461 	popw	%bp
    462 
    463 	iret
    464 
    465 	.code32
    466 
    467 ENTRY(io_map)
    468 	.space	(IO_MAP_SIZE + 1) * 2
    469 
    470 
    471 /*
    472  * set_int15_handler(void)
    473  *
    474  * Set up int15_handler.
    475  */
    476 ENTRY(set_int15_handler)
    477 	pushl	%edi
    478 
    479 	/* save the original int15 handler */
    480 	movl	$0x54, %edi
    481 	movw	(%edi), %ax
    482 	movw	%ax, ABS(int15_offset)
    483 	movw	2(%edi), %ax
    484 	movw	%ax, ABS(int15_segment)
    485 
    486 	/* save the new int15 handler */
    487 	movw	$ABS(int15_handler), %ax
    488 	movw	%ax, (%edi)
    489 	xorw	%ax, %ax
    490 	movw	%ax, 2(%edi)
    491 
    492 	popl	%edi
    493 	ret
    494 
    495 
    496 /*
    497  * unset_int15_handler(void)
    498  *
    499  * Restore the original int15 handler
    500  */
    501 ENTRY(unset_int15_handler)
    502 	pushl	%edi
    503 
    504 	/* check if int15_handler is set */
    505 	movl	$0x54, %edi
    506 	movw	$ABS(int15_handler), %ax
    507 	cmpw	%ax, (%edi)
    508 	jne	1f
    509 	xorw	%ax, %ax
    510 	cmpw	%ax, 2(%edi)
    511 	jne	1f
    512 
    513 	/* restore the original */
    514 	movw	ABS(int15_offset), %ax
    515 	movw	%ax, (%edi)
    516 	movw	ABS(int15_segment), %ax
    517 	movw	%ax, 2(%edi)
    518 
    519 1:
    520 	popl	%edi
    521 	ret
    522 
    523 
    524 /*
    525  * Translate a key code to another.
    526  *
    527  * Note: This implementation cannot handle more than one length
    528  * scancodes (such as Right Ctrl).
    529  */
    530 	.code16
    531 int15_handler:
    532 	/* if non-carrier, ignore it */
    533 	jnc	1f
    534 	/* check if AH=4F */
    535 	cmpb	$0x4F, %ah
    536 	jne	1f
    537 
    538 	/* E0 and E1 are special */
    539 	cmpb	$0xE1, %al
    540 	je	4f
    541 	cmpb	$0xE0, %al
    542 	/* this flag is actually the machine code (je or jmp) */
    543 int15_skip_flag:
    544 	je	4f
    545 
    546 	pushw	%bp
    547 	movw	%sp, %bp
    548 
    549 	pushw	%bx
    550 	pushw	%dx
    551 	pushw	%ds
    552 	pushw	%si
    553 
    554 	/* save bits 0-6 of %al in %dl */
    555 	movw	%ax, %dx
    556 	andb	$0x7f, %dl
    557 	/* save the highest bit in %bl */
    558 	movb	%al, %bl
    559 	xorb	%dl, %bl
    560 	/* set %ds to 0 */
    561 	xorw	%ax, %ax
    562 	movw	%ax, %ds
    563 	/* set %si to the key map */
    564 	movw	$ABS(EXT_C(bios_key_map)), %si
    565 
    566 	/* find the key code from the key map */
    567 2:
    568 	lodsw
    569 	/* check if this is the end */
    570 	testw	%ax, %ax
    571 	jz	3f
    572 	/* check if this matches the key code */
    573 	cmpb	%al, %dl
    574 	jne	2b
    575 	/* if so, perform the mapping */
    576 	movb	%ah, %dl
    577 3:
    578 	/* restore %ax */
    579 	movw	%dx, %ax
    580 	orb	%bl, %al
    581 	/* make sure that CF is set */
    582 	orw	$1, 6(%bp)
    583 	/* restore other registers */
    584 	popw	%si
    585 	popw	%ds
    586 	popw	%dx
    587 	popw	%bx
    588 	popw	%bp
    589 	iret
    590 
    591 4:
    592 	/* tricky: jmp (0x74) <-> je (0xeb) */
    593 	xorb	$(0x74 ^ 0xeb), ABS(int15_skip_flag)
    594 1:
    595 	/* just cascade to the original */
    596 	/* ljmp */
    597 	.byte	0xea
    598 int15_offset:	.word	0
    599 int15_segment:	.word	0
    600 
    601 	.code32
    602 
    603 	.align	4
    604 ENTRY(bios_key_map)
    605 	.space	(KEY_MAP_SIZE + 1) * 2
    606 
    607 
    608 /*
    609  * set_int13_handler(map)
    610  *
    611  * Copy MAP to the drive map and set up int13_handler.
    612  */
    613 ENTRY(set_int13_handler)
    614 	pushl	%ebp
    615 	movl	%esp, %ebp
    616 
    617 	pushl	%edi
    618 	pushl	%esi
    619 
    620 	/* copy MAP to the drive map */
    621 	movl	$(DRIVE_MAP_SIZE * 2), %ecx
    622 	movl	$ABS(drive_map), %edi
    623 	movl	8(%ebp), %esi
    624 	cld
    625 	rep
    626 	movsb
    627 
    628 	/* save the original int13 handler */
    629 	movl	$0x4c, %edi
    630 	movw	(%edi), %ax
    631 	movw	%ax, ABS(int13_offset)
    632 	movw	2(%edi), %ax
    633 	movw	%ax, ABS(int13_segment)
    634 
    635 	/* decrease the lower memory size and set it to the BIOS memory */
    636 	movl	$0x413, %edi
    637 	decw	(%edi)
    638 	xorl	%eax, %eax
    639 	movw	(%edi), %ax
    640 
    641 	/* compute the segment */
    642 	shll	$6, %eax
    643 
    644 	/* save the new int13 handler */
    645 	movl	$0x4c, %edi
    646 	movw	%ax, 2(%edi)
    647 	xorw	%cx, %cx
    648 	movw	%cx, (%edi)
    649 
    650 	/* copy int13_handler to the reserved area */
    651 	shll	$4, %eax
    652 	movl	%eax, %edi
    653 	movl	$ABS(int13_handler), %esi
    654 	movl	$(int13_handler_end - int13_handler), %ecx
    655 	rep
    656 	movsb
    657 
    658 	popl	%esi
    659 	popl	%edi
    660 	popl	%ebp
    661 	ret
    662 
    663 
    664 /*
    665  * Map a drive to another drive.
    666  */
    667 
    668 	.code16
    669 
    670 int13_handler:
    671 	pushw	%ax
    672 	pushw	%bp
    673 	movw	%sp, %bp
    674 
    675 	pushw	%si
    676 
    677 	/* set %si to the drive map */
    678 	movw	$(drive_map - int13_handler), %si
    679 	/* find the drive number from the drive map */
    680 	cld
    681 1:
    682 	lodsw	%cs:(%si), %ax
    683 	/* check if this is the end */
    684 	testw	%ax, %ax
    685 	jz	2f
    686 	/* check if this matches the drive number */
    687 	cmpb	%al, %dl
    688 	jne	1b
    689 	/* if so, perform the mapping */
    690 	movb	%ah, %dl
    691 2:
    692 	/* restore %si */
    693 	popw	%si
    694 	/* save %ax in the stack */
    695 	pushw	%ax
    696 	/* simulate the interrupt call */
    697 	pushw	8(%bp)
    698 	/* set %ax and %bp to the original values */
    699 	movw	2(%bp), %ax
    700 	movw	(%bp), %bp
    701 	/* lcall */
    702 	.byte	0x9a
    703 int13_offset:	.word	0
    704 int13_segment:	.word	0
    705 	/* save flags */
    706 	pushf
    707 	/* restore %bp */
    708 	movw	%sp, %bp
    709 	/* save %ax */
    710 	pushw	%ax
    711 	/* set the flags in the stack to the value returned by int13 */
    712 	movw	(%bp), %ax
    713 	movw	%ax, 0xc(%bp)
    714 	/* check if should map the drive number */
    715 	movw	6(%bp), %ax
    716 	cmpw	$0x8, %ax
    717 	jne	3f
    718 	cmpw	$0x15, %ax
    719 	jne	3f
    720 	/* check if the mapping was performed */
    721 	movw	2(%bp), %ax
    722 	testw	%ax, %ax
    723 	jz	3f
    724 	/* perform the mapping */
    725 	movb	%al, %dl
    726 3:
    727 	popw	%ax
    728 	movw	4(%bp), %bp
    729 	addw	$8, %sp
    730 	iret
    731 
    732 	.align	4
    733 drive_map:	.space	(DRIVE_MAP_SIZE + 1) * 2
    734 int13_handler_end:
    735 
    736 	.code32
    737 
    738 
    739 /*
    740  * chain_stage1(segment, offset, part_table_addr)
    741  *
    742  *  This starts another stage1 loader, at segment:offset.
    743  */
    744 
    745 ENTRY(chain_stage1)
    746 	/* no need to save anything, just use %esp */
    747 
    748 	/* store %ESI, presuming %ES is 0 */
    749 	movl	0xc(%esp), %esi
    750 
    751 	/* store new offset */
    752 	movl	0x8(%esp), %eax
    753 	movl	%eax, offset
    754 
    755 	/* store new segment */
    756 	movw	0x4(%esp), %ax
    757 	movw	%ax, segment
    758 
    759 	/* set up to pass boot drive */
    760 	movb	EXT_C(boot_drive), %dl
    761 
    762 	call	EXT_C(prot_to_real)
    763 	.code16
    764 
    765 #ifdef ABSOLUTE_WITHOUT_ASTERISK
    766 	DATA32	ADDR32	ljmp	(offset)
    767 #else
    768 	DATA32	ADDR32	ljmp	*(offset)
    769 #endif
    770 	.code32
    771 #endif /* STAGE1_5 */
    772 
    773 
    774 #ifdef STAGE1_5
    775 /*
    776  * chain_stage2(segment, offset, second_sector)
    777  *
    778  *  This starts another stage2 loader, at segment:offset.  It presumes
    779  *  that the other one starts with this same "asm.S" file, and passes
    780  *  parameters by writing the embedded install variables.
    781  */
    782 
    783 ENTRY(chain_stage2)
    784 	/* no need to save anything, just use %esp */
    785 
    786 	/* store new offset */
    787 	movl	0x8(%esp), %eax
    788 	movl	%eax, offset
    789 	movl	%eax, %ebx
    790 
    791 	/* store new segment */
    792 	movw	0x4(%esp), %ax
    793 	movw	%ax, segment
    794 	shll	$4, %eax
    795 
    796 	/* generate linear address */
    797 	addl	%eax, %ebx
    798 
    799 	/* set up to pass the partition where stage2 is located in */
    800 	movl	EXT_C(current_partition), %eax
    801 	movl	%eax, (EXT_C(install_partition)-EXT_C(main))(%ebx)
    802 
    803 	/* set up to pass the drive where stage2 is located in */
    804 	movb	EXT_C(current_drive), %dl
    805 
    806 	/* set up to pass the second sector of stage2 */
    807 	movl	0xc(%esp), %ecx
    808 
    809 	call	EXT_C(prot_to_real)
    810 	.code16
    811 
    812 	movl	%ecx, %ebp
    813 
    814 #ifdef ABSOLUTE_WITHOUT_ASTERISK
    815 	DATA32	ADDR32	ljmp	(offset)
    816 #else
    817 	DATA32	ADDR32	ljmp	*(offset)
    818 #endif
    819 
    820 	.code32
    821 #endif /* STAGE1_5 */
    822 
    823 /*
    824  *  These next two routines, "real_to_prot" and "prot_to_real" are structured
    825  *  in a very specific way.  Be very careful when changing them.
    826  *
    827  *  NOTE:  Use of either one messes up %eax and %ebp.
    828  */
    829 
    830 ENTRY(real_to_prot)
    831 	.code16
    832 	cli
    833 
    834 	/* load the GDT register */
    835 	DATA32	ADDR32	lgdt	gdtdesc
    836 
    837 	/* turn on protected mode */
    838 	movl	%cr0, %eax
    839 	orl	$CR0_PE_ON, %eax
    840 	movl	%eax, %cr0
    841 
    842 	/* jump to relocation, flush prefetch queue, and reload %cs */
    843 	DATA32	ljmp	$PROT_MODE_CSEG, $protcseg
    844 
    845 	/*
    846 	 *  The ".code32" directive only works in GAS, the GNU assembler!
    847 	 *  This gets out of "16-bit" mode.
    848 	 */
    849 	.code32
    850 
    851 protcseg:
    852 	/* reload other segment registers */
    853 	movw	$PROT_MODE_DSEG, %ax
    854 	movw	%ax, %ds
    855 	movw	%ax, %es
    856 	movw	%ax, %fs
    857 	movw	%ax, %gs
    858 	movw	%ax, %ss
    859 
    860 	/* put the return address in a known safe location */
    861 	movl	(%esp), %eax
    862 	movl	%eax, STACKOFF
    863 
    864 	/* get protected mode stack */
    865 	movl	protstack, %eax
    866 	movl	%eax, %esp
    867 	movl	%eax, %ebp
    868 
    869 	/* get return address onto the right stack */
    870 	movl	STACKOFF, %eax
    871 	movl	%eax, (%esp)
    872 
    873 	/* zero %eax */
    874 	xorl	%eax, %eax
    875 
    876 	/* return on the old (or initialized) stack! */
    877 	ret
    878 
    879 
    880 ENTRY(prot_to_real)
    881 	/* just in case, set GDT */
    882 	lgdt	gdtdesc
    883 
    884 	/* save the protected mode stack */
    885 	movl	%esp, %eax
    886 	movl	%eax, protstack
    887 
    888 	/* get the return address */
    889 	movl	(%esp), %eax
    890 	movl	%eax, STACKOFF
    891 
    892 	/* set up new stack */
    893 	movl	$STACKOFF, %eax
    894 	movl	%eax, %esp
    895 	movl	%eax, %ebp
    896 
    897 	/* set up segment limits */
    898 	movw	$PSEUDO_RM_DSEG, %ax
    899 	movw	%ax, %ds
    900 	movw	%ax, %es
    901 	movw	%ax, %fs
    902 	movw	%ax, %gs
    903 	movw	%ax, %ss
    904 
    905 	/* this might be an extra step */
    906 	ljmp	$PSEUDO_RM_CSEG, $tmpcseg	/* jump to a 16 bit segment */
    907 
    908 tmpcseg:
    909 	.code16
    910 
    911 	/* clear the PE bit of CR0 */
    912 	movl	%cr0, %eax
    913 	andl 	$CR0_PE_OFF, %eax
    914 	movl	%eax, %cr0
    915 
    916 	/* flush prefetch queue, reload %cs */
    917 	DATA32	ljmp	$0, $realcseg
    918 
    919 realcseg:
    920 	/* we are in real mode now
    921 	 * set up the real mode segment registers : DS, SS, ES
    922 	 */
    923 	/* zero %eax */
    924 	xorl	%eax, %eax
    925 
    926 	movw	%ax, %ds
    927 	movw	%ax, %es
    928 	movw	%ax, %fs
    929 	movw	%ax, %gs
    930 	movw	%ax, %ss
    931 
    932 	/* restore interrupts */
    933 	sti
    934 
    935 	/* return on new stack! */
    936 	DATA32	ret
    937 
    938 	.code32
    939 
    940 
    941 /*
    942  *   int biosdisk_int13_extensions (int ax, int drive, void *dap)
    943  *
    944  *   Call IBM/MS INT13 Extensions (int 13 %ax=AX) for DRIVE. DAP
    945  *   is passed for disk address packet. If an error occurs, return
    946  *   non-zero, otherwise zero.
    947  */
    948 
    949 ENTRY(biosdisk_int13_extensions)
    950 	pushl	%ebp
    951 	movl	%esp, %ebp
    952 
    953 	pushl	%esi
    954 	pushl	%ebx
    955 
    956 	/* compute the address of disk_address_packet */
    957 	movl	0x10(%ebp), %eax
    958 	movw	%ax, %si
    959 	xorw	%ax, %ax
    960 	shrl	$4, %eax
    961 	movw	%ax, %cx	/* save the segment to cx */
    962 
    963 	/* drive */
    964 	movb	0xc(%ebp), %dl
    965 	/* ax */
    966 	movw	0x8(%ebp), %bx
    967 	/* enter real mode */
    968 	call	EXT_C(prot_to_real)
    969 
    970 	.code16
    971 	movw	%bx, %ax
    972 	movw	%cx, %ds
    973 	int	$0x13		/* do the operation */
    974 	movb	%ah, %dl	/* save return value */
    975 	/* clear the data segment */
    976 	xorw	%ax, %ax
    977 	movw	%ax, %ds
    978 	/* back to protected mode */
    979 	DATA32	call	EXT_C(real_to_prot)
    980 	.code32
    981 
    982 	movb	%dl, %al	/* return value in %eax */
    983 
    984 	popl	%ebx
    985 	popl	%esi
    986 	popl	%ebp
    987 
    988 	ret
    989 
    990 /*
    991  *   int biosdisk_standard (int ah, int drive, int coff, int hoff, int soff,
    992  *                          int nsec, int segment)
    993  *
    994  *   Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write
    995  *   NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs,
    996  *   return non-zero, otherwise zero.
    997  */
    998 
    999 ENTRY(biosdisk_standard)
   1000 	pushl	%ebp
   1001 	movl	%esp, %ebp
   1002 
   1003 	pushl	%ebx
   1004 	pushl	%edi
   1005 	pushl	%esi
   1006 
   1007 	/* set up CHS information */
   1008 	movl	0x10(%ebp), %eax
   1009 	movb	%al, %ch
   1010 	movb	0x18(%ebp), %al
   1011 	shlb	$2, %al
   1012 	shrw	$2, %ax
   1013 	movb	%al, %cl
   1014 	movb	0x14(%ebp), %dh
   1015 	/* drive */
   1016 	movb	0xc(%ebp), %dl
   1017 	/* segment */
   1018 	movw	0x20(%ebp), %bx
   1019 	/* save nsec and ah to %di */
   1020 	movb	0x8(%ebp), %ah
   1021 	movb	0x1c(%ebp), %al
   1022 	movw	%ax, %di
   1023 	/* enter real mode */
   1024 	call	EXT_C(prot_to_real)
   1025 
   1026 	.code16
   1027 	movw	%bx, %es
   1028 	xorw	%bx, %bx
   1029 	movw	$3, %si		/* attempt at least three times */
   1030 
   1031 1:
   1032 	movw	%di, %ax
   1033 	int	$0x13		/* do the operation */
   1034 	jnc	2f		/* check if successful */
   1035 
   1036 	movb	%ah, %bl	/* save return value */
   1037 	/* if fail, reset the disk system */
   1038 	xorw	%ax, %ax
   1039 	int	$0x13
   1040 
   1041 	decw	%si
   1042 	cmpw	$0, %si
   1043 	je	2f
   1044 	xorb	%bl, %bl
   1045 	jmp	1b		/* retry */
   1046 2:
   1047 	/* back to protected mode */
   1048 	DATA32	call	EXT_C(real_to_prot)
   1049 	.code32
   1050 
   1051 	movb	%bl, %al	/* return value in %eax */
   1052 
   1053 	popl	%esi
   1054 	popl	%edi
   1055 	popl	%ebx
   1056 	popl	%ebp
   1057 
   1058 	ret
   1059 
   1060 
   1061 /*
   1062  *   int check_int13_extensions (int drive)
   1063  *
   1064  *   Check if LBA is supported for DRIVE. If it is supported, then return
   1065  *   the major version of extensions, otherwise zero.
   1066  */
   1067 
   1068 ENTRY(check_int13_extensions)
   1069 	pushl	%ebp
   1070 	movl	%esp, %ebp
   1071 
   1072 	pushl	%ebx
   1073 
   1074 	/* drive */
   1075 	movb	0x8(%ebp), %dl
   1076 	/* enter real mode */
   1077 	call	EXT_C(prot_to_real)
   1078 
   1079 	.code16
   1080 	movb	$0x41, %ah
   1081 	movw	$0x55aa, %bx
   1082 	int	$0x13		/* do the operation */
   1083 
   1084 	/* check the result */
   1085 	jc	1f
   1086 	cmpw	$0xaa55, %bx
   1087 	jne	1f
   1088 
   1089 	movb	%ah, %bl	/* save the major version into %bl */
   1090 
   1091 	/* check if AH=0x42 is supported if FORCE_LBA is zero */
   1092 	movb	EXT_C(force_lba), %al
   1093 	testb	%al, %al
   1094 	jnz	2f
   1095 	andw	$1, %cx
   1096 	jnz	2f
   1097 
   1098 1:
   1099 	xorb	%bl, %bl
   1100 2:
   1101 	/* back to protected mode */
   1102 	DATA32	call	EXT_C(real_to_prot)
   1103 	.code32
   1104 
   1105 	movb	%bl, %al	/* return value in %eax */
   1106 
   1107 	popl	%ebx
   1108 	popl	%ebp
   1109 
   1110 	ret
   1111 
   1112 
   1113 /*
   1114  *   int get_diskinfo_standard (int drive, unsigned long *cylinders,
   1115  *                              unsigned long *heads, unsigned long *sectors)
   1116  *
   1117  *   Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
   1118  *   error occurs, then return non-zero, otherwise zero.
   1119  */
   1120 
   1121 ENTRY(get_diskinfo_standard)
   1122 	pushl	%ebp
   1123 	movl	%esp, %ebp
   1124 
   1125 	pushl	%ebx
   1126 	pushl	%edi
   1127 
   1128 	/* drive */
   1129 	movb	0x8(%ebp), %dl
   1130 	/* enter real mode */
   1131 	call	EXT_C(prot_to_real)
   1132 
   1133 	.code16
   1134 	movb	$0x8, %ah
   1135 	int	$0x13		/* do the operation */
   1136 	/* check if successful */
   1137 	testb	%ah, %ah
   1138 	jnz	1f
   1139 	/* bogus BIOSes may not return an error number */
   1140 	testb	$0x3f, %cl	/* 0 sectors means no disk */
   1141 	jnz	1f		/* if non-zero, then succeed */
   1142 	/* XXX 0x60 is one of the unused error numbers */
   1143 	movb	$0x60, %ah
   1144 1:
   1145 	movb	%ah, %bl	/* save return value in %bl */
   1146 	/* back to protected mode */
   1147 	DATA32	call	EXT_C(real_to_prot)
   1148 	.code32
   1149 
   1150 	/* restore %ebp */
   1151 	leal	0x8(%esp), %ebp
   1152 
   1153 	/* heads */
   1154 	movb	%dh, %al
   1155 	incl	%eax		/* the number of heads is counted from zero */
   1156 	movl	0x10(%ebp), %edi
   1157 	movl	%eax, (%edi)
   1158 
   1159 	/* sectors */
   1160 	xorl	%eax, %eax
   1161 	movb	%cl, %al
   1162 	andb	$0x3f, %al
   1163 	movl	0x14(%ebp), %edi
   1164 	movl	%eax, (%edi)
   1165 
   1166 	/* cylinders */
   1167 	shrb	$6, %cl
   1168 	movb	%cl, %ah
   1169 	movb	%ch, %al
   1170 	incl	%eax		/* the number of cylinders is
   1171 				   counted from zero */
   1172 	movl	0xc(%ebp), %edi
   1173 	movl	%eax, (%edi)
   1174 
   1175 	xorl	%eax, %eax
   1176 	movb	%bl, %al	/* return value in %eax */
   1177 
   1178 	popl	%edi
   1179 	popl	%ebx
   1180 	popl	%ebp
   1181 
   1182 	ret
   1183 
   1184 
   1185 #if 0
   1186 /*
   1187  *   int get_diskinfo_floppy (int drive, unsigned long *cylinders,
   1188  *                            unsigned long *heads, unsigned long *sectors)
   1189  *
   1190  *   Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
   1191  *   error occurs, then return non-zero, otherwise zero.
   1192  */
   1193 
   1194 ENTRY(get_diskinfo_floppy)
   1195 	pushl	%ebp
   1196 	movl	%esp, %ebp
   1197 
   1198 	pushl	%ebx
   1199 	pushl	%esi
   1200 
   1201 	/* drive */
   1202 	movb	0x8(%ebp), %dl
   1203 	/* enter real mode */
   1204 	call	EXT_C(prot_to_real)
   1205 
   1206 	.code16
   1207 	/* init probe value */
   1208 	movl	$probe_values-1, %esi
   1209 1:
   1210 	xorw	%ax, %ax
   1211 	int	$0x13		/* reset floppy controller */
   1212 
   1213 	incw	%si
   1214 	movb	(%si), %cl
   1215 	cmpb	$0, %cl		/* probe failed if zero */
   1216 	je	2f
   1217 
   1218 	/* perform read */
   1219 	movw	$SCRATCHSEG, %ax
   1220 	movw	%ax, %es
   1221 	xorw	%bx, %bx
   1222 	movw	$0x0201, %ax
   1223 	movb	$0, %ch
   1224 	movb	$0, %dh
   1225 	int	$0x13
   1226 
   1227 	/* FIXME: Read from floppy may fail even if the geometry is correct.
   1228 	   So should retry at least three times.  */
   1229 	jc	1b		/* next value */
   1230 
   1231 	/* succeed */
   1232 	jmp	2f
   1233 
   1234 probe_values:
   1235 	.byte	36, 18, 15, 9, 0
   1236 
   1237 2:
   1238 	/* back to protected mode */
   1239 	DATA32	call	EXT_C(real_to_prot)
   1240 	.code32
   1241 
   1242 	/* restore %ebp */
   1243 	leal	0x8(%esp), %ebp
   1244 
   1245 	/* cylinders */
   1246 	movl	0xc(%ebp), %eax
   1247 	movl	$80, %ebx
   1248 	movl	%ebx, (%eax)
   1249 	/* heads */
   1250 	movl	0x10(%ebp), %eax
   1251 	movl	$2, %ebx
   1252 	movl	%ebx, (%eax)
   1253 	/* sectors */
   1254 	movl	0x14(%ebp), %eax
   1255 	movzbl	%cl, %ebx
   1256 	movl	%ebx, (%eax)
   1257 
   1258 	/* return value in %eax */
   1259 	xorl	%eax, %eax
   1260 	cmpb	$0, %cl
   1261 	jne	3f
   1262 	incl	%eax		/* %eax = 1 (non-zero) */
   1263 3:
   1264 	popl	%esi
   1265 	popl	%ebx
   1266 	popl	%ebp
   1267 
   1268 	ret
   1269 #endif
   1270 
   1271 
   1272 /* Source files are splitted, as they have different copyrights.  */
   1273 #ifndef STAGE1_5
   1274 # include "setjmp.S"
   1275 # include "apm.S"
   1276 #endif /* ! STAGE1_5 */
   1277 
   1278 
   1279 
   1280 #ifndef STAGE1_5
   1281 /* get_code_end() :  return the address of the end of the code
   1282  * This is here so that it can be replaced by asmstub.c.
   1283  */
   1284 ENTRY(get_code_end)
   1285 	/* will be the end of the bss */
   1286 # if defined(HAVE_END_SYMBOL)
   1287 	movl	$end, %eax
   1288 # elif defined(HAVE_USCORE_END_SYMBOL)
   1289 	movl	$_end, %eax
   1290 # endif
   1291 	shrl	$2, %eax		/* Round up to the next word. */
   1292 	incl	%eax
   1293 	shll	$2, %eax
   1294 	ret
   1295 #endif /* ! STAGE1_5 */
   1296 
   1297 /*
   1298  *
   1299  * get_memsize(i) :  return the memory size in KB. i == 0 for conventional
   1300  *		memory, i == 1 for extended memory
   1301  *	BIOS call "INT 12H" to get conventional memory size
   1302  *	BIOS call "INT 15H, AH=88H" to get extended memory size
   1303  *		Both have the return value in AX.
   1304  *
   1305  */
   1306 
   1307 ENTRY(get_memsize)
   1308 	push	%ebp
   1309 	push	%ebx
   1310 
   1311 	mov	0xc(%esp), %ebx
   1312 
   1313 	call	EXT_C(prot_to_real)	/* enter real mode */
   1314 	.code16
   1315 
   1316 	cmpb	$0x1, %bl
   1317 	DATA32	je	xext
   1318 
   1319 	int	$0x12
   1320 	DATA32	jmp	xdone
   1321 
   1322 xext:
   1323 	movb	$0x88, %ah
   1324 	int	$0x15
   1325 
   1326 xdone:
   1327 	movw	%ax, %bx
   1328 
   1329 	DATA32	call	EXT_C(real_to_prot)
   1330 	.code32
   1331 
   1332 	movw	%bx, %ax
   1333 	pop	%ebx
   1334 	pop	%ebp
   1335 	ret
   1336 
   1337 
   1338 #ifndef STAGE1_5
   1339 
   1340 /*
   1341  *
   1342  * get_eisamemsize() :  return packed EISA memory map, lower 16 bits is
   1343  *		memory between 1M and 16M in 1K parts, upper 16 bits is
   1344  *		memory above 16M in 64K parts.  If error, return -1.
   1345  *	BIOS call "INT 15H, AH=E801H" to get EISA memory map,
   1346  *		AX = memory between 1M and 16M in 1K parts.
   1347  *		BX = memory above 16M in 64K parts.
   1348  *
   1349  */
   1350 
   1351 ENTRY(get_eisamemsize)
   1352 	push	%ebp
   1353 	push	%ebx
   1354 
   1355 	call	EXT_C(prot_to_real)	/* enter real mode */
   1356 	.code16
   1357 
   1358 	movw	$0xe801, %ax
   1359 	int	$0x15
   1360 
   1361 	shll	$16, %ebx
   1362 	movw	%ax, %bx
   1363 
   1364 	DATA32	call	EXT_C(real_to_prot)
   1365 	.code32
   1366 
   1367 	movl	$0xFFFFFFFF, %eax
   1368 	cmpb	$0x86, %bh
   1369 	je	xnoteisa
   1370 
   1371 	movl	%ebx, %eax
   1372 
   1373 xnoteisa:
   1374 	pop	%ebx
   1375 	pop	%ebp
   1376 	ret
   1377 
   1378 /*
   1379  *
   1380  * get_mmap_entry(addr, cont) :  address and old continuation value (zero to
   1381  *		start), for the Query System Address Map BIOS call.
   1382  *
   1383  *  Sets the first 4-byte int value of "addr" to the size returned by
   1384  *  the call.  If the call fails, sets it to zero.
   1385  *
   1386  *	Returns:  new (non-zero) continuation value, 0 if done.
   1387  *
   1388  * NOTE: Currently hard-coded for a maximum buffer length of 1024.
   1389  */
   1390 
   1391 ENTRY(get_mmap_entry)
   1392 	push	%ebp
   1393 	push	%ebx
   1394 	push	%edi
   1395 	push	%esi
   1396 
   1397 	/* place address (+4) in ES:DI */
   1398 	movl	0x14(%esp), %eax
   1399 	addl	$4, %eax
   1400 	movl	%eax, %edi
   1401 	andl	$0xf, %edi
   1402 	shrl	$4, %eax
   1403 	movl	%eax, %esi
   1404 
   1405 	/* set continuation value */
   1406 	movl	0x18(%esp), %ebx
   1407 
   1408 	/* set default maximum buffer size */
   1409 	movl	$0x14, %ecx
   1410 
   1411 	/* set EDX to 'SMAP' */
   1412 	movl	$0x534d4150, %edx
   1413 
   1414 	call	EXT_C(prot_to_real)	/* enter real mode */
   1415 	.code16
   1416 
   1417 	movw	%si, %es
   1418 	movl	$0xe820, %eax
   1419 	int	$0x15
   1420 
   1421 	DATA32	jc	xnosmap
   1422 
   1423 	cmpl	$0x534d4150, %eax
   1424 	DATA32	jne	xnosmap
   1425 
   1426 	cmpl	$0x14, %ecx
   1427 	DATA32	jl	xnosmap
   1428 
   1429 	cmpl	$0x400, %ecx
   1430 	DATA32	jg	xnosmap
   1431 
   1432 	DATA32	jmp	xsmap
   1433 
   1434 xnosmap:
   1435 	movl	$0, %ecx
   1436 
   1437 xsmap:
   1438 	DATA32	call	EXT_C(real_to_prot)
   1439 	.code32
   1440 
   1441 	/* write length of buffer (zero if error) into "addr" */
   1442 	movl	0x14(%esp), %eax
   1443 	movl	%ecx, (%eax)
   1444 
   1445 	/* set return value to continuation */
   1446 	movl	%ebx, %eax
   1447 
   1448 	pop	%esi
   1449 	pop	%edi
   1450 	pop	%ebx
   1451 	pop	%ebp
   1452 	ret
   1453 
   1454 /*
   1455  * get_rom_config_table()
   1456  *
   1457  * Get the linear address of a ROM configuration table. Return zero,
   1458  * if fails.
   1459  */
   1460 
   1461 ENTRY(get_rom_config_table)
   1462 	pushl	%ebp
   1463 	pushl	%ebx
   1464 
   1465 	/* zero %ebx for simplicity */
   1466 	xorl	%ebx, %ebx
   1467 
   1468 	call	EXT_C(prot_to_real)
   1469 	.code16
   1470 
   1471 	movw	$0xc0, %ax
   1472 	int	$0x15
   1473 
   1474 	jc	no_rom_table
   1475 	testb	%ah, %ah
   1476 	jnz	no_rom_table
   1477 
   1478 	movw	%es, %dx
   1479 	jmp	found_rom_table
   1480 
   1481 no_rom_table:
   1482 	xorw	%dx, %dx
   1483 	xorw	%bx, %bx
   1484 
   1485 found_rom_table:
   1486 	DATA32	call	EXT_C(real_to_prot)
   1487 	.code32
   1488 
   1489 	/* compute the linear address */
   1490 	movw	%dx, %ax
   1491 	shll	$4, %eax
   1492 	addl	%ebx, %eax
   1493 
   1494 	popl	%ebx
   1495 	popl	%ebp
   1496 	ret
   1497 
   1498 
   1499 /*
   1500  * int get_vbe_controller_info (struct vbe_controller *controller_ptr)
   1501  *
   1502  * Get VBE controller information.
   1503  */
   1504 
   1505 ENTRY(get_vbe_controller_info)
   1506 	pushl	%ebp
   1507 	movl	%esp, %ebp
   1508 
   1509 	pushl	%edi
   1510 	pushl	%ebx
   1511 
   1512 	/* Convert the linear address to segment:offset */
   1513 	movl	8(%ebp), %eax
   1514 	movl	%eax, %edi
   1515 	andl	$0x0000000f, %edi
   1516 	shrl	$4, %eax
   1517 	movl	%eax, %ebx
   1518 
   1519 	call	EXT_C(prot_to_real)
   1520 	.code16
   1521 
   1522 	movw	%bx, %es
   1523 	movw	$0x4F00, %ax
   1524 	int	$0x10
   1525 
   1526 	movw	%ax, %bx
   1527 	DATA32	call	EXT_C(real_to_prot)
   1528 	.code32
   1529 
   1530 	movzwl	%bx, %eax
   1531 
   1532 	popl	%ebx
   1533 	popl	%edi
   1534 	popl	%ebp
   1535 	ret
   1536 
   1537 
   1538 /*
   1539  * int get_vbe_mode_info (int mode_number, struct vbe_mode *mode_ptr)
   1540  *
   1541  * Get VBE mode information.
   1542  */
   1543 
   1544 ENTRY(get_vbe_mode_info)
   1545 	pushl	%ebp
   1546 	movl	%esp, %ebp
   1547 
   1548 	pushl	%edi
   1549 	pushl	%ebx
   1550 
   1551 	/* Convert the linear address to segment:offset */
   1552 	movl	0xc(%ebp), %eax
   1553 	movl	%eax, %edi
   1554 	andl	$0x0000000f, %edi
   1555 	shrl	$4, %eax
   1556 	movl	%eax, %ebx
   1557 
   1558 	/* Save the mode number in %cx */
   1559 	movl	0x8(%ebp), %ecx
   1560 
   1561 	call	EXT_C(prot_to_real)
   1562 	.code16
   1563 
   1564 	movw	%bx, %es
   1565 	movw	$0x4F01, %ax
   1566 	int	$0x10
   1567 
   1568 	movw	%ax, %bx
   1569 	DATA32	call	EXT_C(real_to_prot)
   1570 	.code32
   1571 
   1572 	movzwl	%bx, %eax
   1573 
   1574 	popl	%ebx
   1575 	popl	%edi
   1576 	popl	%ebp
   1577 	ret
   1578 
   1579 
   1580 /*
   1581  * int set_vbe_mode (int mode_number)
   1582  *
   1583  * Set VBE mode. Don't support user-specified CRTC information.
   1584  */
   1585 
   1586 ENTRY(set_vbe_mode)
   1587 	pushl	%ebp
   1588 	movl	%esp, %ebp
   1589 
   1590 	pushl	%ebx
   1591 
   1592 	/* Save the mode number in %bx */
   1593 	movl	0x8(%ebp), %ebx
   1594 	/* Clear bit D11 */
   1595 	andl	$0xF7FF, %ebx
   1596 
   1597 	call	EXT_C(prot_to_real)
   1598 	.code16
   1599 
   1600 	movw	$0x4F02, %ax
   1601 	int	$0x10
   1602 
   1603 	movw	%ax, %bx
   1604 	DATA32	call	EXT_C(real_to_prot)
   1605 	.code32
   1606 
   1607 	movzwl	%bx, %eax
   1608 
   1609 	popl	%ebx
   1610 	popl	%ebp
   1611 	ret
   1612 
   1613 
   1614 /*
   1615  * gateA20(int linear)
   1616  *
   1617  * Gate address-line 20 for high memory.
   1618  *
   1619  * This routine is probably overconservative in what it does, but so what?
   1620  *
   1621  * It also eats any keystrokes in the keyboard buffer.  :-(
   1622  */
   1623 
   1624 ENTRY(gateA20)
   1625 	/* first, try a BIOS call */
   1626 	pushl	%ebp
   1627 	movl	8(%esp), %edx
   1628 
   1629 	call	EXT_C(prot_to_real)
   1630 
   1631 	.code16
   1632 	movw	$0x2400, %ax
   1633 	testw	%dx, %dx
   1634 	jz	1f
   1635 	incw	%ax
   1636 1:	stc
   1637 	int	$0x15
   1638 	jnc	2f
   1639 
   1640 	/* set non-zero if failed */
   1641 	movb	$1, %ah
   1642 
   1643 	/* save the status */
   1644 2:	movb	%ah, %dl
   1645 
   1646 	DATA32	call	EXT_C(real_to_prot)
   1647 	.code32
   1648 
   1649 	popl	%ebp
   1650 	testb	%dl, %dl
   1651 	jnz	3f
   1652 	ret
   1653 
   1654 3:	/* use keyboard controller */
   1655 	pushl	%eax
   1656 
   1657 	call    gloop1
   1658 
   1659 	movb	$KC_CMD_WOUT, %al
   1660 	outb	$K_CMD
   1661 
   1662 gloopint1:
   1663 	inb	$K_STATUS
   1664 	andb	$K_IBUF_FUL, %al
   1665 	jnz	gloopint1
   1666 
   1667 	movb	$KB_OUTPUT_MASK, %al
   1668 	cmpb	$0, 0x8(%esp)
   1669 	jz	gdoit
   1670 
   1671 	orb	$KB_A20_ENABLE, %al
   1672 gdoit:
   1673 	outb	$K_RDWR
   1674 
   1675 	call	gloop1
   1676 
   1677 	/* output a dummy command (USB keyboard hack) */
   1678 	movb	$0xff, %al
   1679 	outb	$K_CMD
   1680 	call	gloop1
   1681 
   1682 	popl	%eax
   1683 	ret
   1684 
   1685 gloop1:
   1686 	inb	$K_STATUS
   1687 	andb	$K_IBUF_FUL, %al
   1688 	jnz	gloop1
   1689 
   1690 gloop2:
   1691 	inb	$K_STATUS
   1692 	andb	$K_OBUF_FUL, %al
   1693 	jz	gloop2ret
   1694 	inb	$K_RDWR
   1695 	jmp	gloop2
   1696 
   1697 gloop2ret:
   1698 	ret
   1699 
   1700 
   1701 ENTRY(patch_code)	/* labels start with "pc_" */
   1702 	.code16
   1703 
   1704 	mov	%cs, %ax
   1705 	mov	%ax, %ds
   1706 	mov	%ax, %es
   1707 	mov	%ax, %fs
   1708 	mov	%ax, %gs
   1709 	ADDR32	movl	$0, 0
   1710 pc_stop:
   1711 	hlt
   1712 	DATA32	jmp	pc_stop
   1713 ENTRY(patch_code_end)
   1714 
   1715 	.code32
   1716 
   1717 
   1718 /*
   1719  * linux_boot()
   1720  *
   1721  * Does some funky things (including on the stack!), then jumps to the
   1722  * entry point of the Linux setup code.
   1723  */
   1724 
   1725 VARIABLE(linux_text_len)
   1726 	.long	0
   1727 
   1728 VARIABLE(linux_data_tmp_addr)
   1729 	.long	0
   1730 
   1731 VARIABLE(linux_data_real_addr)
   1732 	.long	0
   1733 
   1734 ENTRY(linux_boot)
   1735 	/* don't worry about saving anything, we're committed at this point */
   1736 	cld	/* forward copying */
   1737 
   1738 	/* copy kernel */
   1739 	movl	EXT_C(linux_text_len), %ecx
   1740 	addl	$3, %ecx
   1741 	shrl	$2, %ecx
   1742 	movl	$LINUX_BZIMAGE_ADDR, %esi
   1743 	movl	$LINUX_ZIMAGE_ADDR, %edi
   1744 
   1745 	rep
   1746 	movsl
   1747 
   1748 ENTRY(big_linux_boot)
   1749 	movl	EXT_C(linux_data_real_addr), %ebx
   1750 
   1751 	/* copy the real mode part */
   1752 	movl	EXT_C(linux_data_tmp_addr), %esi
   1753 	movl	%ebx, %edi
   1754 	movl	$LINUX_SETUP_MOVE_SIZE, %ecx
   1755 	cld
   1756 	rep
   1757 	movsb
   1758 
   1759 	/* change %ebx to the segment address */
   1760 	shrl	$4, %ebx
   1761 	movl	%ebx, %eax
   1762 	addl	$0x20, %eax
   1763 	movl	%eax, linux_setup_seg
   1764 
   1765 	/* XXX new stack pointer in safe area for calling functions */
   1766 	movl	$0x4000, %esp
   1767 	call	EXT_C(stop_floppy)
   1768 
   1769 	/* final setup for linux boot */
   1770 
   1771 	call	EXT_C(prot_to_real)
   1772 	.code16
   1773 
   1774 	/* final setup for linux boot */
   1775 	cli
   1776 	movw	%bx, %ss
   1777 	movw	$LINUX_SETUP_STACK, %sp
   1778 
   1779 	movw	%bx, %ds
   1780 	movw	%bx, %es
   1781 	movw	%bx, %fs
   1782 	movw	%bx, %gs
   1783 
   1784 	/* jump to start */
   1785 	/* ljmp */
   1786 	.byte	0xea
   1787 	.word	0
   1788 linux_setup_seg:
   1789 	.word	0
   1790 	.code32
   1791 
   1792 
   1793 /*
   1794  * multi_boot(int start, int mb_info)
   1795  *
   1796  *  This starts a kernel in the manner expected of the multiboot standard.
   1797  */
   1798 
   1799 ENTRY(multi_boot)
   1800 	/* no need to save anything */
   1801 	call	EXT_C(stop_floppy)
   1802 
   1803 	movl	$0x2BADB002, %eax
   1804 	movl	0x8(%esp), %ebx
   1805 
   1806 	/* boot kernel here (absolute address call) */
   1807 	call	*0x4(%esp)
   1808 
   1809 	/* error */
   1810 	call	EXT_C(stop)
   1811 
   1812 #endif /* ! STAGE1_5 */
   1813 
   1814 /*
   1815  * void console_putchar (int c)
   1816  *
   1817  * Put the character C on the console. Because GRUB wants to write a
   1818  * character with an attribute, this implementation is a bit tricky.
   1819  * If C is a control character (CR, LF, BEL, BS), use INT 10, AH = 0Eh
   1820  * (TELETYPE OUTPUT). Otherwise, save the original position, put a space,
   1821  * save the current position, restore the original position, write the
   1822  * character and the attribute, and restore the current position.
   1823  *
   1824  * The reason why this is so complicated is that there is no easy way to
   1825  * get the height of the screen, and the TELETYPE OUPUT BIOS call doesn't
   1826  * support setting a background attribute.
   1827  */
   1828 ENTRY(console_putchar)
   1829 	movl	0x4(%esp), %edx
   1830 	pusha
   1831 #ifdef STAGE1_5
   1832 	movb	$0x07, %bl
   1833 #else
   1834 	movl	EXT_C(console_current_color), %ebx
   1835 #endif
   1836 
   1837 	call	EXT_C(prot_to_real)
   1838 	.code16
   1839 	movb	%dl, %al
   1840 	xorb	%bh, %bh
   1841 
   1842 #ifndef STAGE1_5
   1843 	/* use teletype output if control character */
   1844 	cmpb	$0x7, %al
   1845 	je	1f
   1846 	cmpb	$0x8, %al
   1847 	je	1f
   1848 	cmpb	$0xa, %al
   1849 	je	1f
   1850 	cmpb	$0xd, %al
   1851 	je	1f
   1852 
   1853 	/* save the character and the attribute on the stack */
   1854 	pushw	%ax
   1855 	pushw	%bx
   1856 
   1857 	/* get the current position */
   1858 	movb	$0x3, %ah
   1859 	int	$0x10
   1860 
   1861 	/* check the column with the width */
   1862 	cmpb	$79, %dl
   1863 	jl	2f
   1864 
   1865 	/* print CR and LF, if next write will exceed the width */
   1866 	movw	$0x0e0d, %ax
   1867 	int	$0x10
   1868 	movb	$0x0a, %al
   1869 	int	$0x10
   1870 
   1871 	/* get the current position */
   1872 	movb	$0x3, %ah
   1873 	int	$0x10
   1874 
   1875 2:
   1876 	/* restore the character and the attribute */
   1877 	popw	%bx
   1878 	popw	%ax
   1879 
   1880 	/* write the character with the attribute */
   1881 	movb	$0x9, %ah
   1882 	movw	$1, %cx
   1883 	int	$0x10
   1884 
   1885 	/* move the cursor forward */
   1886 	incb	%dl
   1887 	movb	$0x2, %ah
   1888 	int	$0x10
   1889 
   1890 	jmp	3f
   1891 #endif /* ! STAGE1_5 */
   1892 
   1893 1:	movb	$0xe, %ah
   1894 	int	$0x10
   1895 
   1896 3:	DATA32	call	EXT_C(real_to_prot)
   1897 	.code32
   1898 
   1899 	popa
   1900 	ret
   1901 
   1902 
   1903 #ifndef STAGE1_5
   1904 
   1905 /* this table is used in translate_keycode below */
   1906 translation_table:
   1907 	.word	KEY_LEFT, 2
   1908 	.word	KEY_RIGHT, 6
   1909 	.word	KEY_UP, 16
   1910 	.word	KEY_DOWN, 14
   1911 	.word	KEY_HOME, 1
   1912 	.word	KEY_END, 5
   1913 	.word	KEY_DC, 4
   1914 	.word	KEY_BACKSPACE, 8
   1915 	.word	KEY_PPAGE, 7
   1916 	.word	KEY_NPAGE, 3
   1917 	.word	0
   1918 
   1919 /*
   1920  * translate_keycode translates the key code %dx to an ascii code.
   1921  */
   1922 	.code16
   1923 
   1924 translate_keycode:
   1925 	pushw	%bx
   1926 	pushw	%si
   1927 
   1928 	movw	$ABS(translation_table), %si
   1929 
   1930 1:	lodsw
   1931 	/* check if this is the end */
   1932 	testw	%ax, %ax
   1933 	jz	2f
   1934 	/* load the ascii code into %ax */
   1935 	movw	%ax, %bx
   1936 	lodsw
   1937 	/* check if this matches the key code */
   1938 	cmpw	%bx, %dx
   1939 	jne	1b
   1940 	/* translate %dx, if successful */
   1941 	movw	%ax, %dx
   1942 
   1943 2:	popw	%si
   1944 	popw	%bx
   1945 	ret
   1946 
   1947 	.code32
   1948 
   1949 
   1950 /*
   1951  * remap_ascii_char remaps the ascii code %dl to another if the code is
   1952  * contained in ASCII_KEY_MAP.
   1953  */
   1954 	.code16
   1955 
   1956 remap_ascii_char:
   1957 	pushw	%si
   1958 
   1959 	movw	$ABS(EXT_C(ascii_key_map)), %si
   1960 1:
   1961 	lodsw
   1962 	/* check if this is the end */
   1963 	testw	%ax, %ax
   1964 	jz	2f
   1965 	/* check if this matches the ascii code */
   1966 	cmpb	%al, %dl
   1967 	jne	1b
   1968 	/* if so, perform the mapping */
   1969 	movb	%ah, %dl
   1970 2:
   1971 	/* restore %si */
   1972 	popw	%si
   1973 
   1974 	ret
   1975 
   1976 	.code32
   1977 
   1978 	.align	4
   1979 ENTRY(ascii_key_map)
   1980 	.space	(KEY_MAP_SIZE + 1) * 2
   1981 
   1982 
   1983 /*
   1984  * int console_getkey (void)
   1985  * BIOS call "INT 16H Function 00H" to read character from keyboard
   1986  *	Call with	%ah = 0x0
   1987  *	Return:		%ah = keyboard scan code
   1988  *			%al = ASCII character
   1989  */
   1990 
   1991 ENTRY(console_getkey)
   1992 	push	%ebp
   1993 
   1994 	call	EXT_C(prot_to_real)
   1995 	.code16
   1996 
   1997 	int	$0x16
   1998 
   1999 	movw	%ax, %dx		/* real_to_prot uses %eax */
   2000 	call	translate_keycode
   2001 	call	remap_ascii_char
   2002 
   2003 	DATA32	call	EXT_C(real_to_prot)
   2004 	.code32
   2005 
   2006 	movw	%dx, %ax
   2007 
   2008 	pop	%ebp
   2009 	ret
   2010 
   2011 
   2012 /*
   2013  * int console_checkkey (void)
   2014  *	if there is a character pending, return it; otherwise return -1
   2015  * BIOS call "INT 16H Function 01H" to check whether a character is pending
   2016  *	Call with	%ah = 0x1
   2017  *	Return:
   2018  *		If key waiting to be input:
   2019  *			%ah = keyboard scan code
   2020  *			%al = ASCII character
   2021  *			Zero flag = clear
   2022  *		else
   2023  *			Zero flag = set
   2024  */
   2025 ENTRY(console_checkkey)
   2026 	push	%ebp
   2027 	xorl	%edx, %edx
   2028 
   2029 	call	EXT_C(prot_to_real)	/* enter real mode */
   2030 	.code16
   2031 
   2032 	movb	$0x1, %ah
   2033 	int	$0x16
   2034 
   2035 	DATA32	jz	notpending
   2036 
   2037 	movw	%ax, %dx
   2038 	call	translate_keycode
   2039 	call	remap_ascii_char
   2040 	DATA32	jmp	pending
   2041 
   2042 notpending:
   2043 	movl	$0xFFFFFFFF, %edx
   2044 
   2045 pending:
   2046 	DATA32	call	EXT_C(real_to_prot)
   2047 	.code32
   2048 
   2049 	mov	%edx, %eax
   2050 
   2051 	pop	%ebp
   2052 	ret
   2053 
   2054 
   2055 /*
   2056  * int console_getxy (void)
   2057  * BIOS call "INT 10H Function 03h" to get cursor position
   2058  *	Call with	%ah = 0x03
   2059  *			%bh = page
   2060  *      Returns         %ch = starting scan line
   2061  *                      %cl = ending scan line
   2062  *                      %dh = row (0 is top)
   2063  *                      %dl = column (0 is left)
   2064  */
   2065 
   2066 
   2067 ENTRY(console_getxy)
   2068 	push	%ebp
   2069 	push	%ebx                    /* save EBX */
   2070 
   2071 	call	EXT_C(prot_to_real)
   2072 	.code16
   2073 
   2074         xorb	%bh, %bh                /* set page to 0 */
   2075 	movb	$0x3, %ah
   2076 	int	$0x10			/* get cursor position */
   2077 
   2078 	DATA32	call	EXT_C(real_to_prot)
   2079 	.code32
   2080 
   2081 	movb	%dl, %ah
   2082 	movb	%dh, %al
   2083 
   2084 	pop	%ebx
   2085 	pop	%ebp
   2086 	ret
   2087 
   2088 
   2089 /*
   2090  * void console_gotoxy(int x, int y)
   2091  * BIOS call "INT 10H Function 02h" to set cursor position
   2092  *	Call with	%ah = 0x02
   2093  *			%bh = page
   2094  *                      %dh = row (0 is top)
   2095  *                      %dl = column (0 is left)
   2096  */
   2097 
   2098 
   2099 ENTRY(console_gotoxy)
   2100 	push	%ebp
   2101 	push	%ebx                    /* save EBX */
   2102 
   2103 	movb	0xc(%esp), %dl           /* %dl = x */
   2104 	movb	0x10(%esp), %dh          /* %dh = y */
   2105 
   2106 	call	EXT_C(prot_to_real)
   2107 	.code16
   2108 
   2109         xorb	%bh, %bh                /* set page to 0 */
   2110 	movb	$0x2, %ah
   2111 	int	$0x10			/* set cursor position */
   2112 
   2113 	DATA32	call	EXT_C(real_to_prot)
   2114 	.code32
   2115 
   2116 	pop	%ebx
   2117 	pop	%ebp
   2118 	ret
   2119 
   2120 
   2121 /*
   2122  * void console_cls (void)
   2123  * BIOS call "INT 10H Function 09h" to write character and attribute
   2124  *	Call with	%ah = 0x09
   2125  *                      %al = (character)
   2126  *                      %bh = (page number)
   2127  *                      %bl = (attribute)
   2128  *                      %cx = (number of times)
   2129  */
   2130 
   2131 
   2132 ENTRY(console_cls)
   2133 	push	%ebp
   2134 	push	%ebx                    /* save EBX */
   2135 
   2136 	call	EXT_C(prot_to_real)
   2137 	.code16
   2138 
   2139 	/* move the cursor to the beginning */
   2140 	movb	$0x02, %ah
   2141 	xorb	%bh, %bh
   2142 	xorw	%dx, %dx
   2143 	int	$0x10
   2144 
   2145 	/* write spaces to the entire screen */
   2146 	movw	$0x0920, %ax
   2147 	movw	$0x07, %bx
   2148 	movw	$(80 * 25), %cx
   2149         int	$0x10
   2150 
   2151 	/* move back the cursor */
   2152 	movb	$0x02, %ah
   2153 	int	$0x10
   2154 
   2155 	DATA32	call	EXT_C(real_to_prot)
   2156 	.code32
   2157 
   2158 	pop	%ebx
   2159 	pop	%ebp
   2160 	ret
   2161 
   2162 
   2163 /*
   2164  * int console_setcursor (int on)
   2165  * BIOS call "INT 10H Function 01h" to set cursor type
   2166  *      Call with       %ah = 0x01
   2167  *                      %ch = cursor starting scanline
   2168  *                      %cl = cursor ending scanline
   2169  */
   2170 
   2171 console_cursor_state:
   2172 	.byte	1
   2173 console_cursor_shape:
   2174 	.word	0
   2175 
   2176 ENTRY(console_setcursor)
   2177 	push	%ebp
   2178 	push	%ebx
   2179 
   2180 	/* check if the standard cursor shape has already been saved */
   2181 	movw	console_cursor_shape, %ax
   2182 	testw	%ax, %ax
   2183 	jne	1f
   2184 
   2185 	call	EXT_C(prot_to_real)
   2186 	.code16
   2187 
   2188 	movb	$0x03, %ah
   2189 	xorb	%bh, %bh
   2190 	int	$0x10
   2191 
   2192 	DATA32	call	EXT_C(real_to_prot)
   2193 	.code32
   2194 
   2195 	movw	%cx, console_cursor_shape
   2196 1:
   2197 	/* set %cx to the designated cursor shape */
   2198 	movw	$0x2000, %cx
   2199 	movl	0xc(%esp), %ebx
   2200 	testl	%ebx, %ebx
   2201 	jz	2f
   2202 	movw	console_cursor_shape, %cx
   2203 2:
   2204 	call	EXT_C(prot_to_real)
   2205 	.code16
   2206 
   2207 	movb    $0x1, %ah
   2208 	int     $0x10
   2209 
   2210 	DATA32	call	EXT_C(real_to_prot)
   2211 	.code32
   2212 
   2213 	movzbl	console_cursor_state, %eax
   2214 	movb	%bl, console_cursor_state
   2215 
   2216 	pop	%ebx
   2217 	pop	%ebp
   2218 	ret
   2219 
   2220 /*
   2221  * getrtsecs()
   2222  *	if a seconds value can be read, read it and return it (BCD),
   2223  *      otherwise return 0xFF
   2224  * BIOS call "INT 1AH Function 02H" to check whether a character is pending
   2225  *	Call with	%ah = 0x2
   2226  *	Return:
   2227  *		If RT Clock can give correct values
   2228  *			%ch = hour (BCD)
   2229  *			%cl = minutes (BCD)
   2230  *                      %dh = seconds (BCD)
   2231  *                      %dl = daylight savings time (00h std, 01h daylight)
   2232  *			Carry flag = clear
   2233  *		else
   2234  *			Carry flag = set
   2235  *                         (this indicates that the clock is updating, or
   2236  *                          that it isn't running)
   2237  */
   2238 ENTRY(getrtsecs)
   2239 	push	%ebp
   2240 
   2241 	call	EXT_C(prot_to_real)	/* enter real mode */
   2242 	.code16
   2243 
   2244 	movb	$0x2, %ah
   2245 	int	$0x1a
   2246 
   2247 	DATA32	jnc	gottime
   2248 	movb	$0xff, %dh
   2249 
   2250 gottime:
   2251 	DATA32	call	EXT_C(real_to_prot)
   2252 	.code32
   2253 
   2254 	movb	%dh, %al
   2255 
   2256 	pop	%ebp
   2257 	ret
   2258 
   2259 
   2260 /*
   2261  * currticks()
   2262  *	return the real time in ticks, of which there are about
   2263  *	18-20 per second
   2264  */
   2265 ENTRY(currticks)
   2266 	pushl	%ebp
   2267 
   2268 	call	EXT_C(prot_to_real)	/* enter real mode */
   2269 	.code16
   2270 
   2271 	/* %ax is already zero */
   2272         int	$0x1a
   2273 
   2274 	DATA32	call	EXT_C(real_to_prot)
   2275 	.code32
   2276 
   2277 	movl	%ecx, %eax
   2278 	shll	$16, %eax
   2279 	movw	%dx, %ax
   2280 
   2281 	popl	%ebp
   2282 	ret
   2283 
   2284 #endif /* STAGE1_5 */
   2285 
   2286 /*
   2287  *  This is the area for all of the special variables.
   2288  */
   2289 
   2290 	.p2align	2	/* force 4-byte alignment */
   2291 
   2292 protstack:
   2293 	.long	PROTSTACKINIT
   2294 
   2295 VARIABLE(boot_drive)
   2296 #ifdef SUPPORT_DISKLESS
   2297 	.long	NETWORK_DRIVE
   2298 #else
   2299 	.long	0
   2300 #endif
   2301 
   2302 VARIABLE(install_second_sector)
   2303 	.long	0
   2304 
   2305 	/* an address can only be long-jumped to if it is in memory, this
   2306 	   is used by multiple routines */
   2307 offset:
   2308 	.long	0x8000
   2309 segment:
   2310 	.word	0
   2311 
   2312 VARIABLE(apm_bios_info)
   2313 	.word	0	/* version */
   2314 	.word	0	/* cseg */
   2315 	.long	0	/* offset */
   2316 	.word	0	/* cseg_16 */
   2317 	.word	0	/* dseg_16 */
   2318 	.word	0	/* cseg_len */
   2319 	.word	0	/* cseg_16_len */
   2320 	.word	0	/* dseg_16_len */
   2321 
   2322 /*
   2323  * This is the Global Descriptor Table
   2324  *
   2325  *  An entry, a "Segment Descriptor", looks like this:
   2326  *
   2327  * 31          24         19   16                 7           0
   2328  * ------------------------------------------------------------
   2329  * |             | |B| |A|       | |   |1|0|E|W|A|            |
   2330  * | BASE 31..24 |G|/|0|V| LIMIT |P|DPL|  TYPE   | BASE 23:16 |
   2331  * |             | |D| |L| 19..16| |   |1|1|C|R|A|            |
   2332  * ------------------------------------------------------------
   2333  * |                             |                            |
   2334  * |        BASE 15..0           |       LIMIT 15..0          |
   2335  * |                             |                            |
   2336  * ------------------------------------------------------------
   2337  *
   2338  *  Note the ordering of the data items is reversed from the above
   2339  *  description.
   2340  */
   2341 
   2342 	.p2align	2	/* force 4-byte alignment */
   2343 gdt:
   2344 	.word	0, 0
   2345 	.byte	0, 0, 0, 0
   2346 
   2347 	/* code segment */
   2348 	.word	0xFFFF, 0
   2349 	.byte	0, 0x9A, 0xCF, 0
   2350 
   2351 	/* data segment */
   2352 	.word	0xFFFF, 0
   2353 	.byte	0, 0x92, 0xCF, 0
   2354 
   2355 	/* 16 bit real mode CS */
   2356 	.word	0xFFFF, 0
   2357 	.byte	0, 0x9E, 0, 0
   2358 
   2359 	/* 16 bit real mode DS */
   2360 	.word	0xFFFF, 0
   2361 	.byte	0, 0x92, 0, 0
   2362 
   2363 
   2364 /* this is the GDT descriptor */
   2365 gdtdesc:
   2366 	.word	0x27			/* limit */
   2367 	.long	gdt			/* addr */
   2368