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
#define _rom_start _mrom_start
34
#include "romprefix.S"
40
/* Obtain access to payload by exposing the expansion ROM BAR at the
41
* address currently used by a suitably large memory BAR on the same
42
* device. The memory BAR is temporarily disabled. Using a memory
43
* BAR on the same device means that we don't have to worry about the
44
* configuration of any intermediate PCI bridges.
48
* %esi : Buffer for copy of image source (or zero if no buffer available)
50
* %esi : Valid image source address (buffered or unbuffered)
53
.section ".text16.early", "awx", @progbits
56
/* Preserve registers */
65
/* Retrieve bus:dev.fn and image source length from .prefix */
66
movw init_pci_busdevfn, %bx
67
movl image_source_len_dword, %ecx
69
/* Set up %ds for access to .text16.early */
73
/* Store bus:dev.fn and image source length to .text16.early */
74
movw %bx, payload_pci_busdevfn
75
movl %ecx, rom_bar_copy_len_dword
77
/* Get expansion ROM BAR current value */
78
movw $PCI_BAR_EXPROM, %di
80
movl %eax, rom_bar_orig_value
82
/* Get expansion ROM BAR size */
83
call pci_size_mem_bar_low
84
movl %ecx, rom_bar_size
86
/* Find a suitable memory BAR to use */
87
movw $PCI_BAR_0, %di /* %di is PCI BAR register */
88
xorw %bp, %bp /* %bp is increment */
90
/* Move to next BAR */
98
/* Get BAR current value */
101
/* Skip non-existent BARs */
111
/* Set increment to 8 for 64-bit BARs */
116
/* Skip 64-bit BARs with high dword set; we couldn't use this
117
* address for the (32-bit) expansion ROM BAR anyway
122
/* Get low dword of BAR size */
123
call pci_size_mem_bar_low
125
/* Skip BARs smaller than the expansion ROM BAR */
126
cmpl %ecx, rom_bar_size
129
/* We have a memory BAR with a 32-bit address that is large
130
* enough to use. Store BAR number and original value.
132
movw %di, stolen_bar_register
133
movl %eax, stolen_bar_orig_value
135
/* Remove flags from BAR address */
138
/* Write zero to our stolen BAR. This doesn't technically
139
* disable it, but it's a pretty safe bet that the PCI bridge
140
* won't pass through accesses to this region anyway. Note
141
* that the high dword (if any) must already be zero.
144
call pci_write_config_dword
146
/* Enable expansion ROM BAR at stolen BAR's address */
149
movw $PCI_BAR_EXPROM, %di
150
call pci_write_config_dword
152
/* Copy payload to buffer, or set buffer address to BAR address */
155
/* We have a buffer; copy payload to it. Since .mrom is
156
* designed specifically for real hardware, we assume that
157
* flat real mode is working properly. (In the unlikely event
158
* that this code is run inside a hypervisor that doesn't
159
* properly support flat real mode, it will die horribly.)
165
movl rom_bar_copy_len_dword, %ecx
172
1: /* We have no buffer; set %esi to the BAR address */
177
/* Restore registers and return */
186
.size open_payload, . - open_payload
188
.section ".text16.early.data", "aw", @progbits
189
payload_pci_busdevfn:
191
.size payload_pci_busdevfn, . - payload_pci_busdevfn
193
.section ".text16.early.data", "aw", @progbits
196
.size rom_bar_orig_value, . - rom_bar_orig_value
198
.section ".text16.early.data", "aw", @progbits
201
.size rom_bar_size, . - rom_bar_size
203
.section ".text16.early.data", "aw", @progbits
204
rom_bar_copy_len_dword:
206
.size rom_bar_copy_len_dword, . - rom_bar_copy_len_dword
208
.section ".text16.early.data", "aw", @progbits
211
.size stolen_bar_register, . - stolen_bar_register
213
.section ".text16.early.data", "aw", @progbits
214
stolen_bar_orig_value:
216
.size stolen_bar_orig_value, . - stolen_bar_orig_value
218
/* Restore original BAR values
225
.section ".text16.early", "awx", @progbits
228
/* Preserve registers */
234
/* Set up %ds for access to .text16.early */
238
/* Retrieve stored bus:dev.fn */
239
movw payload_pci_busdevfn, %bx
241
/* Restore expansion ROM BAR original value */
242
movw $PCI_BAR_EXPROM, %di
243
movl rom_bar_orig_value, %ecx
244
call pci_write_config_dword
246
/* Restore stolen BAR original value */
247
movw stolen_bar_register, %di
248
movl stolen_bar_orig_value, %ecx
249
call pci_write_config_dword
251
/* Restore registers and return */
257
.size close_payload, . - close_payload
262
* %bx : PCI bus:dev.fn
263
* %di : PCI BAR register number
265
* %edx:%eax : PCI BAR value
267
.section ".text16.early", "awx", @progbits
269
/* Preserve registers */
273
/* Read low dword value */
274
call pci_read_config_dword
277
/* Read high dword value, if applicable */
283
call pci_read_config_dword
286
/* Restore registers and return */
290
.size pci_read_bar, . - pci_read_bar
292
/* Get low dword of PCI memory BAR size
295
* %bx : PCI bus:dev.fn
296
* %di : PCI BAR register number
297
* %eax : Low dword of current PCI BAR value
299
* %ecx : PCI BAR size
301
.section ".text16.early", "awx", @progbits
302
pci_size_mem_bar_low:
303
/* Preserve registers */
306
/* Disable memory accesses */
308
call pci_set_mem_access
310
/* Write all ones to BAR */
313
call pci_write_config_dword
316
call pci_read_config_dword
323
/* Restore original value */
326
call pci_write_config_dword
329
/* Enable memory accesses */
330
movw $PCI_COMMAND_MEM, %dx
331
call pci_set_mem_access
333
/* Restore registers and return */
336
.size pci_size_mem_bar_low, . - pci_size_mem_bar_low
338
/* Read PCI config dword
341
* %bx : PCI bus:dev.fn
342
* %di : PCI register number
346
.section ".text16.early", "awx", @progbits
347
pci_read_config_dword:
348
/* Preserve registers */
353
/* Issue INT 0x1a,b10a */
354
movw $PCIBIOS_READ_CONFIG_DWORD, %ax
357
/* Restore registers and return */
362
.size pci_read_config_dword, . - pci_read_config_dword
364
/* Write PCI config dword
367
* %bx : PCI bus:dev.fn
368
* %di : PCI register number
369
* %ecx : PCI BAR value
373
.section ".text16.early", "awx", @progbits
374
pci_write_config_dword:
375
/* Preserve registers */
378
/* Issue INT 0x1a,b10d */
379
movw $PCIBIOS_WRITE_CONFIG_DWORD, %ax
382
/* Restore registers and return */
385
.size pci_write_config_dword, . - pci_write_config_dword
387
/* Enable/disable memory access response in PCI command word
390
* %bx : PCI bus:dev.fn
391
* %dx : PCI_COMMAND_MEM, or zero
395
.section ".text16.early", "awx", @progbits
397
/* Preserve registers */
400
/* Read current value of command register */
403
movw $PCI_COMMAND, %di
404
movw $PCIBIOS_READ_CONFIG_WORD, %ax
409
/* Set memory access enable as appropriate */
410
andw $~PCI_COMMAND_MEM, %cx
413
/* Write new value of command register */
414
movw $PCIBIOS_WRITE_CONFIG_WORD, %ax
417
/* Restore registers and return */
420
.size pci_set_mem_access, . - pci_set_mem_access
422
/* Image source area length (in dwords)
425
.section ".prefix", "ax", @progbits
426
image_source_len_dword:
428
.size image_source_len_dword, . - image_source_len_dword
429
.section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
431
.long image_source_len_dword