2 * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 FILE_LICENCE ( GPL2_OR_LATER )
23 #define PCIBIOS_READ_CONFIG_WORD 0xb109
24 #define PCIBIOS_READ_CONFIG_DWORD 0xb10a
25 #define PCIBIOS_WRITE_CONFIG_WORD 0xb10c
26 #define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d
27 #define PCI_COMMAND 0x04
28 #define PCI_COMMAND_MEM 0x02
29 #define PCI_BAR_0 0x10
30 #define PCI_BAR_5 0x24
31 #define PCI_BAR_EXPROM 0x30
33 #define PCIR_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( 'R' << 24 ) )
35 #define ROMPREFIX_EXCLUDE_PAYLOAD 1
36 #define ROMPREFIX_MORE_IMAGES 1
37 #define _pcirom_start _mrom_start
38 #include "pciromprefix.S"
44 /* Obtain access to payload by exposing the expansion ROM BAR at the
45 * address currently used by a suitably large memory BAR on the same
46 * device. The memory BAR is temporarily disabled. Using a memory
47 * BAR on the same device means that we don't have to worry about the
48 * configuration of any intermediate PCI bridges.
52 * %esi : Buffer for copy of image source (or zero if no buffer available)
53 * %ecx : Expected offset within buffer of first payload block
55 * %esi : Valid image source address (buffered or unbuffered)
56 * %ecx : Actual offset within buffer of first payload block
59 .section ".text16.early", "awx", @progbits
62 /* Preserve registers */
71 /* Retrieve bus:dev.fn from .prefix */
72 movw init_pci_busdevfn, %bx
74 /* Set up %ds for access to .text16.early */
78 /* Set up %es for access to flat address space */
82 /* Store bus:dev.fn to .text16.early */
83 movw %bx, payload_pci_busdevfn
85 /* Get expansion ROM BAR current value */
86 movw $PCI_BAR_EXPROM, %di
88 movl %eax, rom_bar_orig_value
90 /* Get expansion ROM BAR size */
91 call pci_size_mem_bar_low
92 movl %ecx, rom_bar_size
94 /* Find a suitable memory BAR to use */
95 movw $PCI_BAR_0, %di /* %di is PCI BAR register */
96 xorw %bp, %bp /* %bp is increment */
98 /* Move to next BAR */
103 movl $0xbabababa, %esi /* Report "No suitable BAR" */
104 movl rom_bar_size, %ecx
108 /* Get BAR current value */
111 /* Skip non-existent BARs */
121 /* Set increment to 8 for 64-bit BARs */
126 /* Skip 64-bit BARs with high dword set; we couldn't use this
127 * address for the (32-bit) expansion ROM BAR anyway
132 /* Get low dword of BAR size */
133 call pci_size_mem_bar_low
135 /* Skip BARs smaller than the expansion ROM BAR */
136 cmpl %ecx, rom_bar_size
139 /* We have a memory BAR with a 32-bit address that is large
140 * enough to use. Store BAR number and original value.
142 movw %di, stolen_bar_register
143 movl %eax, stolen_bar_orig_value
145 /* Remove flags from BAR address */
148 /* Write zero to our stolen BAR. This doesn't technically
149 * disable it, but it's a pretty safe bet that the PCI bridge
150 * won't pass through accesses to this region anyway. Note
151 * that the high dword (if any) must already be zero.
154 call pci_write_config_dword
156 /* Enable expansion ROM BAR at stolen BAR's address */
159 movw $PCI_BAR_EXPROM, %di
160 call pci_write_config_dword
162 /* Locate our ROM image */
163 1: movl $0xaa55, %ecx /* 55aa signature */
164 addr32 es cmpw %cx, (%eax)
166 movl $PCIR_SIGNATURE, %ecx /* PCIR signature */
167 addr32 es movzwl 0x18(%eax), %edx
168 addr32 es cmpl %ecx, (%eax,%edx)
170 addr32 es cmpl $_build_id, build_id(%eax) /* iPXE build ID */
172 movl $0x80, %ecx /* Last image */
173 addr32 es testb %cl, 0x15(%eax,%edx)
175 addr32 es movzwl 0x10(%eax,%edx), %ecx /* PCIR image length */
181 movl %eax, %esi /* Report failure address */
185 /* Copy payload to buffer, or set buffer address to BAR address */
188 /* We have a buffer; copy payload to it. Since .mrom is
189 * designed specifically for real hardware, we assume that
190 * flat real mode is working properly. (In the unlikely event
191 * that this code is run inside a hypervisor that doesn't
192 * properly support flat real mode, it will die horribly.)
197 addr32 es movzbl 2(%esi), %ecx
199 addr32 es movzwl mpciheader_image_length(%esi,%ecx,4), %edx
205 1: /* We have no buffer; set %esi to the BAR address */
209 /* Locate first payload block (after the dummy ROM header) */
210 addr32 es movzbl 2(%esi), %ecx
212 addl $_pprefix_skip, %ecx
215 /* Restore registers and return */
224 .size open_payload, . - open_payload
226 .section ".text16.early.data", "aw", @progbits
227 payload_pci_busdevfn:
229 .size payload_pci_busdevfn, . - payload_pci_busdevfn
231 .section ".text16.early.data", "aw", @progbits
234 .size rom_bar_orig_value, . - rom_bar_orig_value
236 .section ".text16.early.data", "aw", @progbits
239 .size rom_bar_size, . - rom_bar_size
241 .section ".text16.early.data", "aw", @progbits
244 .size stolen_bar_register, . - stolen_bar_register
246 .section ".text16.early.data", "aw", @progbits
247 stolen_bar_orig_value:
249 .size stolen_bar_orig_value, . - stolen_bar_orig_value
251 /* Restore original BAR values
258 .section ".text16.early", "awx", @progbits
261 /* Preserve registers */
267 /* Set up %ds for access to .text16.early */
271 /* Retrieve stored bus:dev.fn */
272 movw payload_pci_busdevfn, %bx
274 /* Restore expansion ROM BAR original value */
275 movw $PCI_BAR_EXPROM, %di
276 movl rom_bar_orig_value, %ecx
277 call pci_write_config_dword
279 /* Restore stolen BAR original value */
280 movw stolen_bar_register, %di
281 movl stolen_bar_orig_value, %ecx
282 call pci_write_config_dword
284 /* Restore registers and return */
290 .size close_payload, . - close_payload
295 * %bx : PCI bus:dev.fn
296 * %di : PCI BAR register number
298 * %edx:%eax : PCI BAR value
300 .section ".text16.early", "awx", @progbits
302 /* Preserve registers */
306 /* Read low dword value */
307 call pci_read_config_dword
310 /* Read high dword value, if applicable */
316 call pci_read_config_dword
319 /* Restore registers and return */
323 .size pci_read_bar, . - pci_read_bar
325 /* Get low dword of PCI memory BAR size
328 * %bx : PCI bus:dev.fn
329 * %di : PCI BAR register number
330 * %eax : Low dword of current PCI BAR value
332 * %ecx : PCI BAR size
334 .section ".text16.early", "awx", @progbits
335 pci_size_mem_bar_low:
336 /* Preserve registers */
339 /* Disable memory accesses */
341 call pci_set_mem_access
343 /* Write all ones to BAR */
346 call pci_write_config_dword
349 call pci_read_config_dword
356 /* Restore original value */
359 call pci_write_config_dword
362 /* Enable memory accesses */
363 movw $PCI_COMMAND_MEM, %dx
364 call pci_set_mem_access
366 /* Restore registers and return */
369 .size pci_size_mem_bar_low, . - pci_size_mem_bar_low
371 /* Read PCI config dword
374 * %bx : PCI bus:dev.fn
375 * %di : PCI register number
379 .section ".text16.early", "awx", @progbits
380 pci_read_config_dword:
381 /* Preserve registers */
386 /* Issue INT 0x1a,b10a */
387 movw $PCIBIOS_READ_CONFIG_DWORD, %ax
390 /* Restore registers and return */
395 .size pci_read_config_dword, . - pci_read_config_dword
397 /* Write PCI config dword
400 * %bx : PCI bus:dev.fn
401 * %di : PCI register number
402 * %ecx : PCI BAR value
406 .section ".text16.early", "awx", @progbits
407 pci_write_config_dword:
408 /* Preserve registers */
411 /* Issue INT 0x1a,b10d */
412 movw $PCIBIOS_WRITE_CONFIG_DWORD, %ax
415 /* Restore registers and return */
418 .size pci_write_config_dword, . - pci_write_config_dword
420 /* Enable/disable memory access response in PCI command word
423 * %bx : PCI bus:dev.fn
424 * %dx : PCI_COMMAND_MEM, or zero
428 .section ".text16.early", "awx", @progbits
430 /* Preserve registers */
433 /* Read current value of command register */
436 movw $PCI_COMMAND, %di
437 movw $PCIBIOS_READ_CONFIG_WORD, %ax
442 /* Set memory access enable as appropriate */
443 andw $~PCI_COMMAND_MEM, %cx
446 /* Write new value of command register */
447 movw $PCIBIOS_WRITE_CONFIG_WORD, %ax
450 /* Restore registers and return */
453 .size pci_set_mem_access, . - pci_set_mem_access
457 * We include a dummy ROM header to cover the "hidden" portion of the
460 .globl _payload_align
461 .equ _payload_align, 512
462 .section ".pprefix", "ax", @progbits
465 .word 0xaa55 /* BIOS extension signature */
470 .size mromheader, . - mromheader
473 .ascii "PCIR" /* Signature */
474 .word pci_vendor_id /* Vendor identification */
475 .word pci_device_id /* Device identification */
476 .word 0x0000 /* Device list pointer */
477 .word mpciheader_len /* PCI data structure length */
478 .byte 0x03 /* PCI data structure revision */
479 .byte 0x02, 0x00, 0x00 /* Class code */
480 mpciheader_image_length:
481 .word 0 /* Image length */
482 .word 0x0001 /* Revision level */
483 .byte 0xff /* Code type */
484 .byte 0x80 /* Last image indicator */
485 mpciheader_runtime_length:
486 .word 0 /* Maximum run-time image length */
487 .word 0x0000 /* Configuration utility code header */
488 .word 0x0000 /* DMTF CLP entry point */
489 .equ mpciheader_len, . - mpciheader
490 .size mpciheader, . - mpciheader
492 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
494 .long mpciheader_image_length
498 .long mpciheader_runtime_length
503 /* Fix up additional image source size
506 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */