diff options
Diffstat (limited to 'contrib/syslinux-4.02/gpxe/src/arch/i386/prefix/romprefix.S')
-rw-r--r-- | contrib/syslinux-4.02/gpxe/src/arch/i386/prefix/romprefix.S | 1079 |
1 files changed, 1079 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/gpxe/src/arch/i386/prefix/romprefix.S b/contrib/syslinux-4.02/gpxe/src/arch/i386/prefix/romprefix.S new file mode 100644 index 0000000..02e5497 --- /dev/null +++ b/contrib/syslinux-4.02/gpxe/src/arch/i386/prefix/romprefix.S @@ -0,0 +1,1079 @@ +/* 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 PNP_GET_BBS_VERSION 0x60 +#define PMM_ALLOCATE 0x0000 +#define PMM_DEALLOCATE 0x0002 + +/* 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 ) + +/* We can load a ROM in two ways: have the BIOS load all of it (.rom prefix) + * or have the BIOS load a stub that loads the rest using PCI (.xrom prefix). + * The latter is not as widely supported, but allows the use of large ROMs + * on some systems with crowded option ROM space. + */ + +#ifdef LOAD_ROM_FROM_PCI +#define ROM_SIZE_VALUE _prefix_filesz_sect /* Amount to load in BIOS */ +#else +#define ROM_SIZE_VALUE 0 /* Load amount (before compr. fixup) */ +#endif + + + .text + .code16 + .arch i386 + .section ".prefix", "ax", @progbits + + .org 0x00 +romheader: + .word 0xAA55 /* BIOS extension signature */ +romheader_size: .byte ROM_SIZE_VALUE /* Size in 512-byte blocks */ + jmp init /* Initialisation vector */ +checksum: + .byte 0, 0 +real_size: + .word 0 + .org 0x16 + .word undiheader + .org 0x18 + .word pciheader + .org 0x1a + .word pnpheader + .size romheader, . - romheader + + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ +#ifndef LOAD_ROM_FROM_PCI + .ascii "ADDB" + .long romheader_size + .long 512 + .long 0 +#endif + .ascii "ADDB" + .long real_size + .long 512 + .long 0 + .previous + +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 ROM_SIZE_VALUE /* Image length */ + .word 0x0001 /* Revision level */ + .byte 0x00 /* Code type */ + .byte 0x80 /* Last image indicator */ +pciheader_runtime_length: + .word ROM_SIZE_VALUE /* Maximum run-time image length */ + .word 0x0000 /* Configuration utility code header */ + .word 0x0000 /* DMTF CLP entry point */ + .equ pciheader_len, . - pciheader + .size pciheader, . - pciheader + +#ifndef LOAD_ROM_FROM_PCI + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "ADDW" + .long pciheader_image_length + .long 512 + .long 0 + .ascii "ADDW" + .long pciheader_runtime_length + .long 512 + .long 0 + .previous +#endif + +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://etherboot.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 + + /* Print message as early as possible */ + movw $init_message, %si + xorw %di, %di + call print_message + call print_pci_busdevfn + +#ifdef LOAD_ROM_FROM_PCI + /* Save PCI bus:dev.fn for later use */ + movw %ax, pci_busdevfn +#endif + + /* 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 +#ifdef LOAD_ROM_FROM_PCI + incb pcibios_present +#endif + 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 + /* Check for BBS */ + pushw %es:0x1b /* Real-mode data segment */ + pushw %ds /* &(bbs_version) */ + pushw $bbs_version + pushw $PNP_GET_BBS_VERSION + lcall *%es:0xd + addw $8, %sp + testw %ax, %ax + je got_bbs +no_pnp: /* Not PnP-compliant - therefore cannot be BBS-compliant */ +no_bbs: /* Not BBS-compliant - must 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 ) + jmp bbs_done +got_bbs: /* BBS compliant - no need to hook INT 19 */ + movw $init_message_bbs, %si + xorw %di, %di + call print_message +bbs_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 upper register halves */ + pushal + /* Calculate required allocation size in %esi */ + movzwl real_size, %eax + shll $9, %eax + addl $_textdata_memsz, %eax + orw $0xffff, %ax /* Ensure allocation size is at least 64kB */ + bsrl %eax, %ecx + subw $15, %cx /* Round up and convert to 64kB count */ + movw $1, %si + shlw %cl, %si +pmm_loop: + /* Try to allocate block via PMM */ + pushw $0x0006 /* Aligned, extended memory */ + pushl $0xffffffff /* No handle */ + movzwl %si, %eax + shll $12, %eax + pushl %eax /* Allocation size in paragraphs */ + pushw $PMM_ALLOCATE + lcall *%es:7 + addw $12, %sp + /* Abort if allocation fails */ + testw %dx, %dx /* %ax==0 even on success, since align>=64kB */ + jz pmm_fail + /* If block has A20==1, free block and try again with twice + * the allocation size (and hence alignment). + */ + testw $0x0010, %dx + jz got_pmm + pushw %dx + pushw $0 + pushw $PMM_DEALLOCATE + lcall *%es:7 + addw $6, %sp + addw %si, %si + jmp pmm_loop +got_pmm: /* PMM allocation succeeded */ + movw %dx, ( image_source + 2 ) + movw %dx, %ax + xorw %di, %di + call print_hex_word + movb $( '@' ), %al + call print_character + movw %si, %ax + call print_hex_byte +pmm_copy: + /* Copy ROM to PMM block */ + xorw %ax, %ax + movw %ax, %es + movl image_source, %edi + xorl %esi, %esi + movzbl romheader_size, %ecx + shll $9, %ecx + addr32 rep movsb /* PMM presence implies flat real mode */ + movl %edi, decompress_to + /* Shrink ROM */ + movb $_prefix_memsz_sect, romheader_size +#if defined(SHRINK_WITHOUT_PMM) || defined(LOAD_ROM_FROM_PCI) + jmp pmm_done +pmm_fail: + /* Print marker and copy ourselves to high memory */ + movl $HIGHMEM_LOADPOINT, image_source + xorw %di, %di + movb $( '!' ), %al + call print_character + jmp pmm_copy +pmm_done: +#else +pmm_fail: +#endif + /* Restore upper register halves */ + popal +#if defined(LOAD_ROM_FROM_PCI) + call load_from_pci + jc load_err + jmp load_ok +no_pmm: + /* Cannot continue without PMM - print error message */ + xorw %di, %di + movw $init_message_no_pmm, %si + call print_message +load_err: + /* Wait for five seconds to let user see message */ + movw $90, %cx +1: call wait_for_tick + loop 1b + /* Mark environment as invalid and return */ + movl $0, decompress_to + jmp out + +load_ok: +#else +no_pmm: +#endif + /* 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 out + /* Ctrl-B was pressed: invoke gPXE. The keypress will be + * picked up by the initial shell prompt, and we will drop + * into a shell. + */ + pushw %cs + call exec +out: + /* 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 + +/* + * 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 gPXE or http://etherboot.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 "gPXE (http://etherboot.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_bbs: + .asciz " BBS" + .size init_message_bbs, . - init_message_bbs +init_message_pmm: + .asciz " PMM" + .size init_message_pmm, . - init_message_pmm +#ifdef LOAD_ROM_FROM_PCI +init_message_no_pmm: + .asciz "\nPMM required but not present!\n" + .size init_message_no_pmm, . - init_message_no_pmm +#endif +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 + +/* ROM image location + * + * May be either within option ROM space, or within PMM-allocated block. + */ + .globl image_source +image_source: + .long 0 + .size image_source, . - image_source + +/* Temporary decompression area + * + * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block. + * If a PCI ROM load fails, this will be set to zero. + */ + .globl decompress_to +decompress_to: + .long HIGHMEM_LOADPOINT + .size decompress_to, . - decompress_to + +#ifdef LOAD_ROM_FROM_PCI + +/* Set if the PCI BIOS is present, even <3.0 */ +pcibios_present: + .byte 0 + .byte 0 /* for alignment */ + .size pcibios_present, . - pcibios_present + +/* PCI bus:device.function word + * + * Filled in by init in the .xrom case, so the remainder of the ROM + * can be located. + */ +pci_busdevfn: + .word 0 + .size pci_busdevfn, . - pci_busdevfn + +#endif + +/* BBS version + * + * Filled in by BBS BIOS. We ignore the value. + */ +bbs_version: + .word 0 + .size bbs_version, . - bbs_version + +/* Boot Execution Vector entry point + * + * Called by the PnP BIOS when it wants to boot us. + */ +bev_entry: + pushw %cs + call exec + lret + .size bev_entry, . - bev_entry + + +#ifdef LOAD_ROM_FROM_PCI + +#define PCI_ROM_ADDRESS 0x30 /* Bits 31:11 address, 10:1 reserved */ +#define PCI_ROM_ADDRESS_ENABLE 0x00000001 +#define PCI_ROM_ADDRESS_MASK 0xfffff800 + +#define PCIBIOS_READ_WORD 0xb109 +#define PCIBIOS_READ_DWORD 0xb10a +#define PCIBIOS_WRITE_WORD 0xb10c +#define PCIBIOS_WRITE_DWORD 0xb10d + +/* Determine size of PCI BAR + * + * %bx : PCI bus:dev.fn to probe + * %di : Address of BAR to find size of + * %edx : Mask of address bits within BAR + * + * %ecx : Size for a memory resource, + * 1 for an I/O resource (bit 0 set). + * CF : Set on error or nonexistent device (all-ones read) + * + * All other registers saved. + */ +pci_bar_size: + /* Save registers */ + pushw %ax + pushl %esi + pushl %edx + + /* Read current BAR value */ + movw $PCIBIOS_READ_DWORD, %ax + int $0x1a + + /* Check for device existence and save it */ + testb $1, %cl /* I/O bit? */ + jz 1f + andl $1, %ecx /* If so, exit with %ecx = 1 */ + jmp 99f +1: notl %ecx + testl %ecx, %ecx /* Set ZF iff %ecx was all-ones */ + notl %ecx + jnz 1f + stc /* All ones - exit with CF set */ + jmp 99f +1: movl %ecx, %esi /* Save in %esi */ + + /* Write all ones to BAR */ + movl %edx, %ecx + movw $PCIBIOS_WRITE_DWORD, %ax + int $0x1a + + /* Read back BAR */ + movw $PCIBIOS_READ_DWORD, %ax + int $0x1a + + /* Find decode size from least set bit in mask BAR */ + bsfl %ecx, %ecx /* Find least set bit, log2(decode size) */ + jz 1f /* Mask BAR should not be zero */ + xorl %edx, %edx + incl %edx + shll %cl, %edx /* %edx = decode size */ + jmp 2f +1: xorl %edx, %edx /* Return zero size for mask BAR zero */ + + /* Restore old BAR value */ +2: movl %esi, %ecx + movw $PCIBIOS_WRITE_DWORD, %ax + int $0x1a + + movl %edx, %ecx /* Return size in %ecx */ + + /* Restore registers and return */ +99: popl %edx + popl %esi + popw %ax + ret + + .size pci_bar_size, . - pci_bar_size + +/* PCI ROM loader + * + * Called from init in the .xrom case to load the non-prefix code + * using the PCI ROM BAR. + * + * Returns with carry flag set on error. All registers saved. + */ +load_from_pci: + /* + * Use PCI BIOS access to config space. The calls take + * + * %ah : 0xb1 %al : function + * %bx : bus/dev/fn + * %di : config space address + * %ecx : value to write (for writes) + * + * %ecx : value read (for reads) + * %ah : return code + * CF : error indication + * + * All registers not used for return are preserved. + */ + + /* Save registers and set up %es for big real mode */ + pushal + pushw %es + xorw %ax, %ax + movw %ax, %es + + /* Check PCI BIOS presence */ + cmpb $0, pcibios_present + jz err_pcibios + + /* Load existing PCI ROM BAR */ + movw $PCIBIOS_READ_DWORD, %ax + movw pci_busdevfn, %bx + movw $PCI_ROM_ADDRESS, %di + int $0x1a + + /* Maybe it's already enabled? */ + testb $PCI_ROM_ADDRESS_ENABLE, %cl + jz 1f + movb $1, %dl /* Flag indicating no deinit required */ + movl %ecx, %ebp + jmp check_rom + + /* Determine PCI BAR decode size */ +1: movl $PCI_ROM_ADDRESS_MASK, %edx + call pci_bar_size /* Returns decode size in %ecx */ + jc err_size_insane /* CF => no ROM BAR, %ecx == ffffffff */ + + /* Check sanity of decode size */ + xorl %eax, %eax + movw real_size, %ax + shll $9, %eax /* %eax = ROM size */ + cmpl %ecx, %eax + ja err_size_insane /* Insane if decode size < ROM size */ + cmpl $0x100000, %ecx + jae err_size_insane /* Insane if decode size >= 1MB */ + + /* Find a place to map the BAR + * In theory we should examine e820 and all PCI BARs to find a + * free region. However, we run at POST when e820 may not be + * available, and memory reads of an unmapped location are + * de facto standardized to return all-ones. Thus, we can get + * away with searching high memory (0xf0000000 and up) on + * multiples of the ROM BAR decode size for a sufficiently + * large all-ones region. + */ + movl %ecx, %edx /* Save ROM BAR size in %edx */ + movl $0xf0000000, %ebp + xorl %eax, %eax + notl %eax /* %eax = all ones */ +bar_search: + movl %ebp, %edi + movl %edx, %ecx + shrl $2, %ecx + addr32 repe scasl /* Scan %es:edi for anything not all-ones */ + jz bar_found + addl %edx, %ebp + testl $0x80000000, %ebp + jz err_no_bar + jmp bar_search + +bar_found: + movl %edi, %ebp + /* Save current BAR value on stack to restore later */ + movw $PCIBIOS_READ_DWORD, %ax + movw $PCI_ROM_ADDRESS, %di + int $0x1a + pushl %ecx + + /* Map the ROM */ + movw $PCIBIOS_WRITE_DWORD, %ax + movl %ebp, %ecx + orb $PCI_ROM_ADDRESS_ENABLE, %cl + int $0x1a + + xorb %dl, %dl /* %dl = 0 : ROM was not already mapped */ +check_rom: + /* Check and copy ROM - enter with %dl set to skip unmapping, + * %ebp set to mapped ROM BAR address. + * We check up to prodstr_separator for equality, since anything past + * that may have been modified. Since our check includes the checksum + * byte over the whole ROM stub, that should be sufficient. + */ + xorb %dh, %dh /* %dh = 0 : ROM did not fail integrity check */ + + /* Verify ROM integrity */ + xorl %esi, %esi + movl %ebp, %edi + movl $prodstr_separator, %ecx + addr32 repe cmpsb + jz copy_rom + incb %dh /* ROM failed integrity check */ + movl %ecx, %ebp /* Save number of bytes left */ + jmp skip_load + +copy_rom: + /* Print BAR address and indicate whether we mapped it ourselves */ + movb $( ' ' ), %al + xorw %di, %di + call print_character + movl %ebp, %eax + call print_hex_dword + movb $( '-' ), %al /* '-' for self-mapped */ + subb %dl, %al + subb %dl, %al /* '+' = '-' - 2 for BIOS-mapped */ + call print_character + + /* Copy ROM at %ebp to PMM or highmem block */ + movl %ebp, %esi + movl image_source, %edi + movzwl real_size, %ecx + shll $9, %ecx + addr32 es rep movsb + movl %edi, decompress_to +skip_load: + testb %dl, %dl /* Was ROM already mapped? */ + jnz skip_unmap + + /* Unmap the ROM by restoring old ROM BAR */ + movw $PCIBIOS_WRITE_DWORD, %ax + movw $PCI_ROM_ADDRESS, %di + popl %ecx + int $0x1a + +skip_unmap: + /* Error handling */ + testb %dh, %dh + jnz err_rom_invalid + clc + jmp 99f + +err_pcibios: /* No PCI BIOS available */ + movw $load_message_no_pcibios, %si + xorl %eax, %eax /* "error code" is zero */ + jmp 1f +err_size_insane: /* BAR has size (%ecx) that is insane */ + movw $load_message_size_insane, %si + movl %ecx, %eax + jmp 1f +err_no_bar: /* No space of sufficient size (%edx) found */ + movw $load_message_no_bar, %si + movl %edx, %eax + jmp 1f +err_rom_invalid: /* Loaded ROM does not match (%ebp bytes left) */ + movw $load_message_rom_invalid, %si + movzbl romheader_size, %eax + shll $9, %eax + subl %ebp, %eax + decl %eax /* %eax is now byte index of failure */ + +1: /* Error handler - print message at %si and dword in %eax */ + xorw %di, %di + call print_message + call print_hex_dword + stc +99: popw %es + popal + ret + + .size load_from_pci, . - load_from_pci + +load_message_no_pcibios: + .asciz "\nNo PCI BIOS found! " + .size load_message_no_pcibios, . - load_message_no_pcibios + +load_message_size_insane: + .asciz "\nROM resource has invalid size " + .size load_message_size_insane, . - load_message_size_insane + +load_message_no_bar: + .asciz "\nNo memory hole of sufficient size " + .size load_message_no_bar, . - load_message_no_bar + +load_message_rom_invalid: + .asciz "\nLoaded ROM is invalid at " + .size load_message_rom_invalid, . - load_message_rom_invalid + +#endif /* LOAD_ROM_FROM_PCI */ + + +/* 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 gPXE. The keypress will + * cause the usual initial Ctrl-B prompt to be skipped. + */ + 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 + +#ifdef LOAD_ROM_FROM_PCI + /* Don't execute if load was invalid */ + cmpl $0, decompress_to + jne 1f + lret +1: +#endif + + /* Print message as soon as possible */ + movw $prodstr, %si + xorw %di, %di + call print_message + movw $exec_message, %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 %ax, %ax + movw %ax, %ss + movw $0x7c00, %sp + + /* Install gPXE */ + movl image_source, %esi + movl decompress_to, %edi + call alloc_basemem + call install_prealloc + + /* 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 gPXE */ + 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: + .asciz " starting execution\n" + .size exec_message, . - exec_message + +/* 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 |