source: bootcd/isolinux/syslinux-6.03/gpxe/src/arch/i386/prefix/romprefix.S

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 25.4 KB
Line 
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
9FILE_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
47romheader:
48        .word   0xAA55                  /* BIOS extension signature */
49romheader_size: .byte ROM_SIZE_VALUE    /* Size in 512-byte blocks */
50        jmp     init                    /* Initialisation vector */
51checksum:
52        .byte   0, 0
53real_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
76pciheader:
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 */
84pciheader_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 */
89pciheader_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
109pnpheader:
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 */
132mfgstr:
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 */
142prodstr:
143        .ascii  PRODUCT_SHORT_NAME
144prodstr_separator:
145        .byte   0
146        .ascii  "(PCI "
147prodstr_pci_id:
148        .asciz  "xx:xx.x)"              /* Filled in by init code */
149        .size prodstr, . - prodstr
150
151        .globl  undiheader     
152        .weak   undiloader
153undiheader:
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 */
174init:
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
260pci3_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
265no_pci3:
266        /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */
267        pushw   %cs
268        popw    %gs
2691:      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
277pnp_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
2861:      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
303no_pnp: /* Not PnP-compliant - therefore cannot be BBS-compliant */
304no_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
316got_bbs: /* BBS compliant - no need to hook INT 19 */
317        movw    $init_message_bbs, %si
318        xorw    %di, %di
319        call    print_message
320bbs_done:
321
322        /* Check for PMM */
323        movw    $( 0xe000 - 1 ), %bx
324pmm_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
3331:      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
352pmm_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
377got_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
386pmm_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
400pmm_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
407pmm_done:
408#else
409pmm_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
417no_pmm:
418        /* Cannot continue without PMM - print error message */
419        xorw    %di, %di
420        movw    $init_message_no_pmm, %si
421        call    print_message
422load_err:
423        /* Wait for five seconds to let user see message */
424        movw    $90, %cx
4251:      call    wait_for_tick
426        loop    1b
427        /* Mark environment as invalid and return */
428        movl    $0, decompress_to
429        jmp     out
430
431load_ok:
432#else
433no_pmm:
434#endif
435        /* Update checksum */
436        xorw    %bx, %bx
437        xorw    %si, %si
438        movzbw  romheader_size, %cx
439        shlw    $9, %cx
4401:      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
486out:
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 */
515init_message:
516        .ascii  "\n"
517        .ascii  PRODUCT_NAME
518        .ascii  "\n"
519        .asciz  "gPXE (http://etherboot.org) - "
520        .size   init_message, . - init_message
521init_message_pci:
522        .asciz  " PCI"
523        .size   init_message_pci, . - init_message_pci
524init_message_pnp:
525        .asciz  " PnP"
526        .size   init_message_pnp, . - init_message_pnp
527init_message_bbs:
528        .asciz  " BBS"
529        .size   init_message_bbs, . - init_message_bbs
530init_message_pmm:
531        .asciz  " PMM"
532        .size   init_message_pmm, . - init_message_pmm
533#ifdef LOAD_ROM_FROM_PCI
534init_message_no_pmm:
535        .asciz  "\nPMM required but not present!\n"
536        .size   init_message_no_pmm, . - init_message_no_pmm
537#endif
538init_message_int19:
539        .asciz  " INT19"
540        .size   init_message_int19, . - init_message_int19
541init_message_prompt:
542        .asciz  "\nPress Ctrl-B to configure "
543        .size   init_message_prompt, . - init_message_prompt
544init_message_dots:
545        .asciz  "..."
546        .size   init_message_dots, . - init_message_dots
547init_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
556image_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
566decompress_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 */
573pcibios_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 */
583pci_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 */
593bbs_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 */
601bev_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 */
631pci_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
6461:      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
6521:      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
6701:      xorl    %edx, %edx      /* Return zero size for mask BAR zero */
671
672        /* Restore old BAR value */
6732:      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 */
68099:     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 */
694load_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 */
7341:      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 */
760bar_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
771bar_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 */
786check_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
805copy_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
824skip_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
834skip_unmap:
835        /* Error handling */
836        testb   %dh, %dh
837        jnz     err_rom_invalid
838        clc
839        jmp     99f
840
841err_pcibios:                    /* No PCI BIOS available */
842        movw    $load_message_no_pcibios, %si
843        xorl    %eax, %eax      /* "error code" is zero */
844        jmp     1f
845err_size_insane:                /* BAR has size (%ecx) that is insane */
846        movw    $load_message_size_insane, %si
847        movl    %ecx, %eax
848        jmp     1f
849err_no_bar:                     /* No space of sufficient size (%edx) found */
850        movw    $load_message_no_bar, %si
851        movl    %edx, %eax
852        jmp     1f
853err_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
8601:      /* 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
86599:     popw    %es
866        popal
867        ret
868
869        .size   load_from_pci, . - load_from_pci
870
871load_message_no_pcibios:
872        .asciz  "\nNo PCI BIOS found! "
873        .size   load_message_no_pcibios, . - load_message_no_pcibios
874
875load_message_size_insane:
876        .asciz  "\nROM resource has invalid size "
877        .size   load_message_size_insane, . - load_message_size_insane
878
879load_message_no_bar:
880        .asciz  "\nNo memory hole of sufficient size "
881        .size   load_message_no_bar, . - load_message_no_bar
882
883load_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 */
896int19_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
9211:      /* 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
9262:      /* No chained vector: issue INT 18 as a last resort */
927        int     $0x18
928        .size   int19_entry, . - int19_entry
929orig_int19:
930        .long   0
931        .size   orig_int19, . - orig_int19
932
933int19_message_prompt:
934        .asciz  "Press N to skip booting from "
935        .size   int19_message_prompt, . - int19_message_prompt
936int19_message_dots:
937        .asciz  "..."
938        .size   int19_message_dots, . - int19_message_dots
939int19_message_done:
940        .asciz  "\n\n"
941        .size   int19_message_done, . - int19_message_done
942       
943/* Execute as a boot device
944 *
945 */
946exec:   /* 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
9551:
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
9901:      /* 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
10091:      /* BIOS stack corrupt: use INT 18 */
1010        int     $0x18
1011        .previous
1012
1013exec_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 */
1024wait_for_key:
1025        /* Preserve registers */
1026        pushw   %cx
1027        pushw   %ax
10281:      /* 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
10352:      /* Wait for a key press */
1036        movw    $ROM_BANNER_TIMEOUT, %cx
10373:      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 */
105499:     /* 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 */
1064wait_for_tick:
1065        pushl   %eax
1066        pushw   %fs
1067        movw    $0x40, %ax
1068        movw    %ax, %fs
1069        movl    %fs:(0x6c), %eax
10701:      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
Note: See TracBrowser for help on using the repository browser.