/* * Copyright (C) 2010 Michael Brown . * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * You can also choose to distribute this program under the terms of * the Unmodified Binary Distribution Licence (as given in the file * COPYING.UBDL), provided that you have satisfied its requirements. * */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define PCIBIOS_READ_CONFIG_WORD 0xb109 #define PCIBIOS_READ_CONFIG_DWORD 0xb10a #define PCIBIOS_WRITE_CONFIG_WORD 0xb10c #define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d #define PCI_COMMAND 0x04 #define PCI_COMMAND_MEM 0x02 #define PCI_BAR_0 0x10 #define PCI_BAR_5 0x24 #define PCI_BAR_EXPROM 0x30 #define PCIR_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( 'R' << 24 ) ) #define ROMPREFIX_EXCLUDE_PAYLOAD 1 #define ROMPREFIX_MORE_IMAGES 1 #define _pcirom_start _mrom_start #include "pciromprefix.S" .text .arch i386 .code16 /* Obtain access to payload by exposing the expansion ROM BAR at the * address currently used by a suitably large memory BAR on the same * device. The memory BAR is temporarily disabled. Using a memory * BAR on the same device means that we don't have to worry about the * configuration of any intermediate PCI bridges. * * Parameters: * %ds:0000 : Prefix * %esi : Buffer for copy of image source (or zero if no buffer available) * %ecx : Expected offset within buffer of first payload block * Returns: * %esi : Valid image source address (buffered or unbuffered) * %ecx : Actual offset within buffer of first payload block * CF set on error */ .section ".text16.early", "awx", @progbits .globl open_payload open_payload: /* Preserve registers */ pushl %eax pushw %bx pushl %edx pushl %edi pushw %bp pushw %es pushw %ds /* Retrieve bus:dev.fn from .prefix */ movw init_pci_busdevfn, %bx /* Set up %ds for access to .text16.early */ pushw %cs popw %ds /* Set up %es for access to flat address space */ xorw %ax, %ax movw %ax, %es /* Store bus:dev.fn to .text16.early */ movw %bx, payload_pci_busdevfn /* Get expansion ROM BAR current value */ movw $PCI_BAR_EXPROM, %di call pci_read_bar movl %eax, rom_bar_orig_value /* Get expansion ROM BAR size */ call pci_size_mem_bar_low movl %ecx, rom_bar_size /* Find a suitable memory BAR to use */ movw $PCI_BAR_0, %di /* %di is PCI BAR register */ xorw %bp, %bp /* %bp is increment */ find_mem_bar: /* Move to next BAR */ addw %bp, %di cmpw $PCI_BAR_5, %di jle 1f stc movl $0xbabababa, %esi /* Report "No suitable BAR" */ movl rom_bar_size, %ecx jmp 99f 1: movw $4, %bp /* Get BAR current value */ call pci_read_bar /* Skip non-existent BARs */ notl %eax testl %eax, %eax notl %eax jz find_mem_bar /* Skip I/O BARs */ testb $0x01, %al jnz find_mem_bar /* Set increment to 8 for 64-bit BARs */ testb $0x04, %al jz 1f movw $8, %bp 1: /* Skip 64-bit BARs with high dword set; we couldn't use this * address for the (32-bit) expansion ROM BAR anyway */ testl %edx, %edx jnz find_mem_bar /* Get low dword of BAR size */ call pci_size_mem_bar_low /* Skip BARs smaller than the expansion ROM BAR */ cmpl %ecx, rom_bar_size ja find_mem_bar /* We have a memory BAR with a 32-bit address that is large * enough to use. Store BAR number and original value. */ movw %di, stolen_bar_register movl %eax, stolen_bar_orig_value /* Remove flags from BAR address */ xorb %al, %al /* Write zero to our stolen BAR. This doesn't technically * disable it, but it's a pretty safe bet that the PCI bridge * won't pass through accesses to this region anyway. Note * that the high dword (if any) must already be zero. */ xorl %ecx, %ecx call pci_write_config_dword /* Enable expansion ROM BAR at stolen BAR's address */ movl %eax, %ecx orb $0x1, %cl movw $PCI_BAR_EXPROM, %di call pci_write_config_dword /* Locate our ROM image */ 1: movl $0xaa55, %ecx /* 55aa signature */ addr32 es cmpw %cx, (%eax) jne 2f movl $PCIR_SIGNATURE, %ecx /* PCIR signature */ addr32 es movzwl 0x18(%eax), %edx addr32 es cmpl %ecx, (%eax,%edx) jne 2f addr32 es cmpl $_build_id, build_id(%eax) /* iPXE build ID */ je 3f movl $0x80, %ecx /* Last image */ addr32 es testb %cl, 0x15(%eax,%edx) jnz 2f addr32 es movzwl 0x10(%eax,%edx), %ecx /* PCIR image length */ shll $9, %ecx addl %ecx, %eax jmp 1b 2: /* Failure */ stc movl %eax, %esi /* Report failure address */ jmp 99f 3: /* Copy payload to buffer, or set buffer address to BAR address */ testl %esi, %esi jz 1f /* We have a buffer; copy payload to it. Since .mrom is * designed specifically for real hardware, we assume that * flat real mode is working properly. (In the unlikely event * that this code is run inside a hypervisor that doesn't * properly support flat real mode, it will die horribly.) */ pushl %esi movl %esi, %edi movl %eax, %esi addr32 es movzbl 2(%esi), %ecx shll $7, %ecx addr32 es movzwl mpciheader_image_length(%esi,%ecx,4), %edx shll $7, %edx addl %edx, %ecx addr32 es rep movsl popl %esi jmp 2f 1: /* We have no buffer; set %esi to the BAR address */ movl %eax, %esi 2: /* Locate first payload block (after the dummy ROM header) */ addr32 es movzbl 2(%esi), %ecx shll $9, %ecx addl $_pprefix_skip, %ecx clc /* Restore registers and return */ 99: popw %ds popw %es popw %bp popl %edi popl %edx popw %bx popl %eax lret .size open_payload, . - open_payload .section ".text16.early.data", "aw", @progbits payload_pci_busdevfn: .word 0 .size payload_pci_busdevfn, . - payload_pci_busdevfn .section ".text16.early.data", "aw", @progbits rom_bar_orig_value: .long 0 .size rom_bar_orig_value, . - rom_bar_orig_value .section ".text16.early.data", "aw", @progbits rom_bar_size: .long 0 .size rom_bar_size, . - rom_bar_size .section ".text16.early.data", "aw", @progbits stolen_bar_register: .word 0 .size stolen_bar_register, . - stolen_bar_register .section ".text16.early.data", "aw", @progbits stolen_bar_orig_value: .long 0 .size stolen_bar_orig_value, . - stolen_bar_orig_value /* Restore original BAR values * * Parameters: * none * Returns: * none */ .section ".text16.early", "awx", @progbits .globl close_payload close_payload: /* Preserve registers */ pushw %bx pushw %di pushl %ecx pushw %ds /* Set up %ds for access to .text16.early */ pushw %cs popw %ds /* Retrieve stored bus:dev.fn */ movw payload_pci_busdevfn, %bx /* Restore expansion ROM BAR original value */ movw $PCI_BAR_EXPROM, %di movl rom_bar_orig_value, %ecx call pci_write_config_dword /* Restore stolen BAR original value */ movw stolen_bar_register, %di movl stolen_bar_orig_value, %ecx call pci_write_config_dword /* Restore registers and return */ popw %ds popl %ecx popw %di popw %bx lret .size close_payload, . - close_payload /* Get PCI BAR value * * Parameters: * %bx : PCI bus:dev.fn * %di : PCI BAR register number * Returns: * %edx:%eax : PCI BAR value */ .section ".text16.early", "awx", @progbits pci_read_bar: /* Preserve registers */ pushl %ecx pushw %di /* Read low dword value */ call pci_read_config_dword movl %ecx, %eax /* Read high dword value, if applicable */ xorl %edx, %edx andb $0x07, %cl cmpb $0x04, %cl jne 1f addw $4, %di call pci_read_config_dword movl %ecx, %edx 1: /* Restore registers and return */ popw %di popl %ecx ret .size pci_read_bar, . - pci_read_bar /* Get low dword of PCI memory BAR size * * Parameters: * %bx : PCI bus:dev.fn * %di : PCI BAR register number * %eax : Low dword of current PCI BAR value * Returns: * %ecx : PCI BAR size */ .section ".text16.early", "awx", @progbits pci_size_mem_bar_low: /* Preserve registers */ pushw %dx /* Disable memory accesses */ xorw %dx, %dx call pci_set_mem_access /* Write all ones to BAR */ xorl %ecx, %ecx decl %ecx call pci_write_config_dword /* Read back BAR */ call pci_read_config_dword /* Calculate size */ notl %ecx orb $0x0f, %cl incl %ecx /* Restore original value */ pushl %ecx movl %eax, %ecx call pci_write_config_dword popl %ecx /* Enable memory accesses */ movw $PCI_COMMAND_MEM, %dx call pci_set_mem_access /* Restore registers and return */ popw %dx ret .size pci_size_mem_bar_low, . - pci_size_mem_bar_low /* Read PCI config dword * * Parameters: * %bx : PCI bus:dev.fn * %di : PCI register number * Returns: * %ecx : Dword value */ .section ".text16.early", "awx", @progbits pci_read_config_dword: /* Preserve registers */ pushl %eax pushl %ebx pushl %edx /* Issue INT 0x1a,b10a */ movw $PCIBIOS_READ_CONFIG_DWORD, %ax int $0x1a /* Restore registers and return */ popl %edx popl %ebx popl %eax ret .size pci_read_config_dword, . - pci_read_config_dword /* Write PCI config dword * * Parameters: * %bx : PCI bus:dev.fn * %di : PCI register number * %ecx : PCI BAR value * Returns: * none */ .section ".text16.early", "awx", @progbits pci_write_config_dword: /* Preserve registers */ pushal /* Issue INT 0x1a,b10d */ movw $PCIBIOS_WRITE_CONFIG_DWORD, %ax int $0x1a /* Restore registers and return */ popal ret .size pci_write_config_dword, . - pci_write_config_dword /* Enable/disable memory access response in PCI command word * * Parameters: * %bx : PCI bus:dev.fn * %dx : PCI_COMMAND_MEM, or zero * Returns: * none */ .section ".text16.early", "awx", @progbits pci_set_mem_access: /* Preserve registers */ pushal /* Read current value of command register */ pushw %bx pushw %dx movw $PCI_COMMAND, %di movw $PCIBIOS_READ_CONFIG_WORD, %ax int $0x1a popw %dx popw %bx /* Set memory access enable as appropriate */ andw $~PCI_COMMAND_MEM, %cx orw %dx, %cx /* Write new value of command register */ movw $PCIBIOS_WRITE_CONFIG_WORD, %ax int $0x1a /* Restore registers and return */ popal ret .size pci_set_mem_access, . - pci_set_mem_access /* Update image source address for UNDI loader * * Parameters: * %esi : Image source address * Returns: * %esi : Image source address */ .section ".prefix", "ax", @progbits .globl undiloader_source undiloader_source: /* Always use expansion ROM BAR directly when installing via * the UNDI loader entry point, since the PMM-allocated block * may collide with whatever is calling the UNDI loader entry * point. */ xorl %esi, %esi ret /* Payload prefix * * We include a dummy ROM header to cover the "hidden" portion of the * overall ROM image. */ .globl _payload_align .equ _payload_align, 512 .section ".pprefix", "ax", @progbits .org 0x00 mromheader: .word 0xaa55 /* BIOS extension signature */ .byte 0x01 /* Dummy size (BIOS bug workaround) */ .org 0x18 .word mpciheader .org 0x1a .word 0 .size mromheader, . - mromheader .align 4 mpciheader: .ascii "PCIR" /* Signature */ .word pci_vendor_id /* Vendor identification */ .word pci_device_id /* Device identification */ .word 0x0000 /* Device list pointer */ .word mpciheader_len /* PCI data structure length */ .byte 0x03 /* PCI data structure revision */ .byte 0x02, 0x00, 0x00 /* Class code */ mpciheader_image_length: .word 0 /* Image length */ .word 0x0001 /* Revision level */ .byte 0xff /* Code type */ .byte 0x80 /* Last image indicator */ mpciheader_runtime_length: .word 0 /* Maximum run-time image length */ .word 0x0000 /* Configuration utility code header */ .word 0x0000 /* DMTF CLP entry point */ .equ mpciheader_len, . - mpciheader .size mpciheader, . - mpciheader .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ .ascii "APPW" .long mpciheader_image_length .long 512 .long 0 .ascii "APPW" .long mpciheader_runtime_length .long 512 .long 0 .previous /* Fix up additional image source size * */ .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ .ascii "ADPW" .long extra_size .long 512 .long 0 .previous