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., 675 Mass Ave, Cambridge, MA 02139, USA.
20 FILE_LICENCE ( GPL2_OR_LATER )
22 #define PCIBIOS_READ_CONFIG_WORD 0xb109
23 #define PCIBIOS_READ_CONFIG_DWORD 0xb10a
24 #define PCIBIOS_WRITE_CONFIG_WORD 0xb10c
25 #define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d
26 #define PCI_COMMAND 0x04
27 #define PCI_COMMAND_MEM 0x02
28 #define PCI_BAR_0 0x10
29 #define PCI_BAR_5 0x24
30 #define PCI_BAR_EXPROM 0x30
32 #define ROMPREFIX_EXCLUDE_PAYLOAD 1
33 #include "romprefix.S"
39 /* Obtain access to payload by exposing the expansion ROM BAR at the
40 * address currently used by a suitably large memory BAR on the same
41 * device. The memory BAR is temporarily disabled. Using a memory
42 * BAR on the same device means that we don't have to worry about the
43 * configuration of any intermediate PCI bridges.
47 * %esi : Buffer for copy of image source (or zero if no buffer available)
49 * %esi : Valid image source address (buffered or unbuffered)
52 .section ".text16.early", "awx", @progbits
55 /* Preserve registers */
64 /* Retrieve bus:dev.fn and image source length from .prefix */
65 movw init_pci_busdevfn, %bx
66 movl image_source_len_dword, %ecx
68 /* Set up %ds for access to .text16.early */
72 /* Store bus:dev.fn and image source length to .text16.early */
73 movw %bx, payload_pci_busdevfn
74 movl %ecx, rom_bar_copy_len_dword
76 /* Get expansion ROM BAR current value */
77 movw $PCI_BAR_EXPROM, %di
79 movl %eax, rom_bar_orig_value
81 /* Get expansion ROM BAR size */
82 call pci_size_mem_bar_low
83 movl %ecx, rom_bar_size
85 /* Find a suitable memory BAR to use */
86 movw $PCI_BAR_0, %di /* %di is PCI BAR register */
87 xorw %bp, %bp /* %bp is increment */
89 /* Move to next BAR */
97 /* Get BAR current value */
100 /* Skip non-existent BARs */
110 /* Set increment to 8 for 64-bit BARs */
115 /* Skip 64-bit BARs with high dword set; we couldn't use this
116 * address for the (32-bit) expansion ROM BAR anyway
121 /* Get low dword of BAR size */
122 call pci_size_mem_bar_low
124 /* Skip BARs smaller than the expansion ROM BAR */
125 cmpl %ecx, rom_bar_size
128 /* We have a memory BAR with a 32-bit address that is large
129 * enough to use. Store BAR number and original value.
131 movw %di, stolen_bar_register
132 movl %eax, stolen_bar_orig_value
134 /* Remove flags from BAR address */
137 /* Write zero to our stolen BAR. This doesn't technically
138 * disable it, but it's a pretty safe bet that the PCI bridge
139 * won't pass through accesses to this region anyway. Note
140 * that the high dword (if any) must already be zero.
143 call pci_write_config_dword
145 /* Enable expansion ROM BAR at stolen BAR's address */
148 movw $PCI_BAR_EXPROM, %di
149 call pci_write_config_dword
151 /* Copy payload to buffer, or set buffer address to BAR address */
154 /* We have a buffer; copy payload to it */
159 movl rom_bar_copy_len_dword, %ecx
166 1: /* We have no buffer; set %esi to the BAR address */
171 /* Restore registers and return */
180 .size open_payload, . - open_payload
182 .section ".text16.early.data", "aw", @progbits
183 payload_pci_busdevfn:
185 .size payload_pci_busdevfn, . - payload_pci_busdevfn
187 .section ".text16.early.data", "aw", @progbits
190 .size rom_bar_orig_value, . - rom_bar_orig_value
192 .section ".text16.early.data", "aw", @progbits
195 .size rom_bar_size, . - rom_bar_size
197 .section ".text16.early.data", "aw", @progbits
198 rom_bar_copy_len_dword:
200 .size rom_bar_copy_len_dword, . - rom_bar_copy_len_dword
202 .section ".text16.early.data", "aw", @progbits
205 .size stolen_bar_register, . - stolen_bar_register
207 .section ".text16.early.data", "aw", @progbits
208 stolen_bar_orig_value:
210 .size stolen_bar_orig_value, . - stolen_bar_orig_value
212 /* Restore original BAR values
219 .section ".text16.early", "awx", @progbits
222 /* Preserve registers */
228 /* Set up %ds for access to .text16.early */
232 /* Retrieve stored bus:dev.fn */
233 movw payload_pci_busdevfn, %bx
235 /* Restore expansion ROM BAR original value */
236 movw $PCI_BAR_EXPROM, %di
237 movl rom_bar_orig_value, %ecx
238 call pci_write_config_dword
240 /* Restore stolen BAR original value */
241 movw stolen_bar_register, %di
242 movl stolen_bar_orig_value, %ecx
243 call pci_write_config_dword
245 /* Restore registers and return */
251 .size close_payload, . - close_payload
256 * %bx : PCI bus:dev.fn
257 * %di : PCI BAR register number
259 * %edx:%eax : PCI BAR value
261 .section ".text16.early", "awx", @progbits
263 /* Preserve registers */
267 /* Read low dword value */
268 call pci_read_config_dword
271 /* Read high dword value, if applicable */
277 call pci_read_config_dword
280 /* Restore registers and return */
284 .size pci_read_bar, . - pci_read_bar
286 /* Get low dword of PCI memory BAR size
289 * %bx : PCI bus:dev.fn
290 * %di : PCI BAR register number
291 * %eax : Low dword of current PCI BAR value
293 * %ecx : PCI BAR size
295 .section ".text16.early", "awx", @progbits
296 pci_size_mem_bar_low:
297 /* Preserve registers */
300 /* Disable memory accesses */
302 call pci_set_mem_access
304 /* Write all ones to BAR */
307 call pci_write_config_dword
310 call pci_read_config_dword
317 /* Restore original value */
320 call pci_write_config_dword
323 /* Enable memory accesses */
324 movw $PCI_COMMAND_MEM, %dx
325 call pci_set_mem_access
327 /* Restore registers and return */
330 .size pci_size_mem_bar_low, . - pci_size_mem_bar_low
332 /* Read PCI config dword
335 * %bx : PCI bus:dev.fn
336 * %di : PCI register number
340 .section ".text16.early", "awx", @progbits
341 pci_read_config_dword:
342 /* Preserve registers */
347 /* Issue INT 0x1a,b10a */
348 movw $PCIBIOS_READ_CONFIG_DWORD, %ax
351 /* Restore registers and return */
356 .size pci_read_config_dword, . - pci_read_config_dword
358 /* Write PCI config dword
361 * %bx : PCI bus:dev.fn
362 * %di : PCI register number
363 * %ecx : PCI BAR value
367 .section ".text16.early", "awx", @progbits
368 pci_write_config_dword:
369 /* Preserve registers */
372 /* Issue INT 0x1a,b10d */
373 movw $PCIBIOS_WRITE_CONFIG_DWORD, %ax
376 /* Restore registers and return */
379 .size pci_write_config_dword, . - pci_write_config_dword
381 /* Enable/disable memory access response in PCI command word
384 * %bx : PCI bus:dev.fn
385 * %dx : PCI_COMMAND_MEM, or zero
389 .section ".text16.early", "awx", @progbits
391 /* Preserve registers */
394 /* Read current value of command register */
397 movw $PCI_COMMAND, %di
398 movw $PCIBIOS_READ_CONFIG_WORD, %ax
403 /* Set memory access enable as appropriate */
404 andw $~PCI_COMMAND_MEM, %cx
407 /* Write new value of command register */
408 movw $PCIBIOS_WRITE_CONFIG_WORD, %ax
411 /* Restore registers and return */
414 .size pci_set_mem_access, . - pci_set_mem_access
416 /* Image source area length (in dwords)
419 .section ".prefix", "ax", @progbits
420 image_source_len_dword:
422 .size image_source_len_dword, . - image_source_len_dword
423 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
425 .long image_source_len_dword