9407e64cc97b5d07a244461eb1450b35ae2b7689
[people/pravin/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      no_pci3
194         cmpl    $PCI_SIGNATURE, %edx
195         jne     no_pci3
196         testb   %ah, %ah
197         jnz     no_pci3
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      no_pci3
209         /* PCI >=3.0: leave %gs as-is if sane */
210         movw    %gs, %ax
211         cmpw    $0xa000, %ax    /* Insane if %gs < 0xa000 */
212         jb      pci3_insane
213         movw    %cs, %bx        /* Sane if %cs == %gs */
214         cmpw    %bx, %ax
215         je      1f
216         movzbw  romheader_size, %cx /* Sane if %cs+len <= %gs */
217         shlw    $5, %cx
218         addw    %cx, %bx
219         cmpw    %bx, %ax
220         jae     1f
221         movw    %cs, %bx        /* Sane if %gs+len <= %cs */
222         addw    %cx, %ax
223         cmpw    %bx, %ax
224         jbe     1f
225 pci3_insane: /* PCI 3.0 with insane %gs value: print error and ignore %gs */
226         movb    $'!', %al
227         call    print_character
228         movw    %gs, %ax
229         call    print_hex_word
230 no_pci3:
231         /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */
232         pushw   %cs
233         popw    %gs
234 1:      popl    %edi
235         popl    %edx
236         popl    %ebx
237
238         /* Check for PnP BIOS */
239         testw   $0x0f, %bx      /* PnP signature must be aligned - bochs    */
240         jnz     no_bbs          /* uses unalignment to indicate 'fake' PnP. */
241         cmpl    $PNP_SIGNATURE, %es:0(%bx)
242         jne     no_bbs
243         /* Is PnP: print PnP message */
244         movw    $init_message_pnp, %si
245         xorw    %di, %di
246         call    print_message
247         /* Check for BBS */
248         pushw   %es:0x1b(%bx)   /* Real-mode data segment */
249         pushw   %ds             /* &(bbs_version) */
250         pushw   $bbs_version
251         pushw   $PNP_GET_BBS_VERSION
252         lcall   *%es:0xd(%bx)
253         addw    $8, %sp
254         testw   %ax, %ax
255         je      got_bbs
256 no_bbs: /* Not BBS-compliant - must hook INT 19 */
257         movw    $init_message_int19, %si
258         xorw    %di, %di
259         call    print_message
260         xorw    %ax, %ax
261         movw    %ax, %es
262         pushl   %es:( 0x19 * 4 )
263         popl    orig_int19
264         pushw   %gs /* %gs contains runtime %cs */
265         pushw   $int19_entry
266         popl    %es:( 0x19 * 4 )
267         jmp     bbs_done
268 got_bbs: /* BBS compliant - no need to hook INT 19 */
269         movw    $init_message_bbs, %si
270         xorw    %di, %di
271         call    print_message
272 bbs_done:
273
274         /* Check for PMM */
275         movw    $( 0xe000 - 1 ), %bx
276 pmm_scan:
277         incw    %bx
278         jz      no_pmm
279         movw    %bx, %es
280         cmpl    $PMM_SIGNATURE, %es:0
281         jne     pmm_scan
282         xorw    %dx, %dx
283         xorw    %si, %si
284         movzbw  %es:5, %cx
285 1:      es lodsb
286         addb    %al, %dl
287         loop    1b
288         jnz     pmm_scan
289         /* PMM found: print PMM message */
290         movw    $init_message_pmm, %si
291         xorw    %di, %di
292         call    print_message
293         /* Try to allocate 2MB block via PMM */
294         pushw   $0x0006         /* Aligned, extended memory */
295         pushl   $0xffffffff     /* No handle */
296         pushl   $( 0x00200000 / 16 ) /* 2MB in paragraphs */
297         pushw   $PMM_ALLOCATE
298         lcall   *%es:7
299         addw    $12, %sp
300         movw    %dx, %ax
301         xorw    %di, %di
302         call    print_hex_word
303         movw    %dx, ( image_source + 2 )
304         testw   %dx, %dx        /* %ax==0 even on success, since align=2MB */
305         jz      no_pmm
306         /* PMM allocation succeeded: copy ROM to PMM block */
307         pushal                  /* PMM presence implies 1kB stack */
308         xorw    %ax, %ax
309         movw    %ax, %es
310         movl    image_source, %edi
311         xorl    %esi, %esi
312         movzbl  romheader_size, %ecx
313         shll    $9, %ecx
314         addr32 rep movsb        /* PMM presence implies flat real mode */
315         movl    %edi, decompress_to
316         /* Shrink ROM and update checksum */
317         xorw    %bx, %bx
318         xorw    %si, %si
319         movw    $_prefix_memsz_sect, %cx
320         movb    %cl, romheader_size
321         shlw    $9, %cx
322 1:      lodsb
323         addb    %al, %bl
324         loop    1b
325         subb    %bl, checksum
326         popal
327 no_pmm:
328
329         /* Copy self to option ROM space.  Required for PCI3.0, which
330          * loads us to a temporary location in low memory.  Will be a
331          * no-op for lower PCI versions.
332          */
333         movb    $' ', %al
334         xorw    %di, %di
335         call    print_character
336         movw    %gs, %ax
337         call    print_hex_word
338         movzbw  romheader_size, %cx
339         shlw    $9, %cx
340         movw    %ax, %es
341         xorw    %si, %si
342         xorw    %di, %di
343         cs rep  movsb
344
345         /* Prompt for POST-time shell */
346         movw    $init_message_prompt, %si
347         xorw    %di, %di
348         call    print_message
349         /* Wait for Ctrl-B */
350         movw    $0xff02, %bx
351         call    wait_for_key
352         /* Clear prompt */
353         pushf
354         movw    $clear_message, %si
355         xorw    %di, %di
356         call    print_message
357         popf
358         jnz     1f
359         /* Ctrl-B was pressed: invoke gPXE.  The keypress will be
360          * picked up by the initial shell prompt, and we will drop
361          * into a shell.
362          */
363         pushw   %cs
364         call    exec
365 1:
366         /* Restore registers */
367         popw    %gs
368         popw    %fs
369         popw    %es
370         popw    %ds
371         popaw
372
373         /* Indicate boot capability to PnP BIOS, if present */
374         movw    $0x20, %ax
375         lret
376         .size init, . - init
377
378 init_message:
379         .asciz  "gPXE (http://etherboot.org) - "
380         .size   init_message, . - init_message
381 init_message_pci:
382         .asciz  " PCI"
383         .size   init_message_pci, . - init_message_pci
384 init_message_pnp:
385         .asciz  " PnP"
386         .size   init_message_pnp, . - init_message_pnp
387 init_message_bbs:
388         .asciz  " BBS"
389         .size   init_message_bbs, . - init_message_bbs
390 init_message_pmm:
391         .asciz  " PMM"
392         .size   init_message_pmm, . - init_message_pmm
393 init_message_int19:
394         .asciz  " INT19"
395         .size   init_message_int19, . - init_message_int19
396 init_message_prompt:
397         .asciz  "\nPress Ctrl-B to configure gPXE..."
398         .size   init_message_prompt, . - init_message_prompt
399 clear_message:
400         .asciz  "\r                                          \n\n"
401         .size   clear_message, . - clear_message
402
403 /* ROM image location
404  *
405  * May be either within option ROM space, or within PMM-allocated block.
406  */
407 image_source:
408         .long   0
409         .size   image_source, . - image_source
410
411 /* Temporary decompression area
412  *
413  * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block.
414  */
415 decompress_to:
416         .long   HIGHMEM_LOADPOINT
417         .size   decompress_to, . - decompress_to
418
419 /* BBS version
420  *
421  * Filled in by BBS BIOS.  We ignore the value.
422  */
423 bbs_version:
424         .word   0
425         .size   bbs_version, . - bbs_version
426
427 /* Boot Execution Vector entry point
428  *
429  * Called by the PnP BIOS when it wants to boot us.
430  */
431 bev_entry:
432         pushw   %cs
433         call    exec
434         lret
435         .size   bev_entry, . - bev_entry
436
437 /* INT19 entry point
438  *
439  * Called via the hooked INT 19 if we detected a non-PnP BIOS.  We
440  * attempt to return via the original INT 19 vector (if we were able
441  * to store it).
442  */
443 int19_entry:
444         pushw   %cs
445         popw    %ds
446         /* Prompt user to press B to boot */
447         movw    $int19_message_prompt, %si
448         xorw    %di, %di
449         call    print_message
450         movw    $prodstr, %si
451         call    print_message
452         movw    $int19_message_dots, %si
453         call    print_message
454         movw    $0xdf42, %bx
455         call    wait_for_key
456         pushf
457         movw    $clear_message, %si
458         xorw    %di, %di
459         call    print_message
460         popf
461         jnz     1f
462         /* Leave keypress in buffer and start gPXE.  The keypress will
463          * cause the usual initial Ctrl-B prompt to be skipped.
464          */
465         pushw   %cs
466         call    exec
467 1:      /* Try to call original INT 19 vector */
468         movl    %cs:orig_int19, %eax
469         testl   %eax, %eax
470         je      2f
471         ljmp    *%cs:orig_int19
472 2:      /* No chained vector: issue INT 18 as a last resort */
473         int     $0x18
474         .size   int19_entry, . - int19_entry
475 orig_int19:
476         .long   0
477         .size   orig_int19, . - orig_int19
478
479 int19_message_prompt:
480         .asciz  "Press B to boot from "
481         .size   int19_message_prompt, . - int19_message_prompt
482 int19_message_dots:
483         .asciz  "..."
484         .size   int19_message_dots, . - int19_message_dots
485         
486 /* Execute as a boot device
487  *
488  */
489 exec:   /* Set %ds = %cs */
490         pushw   %cs
491         popw    %ds
492
493         /* Print message as soon as possible */
494         movw    $prodstr, %si
495         xorw    %di, %di
496         call    print_message
497         movw    $exec_message, %si
498         call    print_message
499
500         /* Store magic word on BIOS stack and remember BIOS %ss:sp */
501         pushl   $STACK_MAGIC
502         movw    %ss, %dx
503         movw    %sp, %bp
504
505         /* Obtain a reasonably-sized temporary stack */
506         xorw    %ax, %ax
507         movw    %ax, %ss
508         movw    $0x7c00, %sp
509
510         /* Install gPXE */
511         movl    image_source, %esi
512         movl    decompress_to, %edi
513         call    alloc_basemem
514         call    install_prealloc
515
516         /* Set up real-mode stack */
517         movw    %bx, %ss
518         movw    $_estack16, %sp
519
520         /* Jump to .text16 segment */
521         pushw   %ax
522         pushw   $1f
523         lret
524         .section ".text16", "awx", @progbits
525 1:      /* Call main() */
526         pushl   $main
527         pushw   %cs
528         call    prot_call
529         /* No need to clean up stack; we are about to reload %ss:sp */
530         
531         /* Restore BIOS stack */
532         movw    %dx, %ss
533         movw    %bp, %sp
534
535         /* Check magic word on BIOS stack */
536         popl    %eax
537         cmpl    $STACK_MAGIC, %eax
538         jne     1f
539         /* BIOS stack OK: return to caller */
540         lret
541 1:      /* BIOS stack corrupt: use INT 18 */
542         int     $0x18
543         .previous
544
545 exec_message:
546         .asciz  " starting execution\n"
547         .size exec_message, . - exec_message
548
549 /* UNDI loader
550  *
551  * Called by an external program to load our PXE stack.
552  */
553 undiloader:
554         /* Save registers */
555         pushl   %esi
556         pushl   %edi
557         pushw   %ds
558         pushw   %es
559         pushw   %bx
560         /* ROM segment address to %ds */
561         pushw   %cs
562         popw    %ds
563         /* UNDI loader parameter structure address into %es:%di */
564         movw    %sp, %bx
565         movw    %ss:18(%bx), %di
566         movw    %ss:20(%bx), %es
567         /* Install to specified real-mode addresses */
568         pushw   %di
569         movw    %es:12(%di), %bx
570         movw    %es:14(%di), %ax
571         movl    image_source, %esi
572         movl    decompress_to, %edi
573         call    install_prealloc
574         popw    %di
575         /* Call UNDI loader C code */
576         pushl   $pxe_loader_call
577         pushw   %cs
578         pushw   $1f
579         pushw   %ax
580         pushw   $prot_call
581         lret
582 1:      popw    %bx     /* discard */
583         popw    %bx     /* discard */
584         /* Restore registers and return */
585         popw    %bx
586         popw    %es
587         popw    %ds
588         popl    %edi
589         popl    %esi
590         lret
591         .size undiloader, . - undiloader
592
593 /* Wait for key press specified by %bl (masked by %bh)
594  *
595  * Used by init and INT19 code when prompting user.  If the specified
596  * key is pressed, it is left in the keyboard buffer.
597  *
598  * Returns with ZF set iff specified key is pressed.
599  */
600 wait_for_key:
601         /* Preserve registers */
602         pushw   %cx
603         pushw   %ax
604 1:      /* Empty the keyboard buffer before waiting for input */
605         movb    $0x01, %ah
606         int     $0x16
607         jz      2f
608         xorw    %ax, %ax
609         int     $0x16
610         jmp     1b
611 2:      /* Wait for a key press */
612         movw    $ROM_BANNER_TIMEOUT, %cx
613 3:      decw    %cx
614         js      99f             /* Exit with ZF clear */
615         /* Wait for timer tick to be updated */
616         call    wait_for_tick
617         /* Check to see if a key was pressed */
618         movb    $0x01, %ah
619         int     $0x16
620         jz      3b
621         /* Check to see if key was the specified key */
622         andb    %bh, %al
623         cmpb    %al, %bl
624         je      99f             /* Exit with ZF set */
625         /* Not the specified key: remove from buffer and stop waiting */
626         pushfw
627         xorw    %ax, %ax
628         int     $0x16
629         popfw                   /* Exit with ZF clear */
630 99:     /* Restore registers and return */
631         popw    %ax
632         popw    %cx
633         ret
634         .size wait_for_key, . - wait_for_key
635
636 /* Wait for timer tick
637  *
638  * Used by wait_for_key
639  */
640 wait_for_tick:
641         pushl   %eax
642         pushw   %fs
643         movw    $0x40, %ax
644         movw    %ax, %fs
645         movl    %fs:(0x6c), %eax
646 1:      pushf
647         sti
648         hlt
649         popf
650         cmpl    %fs:(0x6c), %eax
651         je      1b
652         popw    %fs
653         popl    %eax
654         ret
655         .size wait_for_tick, . - wait_for_tick