-
-#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 */
-
-