1 FILE_LICENCE ( GPL2_OR_LATER ) 2 3 #define PXENV_UNDI_SHUTDOWN 0x0005 4 #define PXENV_UNDI_GET_NIC_TYPE 0x0012 5 #define PXENV_UNDI_GET_IFACE_INFO 0x0013 6 #define PXENV_STOP_UNDI 0x0015 7 #define PXENV_UNLOAD_STACK 0x0070 8 9 #define PXE_HACK_EB54 0x0001 10 11 .text 12 .arch i386 13 .org 0 14 .code16 15 16 #include <undi.h> 17 18 #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) ) 19 #define EB_MAGIC_1 ( 'E' + ( 't' << 8 ) + ( 'h' << 16 ) + ( 'e' << 24 ) ) 20 #define EB_MAGIC_2 ( 'r' + ( 'b' << 8 ) + ( 'o' << 16 ) + ( 'o' << 24 ) ) 21 22 /***************************************************************************** 23 * Entry point: set operating context, print welcome message 24 ***************************************************************************** 25 */ 26 .section ".prefix", "ax", @progbits 27 jmp $0x7c0, $1f 28 1: 29 /* Preserve registers for possible return to PXE */ 30 pushfl 31 pushal 32 pushw %gs 33 pushw %fs 34 pushw %es 35 pushw %ds 36 37 /* Store magic word on PXE stack and remember PXE %ss:esp */ 38 pushl $STACK_MAGIC 39 movw %ss, %cs:pxe_ss 40 movl %esp, %cs:pxe_esp 41 42 /* Set up segments */ 43 movw %cs, %ax 44 movw %ax, %ds 45 movw $0x40, %ax /* BIOS data segment access */ 46 movw %ax, %fs 47 /* Set up stack just below 0x7c00 */ 48 xorw %ax, %ax 49 movw %ax, %ss 50 movl $0x7c00, %esp 51 /* Clear direction flag, for the sake of sanity */ 52 cld 53 /* Print welcome message */ 54 movw $10f, %si 55 xorw %di, %di 56 call print_message 57 .section ".prefix.data", "aw", @progbits 58 10: .asciz "PXE->EB:" 59 .previous 60 61 /***************************************************************************** 62 * Find us a usable !PXE or PXENV+ entry point 63 ***************************************************************************** 64 */ 65 detect_pxe: 66 /* Plan A: !PXE pointer from the stack */ 67 lgsl pxe_esp, %ebp /* %gs:%bp -> original stack */ 68 lesw %gs:52(%bp), %bx 69 call is_valid_ppxe 70 je have_ppxe 71 72 /* Plan B: PXENV+ pointer from initial ES:BX */ 73 movw %gs:32(%bp),%bx 74 movw %gs:8(%bp),%es 75 call is_valid_pxenv 76 je have_pxenv 77 78 /* Plan C: PXENV+ structure via INT 1Ah */ 79 movw $0x5650, %ax 80 int $0x1a 81 jc 1f 82 cmpw $0x564e, %ax 83 jne 1f 84 call is_valid_pxenv 85 je have_pxenv 86 1: 87 /* Plan D: scan base memory for !PXE */ 88 call memory_scan_ppxe 89 je have_ppxe 90 91 /* Plan E: scan base memory for PXENV+ */ 92 call memory_scan_pxenv 93 jne stack_not_found 94 95 have_pxenv: 96 movw %bx, pxenv_offset 97 movw %es, pxenv_segment 98 99 cmpw $0x201, %es:6(%bx) /* API version >= 2.01 */ 100 jb 1f 101 cmpb $0x2c, %es:8(%bx) /* ... and structure long enough */ 102 jb 2f 103 104 lesw %es:0x28(%bx), %bx /* Find !PXE from PXENV+ */ 105 call is_valid_ppxe 106 je have_ppxe 107 2: 108 call memory_scan_ppxe /* We are *supposed* to have !PXE... */ 109 je have_ppxe 110 1: 111 lesw pxenv_segoff, %bx /* Nope, we're stuck with PXENV+ */ 112 113 /* Record entry point and UNDI segments */ 114 pushl %es:0x0a(%bx) /* Entry point */ 115 pushw %es:0x24(%bx) /* UNDI code segment */ 116 pushw %es:0x26(%bx) /* UNDI code size */ 117 pushw %es:0x20(%bx) /* UNDI data segment */ 118 pushw %es:0x22(%bx) /* UNDI data size */ 119 120 /* Print "PXENV+ at <address>" */ 121 movw $10f, %si 122 jmp check_have_stack 123 .section ".prefix.data", "aw", @progbits 124 10: .asciz " PXENV+ at " 125 .previous 126 127 have_ppxe: 128 movw %bx, ppxe_offset 129 movw %es, ppxe_segment 130 131 pushl %es:0x10(%bx) /* Entry point */ 132 pushw %es:0x30(%bx) /* UNDI code segment */ 133 pushw %es:0x36(%bx) /* UNDI code size */ 134 pushw %es:0x28(%bx) /* UNDI data segment */ 135 pushw %es:0x2e(%bx) /* UNDI data size */ 136 137 /* Print "!PXE at <address>" */ 138 movw $10f, %si 139 jmp check_have_stack 140 .section ".prefix.data", "aw", @progbits 141 10: .asciz " !PXE at " 142 .previous 143 144 is_valid_ppxe: 145 cmpl $0x45585021, %es:(%bx) 146 jne 1f 147 movzbw %es:4(%bx), %cx 148 cmpw $0x58, %cx 149 jae is_valid_checksum 150 1: 151 ret 152 153 is_valid_pxenv: 154 cmpl $0x4e455850, %es:(%bx) 155 jne 1b 156 cmpw $0x2b56, %es:4(%bx) 157 jne 1b 158 movzbw %es:8(%bx), %cx 159 cmpw $0x28, %cx 160 jb 1b 161 162 is_valid_checksum: 163 pushw %ax 164 movw %bx, %si 165 xorw %ax, %ax 166 2: 167 es lodsb 168 addb %al, %ah 169 loopw 2b 170 popw %ax 171 ret 172 173 memory_scan_ppxe: 174 movw $is_valid_ppxe, %dx 175 jmp memory_scan_common 176 177 memory_scan_pxenv: 178 movw $is_valid_pxenv, %dx 179 180 memory_scan_common: 181 movw %fs:(0x13), %ax 182 shlw $6, %ax 183 decw %ax 184 1: incw %ax 185 cmpw $( 0xa000 - 1 ), %ax 186 ja 2f 187 movw %ax, %es 188 xorw %bx, %bx 189 call *%dx 190 jne 1b 191 2: ret 192 193 /***************************************************************************** 194 * Sanity check: we must have an entry point 195 ***************************************************************************** 196 */ 197 check_have_stack: 198 /* Save common values pushed onto the stack */ 199 popl undi_data_segoff 200 popl undi_code_segoff 201 popl entry_segoff 202 203 /* Print have !PXE/PXENV+ message; structure pointer in %es:%bx */ 204 call print_message 205 call print_segoff 206 movb $( ',' ), %al 207 call print_character 208 209 /* Check for entry point */ 210 movl entry_segoff, %eax 211 testl %eax, %eax 212 jnz 99f 213 /* No entry point: print message and skip everything else */ 214 stack_not_found: 215 movw $10f, %si 216 call print_message 217 jmp finished 218 .section ".prefix.data", "aw", @progbits 219 10: .asciz " No PXE stack found!\n" 220 .previous 221 99: 222 223 /***************************************************************************** 224 * Calculate base memory usage by UNDI 225 ***************************************************************************** 226 */ 227 find_undi_basemem_usage: 228 movw undi_code_segment, %ax 229 movw undi_code_size, %bx 230 movw undi_data_segment, %cx 231 movw undi_data_size, %dx 232 cmpw %ax, %cx 233 ja 1f 234 xchgw %ax, %cx 235 xchgw %bx, %dx 236 1: /* %ax:%bx now describes the lower region, %cx:%dx the higher */ 237 shrw $6, %ax /* Round down to nearest kB */ 238 movw %ax, undi_fbms_start 239 addw $0x0f, %dx /* Round up to next segment */ 240 shrw $4, %dx 241 addw %dx, %cx 242 addw $((1024 / 16) - 1), %cx /* Round up to next kB */ 243 shrw $6, %cx 244 movw %cx, undi_fbms_end 245 246 /***************************************************************************** 247 * Print information about detected PXE stack 248 ***************************************************************************** 249 */ 250 print_structure_information: 251 /* Print entry point */ 252 movw $10f, %si 253 call print_message 254 les entry_segoff, %bx 255 call print_segoff 256 .section ".prefix.data", "aw", @progbits 257 10: .asciz " entry point at " 258 .previous 259 /* Print UNDI code segment */ 260 movw $10f, %si 261 call print_message 262 les undi_code_segoff, %bx 263 call print_segoff 264 .section ".prefix.data", "aw", @progbits 265 10: .asciz "\n UNDI code segment " 266 .previous 267 /* Print UNDI data segment */ 268 movw $10f, %si 269 call print_message 270 les undi_data_segoff, %bx 271 call print_segoff 272 .section ".prefix.data", "aw", @progbits 273 10: .asciz ", data segment " 274 .previous 275 /* Print UNDI memory usage */ 276 movw $10f, %si 277 call print_message 278 movw undi_fbms_start, %ax 279 call print_word 280 movb $( '-' ), %al 281 call print_character 282 movw undi_fbms_end, %ax 283 call print_word 284 movw $20f, %si 285 call print_message 286 .section ".prefix.data", "aw", @progbits 287 10: .asciz " (" 288 20: .asciz "kB)\n" 289 .previous 290 291 /***************************************************************************** 292 * Determine physical device 293 ***************************************************************************** 294 */ 295 get_physical_device: 296 /* Issue PXENV_UNDI_GET_NIC_TYPE */ 297 movw $PXENV_UNDI_GET_NIC_TYPE, %bx 298 call pxe_call 299 jnc 1f 300 call print_pxe_error 301 jmp no_physical_device 302 1: /* Determine physical device type */ 303 movb ( pxe_parameter_structure + 0x02 ), %al 304 cmpb $2, %al 305 je pci_physical_device 306 jmp no_physical_device 307 308 pci_physical_device: 309 /* Record PCI bus:dev.fn and vendor/device IDs */ 310 movl ( pxe_parameter_structure + 0x03 ), %eax 311 movl %eax, pci_vendor 312 movw ( pxe_parameter_structure + 0x0b ), %ax 313 movw %ax, pci_busdevfn 314 movw $10f, %si 315 call print_message 316 call print_pci_busdevfn 317 jmp 99f 318 .section ".prefix.data", "aw", @progbits 319 10: .asciz " UNDI device is PCI " 320 .previous 321 322 no_physical_device: 323 /* No device found, or device type not understood */ 324 movw $10f, %si 325 call print_message 326 .section ".prefix.data", "aw", @progbits 327 10: .asciz " Unable to determine UNDI physical device" 328 .previous 329 330 99: 331 332 /***************************************************************************** 333 * Determine interface type 334 ***************************************************************************** 335 */ 336 get_iface_type: 337 /* Issue PXENV_UNDI_GET_IFACE_INFO */ 338 movw $PXENV_UNDI_GET_IFACE_INFO, %bx 339 call pxe_call 340 jnc 1f 341 call print_pxe_error 342 jmp 99f 343 1: /* Print interface type */ 344 movw $10f, %si 345 call print_message 346 leaw ( pxe_parameter_structure + 0x02 ), %si 347 call print_message 348 .section ".prefix.data", "aw", @progbits 349 10: .asciz ", type " 350 .previous 351 /* Check for "Etherboot" interface type */ 352 cmpl $EB_MAGIC_1, ( pxe_parameter_structure + 0x02 ) 353 jne 99f 354 cmpl $EB_MAGIC_2, ( pxe_parameter_structure + 0x06 ) 355 jne 99f 356 movw $10f, %si 357 call print_message 358 .section ".prefix.data", "aw", @progbits 359 10: .asciz " (workaround enabled)" 360 .previous 361 /* Flag Etherboot workarounds as required */ 362 orw $PXE_HACK_EB54, pxe_hacks 363 364 99: movb $0x0a, %al 365 call print_character 366 367 /***************************************************************************** 368 * Leave NIC in a safe state 369 ***************************************************************************** 370 */ 371 #ifndef PXELOADER_KEEP_PXE 372 shutdown_nic: 373 /* Issue PXENV_UNDI_SHUTDOWN */ 374 movw $PXENV_UNDI_SHUTDOWN, %bx 375 call pxe_call 376 jnc 1f 377 call print_pxe_error 378 1: 379 unload_base_code: 380 /* Etherboot treats PXENV_UNLOAD_STACK as PXENV_STOP_UNDI, so 381 * we must not issue this call if the underlying stack is 382 * Etherboot and we were not intending to issue a PXENV_STOP_UNDI. 383 */ 384 #ifdef PXELOADER_KEEP_UNDI 385 testw $PXE_HACK_EB54, pxe_hacks 386 jnz 99f 387 #endif /* PXELOADER_KEEP_UNDI */ 388 /* Issue PXENV_UNLOAD_STACK */ 389 movw $PXENV_UNLOAD_STACK, %bx 390 call pxe_call 391 jnc 1f 392 call print_pxe_error 393 jmp 99f 394 1: /* Free base memory used by PXE base code */ 395 movw undi_fbms_start, %ax 396 movw %fs:(0x13), %bx 397 call free_basemem 398 99: 399 andw $~( UNDI_FL_INITIALIZED | UNDI_FL_KEEP_ALL ), flags 400 #endif /* PXELOADER_KEEP_PXE */ 401 402 /***************************************************************************** 403 * Unload UNDI driver 404 ***************************************************************************** 405 */ 406 #ifndef PXELOADER_KEEP_UNDI 407 unload_undi: 408 /* Issue PXENV_STOP_UNDI */ 409 movw $PXENV_STOP_UNDI, %bx 410 call pxe_call 411 jnc 1f 412 call print_pxe_error 413 jmp 99f 414 1: /* Free base memory used by UNDI */ 415 movw undi_fbms_end, %ax 416 movw undi_fbms_start, %bx 417 call free_basemem 418 /* Clear UNDI_FL_STARTED */ 419 andw $~UNDI_FL_STARTED, flags 420 99: 421 #endif /* PXELOADER_KEEP_UNDI */ 422 423 /***************************************************************************** 424 * Print remaining free base memory 425 ***************************************************************************** 426 */ 427 print_free_basemem: 428 movw $10f, %si 429 call print_message 430 movw %fs:(0x13), %ax 431 call print_word 432 movw $20f, %si 433 call print_message 434 .section ".prefix.data", "aw", @progbits 435 10: .asciz " " 436 20: .asciz "kB free base memory after PXE unload\n" 437 .previous 438 439 /***************************************************************************** 440 * Exit point 441 ***************************************************************************** 442 */ 443 finished: 444 jmp run_gpxe 445 446 /***************************************************************************** 447 * Subroutine: print segment:offset address 448 * 449 * Parameters: 450 * %es:%bx : segment:offset address to print 451 * %ds:di : output buffer (or %di=0 to print to console) 452 * Returns: 453 * %ds:di : next character in output buffer (if applicable) 454 ***************************************************************************** 455 */ 456 print_segoff: 457 /* Preserve registers */ 458 pushw %ax 459 /* Print "<segment>:offset" */ 460 movw %es, %ax 461 call print_hex_word 462 movb $( ':' ), %al 463 call print_character 464 movw %bx, %ax 465 call print_hex_word 466 /* Restore registers and return */ 467 popw %ax 468 ret 469 470 /***************************************************************************** 471 * Subroutine: print decimal word 472 * 473 * Parameters: 474 * %ax : word to print 475 * %ds:di : output buffer (or %di=0 to print to console) 476 * Returns: 477 * %ds:di : next character in output buffer (if applicable) 478 ***************************************************************************** 479 */ 480 print_word: 481 /* Preserve registers */ 482 pushw %ax 483 pushw %bx 484 pushw %cx 485 pushw %dx 486 /* Build up digit sequence on stack */ 487 movw $10, %bx 488 xorw %cx, %cx 489 1: xorw %dx, %dx 490 divw %bx, %ax 491 pushw %dx 492 incw %cx 493 testw %ax, %ax 494 jnz 1b 495 /* Print digit sequence */ 496 1: popw %ax 497 call print_hex_nibble 498 loop 1b 499 /* Restore registers and return */ 500 popw %dx 501 popw %cx 502 popw %bx 503 popw %ax 504 ret 505 506 /***************************************************************************** 507 * Subroutine: zero 1kB block of base memory 508 * 509 * Parameters: 510 * %bx : block to zero (in kB) 511 * Returns: 512 * Nothing 513 ***************************************************************************** 514 */ 515 zero_kb: 516 /* Preserve registers */ 517 pushw %ax 518 pushw %cx 519 pushw %di 520 pushw %es 521 /* Zero block */ 522 movw %bx, %ax 523 shlw $6, %ax 524 movw %ax, %es 525 movw $0x400, %cx 526 xorw %di, %di 527 xorw %ax, %ax 528 rep stosb 529 /* Restore registers and return */ 530 popw %es 531 popw %di 532 popw %cx 533 popw %ax 534 ret 535 536 /***************************************************************************** 537 * Subroutine: free and zero base memory 538 * 539 * Parameters: 540 * %ax : Desired new free base memory counter (in kB) 541 * %bx : Expected current free base memory counter (in kB) 542 * %fs : BIOS data segment (0x40) 543 * Returns: 544 * None 545 * 546 * The base memory from %bx kB to %ax kB is unconditionally zeroed. 547 * It will be freed if and only if the expected current free base 548 * memory counter (%bx) matches the actual current free base memory 549 * counter in 0x40:0x13; if this does not match then the memory will 550 * be leaked. 551 ***************************************************************************** 552 */ 553 free_basemem: 554 /* Zero base memory */ 555 pushw %bx 556 1: cmpw %bx, %ax 557 je 2f 558 call zero_kb 559 incw %bx 560 jmp 1b 561 2: popw %bx 562 /* Free base memory */ 563 cmpw %fs:(0x13), %bx /* Update FBMS only if "old" value */ 564 jne 1f /* is correct */ 565 1: movw %ax, %fs:(0x13) 566 ret 567 568 /***************************************************************************** 569 * Subroutine: make a PXE API call. Works with either !PXE or PXENV+ API. 570 * 571 * Parameters: 572 * %bx : PXE API call number 573 * %ds:pxe_parameter_structure : Parameters for PXE API call 574 * Returns: 575 * %ax : PXE status code (not exit code) 576 * CF set if %ax is non-zero 577 ***************************************************************************** 578 */ 579 pxe_call: 580 /* Preserve registers */ 581 pushw %di 582 pushw %es 583 /* Set up registers for PXENV+ API. %bx already set up */ 584 pushw %ds 585 popw %es 586 movw $pxe_parameter_structure, %di 587 /* Set up stack for !PXE API */ 588 pushw %es 589 pushw %di 590 pushw %bx 591 /* Make the API call */ 592 lcall *entry_segoff 593 /* Reset the stack */ 594 addw $6, %sp 595 movw pxe_parameter_structure, %ax 596 clc 597 testw %ax, %ax 598 jz 1f 599 stc 600 1: /* Clear direction flag, for the sake of sanity */ 601 cld 602 /* Restore registers and return */ 603 popw %es 604 popw %di 605 ret 606 607 /***************************************************************************** 608 * Subroutine: print PXE API call error message 609 * 610 * Parameters: 611 * %ax : PXE status code 612 * %bx : PXE API call number 613 * Returns: 614 * Nothing 615 ***************************************************************************** 616 */ 617 print_pxe_error: 618 pushw %si 619 movw $10f, %si 620 call print_message 621 xchgw %ax, %bx 622 call print_hex_word 623 movw $20f, %si 624 call print_message 625 xchgw %ax, %bx 626 call print_hex_word 627 movw $30f, %si 628 call print_message 629 popw %si 630 ret 631 .section ".prefix.data", "aw", @progbits 632 10: .asciz " UNDI API call " 633 20: .asciz " failed: status code " 634 30: .asciz "\n" 635 .previous 636 637 /***************************************************************************** 638 * PXE data structures 639 ***************************************************************************** 640 */ 641 .section ".prefix.data" 642 643 pxe_esp: .long 0 644 pxe_ss: .word 0 645 646 pxe_parameter_structure: .fill 64 647 648 undi_code_segoff: 649 undi_code_size: .word 0 650 undi_code_segment: .word 0 651 652 undi_data_segoff: 653 undi_data_size: .word 0 654 undi_data_segment: .word 0 655 656 pxe_hacks: .word 0 657 658 /* The following fields are part of a struct undi_device */ 659 660 undi_device: 661 662 pxenv_segoff: 663 pxenv_offset: .word 0 664 pxenv_segment: .word 0 665 666 ppxe_segoff: 667 ppxe_offset: .word 0 668 ppxe_segment: .word 0 669 670 entry_segoff: 671 entry_offset: .word 0 672 entry_segment: .word 0 673 674 undi_fbms_start: .word 0 675 undi_fbms_end: .word 0 676 677 pci_busdevfn: .word UNDI_NO_PCI_BUSDEVFN 678 isapnp_csn: .word UNDI_NO_ISAPNP_CSN 679 isapnp_read_port: .word UNDI_NO_ISAPNP_READ_PORT 680 681 pci_vendor: .word 0 682 pci_device: .word 0 683 flags: 684 .word ( UNDI_FL_INITIALIZED | UNDI_FL_STARTED | UNDI_FL_KEEP_ALL ) 685 686 .equ undi_device_size, ( . - undi_device ) 687 688 /***************************************************************************** 689 * Run gPXE main code 690 ***************************************************************************** 691 */ 692 .section ".prefix" 693 run_gpxe: 694 /* Install gPXE */ 695 call install 696 697 /* Set up real-mode stack */ 698 movw %bx, %ss 699 movw $_estack16, %sp 700 701 #ifdef PXELOADER_KEEP_UNDI 702 /* Copy our undi_device structure to the preloaded_undi variable */ 703 movw %bx, %es 704 movw $preloaded_undi, %di 705 movw $undi_device, %si 706 movw $undi_device_size, %cx 707 rep movsb 708 #endif 709 710 /* Retrieve PXE %ss:esp */ 711 movw pxe_ss, %di 712 movl pxe_esp, %ebp 713 714 /* Jump to .text16 segment with %ds pointing to .data16 */ 715 movw %bx, %ds 716 pushw %ax 717 pushw $1f 718 lret 719 .section ".text16", "ax", @progbits 720 1: 721 /* Update the exit hook */ 722 movw %cs,pxe_exit_hook+2 723 push %ax 724 mov $2f,%ax 725 mov %ax,pxe_exit_hook 726 pop %ax 727 728 /* Run main program */ 729 pushl $main 730 pushw %cs 731 call prot_call 732 popl %ecx /* discard */ 733 734 /* Uninstall gPXE */ 735 call uninstall 736 737 /* Restore PXE stack */ 738 movw %di, %ss 739 movl %ebp, %esp 740 741 /* Jump to hook if applicable */ 742 ljmpw *pxe_exit_hook 743 744 2: /* Check PXE stack magic */ 745 popl %eax 746 cmpl $STACK_MAGIC, %eax 747 jne 1f 748 749 /* PXE stack OK: return to caller */ 750 popw %ds 751 popw %es 752 popw %fs 753 popw %gs 754 popal 755 popfl 756 xorw %ax, %ax /* Return success */ 757 lret 758 759 1: /* PXE stack corrupt or removed: use INT 18 */ 760 int $0x18 761 .previous 762