1 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
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 #define PXENV_GET_CACHED_INFO 0x0071
9 #define PXENV_PACKET_TYPE_DHCP_ACK 0x0002
10 #define PXENV_FILE_CMDLINE 0x00e8
12 #define PXE_HACK_EB54 0x0001
21 #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
22 #define EB_MAGIC_1 ( 'E' + ( 't' << 8 ) + ( 'h' << 16 ) + ( 'e' << 24 ) )
23 #define EB_MAGIC_2 ( 'r' + ( 'b' << 8 ) + ( 'o' << 16 ) + ( 'o' << 24 ) )
25 /* Prefix memory layout:
29 * Temporary copy of DHCPACK packet
30 * Temporary copy of command line
32 #define PREFIX_STACK_SIZE 2048
33 #define PREFIX_TEMP_DHCPACK PREFIX_STACK_SIZE
34 #define PREFIX_TEMP_DHCPACK_SIZE ( 1260 /* sizeof ( BOOTPLAYER_t ) */ )
35 #define PREFIX_TEMP_CMDLINE ( PREFIX_TEMP_DHCPACK + PREFIX_TEMP_DHCPACK_SIZE )
36 #define PREFIX_TEMP_CMDLINE_SIZE 4096
38 /*****************************************************************************
39 * Entry point: set operating context, print welcome message
40 *****************************************************************************
42 .section ".prefix", "ax", @progbits
47 /* Preserve registers for possible return to PXE */
55 /* Store magic word on PXE stack and remember PXE %ss:esp */
58 movl %esp, %cs:pxe_esp
63 movw $0x40, %ax /* BIOS data segment access */
65 /* Set up temporary stack immediately after the iPXE image */
67 addw image_size_pgh, %ax
69 movl $PREFIX_STACK_SIZE, %esp
70 /* Clear direction flag, for the sake of sanity */
72 /* Print welcome message */
76 .section ".prefix.data", "aw", @progbits
80 /* Image size (for stack placement calculation) */
81 .section ".prefix.data", "aw", @progbits
85 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
92 /*****************************************************************************
93 * Find us a usable !PXE or PXENV+ entry point
94 *****************************************************************************
97 /* Plan A: !PXE pointer from the stack */
98 lgsl pxe_esp, %ebp /* %gs:%bp -> original stack */
103 /* Plan B: PXENV+ pointer from initial ES:BX */
109 /* Plan C: PXENV+ structure via INT 1Ah */
118 /* Plan D: scan base memory for !PXE */
119 call memory_scan_ppxe
122 /* Plan E: scan base memory for PXENV+ */
123 call memory_scan_pxenv
127 movw %bx, pxenv_offset
128 movw %es, pxenv_segment
130 cmpw $0x201, %es:6(%bx) /* API version >= 2.01 */
132 cmpb $0x2c, %es:8(%bx) /* ... and structure long enough */
135 lesw %es:0x28(%bx), %bx /* Find !PXE from PXENV+ */
139 call memory_scan_ppxe /* We are *supposed* to have !PXE... */
142 lesw pxenv_segoff, %bx /* Nope, we're stuck with PXENV+ */
144 /* Record entry point and UNDI segments */
145 pushl %es:0x0a(%bx) /* Entry point */
146 pushw %es:0x24(%bx) /* UNDI code segment */
147 pushw %es:0x26(%bx) /* UNDI code size */
148 pushw %es:0x20(%bx) /* UNDI data segment */
149 pushw %es:0x22(%bx) /* UNDI data size */
151 /* Print "PXENV+ at <address>" */
154 .section ".prefix.data", "aw", @progbits
155 10: .asciz " PXENV+ at "
159 movw %bx, ppxe_offset
160 movw %es, ppxe_segment
162 pushl %es:0x10(%bx) /* Entry point */
163 pushw %es:0x30(%bx) /* UNDI code segment */
164 pushw %es:0x36(%bx) /* UNDI code size */
165 pushw %es:0x28(%bx) /* UNDI data segment */
166 pushw %es:0x2e(%bx) /* UNDI data size */
168 /* Print "!PXE at <address>" */
171 .section ".prefix.data", "aw", @progbits
172 10: .asciz " !PXE at "
176 cmpl $0x45585021, %es:(%bx)
178 movzbw %es:4(%bx), %cx
180 jae is_valid_checksum
185 cmpl $0x4e455850, %es:(%bx)
187 cmpw $0x2b56, %es:4(%bx)
189 movzbw %es:8(%bx), %cx
205 movw $is_valid_ppxe, %dx
206 jmp memory_scan_common
209 movw $is_valid_pxenv, %dx
216 cmpw $( 0xa000 - 1 ), %ax
224 /*****************************************************************************
225 * Sanity check: we must have an entry point
226 *****************************************************************************
229 /* Save common values pushed onto the stack */
230 popl undi_data_segoff
231 popl undi_code_segoff
234 /* Print have !PXE/PXENV+ message; structure pointer in %es:%bx */
240 /* Check for entry point */
241 movl entry_segoff, %eax
244 /* No entry point: print message and skip everything else */
249 .section ".prefix.data", "aw", @progbits
250 10: .asciz " No PXE stack found!\n"
254 /*****************************************************************************
255 * Calculate base memory usage by UNDI
256 *****************************************************************************
258 find_undi_basemem_usage:
259 movw undi_code_segment, %ax
260 movw undi_code_size, %bx
261 movw undi_data_segment, %cx
262 movw undi_data_size, %dx
267 1: /* %ax:%bx now describes the lower region, %cx:%dx the higher */
268 shrw $6, %ax /* Round down to nearest kB */
269 movw %ax, undi_fbms_start
270 addw $0x0f, %dx /* Round up to next segment */
273 addw $((1024 / 16) - 1), %cx /* Round up to next kB */
275 movw %cx, undi_fbms_end
277 /*****************************************************************************
278 * Print information about detected PXE stack
279 *****************************************************************************
281 print_structure_information:
282 /* Print entry point */
285 les entry_segoff, %bx
287 .section ".prefix.data", "aw", @progbits
288 10: .asciz " entry point at "
290 /* Print UNDI code segment */
293 les undi_code_segoff, %bx
295 .section ".prefix.data", "aw", @progbits
296 10: .asciz "\n UNDI code segment "
298 /* Print UNDI data segment */
301 les undi_data_segoff, %bx
303 .section ".prefix.data", "aw", @progbits
304 10: .asciz ", data segment "
306 /* Print UNDI memory usage */
309 movw undi_fbms_start, %ax
313 movw undi_fbms_end, %ax
317 .section ".prefix.data", "aw", @progbits
322 /*****************************************************************************
323 * Determine physical device
324 *****************************************************************************
327 /* Issue PXENV_UNDI_GET_NIC_TYPE */
328 movw $PXENV_UNDI_GET_NIC_TYPE, %bx
332 jmp no_physical_device
333 1: /* Determine physical device type */
334 movb ( pxe_parameter_structure + 0x02 ), %al
336 je pci_physical_device
337 jmp no_physical_device
340 /* Record PCI bus:dev.fn and vendor/device IDs */
341 movl ( pxe_parameter_structure + 0x03 ), %eax
342 movl %eax, pci_vendor
343 movw ( pxe_parameter_structure + 0x0b ), %ax
344 movw %ax, pci_busdevfn
347 call print_pci_busdevfn
349 .section ".prefix.data", "aw", @progbits
350 10: .asciz " UNDI device is PCI "
354 /* No device found, or device type not understood */
357 .section ".prefix.data", "aw", @progbits
358 10: .asciz " Unable to determine UNDI physical device"
363 /*****************************************************************************
364 * Determine interface type
365 *****************************************************************************
368 /* Issue PXENV_UNDI_GET_IFACE_INFO */
369 movw $PXENV_UNDI_GET_IFACE_INFO, %bx
374 1: /* Print interface type */
377 leaw ( pxe_parameter_structure + 0x02 ), %si
379 .section ".prefix.data", "aw", @progbits
382 /* Check for "Etherboot" interface type */
383 cmpl $EB_MAGIC_1, ( pxe_parameter_structure + 0x02 )
385 cmpl $EB_MAGIC_2, ( pxe_parameter_structure + 0x06 )
389 .section ".prefix.data", "aw", @progbits
390 10: .asciz " (workaround enabled)"
392 /* Flag Etherboot workarounds as required */
393 orw $PXE_HACK_EB54, pxe_hacks
398 /*****************************************************************************
399 * Get cached DHCP_ACK packet
400 *****************************************************************************
403 /* Issue PXENV_GET_CACHED_INFO */
406 movw %si, ( pxe_parameter_structure + 0x08 )
407 movw $PREFIX_TEMP_DHCPACK, ( pxe_parameter_structure + 0x06 )
408 movw $PREFIX_TEMP_DHCPACK_SIZE, ( pxe_parameter_structure +0x04 )
409 movw $PXENV_PACKET_TYPE_DHCP_ACK, ( pxe_parameter_structure + 0x02 )
410 movw $PXENV_GET_CACHED_INFO, %bx
415 1: /* Store physical address of packet */
417 addl $PREFIX_TEMP_DHCPACK, %esi
418 movl %esi, pxe_cached_dhcpack
420 .section ".prefix.data", "aw", @progbits
425 /*****************************************************************************
426 * Check for a command line
427 *****************************************************************************
430 /* Issue PXENV_FILE_CMDLINE */
433 movw %si, ( pxe_parameter_structure + 0x06 )
434 movw $PREFIX_TEMP_CMDLINE, ( pxe_parameter_structure + 0x04 )
435 movw $PREFIX_TEMP_CMDLINE_SIZE, ( pxe_parameter_structure + 0x02 )
436 movw $PXENV_FILE_CMDLINE, %bx
438 jc 99f /* Suppress errors; this is an iPXE extension API call */
439 /* Check for non-NULL command line */
440 movw ( pxe_parameter_structure + 0x02 ), %ax
443 /* Record command line */
445 addl $PREFIX_TEMP_CMDLINE, %esi
446 movl %esi, pxe_cmdline
448 .section ".prefix.data", "aw", @progbits
453 /*****************************************************************************
454 * Leave NIC in a safe state
455 *****************************************************************************
457 #ifndef PXELOADER_KEEP_PXE
459 /* Issue PXENV_UNDI_SHUTDOWN */
460 movw $PXENV_UNDI_SHUTDOWN, %bx
466 /* Etherboot treats PXENV_UNLOAD_STACK as PXENV_STOP_UNDI, so
467 * we must not issue this call if the underlying stack is
468 * Etherboot and we were not intending to issue a PXENV_STOP_UNDI.
470 #ifdef PXELOADER_KEEP_UNDI
471 testw $PXE_HACK_EB54, pxe_hacks
473 #endif /* PXELOADER_KEEP_UNDI */
474 /* Issue PXENV_UNLOAD_STACK */
475 movw $PXENV_UNLOAD_STACK, %bx
480 1: /* Free base memory used by PXE base code */
481 movw undi_fbms_start, %ax
485 andw $~( UNDI_FL_INITIALIZED | UNDI_FL_KEEP_ALL ), flags
486 #endif /* PXELOADER_KEEP_PXE */
488 /*****************************************************************************
490 *****************************************************************************
492 #ifndef PXELOADER_KEEP_UNDI
494 /* Issue PXENV_STOP_UNDI */
495 movw $PXENV_STOP_UNDI, %bx
500 1: /* Free base memory used by UNDI */
501 movw undi_fbms_end, %ax
502 movw undi_fbms_start, %bx
504 /* Clear UNDI_FL_STARTED */
505 andw $~UNDI_FL_STARTED, flags
507 #endif /* PXELOADER_KEEP_UNDI */
509 /*****************************************************************************
510 * Print remaining free base memory
511 *****************************************************************************
520 .section ".prefix.data", "aw", @progbits
522 20: .asciz "kB free base memory after PXE unload\n"
525 /*****************************************************************************
527 *****************************************************************************
532 /*****************************************************************************
533 * Subroutine: print segment:offset address
536 * %es:%bx : segment:offset address to print
537 * %ds:di : output buffer (or %di=0 to print to console)
539 * %ds:di : next character in output buffer (if applicable)
540 *****************************************************************************
543 /* Preserve registers */
545 /* Print "<segment>:offset" */
552 /* Restore registers and return */
556 /*****************************************************************************
557 * Subroutine: print decimal word
560 * %ax : word to print
561 * %ds:di : output buffer (or %di=0 to print to console)
563 * %ds:di : next character in output buffer (if applicable)
564 *****************************************************************************
567 /* Preserve registers */
572 /* Build up digit sequence on stack */
581 /* Print digit sequence */
583 call print_hex_nibble
585 /* Restore registers and return */
592 /*****************************************************************************
593 * Subroutine: zero 1kB block of base memory
596 * %bx : block to zero (in kB)
599 *****************************************************************************
602 /* Preserve registers */
615 /* Restore registers and return */
622 /*****************************************************************************
623 * Subroutine: free and zero base memory
626 * %ax : Desired new free base memory counter (in kB)
627 * %bx : Expected current free base memory counter (in kB)
628 * %fs : BIOS data segment (0x40)
632 * The base memory from %bx kB to %ax kB is unconditionally zeroed.
633 * It will be freed if and only if the expected current free base
634 * memory counter (%bx) matches the actual current free base memory
635 * counter in 0x40:0x13; if this does not match then the memory will
637 *****************************************************************************
640 /* Zero base memory */
648 /* Free base memory */
649 cmpw %fs:(0x13), %bx /* Update FBMS only if "old" value */
650 jne 1f /* is correct */
651 1: movw %ax, %fs:(0x13)
654 /*****************************************************************************
655 * Subroutine: make a PXE API call. Works with either !PXE or PXENV+ API.
658 * %bx : PXE API call number
659 * %ds:pxe_parameter_structure : Parameters for PXE API call
661 * %ax : PXE status code (not exit code)
662 * CF set if %ax is non-zero
663 *****************************************************************************
666 /* Preserve registers */
669 /* Set up registers for PXENV+ API. %bx already set up */
672 movw $pxe_parameter_structure, %di
673 /* Set up stack for !PXE API */
677 /* Make the API call */
679 /* Reset the stack */
681 movw pxe_parameter_structure, %ax
686 1: /* Clear direction flag, for the sake of sanity */
688 /* Restore registers and return */
693 /*****************************************************************************
694 * Subroutine: print PXE API call error message
697 * %ax : PXE status code
698 * %bx : PXE API call number
701 *****************************************************************************
717 .section ".prefix.data", "aw", @progbits
718 10: .asciz " UNDI API call "
719 20: .asciz " failed: status code "
723 /*****************************************************************************
724 * PXE data structures
725 *****************************************************************************
727 .section ".prefix.data"
732 pxe_parameter_structure: .fill 64
735 undi_code_size: .word 0
736 undi_code_segment: .word 0
739 undi_data_size: .word 0
740 undi_data_segment: .word 0
744 /* The following fields are part of a struct undi_device */
749 pxenv_offset: .word 0
750 pxenv_segment: .word 0
754 ppxe_segment: .word 0
757 entry_offset: .word 0
758 entry_segment: .word 0
760 undi_fbms_start: .word 0
761 undi_fbms_end: .word 0
763 pci_busdevfn: .word UNDI_NO_PCI_BUSDEVFN
764 isapnp_csn: .word UNDI_NO_ISAPNP_CSN
765 isapnp_read_port: .word UNDI_NO_ISAPNP_READ_PORT
770 .word ( UNDI_FL_INITIALIZED | UNDI_FL_STARTED | UNDI_FL_KEEP_ALL )
772 .equ undi_device_size, ( . - undi_device )
774 /*****************************************************************************
776 *****************************************************************************
783 /* Set up real-mode stack */
787 #ifdef PXELOADER_KEEP_UNDI
788 /* Copy our undi_device structure to the preloaded_undi variable */
790 movw $preloaded_undi, %di
791 movw $undi_device, %si
792 movw $undi_device_size, %cx
796 /* Retrieve PXE %ss:esp */
800 /* Retrieve PXE command line, if any */
801 movl pxe_cmdline, %esi
803 /* Retrieve cached DHCPACK, if any */
804 movl pxe_cached_dhcpack, %ecx
806 /* Jump to .text16 segment with %ds pointing to .data16 */
811 .section ".text16", "ax", @progbits
813 /* Update the exit hook */
814 movw %cs, ( pxe_exit_hook + 2 )
816 /* Store command-line pointer */
817 movl %esi, cmdline_phys
819 /* Store cached DHCPACK pointer */
820 movl %ecx, cached_dhcpack_phys
822 /* Run main program */
826 popl %ecx /* discard */
831 /* Restore PXE stack */
835 /* Jump to hook if applicable */
838 .section ".data16", "aw", @progbits
845 /* Check PXE stack magic */
847 cmpl $STACK_MAGIC, %eax
850 /* PXE stack OK: return to caller */
857 xorw %ax, %ax /* Return success */
860 1: /* PXE stack corrupt or removed: use INT 18 */