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