mirror of https://github.com/ipxe/ipxe.git
934 lines
22 KiB
ArmAsm
934 lines
22 KiB
ArmAsm
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
|
|
|
|
#define PXENV_UNDI_SHUTDOWN 0x0005
|
|
#define PXENV_UNDI_GET_INFORMATION 0x000c
|
|
#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 PXENV_GET_CACHED_INFO 0x0071
|
|
#define PXENV_PACKET_TYPE_DHCP_ACK 0x0002
|
|
#define PXENV_FILE_CMDLINE 0x00e8
|
|
|
|
#define PXE_HACK_EB54 0x0001
|
|
|
|
.section ".note.GNU-stack", "", @progbits
|
|
.code16
|
|
.arch i386
|
|
.org 0
|
|
|
|
#include <librm.h>
|
|
#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 ) )
|
|
|
|
/* Prefix memory layout:
|
|
*
|
|
* iPXE binary image
|
|
* Temporary stack
|
|
* Temporary copy of DHCPACK packet
|
|
* Temporary copy of command line
|
|
*/
|
|
#define PREFIX_STACK_SIZE 2048
|
|
#define PREFIX_TEMP_DHCPACK PREFIX_STACK_SIZE
|
|
#define PREFIX_TEMP_DHCPACK_SIZE ( 1260 /* sizeof ( BOOTPLAYER_t ) */ )
|
|
#define PREFIX_TEMP_CMDLINE ( PREFIX_TEMP_DHCPACK + PREFIX_TEMP_DHCPACK_SIZE )
|
|
#define PREFIX_TEMP_CMDLINE_SIZE 4096
|
|
|
|
/*****************************************************************************
|
|
* 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 temporary stack immediately after the iPXE image */
|
|
movw %cs, %ax
|
|
addw image_size_pgh, %ax
|
|
movw %ax, %ss
|
|
movl $PREFIX_STACK_SIZE, %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
|
|
|
|
/* Image size (for stack placement calculation) */
|
|
.section ".prefix.data", "aw", @progbits
|
|
image_size_pgh:
|
|
.word 0
|
|
.previous
|
|
.section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
|
|
.ascii "ADDW"
|
|
.long image_size_pgh
|
|
.long 16
|
|
.long 0
|
|
.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:
|
|
/* Allow for devices that fail to set the physical device type */
|
|
movb $0xeb, %al
|
|
movb %al, ( pxe_parameter_structure + 0x02 )
|
|
/* Issue PXENV_UNDI_GET_NIC_TYPE */
|
|
movw $PXENV_UNDI_GET_NIC_TYPE, %bx
|
|
call pxe_call
|
|
jnc 1f
|
|
call print_pxe_error
|
|
movw $10f, %si
|
|
call print_message
|
|
jmp no_physical_device
|
|
1: /* Determine physical device type */
|
|
movw $10f, %si
|
|
call print_message
|
|
movb ( pxe_parameter_structure + 0x02 ), %al
|
|
cmpb $2, %al
|
|
je pci_physical_device
|
|
cmpb $0xeb, %al
|
|
je probably_pci_physical_device
|
|
jmp no_physical_device
|
|
.section ".prefix.data", "aw", @progbits
|
|
10: .asciz " UNDI device is "
|
|
.previous
|
|
|
|
probably_pci_physical_device:
|
|
/* Device did not write the type byte: assume PCI */
|
|
movw $10f, %si
|
|
call print_message
|
|
.section ".prefix.data", "aw", @progbits
|
|
10: .asciz "probably "
|
|
.previous
|
|
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 "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 "unknown"
|
|
.previous
|
|
|
|
99:
|
|
|
|
/*****************************************************************************
|
|
* Get IRQ number
|
|
*****************************************************************************
|
|
*/
|
|
get_irq:
|
|
/* Issue PXENV_UNDI_GET_INFORMATION */
|
|
movw $PXENV_UNDI_GET_INFORMATION, %bx
|
|
call pxe_call
|
|
jnc 1f
|
|
call print_pxe_error
|
|
jmp 99f
|
|
1: /* Check for a valid IRQ number */
|
|
movw ( pxe_parameter_structure + 0x04 ), %ax
|
|
testw %ax, %ax
|
|
jz 99f
|
|
cmpw $15, %ax
|
|
ja 99f
|
|
/* Store IRQ number */
|
|
movw %ax, undi_irq
|
|
movw $10f, %si
|
|
call print_message
|
|
call print_word
|
|
.section ".prefix.data", "aw", @progbits
|
|
10: .asciz ", IRQ "
|
|
.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
|
|
|
|
/*****************************************************************************
|
|
* Get cached DHCP_ACK packet
|
|
*****************************************************************************
|
|
*/
|
|
get_dhcpack:
|
|
/* Issue PXENV_GET_CACHED_INFO */
|
|
xorl %esi, %esi
|
|
movw %ss, %si
|
|
movw %si, ( pxe_parameter_structure + 0x08 )
|
|
movw $PREFIX_TEMP_DHCPACK, ( pxe_parameter_structure + 0x06 )
|
|
movw $PREFIX_TEMP_DHCPACK_SIZE, ( pxe_parameter_structure +0x04 )
|
|
movw $PXENV_PACKET_TYPE_DHCP_ACK, ( pxe_parameter_structure + 0x02 )
|
|
movw $PXENV_GET_CACHED_INFO, %bx
|
|
call pxe_call
|
|
jnc 1f
|
|
call print_pxe_error
|
|
jmp 99f
|
|
1: /* Store physical address of packet */
|
|
shll $4, %esi
|
|
addl $PREFIX_TEMP_DHCPACK, %esi
|
|
movl %esi, pxe_cached_dhcpack
|
|
99:
|
|
.section ".prefix.data", "aw", @progbits
|
|
pxe_cached_dhcpack:
|
|
.long 0
|
|
.previous
|
|
|
|
/*****************************************************************************
|
|
* Check for a command line
|
|
*****************************************************************************
|
|
*/
|
|
get_cmdline:
|
|
/* Issue PXENV_FILE_CMDLINE */
|
|
xorl %esi, %esi
|
|
movw %ss, %si
|
|
movw %si, ( pxe_parameter_structure + 0x06 )
|
|
movw $PREFIX_TEMP_CMDLINE, ( pxe_parameter_structure + 0x04 )
|
|
movw $PREFIX_TEMP_CMDLINE_SIZE, ( pxe_parameter_structure + 0x02 )
|
|
movw $PXENV_FILE_CMDLINE, %bx
|
|
call pxe_call
|
|
jc 99f /* Suppress errors; this is an iPXE extension API call */
|
|
/* Check for non-NULL command line */
|
|
movw ( pxe_parameter_structure + 0x02 ), %ax
|
|
testw %ax, %ax
|
|
jz 99f
|
|
/* Record command line */
|
|
shll $4, %esi
|
|
addl $PREFIX_TEMP_CMDLINE, %esi
|
|
movl %esi, pxe_cmdline
|
|
99:
|
|
.section ".prefix.data", "aw", @progbits
|
|
pxe_cmdline:
|
|
.long 0
|
|
.previous
|
|
|
|
/*****************************************************************************
|
|
* Ensure NIC interrupt is disabled
|
|
*****************************************************************************
|
|
*/
|
|
disable_irq:
|
|
/* Check for a recorded IRQ number */
|
|
movw undi_irq, %cx
|
|
testw %cx, %cx
|
|
jz 99f
|
|
/* Calculate IMR */
|
|
movw %cx, %dx
|
|
shlw $4, %dx
|
|
andb $0x80, %dl
|
|
orb $0x21, %dl
|
|
/* Calculate mask value */
|
|
movb $0x01, %bl
|
|
andb $0x07, %cl
|
|
shlb %cl, %bl
|
|
/* Mask interrupt */
|
|
inb %dx, %al
|
|
orb %bl, %al
|
|
outb %al, %dx
|
|
99:
|
|
|
|
/*****************************************************************************
|
|
* 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
|
|
|
|
undi_irq: .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
|
|
|
|
/* Retrieve PXE command line, if any */
|
|
movl pxe_cmdline, %esi
|
|
|
|
/* Retrieve cached DHCPACK, if any */
|
|
movl pxe_cached_dhcpack, %ecx
|
|
|
|
/* 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 )
|
|
|
|
/* Store command-line pointer */
|
|
movl %esi, cmdline_phys
|
|
|
|
/* Store cached DHCPACK pointer */
|
|
movl %ecx, cached_dhcpack_phys
|
|
|
|
/* Run main program */
|
|
virtcall main
|
|
|
|
/* 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
|