[romprefix] Sanity-check the runtime segment address for PCI 3
[people/mcb30/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 #include <config/general.h>
10
11 #define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) )
12 #define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) )
13 #define PCI_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( ' ' << 24 ) )
14 #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
15 #define PNP_GET_BBS_VERSION 0x60
16 #define PMM_ALLOCATE 0x0000
17
18 /* ROM banner timeout.  Based on the configurable BANNER_TIMEOUT in
19  * config.h, but converted to a number of (18Hz) timer ticks, and
20  * doubled to allow for BIOSes that switch video modes immediately
21  * beforehand, so rendering the message almost invisible to the user.
22  */
23 #define ROM_BANNER_TIMEOUT ( 2 * ( 18 * BANNER_TIMEOUT ) / 10 )
24
25         .text
26         .code16
27         .arch i386
28         .section ".prefix", "ax", @progbits
29         
30         .org    0x00
31 romheader:
32         .word   0xAA55                  /* BIOS extension signature */
33 romheader_size: .byte _filesz_sect      /* Size in 512-byte blocks */
34         jmp     init                    /* Initialisation vector */
35 checksum:
36         .byte   0
37         .org    0x16
38         .word   undiheader
39         .org    0x18
40         .word   pciheader
41         .org    0x1a
42         .word   pnpheader
43         .size romheader, . - romheader
44         
45         .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
46         .ascii  "SUBB"
47         .long   romheader_size
48         .long   512
49         .long   0
50         .previous
51
52 pciheader:
53         .ascii  "PCIR"                  /* Signature */
54         .word   pci_vendor_id           /* Vendor identification */ 
55         .word   pci_device_id           /* Device identification */
56         .word   0x0000                  /* Device list pointer */
57         .word   pciheader_len           /* PCI data structure length */
58         .byte   0x03                    /* PCI data structure revision */
59         .byte   0x02, 0x00, 0x00        /* Class code */
60 pciheader_image_length:
61         .word   _filesz_sect            /* Image length */
62         .word   0x0001                  /* Revision level */
63         .byte   0x00                    /* Code type */
64         .byte   0x80                    /* Last image indicator */
65 pciheader_runtime_length:
66         .word   _filesz_sect            /* Maximum run-time image length */
67         .word   0x0000                  /* Configuration utility code header */
68         .word   0x0000                  /* DMTF CLP entry point */
69         .equ pciheader_len, . - pciheader
70         .size pciheader, . - pciheader
71         
72         .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
73         .ascii  "SUBW"
74         .long   pciheader_image_length
75         .long   512
76         .long   0
77         .ascii  "SUBW"
78         .long   pciheader_runtime_length
79         .long   512
80         .long   0
81         .previous
82
83 pnpheader:
84         .ascii  "$PnP"                  /* Signature */
85         .byte   0x01                    /* Structure revision */
86         .byte   ( pnpheader_len / 16 )  /* Length (in 16 byte increments) */
87         .word   0x0000                  /* Offset of next header */
88         .byte   0x00                    /* Reserved */
89         .byte   0x00                    /* Checksum */
90         .long   0x00000000              /* Device identifier */
91         .word   mfgstr                  /* Manufacturer string */
92         .word   prodstr                 /* Product name */
93         .byte   0x02                    /* Device base type code */
94         .byte   0x00                    /* Device sub-type code */
95         .byte   0x00                    /* Device interface type code */
96         .byte   0xf4                    /* Device indicator */
97         .word   0x0000                  /* Boot connection vector */
98         .word   0x0000                  /* Disconnect vector */
99         .word   bev_entry               /* Boot execution vector */
100         .word   0x0000                  /* Reserved */
101         .word   0x0000                  /* Static resource information vector*/
102         .equ pnpheader_len, . - pnpheader
103         .size pnpheader, . - pnpheader
104
105 /* Manufacturer string */
106 mfgstr:
107         .asciz  "http://etherboot.org"
108         .size mfgstr, . - mfgstr
109
110 /* Product string
111  *
112  * Defaults to "gPXE".  If the ROM image is writable at initialisation
113  * time, it will be filled in to include the PCI bus:dev.fn number of
114  * the card as well.
115  */
116 prodstr:
117         .ascii  "gPXE"
118 prodstr_separator:
119         .byte   0
120         .ascii  "(PCI "
121 prodstr_pci_id:
122         .asciz  "xx:xx.x)"              /* Filled in by init code */
123         .size prodstr, . - prodstr
124
125         .globl  undiheader      
126 undiheader:
127         .ascii  "UNDI"                  /* Signature */
128         .byte   undiheader_len          /* Length of structure */
129         .byte   0                       /* Checksum */
130         .byte   0                       /* Structure revision */
131         .byte   0,1,2                   /* PXE version: 2.1.0 */
132         .word   undiloader              /* Offset to loader routine */
133         .word   _data16_memsz           /* Stack segment size */
134         .word   _data16_memsz           /* Data segment size */
135         .word   _text16_memsz           /* Code segment size */
136         .ascii  "PCIR"                  /* Bus type */
137         .equ undiheader_len, . - undiheader
138         .size undiheader, . - undiheader
139
140 /* Initialisation (called once during POST)
141  *
142  * Determine whether or not this is a PnP system via a signature
143  * check.  If it is PnP, return to the PnP BIOS indicating that we are
144  * a boot-capable device; the BIOS will call our boot execution vector
145  * if it wants to boot us.  If it is not PnP, hook INT 19.
146  */
147 init:
148         /* Preserve registers, clear direction flag, set %ds=%cs */
149         pushaw
150         pushw   %ds
151         pushw   %es
152         pushw   %fs
153         pushw   %gs
154         cld
155         pushw   %cs
156         popw    %ds
157
158         /* Shuffle some registers around.  We need %di available for
159          * the print_xxx functions, and in a register that's
160          * addressable from %es, so shuffle as follows:
161          *
162          *    %di (pointer to PnP structure) => %bx
163          *    %bx (runtime segment address, for PCI 3.0) => %gs
164          */
165         movw    %bx, %gs
166         movw    %di, %bx
167
168         /* Print message as early as possible */
169         movw    $init_message, %si
170         xorw    %di, %di
171         call    print_message
172         call    print_pci_busdevfn
173
174         /* Fill in product name string, if possible */
175         movw    $prodstr_pci_id, %di
176         call    print_pci_busdevfn
177         movb    $' ', prodstr_separator
178
179         /* Print segment address */
180         movb    $' ', %al
181         xorw    %di, %di
182         call    print_character
183         movw    %cs, %ax
184         call    print_hex_word
185
186         /* Check for PCI BIOS version */
187         pushl   %ebx
188         pushl   %edx
189         pushl   %edi
190         stc
191         movw    $0xb101, %ax
192         int     $0x1a
193         jc      1f
194         cmpl    $PCI_SIGNATURE, %edx
195         jne     1f
196         testb   %ah, %ah
197         jnz     1f
198         movw    $init_message_pci, %si
199         xorw    %di, %di
200         call    print_message
201         movb    %bh, %al
202         call    print_hex_nibble
203         movb    $'.', %al
204         call    print_character
205         movb    %bl, %al
206         call    print_hex_byte
207         cmpb    $3, %bh
208         jb      1f
209         /* PCI >=3.0: leave %gs as-is if sane */
210         movw    %gs, %ax
211         cmpw    $0xc000, %ax
212         jae     2f
213         /* PCI 3.0 with insane %gs value: print error and ignore %gs */
214         movb    $'!', %al
215         call    print_character
216         movw    %gs, %ax
217         call    print_hex_word
218 1:      /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */
219         pushw   %cs
220         popw    %gs
221 2:      popl    %edi
222         popl    %edx
223         popl    %ebx
224
225         /* Check for PnP BIOS */
226         testw   $0x0f, %bx      /* PnP signature must be aligned - bochs    */
227         jnz     no_bbs          /* uses unalignment to indicate 'fake' PnP. */
228         cmpl    $PNP_SIGNATURE, %es:0(%bx)
229         jne     no_bbs
230         /* Is PnP: print PnP message */
231         movw    $init_message_pnp, %si
232         xorw    %di, %di
233         call    print_message
234         /* Check for BBS */
235         pushw   %es:0x1b(%bx)   /* Real-mode data segment */
236         pushw   %ds             /* &(bbs_version) */
237         pushw   $bbs_version
238         pushw   $PNP_GET_BBS_VERSION
239         lcall   *%es:0xd(%bx)
240         addw    $8, %sp
241         testw   %ax, %ax
242         je      got_bbs
243 no_bbs: /* Not BBS-compliant - must hook INT 19 */
244         movw    $init_message_int19, %si
245         xorw    %di, %di
246         call    print_message
247         xorw    %ax, %ax
248         movw    %ax, %es
249         pushl   %es:( 0x19 * 4 )
250         popl    orig_int19
251         pushw   %gs /* %gs contains runtime %cs */
252         pushw   $int19_entry
253         popl    %es:( 0x19 * 4 )
254         jmp     bbs_done
255 got_bbs: /* BBS compliant - no need to hook INT 19 */
256         movw    $init_message_bbs, %si
257         xorw    %di, %di
258         call    print_message
259 bbs_done:
260
261         /* Check for PMM */
262         movw    $( 0xe000 - 1 ), %bx
263 pmm_scan:
264         incw    %bx
265         jz      no_pmm
266         movw    %bx, %es
267         cmpl    $PMM_SIGNATURE, %es:0
268         jne     pmm_scan
269         xorw    %dx, %dx
270         xorw    %si, %si
271         movzbw  %es:5, %cx
272 1:      es lodsb
273         addb    %al, %dl
274         loop    1b
275         jnz     pmm_scan
276         /* PMM found: print PMM message */
277         movw    $init_message_pmm, %si
278         xorw    %di, %di
279         call    print_message
280         /* Try to allocate 2MB block via PMM */
281         pushw   $0x0006         /* Aligned, extended memory */
282         pushl   $0xffffffff     /* No handle */
283         pushl   $( 0x00200000 / 16 ) /* 2MB in paragraphs */
284         pushw   $PMM_ALLOCATE
285         lcall   *%es:7
286         addw    $12, %sp
287         movw    %dx, %ax
288         xorw    %di, %di
289         call    print_hex_word
290         movw    %dx, ( image_source + 2 )
291         testw   %dx, %dx        /* %ax==0 even on success, since align=2MB */
292         jz      no_pmm
293         /* PMM allocation succeeded: copy ROM to PMM block */
294         pushal                  /* PMM presence implies 1kB stack */
295         xorw    %ax, %ax
296         movw    %ax, %es
297         movl    image_source, %edi
298         xorl    %esi, %esi
299         movzbl  romheader_size, %ecx
300         shll    $9, %ecx
301         addr32 rep movsb        /* PMM presence implies flat real mode */
302         movl    %edi, decompress_to
303         /* Shrink ROM and update checksum */
304         xorw    %bx, %bx
305         xorw    %si, %si
306         movw    $_prefix_memsz_sect, %cx
307         movb    %cl, romheader_size
308         shlw    $9, %cx
309 1:      lodsb
310         addb    %al, %bl
311         loop    1b
312         subb    %bl, checksum
313         popal
314 no_pmm:
315
316         /* Copy self to option ROM space.  Required for PCI3.0, which
317          * loads us to a temporary location in low memory.  Will be a
318          * no-op for lower PCI versions.
319          */
320         movb    $' ', %al
321         xorw    %di, %di
322         call    print_character
323         movw    %gs, %ax
324         call    print_hex_word
325         movzbw  romheader_size, %cx
326         shlw    $9, %cx
327         movw    %ax, %es
328         xorw    %si, %si
329         xorw    %di, %di
330         cs rep  movsb
331
332         /* Prompt for POST-time shell */
333         movw    $init_message_prompt, %si
334         xorw    %di, %di
335         call    print_message
336         /* Wait for Ctrl-B */
337         movw    $0xff02, %bx
338         call    wait_for_key
339         /* Clear prompt */
340         pushf
341         movw    $clear_message, %si
342         xorw    %di, %di
343         call    print_message
344         popf
345         jnz     1f
346         /* Ctrl-B was pressed: invoke gPXE.  The keypress will be
347          * picked up by the initial shell prompt, and we will drop
348          * into a shell.
349          */
350         pushw   %cs
351         call    exec
352 1:
353         /* Restore registers */
354         popw    %gs
355         popw    %fs
356         popw    %es
357         popw    %ds
358         popaw
359
360         /* Indicate boot capability to PnP BIOS, if present */
361         movw    $0x20, %ax
362         lret
363         .size init, . - init
364
365 init_message:
366         .asciz  "gPXE (http://etherboot.org) - "
367         .size   init_message, . - init_message
368 init_message_pci:
369         .asciz  " PCI"
370         .size   init_message_pci, . - init_message_pci
371 init_message_pnp:
372         .asciz  " PnP"
373         .size   init_message_pnp, . - init_message_pnp
374 init_message_bbs:
375         .asciz  " BBS"
376         .size   init_message_bbs, . - init_message_bbs
377 init_message_pmm:
378         .asciz  " PMM"
379         .size   init_message_pmm, . - init_message_pmm
380 init_message_int19:
381         .asciz  " INT19"
382         .size   init_message_int19, . - init_message_int19
383 init_message_prompt:
384         .asciz  "\nPress Ctrl-B to configure gPXE..."
385         .size   init_message_prompt, . - init_message_prompt
386 clear_message:
387         .asciz  "\r                                          \n\n"
388         .size   clear_message, . - clear_message
389
390 /* ROM image location
391  *
392  * May be either within option ROM space, or within PMM-allocated block.
393  */
394 image_source:
395         .long   0
396         .size   image_source, . - image_source
397
398 /* Temporary decompression area
399  *
400  * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block.
401  */
402 decompress_to:
403         .long   HIGHMEM_LOADPOINT
404         .size   decompress_to, . - decompress_to
405
406 /* BBS version
407  *
408  * Filled in by BBS BIOS.  We ignore the value.
409  */
410 bbs_version:
411         .word   0
412         .size   bbs_version, . - bbs_version
413
414 /* Boot Execution Vector entry point
415  *
416  * Called by the PnP BIOS when it wants to boot us.
417  */
418 bev_entry:
419         pushw   %cs
420         call    exec
421         lret
422         .size   bev_entry, . - bev_entry
423
424 /* INT19 entry point
425  *
426  * Called via the hooked INT 19 if we detected a non-PnP BIOS.  We
427  * attempt to return via the original INT 19 vector (if we were able
428  * to store it).
429  */
430 int19_entry:
431         pushw   %cs
432         popw    %ds
433         /* Prompt user to press B to boot */
434         movw    $int19_message_prompt, %si
435         xorw    %di, %di
436         call    print_message
437         movw    $prodstr, %si
438         call    print_message
439         movw    $int19_message_dots, %si
440         call    print_message
441         movw    $0xdf42, %bx
442         call    wait_for_key
443         pushf
444         movw    $clear_message, %si
445         xorw    %di, %di
446         call    print_message
447         popf
448         jnz     1f
449         /* Leave keypress in buffer and start gPXE.  The keypress will
450          * cause the usual initial Ctrl-B prompt to be skipped.
451          */
452         pushw   %cs
453         call    exec
454 1:      /* Try to call original INT 19 vector */
455         movl    %cs:orig_int19, %eax
456         testl   %eax, %eax
457         je      2f
458         ljmp    *%cs:orig_int19
459 2:      /* No chained vector: issue INT 18 as a last resort */
460         int     $0x18
461         .size   int19_entry, . - int19_entry
462 orig_int19:
463         .long   0
464         .size   orig_int19, . - orig_int19
465
466 int19_message_prompt:
467         .asciz  "Press B to boot from "
468         .size   int19_message_prompt, . - int19_message_prompt
469 int19_message_dots:
470         .asciz  "..."
471         .size   int19_message_dots, . - int19_message_dots
472         
473 /* Execute as a boot device
474  *
475  */
476 exec:   /* Set %ds = %cs */
477         pushw   %cs
478         popw    %ds
479
480         /* Print message as soon as possible */
481         movw    $prodstr, %si
482         xorw    %di, %di
483         call    print_message
484         movw    $exec_message, %si
485         call    print_message
486
487         /* Store magic word on BIOS stack and remember BIOS %ss:sp */
488         pushl   $STACK_MAGIC
489         movw    %ss, %dx
490         movw    %sp, %bp
491
492         /* Obtain a reasonably-sized temporary stack */
493         xorw    %ax, %ax
494         movw    %ax, %ss
495         movw    $0x7c00, %sp
496
497         /* Install gPXE */
498         movl    image_source, %esi
499         movl    decompress_to, %edi
500         call    alloc_basemem
501         call    install_prealloc
502
503         /* Set up real-mode stack */
504         movw    %bx, %ss
505         movw    $_estack16, %sp
506
507         /* Jump to .text16 segment */
508         pushw   %ax
509         pushw   $1f
510         lret
511         .section ".text16", "awx", @progbits
512 1:      /* Call main() */
513         pushl   $main
514         pushw   %cs
515         call    prot_call
516         /* No need to clean up stack; we are about to reload %ss:sp */
517         
518         /* Restore BIOS stack */
519         movw    %dx, %ss
520         movw    %bp, %sp
521
522         /* Check magic word on BIOS stack */
523         popl    %eax
524         cmpl    $STACK_MAGIC, %eax
525         jne     1f
526         /* BIOS stack OK: return to caller */
527         lret
528 1:      /* BIOS stack corrupt: use INT 18 */
529         int     $0x18
530         .previous
531
532 exec_message:
533         .asciz  " starting execution\n"
534         .size exec_message, . - exec_message
535
536 /* UNDI loader
537  *
538  * Called by an external program to load our PXE stack.
539  */
540 undiloader:
541         /* Save registers */
542         pushl   %esi
543         pushl   %edi
544         pushw   %ds
545         pushw   %es
546         pushw   %bx
547         /* ROM segment address to %ds */
548         pushw   %cs
549         popw    %ds
550         /* UNDI loader parameter structure address into %es:%di */
551         movw    %sp, %bx
552         movw    %ss:18(%bx), %di
553         movw    %ss:20(%bx), %es
554         /* Install to specified real-mode addresses */
555         pushw   %di
556         movw    %es:12(%di), %bx
557         movw    %es:14(%di), %ax
558         movl    image_source, %esi
559         movl    decompress_to, %edi
560         call    install_prealloc
561         popw    %di
562         /* Call UNDI loader C code */
563         pushl   $pxe_loader_call
564         pushw   %cs
565         pushw   $1f
566         pushw   %ax
567         pushw   $prot_call
568         lret
569 1:      popw    %bx     /* discard */
570         popw    %bx     /* discard */
571         /* Restore registers and return */
572         popw    %bx
573         popw    %es
574         popw    %ds
575         popl    %edi
576         popl    %esi
577         lret
578         .size undiloader, . - undiloader
579
580 /* Wait for key press specified by %bl (masked by %bh)
581  *
582  * Used by init and INT19 code when prompting user.  If the specified
583  * key is pressed, it is left in the keyboard buffer.
584  *
585  * Returns with ZF set iff specified key is pressed.
586  */
587 wait_for_key:
588         /* Preserve registers */
589         pushw   %cx
590         pushw   %ax
591 1:      /* Empty the keyboard buffer before waiting for input */
592         movb    $0x01, %ah
593         int     $0x16
594         jz      2f
595         xorw    %ax, %ax
596         int     $0x16
597         jmp     1b
598 2:      /* Wait for a key press */
599         movw    $ROM_BANNER_TIMEOUT, %cx
600 3:      decw    %cx
601         js      99f             /* Exit with ZF clear */
602         /* Wait for timer tick to be updated */
603         call    wait_for_tick
604         /* Check to see if a key was pressed */
605         movb    $0x01, %ah
606         int     $0x16
607         jz      3b
608         /* Check to see if key was the specified key */
609         andb    %bh, %al
610         cmpb    %al, %bl
611         je      99f             /* Exit with ZF set */
612         /* Not the specified key: remove from buffer and stop waiting */
613         pushfw
614         xorw    %ax, %ax
615         int     $0x16
616         popfw                   /* Exit with ZF clear */
617 99:     /* Restore registers and return */
618         popw    %ax
619         popw    %cx
620         ret
621         .size wait_for_key, . - wait_for_key
622
623 /* Wait for timer tick
624  *
625  * Used by wait_for_key
626  */
627 wait_for_tick:
628         pushl   %eax
629         pushw   %fs
630         movw    $0x40, %ax
631         movw    %ax, %fs
632         movl    %fs:(0x6c), %eax
633 1:      pushf
634         sti
635         hlt
636         popf
637         cmpl    %fs:(0x6c), %eax
638         je      1b
639         popw    %fs
640         popl    %eax
641         ret
642         .size wait_for_tick, . - wait_for_tick