1/* At entry, the processor is in 16 bit real mode and the code is being 2 * executed from an address it was not linked to. Code must be pic and 3 * 32 bit sensitive until things are fixed up. 4 * 5 * Also be very careful as the stack is at the rear end of the interrupt 6 * table so using a noticeable amount of stack space is a no-no. 7 */ 8 9FILE_LICENCE ( GPL2_OR_LATER ) 10 11#include <config/general.h> 12 13#define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) ) 14#define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) ) 15#define PCI_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( ' ' << 24 ) ) 16#define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) ) 17#define PNP_GET_BBS_VERSION 0x60 18#define PMM_ALLOCATE 0x0000 19#define PMM_DEALLOCATE 0x0002 20 21/* ROM banner timeout. Based on the configurable BANNER_TIMEOUT in 22 * config.h, but converted to a number of (18Hz) timer ticks, and 23 * doubled to allow for BIOSes that switch video modes immediately 24 * beforehand, so rendering the message almost invisible to the user. 25 */ 26#define ROM_BANNER_TIMEOUT ( 2 * ( 18 * BANNER_TIMEOUT ) / 10 ) 27 28/* We can load a ROM in two ways: have the BIOS load all of it (.rom prefix) 29 * or have the BIOS load a stub that loads the rest using PCI (.xrom prefix). 30 * The latter is not as widely supported, but allows the use of large ROMs 31 * on some systems with crowded option ROM space. 32 */ 33 34#ifdef LOAD_ROM_FROM_PCI 35#define ROM_SIZE_VALUE _prefix_filesz_sect /* Amount to load in BIOS */ 36#else 37#define ROM_SIZE_VALUE 0 /* Load amount (before compr. fixup) */ 38#endif 39 40 41 .text 42 .code16 43 .arch i386 44 .section ".prefix", "ax", @progbits 45 46 .org 0x00 47romheader: 48 .word 0xAA55 /* BIOS extension signature */ 49romheader_size: .byte ROM_SIZE_VALUE /* Size in 512-byte blocks */ 50 jmp init /* Initialisation vector */ 51checksum: 52 .byte 0, 0 53real_size: 54 .word 0 55 .org 0x16 56 .word undiheader 57 .org 0x18 58 .word pciheader 59 .org 0x1a 60 .word pnpheader 61 .size romheader, . - romheader 62 63 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ 64#ifndef LOAD_ROM_FROM_PCI 65 .ascii "ADDB" 66 .long romheader_size 67 .long 512 68 .long 0 69#endif 70 .ascii "ADDB" 71 .long real_size 72 .long 512 73 .long 0 74 .previous 75 76pciheader: 77 .ascii "PCIR" /* Signature */ 78 .word pci_vendor_id /* Vendor identification */ 79 .word pci_device_id /* Device identification */ 80 .word 0x0000 /* Device list pointer */ 81 .word pciheader_len /* PCI data structure length */ 82 .byte 0x03 /* PCI data structure revision */ 83 .byte 0x02, 0x00, 0x00 /* Class code */ 84pciheader_image_length: 85 .word ROM_SIZE_VALUE /* Image length */ 86 .word 0x0001 /* Revision level */ 87 .byte 0x00 /* Code type */ 88 .byte 0x80 /* Last image indicator */ 89pciheader_runtime_length: 90 .word ROM_SIZE_VALUE /* Maximum run-time image length */ 91 .word 0x0000 /* Configuration utility code header */ 92 .word 0x0000 /* DMTF CLP entry point */ 93 .equ pciheader_len, . - pciheader 94 .size pciheader, . - pciheader 95 96#ifndef LOAD_ROM_FROM_PCI 97 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ 98 .ascii "ADDW" 99 .long pciheader_image_length 100 .long 512 101 .long 0 102 .ascii "ADDW" 103 .long pciheader_runtime_length 104 .long 512 105 .long 0 106 .previous 107#endif 108 109pnpheader: 110 .ascii "$PnP" /* Signature */ 111 .byte 0x01 /* Structure revision */ 112 .byte ( pnpheader_len / 16 ) /* Length (in 16 byte increments) */ 113 .word 0x0000 /* Offset of next header */ 114 .byte 0x00 /* Reserved */ 115 .byte 0x00 /* Checksum */ 116 .long 0x00000000 /* Device identifier */ 117 .word mfgstr /* Manufacturer string */ 118 .word prodstr /* Product name */ 119 .byte 0x02 /* Device base type code */ 120 .byte 0x00 /* Device sub-type code */ 121 .byte 0x00 /* Device interface type code */ 122 .byte 0xf4 /* Device indicator */ 123 .word 0x0000 /* Boot connection vector */ 124 .word 0x0000 /* Disconnect vector */ 125 .word bev_entry /* Boot execution vector */ 126 .word 0x0000 /* Reserved */ 127 .word 0x0000 /* Static resource information vector*/ 128 .equ pnpheader_len, . - pnpheader 129 .size pnpheader, . - pnpheader 130 131/* Manufacturer string */ 132mfgstr: 133 .asciz "http://etherboot.org" 134 .size mfgstr, . - mfgstr 135 136/* Product string 137 * 138 * Defaults to PRODUCT_SHORT_NAME. If the ROM image is writable at 139 * initialisation time, it will be filled in to include the PCI 140 * bus:dev.fn number of the card as well. 141 */ 142prodstr: 143 .ascii PRODUCT_SHORT_NAME 144prodstr_separator: 145 .byte 0 146 .ascii "(PCI " 147prodstr_pci_id: 148 .asciz "xx:xx.x)" /* Filled in by init code */ 149 .size prodstr, . - prodstr 150 151 .globl undiheader 152 .weak undiloader 153undiheader: 154 .ascii "UNDI" /* Signature */ 155 .byte undiheader_len /* Length of structure */ 156 .byte 0 /* Checksum */ 157 .byte 0 /* Structure revision */ 158 .byte 0,1,2 /* PXE version: 2.1.0 */ 159 .word undiloader /* Offset to loader routine */ 160 .word _data16_memsz /* Stack segment size */ 161 .word _data16_memsz /* Data segment size */ 162 .word _text16_memsz /* Code segment size */ 163 .ascii "PCIR" /* Bus type */ 164 .equ undiheader_len, . - undiheader 165 .size undiheader, . - undiheader 166 167/* Initialisation (called once during POST) 168 * 169 * Determine whether or not this is a PnP system via a signature 170 * check. If it is PnP, return to the PnP BIOS indicating that we are 171 * a boot-capable device; the BIOS will call our boot execution vector 172 * if it wants to boot us. If it is not PnP, hook INT 19. 173 */ 174init: 175 /* Preserve registers, clear direction flag, set %ds=%cs */ 176 pushaw 177 pushw %ds 178 pushw %es 179 pushw %fs 180 pushw %gs 181 cld 182 pushw %cs 183 popw %ds 184 185 /* Shuffle some registers around. We need %di available for 186 * the print_xxx functions, and in a register that's 187 * addressable from %es, so shuffle as follows: 188 * 189 * %di (pointer to PnP structure) => %bx 190 * %bx (runtime segment address, for PCI 3.0) => %gs 191 */ 192 movw %bx, %gs 193 movw %di, %bx 194 195 /* Print message as early as possible */ 196 movw $init_message, %si 197 xorw %di, %di 198 call print_message 199 call print_pci_busdevfn 200 201#ifdef LOAD_ROM_FROM_PCI 202 /* Save PCI bus:dev.fn for later use */ 203 movw %ax, pci_busdevfn 204#endif 205 206 /* Fill in product name string, if possible */ 207 movw $prodstr_pci_id, %di 208 call print_pci_busdevfn 209 movb $( ' ' ), prodstr_separator 210 211 /* Print segment address */ 212 movb $( ' ' ), %al 213 xorw %di, %di 214 call print_character 215 movw %cs, %ax 216 call print_hex_word 217 218 /* Check for PCI BIOS version */ 219 pushl %ebx 220 pushl %edx 221 pushl %edi 222 stc 223 movw $0xb101, %ax 224 int $0x1a 225 jc no_pci3 226 cmpl $PCI_SIGNATURE, %edx 227 jne no_pci3 228 testb %ah, %ah 229 jnz no_pci3 230#ifdef LOAD_ROM_FROM_PCI 231 incb pcibios_present 232#endif 233 movw $init_message_pci, %si 234 xorw %di, %di 235 call print_message 236 movb %bh, %al 237 call print_hex_nibble 238 movb $( '.' ), %al 239 call print_character 240 movb %bl, %al 241 call print_hex_byte 242 cmpb $3, %bh 243 jb no_pci3 244 /* PCI >=3.0: leave %gs as-is if sane */ 245 movw %gs, %ax 246 cmpw $0xa000, %ax /* Insane if %gs < 0xa000 */ 247 jb pci3_insane 248 movw %cs, %bx /* Sane if %cs == %gs */ 249 cmpw %bx, %ax 250 je 1f 251 movzbw romheader_size, %cx /* Sane if %cs+len <= %gs */ 252 shlw $5, %cx 253 addw %cx, %bx 254 cmpw %bx, %ax 255 jae 1f 256 movw %cs, %bx /* Sane if %gs+len <= %cs */ 257 addw %cx, %ax 258 cmpw %bx, %ax 259 jbe 1f 260pci3_insane: /* PCI 3.0 with insane %gs value: print error and ignore %gs */ 261 movb $( '!' ), %al 262 call print_character 263 movw %gs, %ax 264 call print_hex_word 265no_pci3: 266 /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */ 267 pushw %cs 268 popw %gs 2691: popl %edi 270 popl %edx 271 popl %ebx 272 273 /* Check for PnP BIOS. Although %es:di should point to the 274 * PnP BIOS signature on entry, some BIOSes fail to do this. 275 */ 276 movw $( 0xf000 - 1 ), %bx 277pnp_scan: 278 incw %bx 279 jz no_pnp 280 movw %bx, %es 281 cmpl $PNP_SIGNATURE, %es:0 282 jne pnp_scan 283 xorw %dx, %dx 284 xorw %si, %si 285 movzbw %es:5, %cx 2861: es lodsb 287 addb %al, %dl 288 loop 1b 289 jnz pnp_scan 290 /* Is PnP: print PnP message */ 291 movw $init_message_pnp, %si 292 xorw %di, %di 293 call print_message 294 /* Check for BBS */ 295 pushw %es:0x1b /* Real-mode data segment */ 296 pushw %ds /* &(bbs_version) */ 297 pushw $bbs_version 298 pushw $PNP_GET_BBS_VERSION 299 lcall *%es:0xd 300 addw $8, %sp 301 testw %ax, %ax 302 je got_bbs 303no_pnp: /* Not PnP-compliant - therefore cannot be BBS-compliant */ 304no_bbs: /* Not BBS-compliant - must hook INT 19 */ 305 movw $init_message_int19, %si 306 xorw %di, %di 307 call print_message 308 xorw %ax, %ax 309 movw %ax, %es 310 pushl %es:( 0x19 * 4 ) 311 popl orig_int19 312 pushw %gs /* %gs contains runtime %cs */ 313 pushw $int19_entry 314 popl %es:( 0x19 * 4 ) 315 jmp bbs_done 316got_bbs: /* BBS compliant - no need to hook INT 19 */ 317 movw $init_message_bbs, %si 318 xorw %di, %di 319 call print_message 320bbs_done: 321 322 /* Check for PMM */ 323 movw $( 0xe000 - 1 ), %bx 324pmm_scan: 325 incw %bx 326 jz no_pmm 327 movw %bx, %es 328 cmpl $PMM_SIGNATURE, %es:0 329 jne pmm_scan 330 xorw %dx, %dx 331 xorw %si, %si 332 movzbw %es:5, %cx 3331: es lodsb 334 addb %al, %dl 335 loop 1b 336 jnz pmm_scan 337 /* PMM found: print PMM message */ 338 movw $init_message_pmm, %si 339 xorw %di, %di 340 call print_message 341 /* We have PMM and so a 1kB stack: preserve upper register halves */ 342 pushal 343 /* Calculate required allocation size in %esi */ 344 movzwl real_size, %eax 345 shll $9, %eax 346 addl $_textdata_memsz, %eax 347 orw $0xffff, %ax /* Ensure allocation size is at least 64kB */ 348 bsrl %eax, %ecx 349 subw $15, %cx /* Round up and convert to 64kB count */ 350 movw $1, %si 351 shlw %cl, %si 352pmm_loop: 353 /* Try to allocate block via PMM */ 354 pushw $0x0006 /* Aligned, extended memory */ 355 pushl $0xffffffff /* No handle */ 356 movzwl %si, %eax 357 shll $12, %eax 358 pushl %eax /* Allocation size in paragraphs */ 359 pushw $PMM_ALLOCATE 360 lcall *%es:7 361 addw $12, %sp 362 /* Abort if allocation fails */ 363 testw %dx, %dx /* %ax==0 even on success, since align>=64kB */ 364 jz pmm_fail 365 /* If block has A20==1, free block and try again with twice 366 * the allocation size (and hence alignment). 367 */ 368 testw $0x0010, %dx 369 jz got_pmm 370 pushw %dx 371 pushw $0 372 pushw $PMM_DEALLOCATE 373 lcall *%es:7 374 addw $6, %sp 375 addw %si, %si 376 jmp pmm_loop 377got_pmm: /* PMM allocation succeeded */ 378 movw %dx, ( image_source + 2 ) 379 movw %dx, %ax 380 xorw %di, %di 381 call print_hex_word 382 movb $( '@' ), %al 383 call print_character 384 movw %si, %ax 385 call print_hex_byte 386pmm_copy: 387 /* Copy ROM to PMM block */ 388 xorw %ax, %ax 389 movw %ax, %es 390 movl image_source, %edi 391 xorl %esi, %esi 392 movzbl romheader_size, %ecx 393 shll $9, %ecx 394 addr32 rep movsb /* PMM presence implies flat real mode */ 395 movl %edi, decompress_to 396 /* Shrink ROM */ 397 movb $_prefix_memsz_sect, romheader_size 398#if defined(SHRINK_WITHOUT_PMM) || defined(LOAD_ROM_FROM_PCI) 399 jmp pmm_done 400pmm_fail: 401 /* Print marker and copy ourselves to high memory */ 402 movl $HIGHMEM_LOADPOINT, image_source 403 xorw %di, %di 404 movb $( '!' ), %al 405 call print_character 406 jmp pmm_copy 407pmm_done: 408#else 409pmm_fail: 410#endif 411 /* Restore upper register halves */ 412 popal 413#if defined(LOAD_ROM_FROM_PCI) 414 call load_from_pci 415 jc load_err 416 jmp load_ok 417no_pmm: 418 /* Cannot continue without PMM - print error message */ 419 xorw %di, %di 420 movw $init_message_no_pmm, %si 421 call print_message 422load_err: 423 /* Wait for five seconds to let user see message */ 424 movw $90, %cx 4251: call wait_for_tick 426 loop 1b 427 /* Mark environment as invalid and return */ 428 movl $0, decompress_to 429 jmp out 430 431load_ok: 432#else 433no_pmm: 434#endif 435 /* Update checksum */ 436 xorw %bx, %bx 437 xorw %si, %si 438 movzbw romheader_size, %cx 439 shlw $9, %cx 4401: lodsb 441 addb %al, %bl 442 loop 1b 443 subb %bl, checksum 444 445 /* Copy self to option ROM space. Required for PCI3.0, which 446 * loads us to a temporary location in low memory. Will be a 447 * no-op for lower PCI versions. 448 */ 449 movb $( ' ' ), %al 450 xorw %di, %di 451 call print_character 452 movw %gs, %ax 453 call print_hex_word 454 movzbw romheader_size, %cx 455 shlw $9, %cx 456 movw %ax, %es 457 xorw %si, %si 458 xorw %di, %di 459 cs rep movsb 460 461 /* Prompt for POST-time shell */ 462 movw $init_message_prompt, %si 463 xorw %di, %di 464 call print_message 465 movw $prodstr, %si 466 call print_message 467 movw $init_message_dots, %si 468 call print_message 469 /* Wait for Ctrl-B */ 470 movw $0xff02, %bx 471 call wait_for_key 472 /* Clear prompt */ 473 pushf 474 xorw %di, %di 475 call print_kill_line 476 movw $init_message_done, %si 477 call print_message 478 popf 479 jnz out 480 /* Ctrl-B was pressed: invoke gPXE. The keypress will be 481 * picked up by the initial shell prompt, and we will drop 482 * into a shell. 483 */ 484 pushw %cs 485 call exec 486out: 487 /* Restore registers */ 488 popw %gs 489 popw %fs 490 popw %es 491 popw %ds 492 popaw 493 494 /* Indicate boot capability to PnP BIOS, if present */ 495 movw $0x20, %ax 496 lret 497 .size init, . - init 498 499/* 500 * Note to hardware vendors: 501 * 502 * If you wish to brand this boot ROM, please do so by defining the 503 * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/general.h. 504 * 505 * While nothing in the GPL prevents you from removing all references 506 * to gPXE or http://etherboot.org, we prefer you not to do so. 507 * 508 * If you have an OEM-mandated branding requirement that cannot be 509 * satisfied simply by defining PRODUCT_NAME and PRODUCT_SHORT_NAME, 510 * please contact us. 511 * 512 * [ Including an ASCII NUL in PRODUCT_NAME is considered to be 513 * bypassing the spirit of this request! ] 514 */ 515init_message: 516 .ascii "\n" 517 .ascii PRODUCT_NAME 518 .ascii "\n" 519 .asciz "gPXE (http://etherboot.org) - " 520 .size init_message, . - init_message 521init_message_pci: 522 .asciz " PCI" 523 .size init_message_pci, . - init_message_pci 524init_message_pnp: 525 .asciz " PnP" 526 .size init_message_pnp, . - init_message_pnp 527init_message_bbs: 528 .asciz " BBS" 529 .size init_message_bbs, . - init_message_bbs 530init_message_pmm: 531 .asciz " PMM" 532 .size init_message_pmm, . - init_message_pmm 533#ifdef LOAD_ROM_FROM_PCI 534init_message_no_pmm: 535 .asciz "\nPMM required but not present!\n" 536 .size init_message_no_pmm, . - init_message_no_pmm 537#endif 538init_message_int19: 539 .asciz " INT19" 540 .size init_message_int19, . - init_message_int19 541init_message_prompt: 542 .asciz "\nPress Ctrl-B to configure " 543 .size init_message_prompt, . - init_message_prompt 544init_message_dots: 545 .asciz "..." 546 .size init_message_dots, . - init_message_dots 547init_message_done: 548 .asciz "\n\n" 549 .size init_message_done, . - init_message_done 550 551/* ROM image location 552 * 553 * May be either within option ROM space, or within PMM-allocated block. 554 */ 555 .globl image_source 556image_source: 557 .long 0 558 .size image_source, . - image_source 559 560/* Temporary decompression area 561 * 562 * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block. 563 * If a PCI ROM load fails, this will be set to zero. 564 */ 565 .globl decompress_to 566decompress_to: 567 .long HIGHMEM_LOADPOINT 568 .size decompress_to, . - decompress_to 569 570#ifdef LOAD_ROM_FROM_PCI 571 572/* Set if the PCI BIOS is present, even <3.0 */ 573pcibios_present: 574 .byte 0 575 .byte 0 /* for alignment */ 576 .size pcibios_present, . - pcibios_present 577 578/* PCI bus:device.function word 579 * 580 * Filled in by init in the .xrom case, so the remainder of the ROM 581 * can be located. 582 */ 583pci_busdevfn: 584 .word 0 585 .size pci_busdevfn, . - pci_busdevfn 586 587#endif 588 589/* BBS version 590 * 591 * Filled in by BBS BIOS. We ignore the value. 592 */ 593bbs_version: 594 .word 0 595 .size bbs_version, . - bbs_version 596 597/* Boot Execution Vector entry point 598 * 599 * Called by the PnP BIOS when it wants to boot us. 600 */ 601bev_entry: 602 pushw %cs 603 call exec 604 lret 605 .size bev_entry, . - bev_entry 606 607 608#ifdef LOAD_ROM_FROM_PCI 609 610#define PCI_ROM_ADDRESS 0x30 /* Bits 31:11 address, 10:1 reserved */ 611#define PCI_ROM_ADDRESS_ENABLE 0x00000001 612#define PCI_ROM_ADDRESS_MASK 0xfffff800 613 614#define PCIBIOS_READ_WORD 0xb109 615#define PCIBIOS_READ_DWORD 0xb10a 616#define PCIBIOS_WRITE_WORD 0xb10c 617#define PCIBIOS_WRITE_DWORD 0xb10d 618 619/* Determine size of PCI BAR 620 * 621 * %bx : PCI bus:dev.fn to probe 622 * %di : Address of BAR to find size of 623 * %edx : Mask of address bits within BAR 624 * 625 * %ecx : Size for a memory resource, 626 * 1 for an I/O resource (bit 0 set). 627 * CF : Set on error or nonexistent device (all-ones read) 628 * 629 * All other registers saved. 630 */ 631pci_bar_size: 632 /* Save registers */ 633 pushw %ax 634 pushl %esi 635 pushl %edx 636 637 /* Read current BAR value */ 638 movw $PCIBIOS_READ_DWORD, %ax 639 int $0x1a 640 641 /* Check for device existence and save it */ 642 testb $1, %cl /* I/O bit? */ 643 jz 1f 644 andl $1, %ecx /* If so, exit with %ecx = 1 */ 645 jmp 99f 6461: notl %ecx 647 testl %ecx, %ecx /* Set ZF iff %ecx was all-ones */ 648 notl %ecx 649 jnz 1f 650 stc /* All ones - exit with CF set */ 651 jmp 99f 6521: movl %ecx, %esi /* Save in %esi */ 653 654 /* Write all ones to BAR */ 655 movl %edx, %ecx 656 movw $PCIBIOS_WRITE_DWORD, %ax 657 int $0x1a 658 659 /* Read back BAR */ 660 movw $PCIBIOS_READ_DWORD, %ax 661 int $0x1a 662 663 /* Find decode size from least set bit in mask BAR */ 664 bsfl %ecx, %ecx /* Find least set bit, log2(decode size) */ 665 jz 1f /* Mask BAR should not be zero */ 666 xorl %edx, %edx 667 incl %edx 668 shll %cl, %edx /* %edx = decode size */ 669 jmp 2f 6701: xorl %edx, %edx /* Return zero size for mask BAR zero */ 671 672 /* Restore old BAR value */ 6732: movl %esi, %ecx 674 movw $PCIBIOS_WRITE_DWORD, %ax 675 int $0x1a 676 677 movl %edx, %ecx /* Return size in %ecx */ 678 679 /* Restore registers and return */ 68099: popl %edx 681 popl %esi 682 popw %ax 683 ret 684 685 .size pci_bar_size, . - pci_bar_size 686 687/* PCI ROM loader 688 * 689 * Called from init in the .xrom case to load the non-prefix code 690 * using the PCI ROM BAR. 691 * 692 * Returns with carry flag set on error. All registers saved. 693 */ 694load_from_pci: 695 /* 696 * Use PCI BIOS access to config space. The calls take 697 * 698 * %ah : 0xb1 %al : function 699 * %bx : bus/dev/fn 700 * %di : config space address 701 * %ecx : value to write (for writes) 702 * 703 * %ecx : value read (for reads) 704 * %ah : return code 705 * CF : error indication 706 * 707 * All registers not used for return are preserved. 708 */ 709 710 /* Save registers and set up %es for big real mode */ 711 pushal 712 pushw %es 713 xorw %ax, %ax 714 movw %ax, %es 715 716 /* Check PCI BIOS presence */ 717 cmpb $0, pcibios_present 718 jz err_pcibios 719 720 /* Load existing PCI ROM BAR */ 721 movw $PCIBIOS_READ_DWORD, %ax 722 movw pci_busdevfn, %bx 723 movw $PCI_ROM_ADDRESS, %di 724 int $0x1a 725 726 /* Maybe it's already enabled? */ 727 testb $PCI_ROM_ADDRESS_ENABLE, %cl 728 jz 1f 729 movb $1, %dl /* Flag indicating no deinit required */ 730 movl %ecx, %ebp 731 jmp check_rom 732 733 /* Determine PCI BAR decode size */ 7341: movl $PCI_ROM_ADDRESS_MASK, %edx 735 call pci_bar_size /* Returns decode size in %ecx */ 736 jc err_size_insane /* CF => no ROM BAR, %ecx == ffffffff */ 737 738 /* Check sanity of decode size */ 739 xorl %eax, %eax 740 movw real_size, %ax 741 shll $9, %eax /* %eax = ROM size */ 742 cmpl %ecx, %eax 743 ja err_size_insane /* Insane if decode size < ROM size */ 744 cmpl $0x100000, %ecx 745 jae err_size_insane /* Insane if decode size >= 1MB */ 746 747 /* Find a place to map the BAR 748 * In theory we should examine e820 and all PCI BARs to find a 749 * free region. However, we run at POST when e820 may not be 750 * available, and memory reads of an unmapped location are 751 * de facto standardized to return all-ones. Thus, we can get 752 * away with searching high memory (0xf0000000 and up) on 753 * multiples of the ROM BAR decode size for a sufficiently 754 * large all-ones region. 755 */ 756 movl %ecx, %edx /* Save ROM BAR size in %edx */ 757 movl $0xf0000000, %ebp 758 xorl %eax, %eax 759 notl %eax /* %eax = all ones */ 760bar_search: 761 movl %ebp, %edi 762 movl %edx, %ecx 763 shrl $2, %ecx 764 addr32 repe scasl /* Scan %es:edi for anything not all-ones */ 765 jz bar_found 766 addl %edx, %ebp 767 testl $0x80000000, %ebp 768 jz err_no_bar 769 jmp bar_search 770 771bar_found: 772 movl %edi, %ebp 773 /* Save current BAR value on stack to restore later */ 774 movw $PCIBIOS_READ_DWORD, %ax 775 movw $PCI_ROM_ADDRESS, %di 776 int $0x1a 777 pushl %ecx 778 779 /* Map the ROM */ 780 movw $PCIBIOS_WRITE_DWORD, %ax 781 movl %ebp, %ecx 782 orb $PCI_ROM_ADDRESS_ENABLE, %cl 783 int $0x1a 784 785 xorb %dl, %dl /* %dl = 0 : ROM was not already mapped */ 786check_rom: 787 /* Check and copy ROM - enter with %dl set to skip unmapping, 788 * %ebp set to mapped ROM BAR address. 789 * We check up to prodstr_separator for equality, since anything past 790 * that may have been modified. Since our check includes the checksum 791 * byte over the whole ROM stub, that should be sufficient. 792 */ 793 xorb %dh, %dh /* %dh = 0 : ROM did not fail integrity check */ 794 795 /* Verify ROM integrity */ 796 xorl %esi, %esi 797 movl %ebp, %edi 798 movl $prodstr_separator, %ecx 799 addr32 repe cmpsb 800 jz copy_rom 801 incb %dh /* ROM failed integrity check */ 802 movl %ecx, %ebp /* Save number of bytes left */ 803 jmp skip_load 804 805copy_rom: 806 /* Print BAR address and indicate whether we mapped it ourselves */ 807 movb $( ' ' ), %al 808 xorw %di, %di 809 call print_character 810 movl %ebp, %eax 811 call print_hex_dword 812 movb $( '-' ), %al /* '-' for self-mapped */ 813 subb %dl, %al 814 subb %dl, %al /* '+' = '-' - 2 for BIOS-mapped */ 815 call print_character 816 817 /* Copy ROM at %ebp to PMM or highmem block */ 818 movl %ebp, %esi 819 movl image_source, %edi 820 movzwl real_size, %ecx 821 shll $9, %ecx 822 addr32 es rep movsb 823 movl %edi, decompress_to 824skip_load: 825 testb %dl, %dl /* Was ROM already mapped? */ 826 jnz skip_unmap 827 828 /* Unmap the ROM by restoring old ROM BAR */ 829 movw $PCIBIOS_WRITE_DWORD, %ax 830 movw $PCI_ROM_ADDRESS, %di 831 popl %ecx 832 int $0x1a 833 834skip_unmap: 835 /* Error handling */ 836 testb %dh, %dh 837 jnz err_rom_invalid 838 clc 839 jmp 99f 840 841err_pcibios: /* No PCI BIOS available */ 842 movw $load_message_no_pcibios, %si 843 xorl %eax, %eax /* "error code" is zero */ 844 jmp 1f 845err_size_insane: /* BAR has size (%ecx) that is insane */ 846 movw $load_message_size_insane, %si 847 movl %ecx, %eax 848 jmp 1f 849err_no_bar: /* No space of sufficient size (%edx) found */ 850 movw $load_message_no_bar, %si 851 movl %edx, %eax 852 jmp 1f 853err_rom_invalid: /* Loaded ROM does not match (%ebp bytes left) */ 854 movw $load_message_rom_invalid, %si 855 movzbl romheader_size, %eax 856 shll $9, %eax 857 subl %ebp, %eax 858 decl %eax /* %eax is now byte index of failure */ 859 8601: /* Error handler - print message at %si and dword in %eax */ 861 xorw %di, %di 862 call print_message 863 call print_hex_dword 864 stc 86599: popw %es 866 popal 867 ret 868 869 .size load_from_pci, . - load_from_pci 870 871load_message_no_pcibios: 872 .asciz "\nNo PCI BIOS found! " 873 .size load_message_no_pcibios, . - load_message_no_pcibios 874 875load_message_size_insane: 876 .asciz "\nROM resource has invalid size " 877 .size load_message_size_insane, . - load_message_size_insane 878 879load_message_no_bar: 880 .asciz "\nNo memory hole of sufficient size " 881 .size load_message_no_bar, . - load_message_no_bar 882 883load_message_rom_invalid: 884 .asciz "\nLoaded ROM is invalid at " 885 .size load_message_rom_invalid, . - load_message_rom_invalid 886 887#endif /* LOAD_ROM_FROM_PCI */ 888 889 890/* INT19 entry point 891 * 892 * Called via the hooked INT 19 if we detected a non-PnP BIOS. We 893 * attempt to return via the original INT 19 vector (if we were able 894 * to store it). 895 */ 896int19_entry: 897 pushw %cs 898 popw %ds 899 /* Prompt user to press B to boot */ 900 movw $int19_message_prompt, %si 901 xorw %di, %di 902 call print_message 903 movw $prodstr, %si 904 call print_message 905 movw $int19_message_dots, %si 906 call print_message 907 movw $0xdf4e, %bx 908 call wait_for_key 909 pushf 910 xorw %di, %di 911 call print_kill_line 912 movw $int19_message_done, %si 913 call print_message 914 popf 915 jz 1f 916 /* Leave keypress in buffer and start gPXE. The keypress will 917 * cause the usual initial Ctrl-B prompt to be skipped. 918 */ 919 pushw %cs 920 call exec 9211: /* Try to call original INT 19 vector */ 922 movl %cs:orig_int19, %eax 923 testl %eax, %eax 924 je 2f 925 ljmp *%cs:orig_int19 9262: /* No chained vector: issue INT 18 as a last resort */ 927 int $0x18 928 .size int19_entry, . - int19_entry 929orig_int19: 930 .long 0 931 .size orig_int19, . - orig_int19 932 933int19_message_prompt: 934 .asciz "Press N to skip booting from " 935 .size int19_message_prompt, . - int19_message_prompt 936int19_message_dots: 937 .asciz "..." 938 .size int19_message_dots, . - int19_message_dots 939int19_message_done: 940 .asciz "\n\n" 941 .size int19_message_done, . - int19_message_done 942 943/* Execute as a boot device 944 * 945 */ 946exec: /* Set %ds = %cs */ 947 pushw %cs 948 popw %ds 949 950#ifdef LOAD_ROM_FROM_PCI 951 /* Don't execute if load was invalid */ 952 cmpl $0, decompress_to 953 jne 1f 954 lret 9551: 956#endif 957 958 /* Print message as soon as possible */ 959 movw $prodstr, %si 960 xorw %di, %di 961 call print_message 962 movw $exec_message, %si 963 call print_message 964 965 /* Store magic word on BIOS stack and remember BIOS %ss:sp */ 966 pushl $STACK_MAGIC 967 movw %ss, %dx 968 movw %sp, %bp 969 970 /* Obtain a reasonably-sized temporary stack */ 971 xorw %ax, %ax 972 movw %ax, %ss 973 movw $0x7c00, %sp 974 975 /* Install gPXE */ 976 movl image_source, %esi 977 movl decompress_to, %edi 978 call alloc_basemem 979 call install_prealloc 980 981 /* Set up real-mode stack */ 982 movw %bx, %ss 983 movw $_estack16, %sp 984 985 /* Jump to .text16 segment */ 986 pushw %ax 987 pushw $1f 988 lret 989 .section ".text16", "awx", @progbits 9901: /* Call main() */ 991 pushl $main 992 pushw %cs 993 call prot_call 994 popl %ecx /* discard */ 995 996 /* Uninstall gPXE */ 997 call uninstall 998 999 /* Restore BIOS stack */ 1000 movw %dx, %ss 1001 movw %bp, %sp 1002 1003 /* Check magic word on BIOS stack */ 1004 popl %eax 1005 cmpl $STACK_MAGIC, %eax 1006 jne 1f 1007 /* BIOS stack OK: return to caller */ 1008 lret 10091: /* BIOS stack corrupt: use INT 18 */ 1010 int $0x18 1011 .previous 1012 1013exec_message: 1014 .asciz " starting execution\n" 1015 .size exec_message, . - exec_message 1016 1017/* Wait for key press specified by %bl (masked by %bh) 1018 * 1019 * Used by init and INT19 code when prompting user. If the specified 1020 * key is pressed, it is left in the keyboard buffer. 1021 * 1022 * Returns with ZF set iff specified key is pressed. 1023 */ 1024wait_for_key: 1025 /* Preserve registers */ 1026 pushw %cx 1027 pushw %ax 10281: /* Empty the keyboard buffer before waiting for input */ 1029 movb $0x01, %ah 1030 int $0x16 1031 jz 2f 1032 xorw %ax, %ax 1033 int $0x16 1034 jmp 1b 10352: /* Wait for a key press */ 1036 movw $ROM_BANNER_TIMEOUT, %cx 10373: decw %cx 1038 js 99f /* Exit with ZF clear */ 1039 /* Wait for timer tick to be updated */ 1040 call wait_for_tick 1041 /* Check to see if a key was pressed */ 1042 movb $0x01, %ah 1043 int $0x16 1044 jz 3b 1045 /* Check to see if key was the specified key */ 1046 andb %bh, %al 1047 cmpb %al, %bl 1048 je 99f /* Exit with ZF set */ 1049 /* Not the specified key: remove from buffer and stop waiting */ 1050 pushfw 1051 xorw %ax, %ax 1052 int $0x16 1053 popfw /* Exit with ZF clear */ 105499: /* Restore registers and return */ 1055 popw %ax 1056 popw %cx 1057 ret 1058 .size wait_for_key, . - wait_for_key 1059 1060/* Wait for timer tick 1061 * 1062 * Used by wait_for_key 1063 */ 1064wait_for_tick: 1065 pushl %eax 1066 pushw %fs 1067 movw $0x40, %ax 1068 movw %ax, %fs 1069 movl %fs:(0x6c), %eax 10701: pushf 1071 sti 1072 hlt 1073 popf 1074 cmpl %fs:(0x6c), %eax 1075 je 1b 1076 popw %fs 1077 popl %eax 1078 ret 1079 .size wait_for_tick, . - wait_for_tick 1080