Home | History | Annotate | Download | only in prefix
      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