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