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
6 #define PXE_STACK_MAGIC 0x57ac /* 'STac' */
11 .section ".prefix", "ax", @progbits
12 .section ".prefix.data", "aw", @progbits
15 /*****************************************************************************
16 * Entry point: set operating context, print welcome message
17 *****************************************************************************
21 1: /* Preserve registers for return to PXE stack */
30 pushw $PXE_STACK_MAGIC /* PXE stack magic marker */
31 /* Set up stack just below 0x7c00 */
34 movw %sp, %bp /* %gs:%bp points to old PXE stack */
38 pushw %gs /* Save old PXE stack pointer */
40 /* Set up our other segment registers */
43 movw $0x40, %ax /* BIOS data segment access */
45 /* Clear direction flag, for the sake of sanity */
47 /* Print welcome message */
50 .section ".prefix.data"
54 /*****************************************************************************
55 * Verify PXENV+ structure and record parameters of interest
56 *****************************************************************************
60 cmpl $0x4e455850, %es:(%bx) /* 'PXEN' signature */
62 cmpw $0x2b56, %es:4(%bx) /* 'V+' signature */
64 /* Record structure address, entry point, and UNDI segments */
67 movw %bx, pxenv_offset
68 pushl %es:0x0a(%bx) /* Entry point */
70 pushw %es:0x24(%bx) /* UNDI code segment */
71 pushw %es:0x26(%bx) /* UNDI code size */
73 pushw %es:0x20(%bx) /* UNDI data segment */
74 pushw %es:0x22(%bx) /* UNDI data size */
76 /* Print "PXENV+ at <address>" */
83 .section ".prefix.data"
84 10: .asciz " PXENV+ at "
88 /*****************************************************************************
89 * Verify !PXE structure and record parameters of interest
90 *****************************************************************************
94 les %gs:54(%bp), %di /* !PXE structure */
95 cmpl $0x45585021, %es:(%di) /* '!PXE' signature */
97 /* Record structure address, entry point, and UNDI segments */
100 movw %di, ppxe_offset
101 pushl %es:0x10(%di) /* Entry point */
103 pushw %es:0x30(%di) /* UNDI code segment */
104 pushw %es:0x36(%di) /* UNDI code size */
105 popl undi_code_segoff
106 pushw %es:0x28(%di) /* UNDI data segment */
107 pushw %es:0x2e(%di) /* UNDI data size */
108 popl undi_data_segoff
109 /* Print "!PXE at <address>" */
115 .section ".prefix.data"
116 10: .asciz " !PXE at "
120 /*****************************************************************************
121 * Sanity check: we must have an entry point
122 *****************************************************************************
125 /* Check for entry point */
126 movl entry_segoff, %eax
129 /* No entry point: print message and skip everything else */
133 .section ".prefix.data"
134 10: .asciz " No PXE stack found!\n"
138 /*****************************************************************************
139 * Calculate base memory usage by UNDI
140 *****************************************************************************
142 find_undi_basemem_usage:
143 movw undi_code_segment, %ax
144 movw undi_code_size, %bx
145 movw undi_data_segment, %cx
146 movw undi_data_size, %dx
151 1: /* %ax:%bx now describes the lower region, %cx:%dx the higher */
152 shrw $6, %ax /* Round down to nearest kB */
153 movw %ax, undi_fbms_start
154 addw $0x0f, %dx /* Round up to next segment */
157 addw $((1024 / 16) - 1), %cx /* Round up to next kB */
159 movw %cx, undi_fbms_end
161 /*****************************************************************************
162 * Print information about detected PXE stack
163 *****************************************************************************
165 print_structure_information:
166 /* Print entry point */
169 les entry_segoff, %di
171 .section ".prefix.data"
172 10: .asciz " entry point at "
174 /* Print UNDI code segment */
177 les undi_code_segoff, %di
179 .section ".prefix.data"
180 10: .asciz "\n UNDI code segment "
182 /* Print UNDI data segment */
185 les undi_data_segoff, %di
187 .section ".prefix.data"
188 10: .asciz ", data segment "
190 /* Print UNDI memory usage */
193 movw undi_fbms_start, %ax
197 movw undi_fbms_end, %ax
201 .section ".prefix.data"
206 /*****************************************************************************
207 * Determine physical device
208 *****************************************************************************
211 /* Issue PXENV_UNDI_GET_NIC_TYPE */
212 movw $PXENV_UNDI_GET_NIC_TYPE, %bx
216 jmp no_physical_device
217 1: /* Determine physical device type */
218 movb ( pxe_parameter_structure + 0x02 ), %al
220 je pci_physical_device
221 jmp no_physical_device
224 /* Record PCI bus:dev.fn */
225 movw ( pxe_parameter_structure + 0x0b ), %ax
226 movw %ax, pci_busdevfn
229 call print_pci_busdevfn
233 .section ".prefix.data"
234 10: .asciz " UNDI device is PCI "
238 /* No device found, or device type not understood */
241 .section ".prefix.data"
242 10: .asciz " Unable to determine UNDI physical device\n"
247 /*****************************************************************************
248 * Leave NIC in a safe state
249 *****************************************************************************
252 /* Issue PXENV_UNDI_SHUTDOWN */
253 movw $PXENV_UNDI_SHUTDOWN, %bx
259 /*****************************************************************************
260 * Unload PXE base code
261 *****************************************************************************
264 /* Issue PXENV_UNLOAD_STACK */
265 movw $PXENV_UNLOAD_STACK, %bx
270 1: /* Free base memory used by PXE base code */
272 movw undi_fbms_start, %di
276 /*****************************************************************************
278 *****************************************************************************
281 /* Issue PXENV_STOP_UNDI */
282 movw $PXENV_STOP_UNDI, %bx
287 1: /* Free base memory used by UNDI */
288 #ifndef PXELOADER_KEEP_UNDI
289 movw undi_fbms_start, %si
290 movw undi_fbms_end, %di
292 #endif /* PXELOADER_KEEP_UNDI */
295 /*****************************************************************************
296 * Print remaining free base memory
297 *****************************************************************************
306 .section ".prefix.data"
308 20: .asciz "kB free base memory after PXE unload\n"
311 /*****************************************************************************
313 *****************************************************************************
318 /*****************************************************************************
319 * Subroutine: print character (with LF -> LF,CR translation)
322 * %al : character to print
325 *****************************************************************************
328 /* Preserve registers */
332 /* Print character */
333 movw $0x0007, %bx /* page 0, attribute 7 (normal) */
334 movb $0x0e, %ah /* write char, tty mode */
335 cmpb $0x0a, %al /* '\n'? */
340 /* Restore registers and return */
346 /*****************************************************************************
347 * Subroutine: print a NUL-terminated string
350 * %ds:%si : string to print
353 *****************************************************************************
356 /* Preserve registers */
365 2: /* Restore registers and return */
370 /*****************************************************************************
371 * Subroutine: print hex digit
374 * %al (low nibble) : digit to print
377 *****************************************************************************
380 /* Preserve registers */
382 /* Print digit (technique by Norbert Juffa <norbert.juffa@amd.com> */
388 /* Restore registers and return */
392 /*****************************************************************************
393 * Subroutine: print hex byte
396 * %al : byte to print
399 *****************************************************************************
403 call print_hex_nibble
405 call print_hex_nibble
408 /*****************************************************************************
409 * Subroutine: print hex word
412 * %ax : word to print
415 *****************************************************************************
424 /*****************************************************************************
425 * Subroutine: print segment:offset address
428 * %es:%di : segment:offset address to print
431 *****************************************************************************
434 /* Preserve registers */
436 /* Print "<segment>:offset" */
443 /* Restore registers and return */
447 /*****************************************************************************
448 * Subroutine: print decimal word
451 * %ax : word to print
454 *****************************************************************************
457 /* Preserve registers */
462 /* Build up digit sequence on stack */
471 /* Print digit sequence */
473 call print_hex_nibble
475 /* Restore registers and return */
482 /*****************************************************************************
483 * Subroutine: print PCI bus:dev.fn
486 * %ax : PCI bus:dev.fn to print
489 *****************************************************************************
492 /* Preserve registers */
510 call print_hex_nibble
511 /* Restore registers and return */
515 /*****************************************************************************
516 * Subroutine: zero 1kB block of base memory
519 * %si : block to zero (in kB)
522 *****************************************************************************
525 /* Preserve registers */
538 /* Restore registers and return */
545 /*****************************************************************************
546 * Subroutine: free and zero base memory
549 * %si : Expected current free base memory counter (in kB)
550 * %di : Desired new free base memory counter (in kB)
551 * %fs : BIOS data segment (0x40)
553 * %ax : Actual new free base memory counter (in kB)
555 * The base memory from %si kB to %di kB is unconditionally zeroed.
556 * It will be freed if and only if the expected current free base
557 * memory counter (%si) matches the actual current free base memory
558 * counter in 0x40:0x13; if this does not match then the memory will
560 *****************************************************************************
563 /* Zero base memory */
571 /* Free base memory */
572 movw %fs:(0x13), %ax /* Current FBMS to %ax */
573 cmpw %ax, %si /* Update FBMS only if "old" value */
574 jne 1f /* is correct */
576 1: movw %ax, %fs:(0x13)
579 /*****************************************************************************
580 * Subroutine: make a PXE API call. Works with either !PXE or PXENV+ API.
583 * %bx : PXE API call number
584 * %ds:pxe_parameter_structure : Parameters for PXE API call
586 * %ax : PXE status code (not exit code)
587 * CF set if %ax is non-zero
588 *****************************************************************************
591 /* Preserve registers */
594 /* Set up registers for PXENV+ API. %bx already set up */
597 movw $pxe_parameter_structure, %di
598 /* Set up stack for !PXE API */
602 /* Make the API call */
604 /* Reset the stack */
606 movw pxe_parameter_structure, %ax
611 1: /* Restore registers and return */
616 /*****************************************************************************
617 * Subroutine: print PXE API call error message
620 * %ax : PXE status code
621 * %bx : PXE API call number
624 *****************************************************************************
640 .section ".prefix.data"
641 10: .asciz " UNDI API call "
642 20: .asciz " failed: status code "
646 /*****************************************************************************
647 * PXE data structures
648 *****************************************************************************
651 pxe_parameter_structure: .fill 20
654 undi_code_size: .word 0
655 undi_code_segment: .word 0
658 undi_data_size: .word 0
659 undi_data_segment: .word 0
661 /* The following fields are part of a struct undi_device */
666 pxenv_offset: .word 0
667 pxenv_segment: .word 0
671 ppxe_segment: .word 0
674 entry_offset: .word 0
675 entry_segment: .word 0
677 undi_fbms_start: .word 0
678 undi_fbms_end: .word 0
680 pci_busdevfn: .word 0xffff
681 isapnp_csn: .word 0xffff
682 isapnp_read_port: .word 0xffff
684 .equ undi_device_size, ( . - undi_device )
686 /*****************************************************************************
687 * Run Etherboot main code
688 *****************************************************************************
691 /* Install Etherboot */
694 #ifdef PXELOADER_KEEP_UNDI
695 /* Copy our undi_device structure to the preloaded_undi variable */
697 movw $preloaded_undi, %di
698 movw $undi_device, %si
699 movw $undi_device_size, %cx
703 /* Jump to .text16 segment with %ds pointing to .data16*/
708 .section ".text16", "ax", @progbits
710 /* Original PXE stack pointer to es:di. We must hold it in
711 * registers, because our current stack may be vapourised by
712 * the time main() returns. (main() will still be able to
713 * return, because prot_call() transfers the return address to
714 * the internal stack and back again).
719 /* Run main program */
723 popl %eax /* discard */
725 /* If original PXE stack is intact, return via PXE, else via INT 18 */
726 cmpw $PXE_STACK_MAGIC, %es:0(%di)
728 exit_via_pxe: /* Stack OK, return to PXE */
729 movw $exit_via_pxe_message, %si
730 call print_exit_message
731 pushw %es /* Restore original PXE stack */
734 popw %ax /* discard PXE_STACK_MAGIC */
735 popw %ax /* discard %cs */
736 popw %ax /* discard %ss */
743 xorw %ax, %ax /* Return PXENV_STATUS_SUCCESS */
745 exit_via_int18: /* Stack damaged, do int 18 */
746 movw $exit_via_int18_message, %si
747 call print_exit_message
751 movw $0x0007, %bx /* page 0, attribute 7 (normal) */
752 movb $0x0e, %ah /* write char, tty mode */
760 .section ".data16", "aw", @progbits
761 exit_via_pxe_message:
763 exit_via_int18_message:
764 .asciz "EB->BIOS\r\n"