1 #define PXENV_UNDI_SHUTDOWN 0x0005
2 #define PXENV_UNDI_GET_NIC_TYPE 0x0012
3 #define PXENV_STOP_UNDI 0x0015
4 #define PXENV_UNLOAD_STACK 0x0070
13 #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
15 /*****************************************************************************
16 * Entry point: set operating context, print welcome message
17 *****************************************************************************
19 .section ".prefix", "ax", @progbits
22 /* Preserve registers for possible return to PXE */
30 /* Store magic word on PXE stack and remember PXE %ss:esp */
33 movl %esp, %cs:pxe_esp
38 movw $0x40, %ax /* BIOS data segment access */
40 /* Set up stack just below 0x7c00 */
44 /* Clear direction flag, for the sake of sanity */
46 /* Print welcome message */
50 .section ".prefix.data", "aw", @progbits
54 /*****************************************************************************
55 * Find us a usable !PXE or PXENV+ entry point
56 *****************************************************************************
59 /* Plan A: !PXE pointer from the stack */
60 lgsl pxe_esp, %ebp /* %gs:%bp -> original stack */
65 /* Plan B: PXENV+ pointer from initial ES:BX */
71 /* Plan C: PXENV+ structure via INT 1Ah */
80 /* Plan D: scan base memory for !PXE */
84 /* Plan E: scan base memory for PXENV+ */
85 call memory_scan_pxenv
89 movw %bx, pxenv_offset
90 movw %es, pxenv_segment
92 cmpw $0x201, %es:6(%bx) /* API version >= 2.01 */
94 cmpb $0x2c, %es:8(%bx) /* ... and structure long enough */
97 lesw %es:0x28(%bx), %bx /* Find !PXE from PXENV+ */
101 call memory_scan_ppxe /* We are *supposed* to have !PXE... */
104 lesw pxenv_segoff, %bx /* Nope, we're stuck with PXENV+ */
106 /* Record entry point and UNDI segments */
107 pushl %es:0x0a(%bx) /* Entry point */
109 pushw %es:0x24(%bx) /* UNDI code segment */
110 pushw %es:0x26(%bx) /* UNDI code size */
111 popl undi_code_segoff
112 pushw %es:0x20(%bx) /* UNDI data segment */
113 pushw %es:0x22(%bx) /* UNDI data size */
114 popl undi_data_segoff
116 /* 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 */
133 pushw %es:0x30(%bx) /* UNDI code segment */
134 pushw %es:0x36(%bx) /* UNDI code size */
135 popl undi_code_segoff
136 pushw %es:0x28(%bx) /* UNDI data segment */
137 pushw %es:0x2e(%bx) /* UNDI data size */
138 popl undi_data_segoff
139 /* Print "!PXE at <address>" */
146 .section ".prefix.data", "aw", @progbits
147 10: .asciz " !PXE at "
151 cmpl $0x45585021, %es:(%bx)
153 movzbw %es:4(%bx), %cx
155 jae is_valid_checksum
160 cmpl $0x4e455850, %es:(%bx)
162 cmpw $0x2b56, %es:4(%bx)
164 movzbw %es:8(%bx), %cx
180 movw $is_valid_ppxe, %dx
181 jmp memory_scan_common
184 movw $is_valid_pxenv, %dx
191 cmpw $( 0xa000 - 1 ), %ax
199 /*****************************************************************************
200 * Sanity check: we must have an entry point
201 *****************************************************************************
204 /* Check for entry point */
205 movl entry_segoff, %eax
208 /* No entry point: print message and skip everything else */
213 .section ".prefix.data", "aw", @progbits
214 10: .asciz " No PXE stack found!\n"
218 /*****************************************************************************
219 * Calculate base memory usage by UNDI
220 *****************************************************************************
222 find_undi_basemem_usage:
223 movw undi_code_segment, %ax
224 movw undi_code_size, %bx
225 movw undi_data_segment, %cx
226 movw undi_data_size, %dx
231 1: /* %ax:%bx now describes the lower region, %cx:%dx the higher */
232 shrw $6, %ax /* Round down to nearest kB */
233 movw %ax, undi_fbms_start
234 addw $0x0f, %dx /* Round up to next segment */
237 addw $((1024 / 16) - 1), %cx /* Round up to next kB */
239 movw %cx, undi_fbms_end
241 /*****************************************************************************
242 * Print information about detected PXE stack
243 *****************************************************************************
245 print_structure_information:
246 /* Print entry point */
249 les entry_segoff, %bx
251 .section ".prefix.data", "aw", @progbits
252 10: .asciz " entry point at "
254 /* Print UNDI code segment */
257 les undi_code_segoff, %bx
259 .section ".prefix.data", "aw", @progbits
260 10: .asciz "\n UNDI code segment "
262 /* Print UNDI data segment */
265 les undi_data_segoff, %bx
267 .section ".prefix.data", "aw", @progbits
268 10: .asciz ", data segment "
270 /* Print UNDI memory usage */
273 movw undi_fbms_start, %ax
277 movw undi_fbms_end, %ax
281 .section ".prefix.data", "aw", @progbits
286 /*****************************************************************************
287 * Determine physical device
288 *****************************************************************************
291 /* Issue PXENV_UNDI_GET_NIC_TYPE */
292 movw $PXENV_UNDI_GET_NIC_TYPE, %bx
296 jmp no_physical_device
297 1: /* Determine physical device type */
298 movb ( pxe_parameter_structure + 0x02 ), %al
300 je pci_physical_device
301 jmp no_physical_device
304 /* Record PCI bus:dev.fn and vendor/device IDs */
305 movl ( pxe_parameter_structure + 0x03 ), %eax
306 movl %eax, pci_vendor
307 movw ( pxe_parameter_structure + 0x0b ), %ax
308 movw %ax, pci_busdevfn
311 call print_pci_busdevfn
315 .section ".prefix.data", "aw", @progbits
316 10: .asciz " UNDI device is PCI "
320 /* No device found, or device type not understood */
323 .section ".prefix.data", "aw", @progbits
324 10: .asciz " Unable to determine UNDI physical device\n"
329 /*****************************************************************************
330 * Leave NIC in a safe state
331 *****************************************************************************
333 #ifndef PXELOADER_KEEP_PXE
335 /* Issue PXENV_UNDI_SHUTDOWN */
336 movw $PXENV_UNDI_SHUTDOWN, %bx
342 /* Issue PXENV_UNLOAD_STACK */
343 movw $PXENV_UNLOAD_STACK, %bx
348 1: /* Free base memory used by PXE base code */
349 movw undi_fbms_start, %ax
353 andw $~( UNDI_FL_INITIALIZED | UNDI_FL_KEEP_ALL ), flags
354 #endif /* PXELOADER_KEEP_PXE */
356 /*****************************************************************************
358 *****************************************************************************
360 #ifndef PXELOADER_KEEP_UNDI
362 /* Issue PXENV_STOP_UNDI */
363 movw $PXENV_STOP_UNDI, %bx
368 1: /* Free base memory used by UNDI */
369 movw undi_fbms_end, %ax
370 movw undi_fbms_start, %bx
372 /* Clear UNDI_FL_STARTED */
373 andw $~UNDI_FL_STARTED, flags
375 #endif /* PXELOADER_KEEP_UNDI */
377 /*****************************************************************************
378 * Print remaining free base memory
379 *****************************************************************************
388 .section ".prefix.data", "aw", @progbits
390 20: .asciz "kB free base memory after PXE unload\n"
393 /*****************************************************************************
395 *****************************************************************************
400 /*****************************************************************************
401 * Subroutine: print segment:offset address
404 * %es:%bx : segment:offset address to print
405 * %ds:di : output buffer (or %di=0 to print to console)
407 * %ds:di : next character in output buffer (if applicable)
408 *****************************************************************************
411 /* Preserve registers */
413 /* Print "<segment>:offset" */
420 /* Restore registers and return */
424 /*****************************************************************************
425 * Subroutine: print decimal word
428 * %ax : word to print
429 * %ds:di : output buffer (or %di=0 to print to console)
431 * %ds:di : next character in output buffer (if applicable)
432 *****************************************************************************
435 /* Preserve registers */
440 /* Build up digit sequence on stack */
449 /* Print digit sequence */
451 call print_hex_nibble
453 /* Restore registers and return */
460 /*****************************************************************************
461 * Subroutine: zero 1kB block of base memory
464 * %bx : block to zero (in kB)
467 *****************************************************************************
470 /* Preserve registers */
483 /* Restore registers and return */
490 /*****************************************************************************
491 * Subroutine: free and zero base memory
494 * %ax : Desired new free base memory counter (in kB)
495 * %bx : Expected current free base memory counter (in kB)
496 * %fs : BIOS data segment (0x40)
500 * The base memory from %bx kB to %ax kB is unconditionally zeroed.
501 * It will be freed if and only if the expected current free base
502 * memory counter (%bx) matches the actual current free base memory
503 * counter in 0x40:0x13; if this does not match then the memory will
505 *****************************************************************************
508 /* Zero base memory */
516 /* Free base memory */
517 cmpw %fs:(0x13), %bx /* Update FBMS only if "old" value */
518 jne 1f /* is correct */
519 1: movw %ax, %fs:(0x13)
522 /*****************************************************************************
523 * Subroutine: make a PXE API call. Works with either !PXE or PXENV+ API.
526 * %bx : PXE API call number
527 * %ds:pxe_parameter_structure : Parameters for PXE API call
529 * %ax : PXE status code (not exit code)
530 * CF set if %ax is non-zero
531 *****************************************************************************
534 /* Preserve registers */
537 /* Set up registers for PXENV+ API. %bx already set up */
540 movw $pxe_parameter_structure, %di
541 /* Set up stack for !PXE API */
545 /* Make the API call */
547 /* Reset the stack */
549 movw pxe_parameter_structure, %ax
554 1: /* Restore registers and return */
559 /*****************************************************************************
560 * Subroutine: print PXE API call error message
563 * %ax : PXE status code
564 * %bx : PXE API call number
567 *****************************************************************************
583 .section ".prefix.data", "aw", @progbits
584 10: .asciz " UNDI API call "
585 20: .asciz " failed: status code "
589 /*****************************************************************************
590 * PXE data structures
591 *****************************************************************************
593 .section ".prefix.data"
598 pxe_parameter_structure: .fill 20
601 undi_code_size: .word 0
602 undi_code_segment: .word 0
605 undi_data_size: .word 0
606 undi_data_segment: .word 0
608 /* The following fields are part of a struct undi_device */
613 pxenv_offset: .word 0
614 pxenv_segment: .word 0
618 ppxe_segment: .word 0
621 entry_offset: .word 0
622 entry_segment: .word 0
624 undi_fbms_start: .word 0
625 undi_fbms_end: .word 0
627 pci_busdevfn: .word UNDI_NO_PCI_BUSDEVFN
628 isapnp_csn: .word UNDI_NO_ISAPNP_CSN
629 isapnp_read_port: .word UNDI_NO_ISAPNP_READ_PORT
634 .word ( UNDI_FL_INITIALIZED | UNDI_FL_STARTED | UNDI_FL_KEEP_ALL )
636 .equ undi_device_size, ( . - undi_device )
638 /*****************************************************************************
640 *****************************************************************************
647 /* Set up real-mode stack */
651 #ifdef PXELOADER_KEEP_UNDI
652 /* Copy our undi_device structure to the preloaded_undi variable */
654 movw $preloaded_undi, %di
655 movw $undi_device, %si
656 movw $undi_device_size, %cx
660 /* Retrieve PXE %ss:esp */
664 /* Jump to .text16 segment with %ds pointing to .data16 */
669 .section ".text16", "ax", @progbits
671 /* Run main program */
675 popl %ecx /* discard */
680 /* Restore PXE stack */
684 /* Check PXE stack magic */
686 cmpl $STACK_MAGIC, %eax
689 /* PXE stack OK: return to caller */
696 xorw %ax, %ax /* Return success */
699 1: /* PXE stack corrupt or removed: use INT 18 */