[romprefix] Add vendor branding facilities and guidelines
[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 PRODUCT_SHORT_NAME.  If the ROM image is writable at
113  * initialisation time, it will be filled in to include the PCI
114  * bus:dev.fn number of the card as well.
115  */
116 prodstr:
117         .ascii  PRODUCT_SHORT_NAME
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         movw    $prodstr, %si
350         call    print_message
351         movw    $init_message_dots, %si
352         call    print_message
353         /* Wait for Ctrl-B */
354         movw    $0xff02, %bx
355         call    wait_for_key
356         /* Clear prompt */
357         pushf
358         xorw    %di, %di
359         call    print_kill_line
360         movw    $init_message_done, %si
361         call    print_message
362         popf
363         jnz     2f
364         /* Ctrl-B was pressed: invoke gPXE.  The keypress will be
365          * picked up by the initial shell prompt, and we will drop
366          * into a shell.
367          */
368         pushw   %cs
369         call    exec
370 2:
371         /* Restore registers */
372         popw    %gs
373         popw    %fs
374         popw    %es
375         popw    %ds
376         popaw
377
378         /* Indicate boot capability to PnP BIOS, if present */
379         movw    $0x20, %ax
380         lret
381         .size init, . - init
382
383 /*
384  * Note to hardware vendors:
385  *
386  * If you wish to brand this boot ROM, please do so by defining the
387  * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/general.h.
388  *
389  * While nothing in the GPL prevents you from removing all references
390  * to gPXE or http://etherboot.org, we prefer you not to do so.
391  *
392  * If you have an OEM-mandated branding requirement that cannot be
393  * satisfied simply by defining PRODUCT_NAME and PRODUCT_SHORT_NAME,
394  * please contact us.
395  *
396  * [ Including an ASCII NUL in PRODUCT_NAME is considered to be
397  *   bypassing the spirit of this request! ]
398  */
399 init_message:
400         .ascii  "\n"
401         .ascii  PRODUCT_NAME
402         .ascii  "\n"
403         .asciz  "gPXE (http://etherboot.org) - "
404         .size   init_message, . - init_message
405 init_message_pci:
406         .asciz  " PCI"
407         .size   init_message_pci, . - init_message_pci
408 init_message_pnp:
409         .asciz  " PnP"
410         .size   init_message_pnp, . - init_message_pnp
411 init_message_bbs:
412         .asciz  " BBS"
413         .size   init_message_bbs, . - init_message_bbs
414 init_message_pmm:
415         .asciz  " PMM"
416         .size   init_message_pmm, . - init_message_pmm
417 init_message_int19:
418         .asciz  " INT19"
419         .size   init_message_int19, . - init_message_int19
420 init_message_prompt:
421         .asciz  "\nPress Ctrl-B to configure "
422         .size   init_message_prompt, . - init_message_prompt
423 init_message_dots:
424         .asciz  "..."
425         .size   init_message_dots, . - init_message_dots
426 init_message_done:
427         .asciz  "\n\n"
428         .size   init_message_done, . - init_message_done
429
430 /* ROM image location
431  *
432  * May be either within option ROM space, or within PMM-allocated block.
433  */
434 image_source:
435         .long   0
436         .size   image_source, . - image_source
437
438 /* Temporary decompression area
439  *
440  * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block.
441  */
442 decompress_to:
443         .long   HIGHMEM_LOADPOINT
444         .size   decompress_to, . - decompress_to
445
446 /* BBS version
447  *
448  * Filled in by BBS BIOS.  We ignore the value.
449  */
450 bbs_version:
451         .word   0
452         .size   bbs_version, . - bbs_version
453
454 /* Boot Execution Vector entry point
455  *
456  * Called by the PnP BIOS when it wants to boot us.
457  */
458 bev_entry:
459         pushw   %cs
460         call    exec
461         lret
462         .size   bev_entry, . - bev_entry
463
464 /* INT19 entry point
465  *
466  * Called via the hooked INT 19 if we detected a non-PnP BIOS.  We
467  * attempt to return via the original INT 19 vector (if we were able
468  * to store it).
469  */
470 int19_entry:
471         pushw   %cs
472         popw    %ds
473         /* Prompt user to press B to boot */
474         movw    $int19_message_prompt, %si
475         xorw    %di, %di
476         call    print_message
477         movw    $prodstr, %si
478         call    print_message
479         movw    $int19_message_dots, %si
480         call    print_message
481         movw    $0xdf42, %bx
482         call    wait_for_key
483         pushf
484         xorw    %di, %di
485         call    print_kill_line
486         movw    $int19_message_done, %si
487         call    print_message
488         popf
489         jnz     1f
490         /* Leave keypress in buffer and start gPXE.  The keypress will
491          * cause the usual initial Ctrl-B prompt to be skipped.
492          */
493         pushw   %cs
494         call    exec
495 1:      /* Try to call original INT 19 vector */
496         movl    %cs:orig_int19, %eax
497         testl   %eax, %eax
498         je      2f
499         ljmp    *%cs:orig_int19
500 2:      /* No chained vector: issue INT 18 as a last resort */
501         int     $0x18
502         .size   int19_entry, . - int19_entry
503 orig_int19:
504         .long   0
505         .size   orig_int19, . - orig_int19
506
507 int19_message_prompt:
508         .asciz  "Press B to boot from "
509         .size   int19_message_prompt, . - int19_message_prompt
510 int19_message_dots:
511         .asciz  "..."
512         .size   int19_message_dots, . - int19_message_dots
513 int19_message_done:
514         .asciz  "\n\n"
515         .size   int19_message_done, . - int19_message_done
516         
517 /* Execute as a boot device
518  *
519  */
520 exec:   /* Set %ds = %cs */
521         pushw   %cs
522         popw    %ds
523
524         /* Print message as soon as possible */
525         movw    $prodstr, %si
526         xorw    %di, %di
527         call    print_message
528         movw    $exec_message, %si
529         call    print_message
530
531         /* Store magic word on BIOS stack and remember BIOS %ss:sp */
532         pushl   $STACK_MAGIC
533         movw    %ss, %dx
534         movw    %sp, %bp
535
536         /* Obtain a reasonably-sized temporary stack */
537         xorw    %ax, %ax
538         movw    %ax, %ss
539         movw    $0x7c00, %sp
540
541         /* Install gPXE */
542         movl    image_source, %esi
543         movl    decompress_to, %edi
544         call    alloc_basemem
545         call    install_prealloc
546
547         /* Set up real-mode stack */
548         movw    %bx, %ss
549         movw    $_estack16, %sp
550
551         /* Jump to .text16 segment */
552         pushw   %ax
553         pushw   $1f
554         lret
555         .section ".text16", "awx", @progbits
556 1:      /* Call main() */
557         pushl   $main
558         pushw   %cs
559         call    prot_call
560         /* No need to clean up stack; we are about to reload %ss:sp */
561         
562         /* Restore BIOS stack */
563         movw    %dx, %ss
564         movw    %bp, %sp
565
566         /* Check magic word on BIOS stack */
567         popl    %eax
568         cmpl    $STACK_MAGIC, %eax
569         jne     1f
570         /* BIOS stack OK: return to caller */
571         lret
572 1:      /* BIOS stack corrupt: use INT 18 */
573         int     $0x18
574         .previous
575
576 exec_message:
577         .asciz  " starting execution\n"
578         .size exec_message, . - exec_message
579
580 /* UNDI loader
581  *
582  * Called by an external program to load our PXE stack.
583  */
584 undiloader:
585         /* Save registers */
586         pushl   %esi
587         pushl   %edi
588         pushw   %ds
589         pushw   %es
590         pushw   %bx
591         /* ROM segment address to %ds */
592         pushw   %cs
593         popw    %ds
594         /* UNDI loader parameter structure address into %es:%di */
595         movw    %sp, %bx
596         movw    %ss:18(%bx), %di
597         movw    %ss:20(%bx), %es
598         /* Install to specified real-mode addresses */
599         pushw   %di
600         movw    %es:12(%di), %bx
601         movw    %es:14(%di), %ax
602         movl    image_source, %esi
603         movl    decompress_to, %edi
604         call    install_prealloc
605         popw    %di
606         /* Call UNDI loader C code */
607         pushl   $pxe_loader_call
608         pushw   %cs
609         pushw   $1f
610         pushw   %ax
611         pushw   $prot_call
612         lret
613 1:      popw    %bx     /* discard */
614         popw    %bx     /* discard */
615         /* Restore registers and return */
616         popw    %bx
617         popw    %es
618         popw    %ds
619         popl    %edi
620         popl    %esi
621         lret
622         .size undiloader, . - undiloader
623
624 /* Wait for key press specified by %bl (masked by %bh)
625  *
626  * Used by init and INT19 code when prompting user.  If the specified
627  * key is pressed, it is left in the keyboard buffer.
628  *
629  * Returns with ZF set iff specified key is pressed.
630  */
631 wait_for_key:
632         /* Preserve registers */
633         pushw   %cx
634         pushw   %ax
635 1:      /* Empty the keyboard buffer before waiting for input */
636         movb    $0x01, %ah
637         int     $0x16
638         jz      2f
639         xorw    %ax, %ax
640         int     $0x16
641         jmp     1b
642 2:      /* Wait for a key press */
643         movw    $ROM_BANNER_TIMEOUT, %cx
644 3:      decw    %cx
645         js      99f             /* Exit with ZF clear */
646         /* Wait for timer tick to be updated */
647         call    wait_for_tick
648         /* Check to see if a key was pressed */
649         movb    $0x01, %ah
650         int     $0x16
651         jz      3b
652         /* Check to see if key was the specified key */
653         andb    %bh, %al
654         cmpb    %al, %bl
655         je      99f             /* Exit with ZF set */
656         /* Not the specified key: remove from buffer and stop waiting */
657         pushfw
658         xorw    %ax, %ax
659         int     $0x16
660         popfw                   /* Exit with ZF clear */
661 99:     /* Restore registers and return */
662         popw    %ax
663         popw    %cx
664         ret
665         .size wait_for_key, . - wait_for_key
666
667 /* Wait for timer tick
668  *
669  * Used by wait_for_key
670  */
671 wait_for_tick:
672         pushl   %eax
673         pushw   %fs
674         movw    $0x40, %ax
675         movw    %ax, %fs
676         movl    %fs:(0x6c), %eax
677 1:      pushf
678         sti
679         hlt
680         popf
681         cmpl    %fs:(0x6c), %eax
682         je      1b
683         popw    %fs
684         popl    %eax
685         ret
686         .size wait_for_tick, . - wait_for_tick