tizen 2.3.1 release
[external/qemu.git] / roms / ipxe / 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., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  */
20
21 FILE_LICENCE ( GPL2_OR_LATER )
22
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
32
33 #define ROMPREFIX_EXCLUDE_PAYLOAD 1
34 #define ROMPREFIX_MORE_IMAGES 1
35 #define _rom_start _mrom_start
36 #include "romprefix.S"
37
38         .text
39         .arch i386
40         .code16
41
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.
47  *
48  * Parameters:
49  *   %ds:0000 : Prefix
50  *   %esi : Buffer for copy of image source (or zero if no buffer available)
51  *   %ecx : Expected offset within buffer of first payload block
52  * Returns:
53  *   %esi : Valid image source address (buffered or unbuffered)
54  *   %ecx : Actual offset within buffer of first payload block
55  *   CF set on error
56  */
57         .section ".text16.early", "awx", @progbits
58         .globl  open_payload
59 open_payload:
60         /* Preserve registers */
61         pushl   %eax
62         pushw   %bx
63         pushl   %edx
64         pushl   %edi
65         pushw   %bp
66         pushw   %es
67         pushw   %ds
68
69         /* Retrieve bus:dev.fn from .prefix */
70         movw    init_pci_busdevfn, %bx
71
72         /* Set up %ds for access to .text16.early */
73         pushw   %cs
74         popw    %ds
75
76         /* Set up %es for access to flat address space */
77         xorw    %ax, %ax
78         movw    %ax, %es
79
80         /* Store bus:dev.fn to .text16.early */
81         movw    %bx, payload_pci_busdevfn
82
83         /* Get expansion ROM BAR current value */
84         movw    $PCI_BAR_EXPROM, %di
85         call    pci_read_bar
86         movl    %eax, rom_bar_orig_value
87
88         /* Get expansion ROM BAR size */
89         call    pci_size_mem_bar_low
90         movl    %ecx, rom_bar_size
91
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 */
95 find_mem_bar:
96         /* Move to next BAR */
97         addw    %bp, %di
98         cmpw    $PCI_BAR_5, %di
99         jle     1f
100         stc
101         jmp     99f
102 1:      movw    $4, %bp
103
104         /* Get BAR current value */
105         call    pci_read_bar
106
107         /* Skip non-existent BARs */
108         notl    %eax
109         testl   %eax, %eax
110         notl    %eax
111         jz      find_mem_bar
112
113         /* Skip I/O BARs */
114         testb   $0x01, %al
115         jnz     find_mem_bar
116
117         /* Set increment to 8 for 64-bit BARs */
118         testb   $0x04, %al
119         jz      1f
120         movw    $8, %bp
121 1:
122         /* Skip 64-bit BARs with high dword set; we couldn't use this
123          * address for the (32-bit) expansion ROM BAR anyway
124          */
125         testl   %edx, %edx
126         jnz     find_mem_bar
127
128         /* Get low dword of BAR size */
129         call    pci_size_mem_bar_low
130
131         /* Skip BARs smaller than the expansion ROM BAR */
132         cmpl    %ecx, rom_bar_size
133         ja      find_mem_bar
134
135         /* We have a memory BAR with a 32-bit address that is large
136          * enough to use.  Store BAR number and original value.
137          */
138         movw    %di, stolen_bar_register
139         movl    %eax, stolen_bar_orig_value
140
141         /* Remove flags from BAR address */
142         xorb    %al, %al
143
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.
148          */
149         xorl    %ecx, %ecx
150         call    pci_write_config_dword
151
152         /* Enable expansion ROM BAR at stolen BAR's address */
153         movl    %eax, %ecx
154         orb     $0x1, %cl
155         movw    $PCI_BAR_EXPROM, %di
156         call    pci_write_config_dword
157
158         /* Locate our ROM image */
159 1:      addr32 es cmpw $0xaa55, (%eax)
160         stc
161         jne     99f
162         addr32 es cmpl $_build_id, build_id(%eax)
163         je      2f
164         addr32 es movzbl 2(%eax), %ecx
165         shll    $9, %ecx
166         addl    %ecx, %eax
167         jmp     1b
168 2:
169
170         /* Copy payload to buffer, or set buffer address to BAR address */
171         testl   %esi, %esi
172         jz      1f
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.)
178          */
179         pushl   %esi
180         movl    %esi, %edi
181         movl    %eax, %esi
182         addr32 es movzbl 2(%esi), %ecx
183         shll    $7, %ecx
184         addr32 es movzbl 2(%esi,%ecx,4), %edx
185         shll    $7, %edx
186         addl    %edx, %ecx
187         addr32 es rep movsl
188         popl    %esi
189         jmp     2f
190 1:      /* We have no buffer; set %esi to the BAR address */
191         movl    %eax, %esi
192 2:
193
194         /* Locate first payload block (after the dummy ROM header) */
195         addr32 es movzbl 2(%esi), %ecx
196         shll    $9, %ecx
197         addl    $_pprefix_skip, %ecx
198
199         clc
200         /* Restore registers and return */
201 99:     popw    %ds
202         popw    %es
203         popw    %bp
204         popl    %edi
205         popl    %edx
206         popw    %bx
207         popl    %eax
208         lret
209         .size   open_payload, . - open_payload
210
211         .section ".text16.early.data", "aw", @progbits
212 payload_pci_busdevfn:
213         .word   0
214         .size   payload_pci_busdevfn, . - payload_pci_busdevfn
215
216         .section ".text16.early.data", "aw", @progbits
217 rom_bar_orig_value:
218         .long   0
219         .size   rom_bar_orig_value, . - rom_bar_orig_value
220
221         .section ".text16.early.data", "aw", @progbits
222 rom_bar_size:
223         .long   0
224         .size   rom_bar_size, . - rom_bar_size
225
226         .section ".text16.early.data", "aw", @progbits
227 stolen_bar_register:
228         .word   0
229         .size   stolen_bar_register, . - stolen_bar_register
230
231         .section ".text16.early.data", "aw", @progbits
232 stolen_bar_orig_value:
233         .long   0
234         .size   stolen_bar_orig_value, . - stolen_bar_orig_value
235
236 /* Restore original BAR values
237  *
238  * Parameters:
239  *   none
240  * Returns:
241  *   none
242  */
243         .section ".text16.early", "awx", @progbits
244         .globl  close_payload
245 close_payload:
246         /* Preserve registers */
247         pushw   %bx
248         pushw   %di
249         pushl   %ecx
250         pushw   %ds
251
252         /* Set up %ds for access to .text16.early */
253         pushw   %cs
254         popw    %ds
255
256         /* Retrieve stored bus:dev.fn */
257         movw    payload_pci_busdevfn, %bx
258
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
263
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
268
269         /* Restore registers and return */
270         popw    %ds
271         popl    %ecx
272         popw    %di
273         popw    %bx
274         lret
275         .size   close_payload, . - close_payload
276
277 /* Get PCI BAR value
278  *
279  * Parameters:
280  *   %bx : PCI bus:dev.fn
281  *   %di : PCI BAR register number
282  * Returns:
283  *   %edx:%eax : PCI BAR value
284  */
285         .section ".text16.early", "awx", @progbits
286 pci_read_bar:
287         /* Preserve registers */
288         pushl   %ecx
289         pushw   %di
290
291         /* Read low dword value */
292         call    pci_read_config_dword
293         movl    %ecx, %eax
294
295         /* Read high dword value, if applicable */
296         xorl    %edx, %edx
297         andb    $0x07, %cl
298         cmpb    $0x04, %cl
299         jne     1f
300         addw    $4, %di
301         call    pci_read_config_dword
302         movl    %ecx, %edx
303 1:
304         /* Restore registers and return */
305         popw    %di
306         popl    %ecx
307         ret
308         .size   pci_read_bar, . - pci_read_bar
309
310 /* Get low dword of PCI memory BAR size
311  *
312  * Parameters:
313  *   %bx : PCI bus:dev.fn
314  *   %di : PCI BAR register number
315  *   %eax : Low dword of current PCI BAR value
316  * Returns:
317  *   %ecx : PCI BAR size
318  */
319         .section ".text16.early", "awx", @progbits
320 pci_size_mem_bar_low:
321         /* Preserve registers */
322         pushw   %dx
323
324         /* Disable memory accesses */
325         xorw    %dx, %dx
326         call    pci_set_mem_access
327
328         /* Write all ones to BAR */
329         xorl    %ecx, %ecx
330         decl    %ecx
331         call    pci_write_config_dword
332
333         /* Read back BAR */
334         call    pci_read_config_dword
335
336         /* Calculate size */
337         notl    %ecx
338         orb     $0x0f, %cl
339         incl    %ecx
340
341         /* Restore original value */
342         pushl   %ecx
343         movl    %eax, %ecx
344         call    pci_write_config_dword
345         popl    %ecx
346
347         /* Enable memory accesses */
348         movw    $PCI_COMMAND_MEM, %dx
349         call    pci_set_mem_access
350
351         /* Restore registers and return */
352         popw    %dx
353         ret
354         .size   pci_size_mem_bar_low, . - pci_size_mem_bar_low
355
356 /* Read PCI config dword
357  *
358  * Parameters:
359  *   %bx : PCI bus:dev.fn
360  *   %di : PCI register number
361  * Returns:
362  *   %ecx : Dword value
363  */
364         .section ".text16.early", "awx", @progbits
365 pci_read_config_dword:
366         /* Preserve registers */
367         pushl   %eax
368         pushl   %ebx
369         pushl   %edx
370
371         /* Issue INT 0x1a,b10a */
372         movw    $PCIBIOS_READ_CONFIG_DWORD, %ax
373         int     $0x1a
374
375         /* Restore registers and return */
376         popl    %edx
377         popl    %ebx
378         popl    %eax
379         ret
380         .size   pci_read_config_dword, . - pci_read_config_dword
381
382 /* Write PCI config dword
383  *
384  * Parameters:
385  *   %bx : PCI bus:dev.fn
386  *   %di : PCI register number
387  *   %ecx : PCI BAR value
388  * Returns:
389  *   none
390  */
391         .section ".text16.early", "awx", @progbits
392 pci_write_config_dword:
393         /* Preserve registers */
394         pushal
395
396         /* Issue INT 0x1a,b10d */
397         movw    $PCIBIOS_WRITE_CONFIG_DWORD, %ax
398         int     $0x1a
399
400         /* Restore registers and return */
401         popal
402         ret
403         .size   pci_write_config_dword, . - pci_write_config_dword
404
405 /* Enable/disable memory access response in PCI command word
406  *
407  * Parameters:
408  *   %bx : PCI bus:dev.fn
409  *   %dx : PCI_COMMAND_MEM, or zero
410  * Returns:
411  *   none
412  */
413         .section ".text16.early", "awx", @progbits
414 pci_set_mem_access:
415         /* Preserve registers */
416         pushal
417
418         /* Read current value of command register */
419         pushw   %bx
420         pushw   %dx
421         movw    $PCI_COMMAND, %di
422         movw    $PCIBIOS_READ_CONFIG_WORD, %ax
423         int     $0x1a
424         popw    %dx
425         popw    %bx
426
427         /* Set memory access enable as appropriate */
428         andw    $~PCI_COMMAND_MEM, %cx
429         orw     %dx, %cx
430
431         /* Write new value of command register */
432         movw    $PCIBIOS_WRITE_CONFIG_WORD, %ax
433         int     $0x1a
434
435         /* Restore registers and return */
436         popal
437         ret
438         .size   pci_set_mem_access, . - pci_set_mem_access
439
440 /* Payload prefix
441  *
442  * We include a dummy ROM header to cover the "hidden" portion of the
443  * overall ROM image.
444  */
445         .globl  _payload_align
446         .equ    _payload_align, 512
447         .section ".pprefix", "ax", @progbits
448         .org    0x00
449 mromheader:
450         .word   0xaa55                  /* BIOS extension signature */
451 mromheader_size: .byte 0                /* Size in 512-byte blocks */
452         .org    0x18
453         .word   mpciheader
454         .org    0x1a
455         .word   0
456         .size   mromheader, . - mromheader
457
458         .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
459         .ascii  "APPB"
460         .long   mromheader_size
461         .long   512
462         .long   0
463         .previous
464
465 mpciheader:
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
484
485         .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
486         .ascii  "APPW"
487         .long   mpciheader_image_length
488         .long   512
489         .long   0
490         .ascii  "APPW"
491         .long   mpciheader_runtime_length
492         .long   512
493         .long   0
494         .previous
495
496 /* Fix up additional image source size
497  *
498  */
499         .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
500         .ascii  "ADPW"
501         .long   extra_size
502         .long   512
503         .long   0
504         .previous