1 FILE_LICENCE ( GPL2_OR_LATER )
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
9 #define PXE_HACK_EB54 0x0001
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 ) )
22 /*****************************************************************************
23 * Entry point: set operating context, print welcome message
24 *****************************************************************************
26 .section ".prefix", "ax", @progbits
29 /* Preserve registers for possible return to PXE */
37 /* Store magic word on PXE stack and remember PXE %ss:esp */
40 movl %esp, %cs:pxe_esp
45 movw $0x40, %ax /* BIOS data segment access */
47 /* Set up stack just below 0x7c00 */
51 /* Clear direction flag, for the sake of sanity */
53 /* Print welcome message */
57 .section ".prefix.data", "aw", @progbits
61 /*****************************************************************************
62 * Find us a usable !PXE or PXENV+ entry point
63 *****************************************************************************
66 /* Plan A: !PXE pointer from the stack */
67 lgsl pxe_esp, %ebp /* %gs:%bp -> original stack */
72 /* Plan B: PXENV+ pointer from initial ES:BX */
78 /* Plan C: PXENV+ structure via INT 1Ah */
87 /* Plan D: scan base memory for !PXE */
91 /* Plan E: scan base memory for PXENV+ */
92 call memory_scan_pxenv
96 movw %bx, pxenv_offset
97 movw %es, pxenv_segment
99 cmpw $0x201, %es:6(%bx) /* API version >= 2.01 */
101 cmpb $0x2c, %es:8(%bx) /* ... and structure long enough */
104 lesw %es:0x28(%bx), %bx /* Find !PXE from PXENV+ */
108 call memory_scan_ppxe /* We are *supposed* to have !PXE... */
111 lesw pxenv_segoff, %bx /* Nope, we're stuck with PXENV+ */
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 */
120 /* Print "PXENV+ at <address>" */
123 .section ".prefix.data", "aw", @progbits
124 10: .asciz " PXENV+ at "
128 movw %bx, ppxe_offset
129 movw %es, ppxe_segment
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 */
137 /* Print "!PXE at <address>" */
140 .section ".prefix.data", "aw", @progbits
141 10: .asciz " !PXE at "
145 cmpl $0x45585021, %es:(%bx)
147 movzbw %es:4(%bx), %cx
149 jae is_valid_checksum
154 cmpl $0x4e455850, %es:(%bx)
156 cmpw $0x2b56, %es:4(%bx)
158 movzbw %es:8(%bx), %cx
174 movw $is_valid_ppxe, %dx
175 jmp memory_scan_common
178 movw $is_valid_pxenv, %dx
185 cmpw $( 0xa000 - 1 ), %ax
193 /*****************************************************************************
194 * Sanity check: we must have an entry point
195 *****************************************************************************
198 /* Save common values pushed onto the stack */
199 popl undi_data_segoff
200 popl undi_code_segoff
203 /* Print have !PXE/PXENV+ message; structure pointer in %es:%bx */
209 /* Check for entry point */
210 movl entry_segoff, %eax
213 /* No entry point: print message and skip everything else */
218 .section ".prefix.data", "aw", @progbits
219 10: .asciz " No PXE stack found!\n"
223 /*****************************************************************************
224 * Calculate base memory usage by UNDI
225 *****************************************************************************
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
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 */
242 addw $((1024 / 16) - 1), %cx /* Round up to next kB */
244 movw %cx, undi_fbms_end
246 /*****************************************************************************
247 * Print information about detected PXE stack
248 *****************************************************************************
250 print_structure_information:
251 /* Print entry point */
254 les entry_segoff, %bx
256 .section ".prefix.data", "aw", @progbits
257 10: .asciz " entry point at "
259 /* Print UNDI code segment */
262 les undi_code_segoff, %bx
264 .section ".prefix.data", "aw", @progbits
265 10: .asciz "\n UNDI code segment "
267 /* Print UNDI data segment */
270 les undi_data_segoff, %bx
272 .section ".prefix.data", "aw", @progbits
273 10: .asciz ", data segment "
275 /* Print UNDI memory usage */
278 movw undi_fbms_start, %ax
282 movw undi_fbms_end, %ax
286 .section ".prefix.data", "aw", @progbits
291 /*****************************************************************************
292 * Determine physical device
293 *****************************************************************************
296 /* Issue PXENV_UNDI_GET_NIC_TYPE */
297 movw $PXENV_UNDI_GET_NIC_TYPE, %bx
301 jmp no_physical_device
302 1: /* Determine physical device type */
303 movb ( pxe_parameter_structure + 0x02 ), %al
305 je pci_physical_device
306 jmp no_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
316 call print_pci_busdevfn
318 .section ".prefix.data", "aw", @progbits
319 10: .asciz " UNDI device is PCI "
323 /* No device found, or device type not understood */
326 .section ".prefix.data", "aw", @progbits
327 10: .asciz " Unable to determine UNDI physical device"
332 /*****************************************************************************
333 * Determine interface type
334 *****************************************************************************
337 /* Issue PXENV_UNDI_GET_IFACE_INFO */
338 movw $PXENV_UNDI_GET_IFACE_INFO, %bx
343 1: /* Print interface type */
346 leaw ( pxe_parameter_structure + 0x02 ), %si
348 .section ".prefix.data", "aw", @progbits
351 /* Check for "Etherboot" interface type */
352 cmpl $EB_MAGIC_1, ( pxe_parameter_structure + 0x02 )
354 cmpl $EB_MAGIC_2, ( pxe_parameter_structure + 0x06 )
358 .section ".prefix.data", "aw", @progbits
359 10: .asciz " (workaround enabled)"
361 /* Flag Etherboot workarounds as required */
362 orw $PXE_HACK_EB54, pxe_hacks
367 /*****************************************************************************
368 * Leave NIC in a safe state
369 *****************************************************************************
371 #ifndef PXELOADER_KEEP_PXE
373 /* Issue PXENV_UNDI_SHUTDOWN */
374 movw $PXENV_UNDI_SHUTDOWN, %bx
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.
384 #ifdef PXELOADER_KEEP_UNDI
385 testw $PXE_HACK_EB54, pxe_hacks
387 #endif /* PXELOADER_KEEP_UNDI */
388 /* Issue PXENV_UNLOAD_STACK */
389 movw $PXENV_UNLOAD_STACK, %bx
394 1: /* Free base memory used by PXE base code */
395 movw undi_fbms_start, %ax
399 andw $~( UNDI_FL_INITIALIZED | UNDI_FL_KEEP_ALL ), flags
400 #endif /* PXELOADER_KEEP_PXE */
402 /*****************************************************************************
404 *****************************************************************************
406 #ifndef PXELOADER_KEEP_UNDI
408 /* Issue PXENV_STOP_UNDI */
409 movw $PXENV_STOP_UNDI, %bx
414 1: /* Free base memory used by UNDI */
415 movw undi_fbms_end, %ax
416 movw undi_fbms_start, %bx
418 /* Clear UNDI_FL_STARTED */
419 andw $~UNDI_FL_STARTED, flags
421 #endif /* PXELOADER_KEEP_UNDI */
423 /*****************************************************************************
424 * Print remaining free base memory
425 *****************************************************************************
434 .section ".prefix.data", "aw", @progbits
436 20: .asciz "kB free base memory after PXE unload\n"
439 /*****************************************************************************
441 *****************************************************************************
446 /*****************************************************************************
447 * Subroutine: print segment:offset address
450 * %es:%bx : segment:offset address to print
451 * %ds:di : output buffer (or %di=0 to print to console)
453 * %ds:di : next character in output buffer (if applicable)
454 *****************************************************************************
457 /* Preserve registers */
459 /* Print "<segment>:offset" */
466 /* Restore registers and return */
470 /*****************************************************************************
471 * Subroutine: print decimal word
474 * %ax : word to print
475 * %ds:di : output buffer (or %di=0 to print to console)
477 * %ds:di : next character in output buffer (if applicable)
478 *****************************************************************************
481 /* Preserve registers */
486 /* Build up digit sequence on stack */
495 /* Print digit sequence */
497 call print_hex_nibble
499 /* Restore registers and return */
506 /*****************************************************************************
507 * Subroutine: zero 1kB block of base memory
510 * %bx : block to zero (in kB)
513 *****************************************************************************
516 /* Preserve registers */
529 /* Restore registers and return */
536 /*****************************************************************************
537 * Subroutine: free and zero base memory
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)
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
551 *****************************************************************************
554 /* Zero base memory */
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)
568 /*****************************************************************************
569 * Subroutine: make a PXE API call. Works with either !PXE or PXENV+ API.
572 * %bx : PXE API call number
573 * %ds:pxe_parameter_structure : Parameters for PXE API call
575 * %ax : PXE status code (not exit code)
576 * CF set if %ax is non-zero
577 *****************************************************************************
580 /* Preserve registers */
583 /* Set up registers for PXENV+ API. %bx already set up */
586 movw $pxe_parameter_structure, %di
587 /* Set up stack for !PXE API */
591 /* Make the API call */
593 /* Reset the stack */
595 movw pxe_parameter_structure, %ax
600 1: /* Clear direction flag, for the sake of sanity */
602 /* Restore registers and return */
607 /*****************************************************************************
608 * Subroutine: print PXE API call error message
611 * %ax : PXE status code
612 * %bx : PXE API call number
615 *****************************************************************************
631 .section ".prefix.data", "aw", @progbits
632 10: .asciz " UNDI API call "
633 20: .asciz " failed: status code "
637 /*****************************************************************************
638 * PXE data structures
639 *****************************************************************************
641 .section ".prefix.data"
646 pxe_parameter_structure: .fill 64
649 undi_code_size: .word 0
650 undi_code_segment: .word 0
653 undi_data_size: .word 0
654 undi_data_segment: .word 0
658 /* The following fields are part of a struct undi_device */
663 pxenv_offset: .word 0
664 pxenv_segment: .word 0
668 ppxe_segment: .word 0
671 entry_offset: .word 0
672 entry_segment: .word 0
674 undi_fbms_start: .word 0
675 undi_fbms_end: .word 0
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
684 .word ( UNDI_FL_INITIALIZED | UNDI_FL_STARTED | UNDI_FL_KEEP_ALL )
686 .equ undi_device_size, ( . - undi_device )
688 /*****************************************************************************
690 *****************************************************************************
697 /* Set up real-mode stack */
701 #ifdef PXELOADER_KEEP_UNDI
702 /* Copy our undi_device structure to the preloaded_undi variable */
704 movw $preloaded_undi, %di
705 movw $undi_device, %si
706 movw $undi_device_size, %cx
710 /* Retrieve PXE %ss:esp */
714 /* Jump to .text16 segment with %ds pointing to .data16 */
719 .section ".text16", "ax", @progbits
721 /* Update the exit hook */
722 movw %cs,pxe_exit_hook+2
725 mov %ax,pxe_exit_hook
728 /* Run main program */
732 popl %ecx /* discard */
737 /* Restore PXE stack */
741 /* Jump to hook if applicable */
744 2: /* Check PXE stack magic */
746 cmpl $STACK_MAGIC, %eax
749 /* PXE stack OK: return to caller */
756 xorw %ax, %ax /* Return success */
759 1: /* PXE stack corrupt or removed: use INT 18 */