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
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
25 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
27 #define PCIBIOS_READ_CONFIG_WORD 0xb109
28 #define PCIBIOS_READ_CONFIG_DWORD 0xb10a
29 #define PCIBIOS_WRITE_CONFIG_WORD 0xb10c
30 #define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d
31 #define PCI_COMMAND 0x04
32 #define PCI_COMMAND_MEM 0x02
33 #define PCI_BAR_0 0x10
34 #define PCI_BAR_5 0x24
35 #define PCI_BAR_EXPROM 0x30
37 #define PCIR_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( 'R' << 24 ) )
39 #define ROMPREFIX_EXCLUDE_PAYLOAD 1
40 #define ROMPREFIX_MORE_IMAGES 1
41 #define _pcirom_start _mrom_start
42 #include "pciromprefix.S"
48 /* Obtain access to payload by exposing the expansion ROM BAR at the
49 * address currently used by a suitably large memory BAR on the same
50 * device. The memory BAR is temporarily disabled. Using a memory
51 * BAR on the same device means that we don't have to worry about the
52 * configuration of any intermediate PCI bridges.
56 * %esi : Buffer for copy of image source (or zero if no buffer available)
57 * %ecx : Expected offset within buffer of first payload block
59 * %esi : Valid image source address (buffered or unbuffered)
60 * %ecx : Actual offset within buffer of first payload block
63 .section ".text16.early", "awx", @progbits
66 /* Preserve registers */
75 /* Retrieve bus:dev.fn from .prefix */
76 movw init_pci_busdevfn, %bx
78 /* Set up %ds for access to .text16.early */
82 /* Set up %es for access to flat address space */
86 /* Store bus:dev.fn to .text16.early */
87 movw %bx, payload_pci_busdevfn
89 /* Get expansion ROM BAR current value */
90 movw $PCI_BAR_EXPROM, %di
92 movl %eax, rom_bar_orig_value
94 /* Get expansion ROM BAR size */
95 call pci_size_mem_bar_low
96 movl %ecx, rom_bar_size
98 /* Find a suitable memory BAR to use */
99 movw $PCI_BAR_0, %di /* %di is PCI BAR register */
100 xorw %bp, %bp /* %bp is increment */
102 /* Move to next BAR */
107 movl $0xbabababa, %esi /* Report "No suitable BAR" */
108 movl rom_bar_size, %ecx
112 /* Get BAR current value */
115 /* Skip non-existent BARs */
125 /* Set increment to 8 for 64-bit BARs */
130 /* Skip 64-bit BARs with high dword set; we couldn't use this
131 * address for the (32-bit) expansion ROM BAR anyway
136 /* Get low dword of BAR size */
137 call pci_size_mem_bar_low
139 /* Skip BARs smaller than the expansion ROM BAR */
140 cmpl %ecx, rom_bar_size
143 /* We have a memory BAR with a 32-bit address that is large
144 * enough to use. Store BAR number and original value.
146 movw %di, stolen_bar_register
147 movl %eax, stolen_bar_orig_value
149 /* Remove flags from BAR address */
152 /* Write zero to our stolen BAR. This doesn't technically
153 * disable it, but it's a pretty safe bet that the PCI bridge
154 * won't pass through accesses to this region anyway. Note
155 * that the high dword (if any) must already be zero.
158 call pci_write_config_dword
160 /* Enable expansion ROM BAR at stolen BAR's address */
163 movw $PCI_BAR_EXPROM, %di
164 call pci_write_config_dword
166 /* Locate our ROM image */
167 1: movl $0xaa55, %ecx /* 55aa signature */
168 addr32 es cmpw %cx, (%eax)
170 movl $PCIR_SIGNATURE, %ecx /* PCIR signature */
171 addr32 es movzwl 0x18(%eax), %edx
172 addr32 es cmpl %ecx, (%eax,%edx)
174 addr32 es cmpl $_build_id, build_id(%eax) /* iPXE build ID */
176 movl $0x80, %ecx /* Last image */
177 addr32 es testb %cl, 0x15(%eax,%edx)
179 addr32 es movzwl 0x10(%eax,%edx), %ecx /* PCIR image length */
185 movl %eax, %esi /* Report failure address */
189 /* Copy payload to buffer, or set buffer address to BAR address */
192 /* We have a buffer; copy payload to it. Since .mrom is
193 * designed specifically for real hardware, we assume that
194 * flat real mode is working properly. (In the unlikely event
195 * that this code is run inside a hypervisor that doesn't
196 * properly support flat real mode, it will die horribly.)
201 addr32 es movzbl 2(%esi), %ecx
203 addr32 es movzwl mpciheader_image_length(%esi,%ecx,4), %edx
209 1: /* We have no buffer; set %esi to the BAR address */
213 /* Locate first payload block (after the dummy ROM header) */
214 addr32 es movzbl 2(%esi), %ecx
216 addl $_pprefix_skip, %ecx
219 /* Restore registers and return */
228 .size open_payload, . - open_payload
230 .section ".text16.early.data", "aw", @progbits
231 payload_pci_busdevfn:
233 .size payload_pci_busdevfn, . - payload_pci_busdevfn
235 .section ".text16.early.data", "aw", @progbits
238 .size rom_bar_orig_value, . - rom_bar_orig_value
240 .section ".text16.early.data", "aw", @progbits
243 .size rom_bar_size, . - rom_bar_size
245 .section ".text16.early.data", "aw", @progbits
248 .size stolen_bar_register, . - stolen_bar_register
250 .section ".text16.early.data", "aw", @progbits
251 stolen_bar_orig_value:
253 .size stolen_bar_orig_value, . - stolen_bar_orig_value
255 /* Restore original BAR values
262 .section ".text16.early", "awx", @progbits
265 /* Preserve registers */
271 /* Set up %ds for access to .text16.early */
275 /* Retrieve stored bus:dev.fn */
276 movw payload_pci_busdevfn, %bx
278 /* Restore expansion ROM BAR original value */
279 movw $PCI_BAR_EXPROM, %di
280 movl rom_bar_orig_value, %ecx
281 call pci_write_config_dword
283 /* Restore stolen BAR original value */
284 movw stolen_bar_register, %di
285 movl stolen_bar_orig_value, %ecx
286 call pci_write_config_dword
288 /* Restore registers and return */
294 .size close_payload, . - close_payload
299 * %bx : PCI bus:dev.fn
300 * %di : PCI BAR register number
302 * %edx:%eax : PCI BAR value
304 .section ".text16.early", "awx", @progbits
306 /* Preserve registers */
310 /* Read low dword value */
311 call pci_read_config_dword
314 /* Read high dword value, if applicable */
320 call pci_read_config_dword
323 /* Restore registers and return */
327 .size pci_read_bar, . - pci_read_bar
329 /* Get low dword of PCI memory BAR size
332 * %bx : PCI bus:dev.fn
333 * %di : PCI BAR register number
334 * %eax : Low dword of current PCI BAR value
336 * %ecx : PCI BAR size
338 .section ".text16.early", "awx", @progbits
339 pci_size_mem_bar_low:
340 /* Preserve registers */
343 /* Disable memory accesses */
345 call pci_set_mem_access
347 /* Write all ones to BAR */
350 call pci_write_config_dword
353 call pci_read_config_dword
360 /* Restore original value */
363 call pci_write_config_dword
366 /* Enable memory accesses */
367 movw $PCI_COMMAND_MEM, %dx
368 call pci_set_mem_access
370 /* Restore registers and return */
373 .size pci_size_mem_bar_low, . - pci_size_mem_bar_low
375 /* Read PCI config dword
378 * %bx : PCI bus:dev.fn
379 * %di : PCI register number
383 .section ".text16.early", "awx", @progbits
384 pci_read_config_dword:
385 /* Preserve registers */
390 /* Issue INT 0x1a,b10a */
391 movw $PCIBIOS_READ_CONFIG_DWORD, %ax
394 /* Restore registers and return */
399 .size pci_read_config_dword, . - pci_read_config_dword
401 /* Write PCI config dword
404 * %bx : PCI bus:dev.fn
405 * %di : PCI register number
406 * %ecx : PCI BAR value
410 .section ".text16.early", "awx", @progbits
411 pci_write_config_dword:
412 /* Preserve registers */
415 /* Issue INT 0x1a,b10d */
416 movw $PCIBIOS_WRITE_CONFIG_DWORD, %ax
419 /* Restore registers and return */
422 .size pci_write_config_dword, . - pci_write_config_dword
424 /* Enable/disable memory access response in PCI command word
427 * %bx : PCI bus:dev.fn
428 * %dx : PCI_COMMAND_MEM, or zero
432 .section ".text16.early", "awx", @progbits
434 /* Preserve registers */
437 /* Read current value of command register */
440 movw $PCI_COMMAND, %di
441 movw $PCIBIOS_READ_CONFIG_WORD, %ax
446 /* Set memory access enable as appropriate */
447 andw $~PCI_COMMAND_MEM, %cx
450 /* Write new value of command register */
451 movw $PCIBIOS_WRITE_CONFIG_WORD, %ax
454 /* Restore registers and return */
457 .size pci_set_mem_access, . - pci_set_mem_access
461 * We include a dummy ROM header to cover the "hidden" portion of the
464 .globl _payload_align
465 .equ _payload_align, 512
466 .section ".pprefix", "ax", @progbits
469 .word 0xaa55 /* BIOS extension signature */
470 .byte 0x01 /* Dummy size (BIOS bug workaround) */
475 .size mromheader, . - mromheader
478 .ascii "PCIR" /* Signature */
479 .word pci_vendor_id /* Vendor identification */
480 .word pci_device_id /* Device identification */
481 .word 0x0000 /* Device list pointer */
482 .word mpciheader_len /* PCI data structure length */
483 .byte 0x03 /* PCI data structure revision */
484 .byte 0x02, 0x00, 0x00 /* Class code */
485 mpciheader_image_length:
486 .word 0 /* Image length */
487 .word 0x0001 /* Revision level */
488 .byte 0xff /* Code type */
489 .byte 0x80 /* Last image indicator */
490 mpciheader_runtime_length:
491 .word 0 /* Maximum run-time image length */
492 .word 0x0000 /* Configuration utility code header */
493 .word 0x0000 /* DMTF CLP entry point */
494 .equ mpciheader_len, . - mpciheader
495 .size mpciheader, . - mpciheader
497 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
499 .long mpciheader_image_length
503 .long mpciheader_runtime_length
508 /* Fix up additional image source size
511 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */