030e5a064e
Some binutils versions will drag in an object to satisfy the entry symbol; some won't. Try to cope with this exciting variety of behaviour by ensuring that all entry symbols are unique. Remove the explicit inclusion of the prefix object on the linker command line, since the entry symbol now provides all the information needed to identify the prefix. Signed-off-by: Michael Brown <mcb30@ipxe.org>
812 lines
19 KiB
ArmAsm
812 lines
19 KiB
ArmAsm
/* At entry, the processor is in 16 bit real mode and the code is being
|
|
* executed from an address it was not linked to. Code must be pic and
|
|
* 32 bit sensitive until things are fixed up.
|
|
*
|
|
* Also be very careful as the stack is at the rear end of the interrupt
|
|
* table so using a noticeable amount of stack space is a no-no.
|
|
*/
|
|
|
|
FILE_LICENCE ( GPL2_OR_LATER )
|
|
|
|
#include <config/general.h>
|
|
|
|
#define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) )
|
|
#define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) )
|
|
#define PCI_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( ' ' << 24 ) )
|
|
#define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
|
|
#define PMM_ALLOCATE 0x0000
|
|
#define PMM_FIND 0x0001
|
|
#define PMM_HANDLE_BASE ( ( ( 'F' - 'A' + 1 ) << 26 ) + \
|
|
( ( 'E' - 'A' + 1 ) << 21 ) + \
|
|
( ( 'N' - 'A' + 1 ) << 16 ) )
|
|
#define PMM_HANDLE_BASE_IMAGE_SOURCE \
|
|
( PMM_HANDLE_BASE | 0x00001000 )
|
|
#define PMM_HANDLE_BASE_DECOMPRESS_TO \
|
|
( PMM_HANDLE_BASE | 0x00002000 )
|
|
|
|
/* ROM banner timeout. Based on the configurable BANNER_TIMEOUT in
|
|
* config.h, but converted to a number of (18Hz) timer ticks, and
|
|
* doubled to allow for BIOSes that switch video modes immediately
|
|
* beforehand, so rendering the message almost invisible to the user.
|
|
*/
|
|
#define ROM_BANNER_TIMEOUT ( 2 * ( 18 * BANNER_TIMEOUT ) / 10 )
|
|
|
|
/* Allow payload to be excluded from ROM size
|
|
*/
|
|
#if ROMPREFIX_EXCLUDE_PAYLOAD
|
|
#define ZINFO_TYPE_ADxB "ADHB"
|
|
#define ZINFO_TYPE_ADxW "ADHW"
|
|
#else
|
|
#define ZINFO_TYPE_ADxB "ADDB"
|
|
#define ZINFO_TYPE_ADxW "ADDW"
|
|
#endif
|
|
|
|
.text
|
|
.code16
|
|
.arch i386
|
|
.section ".prefix", "ax", @progbits
|
|
.globl _rom_start
|
|
_rom_start:
|
|
|
|
.org 0x00
|
|
romheader:
|
|
.word 0xAA55 /* BIOS extension signature */
|
|
romheader_size: .byte 0 /* Size in 512-byte blocks */
|
|
jmp init /* Initialisation vector */
|
|
checksum:
|
|
.byte 0
|
|
.org 0x16
|
|
.word undiheader
|
|
.org 0x18
|
|
.word pciheader
|
|
.org 0x1a
|
|
.word pnpheader
|
|
.size romheader, . - romheader
|
|
|
|
.section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
|
|
.ascii ZINFO_TYPE_ADxB
|
|
.long romheader_size
|
|
.long 512
|
|
.long 0
|
|
.previous
|
|
|
|
build_id:
|
|
.long _build_id /* Randomly-generated build ID */
|
|
|
|
pciheader:
|
|
.ascii "PCIR" /* Signature */
|
|
.word pci_vendor_id /* Vendor identification */
|
|
.word pci_device_id /* Device identification */
|
|
.word 0x0000 /* Device list pointer */
|
|
.word pciheader_len /* PCI data structure length */
|
|
.byte 0x03 /* PCI data structure revision */
|
|
.byte 0x02, 0x00, 0x00 /* Class code */
|
|
pciheader_image_length:
|
|
.word 0 /* Image length */
|
|
.word 0x0001 /* Revision level */
|
|
.byte 0x00 /* Code type */
|
|
.byte 0x80 /* Last image indicator */
|
|
pciheader_runtime_length:
|
|
.word 0 /* Maximum run-time image length */
|
|
.word 0x0000 /* Configuration utility code header */
|
|
.word 0x0000 /* DMTF CLP entry point */
|
|
.equ pciheader_len, . - pciheader
|
|
.size pciheader, . - pciheader
|
|
|
|
.section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
|
|
.ascii ZINFO_TYPE_ADxW
|
|
.long pciheader_image_length
|
|
.long 512
|
|
.long 0
|
|
.ascii ZINFO_TYPE_ADxW
|
|
.long pciheader_runtime_length
|
|
.long 512
|
|
.long 0
|
|
.previous
|
|
|
|
pnpheader:
|
|
.ascii "$PnP" /* Signature */
|
|
.byte 0x01 /* Structure revision */
|
|
.byte ( pnpheader_len / 16 ) /* Length (in 16 byte increments) */
|
|
.word 0x0000 /* Offset of next header */
|
|
.byte 0x00 /* Reserved */
|
|
.byte 0x00 /* Checksum */
|
|
.long 0x00000000 /* Device identifier */
|
|
.word mfgstr /* Manufacturer string */
|
|
.word prodstr /* Product name */
|
|
.byte 0x02 /* Device base type code */
|
|
.byte 0x00 /* Device sub-type code */
|
|
.byte 0x00 /* Device interface type code */
|
|
.byte 0xf4 /* Device indicator */
|
|
.word 0x0000 /* Boot connection vector */
|
|
.word 0x0000 /* Disconnect vector */
|
|
.word bev_entry /* Boot execution vector */
|
|
.word 0x0000 /* Reserved */
|
|
.word 0x0000 /* Static resource information vector*/
|
|
.equ pnpheader_len, . - pnpheader
|
|
.size pnpheader, . - pnpheader
|
|
|
|
/* Manufacturer string */
|
|
mfgstr:
|
|
.asciz "http://ipxe.org"
|
|
.size mfgstr, . - mfgstr
|
|
|
|
/* Product string
|
|
*
|
|
* Defaults to PRODUCT_SHORT_NAME. If the ROM image is writable at
|
|
* initialisation time, it will be filled in to include the PCI
|
|
* bus:dev.fn number of the card as well.
|
|
*/
|
|
prodstr:
|
|
.ascii PRODUCT_SHORT_NAME
|
|
prodstr_separator:
|
|
.byte 0
|
|
.ascii "(PCI "
|
|
prodstr_pci_id:
|
|
.asciz "xx:xx.x)" /* Filled in by init code */
|
|
.size prodstr, . - prodstr
|
|
|
|
.globl undiheader
|
|
.weak undiloader
|
|
undiheader:
|
|
.ascii "UNDI" /* Signature */
|
|
.byte undiheader_len /* Length of structure */
|
|
.byte 0 /* Checksum */
|
|
.byte 0 /* Structure revision */
|
|
.byte 0,1,2 /* PXE version: 2.1.0 */
|
|
.word undiloader /* Offset to loader routine */
|
|
.word _data16_memsz /* Stack segment size */
|
|
.word _data16_memsz /* Data segment size */
|
|
.word _text16_memsz /* Code segment size */
|
|
.ascii "PCIR" /* Bus type */
|
|
.equ undiheader_len, . - undiheader
|
|
.size undiheader, . - undiheader
|
|
|
|
/* Initialisation (called once during POST)
|
|
*
|
|
* Determine whether or not this is a PnP system via a signature
|
|
* check. If it is PnP, return to the PnP BIOS indicating that we are
|
|
* a boot-capable device; the BIOS will call our boot execution vector
|
|
* if it wants to boot us. If it is not PnP, hook INT 19.
|
|
*/
|
|
init:
|
|
/* Preserve registers, clear direction flag, set %ds=%cs */
|
|
pushaw
|
|
pushw %ds
|
|
pushw %es
|
|
pushw %fs
|
|
pushw %gs
|
|
cld
|
|
pushw %cs
|
|
popw %ds
|
|
|
|
/* Shuffle some registers around. We need %di available for
|
|
* the print_xxx functions, and in a register that's
|
|
* addressable from %es, so shuffle as follows:
|
|
*
|
|
* %di (pointer to PnP structure) => %bx
|
|
* %bx (runtime segment address, for PCI 3.0) => %gs
|
|
*/
|
|
movw %bx, %gs
|
|
movw %di, %bx
|
|
|
|
/* Store PCI bus:dev.fn address */
|
|
movw %ax, init_pci_busdevfn
|
|
|
|
/* Print message as early as possible */
|
|
movw $init_message, %si
|
|
xorw %di, %di
|
|
call print_message
|
|
call print_pci_busdevfn
|
|
|
|
/* Fill in product name string, if possible */
|
|
movw $prodstr_pci_id, %di
|
|
call print_pci_busdevfn
|
|
movb $( ' ' ), prodstr_separator
|
|
|
|
/* Print segment address */
|
|
movb $( ' ' ), %al
|
|
xorw %di, %di
|
|
call print_character
|
|
movw %cs, %ax
|
|
call print_hex_word
|
|
|
|
/* Check for PCI BIOS version */
|
|
pushl %ebx
|
|
pushl %edx
|
|
pushl %edi
|
|
stc
|
|
movw $0xb101, %ax
|
|
int $0x1a
|
|
jc no_pci3
|
|
cmpl $PCI_SIGNATURE, %edx
|
|
jne no_pci3
|
|
testb %ah, %ah
|
|
jnz no_pci3
|
|
movw $init_message_pci, %si
|
|
xorw %di, %di
|
|
call print_message
|
|
movb %bh, %al
|
|
call print_hex_nibble
|
|
movb $( '.' ), %al
|
|
call print_character
|
|
movb %bl, %al
|
|
call print_hex_byte
|
|
cmpb $3, %bh
|
|
jb no_pci3
|
|
/* PCI >=3.0: leave %gs as-is if sane */
|
|
movw %gs, %ax
|
|
cmpw $0xa000, %ax /* Insane if %gs < 0xa000 */
|
|
jb pci3_insane
|
|
movw %cs, %bx /* Sane if %cs == %gs */
|
|
cmpw %bx, %ax
|
|
je 1f
|
|
movzbw romheader_size, %cx /* Sane if %cs+len <= %gs */
|
|
shlw $5, %cx
|
|
addw %cx, %bx
|
|
cmpw %bx, %ax
|
|
jae 1f
|
|
movw %cs, %bx /* Sane if %gs+len <= %cs */
|
|
addw %cx, %ax
|
|
cmpw %bx, %ax
|
|
jbe 1f
|
|
pci3_insane: /* PCI 3.0 with insane %gs value: print error and ignore %gs */
|
|
movb $( '!' ), %al
|
|
call print_character
|
|
movw %gs, %ax
|
|
call print_hex_word
|
|
no_pci3:
|
|
/* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */
|
|
pushw %cs
|
|
popw %gs
|
|
1: popl %edi
|
|
popl %edx
|
|
popl %ebx
|
|
|
|
/* Check for PnP BIOS. Although %es:di should point to the
|
|
* PnP BIOS signature on entry, some BIOSes fail to do this.
|
|
*/
|
|
movw $( 0xf000 - 1 ), %bx
|
|
pnp_scan:
|
|
incw %bx
|
|
jz no_pnp
|
|
movw %bx, %es
|
|
cmpl $PNP_SIGNATURE, %es:0
|
|
jne pnp_scan
|
|
xorw %dx, %dx
|
|
xorw %si, %si
|
|
movzbw %es:5, %cx
|
|
1: es lodsb
|
|
addb %al, %dl
|
|
loop 1b
|
|
jnz pnp_scan
|
|
/* Is PnP: print PnP message */
|
|
movw $init_message_pnp, %si
|
|
xorw %di, %di
|
|
call print_message
|
|
jmp pnp_done
|
|
no_pnp: /* Not PnP-compliant - hook INT 19 */
|
|
movw $init_message_int19, %si
|
|
xorw %di, %di
|
|
call print_message
|
|
xorw %ax, %ax
|
|
movw %ax, %es
|
|
pushl %es:( 0x19 * 4 )
|
|
popl orig_int19
|
|
pushw %gs /* %gs contains runtime %cs */
|
|
pushw $int19_entry
|
|
popl %es:( 0x19 * 4 )
|
|
pnp_done:
|
|
|
|
/* Check for PMM */
|
|
movw $( 0xe000 - 1 ), %bx
|
|
pmm_scan:
|
|
incw %bx
|
|
jz no_pmm
|
|
movw %bx, %es
|
|
cmpl $PMM_SIGNATURE, %es:0
|
|
jne pmm_scan
|
|
xorw %dx, %dx
|
|
xorw %si, %si
|
|
movzbw %es:5, %cx
|
|
1: es lodsb
|
|
addb %al, %dl
|
|
loop 1b
|
|
jnz pmm_scan
|
|
/* PMM found: print PMM message */
|
|
movw $init_message_pmm, %si
|
|
xorw %di, %di
|
|
call print_message
|
|
/* We have PMM and so a 1kB stack: preserve whole registers */
|
|
pushal
|
|
/* Allocate image source PMM block */
|
|
movzwl image_source_len_pgh, %ecx
|
|
movl $PMM_HANDLE_BASE_IMAGE_SOURCE, %ebx
|
|
movw $get_pmm_image_source, %bp
|
|
call get_pmm
|
|
movl %esi, image_source
|
|
jc 1f
|
|
/* Copy ROM to image source PMM block */
|
|
pushw %es
|
|
xorw %ax, %ax
|
|
movw %ax, %es
|
|
movl %esi, %edi
|
|
xorl %esi, %esi
|
|
movzbl romheader_size, %ecx
|
|
shll $9, %ecx
|
|
addr32 rep movsb /* PMM presence implies flat real mode */
|
|
popw %es
|
|
/* Shrink ROM */
|
|
movb shrunk_rom_size, %al
|
|
movb %al, romheader_size
|
|
1: /* Allocate decompression PMM block. Round up the size to the
|
|
* nearest 128kB and use the size within the PMM handle; this
|
|
* allows the same decompression area to be shared between
|
|
* multiple iPXE ROMs even with differing build IDs
|
|
*/
|
|
movl $_textdata_memsz_pgh, %ecx
|
|
addl $0x00001fff, %ecx
|
|
andl $0xffffe000, %ecx
|
|
movl %ecx, %ebx
|
|
shrw $12, %bx
|
|
orl $PMM_HANDLE_BASE_DECOMPRESS_TO, %ebx
|
|
movw $get_pmm_decompress_to, %bp
|
|
call get_pmm
|
|
movl %esi, decompress_to
|
|
/* Restore registers */
|
|
popal
|
|
no_pmm:
|
|
|
|
/* Update checksum */
|
|
xorw %bx, %bx
|
|
xorw %si, %si
|
|
movzbw romheader_size, %cx
|
|
shlw $9, %cx
|
|
1: lodsb
|
|
addb %al, %bl
|
|
loop 1b
|
|
subb %bl, checksum
|
|
|
|
/* Copy self to option ROM space. Required for PCI3.0, which
|
|
* loads us to a temporary location in low memory. Will be a
|
|
* no-op for lower PCI versions.
|
|
*/
|
|
movb $( ' ' ), %al
|
|
xorw %di, %di
|
|
call print_character
|
|
movw %gs, %ax
|
|
call print_hex_word
|
|
movzbw romheader_size, %cx
|
|
shlw $9, %cx
|
|
movw %ax, %es
|
|
xorw %si, %si
|
|
xorw %di, %di
|
|
cs rep movsb
|
|
|
|
/* Prompt for POST-time shell */
|
|
movw $init_message_prompt, %si
|
|
xorw %di, %di
|
|
call print_message
|
|
movw $prodstr, %si
|
|
call print_message
|
|
movw $init_message_dots, %si
|
|
call print_message
|
|
/* Wait for Ctrl-B */
|
|
movw $0xff02, %bx
|
|
call wait_for_key
|
|
/* Clear prompt */
|
|
pushf
|
|
xorw %di, %di
|
|
call print_kill_line
|
|
movw $init_message_done, %si
|
|
call print_message
|
|
popf
|
|
jnz 2f
|
|
/* Ctrl-B was pressed: invoke iPXE. The keypress will be
|
|
* picked up by the initial shell prompt, and we will drop
|
|
* into a shell.
|
|
*/
|
|
stc /* Inhibit relocation */
|
|
pushw %cs
|
|
call exec
|
|
2:
|
|
/* Restore registers */
|
|
popw %gs
|
|
popw %fs
|
|
popw %es
|
|
popw %ds
|
|
popaw
|
|
|
|
/* Indicate boot capability to PnP BIOS, if present */
|
|
movw $0x20, %ax
|
|
lret
|
|
.size init, . - init
|
|
|
|
/* Attempt to find or allocate PMM block
|
|
*
|
|
* Parameters:
|
|
* %ecx : size of block to allocate, in paragraphs
|
|
* %ebx : PMM handle base
|
|
* %bp : routine to check acceptability of found blocks
|
|
* %es:0000 : PMM structure
|
|
* Returns:
|
|
* %ebx : PMM handle
|
|
* %esi : allocated block address, or zero (with CF set) if allocation failed
|
|
*/
|
|
get_pmm:
|
|
/* Preserve registers */
|
|
pushl %eax
|
|
pushw %di
|
|
movw $' ', %di
|
|
get_pmm_find:
|
|
/* Try to find existing block */
|
|
pushl %ebx /* PMM handle */
|
|
pushw $PMM_FIND
|
|
lcall *%es:7
|
|
addw $6, %sp
|
|
pushw %dx
|
|
pushw %ax
|
|
popl %esi
|
|
testl %esi, %esi
|
|
jz get_pmm_allocate
|
|
/* Block found - check acceptability */
|
|
call *%bp
|
|
jnc get_pmm_done
|
|
/* Block not acceptable - increment handle and retry */
|
|
incl %ebx
|
|
jmp get_pmm_find
|
|
get_pmm_allocate:
|
|
/* Block not found - try to allocate new block */
|
|
pushw $0x0002 /* Extended memory */
|
|
pushl %ebx /* PMM handle */
|
|
pushl %ecx /* Length */
|
|
pushw $PMM_ALLOCATE
|
|
lcall *%es:7
|
|
addw $12, %sp
|
|
pushw %dx
|
|
pushw %ax
|
|
popl %esi
|
|
movw $'+', %di /* Indicate allocation attempt */
|
|
testl %esi, %esi
|
|
jnz get_pmm_done
|
|
stc
|
|
get_pmm_done:
|
|
/* Print block address */
|
|
pushfw
|
|
movw %di, %ax
|
|
xorw %di, %di
|
|
call print_character
|
|
movl %esi, %eax
|
|
call print_hex_dword
|
|
popfw
|
|
/* Restore registers and return */
|
|
popw %di
|
|
popl %eax
|
|
ret
|
|
.size get_pmm, . - get_pmm
|
|
|
|
/* Check acceptability of image source block */
|
|
get_pmm_image_source:
|
|
pushw %es
|
|
xorw %ax, %ax
|
|
movw %ax, %es
|
|
movl build_id, %eax
|
|
addr32 cmpl %es:build_id(%esi), %eax
|
|
je 1f
|
|
stc
|
|
1: popw %es
|
|
ret
|
|
.size get_pmm_image_source, . - get_pmm_image_source
|
|
|
|
/* Check acceptability of decompression block */
|
|
get_pmm_decompress_to:
|
|
clc
|
|
ret
|
|
.size get_pmm_decompress_to, . - get_pmm_decompress_to
|
|
|
|
/*
|
|
* Note to hardware vendors:
|
|
*
|
|
* If you wish to brand this boot ROM, please do so by defining the
|
|
* strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/general.h.
|
|
*
|
|
* While nothing in the GPL prevents you from removing all references
|
|
* to iPXE or http://ipxe.org, we prefer you not to do so.
|
|
*
|
|
* If you have an OEM-mandated branding requirement that cannot be
|
|
* satisfied simply by defining PRODUCT_NAME and PRODUCT_SHORT_NAME,
|
|
* please contact us.
|
|
*
|
|
* [ Including an ASCII NUL in PRODUCT_NAME is considered to be
|
|
* bypassing the spirit of this request! ]
|
|
*/
|
|
init_message:
|
|
.ascii "\n"
|
|
.ascii PRODUCT_NAME
|
|
.ascii "\n"
|
|
.asciz "iPXE (http://ipxe.org) "
|
|
.size init_message, . - init_message
|
|
init_message_pci:
|
|
.asciz " PCI"
|
|
.size init_message_pci, . - init_message_pci
|
|
init_message_pnp:
|
|
.asciz " PnP"
|
|
.size init_message_pnp, . - init_message_pnp
|
|
init_message_pmm:
|
|
.asciz " PMM"
|
|
.size init_message_pmm, . - init_message_pmm
|
|
init_message_int19:
|
|
.asciz " INT19"
|
|
.size init_message_int19, . - init_message_int19
|
|
init_message_prompt:
|
|
.asciz "\nPress Ctrl-B to configure "
|
|
.size init_message_prompt, . - init_message_prompt
|
|
init_message_dots:
|
|
.asciz "..."
|
|
.size init_message_dots, . - init_message_dots
|
|
init_message_done:
|
|
.asciz "\n\n"
|
|
.size init_message_done, . - init_message_done
|
|
|
|
/* PCI bus:dev.fn
|
|
*
|
|
*/
|
|
init_pci_busdevfn:
|
|
.word 0xffff
|
|
.size init_pci_busdevfn, . - init_pci_busdevfn
|
|
|
|
/* Image source area
|
|
*
|
|
* May be either zero (indicating to use option ROM space as source),
|
|
* or within a PMM-allocated block.
|
|
*/
|
|
.globl image_source
|
|
image_source:
|
|
.long 0
|
|
.size image_source, . - image_source
|
|
|
|
/* Image source area length (in paragraphs)
|
|
*
|
|
*/
|
|
image_source_len_pgh:
|
|
.word 0
|
|
.size image_source_len_pgh, . - image_source_len_pgh
|
|
.section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
|
|
.ascii "ADDW"
|
|
.long image_source_len_pgh
|
|
.long 16
|
|
.long 0
|
|
.previous
|
|
|
|
/* Shrunk ROM size (in 512-byte sectors)
|
|
*
|
|
*/
|
|
shrunk_rom_size:
|
|
.byte 0
|
|
.size shrunk_rom_size, . - shrunk_rom_size
|
|
.section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
|
|
.ascii "ADHB"
|
|
.long shrunk_rom_size
|
|
.long 512
|
|
.long 0
|
|
.previous
|
|
|
|
/* Temporary decompression area
|
|
*
|
|
* May be either zero (indicating to use default decompression area in
|
|
* high memory), or within a PMM-allocated block.
|
|
*/
|
|
.globl decompress_to
|
|
decompress_to:
|
|
.long 0
|
|
.size decompress_to, . - decompress_to
|
|
|
|
/* Boot Execution Vector entry point
|
|
*
|
|
* Called by the PnP BIOS when it wants to boot us.
|
|
*/
|
|
bev_entry:
|
|
clc /* Allow relocation */
|
|
pushw %cs
|
|
call exec
|
|
lret
|
|
.size bev_entry, . - bev_entry
|
|
|
|
/* INT19 entry point
|
|
*
|
|
* Called via the hooked INT 19 if we detected a non-PnP BIOS. We
|
|
* attempt to return via the original INT 19 vector (if we were able
|
|
* to store it).
|
|
*/
|
|
int19_entry:
|
|
pushw %cs
|
|
popw %ds
|
|
/* Prompt user to press B to boot */
|
|
movw $int19_message_prompt, %si
|
|
xorw %di, %di
|
|
call print_message
|
|
movw $prodstr, %si
|
|
call print_message
|
|
movw $int19_message_dots, %si
|
|
call print_message
|
|
movw $0xdf4e, %bx
|
|
call wait_for_key
|
|
pushf
|
|
xorw %di, %di
|
|
call print_kill_line
|
|
movw $int19_message_done, %si
|
|
call print_message
|
|
popf
|
|
jz 1f
|
|
/* Leave keypress in buffer and start iPXE. The keypress will
|
|
* cause the usual initial Ctrl-B prompt to be skipped.
|
|
*/
|
|
clc /* Allow relocation */
|
|
pushw %cs
|
|
call exec
|
|
1: /* Try to call original INT 19 vector */
|
|
movl %cs:orig_int19, %eax
|
|
testl %eax, %eax
|
|
je 2f
|
|
ljmp *%cs:orig_int19
|
|
2: /* No chained vector: issue INT 18 as a last resort */
|
|
int $0x18
|
|
.size int19_entry, . - int19_entry
|
|
orig_int19:
|
|
.long 0
|
|
.size orig_int19, . - orig_int19
|
|
|
|
int19_message_prompt:
|
|
.asciz "Press N to skip booting from "
|
|
.size int19_message_prompt, . - int19_message_prompt
|
|
int19_message_dots:
|
|
.asciz "..."
|
|
.size int19_message_dots, . - int19_message_dots
|
|
int19_message_done:
|
|
.asciz "\n\n"
|
|
.size int19_message_done, . - int19_message_done
|
|
|
|
/* Execute as a boot device
|
|
*
|
|
*/
|
|
exec: /* Set %ds = %cs */
|
|
pushw %cs
|
|
popw %ds
|
|
|
|
/* Preserve state of CF */
|
|
lahf
|
|
|
|
/* Print message as soon as possible */
|
|
movw $prodstr, %si
|
|
xorw %di, %di
|
|
call print_message
|
|
movw $exec_message_pre_install, %si
|
|
call print_message
|
|
|
|
/* Store magic word on BIOS stack and remember BIOS %ss:sp */
|
|
pushl $STACK_MAGIC
|
|
movw %ss, %dx
|
|
movw %sp, %bp
|
|
|
|
/* Obtain a reasonably-sized temporary stack */
|
|
xorw %bx, %bx
|
|
movw %bx, %ss
|
|
movw $0x7c00, %sp
|
|
|
|
/* Install iPXE */
|
|
sahf
|
|
pushfw
|
|
call alloc_basemem
|
|
popfw
|
|
movl image_source, %esi
|
|
movl decompress_to, %edi
|
|
call install_prealloc
|
|
|
|
/* Print message indicating successful installation */
|
|
movw $exec_message_post_install, %si
|
|
xorw %di, %di
|
|
call print_message
|
|
|
|
/* Set up real-mode stack */
|
|
movw %bx, %ss
|
|
movw $_estack16, %sp
|
|
|
|
/* Jump to .text16 segment */
|
|
pushw %ax
|
|
pushw $1f
|
|
lret
|
|
.section ".text16", "awx", @progbits
|
|
1: /* Call main() */
|
|
pushl $main
|
|
pushw %cs
|
|
call prot_call
|
|
popl %ecx /* discard */
|
|
|
|
/* Uninstall iPXE */
|
|
call uninstall
|
|
|
|
/* Restore BIOS stack */
|
|
movw %dx, %ss
|
|
movw %bp, %sp
|
|
|
|
/* Check magic word on BIOS stack */
|
|
popl %eax
|
|
cmpl $STACK_MAGIC, %eax
|
|
jne 1f
|
|
/* BIOS stack OK: return to caller */
|
|
lret
|
|
1: /* BIOS stack corrupt: use INT 18 */
|
|
int $0x18
|
|
.previous
|
|
|
|
exec_message_pre_install:
|
|
.asciz " starting execution..."
|
|
.size exec_message_pre_install, . - exec_message_pre_install
|
|
exec_message_post_install:
|
|
.asciz "ok\n"
|
|
.size exec_message_post_install, . - exec_message_post_install
|
|
|
|
/* Wait for key press specified by %bl (masked by %bh)
|
|
*
|
|
* Used by init and INT19 code when prompting user. If the specified
|
|
* key is pressed, it is left in the keyboard buffer.
|
|
*
|
|
* Returns with ZF set iff specified key is pressed.
|
|
*/
|
|
wait_for_key:
|
|
/* Preserve registers */
|
|
pushw %cx
|
|
pushw %ax
|
|
1: /* Empty the keyboard buffer before waiting for input */
|
|
movb $0x01, %ah
|
|
int $0x16
|
|
jz 2f
|
|
xorw %ax, %ax
|
|
int $0x16
|
|
jmp 1b
|
|
2: /* Wait for a key press */
|
|
movw $ROM_BANNER_TIMEOUT, %cx
|
|
3: decw %cx
|
|
js 99f /* Exit with ZF clear */
|
|
/* Wait for timer tick to be updated */
|
|
call wait_for_tick
|
|
/* Check to see if a key was pressed */
|
|
movb $0x01, %ah
|
|
int $0x16
|
|
jz 3b
|
|
/* Check to see if key was the specified key */
|
|
andb %bh, %al
|
|
cmpb %al, %bl
|
|
je 99f /* Exit with ZF set */
|
|
/* Not the specified key: remove from buffer and stop waiting */
|
|
pushfw
|
|
xorw %ax, %ax
|
|
int $0x16
|
|
popfw /* Exit with ZF clear */
|
|
99: /* Restore registers and return */
|
|
popw %ax
|
|
popw %cx
|
|
ret
|
|
.size wait_for_key, . - wait_for_key
|
|
|
|
/* Wait for timer tick
|
|
*
|
|
* Used by wait_for_key
|
|
*/
|
|
wait_for_tick:
|
|
pushl %eax
|
|
pushw %fs
|
|
movw $0x40, %ax
|
|
movw %ax, %fs
|
|
movl %fs:(0x6c), %eax
|
|
1: pushf
|
|
sti
|
|
hlt
|
|
popf
|
|
cmpl %fs:(0x6c), %eax
|
|
je 1b
|
|
popw %fs
|
|
popl %eax
|
|
ret
|
|
.size wait_for_tick, . - wait_for_tick
|