123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766 |
- 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
- .globl _pxe_start
- _pxe_start:
- 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_ipxe
-
- /*****************************************************************************
- * 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 iPXE main code
- *****************************************************************************
- */
- .section ".prefix"
- run_ipxe:
- /* Install iPXE */
- 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 )
-
- /* Run main program */
- pushl $main
- pushw %cs
- call prot_call
- popl %ecx /* discard */
-
- /* Uninstall iPXE */
- call uninstall
-
- /* Restore PXE stack */
- movw %di, %ss
- movl %ebp, %esp
-
- /* Jump to hook if applicable */
- ljmpw *pxe_exit_hook
-
- .section ".data16", "aw", @progbits
- .globl pxe_exit_hook
- pxe_exit_hook:
- .word exit_ipxe, 0
- .previous
-
- exit_ipxe:
- /* 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
|