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 ROMPREFIX_EXCLUDE_PAYLOAD 1
34 #define ROMPREFIX_MORE_IMAGES 1
35 #define _rom_start _mrom_start
36 #include "romprefix.S"
42 /* Obtain access to payload by exposing the expansion ROM BAR at the
43 * address currently used by a suitably large memory BAR on the same
44 * device. The memory BAR is temporarily disabled. Using a memory
45 * BAR on the same device means that we don't have to worry about the
46 * configuration of any intermediate PCI bridges.
50 * %esi : Buffer for copy of image source (or zero if no buffer available)
51 * %ecx : Expected offset within buffer of first payload block
53 * %esi : Valid image source address (buffered or unbuffered)
54 * %ecx : Actual offset within buffer of first payload block
57 .section ".text16.early", "awx", @progbits
60 /* Preserve registers */
69 /* Retrieve bus:dev.fn from .prefix */
70 movw init_pci_busdevfn, %bx
72 /* Set up %ds for access to .text16.early */
76 /* Set up %es for access to flat address space */
80 /* Store bus:dev.fn to .text16.early */
81 movw %bx, payload_pci_busdevfn
83 /* Get expansion ROM BAR current value */
84 movw $PCI_BAR_EXPROM, %di
86 movl %eax, rom_bar_orig_value
88 /* Get expansion ROM BAR size */
89 call pci_size_mem_bar_low
90 movl %ecx, rom_bar_size
92 /* Find a suitable memory BAR to use */
93 movw $PCI_BAR_0, %di /* %di is PCI BAR register */
94 xorw %bp, %bp /* %bp is increment */
96 /* Move to next BAR */
104 /* Get BAR current value */
107 /* Skip non-existent BARs */
117 /* Set increment to 8 for 64-bit BARs */
122 /* Skip 64-bit BARs with high dword set; we couldn't use this
123 * address for the (32-bit) expansion ROM BAR anyway
128 /* Get low dword of BAR size */
129 call pci_size_mem_bar_low
131 /* Skip BARs smaller than the expansion ROM BAR */
132 cmpl %ecx, rom_bar_size
135 /* We have a memory BAR with a 32-bit address that is large
136 * enough to use. Store BAR number and original value.
138 movw %di, stolen_bar_register
139 movl %eax, stolen_bar_orig_value
141 /* Remove flags from BAR address */
144 /* Write zero to our stolen BAR. This doesn't technically
145 * disable it, but it's a pretty safe bet that the PCI bridge
146 * won't pass through accesses to this region anyway. Note
147 * that the high dword (if any) must already be zero.
150 call pci_write_config_dword
152 /* Enable expansion ROM BAR at stolen BAR's address */
155 movw $PCI_BAR_EXPROM, %di
156 call pci_write_config_dword
158 /* Locate our ROM image */
159 1: addr32 es cmpw $0xaa55, (%eax)
162 addr32 es cmpl $_build_id, build_id(%eax)
164 addr32 es movzbl 2(%eax), %ecx
170 /* Copy payload to buffer, or set buffer address to BAR address */
173 /* We have a buffer; copy payload to it. Since .mrom is
174 * designed specifically for real hardware, we assume that
175 * flat real mode is working properly. (In the unlikely event
176 * that this code is run inside a hypervisor that doesn't
177 * properly support flat real mode, it will die horribly.)
182 addr32 es movzbl 2(%esi), %ecx
184 addr32 es movzbl 2(%esi,%ecx,4), %edx
190 1: /* We have no buffer; set %esi to the BAR address */
194 /* Locate first payload block (after the dummy ROM header) */
195 addr32 es movzbl 2(%esi), %ecx
197 addl $_pprefix_skip, %ecx
200 /* Restore registers and return */
209 .size open_payload, . - open_payload
211 .section ".text16.early.data", "aw", @progbits
212 payload_pci_busdevfn:
214 .size payload_pci_busdevfn, . - payload_pci_busdevfn
216 .section ".text16.early.data", "aw", @progbits
219 .size rom_bar_orig_value, . - rom_bar_orig_value
221 .section ".text16.early.data", "aw", @progbits
224 .size rom_bar_size, . - rom_bar_size
226 .section ".text16.early.data", "aw", @progbits
229 .size stolen_bar_register, . - stolen_bar_register
231 .section ".text16.early.data", "aw", @progbits
232 stolen_bar_orig_value:
234 .size stolen_bar_orig_value, . - stolen_bar_orig_value
236 /* Restore original BAR values
243 .section ".text16.early", "awx", @progbits
246 /* Preserve registers */
252 /* Set up %ds for access to .text16.early */
256 /* Retrieve stored bus:dev.fn */
257 movw payload_pci_busdevfn, %bx
259 /* Restore expansion ROM BAR original value */
260 movw $PCI_BAR_EXPROM, %di
261 movl rom_bar_orig_value, %ecx
262 call pci_write_config_dword
264 /* Restore stolen BAR original value */
265 movw stolen_bar_register, %di
266 movl stolen_bar_orig_value, %ecx
267 call pci_write_config_dword
269 /* Restore registers and return */
275 .size close_payload, . - close_payload
280 * %bx : PCI bus:dev.fn
281 * %di : PCI BAR register number
283 * %edx:%eax : PCI BAR value
285 .section ".text16.early", "awx", @progbits
287 /* Preserve registers */
291 /* Read low dword value */
292 call pci_read_config_dword
295 /* Read high dword value, if applicable */
301 call pci_read_config_dword
304 /* Restore registers and return */
308 .size pci_read_bar, . - pci_read_bar
310 /* Get low dword of PCI memory BAR size
313 * %bx : PCI bus:dev.fn
314 * %di : PCI BAR register number
315 * %eax : Low dword of current PCI BAR value
317 * %ecx : PCI BAR size
319 .section ".text16.early", "awx", @progbits
320 pci_size_mem_bar_low:
321 /* Preserve registers */
324 /* Disable memory accesses */
326 call pci_set_mem_access
328 /* Write all ones to BAR */
331 call pci_write_config_dword
334 call pci_read_config_dword
341 /* Restore original value */
344 call pci_write_config_dword
347 /* Enable memory accesses */
348 movw $PCI_COMMAND_MEM, %dx
349 call pci_set_mem_access
351 /* Restore registers and return */
354 .size pci_size_mem_bar_low, . - pci_size_mem_bar_low
356 /* Read PCI config dword
359 * %bx : PCI bus:dev.fn
360 * %di : PCI register number
364 .section ".text16.early", "awx", @progbits
365 pci_read_config_dword:
366 /* Preserve registers */
371 /* Issue INT 0x1a,b10a */
372 movw $PCIBIOS_READ_CONFIG_DWORD, %ax
375 /* Restore registers and return */
380 .size pci_read_config_dword, . - pci_read_config_dword
382 /* Write PCI config dword
385 * %bx : PCI bus:dev.fn
386 * %di : PCI register number
387 * %ecx : PCI BAR value
391 .section ".text16.early", "awx", @progbits
392 pci_write_config_dword:
393 /* Preserve registers */
396 /* Issue INT 0x1a,b10d */
397 movw $PCIBIOS_WRITE_CONFIG_DWORD, %ax
400 /* Restore registers and return */
403 .size pci_write_config_dword, . - pci_write_config_dword
405 /* Enable/disable memory access response in PCI command word
408 * %bx : PCI bus:dev.fn
409 * %dx : PCI_COMMAND_MEM, or zero
413 .section ".text16.early", "awx", @progbits
415 /* Preserve registers */
418 /* Read current value of command register */
421 movw $PCI_COMMAND, %di
422 movw $PCIBIOS_READ_CONFIG_WORD, %ax
427 /* Set memory access enable as appropriate */
428 andw $~PCI_COMMAND_MEM, %cx
431 /* Write new value of command register */
432 movw $PCIBIOS_WRITE_CONFIG_WORD, %ax
435 /* Restore registers and return */
438 .size pci_set_mem_access, . - pci_set_mem_access
442 * We include a dummy ROM header to cover the "hidden" portion of the
445 .globl _payload_align
446 .equ _payload_align, 512
447 .section ".pprefix", "ax", @progbits
450 .word 0xaa55 /* BIOS extension signature */
451 mromheader_size: .byte 0 /* Size in 512-byte blocks */
456 .size mromheader, . - mromheader
458 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
460 .long mromheader_size
466 .ascii "PCIR" /* Signature */
467 .word pci_vendor_id /* Vendor identification */
468 .word pci_device_id /* Device identification */
469 .word 0x0000 /* Device list pointer */
470 .word mpciheader_len /* PCI data structure length */
471 .byte 0x03 /* PCI data structure revision */
472 .byte 0x02, 0x00, 0x00 /* Class code */
473 mpciheader_image_length:
474 .word 0 /* Image length */
475 .word 0x0001 /* Revision level */
476 .byte 0xff /* Code type */
477 .byte 0x80 /* Last image indicator */
478 mpciheader_runtime_length:
479 .word 0 /* Maximum run-time image length */
480 .word 0x0000 /* Configuration utility code header */
481 .word 0x0000 /* DMTF CLP entry point */
482 .equ mpciheader_len, . - mpciheader
483 .size mpciheader, . - mpciheader
485 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
487 .long mpciheader_image_length
491 .long mpciheader_runtime_length
496 /* Fix up additional image source size
499 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */