1 #define PXENV_UNDI_SHUTDOWN 0x0005
2 #define PXENV_UNDI_GET_NIC_TYPE 0x0012
3 #define PXENV_UNDI_GET_IFACE_INFO 0x0013
4 #define PXENV_STOP_UNDI 0x0015
5 #define PXENV_UNLOAD_STACK 0x0070
7 #define PXE_HACK_EB54 0x0001
16 #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
17 #define EB_MAGIC_1 ( 'E' + ( 't' << 8 ) + ( 'h' << 16 ) + ( 'e' << 24 ) )
18 #define EB_MAGIC_2 ( 'r' + ( 'b' << 8 ) + ( 'o' << 16 ) + ( 'o' << 24 ) )
20 /*****************************************************************************
21 * Entry point: set operating context, print welcome message
22 *****************************************************************************
24 .section ".prefix", "ax", @progbits
27 /* Preserve registers for possible return to PXE */
35 /* Store magic word on PXE stack and remember PXE %ss:esp */
38 movl %esp, %cs:pxe_esp
43 movw $0x40, %ax /* BIOS data segment access */
45 /* Set up stack just below 0x7c00 */
49 /* Clear direction flag, for the sake of sanity */
51 /* Print welcome message */
55 .section ".prefix.data", "aw", @progbits
59 /*****************************************************************************
60 * Find us a usable !PXE or PXENV+ entry point
61 *****************************************************************************
64 /* Plan A: !PXE pointer from the stack */
65 lgsl pxe_esp, %ebp /* %gs:%bp -> original stack */
70 /* Plan B: PXENV+ pointer from initial ES:BX */
76 /* Plan C: PXENV+ structure via INT 1Ah */
85 /* Plan D: scan base memory for !PXE */
89 /* Plan E: scan base memory for PXENV+ */
90 call memory_scan_pxenv
94 movw %bx, pxenv_offset
95 movw %es, pxenv_segment
97 cmpw $0x201, %es:6(%bx) /* API version >= 2.01 */
99 cmpb $0x2c, %es:8(%bx) /* ... and structure long enough */
102 lesw %es:0x28(%bx), %bx /* Find !PXE from PXENV+ */
106 call memory_scan_ppxe /* We are *supposed* to have !PXE... */
109 lesw pxenv_segoff, %bx /* Nope, we're stuck with PXENV+ */
111 /* Record entry point and UNDI segments */
112 pushl %es:0x0a(%bx) /* Entry point */
113 pushw %es:0x24(%bx) /* UNDI code segment */
114 pushw %es:0x26(%bx) /* UNDI code size */
115 pushw %es:0x20(%bx) /* UNDI data segment */
116 pushw %es:0x22(%bx) /* UNDI data size */
118 /* Print "PXENV+ at <address>" */
121 .section ".prefix.data", "aw", @progbits
122 10: .asciz " PXENV+ at "
126 movw %bx, ppxe_offset
127 movw %es, ppxe_segment
129 pushl %es:0x10(%bx) /* Entry point */
130 pushw %es:0x30(%bx) /* UNDI code segment */
131 pushw %es:0x36(%bx) /* UNDI code size */
132 pushw %es:0x28(%bx) /* UNDI data segment */
133 pushw %es:0x2e(%bx) /* UNDI data size */
135 /* Print "!PXE at <address>" */
138 .section ".prefix.data", "aw", @progbits
139 10: .asciz " !PXE at "
143 cmpl $0x45585021, %es:(%bx)
145 movzbw %es:4(%bx), %cx
147 jae is_valid_checksum
152 cmpl $0x4e455850, %es:(%bx)
154 cmpw $0x2b56, %es:4(%bx)
156 movzbw %es:8(%bx), %cx
172 movw $is_valid_ppxe, %dx
173 jmp memory_scan_common
176 movw $is_valid_pxenv, %dx
183 cmpw $( 0xa000 - 1 ), %ax
191 /*****************************************************************************
192 * Sanity check: we must have an entry point
193 *****************************************************************************
196 /* Save common values pushed onto the stack */
197 popl undi_data_segoff
198 popl undi_code_segoff
201 /* Print have !PXE/PXENV+ message; structure pointer in %es:%bx */
207 /* Check for entry point */
208 movl entry_segoff, %eax
211 /* No entry point: print message and skip everything else */
216 .section ".prefix.data", "aw", @progbits
217 10: .asciz " No PXE stack found!\n"
221 /*****************************************************************************
222 * Calculate base memory usage by UNDI
223 *****************************************************************************
225 find_undi_basemem_usage:
226 movw undi_code_segment, %ax
227 movw undi_code_size, %bx
228 movw undi_data_segment, %cx
229 movw undi_data_size, %dx
234 1: /* %ax:%bx now describes the lower region, %cx:%dx the higher */
235 shrw $6, %ax /* Round down to nearest kB */
236 movw %ax, undi_fbms_start
237 addw $0x0f, %dx /* Round up to next segment */
240 addw $((1024 / 16) - 1), %cx /* Round up to next kB */
242 movw %cx, undi_fbms_end
244 /*****************************************************************************
245 * Print information about detected PXE stack
246 *****************************************************************************
248 print_structure_information:
249 /* Print entry point */
252 les entry_segoff, %bx
254 .section ".prefix.data", "aw", @progbits
255 10: .asciz " entry point at "
257 /* Print UNDI code segment */
260 les undi_code_segoff, %bx
262 .section ".prefix.data", "aw", @progbits
263 10: .asciz "\n UNDI code segment "
265 /* Print UNDI data segment */
268 les undi_data_segoff, %bx
270 .section ".prefix.data", "aw", @progbits
271 10: .asciz ", data segment "
273 /* Print UNDI memory usage */
276 movw undi_fbms_start, %ax
280 movw undi_fbms_end, %ax
284 .section ".prefix.data", "aw", @progbits
289 /*****************************************************************************
290 * Determine physical device
291 *****************************************************************************
294 /* Issue PXENV_UNDI_GET_NIC_TYPE */
295 movw $PXENV_UNDI_GET_NIC_TYPE, %bx
299 jmp no_physical_device
300 1: /* Determine physical device type */
301 movb ( pxe_parameter_structure + 0x02 ), %al
303 je pci_physical_device
304 jmp no_physical_device
307 /* Record PCI bus:dev.fn and vendor/device IDs */
308 movl ( pxe_parameter_structure + 0x03 ), %eax
309 movl %eax, pci_vendor
310 movw ( pxe_parameter_structure + 0x0b ), %ax
311 movw %ax, pci_busdevfn
314 call print_pci_busdevfn
316 .section ".prefix.data", "aw", @progbits
317 10: .asciz " UNDI device is PCI "
321 /* No device found, or device type not understood */
324 .section ".prefix.data", "aw", @progbits
325 10: .asciz " Unable to determine UNDI physical device"
330 /*****************************************************************************
331 * Determine interface type
332 *****************************************************************************
335 /* Issue PXENV_UNDI_GET_IFACE_INFO */
336 movw $PXENV_UNDI_GET_IFACE_INFO, %bx
341 1: /* Print interface type */
344 leaw ( pxe_parameter_structure + 0x02 ), %si
346 .section ".prefix.data", "aw", @progbits
349 /* Check for "Etherboot" interface type */
350 cmpl $EB_MAGIC_1, ( pxe_parameter_structure + 0x02 )
352 cmpl $EB_MAGIC_2, ( pxe_parameter_structure + 0x06 )
356 .section ".prefix.data", "aw", @progbits
357 10: .asciz " (workaround enabled)"
359 /* Flag Etherboot workarounds as required */
360 orw $PXE_HACK_EB54, pxe_hacks
365 /*****************************************************************************
366 * Leave NIC in a safe state
367 *****************************************************************************
369 #ifndef PXELOADER_KEEP_PXE
371 /* Issue PXENV_UNDI_SHUTDOWN */
372 movw $PXENV_UNDI_SHUTDOWN, %bx
378 /* Etherboot treats PXENV_UNLOAD_STACK as PXENV_STOP_UNDI, so
379 * we must not issue this call if the underlying stack is
380 * Etherboot and we were not intending to issue a PXENV_STOP_UNDI.
382 #ifdef PXELOADER_KEEP_UNDI
383 testw $PXE_HACK_EB54, pxe_hacks
385 #endif /* PXELOADER_KEEP_UNDI */
386 /* Issue PXENV_UNLOAD_STACK */
387 movw $PXENV_UNLOAD_STACK, %bx
392 1: /* Free base memory used by PXE base code */
393 movw undi_fbms_start, %ax
397 andw $~( UNDI_FL_INITIALIZED | UNDI_FL_KEEP_ALL ), flags
398 #endif /* PXELOADER_KEEP_PXE */
400 /*****************************************************************************
402 *****************************************************************************
404 #ifndef PXELOADER_KEEP_UNDI
406 /* Issue PXENV_STOP_UNDI */
407 movw $PXENV_STOP_UNDI, %bx
412 1: /* Free base memory used by UNDI */
413 movw undi_fbms_end, %ax
414 movw undi_fbms_start, %bx
416 /* Clear UNDI_FL_STARTED */
417 andw $~UNDI_FL_STARTED, flags
419 #endif /* PXELOADER_KEEP_UNDI */
421 /*****************************************************************************
422 * Print remaining free base memory
423 *****************************************************************************
432 .section ".prefix.data", "aw", @progbits
434 20: .asciz "kB free base memory after PXE unload\n"
437 /*****************************************************************************
439 *****************************************************************************
444 /*****************************************************************************
445 * Subroutine: print segment:offset address
448 * %es:%bx : segment:offset address to print
449 * %ds:di : output buffer (or %di=0 to print to console)
451 * %ds:di : next character in output buffer (if applicable)
452 *****************************************************************************
455 /* Preserve registers */
457 /* Print "<segment>:offset" */
464 /* Restore registers and return */
468 /*****************************************************************************
469 * Subroutine: print decimal word
472 * %ax : word to print
473 * %ds:di : output buffer (or %di=0 to print to console)
475 * %ds:di : next character in output buffer (if applicable)
476 *****************************************************************************
479 /* Preserve registers */
484 /* Build up digit sequence on stack */
493 /* Print digit sequence */
495 call print_hex_nibble
497 /* Restore registers and return */
504 /*****************************************************************************
505 * Subroutine: zero 1kB block of base memory
508 * %bx : block to zero (in kB)
511 *****************************************************************************
514 /* Preserve registers */
527 /* Restore registers and return */
534 /*****************************************************************************
535 * Subroutine: free and zero base memory
538 * %ax : Desired new free base memory counter (in kB)
539 * %bx : Expected current free base memory counter (in kB)
540 * %fs : BIOS data segment (0x40)
544 * The base memory from %bx kB to %ax kB is unconditionally zeroed.
545 * It will be freed if and only if the expected current free base
546 * memory counter (%bx) matches the actual current free base memory
547 * counter in 0x40:0x13; if this does not match then the memory will
549 *****************************************************************************
552 /* Zero base memory */
560 /* Free base memory */
561 cmpw %fs:(0x13), %bx /* Update FBMS only if "old" value */
562 jne 1f /* is correct */
563 1: movw %ax, %fs:(0x13)
566 /*****************************************************************************
567 * Subroutine: make a PXE API call. Works with either !PXE or PXENV+ API.
570 * %bx : PXE API call number
571 * %ds:pxe_parameter_structure : Parameters for PXE API call
573 * %ax : PXE status code (not exit code)
574 * CF set if %ax is non-zero
575 *****************************************************************************
578 /* Preserve registers */
581 /* Set up registers for PXENV+ API. %bx already set up */
584 movw $pxe_parameter_structure, %di
585 /* Set up stack for !PXE API */
589 /* Make the API call */
591 /* Reset the stack */
593 movw pxe_parameter_structure, %ax
598 1: /* Clear direction flag, for the sake of sanity */
600 /* Restore registers and return */
605 /*****************************************************************************
606 * Subroutine: print PXE API call error message
609 * %ax : PXE status code
610 * %bx : PXE API call number
613 *****************************************************************************
629 .section ".prefix.data", "aw", @progbits
630 10: .asciz " UNDI API call "
631 20: .asciz " failed: status code "
635 /*****************************************************************************
636 * PXE data structures
637 *****************************************************************************
639 .section ".prefix.data"
644 pxe_parameter_structure: .fill 64
647 undi_code_size: .word 0
648 undi_code_segment: .word 0
651 undi_data_size: .word 0
652 undi_data_segment: .word 0
656 /* The following fields are part of a struct undi_device */
661 pxenv_offset: .word 0
662 pxenv_segment: .word 0
666 ppxe_segment: .word 0
669 entry_offset: .word 0
670 entry_segment: .word 0
672 undi_fbms_start: .word 0
673 undi_fbms_end: .word 0
675 pci_busdevfn: .word UNDI_NO_PCI_BUSDEVFN
676 isapnp_csn: .word UNDI_NO_ISAPNP_CSN
677 isapnp_read_port: .word UNDI_NO_ISAPNP_READ_PORT
682 .word ( UNDI_FL_INITIALIZED | UNDI_FL_STARTED | UNDI_FL_KEEP_ALL )
684 .equ undi_device_size, ( . - undi_device )
686 /*****************************************************************************
688 *****************************************************************************
695 /* Set up real-mode stack */
699 #ifdef PXELOADER_KEEP_UNDI
700 /* Copy our undi_device structure to the preloaded_undi variable */
702 movw $preloaded_undi, %di
703 movw $undi_device, %si
704 movw $undi_device_size, %cx
708 /* Retrieve PXE %ss:esp */
712 /* Jump to .text16 segment with %ds pointing to .data16 */
717 .section ".text16", "ax", @progbits
719 /* Run main program */
723 popl %ecx /* discard */
728 /* Restore PXE stack */
732 /* Check PXE stack magic */
734 cmpl $STACK_MAGIC, %eax
737 /* PXE stack OK: return to caller */
744 xorw %ax, %ax /* Return success */
747 1: /* PXE stack corrupt or removed: use INT 18 */