1/* 2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of the 7 * License, or any later version. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 * 18 */ 19 20FILE_LICENCE ( GPL2_OR_LATER ) 21 22 .arch i386 23 24/** 25 * High memory temporary load address 26 * 27 * Temporary buffer into which to copy (or decompress) our runtime 28 * image, prior to calling get_memmap() and relocate(). We don't 29 * actually leave anything here once install() has returned. 30 * 31 * We use the start of an even megabyte so that we don't have to worry 32 * about the current state of the A20 line. 33 * 34 * We use 4MB rather than 2MB because some PXE stack / PMM BIOS 35 * combinations are known to place data required by other UNDI ROMs 36 * loader around the 2MB mark. 37 */ 38 .globl HIGHMEM_LOADPOINT 39 .equ HIGHMEM_LOADPOINT, ( 4 << 20 ) 40 41/* Image compression enabled */ 42#define COMPRESS 1 43 44#define CR0_PE 1 45 46/***************************************************************************** 47 * Utility function: print character (with LF -> LF,CR translation) 48 * 49 * Parameters: 50 * %al : character to print 51 * %ds:di : output buffer (or %di=0 to print to console) 52 * Returns: 53 * %ds:di : next character in output buffer (if applicable) 54 ***************************************************************************** 55 */ 56 .section ".prefix.lib", "awx", @progbits 57 .code16 58 .globl print_character 59print_character: 60 /* Preserve registers */ 61 pushw %ax 62 pushw %bx 63 pushw %bp 64 /* If %di is non-zero, write character to buffer and exit */ 65 testw %di, %di 66 jz 1f 67 movb %al, %ds:(%di) 68 incw %di 69 jmp 3f 701: /* Print character */ 71 movw $0x0007, %bx /* page 0, attribute 7 (normal) */ 72 movb $0x0e, %ah /* write char, tty mode */ 73 cmpb $0x0a, %al /* '\n'? */ 74 jne 2f 75 int $0x10 76 movb $0x0d, %al 772: int $0x10 78 /* Restore registers and return */ 793: popw %bp 80 popw %bx 81 popw %ax 82 ret 83 .size print_character, . - print_character 84 85/***************************************************************************** 86 * Utility function: print a NUL-terminated string 87 * 88 * Parameters: 89 * %ds:si : string to print 90 * %ds:di : output buffer (or %di=0 to print to console) 91 * Returns: 92 * %ds:si : character after terminating NUL 93 * %ds:di : next character in output buffer (if applicable) 94 ***************************************************************************** 95 */ 96 .section ".prefix.lib", "awx", @progbits 97 .code16 98 .globl print_message 99print_message: 100 /* Preserve registers */ 101 pushw %ax 102 /* Print string */ 1031: lodsb 104 testb %al, %al 105 je 2f 106 call print_character 107 jmp 1b 1082: /* Restore registers and return */ 109 popw %ax 110 ret 111 .size print_message, . - print_message 112 113/***************************************************************************** 114 * Utility functions: print hex digit/byte/word/dword 115 * 116 * Parameters: 117 * %al (low nibble) : digit to print 118 * %al : byte to print 119 * %ax : word to print 120 * %eax : dword to print 121 * %ds:di : output buffer (or %di=0 to print to console) 122 * Returns: 123 * %ds:di : next character in output buffer (if applicable) 124 ***************************************************************************** 125 */ 126 .section ".prefix.lib", "awx", @progbits 127 .code16 128 .globl print_hex_dword 129print_hex_dword: 130 rorl $16, %eax 131 call print_hex_word 132 rorl $16, %eax 133 /* Fall through */ 134 .size print_hex_dword, . - print_hex_dword 135 .globl print_hex_word 136print_hex_word: 137 xchgb %al, %ah 138 call print_hex_byte 139 xchgb %al, %ah 140 /* Fall through */ 141 .size print_hex_word, . - print_hex_word 142 .globl print_hex_byte 143print_hex_byte: 144 rorb $4, %al 145 call print_hex_nibble 146 rorb $4, %al 147 /* Fall through */ 148 .size print_hex_byte, . - print_hex_byte 149 .globl print_hex_nibble 150print_hex_nibble: 151 /* Preserve registers */ 152 pushw %ax 153 /* Print digit (technique by Norbert Juffa <norbert.juffa@amd.com> */ 154 andb $0x0f, %al 155 cmpb $10, %al 156 sbbb $0x69, %al 157 das 158 call print_character 159 /* Restore registers and return */ 160 popw %ax 161 ret 162 .size print_hex_nibble, . - print_hex_nibble 163 164/***************************************************************************** 165 * Utility function: print PCI bus:dev.fn 166 * 167 * Parameters: 168 * %ax : PCI bus:dev.fn to print 169 * %ds:di : output buffer (or %di=0 to print to console) 170 * Returns: 171 * %ds:di : next character in output buffer (if applicable) 172 ***************************************************************************** 173 */ 174 .section ".prefix.lib", "awx", @progbits 175 .code16 176 .globl print_pci_busdevfn 177print_pci_busdevfn: 178 /* Preserve registers */ 179 pushw %ax 180 /* Print bus */ 181 xchgb %al, %ah 182 call print_hex_byte 183 /* Print ":" */ 184 movb $( ':' ), %al 185 call print_character 186 /* Print device */ 187 movb %ah, %al 188 shrb $3, %al 189 call print_hex_byte 190 /* Print "." */ 191 movb $( '.' ), %al 192 call print_character 193 /* Print function */ 194 movb %ah, %al 195 andb $0x07, %al 196 call print_hex_nibble 197 /* Restore registers and return */ 198 popw %ax 199 ret 200 .size print_pci_busdevfn, . - print_pci_busdevfn 201 202/***************************************************************************** 203 * Utility function: clear current line 204 * 205 * Parameters: 206 * %ds:di : output buffer (or %di=0 to print to console) 207 * Returns: 208 * %ds:di : next character in output buffer (if applicable) 209 ***************************************************************************** 210 */ 211 .section ".prefix.lib", "awx", @progbits 212 .code16 213 .globl print_kill_line 214print_kill_line: 215 /* Preserve registers */ 216 pushw %ax 217 pushw %cx 218 /* Print CR */ 219 movb $( '\r' ), %al 220 call print_character 221 /* Print 79 spaces */ 222 movb $( ' ' ), %al 223 movw $79, %cx 2241: call print_character 225 loop 1b 226 /* Print CR */ 227 movb $( '\r' ), %al 228 call print_character 229 /* Restore registers and return */ 230 popw %cx 231 popw %ax 232 ret 233 .size print_kill_line, . - print_kill_line 234 235/**************************************************************************** 236 * pm_call (real-mode near call) 237 * 238 * Call routine in 16-bit protected mode for access to extended memory 239 * 240 * Parameters: 241 * %ax : address of routine to call in 16-bit protected mode 242 * Returns: 243 * none 244 * Corrupts: 245 * %ax 246 * 247 * The specified routine is called in 16-bit protected mode, with: 248 * 249 * %cs : 16-bit code segment with base matching real-mode %cs 250 * %ss : 16-bit data segment with base matching real-mode %ss 251 * %ds,%es,%fs,%gs : 32-bit data segment with zero base and 4GB limit 252 * 253 **************************************************************************** 254 */ 255 256#ifndef KEEP_IT_REAL 257 258 /* GDT for protected-mode calls */ 259 .section ".prefix.lib", "awx", @progbits 260 .align 16 261pm_call_vars: 262gdt: 263gdt_limit: .word gdt_length - 1 264gdt_base: .long 0 265 .word 0 /* padding */ 266pm_cs: /* 16-bit protected-mode code segment */ 267 .equ PM_CS, pm_cs - gdt 268 .word 0xffff, 0 269 .byte 0, 0x9b, 0x00, 0 270pm_ss: /* 16-bit protected-mode stack segment */ 271 .equ PM_SS, pm_ss - gdt 272 .word 0xffff, 0 273 .byte 0, 0x93, 0x00, 0 274pm_ds: /* 32-bit protected-mode flat data segment */ 275 .equ PM_DS, pm_ds - gdt 276 .word 0xffff, 0 277 .byte 0, 0x93, 0xcf, 0 278gdt_end: 279 .equ gdt_length, . - gdt 280 .size gdt, . - gdt 281 282 .section ".prefix.lib", "awx", @progbits 283 .align 16 284pm_saved_gdt: 285 .long 0, 0 286 .size pm_saved_gdt, . - pm_saved_gdt 287 288 .equ pm_call_vars_size, . - pm_call_vars 289#define PM_CALL_VAR(x) ( -pm_call_vars_size + ( (x) - pm_call_vars ) ) 290 291 .section ".prefix.lib", "awx", @progbits 292 .code16 293pm_call: 294 /* Preserve registers, flags, and RM return point */ 295 pushw %bp 296 movw %sp, %bp 297 subw $pm_call_vars_size, %sp 298 andw $0xfff0, %sp 299 pushfl 300 pushw %gs 301 pushw %fs 302 pushw %es 303 pushw %ds 304 pushw %ss 305 pushw %cs 306 pushw $99f 307 308 /* Set up local variable block, and preserve GDT */ 309 pushw %cx 310 pushw %si 311 pushw %di 312 pushw %ss 313 popw %es 314 movw $pm_call_vars, %si 315 leaw PM_CALL_VAR(pm_call_vars)(%bp), %di 316 movw $pm_call_vars_size, %cx 317 cs rep movsb 318 popw %di 319 popw %si 320 popw %cx 321 sgdt PM_CALL_VAR(pm_saved_gdt)(%bp) 322 323 /* Set up GDT bases */ 324 pushl %eax 325 pushl %edi 326 xorl %eax, %eax 327 movw %ss, %ax 328 shll $4, %eax 329 movzwl %bp, %edi 330 addr32 leal PM_CALL_VAR(gdt)(%eax, %edi), %eax 331 movl %eax, PM_CALL_VAR(gdt_base)(%bp) 332 movw %cs, %ax 333 movw $PM_CALL_VAR(pm_cs), %di 334 call set_seg_base 335 movw %ss, %ax 336 movw $PM_CALL_VAR(pm_ss), %di 337 call set_seg_base 338 popl %edi 339 popl %eax 340 341 /* Switch CPU to protected mode and load up segment registers */ 342 pushl %eax 343 cli 344 data32 lgdt PM_CALL_VAR(gdt)(%bp) 345 movl %cr0, %eax 346 orb $CR0_PE, %al 347 movl %eax, %cr0 348 ljmp $PM_CS, $1f 3491: movw $PM_SS, %ax 350 movw %ax, %ss 351 movw $PM_DS, %ax 352 movw %ax, %ds 353 movw %ax, %es 354 movw %ax, %fs 355 movw %ax, %gs 356 popl %eax 357 358 /* Call PM routine */ 359 call *%ax 360 361 /* Set real-mode segment limits on %ds, %es, %fs and %gs */ 362 movw %ss, %ax 363 movw %ax, %ds 364 movw %ax, %es 365 movw %ax, %fs 366 movw %ax, %gs 367 368 /* Return CPU to real mode */ 369 movl %cr0, %eax 370 andb $0!CR0_PE, %al 371 movl %eax, %cr0 372 373 /* Restore registers and flags */ 374 lret /* will ljmp to 99f */ 37599: popw %ss 376 popw %ds 377 popw %es 378 popw %fs 379 popw %gs 380 data32 lgdt PM_CALL_VAR(pm_saved_gdt)(%bp) 381 popfl 382 movw %bp, %sp 383 popw %bp 384 ret 385 .size pm_call, . - pm_call 386 387set_seg_base: 388 rolw $4, %ax 389 movw %ax, 2(%bp,%di) 390 andw $0xfff0, 2(%bp,%di) 391 movb %al, 4(%bp,%di) 392 andb $0x0f, 4(%bp,%di) 393 ret 394 .size set_seg_base, . - set_seg_base 395 396#endif /* KEEP_IT_REAL */ 397 398/**************************************************************************** 399 * copy_bytes (real-mode or 16-bit protected-mode near call) 400 * 401 * Copy bytes 402 * 403 * Parameters: 404 * %ds:esi : source address 405 * %es:edi : destination address 406 * %ecx : length 407 * Returns: 408 * %ds:esi : next source address 409 * %es:edi : next destination address 410 * Corrupts: 411 * None 412 **************************************************************************** 413 */ 414 .section ".prefix.lib", "awx", @progbits 415 .code16 416copy_bytes: 417 pushl %ecx 418 rep addr32 movsb 419 popl %ecx 420 ret 421 .size copy_bytes, . - copy_bytes 422 423/**************************************************************************** 424 * install_block (real-mode near call) 425 * 426 * Install block to specified address 427 * 428 * Parameters: 429 * %esi : source physical address (must be a multiple of 16) 430 * %edi : destination physical address (must be a multiple of 16) 431 * %ecx : length of (decompressed) data 432 * %edx : total length of block (including any uninitialised data portion) 433 * Returns: 434 * %esi : next source physical address (will be a multiple of 16) 435 * Corrupts: 436 * none 437 **************************************************************************** 438 */ 439 .section ".prefix.lib", "awx", @progbits 440 .code16 441install_block: 442 443#ifdef KEEP_IT_REAL 444 445 /* Preserve registers */ 446 pushw %ds 447 pushw %es 448 pushl %ecx 449 pushl %edi 450 451 /* Convert %esi and %edi to segment registers */ 452 shrl $4, %esi 453 movw %si, %ds 454 xorw %si, %si 455 shrl $4, %edi 456 movw %di, %es 457 xorw %di, %di 458 459#else /* KEEP_IT_REAL */ 460 461 /* Call self in protected mode */ 462 pushw %ax 463 movw $1f, %ax 464 call pm_call 465 popw %ax 466 ret 4671: 468 /* Preserve registers */ 469 pushl %ecx 470 pushl %edi 471 472#endif /* KEEP_IT_REAL */ 473 474 475#if COMPRESS 476 /* Decompress source to destination */ 477 call decompress16 478#else 479 /* Copy source to destination */ 480 call copy_bytes 481#endif 482 483 /* Zero .bss portion */ 484 negl %ecx 485 addl %edx, %ecx 486 pushw %ax 487 xorw %ax, %ax 488 rep addr32 stosb 489 popw %ax 490 491 /* Round up %esi to start of next source block */ 492 addl $0xf, %esi 493 andl $~0xf, %esi 494 495 496#ifdef KEEP_IT_REAL 497 498 /* Convert %ds:esi back to a physical address */ 499 movzwl %ds, %cx 500 shll $4, %ecx 501 addl %ecx, %esi 502 503 /* Restore registers */ 504 popl %edi 505 popl %ecx 506 popw %es 507 popw %ds 508 509#else /* KEEP_IT_REAL */ 510 511 /* Restore registers */ 512 popl %edi 513 popl %ecx 514 515#endif 516 517 ret 518 .size install_block, . - install_block 519 520/**************************************************************************** 521 * alloc_basemem (real-mode near call) 522 * 523 * Allocate space for .text16 and .data16 from top of base memory. 524 * Memory is allocated using the BIOS free base memory counter at 525 * 0x40:13. 526 * 527 * Parameters: 528 * none 529 * Returns: 530 * %ax : .text16 segment address 531 * %bx : .data16 segment address 532 * Corrupts: 533 * none 534 **************************************************************************** 535 */ 536 .section ".prefix.lib", "awx", @progbits 537 .code16 538 .globl alloc_basemem 539alloc_basemem: 540 /* Preserve registers */ 541 pushw %fs 542 543 /* FBMS => %ax as segment address */ 544 pushw $0x40 545 popw %fs 546 movw %fs:0x13, %ax 547 shlw $6, %ax 548 549 /* Calculate .data16 segment address */ 550 subw $_data16_memsz_pgh, %ax 551 pushw %ax 552 553 /* Calculate .text16 segment address */ 554 subw $_text16_memsz_pgh, %ax 555 pushw %ax 556 557 /* Update FBMS */ 558 shrw $6, %ax 559 movw %ax, %fs:0x13 560 561 /* Retrieve .text16 and .data16 segment addresses */ 562 popw %ax 563 popw %bx 564 565 /* Restore registers and return */ 566 popw %fs 567 ret 568 .size alloc_basemem, . - alloc_basemem 569 570/**************************************************************************** 571 * free_basemem (real-mode near call) 572 * 573 * Free space allocated with alloc_basemem. 574 * 575 * Parameters: 576 * %ax : .text16 segment address 577 * %bx : .data16 segment address 578 * Returns: 579 * %ax : 0 if successfully freed 580 * Corrupts: 581 * none 582 **************************************************************************** 583 */ 584 .section ".text16", "ax", @progbits 585 .code16 586 .globl free_basemem 587free_basemem: 588 /* Preserve registers */ 589 pushw %fs 590 591 /* Check FBMS counter */ 592 pushw %ax 593 shrw $6, %ax 594 pushw $0x40 595 popw %fs 596 cmpw %ax, %fs:0x13 597 popw %ax 598 jne 1f 599 600 /* Check hooked interrupt count */ 601 cmpw $0, %cs:hooked_bios_interrupts 602 jne 1f 603 604 /* OK to free memory */ 605 addw $_text16_memsz_pgh, %ax 606 addw $_data16_memsz_pgh, %ax 607 shrw $6, %ax 608 movw %ax, %fs:0x13 609 xorw %ax, %ax 610 6111: /* Restore registers and return */ 612 popw %fs 613 ret 614 .size free_basemem, . - free_basemem 615 616 .section ".text16.data", "aw", @progbits 617 .globl hooked_bios_interrupts 618hooked_bios_interrupts: 619 .word 0 620 .size hooked_bios_interrupts, . - hooked_bios_interrupts 621 622/**************************************************************************** 623 * install (real-mode near call) 624 * 625 * Install all text and data segments. 626 * 627 * Parameters: 628 * none 629 * Returns: 630 * %ax : .text16 segment address 631 * %bx : .data16 segment address 632 * Corrupts: 633 * none 634 **************************************************************************** 635 */ 636 .section ".prefix.lib", "awx", @progbits 637 .code16 638 .globl install 639install: 640 /* Preserve registers */ 641 pushl %esi 642 pushl %edi 643 /* Allocate space for .text16 and .data16 */ 644 call alloc_basemem 645 /* Image source = %cs:0000 */ 646 xorl %esi, %esi 647 /* Image destination = HIGHMEM_LOADPOINT */ 648 movl $HIGHMEM_LOADPOINT, %edi 649 /* Install text and data segments */ 650 call install_prealloc 651 /* Restore registers and return */ 652 popl %edi 653 popl %esi 654 ret 655 .size install, . - install 656 657/**************************************************************************** 658 * install_prealloc (real-mode near call) 659 * 660 * Install all text and data segments. 661 * 662 * Parameters: 663 * %ax : .text16 segment address 664 * %bx : .data16 segment address 665 * %esi : Image source physical address (or zero for %cs:0000) 666 * %edi : Decompression temporary area physical address 667 * Corrupts: 668 * none 669 **************************************************************************** 670 */ 671 .section ".prefix.lib", "awx", @progbits 672 .code16 673 .globl install_prealloc 674install_prealloc: 675 /* Save registers */ 676 pushal 677 pushw %ds 678 pushw %es 679 680 /* Sanity: clear the direction flag asap */ 681 cld 682 683 /* Calculate physical address of payload (i.e. first source) */ 684 testl %esi, %esi 685 jnz 1f 686 movw %cs, %si 687 shll $4, %esi 6881: addl $_payload_lma, %esi 689 690 /* Install .text16 and .data16 */ 691 pushl %edi 692 movzwl %ax, %edi 693 shll $4, %edi 694 movl $_text16_memsz, %ecx 695 movl %ecx, %edx 696 call install_block /* .text16 */ 697 movzwl %bx, %edi 698 shll $4, %edi 699 movl $_data16_filesz, %ecx 700 movl $_data16_memsz, %edx 701 call install_block /* .data16 */ 702 popl %edi 703 704 /* Set up %ds for access to .data16 */ 705 movw %bx, %ds 706 707#ifdef KEEP_IT_REAL 708 /* Initialise libkir */ 709 movw %ax, (init_libkir_vector+2) 710 lcall *init_libkir_vector 711#else 712 /* Install .text and .data to temporary area in high memory, 713 * prior to reading the E820 memory map and relocating 714 * properly. 715 */ 716 movl $_textdata_filesz, %ecx 717 movl $_textdata_memsz, %edx 718 call install_block 719 720 /* Initialise librm at current location */ 721 movw %ax, (init_librm_vector+2) 722 lcall *init_librm_vector 723 724 /* Call relocate() to determine target address for relocation. 725 * relocate() will return with %esi, %edi and %ecx set up 726 * ready for the copy to the new location. 727 */ 728 movw %ax, (prot_call_vector+2) 729 pushl $relocate 730 lcall *prot_call_vector 731 popl %edx /* discard */ 732 733 /* Copy code to new location */ 734 pushl %edi 735 pushw %ax 736 movw $copy_bytes, %ax 737 call pm_call 738 popw %ax 739 popl %edi 740 741 /* Initialise librm at new location */ 742 lcall *init_librm_vector 743 744#endif 745 /* Restore registers */ 746 popw %es 747 popw %ds 748 popal 749 ret 750 .size install_prealloc, . - install_prealloc 751 752 /* Vectors for far calls to .text16 functions */ 753 .section ".data16", "aw", @progbits 754#ifdef KEEP_IT_REAL 755init_libkir_vector: 756 .word init_libkir 757 .word 0 758 .size init_libkir_vector, . - init_libkir_vector 759#else 760init_librm_vector: 761 .word init_librm 762 .word 0 763 .size init_librm_vector, . - init_librm_vector 764prot_call_vector: 765 .word prot_call 766 .word 0 767 .size prot_call_vector, . - prot_call_vector 768#endif 769 770/**************************************************************************** 771 * uninstall (real-mode near call) 772 * 773 * Uninstall all text and data segments. 774 * 775 * Parameters: 776 * %ax : .text16 segment address 777 * %bx : .data16 segment address 778 * Returns: 779 * none 780 * Corrupts: 781 * none 782 **************************************************************************** 783 */ 784 .section ".text16", "ax", @progbits 785 .code16 786 .globl uninstall 787uninstall: 788 call free_basemem 789 ret 790 .size uninstall, . - uninstall 791 792 793 794 /* File split information for the compressor */ 795#if COMPRESS 796 .section ".zinfo", "a", @progbits 797 .ascii "COPY" 798 .long _prefix_lma 799 .long _prefix_filesz 800 .long _max_align 801 .ascii "PACK" 802 .long _text16_lma 803 .long _text16_filesz 804 .long _max_align 805 .ascii "PACK" 806 .long _data16_lma 807 .long _data16_filesz 808 .long _max_align 809 .ascii "PACK" 810 .long _textdata_lma 811 .long _textdata_filesz 812 .long _max_align 813#else /* COMPRESS */ 814 .section ".zinfo", "a", @progbits 815 .ascii "COPY" 816 .long _prefix_lma 817 .long _filesz 818 .long _max_align 819#endif /* COMPRESS */ 820