[pci] Correct invalid base-class/sub-class/prog-if order in PCIR
[ipxe.git] / src / arch / x86 / 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  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  *
23  */
24
25 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
26
27 #define PCIBIOS_READ_CONFIG_WORD        0xb109
28 #define PCIBIOS_READ_CONFIG_DWORD       0xb10a
29 #define PCIBIOS_WRITE_CONFIG_WORD       0xb10c
30 #define PCIBIOS_WRITE_CONFIG_DWORD      0xb10d
31 #define PCI_COMMAND                     0x04
32 #define PCI_COMMAND_MEM                         0x02
33 #define PCI_BAR_0                       0x10
34 #define PCI_BAR_5                       0x24
35 #define PCI_BAR_EXPROM                  0x30
36
37 #define PCIR_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( 'R' << 24 ) )
38
39 #define ROMPREFIX_EXCLUDE_PAYLOAD 1
40 #define ROMPREFIX_MORE_IMAGES 1
41 #define _pcirom_start _mrom_start
42 #include "pciromprefix.S"
43
44         .text
45         .arch i386
46         .code16
47
48 /* Obtain access to payload by exposing the expansion ROM BAR at the
49  * address currently used by a suitably large memory BAR on the same
50  * device.  The memory BAR is temporarily disabled.  Using a memory
51  * BAR on the same device means that we don't have to worry about the
52  * configuration of any intermediate PCI bridges.
53  *
54  * Parameters:
55  *   %ds:0000 : Prefix
56  *   %esi : Buffer for copy of image source (or zero if no buffer available)
57  *   %ecx : Expected offset within buffer of first payload block
58  * Returns:
59  *   %esi : Valid image source address (buffered or unbuffered)
60  *   %ecx : Actual offset within buffer of first payload block
61  *   CF set on error
62  */
63         .section ".text16.early", "awx", @progbits
64         .globl  open_payload
65 open_payload:
66         /* Preserve registers */
67         pushl   %eax
68         pushw   %bx
69         pushl   %edx
70         pushl   %edi
71         pushw   %bp
72         pushw   %es
73         pushw   %ds
74
75         /* Retrieve bus:dev.fn from .prefix */
76         movw    init_pci_busdevfn, %bx
77
78         /* Set up %ds for access to .text16.early */
79         pushw   %cs
80         popw    %ds
81
82         /* Set up %es for access to flat address space */
83         xorw    %ax, %ax
84         movw    %ax, %es
85
86         /* Store bus:dev.fn to .text16.early */
87         movw    %bx, payload_pci_busdevfn
88
89         /* Get expansion ROM BAR current value */
90         movw    $PCI_BAR_EXPROM, %di
91         call    pci_read_bar
92         movl    %eax, rom_bar_orig_value
93
94         /* Get expansion ROM BAR size */
95         call    pci_size_mem_bar_low
96         movl    %ecx, rom_bar_size
97
98         /* Find a suitable memory BAR to use */
99         movw    $PCI_BAR_0, %di         /* %di is PCI BAR register */
100         xorw    %bp, %bp                /* %bp is increment */
101 find_mem_bar:
102         /* Move to next BAR */
103         addw    %bp, %di
104         cmpw    $PCI_BAR_5, %di
105         jle     1f
106         stc
107         movl    $0xbabababa, %esi       /* Report "No suitable BAR" */
108         movl    rom_bar_size, %ecx
109         jmp     99f
110 1:      movw    $4, %bp
111
112         /* Get BAR current value */
113         call    pci_read_bar
114
115         /* Skip non-existent BARs */
116         notl    %eax
117         testl   %eax, %eax
118         notl    %eax
119         jz      find_mem_bar
120
121         /* Skip I/O BARs */
122         testb   $0x01, %al
123         jnz     find_mem_bar
124
125         /* Set increment to 8 for 64-bit BARs */
126         testb   $0x04, %al
127         jz      1f
128         movw    $8, %bp
129 1:
130         /* Skip 64-bit BARs with high dword set; we couldn't use this
131          * address for the (32-bit) expansion ROM BAR anyway
132          */
133         testl   %edx, %edx
134         jnz     find_mem_bar
135
136         /* Get low dword of BAR size */
137         call    pci_size_mem_bar_low
138
139         /* Skip BARs smaller than the expansion ROM BAR */
140         cmpl    %ecx, rom_bar_size
141         ja      find_mem_bar
142
143         /* We have a memory BAR with a 32-bit address that is large
144          * enough to use.  Store BAR number and original value.
145          */
146         movw    %di, stolen_bar_register
147         movl    %eax, stolen_bar_orig_value
148
149         /* Remove flags from BAR address */
150         xorb    %al, %al
151
152         /* Write zero to our stolen BAR.  This doesn't technically
153          * disable it, but it's a pretty safe bet that the PCI bridge
154          * won't pass through accesses to this region anyway.  Note
155          * that the high dword (if any) must already be zero.
156          */
157         xorl    %ecx, %ecx
158         call    pci_write_config_dword
159
160         /* Enable expansion ROM BAR at stolen BAR's address */
161         movl    %eax, %ecx
162         orb     $0x1, %cl
163         movw    $PCI_BAR_EXPROM, %di
164         call    pci_write_config_dword
165
166         /* Locate our ROM image */
167 1:      movl    $0xaa55, %ecx                           /* 55aa signature */
168         addr32 es cmpw %cx, (%eax)
169         jne     2f
170         movl    $PCIR_SIGNATURE, %ecx                   /* PCIR signature */
171         addr32 es movzwl 0x18(%eax), %edx
172         addr32 es cmpl %ecx, (%eax,%edx)
173         jne     2f
174         addr32 es cmpl $_build_id, build_id(%eax)       /* iPXE build ID */
175         je      3f
176         movl    $0x80, %ecx                             /* Last image */
177         addr32 es testb %cl, 0x15(%eax,%edx)
178         jnz     2f
179         addr32 es movzwl 0x10(%eax,%edx), %ecx          /* PCIR image length */
180         shll    $9, %ecx
181         addl    %ecx, %eax
182         jmp     1b
183 2:      /* Failure */
184         stc
185         movl    %eax, %esi              /* Report failure address */
186         jmp     99f
187 3:
188
189         /* Copy payload to buffer, or set buffer address to BAR address */
190         testl   %esi, %esi
191         jz      1f
192         /* We have a buffer; copy payload to it.  Since .mrom is
193          * designed specifically for real hardware, we assume that
194          * flat real mode is working properly.  (In the unlikely event
195          * that this code is run inside a hypervisor that doesn't
196          * properly support flat real mode, it will die horribly.)
197          */
198         pushl   %esi
199         movl    %esi, %edi
200         movl    %eax, %esi
201         addr32 es movzbl 2(%esi), %ecx
202         shll    $7, %ecx
203         addr32 es movzwl mpciheader_image_length(%esi,%ecx,4), %edx
204         shll    $7, %edx
205         addl    %edx, %ecx
206         addr32 es rep movsl
207         popl    %esi
208         jmp     2f
209 1:      /* We have no buffer; set %esi to the BAR address */
210         movl    %eax, %esi
211 2:
212
213         /* Locate first payload block (after the dummy ROM header) */
214         addr32 es movzbl 2(%esi), %ecx
215         shll    $9, %ecx
216         addl    $_pprefix_skip, %ecx
217
218         clc
219         /* Restore registers and return */
220 99:     popw    %ds
221         popw    %es
222         popw    %bp
223         popl    %edi
224         popl    %edx
225         popw    %bx
226         popl    %eax
227         lret
228         .size   open_payload, . - open_payload
229
230         .section ".text16.early.data", "aw", @progbits
231 payload_pci_busdevfn:
232         .word   0
233         .size   payload_pci_busdevfn, . - payload_pci_busdevfn
234
235         .section ".text16.early.data", "aw", @progbits
236 rom_bar_orig_value:
237         .long   0
238         .size   rom_bar_orig_value, . - rom_bar_orig_value
239
240         .section ".text16.early.data", "aw", @progbits
241 rom_bar_size:
242         .long   0
243         .size   rom_bar_size, . - rom_bar_size
244
245         .section ".text16.early.data", "aw", @progbits
246 stolen_bar_register:
247         .word   0
248         .size   stolen_bar_register, . - stolen_bar_register
249
250         .section ".text16.early.data", "aw", @progbits
251 stolen_bar_orig_value:
252         .long   0
253         .size   stolen_bar_orig_value, . - stolen_bar_orig_value
254
255 /* Restore original BAR values
256  *
257  * Parameters:
258  *   none
259  * Returns:
260  *   none
261  */
262         .section ".text16.early", "awx", @progbits
263         .globl  close_payload
264 close_payload:
265         /* Preserve registers */
266         pushw   %bx
267         pushw   %di
268         pushl   %ecx
269         pushw   %ds
270
271         /* Set up %ds for access to .text16.early */
272         pushw   %cs
273         popw    %ds
274
275         /* Retrieve stored bus:dev.fn */
276         movw    payload_pci_busdevfn, %bx
277
278         /* Restore expansion ROM BAR original value */
279         movw    $PCI_BAR_EXPROM, %di
280         movl    rom_bar_orig_value, %ecx
281         call    pci_write_config_dword
282
283         /* Restore stolen BAR original value */
284         movw    stolen_bar_register, %di
285         movl    stolen_bar_orig_value, %ecx
286         call    pci_write_config_dword
287
288         /* Restore registers and return */
289         popw    %ds
290         popl    %ecx
291         popw    %di
292         popw    %bx
293         lret
294         .size   close_payload, . - close_payload
295
296 /* Get PCI BAR value
297  *
298  * Parameters:
299  *   %bx : PCI bus:dev.fn
300  *   %di : PCI BAR register number
301  * Returns:
302  *   %edx:%eax : PCI BAR value
303  */
304         .section ".text16.early", "awx", @progbits
305 pci_read_bar:
306         /* Preserve registers */
307         pushl   %ecx
308         pushw   %di
309
310         /* Read low dword value */
311         call    pci_read_config_dword
312         movl    %ecx, %eax
313
314         /* Read high dword value, if applicable */
315         xorl    %edx, %edx
316         andb    $0x07, %cl
317         cmpb    $0x04, %cl
318         jne     1f
319         addw    $4, %di
320         call    pci_read_config_dword
321         movl    %ecx, %edx
322 1:
323         /* Restore registers and return */
324         popw    %di
325         popl    %ecx
326         ret
327         .size   pci_read_bar, . - pci_read_bar
328
329 /* Get low dword of PCI memory BAR size
330  *
331  * Parameters:
332  *   %bx : PCI bus:dev.fn
333  *   %di : PCI BAR register number
334  *   %eax : Low dword of current PCI BAR value
335  * Returns:
336  *   %ecx : PCI BAR size
337  */
338         .section ".text16.early", "awx", @progbits
339 pci_size_mem_bar_low:
340         /* Preserve registers */
341         pushw   %dx
342
343         /* Disable memory accesses */
344         xorw    %dx, %dx
345         call    pci_set_mem_access
346
347         /* Write all ones to BAR */
348         xorl    %ecx, %ecx
349         decl    %ecx
350         call    pci_write_config_dword
351
352         /* Read back BAR */
353         call    pci_read_config_dword
354
355         /* Calculate size */
356         notl    %ecx
357         orb     $0x0f, %cl
358         incl    %ecx
359
360         /* Restore original value */
361         pushl   %ecx
362         movl    %eax, %ecx
363         call    pci_write_config_dword
364         popl    %ecx
365
366         /* Enable memory accesses */
367         movw    $PCI_COMMAND_MEM, %dx
368         call    pci_set_mem_access
369
370         /* Restore registers and return */
371         popw    %dx
372         ret
373         .size   pci_size_mem_bar_low, . - pci_size_mem_bar_low
374
375 /* Read PCI config dword
376  *
377  * Parameters:
378  *   %bx : PCI bus:dev.fn
379  *   %di : PCI register number
380  * Returns:
381  *   %ecx : Dword value
382  */
383         .section ".text16.early", "awx", @progbits
384 pci_read_config_dword:
385         /* Preserve registers */
386         pushl   %eax
387         pushl   %ebx
388         pushl   %edx
389
390         /* Issue INT 0x1a,b10a */
391         movw    $PCIBIOS_READ_CONFIG_DWORD, %ax
392         int     $0x1a
393
394         /* Restore registers and return */
395         popl    %edx
396         popl    %ebx
397         popl    %eax
398         ret
399         .size   pci_read_config_dword, . - pci_read_config_dword
400
401 /* Write PCI config dword
402  *
403  * Parameters:
404  *   %bx : PCI bus:dev.fn
405  *   %di : PCI register number
406  *   %ecx : PCI BAR value
407  * Returns:
408  *   none
409  */
410         .section ".text16.early", "awx", @progbits
411 pci_write_config_dword:
412         /* Preserve registers */
413         pushal
414
415         /* Issue INT 0x1a,b10d */
416         movw    $PCIBIOS_WRITE_CONFIG_DWORD, %ax
417         int     $0x1a
418
419         /* Restore registers and return */
420         popal
421         ret
422         .size   pci_write_config_dword, . - pci_write_config_dword
423
424 /* Enable/disable memory access response in PCI command word
425  *
426  * Parameters:
427  *   %bx : PCI bus:dev.fn
428  *   %dx : PCI_COMMAND_MEM, or zero
429  * Returns:
430  *   none
431  */
432         .section ".text16.early", "awx", @progbits
433 pci_set_mem_access:
434         /* Preserve registers */
435         pushal
436
437         /* Read current value of command register */
438         pushw   %bx
439         pushw   %dx
440         movw    $PCI_COMMAND, %di
441         movw    $PCIBIOS_READ_CONFIG_WORD, %ax
442         int     $0x1a
443         popw    %dx
444         popw    %bx
445
446         /* Set memory access enable as appropriate */
447         andw    $~PCI_COMMAND_MEM, %cx
448         orw     %dx, %cx
449
450         /* Write new value of command register */
451         movw    $PCIBIOS_WRITE_CONFIG_WORD, %ax
452         int     $0x1a
453
454         /* Restore registers and return */
455         popal
456         ret
457         .size   pci_set_mem_access, . - pci_set_mem_access
458
459 /* Update image source address for UNDI loader
460  *
461  * Parameters:
462  *   %esi : Image source address
463  * Returns:
464  *   %esi : Image source address
465  */
466         .section ".prefix", "ax", @progbits
467         .globl  undiloader_source
468 undiloader_source:
469         /* Always use expansion ROM BAR directly when installing via
470          * the UNDI loader entry point, since the PMM-allocated block
471          * may collide with whatever is calling the UNDI loader entry
472          * point.
473          */
474         xorl    %esi, %esi
475         ret
476
477 /* Payload prefix
478  *
479  * We include a dummy ROM header to cover the "hidden" portion of the
480  * overall ROM image.
481  */
482         .globl  _payload_align
483         .equ    _payload_align, 512
484         .section ".pprefix", "ax", @progbits
485         .org    0x00
486 mromheader:
487         .word   0xaa55                  /* BIOS extension signature */
488         .byte   0x01                    /* Dummy size (BIOS bug workaround) */
489         .org    0x18
490         .word   mpciheader
491         .org    0x1a
492         .word   0
493         .size   mromheader, . - mromheader
494
495         .align  4
496 mpciheader:
497         .ascii  "PCIR"                  /* Signature */
498         .word   pci_vendor_id           /* Vendor identification */
499         .word   pci_device_id           /* Device identification */
500         .word   0x0000                  /* Device list pointer */
501         .word   mpciheader_len          /* PCI data structure length */
502         .byte   0x03                    /* PCI data structure revision */
503         .byte   0x00, 0x00, 0x02        /* Class code */
504 mpciheader_image_length:
505         .word   0                       /* Image length */
506         .word   0x0001                  /* Revision level */
507         .byte   0xff                    /* Code type */
508         .byte   0x80                    /* Last image indicator */
509 mpciheader_runtime_length:
510         .word   0                       /* Maximum run-time image length */
511         .word   0x0000                  /* Configuration utility code header */
512         .word   0x0000                  /* DMTF CLP entry point */
513         .equ    mpciheader_len, . - mpciheader
514         .size   mpciheader, . - mpciheader
515
516         .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
517         .ascii  "APPW"
518         .long   mpciheader_image_length
519         .long   512
520         .long   0
521         .ascii  "APPW"
522         .long   mpciheader_runtime_length
523         .long   512
524         .long   0
525         .previous
526
527 /* Fix up additional image source size
528  *
529  */
530         .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
531         .ascii  "ADPW"
532         .long   extra_size
533         .long   512
534         .long   0
535         .previous