diff options
author | H. Peter Anvin <hpa@zytor.com> | 2008-03-26 16:25:35 -0700 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2008-03-26 16:25:35 -0700 |
commit | 9eddd22a7b53b1d02fbae0d987df8af122924248 (patch) | |
tree | 882f5152880b0b1aa2d7a0619d30065acc69fb16 /gpxe/src/arch/i386/prefix/pxeprefix.S | |
parent | bbb8f15936b851e6a0ef6f7bb2c95197bff35994 (diff) | |
download | syslinux-9eddd22a7b53b1d02fbae0d987df8af122924248.tar.gz |
Add gPXE into the source tree; build unified imagesyslinux-3.70-pre7
Diffstat (limited to 'gpxe/src/arch/i386/prefix/pxeprefix.S')
-rw-r--r-- | gpxe/src/arch/i386/prefix/pxeprefix.S | 666 |
1 files changed, 666 insertions, 0 deletions
diff --git a/gpxe/src/arch/i386/prefix/pxeprefix.S b/gpxe/src/arch/i386/prefix/pxeprefix.S new file mode 100644 index 00000000..d7125b61 --- /dev/null +++ b/gpxe/src/arch/i386/prefix/pxeprefix.S @@ -0,0 +1,666 @@ +#define PXENV_UNDI_SHUTDOWN 0x0005 +#define PXENV_UNDI_GET_NIC_TYPE 0x0012 +#define PXENV_STOP_UNDI 0x0015 +#define PXENV_UNLOAD_STACK 0x0070 + + .text + .arch i386 + .org 0 + .section ".prefix", "ax", @progbits + .section ".prefix.data", "aw", @progbits + .code16 + +#include <undi.h> + +/***************************************************************************** + * Entry point: set operating context, print welcome message + ***************************************************************************** + */ + .section ".prefix" + /* Set up our non-stack segment registers */ + jmp $0x7c0, $1f +1: pushfl + /* %ax here is the default return type... */ + movw $5, %ax /* Keep PXE+UNDI */ + pushal + pushw %ds + pushw %es + pushw %fs + pushw %gs + movw %cs, %ax + movw %ax, %ds + + movw $0x40, %ax /* BIOS data segment access */ + movw %ax, %fs + + pushw %fs:0x13 + /* Record PXENV+ and !PXE nominal addresses */ + movw %es, pxenv_segment + movw %bx, pxenv_offset + movw %sp, %bp + movw %ss, return_stack_segment + movl %esp, return_stack_offset + movl 50(%bp), %eax + movl %eax, ppxe_segoff /* !PXE address */ + /* Set up stack just below 0x7c00 */ + xorw %ax, %ax + movw %ax, %ss + movw $0x7c00, %sp + /* Clear direction flag, for the sake of sanity */ + cld + /* Print welcome message */ + movw $10f, %si + call print_message + .section ".prefix.data" +10: .asciz "PXE->EB:" + .previous + +/***************************************************************************** + * Verify PXENV+ structure and record parameters of interest + ***************************************************************************** + */ +detect_pxenv: + /* Signature check */ + les pxenv_segoff, %di + cmpl $0x4e455850, %es:(%di) /* 'PXEN' signature */ + jne no_pxenv + cmpw $0x2b56, %es:4(%di) /* 'V+' signature */ + jne no_pxenv + /* Record entry point and UNDI segments */ + pushl %es:0x0a(%di) /* Entry point */ + popl entry_segoff + pushw %es:0x24(%di) /* UNDI code segment */ + pushw %es:0x26(%di) /* UNDI code size */ + popl undi_code_segoff + pushw %es:0x20(%di) /* UNDI data segment */ + pushw %es:0x22(%di) /* UNDI data size */ + popl undi_data_segoff + /* Print "PXENV+ at <address>" */ + movw $10f, %si + call print_message + movw %bx, %di + call print_segoff + movb $',', %al + call print_character + jmp 99f + .section ".prefix.data" +10: .asciz " PXENV+ at " + .previous + +no_pxenv: + xorl %eax, %eax + movl %eax, pxenv_segoff + +99: + +/***************************************************************************** + * Verify !PXE structure and record parameters of interest + ***************************************************************************** + */ +detect_ppxe: + /* Signature check */ + les ppxe_segoff, %di + cmpl $0x45585021, %es:(%di) /* '!PXE' signature */ + jne no_ppxe + /* Record structure address, entry point, and UNDI segments */ + pushw %es + popw ppxe_segment + movw %di, ppxe_offset + pushl %es:0x10(%di) /* Entry point */ + popl entry_segoff + pushw %es:0x30(%di) /* UNDI code segment */ + pushw %es:0x36(%di) /* UNDI code size */ + popl undi_code_segoff + pushw %es:0x28(%di) /* UNDI data segment */ + pushw %es:0x2e(%di) /* UNDI data size */ + popl undi_data_segoff + /* Print "!PXE at <address>" */ + movw $10f, %si + call print_message + call print_segoff + movb $',', %al + call print_character + jmp 99f + .section ".prefix.data" +10: .asciz " !PXE at " + .previous + +no_ppxe: + xorl %eax, %eax + movl %eax, ppxe_segoff + +99: + +/***************************************************************************** + * Sanity check: we must have an entry point + ***************************************************************************** + */ +check_have_stack: + /* Check for entry point */ + movl entry_segoff, %eax + testl %eax, %eax + jnz 99f + /* No entry point: print message and skip everything else */ + movw $10f, %si + call print_message + jmp finished + .section ".prefix.data" +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, %di + call print_segoff + .section ".prefix.data" +10: .asciz " entry point at " + .previous + /* Print UNDI code segment */ + movw $10f, %si + call print_message + les undi_code_segoff, %di + call print_segoff + .section ".prefix.data" +10: .asciz "\n UNDI code segment " + .previous + /* Print UNDI data segment */ + movw $10f, %si + call print_message + les undi_data_segoff, %di + call print_segoff + .section ".prefix.data" +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" +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 + movb $0x0a, %al + call print_character + jmp 99f + .section ".prefix.data" +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" +10: .asciz " Unable to determine UNDI physical device\n" + .previous + +99: + +/***************************************************************************** + * Leave NIC in a safe state + ***************************************************************************** + */ +#ifndef PXELOADER_KEEP_UNDI +shutdown_nic: + /* Issue PXENV_UNDI_SHUTDOWN */ + movw $PXENV_UNDI_SHUTDOWN, %bx + call pxe_call + jnc 1f + call print_pxe_error +1: + +/***************************************************************************** + * Unload PXE base code + ***************************************************************************** + */ +unload_base_code: + /* 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 %fs:(0x13), %si + movw undi_fbms_start, %di + call free_basemem +99: + +/***************************************************************************** + * Unload UNDI driver + ***************************************************************************** + */ + +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_start, %si + movw undi_fbms_end, %di + call free_basemem + /* Clear UNDI_FL_STARTED */ + andw $~UNDI_FL_STARTED, flags +99: + +/***************************************************************************** + * 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" +10: .asciz " " +20: .asciz "kB free base memory after PXE unload\n" + .previous +#endif /* PXELOADER_KEEP_UNDI */ + +/***************************************************************************** + * Exit point + ***************************************************************************** + */ +finished: + jmp run_etherboot + +/***************************************************************************** + * Subroutine: print segment:offset address + * + * Parameters: + * %es:%di : segment:offset address to print + * Returns: + * Nothing + ***************************************************************************** + */ +print_segoff: + /* Preserve registers */ + pushw %ax + /* Print "<segment>:offset" */ + movw %es, %ax + call print_hex_word + movb $':', %al + call print_character + movw %di, %ax + call print_hex_word + /* Restore registers and return */ + popw %ax + ret + +/***************************************************************************** + * Subroutine: print decimal word + * + * Parameters: + * %ax : word to print + * Returns: + * Nothing + ***************************************************************************** + */ +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: print PCI bus:dev.fn + * + * Parameters: + * %ax : PCI bus:dev.fn to print + * Returns: + * Nothing + ***************************************************************************** + */ +print_pci_busdevfn: + /* Preserve registers */ + pushw %ax + /* Print bus */ + xchgb %al, %ah + call print_hex_byte + /* Print ":" */ + movb $':', %al + call print_character + /* Print device */ + movb %ah, %al + shrb $3, %al + call print_hex_byte + /* Print "." */ + movb $'.', %al + call print_character + /* Print function */ + movb %ah, %al + andb $0x07, %al + call print_hex_nibble + /* Restore registers and return */ + popw %ax + ret + +/***************************************************************************** + * Subroutine: zero 1kB block of base memory + * + * Parameters: + * %si : block to zero (in kB) + * Returns: + * Nothing + ***************************************************************************** + */ +zero_kb: + /* Preserve registers */ + pushw %ax + pushw %cx + pushw %di + pushw %es + /* Zero block */ + movw %si, %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: + * %si : Expected current free base memory counter (in kB) + * %di : Desired new free base memory counter (in kB) + * %fs : BIOS data segment (0x40) + * Returns: + * %ax : Actual new free base memory counter (in kB) + * + * The base memory from %si kB to %di kB is unconditionally zeroed. + * It will be freed if and only if the expected current free base + * memory counter (%si) 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 %si +1: cmpw %si, %di + je 2f + call zero_kb + incw %si + jmp 1b +2: popw %si + /* Free base memory */ + movw %fs:(0x13), %ax /* Current FBMS to %ax */ + cmpw %ax, %si /* Update FBMS only if "old" value */ + jne 1f /* is correct */ + movw %di, %ax +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: /* 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" +10: .asciz " UNDI API call " +20: .asciz " failed: status code " +30: .asciz "\n" + .previous + +/***************************************************************************** + * PXE data structures + ***************************************************************************** + */ + +pxe_parameter_structure: .fill 20 + +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 + +/* 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 + +return_stack_segoff: +return_stack_offset: .long 0 +return_stack_segment: .word 0 + +return_type: .word 0 /* Default: unload PXE and boot next */ + +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_STARTED + + .equ undi_device_size, ( . - undi_device ) + +/***************************************************************************** + * Run Etherboot main code + ***************************************************************************** + */ +run_etherboot: + /* Install Etherboot */ + 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 + + /* Jump to .text16 segment with %ds pointing to .data16 */ + movw %bx, %ds + pushw %ax + pushw $1f + lret + .section ".text16", "ax", @progbits +1: + /* Run main program */ + pushl $main + pushw %cs + call prot_call + popl %eax /* discard */ + +#ifdef PXELOADER_KEEP_UNDI + /* Boot next device */ + movw $0x40, %ax + movw %ax, %fs + movw $preloaded_undi,%bx + + cli + movw %ss:return_type-undi_device(%bx),%ax + lssl %ss:return_stack_segoff-undi_device(%bx), %esp + movw %sp,%bp + movw %ax,38(%bp) /* Overwrite return AX value */ + popw %fs:0x13 /* 0 */ + popw %gs /* 2 */ + popw %fs /* 4 */ + popw %es /* 6 */ + popw %ds /* 8 */ + popal /* 10, 14, 18, 22, 26, 30, 34, 38 */ + popfl /* 42 */ + lret /* 46 */ +#else + int $0x18 +#endif + + .previous |