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