[prefix] Add .xrom prefix for a ROM that loads itself by PCI accesses
[people/cooldavid/gpxe.git] / src / arch / i386 / prefix / romprefix.S
1 /* At entry, the processor is in 16 bit real mode and the code is being
2  * executed from an address it was not linked to. Code must be pic and
3  * 32 bit sensitive until things are fixed up.
4  *
5  * Also be very careful as the stack is at the rear end of the interrupt
6  * table so using a noticeable amount of stack space is a no-no.
7  */
8
9 FILE_LICENCE ( GPL2_OR_LATER )
10
11 #include <config/general.h>
12
13 #define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) )
14 #define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) )
15 #define PCI_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( ' ' << 24 ) )
16 #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
17 #define PNP_GET_BBS_VERSION 0x60
18 #define PMM_ALLOCATE 0x0000
19 #define PMM_DEALLOCATE 0x0002
20
21 /* ROM banner timeout.  Based on the configurable BANNER_TIMEOUT in
22  * config.h, but converted to a number of (18Hz) timer ticks, and
23  * doubled to allow for BIOSes that switch video modes immediately
24  * beforehand, so rendering the message almost invisible to the user.
25  */
26 #define ROM_BANNER_TIMEOUT ( 2 * ( 18 * BANNER_TIMEOUT ) / 10 )
27
28 /* We can load a ROM in two ways: have the BIOS load all of it (.rom prefix)
29  * or have the BIOS load a stub that loads the rest using PCI (.xrom prefix).
30  * The latter is not as widely supported, but allows the use of large ROMs
31  * on some systems with crowded option ROM space.
32  */
33
34 #ifdef LOAD_ROM_FROM_PCI
35 #define ROM_SIZE_VALUE  _prefix_filesz_sect /* Amount to load in BIOS */
36 #else
37 #define ROM_SIZE_VALUE  0               /* Load amount (before compr. fixup) */
38 #endif
39
40
41         .text
42         .code16
43         .arch i386
44         .section ".prefix", "ax", @progbits
45         
46         .org    0x00
47 romheader:
48         .word   0xAA55                  /* BIOS extension signature */
49 romheader_size: .byte ROM_SIZE_VALUE    /* Size in 512-byte blocks */
50         jmp     init                    /* Initialisation vector */
51 checksum:
52         .byte   0, 0
53 real_size:
54         .word   0
55         .org    0x16
56         .word   undiheader
57         .org    0x18
58         .word   pciheader
59         .org    0x1a
60         .word   pnpheader
61         .size romheader, . - romheader
62
63         .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
64 #ifndef LOAD_ROM_FROM_PCI
65         .ascii  "ADDB"
66         .long   romheader_size
67         .long   512
68         .long   0
69 #endif
70         .ascii  "ADDB"
71         .long   real_size
72         .long   512
73         .long   0
74         .previous
75
76 pciheader:
77         .ascii  "PCIR"                  /* Signature */
78         .word   pci_vendor_id           /* Vendor identification */ 
79         .word   pci_device_id           /* Device identification */
80         .word   0x0000                  /* Device list pointer */
81         .word   pciheader_len           /* PCI data structure length */
82         .byte   0x03                    /* PCI data structure revision */
83         .byte   0x02, 0x00, 0x00        /* Class code */
84 pciheader_image_length:
85         .word   ROM_SIZE_VALUE          /* Image length */
86         .word   0x0001                  /* Revision level */
87         .byte   0x00                    /* Code type */
88         .byte   0x80                    /* Last image indicator */
89 pciheader_runtime_length:
90         .word   ROM_SIZE_VALUE          /* Maximum run-time image length */
91         .word   0x0000                  /* Configuration utility code header */
92         .word   0x0000                  /* DMTF CLP entry point */
93         .equ pciheader_len, . - pciheader
94         .size pciheader, . - pciheader
95
96 #ifndef LOAD_ROM_FROM_PCI
97         .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
98         .ascii  "ADDW"
99         .long   pciheader_image_length
100         .long   512
101         .long   0
102         .ascii  "ADDW"
103         .long   pciheader_runtime_length
104         .long   512
105         .long   0
106         .previous
107 #endif
108
109 pnpheader:
110         .ascii  "$PnP"                  /* Signature */
111         .byte   0x01                    /* Structure revision */
112         .byte   ( pnpheader_len / 16 )  /* Length (in 16 byte increments) */
113         .word   0x0000                  /* Offset of next header */
114         .byte   0x00                    /* Reserved */
115         .byte   0x00                    /* Checksum */
116         .long   0x00000000              /* Device identifier */
117         .word   mfgstr                  /* Manufacturer string */
118         .word   prodstr                 /* Product name */
119         .byte   0x02                    /* Device base type code */
120         .byte   0x00                    /* Device sub-type code */
121         .byte   0x00                    /* Device interface type code */
122         .byte   0xf4                    /* Device indicator */
123         .word   0x0000                  /* Boot connection vector */
124         .word   0x0000                  /* Disconnect vector */
125         .word   bev_entry               /* Boot execution vector */
126         .word   0x0000                  /* Reserved */
127         .word   0x0000                  /* Static resource information vector*/
128         .equ pnpheader_len, . - pnpheader
129         .size pnpheader, . - pnpheader
130
131 /* Manufacturer string */
132 mfgstr:
133         .asciz  "http://etherboot.org"
134         .size mfgstr, . - mfgstr
135
136 /* Product string
137  *
138  * Defaults to PRODUCT_SHORT_NAME.  If the ROM image is writable at
139  * initialisation time, it will be filled in to include the PCI
140  * bus:dev.fn number of the card as well.
141  */
142 prodstr:
143         .ascii  PRODUCT_SHORT_NAME
144 prodstr_separator:
145         .byte   0
146         .ascii  "(PCI "
147 prodstr_pci_id:
148         .asciz  "xx:xx.x)"              /* Filled in by init code */
149         .size prodstr, . - prodstr
150
151         .globl  undiheader      
152         .weak   undiloader
153 undiheader:
154         .ascii  "UNDI"                  /* Signature */
155         .byte   undiheader_len          /* Length of structure */
156         .byte   0                       /* Checksum */
157         .byte   0                       /* Structure revision */
158         .byte   0,1,2                   /* PXE version: 2.1.0 */
159         .word   undiloader              /* Offset to loader routine */
160         .word   _data16_memsz           /* Stack segment size */
161         .word   _data16_memsz           /* Data segment size */
162         .word   _text16_memsz           /* Code segment size */
163         .ascii  "PCIR"                  /* Bus type */
164         .equ undiheader_len, . - undiheader
165         .size undiheader, . - undiheader
166
167 /* Initialisation (called once during POST)
168  *
169  * Determine whether or not this is a PnP system via a signature
170  * check.  If it is PnP, return to the PnP BIOS indicating that we are
171  * a boot-capable device; the BIOS will call our boot execution vector
172  * if it wants to boot us.  If it is not PnP, hook INT 19.
173  */
174 init:
175         /* Preserve registers, clear direction flag, set %ds=%cs */
176         pushaw
177         pushw   %ds
178         pushw   %es
179         pushw   %fs
180         pushw   %gs
181         cld
182         pushw   %cs
183         popw    %ds
184
185         /* Shuffle some registers around.  We need %di available for
186          * the print_xxx functions, and in a register that's
187          * addressable from %es, so shuffle as follows:
188          *
189          *    %di (pointer to PnP structure) => %bx
190          *    %bx (runtime segment address, for PCI 3.0) => %gs
191          */
192         movw    %bx, %gs
193         movw    %di, %bx
194
195         /* Print message as early as possible */
196         movw    $init_message, %si
197         xorw    %di, %di
198         call    print_message
199         call    print_pci_busdevfn
200
201 #ifdef LOAD_ROM_FROM_PCI
202         /* Save PCI bus:dev.fn for later use */
203         movw    %ax, pci_busdevfn
204 #endif
205
206         /* Fill in product name string, if possible */
207         movw    $prodstr_pci_id, %di
208         call    print_pci_busdevfn
209         movb    $( ' ' ), prodstr_separator
210
211         /* Print segment address */
212         movb    $( ' ' ), %al
213         xorw    %di, %di
214         call    print_character
215         movw    %cs, %ax
216         call    print_hex_word
217
218         /* Check for PCI BIOS version */
219         pushl   %ebx
220         pushl   %edx
221         pushl   %edi
222         stc
223         movw    $0xb101, %ax
224         int     $0x1a
225         jc      no_pci3
226         cmpl    $PCI_SIGNATURE, %edx
227         jne     no_pci3
228         testb   %ah, %ah
229         jnz     no_pci3
230 #ifdef LOAD_ROM_FROM_PCI
231         incb    pcibios_present
232 #endif
233         movw    $init_message_pci, %si
234         xorw    %di, %di
235         call    print_message
236         movb    %bh, %al
237         call    print_hex_nibble
238         movb    $( '.' ), %al
239         call    print_character
240         movb    %bl, %al
241         call    print_hex_byte
242         cmpb    $3, %bh
243         jb      no_pci3
244         /* PCI >=3.0: leave %gs as-is if sane */
245         movw    %gs, %ax
246         cmpw    $0xa000, %ax    /* Insane if %gs < 0xa000 */
247         jb      pci3_insane
248         movw    %cs, %bx        /* Sane if %cs == %gs */
249         cmpw    %bx, %ax
250         je      1f
251         movzbw  romheader_size, %cx /* Sane if %cs+len <= %gs */
252         shlw    $5, %cx
253         addw    %cx, %bx
254         cmpw    %bx, %ax
255         jae     1f
256         movw    %cs, %bx        /* Sane if %gs+len <= %cs */
257         addw    %cx, %ax
258         cmpw    %bx, %ax
259         jbe     1f
260 pci3_insane: /* PCI 3.0 with insane %gs value: print error and ignore %gs */
261         movb    $( '!' ), %al
262         call    print_character
263         movw    %gs, %ax
264         call    print_hex_word
265 no_pci3:
266         /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */
267         pushw   %cs
268         popw    %gs
269 1:      popl    %edi
270         popl    %edx
271         popl    %ebx
272
273         /* Check for PnP BIOS.  Although %es:di should point to the
274          * PnP BIOS signature on entry, some BIOSes fail to do this.
275          */
276         movw    $( 0xf000 - 1 ), %bx
277 pnp_scan:
278         incw    %bx
279         jz      no_pnp
280         movw    %bx, %es
281         cmpl    $PNP_SIGNATURE, %es:0
282         jne     pnp_scan
283         xorw    %dx, %dx
284         xorw    %si, %si
285         movzbw  %es:5, %cx
286 1:      es lodsb
287         addb    %al, %dl
288         loop    1b
289         jnz     pnp_scan
290         /* Is PnP: print PnP message */
291         movw    $init_message_pnp, %si
292         xorw    %di, %di
293         call    print_message
294         /* Check for BBS */
295         pushw   %es:0x1b        /* Real-mode data segment */
296         pushw   %ds             /* &(bbs_version) */
297         pushw   $bbs_version
298         pushw   $PNP_GET_BBS_VERSION
299         lcall   *%es:0xd
300         addw    $8, %sp
301         testw   %ax, %ax
302         je      got_bbs
303 no_pnp: /* Not PnP-compliant - therefore cannot be BBS-compliant */
304 no_bbs: /* Not BBS-compliant - must hook INT 19 */
305         movw    $init_message_int19, %si
306         xorw    %di, %di
307         call    print_message
308         xorw    %ax, %ax
309         movw    %ax, %es
310         pushl   %es:( 0x19 * 4 )
311         popl    orig_int19
312         pushw   %gs /* %gs contains runtime %cs */
313         pushw   $int19_entry
314         popl    %es:( 0x19 * 4 )
315         jmp     bbs_done
316 got_bbs: /* BBS compliant - no need to hook INT 19 */
317         movw    $init_message_bbs, %si
318         xorw    %di, %di
319         call    print_message
320 bbs_done:
321
322         /* Check for PMM */
323         movw    $( 0xe000 - 1 ), %bx
324 pmm_scan:
325         incw    %bx
326         jz      no_pmm
327         movw    %bx, %es
328         cmpl    $PMM_SIGNATURE, %es:0
329         jne     pmm_scan
330         xorw    %dx, %dx
331         xorw    %si, %si
332         movzbw  %es:5, %cx
333 1:      es lodsb
334         addb    %al, %dl
335         loop    1b
336         jnz     pmm_scan
337         /* PMM found: print PMM message */
338         movw    $init_message_pmm, %si
339         xorw    %di, %di
340         call    print_message
341         /* We have PMM and so a 1kB stack: preserve upper register halves */
342         pushal
343         /* Calculate required allocation size in %esi */
344         movzwl  real_size, %eax
345         shll    $9, %eax
346         addl    $_textdata_memsz, %eax
347         orw     $0xffff, %ax    /* Ensure allocation size is at least 64kB */
348         bsrl    %eax, %ecx
349         subw    $15, %cx        /* Round up and convert to 64kB count */
350         movw    $1, %si
351         shlw    %cl, %si
352 pmm_loop:
353         /* Try to allocate block via PMM */
354         pushw   $0x0006         /* Aligned, extended memory */
355         pushl   $0xffffffff     /* No handle */
356         movzwl  %si, %eax
357         shll    $12, %eax
358         pushl   %eax            /* Allocation size in paragraphs */
359         pushw   $PMM_ALLOCATE
360         lcall   *%es:7
361         addw    $12, %sp
362         /* Abort if allocation fails */
363         testw   %dx, %dx        /* %ax==0 even on success, since align>=64kB */
364         jz      pmm_fail
365         /* If block has A20==1, free block and try again with twice
366          * the allocation size (and hence alignment).
367          */
368         testw   $0x0010, %dx
369         jz      got_pmm
370         pushw   %dx
371         pushw   $0
372         pushw   $PMM_DEALLOCATE
373         lcall   *%es:7
374         addw    $6, %sp
375         addw    %si, %si
376         jmp     pmm_loop
377 got_pmm: /* PMM allocation succeeded */
378         movw    %dx, ( image_source + 2 )
379         movw    %dx, %ax
380         xorw    %di, %di
381         call    print_hex_word
382         movb    $( '@' ), %al
383         call    print_character
384         movw    %si, %ax
385         call    print_hex_byte
386 pmm_copy:
387         /* Copy ROM to PMM block */
388         xorw    %ax, %ax
389         movw    %ax, %es
390         movl    image_source, %edi
391         xorl    %esi, %esi
392         movzbl  romheader_size, %ecx
393         shll    $9, %ecx
394         addr32 rep movsb        /* PMM presence implies flat real mode */
395         movl    %edi, decompress_to
396         /* Shrink ROM */
397         movb    $_prefix_memsz_sect, romheader_size
398 #if defined(SHRINK_WITHOUT_PMM) || defined(LOAD_ROM_FROM_PCI)
399         jmp     pmm_done
400 pmm_fail:
401         /* Print marker and copy ourselves to high memory */
402         movl    $HIGHMEM_LOADPOINT, image_source
403         xorw    %di, %di
404         movb    $( '!' ), %al
405         call    print_character
406         jmp     pmm_copy
407 pmm_done:
408 #else
409 pmm_fail:
410 #endif
411         /* Restore upper register halves */
412         popal
413 #if defined(LOAD_ROM_FROM_PCI)
414         call    load_from_pci
415         jc      load_err
416         jmp     load_ok
417 no_pmm:
418         /* Cannot continue without PMM - print error message */
419         xorw    %di, %di
420         movw    $init_message_no_pmm, %si
421         call    print_message
422 load_err:
423         /* Wait for five seconds to let user see message */
424         movw    $90, %cx
425 1:      call    wait_for_tick
426         loop    1b
427         /* Mark environment as invalid and return */
428         movl    $0, decompress_to
429         jmp     out
430
431 load_ok:
432 #else
433 no_pmm:
434 #endif
435         /* Update checksum */
436         xorw    %bx, %bx
437         xorw    %si, %si
438         movzbw  romheader_size, %cx
439         shlw    $9, %cx
440 1:      lodsb
441         addb    %al, %bl
442         loop    1b
443         subb    %bl, checksum
444
445         /* Copy self to option ROM space.  Required for PCI3.0, which
446          * loads us to a temporary location in low memory.  Will be a
447          * no-op for lower PCI versions.
448          */
449         movb    $( ' ' ), %al
450         xorw    %di, %di
451         call    print_character
452         movw    %gs, %ax
453         call    print_hex_word
454         movzbw  romheader_size, %cx
455         shlw    $9, %cx
456         movw    %ax, %es
457         xorw    %si, %si
458         xorw    %di, %di
459         cs rep  movsb
460
461         /* Prompt for POST-time shell */
462         movw    $init_message_prompt, %si
463         xorw    %di, %di
464         call    print_message
465         movw    $prodstr, %si
466         call    print_message
467         movw    $init_message_dots, %si
468         call    print_message
469         /* Wait for Ctrl-B */
470         movw    $0xff02, %bx
471         call    wait_for_key
472         /* Clear prompt */
473         pushf
474         xorw    %di, %di
475         call    print_kill_line
476         movw    $init_message_done, %si
477         call    print_message
478         popf
479         jnz     out
480         /* Ctrl-B was pressed: invoke gPXE.  The keypress will be
481          * picked up by the initial shell prompt, and we will drop
482          * into a shell.
483          */
484         pushw   %cs
485         call    exec
486 out:
487         /* Restore registers */
488         popw    %gs
489         popw    %fs
490         popw    %es
491         popw    %ds
492         popaw
493
494         /* Indicate boot capability to PnP BIOS, if present */
495         movw    $0x20, %ax
496         lret
497         .size init, . - init
498
499 /*
500  * Note to hardware vendors:
501  *
502  * If you wish to brand this boot ROM, please do so by defining the
503  * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/general.h.
504  *
505  * While nothing in the GPL prevents you from removing all references
506  * to gPXE or http://etherboot.org, we prefer you not to do so.
507  *
508  * If you have an OEM-mandated branding requirement that cannot be
509  * satisfied simply by defining PRODUCT_NAME and PRODUCT_SHORT_NAME,
510  * please contact us.
511  *
512  * [ Including an ASCII NUL in PRODUCT_NAME is considered to be
513  *   bypassing the spirit of this request! ]
514  */
515 init_message:
516         .ascii  "\n"
517         .ascii  PRODUCT_NAME
518         .ascii  "\n"
519         .asciz  "gPXE (http://etherboot.org) - "
520         .size   init_message, . - init_message
521 init_message_pci:
522         .asciz  " PCI"
523         .size   init_message_pci, . - init_message_pci
524 init_message_pnp:
525         .asciz  " PnP"
526         .size   init_message_pnp, . - init_message_pnp
527 init_message_bbs:
528         .asciz  " BBS"
529         .size   init_message_bbs, . - init_message_bbs
530 init_message_pmm:
531         .asciz  " PMM"
532         .size   init_message_pmm, . - init_message_pmm
533 #ifdef LOAD_ROM_FROM_PCI
534 init_message_no_pmm:
535         .asciz  "\nPMM required but not present!\n"
536         .size   init_message_no_pmm, . - init_message_no_pmm
537 #endif
538 init_message_int19:
539         .asciz  " INT19"
540         .size   init_message_int19, . - init_message_int19
541 init_message_prompt:
542         .asciz  "\nPress Ctrl-B to configure "
543         .size   init_message_prompt, . - init_message_prompt
544 init_message_dots:
545         .asciz  "..."
546         .size   init_message_dots, . - init_message_dots
547 init_message_done:
548         .asciz  "\n\n"
549         .size   init_message_done, . - init_message_done
550
551 /* ROM image location
552  *
553  * May be either within option ROM space, or within PMM-allocated block.
554  */
555         .globl  image_source
556 image_source:
557         .long   0
558         .size   image_source, . - image_source
559
560 /* Temporary decompression area
561  *
562  * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block.
563  * If a PCI ROM load fails, this will be set to zero.
564  */
565         .globl  decompress_to
566 decompress_to:
567         .long   HIGHMEM_LOADPOINT
568         .size   decompress_to, . - decompress_to
569
570 #ifdef LOAD_ROM_FROM_PCI
571
572 /* Set if the PCI BIOS is present, even <3.0 */
573 pcibios_present:
574         .byte   0
575         .byte   0               /* for alignment */
576         .size   pcibios_present, . - pcibios_present
577
578 /* PCI bus:device.function word
579  *
580  * Filled in by init in the .xrom case, so the remainder of the ROM
581  * can be located.
582  */
583 pci_busdevfn:
584         .word   0
585         .size   pci_busdevfn, . - pci_busdevfn
586
587 #endif
588
589 /* BBS version
590  *
591  * Filled in by BBS BIOS.  We ignore the value.
592  */
593 bbs_version:
594         .word   0
595         .size   bbs_version, . - bbs_version
596
597 /* Boot Execution Vector entry point
598  *
599  * Called by the PnP BIOS when it wants to boot us.
600  */
601 bev_entry:
602         pushw   %cs
603         call    exec
604         lret
605         .size   bev_entry, . - bev_entry
606
607
608 #ifdef LOAD_ROM_FROM_PCI
609
610 #define PCI_ROM_ADDRESS         0x30    /* Bits 31:11 address, 10:1 reserved */
611 #define PCI_ROM_ADDRESS_ENABLE   0x00000001
612 #define PCI_ROM_ADDRESS_MASK     0xfffff800
613
614 #define PCIBIOS_READ_WORD       0xb109
615 #define PCIBIOS_READ_DWORD      0xb10a
616 #define PCIBIOS_WRITE_WORD      0xb10c
617 #define PCIBIOS_WRITE_DWORD     0xb10d
618
619 /* Determine size of PCI BAR
620  *
621  *  %bx : PCI bus:dev.fn to probe
622  *  %di : Address of BAR to find size of
623  * %edx : Mask of address bits within BAR
624  *
625  * %ecx : Size for a memory resource,
626  *        1 for an I/O resource (bit 0 set).
627  *   CF : Set on error or nonexistent device (all-ones read)
628  *
629  * All other registers saved.
630  */
631 pci_bar_size:
632         /* Save registers */
633         pushw   %ax
634         pushl   %esi
635         pushl   %edx
636
637         /* Read current BAR value */
638         movw    $PCIBIOS_READ_DWORD, %ax
639         int     $0x1a
640
641         /* Check for device existence and save it */
642         testb   $1, %cl         /* I/O bit? */
643         jz      1f
644         andl    $1, %ecx        /* If so, exit with %ecx = 1 */
645         jmp     99f
646 1:      notl    %ecx
647         testl   %ecx, %ecx      /* Set ZF iff %ecx was all-ones */
648         notl    %ecx
649         jnz     1f
650         stc                     /* All ones - exit with CF set */
651         jmp     99f
652 1:      movl    %ecx, %esi      /* Save in %esi */
653
654         /* Write all ones to BAR */
655         movl    %edx, %ecx
656         movw    $PCIBIOS_WRITE_DWORD, %ax
657         int     $0x1a
658
659         /* Read back BAR */
660         movw    $PCIBIOS_READ_DWORD, %ax
661         int     $0x1a
662
663         /* Find decode size from least set bit in mask BAR */
664         bsfl    %ecx, %ecx      /* Find least set bit, log2(decode size) */
665         jz      1f              /* Mask BAR should not be zero */
666         xorl    %edx, %edx
667         incl    %edx
668         shll    %cl, %edx       /* %edx = decode size */
669         jmp     2f
670 1:      xorl    %edx, %edx      /* Return zero size for mask BAR zero */
671
672         /* Restore old BAR value */
673 2:      movl    %esi, %ecx
674         movw    $PCIBIOS_WRITE_DWORD, %ax
675         int     $0x1a
676
677         movl    %edx, %ecx      /* Return size in %ecx */
678
679         /* Restore registers and return */
680 99:     popl    %edx
681         popl    %esi
682         popw    %ax
683         ret
684
685         .size   pci_bar_size, . - pci_bar_size
686
687 /* PCI ROM loader
688  *
689  * Called from init in the .xrom case to load the non-prefix code
690  * using the PCI ROM BAR.
691  *
692  * Returns with carry flag set on error. All registers saved.
693  */
694 load_from_pci:
695         /*
696          * Use PCI BIOS access to config space. The calls take
697          *
698          *   %ah : 0xb1         %al : function
699          *   %bx : bus/dev/fn
700          *   %di : config space address
701          *  %ecx : value to write (for writes)
702          *
703          *  %ecx : value read (for reads)
704          *   %ah : return code
705          *    CF : error indication
706          *
707          * All registers not used for return are preserved.
708          */
709
710         /* Save registers and set up %es for big real mode */
711         pushal
712         pushw   %es
713         xorw    %ax, %ax
714         movw    %ax, %es
715
716         /* Check PCI BIOS presence */
717         cmpb    $0, pcibios_present
718         jz      err_pcibios
719
720         /* Load existing PCI ROM BAR */
721         movw    $PCIBIOS_READ_DWORD, %ax
722         movw    pci_busdevfn, %bx
723         movw    $PCI_ROM_ADDRESS, %di
724         int     $0x1a
725
726         /* Maybe it's already enabled? */
727         testb   $PCI_ROM_ADDRESS_ENABLE, %cl
728         jz      1f
729         movb    $1, %dl         /* Flag indicating no deinit required */
730         movl    %ecx, %ebp
731         jmp     check_rom
732
733         /* Determine PCI BAR decode size */
734 1:      movl    $PCI_ROM_ADDRESS_MASK, %edx
735         call    pci_bar_size    /* Returns decode size in %ecx */
736         jc      err_size_insane /* CF => no ROM BAR, %ecx == ffffffff */
737
738         /* Check sanity of decode size */
739         xorl    %eax, %eax
740         movw    real_size, %ax
741         shll    $9, %eax        /* %eax = ROM size */
742         cmpl    %ecx, %eax
743         ja      err_size_insane /* Insane if decode size < ROM size */
744         cmpl    $0x100000, %ecx
745         jae     err_size_insane /* Insane if decode size >= 1MB */
746
747         /* Find a place to map the BAR
748          * In theory we should examine e820 and all PCI BARs to find a
749          * free region. However, we run at POST when e820 may not be
750          * available, and memory reads of an unmapped location are
751          * de facto standardized to return all-ones. Thus, we can get
752          * away with searching high memory (0xf0000000 and up) on
753          * multiples of the ROM BAR decode size for a sufficiently
754          * large all-ones region.
755          */
756         movl    %ecx, %edx      /* Save ROM BAR size in %edx */
757         movl    $0xf0000000, %ebp
758         xorl    %eax, %eax
759         notl    %eax            /* %eax = all ones */
760 bar_search:
761         movl    %ebp, %edi
762         movl    %edx, %ecx
763         shrl    $2, %ecx
764         addr32 repe scasl       /* Scan %es:edi for anything not all-ones */
765         jz      bar_found
766         addl    %edx, %ebp
767         testl   $0x80000000, %ebp
768         jz      err_no_bar
769         jmp     bar_search
770
771 bar_found:
772         movl    %edi, %ebp
773         /* Save current BAR value on stack to restore later */
774         movw    $PCIBIOS_READ_DWORD, %ax
775         movw    $PCI_ROM_ADDRESS, %di
776         int     $0x1a
777         pushl   %ecx
778
779         /* Map the ROM */
780         movw    $PCIBIOS_WRITE_DWORD, %ax
781         movl    %ebp, %ecx
782         orb     $PCI_ROM_ADDRESS_ENABLE, %cl
783         int     $0x1a
784
785         xorb    %dl, %dl        /* %dl = 0 : ROM was not already mapped */
786 check_rom:
787         /* Check and copy ROM - enter with %dl set to skip unmapping,
788          * %ebp set to mapped ROM BAR address.
789          * We check up to prodstr_separator for equality, since anything past
790          * that may have been modified. Since our check includes the checksum
791          * byte over the whole ROM stub, that should be sufficient.
792          */
793         xorb    %dh, %dh        /* %dh = 0 : ROM did not fail integrity check */
794
795         /* Verify ROM integrity */
796         xorl    %esi, %esi
797         movl    %ebp, %edi
798         movl    $prodstr_separator, %ecx
799         addr32 repe cmpsb
800         jz      copy_rom
801         incb    %dh             /* ROM failed integrity check */
802         movl    %ecx, %ebp      /* Save number of bytes left */
803         jmp     skip_load
804
805 copy_rom:
806         /* Print BAR address and indicate whether we mapped it ourselves */
807         movb    $( ' ' ), %al
808         xorw    %di, %di
809         call    print_character
810         movl    %ebp, %eax
811         call    print_hex_dword
812         movb    $( '-' ), %al   /* '-' for self-mapped */
813         subb    %dl, %al
814         subb    %dl, %al        /* '+' = '-' - 2 for BIOS-mapped */
815         call    print_character
816
817         /* Copy ROM at %ebp to PMM or highmem block */
818         movl    %ebp, %esi
819         movl    image_source, %edi
820         movzwl  real_size, %ecx
821         shll    $9, %ecx
822         addr32 es rep movsb
823         movl    %edi, decompress_to
824 skip_load:
825         testb   %dl, %dl        /* Was ROM already mapped? */
826         jnz     skip_unmap
827
828         /* Unmap the ROM by restoring old ROM BAR */
829         movw    $PCIBIOS_WRITE_DWORD, %ax
830         movw    $PCI_ROM_ADDRESS, %di
831         popl    %ecx
832         int     $0x1a
833
834 skip_unmap:
835         /* Error handling */
836         testb   %dh, %dh
837         jnz     err_rom_invalid
838         clc
839         jmp     99f
840
841 err_pcibios:                    /* No PCI BIOS available */
842         movw    $load_message_no_pcibios, %si
843         xorl    %eax, %eax      /* "error code" is zero */
844         jmp     1f
845 err_size_insane:                /* BAR has size (%ecx) that is insane */
846         movw    $load_message_size_insane, %si
847         movl    %ecx, %eax
848         jmp     1f
849 err_no_bar:                     /* No space of sufficient size (%edx) found */
850         movw    $load_message_no_bar, %si
851         movl    %edx, %eax
852         jmp     1f
853 err_rom_invalid:                /* Loaded ROM does not match (%ebp bytes left) */
854         movw    $load_message_rom_invalid, %si
855         movzbl  romheader_size, %eax
856         shll    $9, %eax
857         subl    %ebp, %eax
858         decl    %eax            /* %eax is now byte index of failure */
859
860 1:      /* Error handler - print message at %si and dword in %eax */
861         xorw    %di, %di
862         call    print_message
863         call    print_hex_dword
864         stc
865 99:     popw    %es
866         popal
867         ret
868
869         .size   load_from_pci, . - load_from_pci
870
871 load_message_no_pcibios:
872         .asciz  "\nNo PCI BIOS found! "
873         .size   load_message_no_pcibios, . - load_message_no_pcibios
874
875 load_message_size_insane:
876         .asciz  "\nROM resource has invalid size "
877         .size   load_message_size_insane, . - load_message_size_insane
878
879 load_message_no_bar:
880         .asciz  "\nNo memory hole of sufficient size "
881         .size   load_message_no_bar, . - load_message_no_bar
882
883 load_message_rom_invalid:
884         .asciz  "\nLoaded ROM is invalid at "
885         .size   load_message_rom_invalid, . - load_message_rom_invalid
886
887 #endif /* LOAD_ROM_FROM_PCI */
888
889
890 /* INT19 entry point
891  *
892  * Called via the hooked INT 19 if we detected a non-PnP BIOS.  We
893  * attempt to return via the original INT 19 vector (if we were able
894  * to store it).
895  */
896 int19_entry:
897         pushw   %cs
898         popw    %ds
899         /* Prompt user to press B to boot */
900         movw    $int19_message_prompt, %si
901         xorw    %di, %di
902         call    print_message
903         movw    $prodstr, %si
904         call    print_message
905         movw    $int19_message_dots, %si
906         call    print_message
907         movw    $0xdf4e, %bx
908         call    wait_for_key
909         pushf
910         xorw    %di, %di
911         call    print_kill_line
912         movw    $int19_message_done, %si
913         call    print_message
914         popf
915         jz      1f
916         /* Leave keypress in buffer and start gPXE.  The keypress will
917          * cause the usual initial Ctrl-B prompt to be skipped.
918          */
919         pushw   %cs
920         call    exec
921 1:      /* Try to call original INT 19 vector */
922         movl    %cs:orig_int19, %eax
923         testl   %eax, %eax
924         je      2f
925         ljmp    *%cs:orig_int19
926 2:      /* No chained vector: issue INT 18 as a last resort */
927         int     $0x18
928         .size   int19_entry, . - int19_entry
929 orig_int19:
930         .long   0
931         .size   orig_int19, . - orig_int19
932
933 int19_message_prompt:
934         .asciz  "Press N to skip booting from "
935         .size   int19_message_prompt, . - int19_message_prompt
936 int19_message_dots:
937         .asciz  "..."
938         .size   int19_message_dots, . - int19_message_dots
939 int19_message_done:
940         .asciz  "\n\n"
941         .size   int19_message_done, . - int19_message_done
942         
943 /* Execute as a boot device
944  *
945  */
946 exec:   /* Set %ds = %cs */
947         pushw   %cs
948         popw    %ds
949
950 #ifdef LOAD_ROM_FROM_PCI
951         /* Don't execute if load was invalid */
952         cmpl    $0, decompress_to
953         jne     1f
954         lret
955 1:
956 #endif
957
958         /* Print message as soon as possible */
959         movw    $prodstr, %si
960         xorw    %di, %di
961         call    print_message
962         movw    $exec_message, %si
963         call    print_message
964
965         /* Store magic word on BIOS stack and remember BIOS %ss:sp */
966         pushl   $STACK_MAGIC
967         movw    %ss, %dx
968         movw    %sp, %bp
969
970         /* Obtain a reasonably-sized temporary stack */
971         xorw    %ax, %ax
972         movw    %ax, %ss
973         movw    $0x7c00, %sp
974
975         /* Install gPXE */
976         movl    image_source, %esi
977         movl    decompress_to, %edi
978         call    alloc_basemem
979         call    install_prealloc
980
981         /* Set up real-mode stack */
982         movw    %bx, %ss
983         movw    $_estack16, %sp
984
985         /* Jump to .text16 segment */
986         pushw   %ax
987         pushw   $1f
988         lret
989         .section ".text16", "awx", @progbits
990 1:      /* Call main() */
991         pushl   $main
992         pushw   %cs
993         call    prot_call
994         popl    %ecx /* discard */
995
996         /* Uninstall gPXE */
997         call    uninstall
998
999         /* Restore BIOS stack */
1000         movw    %dx, %ss
1001         movw    %bp, %sp
1002
1003         /* Check magic word on BIOS stack */
1004         popl    %eax
1005         cmpl    $STACK_MAGIC, %eax
1006         jne     1f
1007         /* BIOS stack OK: return to caller */
1008         lret
1009 1:      /* BIOS stack corrupt: use INT 18 */
1010         int     $0x18
1011         .previous
1012
1013 exec_message:
1014         .asciz  " starting execution\n"
1015         .size exec_message, . - exec_message
1016
1017 /* Wait for key press specified by %bl (masked by %bh)
1018  *
1019  * Used by init and INT19 code when prompting user.  If the specified
1020  * key is pressed, it is left in the keyboard buffer.
1021  *
1022  * Returns with ZF set iff specified key is pressed.
1023  */
1024 wait_for_key:
1025         /* Preserve registers */
1026         pushw   %cx
1027         pushw   %ax
1028 1:      /* Empty the keyboard buffer before waiting for input */
1029         movb    $0x01, %ah
1030         int     $0x16
1031         jz      2f
1032         xorw    %ax, %ax
1033         int     $0x16
1034         jmp     1b
1035 2:      /* Wait for a key press */
1036         movw    $ROM_BANNER_TIMEOUT, %cx
1037 3:      decw    %cx
1038         js      99f             /* Exit with ZF clear */
1039         /* Wait for timer tick to be updated */
1040         call    wait_for_tick
1041         /* Check to see if a key was pressed */
1042         movb    $0x01, %ah
1043         int     $0x16
1044         jz      3b
1045         /* Check to see if key was the specified key */
1046         andb    %bh, %al
1047         cmpb    %al, %bl
1048         je      99f             /* Exit with ZF set */
1049         /* Not the specified key: remove from buffer and stop waiting */
1050         pushfw
1051         xorw    %ax, %ax
1052         int     $0x16
1053         popfw                   /* Exit with ZF clear */
1054 99:     /* Restore registers and return */
1055         popw    %ax
1056         popw    %cx
1057         ret
1058         .size wait_for_key, . - wait_for_key
1059
1060 /* Wait for timer tick
1061  *
1062  * Used by wait_for_key
1063  */
1064 wait_for_tick:
1065         pushl   %eax
1066         pushw   %fs
1067         movw    $0x40, %ax
1068         movw    %ax, %fs
1069         movl    %fs:(0x6c), %eax
1070 1:      pushf
1071         sti
1072         hlt
1073         popf
1074         cmpl    %fs:(0x6c), %eax
1075         je      1b
1076         popw    %fs
1077         popl    %eax
1078         ret
1079         .size wait_for_tick, . - wait_for_tick