diff options
Diffstat (limited to 'contrib/syslinux-4.02/gpxe/src/arch/i386/prefix/pxeprefix.S')
-rw-r--r-- | contrib/syslinux-4.02/gpxe/src/arch/i386/prefix/pxeprefix.S | 761 |
1 files changed, 761 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/gpxe/src/arch/i386/prefix/pxeprefix.S b/contrib/syslinux-4.02/gpxe/src/arch/i386/prefix/pxeprefix.S new file mode 100644 index 0000000..e728c48 --- /dev/null +++ b/contrib/syslinux-4.02/gpxe/src/arch/i386/prefix/pxeprefix.S @@ -0,0 +1,761 @@ +FILE_LICENCE ( GPL2_OR_LATER ) + +#define PXENV_UNDI_SHUTDOWN 0x0005 +#define PXENV_UNDI_GET_NIC_TYPE 0x0012 +#define PXENV_UNDI_GET_IFACE_INFO 0x0013 +#define PXENV_STOP_UNDI 0x0015 +#define PXENV_UNLOAD_STACK 0x0070 + +#define PXE_HACK_EB54 0x0001 + + .text + .arch i386 + .org 0 + .code16 + +#include <undi.h> + +#define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) ) +#define EB_MAGIC_1 ( 'E' + ( 't' << 8 ) + ( 'h' << 16 ) + ( 'e' << 24 ) ) +#define EB_MAGIC_2 ( 'r' + ( 'b' << 8 ) + ( 'o' << 16 ) + ( 'o' << 24 ) ) + +/***************************************************************************** + * Entry point: set operating context, print welcome message + ***************************************************************************** + */ + .section ".prefix", "ax", @progbits + jmp $0x7c0, $1f +1: + /* Preserve registers for possible return to PXE */ + pushfl + pushal + pushw %gs + pushw %fs + pushw %es + pushw %ds + + /* Store magic word on PXE stack and remember PXE %ss:esp */ + pushl $STACK_MAGIC + movw %ss, %cs:pxe_ss + movl %esp, %cs:pxe_esp + + /* Set up segments */ + movw %cs, %ax + movw %ax, %ds + movw $0x40, %ax /* BIOS data segment access */ + movw %ax, %fs + /* Set up stack just below 0x7c00 */ + xorw %ax, %ax + movw %ax, %ss + movl $0x7c00, %esp + /* Clear direction flag, for the sake of sanity */ + cld + /* Print welcome message */ + movw $10f, %si + xorw %di, %di + call print_message + .section ".prefix.data", "aw", @progbits +10: .asciz "PXE->EB:" + .previous + +/***************************************************************************** + * Find us a usable !PXE or PXENV+ entry point + ***************************************************************************** + */ +detect_pxe: + /* Plan A: !PXE pointer from the stack */ + lgsl pxe_esp, %ebp /* %gs:%bp -> original stack */ + lesw %gs:52(%bp), %bx + call is_valid_ppxe + je have_ppxe + + /* Plan B: PXENV+ pointer from initial ES:BX */ + movw %gs:32(%bp),%bx + movw %gs:8(%bp),%es + call is_valid_pxenv + je have_pxenv + + /* Plan C: PXENV+ structure via INT 1Ah */ + movw $0x5650, %ax + int $0x1a + jc 1f + cmpw $0x564e, %ax + jne 1f + call is_valid_pxenv + je have_pxenv +1: + /* Plan D: scan base memory for !PXE */ + call memory_scan_ppxe + je have_ppxe + + /* Plan E: scan base memory for PXENV+ */ + call memory_scan_pxenv + jne stack_not_found + +have_pxenv: + movw %bx, pxenv_offset + movw %es, pxenv_segment + + cmpw $0x201, %es:6(%bx) /* API version >= 2.01 */ + jb 1f + cmpb $0x2c, %es:8(%bx) /* ... and structure long enough */ + jb 2f + + lesw %es:0x28(%bx), %bx /* Find !PXE from PXENV+ */ + call is_valid_ppxe + je have_ppxe +2: + call memory_scan_ppxe /* We are *supposed* to have !PXE... */ + je have_ppxe +1: + lesw pxenv_segoff, %bx /* Nope, we're stuck with PXENV+ */ + + /* Record entry point and UNDI segments */ + pushl %es:0x0a(%bx) /* Entry point */ + pushw %es:0x24(%bx) /* UNDI code segment */ + pushw %es:0x26(%bx) /* UNDI code size */ + pushw %es:0x20(%bx) /* UNDI data segment */ + pushw %es:0x22(%bx) /* UNDI data size */ + + /* Print "PXENV+ at <address>" */ + movw $10f, %si + jmp check_have_stack + .section ".prefix.data", "aw", @progbits +10: .asciz " PXENV+ at " + .previous + +have_ppxe: + movw %bx, ppxe_offset + movw %es, ppxe_segment + + pushl %es:0x10(%bx) /* Entry point */ + pushw %es:0x30(%bx) /* UNDI code segment */ + pushw %es:0x36(%bx) /* UNDI code size */ + pushw %es:0x28(%bx) /* UNDI data segment */ + pushw %es:0x2e(%bx) /* UNDI data size */ + + /* Print "!PXE at <address>" */ + movw $10f, %si + jmp check_have_stack + .section ".prefix.data", "aw", @progbits +10: .asciz " !PXE at " + .previous + +is_valid_ppxe: + cmpl $0x45585021, %es:(%bx) + jne 1f + movzbw %es:4(%bx), %cx + cmpw $0x58, %cx + jae is_valid_checksum +1: + ret + +is_valid_pxenv: + cmpl $0x4e455850, %es:(%bx) + jne 1b + cmpw $0x2b56, %es:4(%bx) + jne 1b + movzbw %es:8(%bx), %cx + cmpw $0x28, %cx + jb 1b + +is_valid_checksum: + pushw %ax + movw %bx, %si + xorw %ax, %ax +2: + es lodsb + addb %al, %ah + loopw 2b + popw %ax + ret + +memory_scan_ppxe: + movw $is_valid_ppxe, %dx + jmp memory_scan_common + +memory_scan_pxenv: + movw $is_valid_pxenv, %dx + +memory_scan_common: + movw %fs:(0x13), %ax + shlw $6, %ax + decw %ax +1: incw %ax + cmpw $( 0xa000 - 1 ), %ax + ja 2f + movw %ax, %es + xorw %bx, %bx + call *%dx + jne 1b +2: ret + +/***************************************************************************** + * Sanity check: we must have an entry point + ***************************************************************************** + */ +check_have_stack: + /* Save common values pushed onto the stack */ + popl undi_data_segoff + popl undi_code_segoff + popl entry_segoff + + /* Print have !PXE/PXENV+ message; structure pointer in %es:%bx */ + call print_message + call print_segoff + movb $( ',' ), %al + call print_character + + /* Check for entry point */ + movl entry_segoff, %eax + testl %eax, %eax + jnz 99f + /* No entry point: print message and skip everything else */ +stack_not_found: + movw $10f, %si + call print_message + jmp finished + .section ".prefix.data", "aw", @progbits +10: .asciz " No PXE stack found!\n" + .previous +99: + +/***************************************************************************** + * Calculate base memory usage by UNDI + ***************************************************************************** + */ +find_undi_basemem_usage: + movw undi_code_segment, %ax + movw undi_code_size, %bx + movw undi_data_segment, %cx + movw undi_data_size, %dx + cmpw %ax, %cx + ja 1f + xchgw %ax, %cx + xchgw %bx, %dx +1: /* %ax:%bx now describes the lower region, %cx:%dx the higher */ + shrw $6, %ax /* Round down to nearest kB */ + movw %ax, undi_fbms_start + addw $0x0f, %dx /* Round up to next segment */ + shrw $4, %dx + addw %dx, %cx + addw $((1024 / 16) - 1), %cx /* Round up to next kB */ + shrw $6, %cx + movw %cx, undi_fbms_end + +/***************************************************************************** + * Print information about detected PXE stack + ***************************************************************************** + */ +print_structure_information: + /* Print entry point */ + movw $10f, %si + call print_message + les entry_segoff, %bx + call print_segoff + .section ".prefix.data", "aw", @progbits +10: .asciz " entry point at " + .previous + /* Print UNDI code segment */ + movw $10f, %si + call print_message + les undi_code_segoff, %bx + call print_segoff + .section ".prefix.data", "aw", @progbits +10: .asciz "\n UNDI code segment " + .previous + /* Print UNDI data segment */ + movw $10f, %si + call print_message + les undi_data_segoff, %bx + call print_segoff + .section ".prefix.data", "aw", @progbits +10: .asciz ", data segment " + .previous + /* Print UNDI memory usage */ + movw $10f, %si + call print_message + movw undi_fbms_start, %ax + call print_word + movb $( '-' ), %al + call print_character + movw undi_fbms_end, %ax + call print_word + movw $20f, %si + call print_message + .section ".prefix.data", "aw", @progbits +10: .asciz " (" +20: .asciz "kB)\n" + .previous + +/***************************************************************************** + * Determine physical device + ***************************************************************************** + */ +get_physical_device: + /* Issue PXENV_UNDI_GET_NIC_TYPE */ + movw $PXENV_UNDI_GET_NIC_TYPE, %bx + call pxe_call + jnc 1f + call print_pxe_error + jmp no_physical_device +1: /* Determine physical device type */ + movb ( pxe_parameter_structure + 0x02 ), %al + cmpb $2, %al + je pci_physical_device + jmp no_physical_device + +pci_physical_device: + /* Record PCI bus:dev.fn and vendor/device IDs */ + movl ( pxe_parameter_structure + 0x03 ), %eax + movl %eax, pci_vendor + movw ( pxe_parameter_structure + 0x0b ), %ax + movw %ax, pci_busdevfn + movw $10f, %si + call print_message + call print_pci_busdevfn + jmp 99f + .section ".prefix.data", "aw", @progbits +10: .asciz " UNDI device is PCI " + .previous + +no_physical_device: + /* No device found, or device type not understood */ + movw $10f, %si + call print_message + .section ".prefix.data", "aw", @progbits +10: .asciz " Unable to determine UNDI physical device" + .previous + +99: + +/***************************************************************************** + * Determine interface type + ***************************************************************************** + */ +get_iface_type: + /* Issue PXENV_UNDI_GET_IFACE_INFO */ + movw $PXENV_UNDI_GET_IFACE_INFO, %bx + call pxe_call + jnc 1f + call print_pxe_error + jmp 99f +1: /* Print interface type */ + movw $10f, %si + call print_message + leaw ( pxe_parameter_structure + 0x02 ), %si + call print_message + .section ".prefix.data", "aw", @progbits +10: .asciz ", type " + .previous + /* Check for "Etherboot" interface type */ + cmpl $EB_MAGIC_1, ( pxe_parameter_structure + 0x02 ) + jne 99f + cmpl $EB_MAGIC_2, ( pxe_parameter_structure + 0x06 ) + jne 99f + movw $10f, %si + call print_message + .section ".prefix.data", "aw", @progbits +10: .asciz " (workaround enabled)" + .previous + /* Flag Etherboot workarounds as required */ + orw $PXE_HACK_EB54, pxe_hacks + +99: movb $0x0a, %al + call print_character + +/***************************************************************************** + * Leave NIC in a safe state + ***************************************************************************** + */ +#ifndef PXELOADER_KEEP_PXE +shutdown_nic: + /* Issue PXENV_UNDI_SHUTDOWN */ + movw $PXENV_UNDI_SHUTDOWN, %bx + call pxe_call + jnc 1f + call print_pxe_error +1: +unload_base_code: + /* Etherboot treats PXENV_UNLOAD_STACK as PXENV_STOP_UNDI, so + * we must not issue this call if the underlying stack is + * Etherboot and we were not intending to issue a PXENV_STOP_UNDI. + */ +#ifdef PXELOADER_KEEP_UNDI + testw $PXE_HACK_EB54, pxe_hacks + jnz 99f +#endif /* PXELOADER_KEEP_UNDI */ + /* Issue PXENV_UNLOAD_STACK */ + movw $PXENV_UNLOAD_STACK, %bx + call pxe_call + jnc 1f + call print_pxe_error + jmp 99f +1: /* Free base memory used by PXE base code */ + movw undi_fbms_start, %ax + movw %fs:(0x13), %bx + call free_basemem +99: + andw $~( UNDI_FL_INITIALIZED | UNDI_FL_KEEP_ALL ), flags +#endif /* PXELOADER_KEEP_PXE */ + +/***************************************************************************** + * Unload UNDI driver + ***************************************************************************** + */ +#ifndef PXELOADER_KEEP_UNDI +unload_undi: + /* Issue PXENV_STOP_UNDI */ + movw $PXENV_STOP_UNDI, %bx + call pxe_call + jnc 1f + call print_pxe_error + jmp 99f +1: /* Free base memory used by UNDI */ + movw undi_fbms_end, %ax + movw undi_fbms_start, %bx + call free_basemem + /* Clear UNDI_FL_STARTED */ + andw $~UNDI_FL_STARTED, flags +99: +#endif /* PXELOADER_KEEP_UNDI */ + +/***************************************************************************** + * Print remaining free base memory + ***************************************************************************** + */ +print_free_basemem: + movw $10f, %si + call print_message + movw %fs:(0x13), %ax + call print_word + movw $20f, %si + call print_message + .section ".prefix.data", "aw", @progbits +10: .asciz " " +20: .asciz "kB free base memory after PXE unload\n" + .previous + +/***************************************************************************** + * Exit point + ***************************************************************************** + */ +finished: + jmp run_gpxe + +/***************************************************************************** + * Subroutine: print segment:offset address + * + * Parameters: + * %es:%bx : segment:offset address to print + * %ds:di : output buffer (or %di=0 to print to console) + * Returns: + * %ds:di : next character in output buffer (if applicable) + ***************************************************************************** + */ +print_segoff: + /* Preserve registers */ + pushw %ax + /* Print "<segment>:offset" */ + movw %es, %ax + call print_hex_word + movb $( ':' ), %al + call print_character + movw %bx, %ax + call print_hex_word + /* Restore registers and return */ + popw %ax + ret + +/***************************************************************************** + * Subroutine: print decimal word + * + * Parameters: + * %ax : word to print + * %ds:di : output buffer (or %di=0 to print to console) + * Returns: + * %ds:di : next character in output buffer (if applicable) + ***************************************************************************** + */ +print_word: + /* Preserve registers */ + pushw %ax + pushw %bx + pushw %cx + pushw %dx + /* Build up digit sequence on stack */ + movw $10, %bx + xorw %cx, %cx +1: xorw %dx, %dx + divw %bx, %ax + pushw %dx + incw %cx + testw %ax, %ax + jnz 1b + /* Print digit sequence */ +1: popw %ax + call print_hex_nibble + loop 1b + /* Restore registers and return */ + popw %dx + popw %cx + popw %bx + popw %ax + ret + +/***************************************************************************** + * Subroutine: zero 1kB block of base memory + * + * Parameters: + * %bx : block to zero (in kB) + * Returns: + * Nothing + ***************************************************************************** + */ +zero_kb: + /* Preserve registers */ + pushw %ax + pushw %cx + pushw %di + pushw %es + /* Zero block */ + movw %bx, %ax + shlw $6, %ax + movw %ax, %es + movw $0x400, %cx + xorw %di, %di + xorw %ax, %ax + rep stosb + /* Restore registers and return */ + popw %es + popw %di + popw %cx + popw %ax + ret + +/***************************************************************************** + * Subroutine: free and zero base memory + * + * Parameters: + * %ax : Desired new free base memory counter (in kB) + * %bx : Expected current free base memory counter (in kB) + * %fs : BIOS data segment (0x40) + * Returns: + * None + * + * The base memory from %bx kB to %ax kB is unconditionally zeroed. + * It will be freed if and only if the expected current free base + * memory counter (%bx) matches the actual current free base memory + * counter in 0x40:0x13; if this does not match then the memory will + * be leaked. + ***************************************************************************** + */ +free_basemem: + /* Zero base memory */ + pushw %bx +1: cmpw %bx, %ax + je 2f + call zero_kb + incw %bx + jmp 1b +2: popw %bx + /* Free base memory */ + cmpw %fs:(0x13), %bx /* Update FBMS only if "old" value */ + jne 1f /* is correct */ +1: movw %ax, %fs:(0x13) + ret + +/***************************************************************************** + * Subroutine: make a PXE API call. Works with either !PXE or PXENV+ API. + * + * Parameters: + * %bx : PXE API call number + * %ds:pxe_parameter_structure : Parameters for PXE API call + * Returns: + * %ax : PXE status code (not exit code) + * CF set if %ax is non-zero + ***************************************************************************** + */ +pxe_call: + /* Preserve registers */ + pushw %di + pushw %es + /* Set up registers for PXENV+ API. %bx already set up */ + pushw %ds + popw %es + movw $pxe_parameter_structure, %di + /* Set up stack for !PXE API */ + pushw %es + pushw %di + pushw %bx + /* Make the API call */ + lcall *entry_segoff + /* Reset the stack */ + addw $6, %sp + movw pxe_parameter_structure, %ax + clc + testw %ax, %ax + jz 1f + stc +1: /* Clear direction flag, for the sake of sanity */ + cld + /* Restore registers and return */ + popw %es + popw %di + ret + +/***************************************************************************** + * Subroutine: print PXE API call error message + * + * Parameters: + * %ax : PXE status code + * %bx : PXE API call number + * Returns: + * Nothing + ***************************************************************************** + */ +print_pxe_error: + pushw %si + movw $10f, %si + call print_message + xchgw %ax, %bx + call print_hex_word + movw $20f, %si + call print_message + xchgw %ax, %bx + call print_hex_word + movw $30f, %si + call print_message + popw %si + ret + .section ".prefix.data", "aw", @progbits +10: .asciz " UNDI API call " +20: .asciz " failed: status code " +30: .asciz "\n" + .previous + +/***************************************************************************** + * PXE data structures + ***************************************************************************** + */ + .section ".prefix.data" + +pxe_esp: .long 0 +pxe_ss: .word 0 + +pxe_parameter_structure: .fill 64 + +undi_code_segoff: +undi_code_size: .word 0 +undi_code_segment: .word 0 + +undi_data_segoff: +undi_data_size: .word 0 +undi_data_segment: .word 0 + +pxe_hacks: .word 0 + +/* The following fields are part of a struct undi_device */ + +undi_device: + +pxenv_segoff: +pxenv_offset: .word 0 +pxenv_segment: .word 0 + +ppxe_segoff: +ppxe_offset: .word 0 +ppxe_segment: .word 0 + +entry_segoff: +entry_offset: .word 0 +entry_segment: .word 0 + +undi_fbms_start: .word 0 +undi_fbms_end: .word 0 + +pci_busdevfn: .word UNDI_NO_PCI_BUSDEVFN +isapnp_csn: .word UNDI_NO_ISAPNP_CSN +isapnp_read_port: .word UNDI_NO_ISAPNP_READ_PORT + +pci_vendor: .word 0 +pci_device: .word 0 +flags: + .word ( UNDI_FL_INITIALIZED | UNDI_FL_STARTED | UNDI_FL_KEEP_ALL ) + + .equ undi_device_size, ( . - undi_device ) + +/***************************************************************************** + * Run gPXE main code + ***************************************************************************** + */ + .section ".prefix" +run_gpxe: + /* Install gPXE */ + call install + + /* Set up real-mode stack */ + movw %bx, %ss + movw $_estack16, %sp + +#ifdef PXELOADER_KEEP_UNDI + /* Copy our undi_device structure to the preloaded_undi variable */ + movw %bx, %es + movw $preloaded_undi, %di + movw $undi_device, %si + movw $undi_device_size, %cx + rep movsb +#endif + + /* Retrieve PXE %ss:esp */ + movw pxe_ss, %di + movl pxe_esp, %ebp + + /* Jump to .text16 segment with %ds pointing to .data16 */ + movw %bx, %ds + pushw %ax + pushw $1f + lret + .section ".text16", "ax", @progbits +1: + /* Update the exit hook */ + movw %cs,pxe_exit_hook+2 + push %ax + mov $2f,%ax + mov %ax,pxe_exit_hook + pop %ax + + /* Run main program */ + pushl $main + pushw %cs + call prot_call + popl %ecx /* discard */ + + /* Uninstall gPXE */ + call uninstall + + /* Restore PXE stack */ + movw %di, %ss + movl %ebp, %esp + + /* Jump to hook if applicable */ + ljmpw *pxe_exit_hook + +2: /* Check PXE stack magic */ + popl %eax + cmpl $STACK_MAGIC, %eax + jne 1f + + /* PXE stack OK: return to caller */ + popw %ds + popw %es + popw %fs + popw %gs + popal + popfl + xorw %ax, %ax /* Return success */ + lret + +1: /* PXE stack corrupt or removed: use INT 18 */ + int $0x18 + .previous |