|
|
1.1 root 1: /*
2: * Copyright (C) 2010 Michael Brown <[email protected]>.
3: *
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.
8: *
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.
13: *
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.
17: *
18: */
19:
20: FILE_LICENCE ( GPL2_OR_LATER )
21:
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
31:
32: #define ROMPREFIX_EXCLUDE_PAYLOAD 1
33: #define _rom_start _mrom_start
34: #include "romprefix.S"
35:
36: .text
37: .arch i386
38: .code16
39:
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.
45: *
46: * Parameters:
47: * %ds:0000 : Prefix
48: * %esi : Buffer for copy of image source (or zero if no buffer available)
49: * Returns:
50: * %esi : Valid image source address (buffered or unbuffered)
51: * CF set on error
52: */
53: .section ".text16.early", "awx", @progbits
54: .globl open_payload
55: open_payload:
56: /* Preserve registers */
57: pushl %eax
58: pushw %bx
59: pushl %ecx
60: pushl %edx
61: pushl %edi
62: pushw %bp
63: pushw %ds
64:
65: /* Retrieve bus:dev.fn and image source length from .prefix */
66: movw init_pci_busdevfn, %bx
67: movl image_source_len_dword, %ecx
68:
69: /* Set up %ds for access to .text16.early */
70: pushw %cs
71: popw %ds
72:
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
76:
77: /* Get expansion ROM BAR current value */
78: movw $PCI_BAR_EXPROM, %di
79: call pci_read_bar
80: movl %eax, rom_bar_orig_value
81:
82: /* Get expansion ROM BAR size */
83: call pci_size_mem_bar_low
84: movl %ecx, rom_bar_size
85:
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 */
89: find_mem_bar:
90: /* Move to next BAR */
91: addw %bp, %di
92: cmpw $PCI_BAR_5, %di
93: jle 1f
94: stc
95: jmp 99f
96: 1: movw $4, %bp
97:
98: /* Get BAR current value */
99: call pci_read_bar
100:
101: /* Skip non-existent BARs */
102: notl %eax
103: testl %eax, %eax
104: notl %eax
105: jz find_mem_bar
106:
107: /* Skip I/O BARs */
108: testb $0x01, %al
109: jnz find_mem_bar
110:
111: /* Set increment to 8 for 64-bit BARs */
112: testb $0x04, %al
113: jz 1f
114: movw $8, %bp
115: 1:
116: /* Skip 64-bit BARs with high dword set; we couldn't use this
117: * address for the (32-bit) expansion ROM BAR anyway
118: */
119: testl %edx, %edx
120: jnz find_mem_bar
121:
122: /* Get low dword of BAR size */
123: call pci_size_mem_bar_low
124:
125: /* Skip BARs smaller than the expansion ROM BAR */
126: cmpl %ecx, rom_bar_size
127: ja find_mem_bar
128:
129: /* We have a memory BAR with a 32-bit address that is large
130: * enough to use. Store BAR number and original value.
131: */
132: movw %di, stolen_bar_register
133: movl %eax, stolen_bar_orig_value
134:
135: /* Remove flags from BAR address */
136: xorb %al, %al
137:
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.
142: */
143: xorl %ecx, %ecx
144: call pci_write_config_dword
145:
146: /* Enable expansion ROM BAR at stolen BAR's address */
147: movl %eax, %ecx
148: orb $0x1, %cl
149: movw $PCI_BAR_EXPROM, %di
150: call pci_write_config_dword
151:
152: /* Copy payload to buffer, or set buffer address to BAR address */
153: testl %esi, %esi
154: jz 1f
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.)
160: */
161: pushl %esi
162: pushw %es
163: movl %esi, %edi
164: movl %eax, %esi
165: movl rom_bar_copy_len_dword, %ecx
166: xorw %ax, %ax
167: movw %ax, %es
168: addr32 es rep movsl
169: popw %es
170: popl %esi
171: jmp 2f
172: 1: /* We have no buffer; set %esi to the BAR address */
173: movl %eax, %esi
174: 2:
175:
176: clc
177: /* Restore registers and return */
178: 99: popw %ds
179: popw %bp
180: popl %edi
181: popl %edx
182: popl %ecx
183: popw %bx
184: popl %eax
185: lret
186: .size open_payload, . - open_payload
187:
188: .section ".text16.early.data", "aw", @progbits
189: payload_pci_busdevfn:
190: .word 0
191: .size payload_pci_busdevfn, . - payload_pci_busdevfn
192:
193: .section ".text16.early.data", "aw", @progbits
194: rom_bar_orig_value:
195: .long 0
196: .size rom_bar_orig_value, . - rom_bar_orig_value
197:
198: .section ".text16.early.data", "aw", @progbits
199: rom_bar_size:
200: .long 0
201: .size rom_bar_size, . - rom_bar_size
202:
203: .section ".text16.early.data", "aw", @progbits
204: rom_bar_copy_len_dword:
205: .long 0
206: .size rom_bar_copy_len_dword, . - rom_bar_copy_len_dword
207:
208: .section ".text16.early.data", "aw", @progbits
209: stolen_bar_register:
210: .word 0
211: .size stolen_bar_register, . - stolen_bar_register
212:
213: .section ".text16.early.data", "aw", @progbits
214: stolen_bar_orig_value:
215: .long 0
216: .size stolen_bar_orig_value, . - stolen_bar_orig_value
217:
218: /* Restore original BAR values
219: *
220: * Parameters:
221: * none
222: * Returns:
223: * none
224: */
225: .section ".text16.early", "awx", @progbits
226: .globl close_payload
227: close_payload:
228: /* Preserve registers */
229: pushw %bx
230: pushw %di
231: pushl %ecx
232: pushw %ds
233:
234: /* Set up %ds for access to .text16.early */
235: pushw %cs
236: popw %ds
237:
238: /* Retrieve stored bus:dev.fn */
239: movw payload_pci_busdevfn, %bx
240:
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
245:
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
250:
251: /* Restore registers and return */
252: popw %ds
253: popl %ecx
254: popw %di
255: popw %bx
256: lret
257: .size close_payload, . - close_payload
258:
259: /* Get PCI BAR value
260: *
261: * Parameters:
262: * %bx : PCI bus:dev.fn
263: * %di : PCI BAR register number
264: * Returns:
265: * %edx:%eax : PCI BAR value
266: */
267: .section ".text16.early", "awx", @progbits
268: pci_read_bar:
269: /* Preserve registers */
270: pushl %ecx
271: pushw %di
272:
273: /* Read low dword value */
274: call pci_read_config_dword
275: movl %ecx, %eax
276:
277: /* Read high dword value, if applicable */
278: xorl %edx, %edx
279: andb $0x07, %cl
280: cmpb $0x04, %cl
281: jne 1f
282: addw $4, %di
283: call pci_read_config_dword
284: movl %ecx, %edx
285: 1:
286: /* Restore registers and return */
287: popw %di
288: popl %ecx
289: ret
290: .size pci_read_bar, . - pci_read_bar
291:
292: /* Get low dword of PCI memory BAR size
293: *
294: * Parameters:
295: * %bx : PCI bus:dev.fn
296: * %di : PCI BAR register number
297: * %eax : Low dword of current PCI BAR value
298: * Returns:
299: * %ecx : PCI BAR size
300: */
301: .section ".text16.early", "awx", @progbits
302: pci_size_mem_bar_low:
303: /* Preserve registers */
304: pushw %dx
305:
306: /* Disable memory accesses */
307: xorw %dx, %dx
308: call pci_set_mem_access
309:
310: /* Write all ones to BAR */
311: xorl %ecx, %ecx
312: decl %ecx
313: call pci_write_config_dword
314:
315: /* Read back BAR */
316: call pci_read_config_dword
317:
318: /* Calculate size */
319: notl %ecx
320: orb $0x0f, %cl
321: incl %ecx
322:
323: /* Restore original value */
324: pushl %ecx
325: movl %eax, %ecx
326: call pci_write_config_dword
327: popl %ecx
328:
329: /* Enable memory accesses */
330: movw $PCI_COMMAND_MEM, %dx
331: call pci_set_mem_access
332:
333: /* Restore registers and return */
334: popw %dx
335: ret
336: .size pci_size_mem_bar_low, . - pci_size_mem_bar_low
337:
338: /* Read PCI config dword
339: *
340: * Parameters:
341: * %bx : PCI bus:dev.fn
342: * %di : PCI register number
343: * Returns:
344: * %ecx : Dword value
345: */
346: .section ".text16.early", "awx", @progbits
347: pci_read_config_dword:
348: /* Preserve registers */
349: pushl %eax
350: pushl %ebx
351: pushl %edx
352:
353: /* Issue INT 0x1a,b10a */
354: movw $PCIBIOS_READ_CONFIG_DWORD, %ax
355: int $0x1a
356:
357: /* Restore registers and return */
358: popl %edx
359: popl %ebx
360: popl %eax
361: ret
362: .size pci_read_config_dword, . - pci_read_config_dword
363:
364: /* Write PCI config dword
365: *
366: * Parameters:
367: * %bx : PCI bus:dev.fn
368: * %di : PCI register number
369: * %ecx : PCI BAR value
370: * Returns:
371: * none
372: */
373: .section ".text16.early", "awx", @progbits
374: pci_write_config_dword:
375: /* Preserve registers */
376: pushal
377:
378: /* Issue INT 0x1a,b10d */
379: movw $PCIBIOS_WRITE_CONFIG_DWORD, %ax
380: int $0x1a
381:
382: /* Restore registers and return */
383: popal
384: ret
385: .size pci_write_config_dword, . - pci_write_config_dword
386:
387: /* Enable/disable memory access response in PCI command word
388: *
389: * Parameters:
390: * %bx : PCI bus:dev.fn
391: * %dx : PCI_COMMAND_MEM, or zero
392: * Returns:
393: * none
394: */
395: .section ".text16.early", "awx", @progbits
396: pci_set_mem_access:
397: /* Preserve registers */
398: pushal
399:
400: /* Read current value of command register */
401: pushw %bx
402: pushw %dx
403: movw $PCI_COMMAND, %di
404: movw $PCIBIOS_READ_CONFIG_WORD, %ax
405: int $0x1a
406: popw %dx
407: popw %bx
408:
409: /* Set memory access enable as appropriate */
410: andw $~PCI_COMMAND_MEM, %cx
411: orw %dx, %cx
412:
413: /* Write new value of command register */
414: movw $PCIBIOS_WRITE_CONFIG_WORD, %ax
415: int $0x1a
416:
417: /* Restore registers and return */
418: popal
419: ret
420: .size pci_set_mem_access, . - pci_set_mem_access
421:
422: /* Image source area length (in dwords)
423: *
424: */
425: .section ".prefix", "ax", @progbits
426: image_source_len_dword:
427: .long 0
428: .size image_source_len_dword, . - image_source_len_dword
429: .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
430: .ascii "ADDL"
431: .long image_source_len_dword
432: .long 4
433: .long 0
434: .previous
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.